feat(user): 新增站内消息功能
- 添加聊天消息相关API和模型定义 - 实现消息列表、消息详情和发送消息页面 - 集成消息功能到首页和团队页面 -优化用户模型,增加别名字段
This commit is contained in:
101
src/api/shop/shopChatConversation/index.ts
Normal file
101
src/api/shop/shopChatConversation/index.ts
Normal file
@@ -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));
|
||||||
|
}
|
||||||
37
src/api/shop/shopChatConversation/model/index.ts
Normal file
37
src/api/shop/shopChatConversation/model/index.ts
Normal file
@@ -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;
|
||||||
|
}
|
||||||
115
src/api/shop/shopChatMessage/index.ts
Normal file
115
src/api/shop/shopChatMessage/index.ts
Normal file
@@ -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));
|
||||||
|
}
|
||||||
63
src/api/shop/shopChatMessage/model/index.ts
Normal file
63
src/api/shop/shopChatMessage/model/index.ts
Normal file
@@ -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 @@
|
|||||||
import type { PageParam } from '@/api/index';
|
import type { PageParam } from '@/api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 优惠券
|
* 优惠券
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ export interface ShopDealerReferee {
|
|||||||
avatar?: string;
|
avatar?: string;
|
||||||
// 用户昵称
|
// 用户昵称
|
||||||
nickname?: string;
|
nickname?: string;
|
||||||
|
// 用户别名
|
||||||
|
alias?: string;
|
||||||
// 用户手机号
|
// 用户手机号
|
||||||
phone?: string;
|
phone?: string;
|
||||||
// 推荐关系层级(1,2,3)
|
// 推荐关系层级(1,2,3)
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ export interface ChatMessage {
|
|||||||
withdraw?: number;
|
withdraw?: number;
|
||||||
// 文件信息
|
// 文件信息
|
||||||
fileInfo?: string;
|
fileInfo?: string;
|
||||||
toUserName?: any;
|
//
|
||||||
|
toUserName?: string;
|
||||||
formUserName?: string;
|
formUserName?: string;
|
||||||
// 批量发送
|
// 批量发送
|
||||||
toUserIds?: any[];
|
toUserIds?: any[];
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export async function pageUsers(params: UserParam) {
|
|||||||
*/
|
*/
|
||||||
export async function listUsers(params?: UserParam) {
|
export async function listUsers(params?: UserParam) {
|
||||||
const res = await request.get<ApiResult<User[]>>(
|
const res = await request.get<ApiResult<User[]>>(
|
||||||
'/system/user',
|
SERVER_API_URL + '/system/user',
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
if (res.code === 0 && res.data) {
|
if (res.code === 0 && res.data) {
|
||||||
@@ -36,7 +36,7 @@ export async function listUsers(params?: UserParam) {
|
|||||||
*/
|
*/
|
||||||
export async function getStaffs(params?: UserParam) {
|
export async function getStaffs(params?: UserParam) {
|
||||||
const res = await request.get<ApiResult<User[]>>(
|
const res = await request.get<ApiResult<User[]>>(
|
||||||
'/system/user',
|
SERVER_API_URL + '/system/user',
|
||||||
{
|
{
|
||||||
params
|
params
|
||||||
}
|
}
|
||||||
@@ -52,7 +52,7 @@ export async function getStaffs(params?: UserParam) {
|
|||||||
*/
|
*/
|
||||||
export async function getCompanyList(params?: UserParam) {
|
export async function getCompanyList(params?: UserParam) {
|
||||||
const res = await request.get<ApiResult<User[]>>(
|
const res = await request.get<ApiResult<User[]>>(
|
||||||
'/system/user',
|
SERVER_API_URL + '/system/user',
|
||||||
{
|
{
|
||||||
params
|
params
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ export async function getCompanyList(params?: UserParam) {
|
|||||||
*/
|
*/
|
||||||
export async function getUser(id: number) {
|
export async function getUser(id: number) {
|
||||||
const res = await request.get<ApiResult<User>>(
|
const res = await request.get<ApiResult<User>>(
|
||||||
'/system/user/' + id,
|
SERVER_API_URL + '/system/user/' + id,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
if (res.code === 0 && res.data) {
|
if (res.code === 0 && res.data) {
|
||||||
@@ -82,7 +82,7 @@ export async function getUser(id: number) {
|
|||||||
*/
|
*/
|
||||||
export async function addUser(data: User) {
|
export async function addUser(data: User) {
|
||||||
const res = await request.post<ApiResult<unknown>>(
|
const res = await request.post<ApiResult<unknown>>(
|
||||||
'/system/user',
|
SERVER_API_URL + '/system/user',
|
||||||
data
|
data
|
||||||
);
|
);
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
@@ -110,7 +110,7 @@ export async function updateUser(data: User) {
|
|||||||
*/
|
*/
|
||||||
export async function removeUser(id?: number) {
|
export async function removeUser(id?: number) {
|
||||||
const res = await request.del<ApiResult<unknown>>(
|
const res = await request.del<ApiResult<unknown>>(
|
||||||
'/system/user/' + id
|
SERVER_API_URL + '/system/user/' + id
|
||||||
);
|
);
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
return res.message;
|
return res.message;
|
||||||
@@ -123,7 +123,7 @@ export async function removeUser(id?: number) {
|
|||||||
*/
|
*/
|
||||||
export async function removeUsers(data: (number | undefined)[]) {
|
export async function removeUsers(data: (number | undefined)[]) {
|
||||||
const res = await request.del<ApiResult<unknown>>(
|
const res = await request.del<ApiResult<unknown>>(
|
||||||
'/system/user/batch',
|
SERVER_API_URL + '/system/user/batch',
|
||||||
{
|
{
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
@@ -139,7 +139,7 @@ export async function removeUsers(data: (number | undefined)[]) {
|
|||||||
*/
|
*/
|
||||||
export async function updateUserStatus(userId?: number, status?: number) {
|
export async function updateUserStatus(userId?: number, status?: number) {
|
||||||
const res = await request.put<ApiResult<unknown>>(
|
const res = await request.put<ApiResult<unknown>>(
|
||||||
'/system/user/status',
|
SERVER_API_URL + '/system/user/status',
|
||||||
{
|
{
|
||||||
userId,
|
userId,
|
||||||
status
|
status
|
||||||
@@ -156,7 +156,7 @@ export async function updateUserStatus(userId?: number, status?: number) {
|
|||||||
*/
|
*/
|
||||||
export async function updateUserRecommend(form:any) {
|
export async function updateUserRecommend(form:any) {
|
||||||
const res = await request.put<ApiResult<unknown>>(
|
const res = await request.put<ApiResult<unknown>>(
|
||||||
'/system/user/recommend',
|
SERVER_API_URL + '/system/user/recommend',
|
||||||
form
|
form
|
||||||
);
|
);
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
@@ -170,7 +170,7 @@ export async function updateUserRecommend(form:any) {
|
|||||||
*/
|
*/
|
||||||
export async function updateUserPassword(userId?: number, password = '123456') {
|
export async function updateUserPassword(userId?: number, password = '123456') {
|
||||||
const res = await request.put<ApiResult<unknown>>(
|
const res = await request.put<ApiResult<unknown>>(
|
||||||
'/system/user/password',
|
SERVER_API_URL + '/system/user/password',
|
||||||
{
|
{
|
||||||
userId,
|
userId,
|
||||||
password
|
password
|
||||||
@@ -189,7 +189,7 @@ export async function importUsers(file: File) {
|
|||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
const res = await request.post<ApiResult<unknown>>(
|
const res = await request.post<ApiResult<unknown>>(
|
||||||
'/system/user/import',
|
SERVER_API_URL + '/system/user/import',
|
||||||
formData
|
formData
|
||||||
);
|
);
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
@@ -207,7 +207,7 @@ export async function checkExistence(
|
|||||||
id?: number
|
id?: number
|
||||||
) {
|
) {
|
||||||
const res = await request.get<ApiResult<unknown>>(
|
const res = await request.get<ApiResult<unknown>>(
|
||||||
'/system/user/existence',
|
SERVER_API_URL + '/system/user/existence',
|
||||||
{
|
{
|
||||||
params: {field, value, id}
|
params: {field, value, id}
|
||||||
}
|
}
|
||||||
@@ -223,7 +223,7 @@ export async function checkExistence(
|
|||||||
*/
|
*/
|
||||||
export async function countUserBalance(params?: UserParam) {
|
export async function countUserBalance(params?: UserParam) {
|
||||||
const res = await request.get<ApiResult<unknown>>(
|
const res = await request.get<ApiResult<unknown>>(
|
||||||
'/system/user/countUserBalance',
|
SERVER_API_URL + '/system/user/countUserBalance',
|
||||||
{
|
{
|
||||||
params
|
params
|
||||||
}
|
}
|
||||||
@@ -241,7 +241,7 @@ export async function countUserBalance(params?: UserParam) {
|
|||||||
*/
|
*/
|
||||||
export async function listAdminsByPhoneAll(params?: UserParam) {
|
export async function listAdminsByPhoneAll(params?: UserParam) {
|
||||||
const res = await request.get<ApiResult<User[]>>(
|
const res = await request.get<ApiResult<User[]>>(
|
||||||
'/system/user/listAdminsByPhoneAll',
|
SERVER_API_URL + '/system/user/listAdminsByPhoneAll',
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
if (res.code === 0 && res.data) {
|
if (res.code === 0 && res.data) {
|
||||||
|
|||||||
@@ -44,7 +44,11 @@ export default defineAppConfig({
|
|||||||
"gift/redeem",
|
"gift/redeem",
|
||||||
"gift/detail",
|
"gift/detail",
|
||||||
"store/verification",
|
"store/verification",
|
||||||
"theme/index"
|
"theme/index",
|
||||||
|
"chat/conversation/index",
|
||||||
|
"chat/message/index",
|
||||||
|
"chat/message/add",
|
||||||
|
"chat/message/detail"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, {useState, useEffect, useCallback} from 'react'
|
import React, {useState, useEffect, useCallback} from 'react'
|
||||||
import {View, Text} from '@tarojs/components'
|
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 {Space, Empty, Avatar, Button} from '@nutui/nutui-react-taro'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import {useDealerUser} from '@/hooks/useDealerUser'
|
import {useDealerUser} from '@/hooks/useDealerUser'
|
||||||
@@ -9,11 +9,13 @@ import {pageShopDealerOrder} from '@/api/shop/shopDealerOrder'
|
|||||||
import type {ShopDealerReferee} from '@/api/shop/shopDealerReferee/model'
|
import type {ShopDealerReferee} from '@/api/shop/shopDealerReferee/model'
|
||||||
import FixedButton from "@/components/FixedButton";
|
import FixedButton from "@/components/FixedButton";
|
||||||
import navTo from "@/utils/common";
|
import navTo from "@/utils/common";
|
||||||
|
import {updateUser} from "@/api/system/user";
|
||||||
|
|
||||||
interface TeamMemberWithStats extends ShopDealerReferee {
|
interface TeamMemberWithStats extends ShopDealerReferee {
|
||||||
name?: string
|
name?: string
|
||||||
avatar?: string
|
avatar?: string
|
||||||
nickname?: string;
|
nickname?: string;
|
||||||
|
alias?: string;
|
||||||
phone?: string;
|
phone?: string;
|
||||||
orderCount?: number
|
orderCount?: number
|
||||||
commission?: string
|
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(() => {
|
useEffect(() => {
|
||||||
if (dealerUser?.userId || dealerId) {
|
if (dealerUser?.userId || dealerId) {
|
||||||
@@ -233,7 +283,7 @@ const DealerTeam: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}, [dealerUser, dealerId, currentDealerName])
|
}, [dealerUser, dealerId, currentDealerName])
|
||||||
|
|
||||||
const renderMemberItem = (member: TeamMemberWithStats) => {
|
const renderMemberItem = (member: TeamMemberWithStats, index: number) => {
|
||||||
// 判断是否可以点击:有下级成员且未达到层级限制
|
// 判断是否可以点击:有下级成员且未达到层级限制
|
||||||
const canClick = member.subMembers && member.subMembers > 0 && levelStack.length < 1
|
const canClick = member.subMembers && member.subMembers > 0 && levelStack.length < 1
|
||||||
// 判断是否显示手机号:只有本级(levelStack.length === 0)才显示
|
// 判断是否显示手机号:只有本级(levelStack.length === 0)才显示
|
||||||
@@ -258,13 +308,27 @@ const DealerTeam: React.FC = () => {
|
|||||||
<View className="flex-1">
|
<View className="flex-1">
|
||||||
<View className="flex items-center justify-between mb-1">
|
<View className="flex items-center justify-between mb-1">
|
||||||
<View className="flex items-center">
|
<View className="flex items-center">
|
||||||
<Text className="font-semibold text-gray-800 mr-2">
|
<Space>
|
||||||
{member.nickname}
|
{member.alias ? <Text className="font-semibold text-blue-700 mr-2">{member.alias}</Text> :
|
||||||
</Text>
|
<Text className="font-semibold text-gray-800 mr-2">{member.nickname}</Text>}
|
||||||
|
{/*别名备注*/}
|
||||||
|
<Edit size={14} className={'text-blue-500 mr-2'} onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
editAlias(member, index)
|
||||||
|
}}/>
|
||||||
|
{/*发送消息*/}
|
||||||
|
<Message size={14} className={'text-orange-500 mr-2'} onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
sendMessage(member)
|
||||||
|
}}/>
|
||||||
|
</Space>
|
||||||
</View>
|
</View>
|
||||||
{/* 显示手机号(仅本级可见) */}
|
{/* 显示手机号(仅本级可见) */}
|
||||||
{showPhone && member.phone && (
|
{showPhone && member.phone && (
|
||||||
<Text className="text-sm text-gray-500">
|
<Text className="text-sm text-gray-500" onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
makePhoneCall(member.phone || '');
|
||||||
|
}}>
|
||||||
{member.phone}
|
{member.phone}
|
||||||
<Phone size={12} className="ml-1 text-green-500"/>
|
<Phone size={12} className="ml-1 text-green-500"/>
|
||||||
</Text>
|
</Text>
|
||||||
@@ -304,6 +368,11 @@ const DealerTeam: React.FC = () => {
|
|||||||
|
|
||||||
const renderOverview = () => (
|
const renderOverview = () => (
|
||||||
<View className="rounded-xl p-4">
|
<View className="rounded-xl p-4">
|
||||||
|
<View
|
||||||
|
className={'bg-white rounded-lg py-2 px-4 mb-3 shadow-sm text-right text-sm font-medium flex justify-between items-center'}>
|
||||||
|
<Text className="text-lg font-semibold">我的团队成员</Text>
|
||||||
|
<Text className={'text-gray-500 '}>成员数:{teamMembers.length}</Text>
|
||||||
|
</View>
|
||||||
{teamMembers.map(renderMemberItem)}
|
{teamMembers.map(renderMemberItem)}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {pageShopDealerWithdraw, addShopDealerWithdraw} from '@/api/shop/shopDeal
|
|||||||
import type {ShopDealerWithdraw} from '@/api/shop/shopDealerWithdraw/model'
|
import type {ShopDealerWithdraw} from '@/api/shop/shopDealerWithdraw/model'
|
||||||
import {ShopDealerBank} from "@/api/shop/shopDealerBank/model";
|
import {ShopDealerBank} from "@/api/shop/shopDealerBank/model";
|
||||||
import {listShopDealerBank} from "@/api/shop/shopDealerBank";
|
import {listShopDealerBank} from "@/api/shop/shopDealerBank";
|
||||||
|
import {listCmsWebsiteField} from "@/api/cms/cmsWebsiteField";
|
||||||
|
|
||||||
interface WithdrawRecordWithDetails extends ShopDealerWithdraw {
|
interface WithdrawRecordWithDetails extends ShopDealerWithdraw {
|
||||||
accountDisplay?: string
|
accountDisplay?: string
|
||||||
@@ -37,6 +38,7 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
const [availableAmount, setAvailableAmount] = useState<string>('0.00')
|
const [availableAmount, setAvailableAmount] = useState<string>('0.00')
|
||||||
const [withdrawRecords, setWithdrawRecords] = useState<WithdrawRecordWithDetails[]>([])
|
const [withdrawRecords, setWithdrawRecords] = useState<WithdrawRecordWithDetails[]>([])
|
||||||
const [withdrawAmount, setWithdrawAmount] = useState<string>('')
|
const [withdrawAmount, setWithdrawAmount] = useState<string>('')
|
||||||
|
const [withdrawValue, setWithdrawValue] = useState<string>('')
|
||||||
|
|
||||||
const {dealerUser} = useDealerUser()
|
const {dealerUser} = useDealerUser()
|
||||||
|
|
||||||
@@ -135,12 +137,22 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
setIsVisible(false)
|
setIsVisible(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fetchCmsField() {
|
||||||
|
listCmsWebsiteField({ name: 'WithdrawValue'}).then(res => {
|
||||||
|
if(res && res.length > 0){
|
||||||
|
const text = res[0].value;
|
||||||
|
setWithdrawValue(text || '')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 初始化加载数据
|
// 初始化加载数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dealerUser?.userId) {
|
if (dealerUser?.userId) {
|
||||||
fetchBalance().then()
|
fetchBalance().then()
|
||||||
fetchWithdrawRecords().then()
|
fetchWithdrawRecords().then()
|
||||||
fetchBanks()
|
fetchBanks()
|
||||||
|
fetchCmsField()
|
||||||
}
|
}
|
||||||
}, [fetchBalance, fetchWithdrawRecords])
|
}, [fetchBalance, fetchWithdrawRecords])
|
||||||
|
|
||||||
@@ -353,6 +365,7 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
<Text className={'text-orange-500 px-2 text-lg'}>¥{calculateExpectedAmount(withdrawAmount)}</Text>
|
<Text className={'text-orange-500 px-2 text-lg'}>¥{calculateExpectedAmount(withdrawAmount)}</Text>
|
||||||
</View>
|
</View>
|
||||||
}/>
|
}/>
|
||||||
|
<Cell title={<Text className={'text-gray-400'}>说明:{withdrawValue}</Text>}/>
|
||||||
</CellGroup>
|
</CellGroup>
|
||||||
|
|
||||||
<View className="mt-6 px-4">
|
<View className="mt-6 px-4">
|
||||||
|
|||||||
@@ -9,9 +9,14 @@ import navTo from "@/utils/common";
|
|||||||
const MyGrid = () => {
|
const MyGrid = () => {
|
||||||
const [list, setList] = useState<CmsNavigation[]>([])
|
const [list, setList] = useState<CmsNavigation[]>([])
|
||||||
const reload = async () => {
|
const reload = async () => {
|
||||||
const menu = await listCmsNavigation({home: 0})
|
// 读取首页菜单
|
||||||
|
const home = await listCmsNavigation({model: 'index'});
|
||||||
|
const homeId = home[0].navigationId;
|
||||||
|
if(homeId){
|
||||||
|
const menu = await listCmsNavigation({home: 0, parentId: homeId})
|
||||||
setList(menu)
|
setList(menu)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reload().then()
|
reload().then()
|
||||||
@@ -24,10 +29,7 @@ const MyGrid = () => {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<View className={'p-4'}
|
<View className={'p-4'}>
|
||||||
style={{
|
|
||||||
marginTop: '5vh'
|
|
||||||
}}>
|
|
||||||
<View className={' bg-white rounded-2xl py-4'}>
|
<View className={' bg-white rounded-2xl py-4'}>
|
||||||
<View className={'title font-medium px-4'}>功能菜单</View>
|
<View className={'title font-medium px-4'}>功能菜单</View>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|||||||
@@ -14,9 +14,10 @@ const Page = () => {
|
|||||||
const reload = async () => {
|
const reload = async () => {
|
||||||
// 读取首页菜单
|
// 读取首页菜单
|
||||||
const home = await listCmsNavigation({model: 'index'});
|
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 || [])
|
setNavItems(menus || [])
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
47
src/pages/index/PopUpAd.tsx
Normal file
47
src/pages/index/PopUpAd.tsx
Normal file
@@ -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<CmsNavigation>()
|
||||||
|
const reload = async () => {
|
||||||
|
const navigation = await getCmsNavigation(4426)
|
||||||
|
if(navigation && navigation.hide == 0){
|
||||||
|
setItem(navigation)
|
||||||
|
setVisible(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
reload().then()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Dialog
|
||||||
|
title={
|
||||||
|
<div className={'font-bold mb-3'}>重要通知</div>
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RichText nodes={item?.design?.content}/>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default PopUpAd
|
||||||
@@ -10,6 +10,7 @@ import Menu from "./Menu";
|
|||||||
import Banner from "./Banner";
|
import Banner from "./Banner";
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
import Grid from "@/pages/index/Grid";
|
import Grid from "@/pages/index/Grid";
|
||||||
|
import PopUpAd from "@/pages/index/PopUpAd";
|
||||||
|
|
||||||
function Home() {
|
function Home() {
|
||||||
// 吸顶状态
|
// 吸顶状态
|
||||||
@@ -117,6 +118,7 @@ function Home() {
|
|||||||
<BestSellers/>
|
<BestSellers/>
|
||||||
<Grid />
|
<Grid />
|
||||||
</View>
|
</View>
|
||||||
|
<PopUpAd />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
3
src/user/chat/conversation/index.config.ts
Normal file
3
src/user/chat/conversation/index.config.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '站内消息'
|
||||||
|
})
|
||||||
167
src/user/chat/conversation/index.tsx
Normal file
167
src/user/chat/conversation/index.tsx
Normal file
@@ -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;
|
||||||
4
src/user/chat/message/add.config.ts
Normal file
4
src/user/chat/message/add.config.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '发送消息',
|
||||||
|
navigationBarTextStyle: 'black'
|
||||||
|
})
|
||||||
135
src/user/chat/message/add.tsx
Normal file
135
src/user/chat/message/add.tsx
Normal file
@@ -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;
|
||||||
4
src/user/chat/message/detail.config.ts
Normal file
4
src/user/chat/message/detail.config.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '查看消息',
|
||||||
|
navigationBarTextStyle: 'black'
|
||||||
|
})
|
||||||
77
src/user/chat/message/detail.tsx
Normal file
77
src/user/chat/message/detail.tsx
Normal file
@@ -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;
|
||||||
3
src/user/chat/message/index.config.ts
Normal file
3
src/user/chat/message/index.config.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '我的消息'
|
||||||
|
})
|
||||||
179
src/user/chat/message/index.tsx
Normal file
179
src/user/chat/message/index.tsx
Normal file
@@ -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;
|
||||||
Reference in New Issue
Block a user