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({ |
export default definePageConfig({ |
||||
navigationBarTitleText: '注册成为VIP', |
|
||||
|
navigationBarTitleText: '邀请注册', |
||||
navigationBarTextStyle: 'black' |
navigationBarTextStyle: 'black' |
||||
}) |
}) |
||||
|
@ -1,3 +1,3 @@ |
|||||
export default definePageConfig({ |
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