Browse Source
feat(shop): 新增聊天会话与消息模块API新增了聊天会话(ShopChatConversation)和聊天消息(ShopChatMessage)两个模块的完整API接口及数据模型,包括分页查询、列表查询、新增、修改、删除、批量删除及根据ID查询等功能。feat(system): 扩展用户模型并重构API调用方式 为用户模型添加推荐人ID字段(refereeId),并在用户相关API中引入SERVER_API_URL常量以统一管理接口前缀,优化调用结构。 feat(dealer): 优化经销商邀请注册流程将经销商申请页面调整为邀请注册模式,增强微信手机号获取、头像上传及昵称校验逻辑,完善邀请关系绑定机制,并更新页面标题提示信息。 ```master
21 changed files with 1624 additions and 243 deletions
@ -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<ApiResult<PageResult<ShopChatConversation>>>( |
|||
'/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<ApiResult<ShopChatConversation[]>>( |
|||
'/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<ApiResult<unknown>>( |
|||
'/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<ApiResult<unknown>>( |
|||
'/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<ApiResult<unknown>>( |
|||
'/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<ApiResult<unknown>>( |
|||
'/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<ApiResult<ShopChatConversation>>( |
|||
'/shop/shop-chat-conversation/' + id |
|||
); |
|||
if (res.code === 0 && res.data) { |
|||
return res.data; |
|||
} |
|||
return Promise.reject(new Error(res.message)); |
|||
} |
@ -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; |
|||
} |
@ -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<ApiResult<PageResult<ShopChatMessage>>>( |
|||
'/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<ApiResult<ShopChatMessage[]>>( |
|||
'/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<ApiResult<unknown>>( |
|||
'/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<ApiResult<unknown>>( |
|||
'/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<ApiResult<unknown>>( |
|||
'/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<ApiResult<unknown>>( |
|||
'/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<ApiResult<unknown>>( |
|||
'/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<ApiResult<ShopChatMessage>>( |
|||
'/shop/shop-chat-message/' + id |
|||
); |
|||
if (res.code === 0 && res.data) { |
|||
return res.data; |
|||
} |
|||
return Promise.reject(new Error(res.message)); |
|||
} |
@ -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; |
|||
} |
@ -1,4 +1,4 @@ |
|||
export default definePageConfig({ |
|||
navigationBarTitleText: '注册成为VIP', |
|||
navigationBarTitleText: '邀请注册', |
|||
navigationBarTextStyle: 'black' |
|||
}) |
|||
|
@ -1,3 +1,3 @@ |
|||
export default definePageConfig({ |
|||
navigationBarTitleText: '我的团队' |
|||
navigationBarTitleText: '邀请推广' |
|||
}) |
|||
|
@ -0,0 +1,3 @@ |
|||
export default definePageConfig({ |
|||
navigationBarTitleText: '站内消息' |
|||
}) |
@ -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<any[]>([]) |
|||
const [loading, setLoading] = useState<boolean>(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) => ( |
|||
<View key={customer.userId} className="bg-white rounded-lg p-4 mb-3 shadow-sm"> |
|||
<View className="flex items-center"> |
|||
<View className="flex-1"> |
|||
<View className="flex items-center justify-between mb-1"> |
|||
<Text className="font-semibold text-gray-800 mr-2"> |
|||
关于XXXX的通知 |
|||
</Text> |
|||
<Tag type={'warning'}>未读</Tag> |
|||
{/*<Tag type={'success'}>已读</Tag>*/} |
|||
</View> |
|||
<Space direction={'vertical'}> |
|||
{/*<Text className="text-xs text-gray-500">统一代码:{customer.dealerCode}</Text>*/} |
|||
<Text className="text-xs text-gray-500"> |
|||
创建时间:{customer.createTime} |
|||
</Text> |
|||
</Space> |
|||
</View> |
|||
</View> |
|||
</View> |
|||
); |
|||
|
|||
// 渲染消息列表
|
|||
const renderMessageList = () => { |
|||
const filteredList = getFilteredList(); |
|||
|
|||
return ( |
|||
<View className="p-4" style={{ |
|||
height: '90vh', |
|||
overflowY: 'auto', |
|||
overflowX: 'hidden' |
|||
}}> |
|||
<InfiniteLoading |
|||
target="scroll" |
|||
hasMore={hasMore} |
|||
onLoadMore={reloadMore} |
|||
onScroll={() => { |
|||
// 滚动事件处理
|
|||
}} |
|||
onScrollToUpper={() => { |
|||
// 滚动到顶部事件处理
|
|||
}} |
|||
loadingText={ |
|||
<> |
|||
加载中... |
|||
</> |
|||
} |
|||
loadMoreText={ |
|||
filteredList.length === 0 ? ( |
|||
<Empty |
|||
style={{backgroundColor: 'transparent'}} |
|||
description={loading ? "加载中..." : "暂无消息数据"} |
|||
/> |
|||
) : ( |
|||
<View className={'h-12 flex items-center justify-center'}> |
|||
<Text className="text-gray-500 text-sm">没有更多了</Text> |
|||
</View> |
|||
) |
|||
} |
|||
> |
|||
{loading && filteredList.length === 0 ? ( |
|||
<View className="flex items-center justify-center py-8"> |
|||
<Loading/> |
|||
<Text className="text-gray-500 mt-2 ml-2">加载中...</Text> |
|||
</View> |
|||
) : ( |
|||
filteredList.map(renderMessageItem) |
|||
)} |
|||
</InfiniteLoading> |
|||
</View> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<View className="min-h-screen bg-gray-50"> |
|||
{/* 消息列表 */} |
|||
{renderMessageList()} |
|||
<FixedButton /> |
|||
</View> |
|||
); |
|||
}; |
|||
|
|||
export default Index; |
@ -0,0 +1,4 @@ |
|||
export default definePageConfig({ |
|||
navigationBarTitleText: '发送消息', |
|||
navigationBarTextStyle: 'black' |
|||
}) |
@ -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<User>() |
|||
const [loading, setLoading] = useState<boolean>(true) |
|||
const [FormData, _] = useState<ShopChatMessage>() |
|||
const formRef = useRef<any>(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 <Loading className={'px-2'}>加载中</Loading> |
|||
} |
|||
|
|||
return ( |
|||
<> |
|||
<Cell title={toUser ? ( |
|||
<View className={'flex items-center'}> |
|||
<Avatar src={toUser.avatar}/> |
|||
<View className={'ml-2 flex flex-col'}> |
|||
<Text>{toUser.alias || toUser.nickname}</Text> |
|||
<Text className={'text-gray-300'}>{toUser.mobile}</Text> |
|||
</View> |
|||
</View> |
|||
) : '选择发送对象'} extra={( |
|||
<ArrowRight color="#cccccc" className={toUser ? 'mt-2' : ''} size={toUser ? 20 : 18}/> |
|||
)} |
|||
onClick={() => navTo(`/dealer/team/index`, true)}/> |
|||
<Form |
|||
ref={formRef} |
|||
divider |
|||
initialValues={FormData} |
|||
labelPosition="left" |
|||
onFinish={(values) => submitSucceed(values)} |
|||
onFinishFailed={(errors) => submitFailed(errors)} |
|||
> |
|||
<CellGroup style={{padding: '4px 0'}}> |
|||
<Form.Item name="content" initialValue={FormData?.content} required> |
|||
<Input placeholder="填写消息内容" maxLength={300}/> |
|||
</Form.Item> |
|||
</CellGroup> |
|||
</Form> |
|||
|
|||
{/* 底部浮动按钮 */} |
|||
<FixedButton text={isEditMode ? '立即发送' : '立即发送'} onClick={() => formRef.current?.submit()}/> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
export default AddMessage; |
@ -0,0 +1,4 @@ |
|||
export default definePageConfig({ |
|||
navigationBarTitleText: '查看消息', |
|||
navigationBarTextStyle: 'black' |
|||
}) |
@ -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<boolean>(true) |
|||
const [item, setItem] = useState<ShopChatMessage>() |
|||
|
|||
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 <Loading className={'px-2'}>加载中</Loading> |
|||
} |
|||
|
|||
return ( |
|||
<> |
|||
<Cell style={{ |
|||
display: 'none' |
|||
}} title={item?.formUserId ? ( |
|||
<View className={'flex items-center'}> |
|||
<Avatar src={item.formUserAvatar}/> |
|||
<View className={'ml-2 flex flex-col'}> |
|||
<Text>{item.formUserAlias || item.formUserName}</Text> |
|||
<Text className={'text-gray-300'}>{item.formUserPhone}</Text> |
|||
</View> |
|||
</View> |
|||
) : '选择发送对象'} extra={( |
|||
<ArrowRight color="#cccccc" className={item ? 'mt-2' : ''} size={item ? 20 : 18}/> |
|||
)} |
|||
onClick={() => navTo(`/dealer/team/index`, true)}/> |
|||
<CellGroup> |
|||
<Cell title={'发布人'} extra={item?.formUserAlias || item?.formUserName}/> |
|||
<Cell title={'创建时间'} extra={item?.createTime}/> |
|||
<Cell title={'状态'} extra={ |
|||
item?.status === 0 ? '未读' : '已读' |
|||
}/> |
|||
{/*<Cell title={(*/} |
|||
{/* <>*/} |
|||
{/* <Text>{'消息内容:'}</Text>*/} |
|||
{/* <Text>{item?.content}</Text>*/} |
|||
{/* </>*/} |
|||
{/*)} />*/} |
|||
</CellGroup> |
|||
<CellGroup> |
|||
<Cell title={( |
|||
<Text>{item?.content}</Text> |
|||
)} /> |
|||
</CellGroup> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
export default AddMessageDetail; |
@ -0,0 +1,3 @@ |
|||
export default definePageConfig({ |
|||
navigationBarTitleText: '我的消息' |
|||
}) |
@ -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<ShopChatMessage[]>([]) |
|||
const [loading, setLoading] = useState<boolean>(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) => ( |
|||
<View key={item.userId} className="bg-white rounded-lg p-4 mb-3 shadow-sm"> |
|||
<View className="flex items-center" onClick={() => navTo(`/user/chat/message/detail?id=${item.id}`,true)}> |
|||
<View className="flex-1"> |
|||
<View className="flex items-center justify-between mb-1"> |
|||
<View className={'flex w-full'}> |
|||
<Badge style={{marginInlineEnd: '10px'}} dot={item.status === 0} top="2" right="4"> |
|||
<Avatar |
|||
size="40" |
|||
src={item.formUserAvatar} |
|||
/> |
|||
</Badge> |
|||
<View className="flex flex-col w-full"> |
|||
<View className="flex items-center w-full justify-between"> |
|||
<Text className="font-semibold text-gray-800 mr-2">{item.formUserAlias || item.formUserName}</Text> |
|||
<Text className="text-xs text-gray-500"> |
|||
{item.createTime} |
|||
</Text> |
|||
</View> |
|||
<Text className="text-gray-500 mt-2 mr-2"> |
|||
{item.content} |
|||
</Text> |
|||
</View> |
|||
</View> |
|||
</View> |
|||
</View> |
|||
</View> |
|||
</View> |
|||
); |
|||
|
|||
// 渲染消息列表
|
|||
const renderMessageList = () => { |
|||
const filteredList = getFilteredList(); |
|||
|
|||
return ( |
|||
<View className="p-4" style={{ |
|||
height: '90vh', |
|||
overflowY: 'auto', |
|||
overflowX: 'hidden' |
|||
}}> |
|||
<InfiniteLoading |
|||
target="scroll" |
|||
hasMore={hasMore} |
|||
onLoadMore={reloadMore} |
|||
onScroll={() => { |
|||
// 滚动事件处理
|
|||
}} |
|||
onScrollToUpper={() => { |
|||
// 滚动到顶部事件处理
|
|||
}} |
|||
loadingText={ |
|||
<> |
|||
加载中... |
|||
</> |
|||
} |
|||
loadMoreText={ |
|||
filteredList.length === 0 ? ( |
|||
<Empty |
|||
style={{backgroundColor: 'transparent'}} |
|||
description={loading ? "加载中..." : "暂无消息数据"} |
|||
/> |
|||
) : ( |
|||
<View className={'h-12 flex items-center justify-center'}> |
|||
<Text className="text-gray-500 text-sm">没有更多了</Text> |
|||
</View> |
|||
) |
|||
} |
|||
> |
|||
{loading && filteredList.length === 0 ? ( |
|||
<View className="flex items-center justify-center py-8"> |
|||
<Loading/> |
|||
<Text className="text-gray-500 mt-2 ml-2">加载中...</Text> |
|||
</View> |
|||
) : ( |
|||
filteredList.map(renderMessageItem) |
|||
)} |
|||
</InfiniteLoading> |
|||
</View> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<View className="min-h-screen bg-gray-50"> |
|||
{/* 消息列表 */} |
|||
{renderMessageList()} |
|||
<FixedButton text={'发送消息'} onClick={() => navTo(`/user/chat/message/add`,true)}/> |
|||
</View> |
|||
); |
|||
}; |
|||
|
|||
export default MessageIndex; |
Loading…
Reference in new issue