From cb40ed7cb7d22399af4e75c9f4587cda1a3a943c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Tue, 16 Sep 2025 17:42:49 +0800 Subject: [PATCH] =?UTF-8?q?feat(user):=20=E6=96=B0=E5=A2=9E=E7=AB=99?= =?UTF-8?q?=E5=86=85=E6=B6=88=E6=81=AF=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加聊天消息相关API和模型定义 - 实现消息列表、消息详情和发送消息页面 - 集成消息功能到首页和团队页面 -优化用户模型,增加别名字段 --- src/api/shop/shopChatConversation/index.ts | 101 ++++++++++ .../shop/shopChatConversation/model/index.ts | 37 ++++ src/api/shop/shopChatMessage/index.ts | 115 +++++++++++ src/api/shop/shopChatMessage/model/index.ts | 63 ++++++ src/api/shop/shopCoupon/model/index.ts | 2 +- src/api/shop/shopDealerReferee/model/index.ts | 2 + src/api/system/chatMessage/model/index.ts | 3 +- src/api/system/user/index.ts | 28 +-- src/app.config.ts | 6 +- src/dealer/team/index.tsx | 81 +++++++- src/dealer/withdraw/index.tsx | 13 ++ src/pages/index/Grid.tsx | 14 +- src/pages/index/Menu.tsx | 5 +- src/pages/index/PopUpAd.tsx | 47 +++++ src/pages/index/index.tsx | 2 + src/user/chat/conversation/index.config.ts | 3 + src/user/chat/conversation/index.tsx | 167 ++++++++++++++++ src/user/chat/message/add.config.ts | 4 + src/user/chat/message/add.tsx | 135 +++++++++++++ src/user/chat/message/detail.config.ts | 4 + src/user/chat/message/detail.tsx | 77 ++++++++ src/user/chat/message/index.config.ts | 3 + src/user/chat/message/index.tsx | 179 ++++++++++++++++++ 23 files changed, 1060 insertions(+), 31 deletions(-) create mode 100644 src/api/shop/shopChatConversation/index.ts create mode 100644 src/api/shop/shopChatConversation/model/index.ts create mode 100644 src/api/shop/shopChatMessage/index.ts create mode 100644 src/api/shop/shopChatMessage/model/index.ts create mode 100644 src/pages/index/PopUpAd.tsx create mode 100644 src/user/chat/conversation/index.config.ts create mode 100644 src/user/chat/conversation/index.tsx create mode 100644 src/user/chat/message/add.config.ts create mode 100644 src/user/chat/message/add.tsx create mode 100644 src/user/chat/message/detail.config.ts create mode 100644 src/user/chat/message/detail.tsx create mode 100644 src/user/chat/message/index.config.ts create mode 100644 src/user/chat/message/index.tsx diff --git a/src/api/shop/shopChatConversation/index.ts b/src/api/shop/shopChatConversation/index.ts new file mode 100644 index 0000000..6a5ba5b --- /dev/null +++ b/src/api/shop/shopChatConversation/index.ts @@ -0,0 +1,101 @@ +import request from '@/utils/request'; +import type { ApiResult, PageResult } from '@/api'; +import type { ShopChatConversation, ShopChatConversationParam } from './model'; + +/** + * 分页查询聊天会话表 + */ +export async function pageShopChatConversation(params: ShopChatConversationParam) { + const res = await request.get>>( + '/shop/shop-chat-conversation/page', + params + ); + if (res.code === 0) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 查询聊天会话表列表 + */ +export async function listShopChatConversation(params?: ShopChatConversationParam) { + const res = await request.get>( + '/shop/shop-chat-conversation', + params + ); + if (res.code === 0 && res.data) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 添加聊天会话表 + */ +export async function addShopChatConversation(data: ShopChatConversation) { + const res = await request.post>( + '/shop/shop-chat-conversation', + data + ); + if (res.code === 0) { + return res.message; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 修改聊天会话表 + */ +export async function updateShopChatConversation(data: ShopChatConversation) { + const res = await request.put>( + '/shop/shop-chat-conversation', + data + ); + if (res.code === 0) { + return res.message; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 删除聊天会话表 + */ +export async function removeShopChatConversation(id?: number) { + const res = await request.del>( + '/shop/shop-chat-conversation/' + id + ); + if (res.code === 0) { + return res.message; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 批量删除聊天会话表 + */ +export async function removeShopBatchChatConversation(data: (number | undefined)[]) { + const res = await request.del>( + '/shop/shop-chat-conversation/batch', + { + data + } + ); + if (res.code === 0) { + return res.message; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 根据id查询聊天会话表 + */ +export async function getShopChatConversation(id: number) { + const res = await request.get>( + '/shop/shop-chat-conversation/' + id + ); + if (res.code === 0 && res.data) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} diff --git a/src/api/shop/shopChatConversation/model/index.ts b/src/api/shop/shopChatConversation/model/index.ts new file mode 100644 index 0000000..ea6a9b3 --- /dev/null +++ b/src/api/shop/shopChatConversation/model/index.ts @@ -0,0 +1,37 @@ +import type { PageParam } from '@/api'; + +/** + * 聊天消息表 + */ +export interface ShopChatConversation { + // 自增ID + id?: number; + // 用户ID + userId?: number; + // 好友ID + friendId?: number; + // 消息类型 + type?: number; + // 消息内容 + content?: string; + // 未读消息 + unRead?: number; + // 状态, 0未读, 1已读 + status?: number; + // 是否删除, 0否, 1是 + deleted?: number; + // 租户id + tenantId?: number; + // 注册时间 + createTime?: string; + // 修改时间 + updateTime?: string; +} + +/** + * 聊天消息表搜索条件 + */ +export interface ShopChatConversationParam extends PageParam { + id?: number; + keywords?: string; +} diff --git a/src/api/shop/shopChatMessage/index.ts b/src/api/shop/shopChatMessage/index.ts new file mode 100644 index 0000000..4667ce7 --- /dev/null +++ b/src/api/shop/shopChatMessage/index.ts @@ -0,0 +1,115 @@ +import request from '@/utils/request'; +import type { ApiResult, PageResult } from '@/api'; +import type { ShopChatMessage, ShopChatMessageParam } from './model'; + +/** + * 分页查询聊天消息表 + */ +export async function pageShopChatMessage(params: ShopChatMessageParam) { + const res = await request.get>>( + '/shop/shop-chat-message/page', + params + ); + if (res.code === 0) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 查询聊天消息表列表 + */ +export async function listShopChatMessage(params?: ShopChatMessageParam) { + const res = await request.get>( + '/shop/shop-chat-message', + params + ); + if (res.code === 0 && res.data) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 添加聊天消息表 + */ +export async function addShopChatMessage(data: ShopChatMessage) { + const res = await request.post>( + '/shop/shop-chat-message', + data + ); + if (res.code === 0) { + return res.message; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 添加聊天消息表 + */ +export async function addShopBatchChatMessage(data: ShopChatMessage[]) { + const res = await request.post>( + '/shop/shop-chat-message/batch', + data + ); + if (res.code === 0) { + return res.message; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 修改聊天消息表 + */ +export async function updateShopChatMessage(data: ShopChatMessage) { + const res = await request.put>( + '/shop/shop-chat-message', + data + ); + if (res.code === 0) { + return res.message; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 删除聊天消息表 + */ +export async function removeShopChatMessage(id?: number) { + const res = await request.del>( + '/shop/shop-chat-message/' + id + ); + if (res.code === 0) { + return res.message; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 批量删除聊天消息表 + */ +export async function removeShopBatchChatMessage(data: (number | undefined)[]) { + const res = await request.del>( + '/shop/shop-chat-message/batch', + { + data + } + ); + if (res.code === 0) { + return res.message; + } + return Promise.reject(new Error(res.message)); +} + +/** + * 根据id查询聊天消息表 + */ +export async function getShopChatMessage(id: number) { + const res = await request.get>( + '/shop/shop-chat-message/' + id + ); + if (res.code === 0 && res.data) { + return res.data; + } + return Promise.reject(new Error(res.message)); +} diff --git a/src/api/shop/shopChatMessage/model/index.ts b/src/api/shop/shopChatMessage/model/index.ts new file mode 100644 index 0000000..411d45c --- /dev/null +++ b/src/api/shop/shopChatMessage/model/index.ts @@ -0,0 +1,63 @@ +import type { PageParam } from '@/api'; + +/** + * 聊天消息表 + */ +export interface ShopChatMessage { + // 自增ID + id?: number; + // 发送人ID + formUserId?: number; + // 发送人名称 + formUserName?: string; + // 发送人头像 + formUserAvatar?: string; + // 发送人手机号 + formUserPhone?: string; + // 发送人别名 + formUserAlias?: string; + // 接收人ID + toUserId?: number; + // 接收人名称 + toUserName?: string; + // 接收人头像 + toUserAvatar?: string; + // 接收人手机号 + toUserPhone?: string; + // 接收人别名 + toUserAlias?: string; + // 消息类型 + type?: string; + // 消息内容 + content?: string; + // 屏蔽接收方 + sideTo?: number; + // 屏蔽发送方 + sideFrom?: number; + // 是否撤回 + withdraw?: number; + // 文件信息 + fileInfo?: string; + // 批量发送 + toUserIds?: any[]; + // 存在联系方式 + hasContact?: number; + // 状态, 0未读, 1已读 + status?: number; + // 是否删除, 0否, 1是 + deleted?: number; + // 租户id + tenantId?: number; + // 注册时间 + createTime?: string; + // 修改时间 + updateTime?: string; +} + +/** + * 聊天消息表搜索条件 + */ +export interface ShopChatMessageParam extends PageParam { + id?: number; + keywords?: string; +} diff --git a/src/api/shop/shopCoupon/model/index.ts b/src/api/shop/shopCoupon/model/index.ts index 7837ba0..823a6f4 100644 --- a/src/api/shop/shopCoupon/model/index.ts +++ b/src/api/shop/shopCoupon/model/index.ts @@ -1,4 +1,4 @@ -import type { PageParam } from '@/api/index'; +import type { PageParam } from '@/api'; /** * 优惠券 diff --git a/src/api/shop/shopDealerReferee/model/index.ts b/src/api/shop/shopDealerReferee/model/index.ts index f2021aa..70eee04 100644 --- a/src/api/shop/shopDealerReferee/model/index.ts +++ b/src/api/shop/shopDealerReferee/model/index.ts @@ -18,6 +18,8 @@ export interface ShopDealerReferee { avatar?: string; // 用户昵称 nickname?: string; + // 用户别名 + alias?: string; // 用户手机号 phone?: string; // 推荐关系层级(1,2,3) diff --git a/src/api/system/chatMessage/model/index.ts b/src/api/system/chatMessage/model/index.ts index 4352a3f..27c9f51 100644 --- a/src/api/system/chatMessage/model/index.ts +++ b/src/api/system/chatMessage/model/index.ts @@ -22,7 +22,8 @@ export interface ChatMessage { withdraw?: number; // 文件信息 fileInfo?: string; - toUserName?: any; + // + toUserName?: string; formUserName?: string; // 批量发送 toUserIds?: any[]; diff --git a/src/api/system/user/index.ts b/src/api/system/user/index.ts index 1f14ccd..0c55116 100644 --- a/src/api/system/user/index.ts +++ b/src/api/system/user/index.ts @@ -22,7 +22,7 @@ export async function pageUsers(params: UserParam) { */ export async function listUsers(params?: UserParam) { const res = await request.get>( - '/system/user', + SERVER_API_URL + '/system/user', params ); if (res.code === 0 && res.data) { @@ -36,7 +36,7 @@ export async function listUsers(params?: UserParam) { */ export async function getStaffs(params?: UserParam) { const res = await request.get>( - '/system/user', + SERVER_API_URL + '/system/user', { params } @@ -52,7 +52,7 @@ export async function getStaffs(params?: UserParam) { */ export async function getCompanyList(params?: UserParam) { const res = await request.get>( - '/system/user', + SERVER_API_URL + '/system/user', { params } @@ -68,7 +68,7 @@ export async function getCompanyList(params?: UserParam) { */ export async function getUser(id: number) { const res = await request.get>( - '/system/user/' + id, + SERVER_API_URL + '/system/user/' + id, {} ); if (res.code === 0 && res.data) { @@ -82,7 +82,7 @@ export async function getUser(id: number) { */ export async function addUser(data: User) { const res = await request.post>( - '/system/user', + SERVER_API_URL + '/system/user', data ); if (res.code === 0) { @@ -110,7 +110,7 @@ export async function updateUser(data: User) { */ export async function removeUser(id?: number) { const res = await request.del>( - '/system/user/' + id + SERVER_API_URL + '/system/user/' + id ); if (res.code === 0) { return res.message; @@ -123,7 +123,7 @@ export async function removeUser(id?: number) { */ export async function removeUsers(data: (number | undefined)[]) { const res = await request.del>( - '/system/user/batch', + SERVER_API_URL + '/system/user/batch', { data } @@ -139,7 +139,7 @@ export async function removeUsers(data: (number | undefined)[]) { */ export async function updateUserStatus(userId?: number, status?: number) { const res = await request.put>( - '/system/user/status', + SERVER_API_URL + '/system/user/status', { userId, status @@ -156,7 +156,7 @@ export async function updateUserStatus(userId?: number, status?: number) { */ export async function updateUserRecommend(form:any) { const res = await request.put>( - '/system/user/recommend', + SERVER_API_URL + '/system/user/recommend', form ); if (res.code === 0) { @@ -170,7 +170,7 @@ export async function updateUserRecommend(form:any) { */ export async function updateUserPassword(userId?: number, password = '123456') { const res = await request.put>( - '/system/user/password', + SERVER_API_URL + '/system/user/password', { userId, password @@ -189,7 +189,7 @@ export async function importUsers(file: File) { const formData = new FormData(); formData.append('file', file); const res = await request.post>( - '/system/user/import', + SERVER_API_URL + '/system/user/import', formData ); if (res.code === 0) { @@ -207,7 +207,7 @@ export async function checkExistence( id?: number ) { const res = await request.get>( - '/system/user/existence', + SERVER_API_URL + '/system/user/existence', { params: {field, value, id} } @@ -223,7 +223,7 @@ export async function checkExistence( */ export async function countUserBalance(params?: UserParam) { const res = await request.get>( - '/system/user/countUserBalance', + SERVER_API_URL + '/system/user/countUserBalance', { params } @@ -241,7 +241,7 @@ export async function countUserBalance(params?: UserParam) { */ export async function listAdminsByPhoneAll(params?: UserParam) { const res = await request.get>( - '/system/user/listAdminsByPhoneAll', + SERVER_API_URL + '/system/user/listAdminsByPhoneAll', params ); if (res.code === 0 && res.data) { diff --git a/src/app.config.ts b/src/app.config.ts index bfdf8f8..3f800f5 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -44,7 +44,11 @@ export default defineAppConfig({ "gift/redeem", "gift/detail", "store/verification", - "theme/index" + "theme/index", + "chat/conversation/index", + "chat/message/index", + "chat/message/add", + "chat/message/detail" ] }, { diff --git a/src/dealer/team/index.tsx b/src/dealer/team/index.tsx index e6cb1ec..184da70 100644 --- a/src/dealer/team/index.tsx +++ b/src/dealer/team/index.tsx @@ -1,6 +1,6 @@ import React, {useState, useEffect, useCallback} from 'react' import {View, Text} from '@tarojs/components' -import {Phone} from '@nutui/icons-react-taro' +import {Phone, Edit, Message} from '@nutui/icons-react-taro' import {Space, Empty, Avatar, Button} from '@nutui/nutui-react-taro' import Taro from '@tarojs/taro' import {useDealerUser} from '@/hooks/useDealerUser' @@ -9,11 +9,13 @@ import {pageShopDealerOrder} from '@/api/shop/shopDealerOrder' import type {ShopDealerReferee} from '@/api/shop/shopDealerReferee/model' import FixedButton from "@/components/FixedButton"; import navTo from "@/utils/common"; +import {updateUser} from "@/api/system/user"; interface TeamMemberWithStats extends ShopDealerReferee { name?: string avatar?: string nickname?: string; + alias?: string; phone?: string; orderCount?: number commission?: string @@ -219,6 +221,54 @@ const DealerTeam: React.FC = () => { } } + // 一键拨打 + const makePhoneCall = (phone: string) => { + Taro.makePhoneCall({ + phoneNumber: phone, + fail: () => { + Taro.showToast({ + title: '拨打取消', + icon: 'error' + }); + } + }); + }; + + // 别名备注 + const editAlias = (item: any, index: number) => { + Taro.showModal({ + title: '备注', + // @ts-ignore + editable: true, + placeholderText: '真实姓名', + content: item.alias || '', + success: async (res: any) => { + if (res.confirm && res.content !== undefined) { + try { + // 更新跟进情况 + await updateUser({ + userId: item.userId, + alias: res.content.trim() + }); + teamMembers[index].alias = res.content.trim() + setTeamMembers(teamMembers) + } catch (error) { + console.error('备注失败:', error); + Taro.showToast({ + title: '备注失败,请重试', + icon: 'error' + }); + } + } + } + }); + }; + + // 发送消息 + const sendMessage = (item: TeamMemberWithStats) => { + return navTo(`/user/chat/message/add?id=${item.userId}`, true) + } + // 监听数据变化,获取团队数据 useEffect(() => { if (dealerUser?.userId || dealerId) { @@ -233,7 +283,7 @@ const DealerTeam: React.FC = () => { } }, [dealerUser, dealerId, currentDealerName]) - const renderMemberItem = (member: TeamMemberWithStats) => { + const renderMemberItem = (member: TeamMemberWithStats, index: number) => { // 判断是否可以点击:有下级成员且未达到层级限制 const canClick = member.subMembers && member.subMembers > 0 && levelStack.length < 1 // 判断是否显示手机号:只有本级(levelStack.length === 0)才显示 @@ -258,13 +308,27 @@ const DealerTeam: React.FC = () => { - - {member.nickname} - + + {member.alias ? {member.alias} : + {member.nickname}} + {/*别名备注*/} + { + e.stopPropagation() + editAlias(member, index) + }}/> + {/*发送消息*/} + { + e.stopPropagation() + sendMessage(member) + }}/> + {/* 显示手机号(仅本级可见) */} {showPhone && member.phone && ( - + { + e.stopPropagation(); + makePhoneCall(member.phone || ''); + }}> {member.phone} @@ -304,6 +368,11 @@ const DealerTeam: React.FC = () => { const renderOverview = () => ( + + 我的团队成员 + 成员数:{teamMembers.length} + {teamMembers.map(renderMemberItem)} ) diff --git a/src/dealer/withdraw/index.tsx b/src/dealer/withdraw/index.tsx index e8f6cc1..49fc439 100644 --- a/src/dealer/withdraw/index.tsx +++ b/src/dealer/withdraw/index.tsx @@ -21,6 +21,7 @@ import {pageShopDealerWithdraw, addShopDealerWithdraw} from '@/api/shop/shopDeal import type {ShopDealerWithdraw} from '@/api/shop/shopDealerWithdraw/model' import {ShopDealerBank} from "@/api/shop/shopDealerBank/model"; import {listShopDealerBank} from "@/api/shop/shopDealerBank"; +import {listCmsWebsiteField} from "@/api/cms/cmsWebsiteField"; interface WithdrawRecordWithDetails extends ShopDealerWithdraw { accountDisplay?: string @@ -37,6 +38,7 @@ const DealerWithdraw: React.FC = () => { const [availableAmount, setAvailableAmount] = useState('0.00') const [withdrawRecords, setWithdrawRecords] = useState([]) const [withdrawAmount, setWithdrawAmount] = useState('') + const [withdrawValue, setWithdrawValue] = useState('') const {dealerUser} = useDealerUser() @@ -135,12 +137,22 @@ const DealerWithdraw: React.FC = () => { setIsVisible(false) } + function fetchCmsField() { + listCmsWebsiteField({ name: 'WithdrawValue'}).then(res => { + if(res && res.length > 0){ + const text = res[0].value; + setWithdrawValue(text || '') + } + }) + } + // 初始化加载数据 useEffect(() => { if (dealerUser?.userId) { fetchBalance().then() fetchWithdrawRecords().then() fetchBanks() + fetchCmsField() } }, [fetchBalance, fetchWithdrawRecords]) @@ -353,6 +365,7 @@ const DealerWithdraw: React.FC = () => { ¥{calculateExpectedAmount(withdrawAmount)} }/> + 说明:{withdrawValue}}/> diff --git a/src/pages/index/Grid.tsx b/src/pages/index/Grid.tsx index 6ff3be2..2e3ccce 100644 --- a/src/pages/index/Grid.tsx +++ b/src/pages/index/Grid.tsx @@ -9,8 +9,13 @@ import navTo from "@/utils/common"; const MyGrid = () => { const [list, setList] = useState([]) const reload = async () => { - const menu = await listCmsNavigation({home: 0}) - setList(menu) + // 读取首页菜单 + const home = await listCmsNavigation({model: 'index'}); + const homeId = home[0].navigationId; + if(homeId){ + const menu = await listCmsNavigation({home: 0, parentId: homeId}) + setList(menu) + } } useEffect(() => { @@ -24,10 +29,7 @@ const MyGrid = () => { // @ts-ignore return ( <> - + 功能菜单 diff --git a/src/pages/index/Menu.tsx b/src/pages/index/Menu.tsx index 8672fbb..8f00a7f 100644 --- a/src/pages/index/Menu.tsx +++ b/src/pages/index/Menu.tsx @@ -14,9 +14,10 @@ const Page = () => { const reload = async () => { // 读取首页菜单 const home = await listCmsNavigation({model: 'index'}); - if (home && home.length > 0) { + const homeId = home[0].navigationId; + if (homeId) { // 读取首页导航条 - const menus = await listCmsNavigation({parentId: home[0].navigationId, hide: 0}); + const menus = await listCmsNavigation({parentId: homeId, hide: 0}); setNavItems(menus || []) } }; diff --git a/src/pages/index/PopUpAd.tsx b/src/pages/index/PopUpAd.tsx new file mode 100644 index 0000000..8d89740 --- /dev/null +++ b/src/pages/index/PopUpAd.tsx @@ -0,0 +1,47 @@ +import {useEffect, useState} from 'react' +import { Dialog } from '@nutui/nutui-react-taro' +import {getCmsNavigation} from "@/api/cms/cmsNavigation"; +import {CmsNavigation} from "@/api/cms/cmsNavigation/model"; +import {RichText} from '@tarojs/components' + +const PopUpAd = () => { + const [visible, setVisible] = useState(false) + const [item, setItem] = useState() + const reload = async () => { + const navigation = await getCmsNavigation(4426) + if(navigation && navigation.hide == 0){ + setItem(navigation) + setVisible(true) + } + } + + useEffect(() => { + reload().then() + }, []) + + return ( + <> + 重要通知 + } + footer={null} + closeIcon + closeIconPosition="top-right" + style={{ + // @ts-ignore + '--nutui-dialog-close-color': '#8c8c8c', + }} + onConfirm={() => setVisible(false)} + onCancel={() => setVisible(false)} + visible={visible} + onClose={() => { + setVisible(false) + }} + > + + + + ) +} +export default PopUpAd diff --git a/src/pages/index/index.tsx b/src/pages/index/index.tsx index 1423fda..82ddb1d 100644 --- a/src/pages/index/index.tsx +++ b/src/pages/index/index.tsx @@ -10,6 +10,7 @@ import Menu from "./Menu"; import Banner from "./Banner"; import './index.scss' import Grid from "@/pages/index/Grid"; +import PopUpAd from "@/pages/index/PopUpAd"; function Home() { // 吸顶状态 @@ -117,6 +118,7 @@ function Home() { + ) } diff --git a/src/user/chat/conversation/index.config.ts b/src/user/chat/conversation/index.config.ts new file mode 100644 index 0000000..93dd2b9 --- /dev/null +++ b/src/user/chat/conversation/index.config.ts @@ -0,0 +1,3 @@ +export default definePageConfig({ + navigationBarTitleText: '站内消息' +}) diff --git a/src/user/chat/conversation/index.tsx b/src/user/chat/conversation/index.tsx new file mode 100644 index 0000000..90b3e6f --- /dev/null +++ b/src/user/chat/conversation/index.tsx @@ -0,0 +1,167 @@ +import {useState, useCallback, useEffect} from 'react' +import {View, Text} from '@tarojs/components' +import Taro from '@tarojs/taro' +import {Loading, InfiniteLoading, Empty, Space, Tag} from '@nutui/nutui-react-taro' +import {pageShopChatConversation} from "@/api/shop/shopChatConversation"; +import FixedButton from "@/components/FixedButton"; + +const Index = () => { + const [list, setList] = useState([]) + const [loading, setLoading] = useState(false) + const [page, setPage] = useState(1) + const [hasMore, setHasMore] = useState(true) + + // 获取消息数据 + const fetchMessageData = useCallback(async (resetPage = false, targetPage?: number, searchKeyword?: string) => { + setLoading(true); + try { + const currentPage = resetPage ? 1 : (targetPage || page); + + // 构建API参数,根据状态筛选 + const params: any = { + page: currentPage + }; + + // 添加搜索关键词 + if (searchKeyword && searchKeyword.trim()) { + params.keywords = searchKeyword.trim(); + } + + const res = await pageShopChatConversation(params); + + if (res?.list && res.list.length > 0) { + // 正确映射状态 + const mappedList = res.list.map(customer => ({ + ...customer + })); + + // 如果是重置页面或第一页,直接设置新数据;否则追加数据 + if (resetPage || currentPage === 1) { + setList(mappedList); + } else { + setList(prevList => prevList.concat(mappedList)); + } + + // 正确判断是否还有更多数据 + const hasMoreData = res.list.length >= 10; // 假设每页10条数据 + setHasMore(hasMoreData); + } else { + if (resetPage || currentPage === 1) { + setList([]); + } + setHasMore(false); + } + + setPage(currentPage); + } catch (error) { + console.error('获取消息数据失败:', error); + Taro.showToast({ + title: '加载失败,请重试', + icon: 'none' + }); + } finally { + setLoading(false); + } + }, [page]); + + const reloadMore = async () => { + if (loading || !hasMore) return; // 防止重复加载 + const nextPage = page + 1; + await fetchMessageData(false, nextPage); + } + + + // 获取列表数据(现在使用服务端搜索,不需要消息端过滤) + const getFilteredList = () => { + return list; + }; + + useEffect(() => { + // 初始化时加载数据 + fetchMessageData(true, 1, ''); + }, []); + + // 渲染消息项 + const renderMessageItem = (customer: any) => ( + + + + + + 关于XXXX的通知 + + 未读 + {/*已读*/} + + + {/*统一代码:{customer.dealerCode}*/} + + 创建时间:{customer.createTime} + + + + + + ); + + // 渲染消息列表 + const renderMessageList = () => { + const filteredList = getFilteredList(); + + return ( + + { + // 滚动事件处理 + }} + onScrollToUpper={() => { + // 滚动到顶部事件处理 + }} + loadingText={ + <> + 加载中... + + } + loadMoreText={ + filteredList.length === 0 ? ( + + ) : ( + + 没有更多了 + + ) + } + > + {loading && filteredList.length === 0 ? ( + + + 加载中... + + ) : ( + filteredList.map(renderMessageItem) + )} + + + ); + }; + + return ( + + {/* 消息列表 */} + {renderMessageList()} + + + ); +}; + +export default Index; diff --git a/src/user/chat/message/add.config.ts b/src/user/chat/message/add.config.ts new file mode 100644 index 0000000..f59b48a --- /dev/null +++ b/src/user/chat/message/add.config.ts @@ -0,0 +1,4 @@ +export default definePageConfig({ + navigationBarTitleText: '发送消息', + navigationBarTextStyle: 'black' +}) diff --git a/src/user/chat/message/add.tsx b/src/user/chat/message/add.tsx new file mode 100644 index 0000000..adee859 --- /dev/null +++ b/src/user/chat/message/add.tsx @@ -0,0 +1,135 @@ +import {useEffect, useState, useRef} from "react"; +import {useRouter} from '@tarojs/taro' +import {Loading, CellGroup, Input, Form, Cell, Avatar} from '@nutui/nutui-react-taro' +import {ArrowRight} from '@nutui/icons-react-taro' +import {View, Text} from '@tarojs/components' +import Taro from '@tarojs/taro' +import FixedButton from "@/components/FixedButton"; +import {addShopChatMessage} from "@/api/shop/shopChatMessage"; +import {ShopChatMessage} from "@/api/shop/shopChatMessage/model"; +import navTo from "@/utils/common"; +import {getUser} from "@/api/system/user"; +import {User} from "@/api/system/user/model"; + +const AddMessage = () => { + const {params} = useRouter(); + const [toUser, setToUser] = useState() + const [loading, setLoading] = useState(true) + const [FormData, _] = useState() + const formRef = useRef(null) + + // 判断是编辑还是新增模式 + const isEditMode = !!params.id + const toUserId = params.id ? Number(params.id) : undefined + + const reload = async () => { + if(toUserId){ + getUser(Number(toUserId)).then(data => { + setToUser(data) + }) + } + } + + // 提交表单 + const submitSucceed = async (values: any) => { + try { + // 准备提交的数据 + const submitData = { + ...values + }; + + console.log('提交数据:', submitData) + + // 参数校验 + if(!toUser){ + Taro.showToast({ + title: `请选择发送对象`, + icon: 'error' + }); + return false; + } + + // 判断内容是否为空 + if (!values.content) { + Taro.showToast({ + title: `请输入内容`, + icon: 'error' + }); + return false; + } + // 执行新增或更新操作 + await addShopChatMessage({ + toUserId: toUserId, + formUserId: Taro.getStorageSync('UserId'), + type: 'text', + content: values.content + }); + + Taro.showToast({ + title: `发送成功`, + icon: 'success' + }); + + setTimeout(() => { + Taro.navigateBack(); + }, 1000); + + } catch (error) { + console.error('发送失败:', error); + Taro.showToast({ + title: `发送失败`, + icon: 'error' + }); + } + } + + const submitFailed = (error: any) => { + console.log(error, 'err...') + } + + useEffect(() => { + reload().then(() => { + setLoading(false) + }) + }, [isEditMode]); + + if (loading) { + return 加载中 + } + + return ( + <> + + + + {toUser.alias || toUser.nickname} + {toUser.mobile} + + + ) : '选择发送对象'} extra={( + + )} + onClick={() => navTo(`/dealer/team/index`, true)}/> +
submitSucceed(values)} + onFinishFailed={(errors) => submitFailed(errors)} + > + + + + + +
+ + {/* 底部浮动按钮 */} + formRef.current?.submit()}/> + + ); +}; + +export default AddMessage; diff --git a/src/user/chat/message/detail.config.ts b/src/user/chat/message/detail.config.ts new file mode 100644 index 0000000..5072884 --- /dev/null +++ b/src/user/chat/message/detail.config.ts @@ -0,0 +1,4 @@ +export default definePageConfig({ + navigationBarTitleText: '查看消息', + navigationBarTextStyle: 'black' +}) diff --git a/src/user/chat/message/detail.tsx b/src/user/chat/message/detail.tsx new file mode 100644 index 0000000..a7b9c78 --- /dev/null +++ b/src/user/chat/message/detail.tsx @@ -0,0 +1,77 @@ +import {useEffect, useState} from "react"; +import {useRouter} from '@tarojs/taro' +import {CellGroup, Cell, Loading, Avatar} from '@nutui/nutui-react-taro' +import {View,Text} from '@tarojs/components' +import {ArrowRight} from '@nutui/icons-react-taro' +import {getShopChatMessage, updateShopChatMessage} from "@/api/shop/shopChatMessage"; +import {ShopChatMessage} from "@/api/shop/shopChatMessage/model"; +import navTo from "@/utils/common"; + +const AddMessageDetail = () => { + const {params} = useRouter(); + const [loading, setLoading] = useState(true) + const [item, setItem] = useState() + + const reload = () => { + const id = params.id ? Number(params.id) : undefined + if (id) { + getShopChatMessage(id).then(data => { + setItem(data) + setLoading(false) + updateShopChatMessage({ + ...data, + status: 1 + }).then(() => { + console.log('设为已读') + }) + }) + } + } + + useEffect(() => { + reload() + }, []); + + if (loading) { + return 加载中 + } + + return ( + <> + + + + {item.formUserAlias || item.formUserName} + {item.formUserPhone} + +
+ ) : '选择发送对象'} extra={( + + )} + onClick={() => navTo(`/dealer/team/index`, true)}/> + + + + + {/**/} + {/* {'消息内容:'}*/} + {/* {item?.content}*/} + {/* */} + {/*)} />*/} + + + {item?.content} + )} /> + + + ); +}; + +export default AddMessageDetail; diff --git a/src/user/chat/message/index.config.ts b/src/user/chat/message/index.config.ts new file mode 100644 index 0000000..8c6bf6a --- /dev/null +++ b/src/user/chat/message/index.config.ts @@ -0,0 +1,3 @@ +export default definePageConfig({ + navigationBarTitleText: '我的消息' +}) diff --git a/src/user/chat/message/index.tsx b/src/user/chat/message/index.tsx new file mode 100644 index 0000000..479e7a5 --- /dev/null +++ b/src/user/chat/message/index.tsx @@ -0,0 +1,179 @@ +import {useState, useCallback, useEffect} from 'react' +import {View, Text} from '@tarojs/components' +import Taro from '@tarojs/taro' +import {Loading, InfiniteLoading, Empty, Avatar, Badge} from '@nutui/nutui-react-taro' +import FixedButton from "@/components/FixedButton"; +import {ShopChatMessage} from "@/api/shop/shopChatMessage/model"; +import {pageShopChatMessage} from "@/api/shop/shopChatMessage"; +import navTo from "@/utils/common"; + +const MessageIndex = () => { + const [list, setList] = useState([]) + const [loading, setLoading] = useState(false) + const [page, setPage] = useState(1) + const [hasMore, setHasMore] = useState(true) + + // 获取消息数据 + const fetchMessageData = useCallback(async (resetPage = false, targetPage?: number, searchKeyword?: string) => { + setLoading(true); + try { + const currentPage = resetPage ? 1 : (targetPage || page); + + // 构建API参数,根据状态筛选 + const params: any = { + type: 'text', + page: currentPage, + toUserId: Taro.getStorageSync('UserId') + }; + + // 添加搜索关键词 + if (searchKeyword && searchKeyword.trim()) { + params.keywords = searchKeyword.trim(); + } + + const res = await pageShopChatMessage(params); + + if (res?.list && res.list.length > 0) { + // 正确映射状态 + const mappedList = res.list.map(customer => ({ + ...customer + })); + + // 如果是重置页面或第一页,直接设置新数据;否则追加数据 + if (resetPage || currentPage === 1) { + setList(mappedList); + } else { + setList(prevList => prevList.concat(mappedList)); + } + + // 正确判断是否还有更多数据 + const hasMoreData = res.list.length >= 10; // 假设每页10条数据 + setHasMore(hasMoreData); + } else { + if (resetPage || currentPage === 1) { + setList([]); + } + setHasMore(false); + } + + setPage(currentPage); + } catch (error) { + console.error('获取消息数据失败:', error); + Taro.showToast({ + title: '加载失败,请重试', + icon: 'none' + }); + } finally { + setLoading(false); + } + }, [page]); + + const reloadMore = async () => { + if (loading || !hasMore) return; // 防止重复加载 + const nextPage = page + 1; + await fetchMessageData(false, nextPage); + } + + + // 获取列表数据(现在使用服务端搜索,不需要消息端过滤) + const getFilteredList = () => { + return list; + }; + + useEffect(() => { + // 初始化时加载数据 + fetchMessageData(true, 1, ''); + }, []); + + // 渲染消息项 + const renderMessageItem = (item: any) => ( + + navTo(`/user/chat/message/detail?id=${item.id}`,true)}> + + + + + + + + + {item.formUserAlias || item.formUserName} + + {item.createTime} + + + + {item.content} + + + + + + + + ); + + // 渲染消息列表 + const renderMessageList = () => { + const filteredList = getFilteredList(); + + return ( + + { + // 滚动事件处理 + }} + onScrollToUpper={() => { + // 滚动到顶部事件处理 + }} + loadingText={ + <> + 加载中... + + } + loadMoreText={ + filteredList.length === 0 ? ( + + ) : ( + + 没有更多了 + + ) + } + > + {loading && filteredList.length === 0 ? ( + + + 加载中... + + ) : ( + filteredList.map(renderMessageItem) + )} + + + ); + }; + + return ( + + {/* 消息列表 */} + {renderMessageList()} + navTo(`/user/chat/message/add`,true)}/> + + ); +}; + +export default MessageIndex;