```
feat(shop): 新增聊天会话与消息模块API新增了聊天会话(ShopChatConversation)和聊天消息(ShopChatMessage)两个模块的完整API接口及数据模型,包括分页查询、列表查询、新增、修改、删除、批量删除及根据ID查询等功能。feat(system): 扩展用户模型并重构API调用方式 为用户模型添加推荐人ID字段(refereeId),并在用户相关API中引入SERVER_API_URL常量以统一管理接口前缀,优化调用结构。 feat(dealer): 优化经销商邀请注册流程将经销商申请页面调整为邀请注册模式,增强微信手机号获取、头像上传及昵称校验逻辑,完善邀请关系绑定机制,并更新页面标题提示信息。 ```
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;
|
||||||
|
}
|
||||||
@@ -27,4 +27,5 @@ export interface ShopDealerRefereeParam extends PageParam {
|
|||||||
id?: number;
|
id?: number;
|
||||||
dealerId?: number;
|
dealerId?: number;
|
||||||
keywords?: string;
|
keywords?: string;
|
||||||
|
deleted?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import request from '@/utils/request';
|
import request from '@/utils/request';
|
||||||
import type {ApiResult, PageResult} from '@/api/index';
|
import type {ApiResult, PageResult} from '@/api';
|
||||||
import type {User, UserParam} from './model';
|
import type {User, UserParam} from './model';
|
||||||
import {SERVER_API_URL} from "@/utils/server";
|
import {SERVER_API_URL} from "@/utils/server";
|
||||||
|
|
||||||
@@ -8,8 +8,8 @@ import {SERVER_API_URL} from "@/utils/server";
|
|||||||
*/
|
*/
|
||||||
export async function pageUsers(params: UserParam) {
|
export async function pageUsers(params: UserParam) {
|
||||||
const res = await request.get<ApiResult<PageResult<User>>>(
|
const res = await request.get<ApiResult<PageResult<User>>>(
|
||||||
'/system/user/page',
|
SERVER_API_URL + '/system/user/page',
|
||||||
{params}
|
params
|
||||||
);
|
);
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
return res.data;
|
return res.data;
|
||||||
@@ -22,10 +22,8 @@ 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) {
|
||||||
return res.data;
|
return res.data;
|
||||||
@@ -38,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
|
||||||
}
|
}
|
||||||
@@ -54,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
|
||||||
}
|
}
|
||||||
@@ -70,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) {
|
||||||
@@ -84,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) {
|
||||||
@@ -112,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;
|
||||||
@@ -125,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
|
||||||
}
|
}
|
||||||
@@ -141,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,9 +154,9 @@ export async function updateUserStatus(userId?: number, status?: number) {
|
|||||||
/**
|
/**
|
||||||
* 修改推荐状态
|
* 修改推荐状态
|
||||||
*/
|
*/
|
||||||
export async function updateUserRecommend(form) {
|
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) {
|
||||||
@@ -172,7 +170,7 @@ export async function updateUserRecommend(form) {
|
|||||||
*/
|
*/
|
||||||
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
|
||||||
@@ -191,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) {
|
||||||
@@ -209,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}
|
||||||
}
|
}
|
||||||
@@ -225,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
|
||||||
}
|
}
|
||||||
@@ -243,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) {
|
||||||
|
|||||||
@@ -128,6 +128,8 @@ export interface User {
|
|||||||
certification?: boolean;
|
certification?: boolean;
|
||||||
// 实名认证类型
|
// 实名认证类型
|
||||||
certificationType?: number;
|
certificationType?: number;
|
||||||
|
// 推荐人ID
|
||||||
|
refereeId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -60,6 +60,10 @@ export default {
|
|||||||
"store/verification",
|
"store/verification",
|
||||||
"theme/index",
|
"theme/index",
|
||||||
"poster/poster",
|
"poster/poster",
|
||||||
|
"chat/conversation/index",
|
||||||
|
"chat/message/index",
|
||||||
|
"chat/message/add",
|
||||||
|
"chat/message/detail"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export default definePageConfig({
|
export default definePageConfig({
|
||||||
navigationBarTitleText: '注册成为VIP',
|
navigationBarTitleText: '邀请注册',
|
||||||
navigationBarTextStyle: 'black'
|
navigationBarTextStyle: 'black'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,96 +1,210 @@
|
|||||||
import {useEffect, useState, useRef} from "react";
|
import {useEffect, useState, useRef} from "react";
|
||||||
import {Loading, CellGroup, Cell, Input, Form} from '@nutui/nutui-react-taro'
|
import {Loading, CellGroup, Input, Form, Avatar, Button, Space} from '@nutui/nutui-react-taro'
|
||||||
import {Edit} from '@nutui/icons-react-taro'
|
import {Edit} from '@nutui/icons-react-taro'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import {View} from '@tarojs/components'
|
import {View} from '@tarojs/components'
|
||||||
import FixedButton from "@/components/FixedButton";
|
import FixedButton from "@/components/FixedButton";
|
||||||
import {useUser} from "@/hooks/useUser";
|
import {useUser} from "@/hooks/useUser";
|
||||||
import {ShopDealerApply} from "@/api/shop/shopDealerApply/model";
|
import {TenantId} from "@/config/app";
|
||||||
import {
|
import {updateUser} from "@/api/system/user";
|
||||||
addShopDealerApply,
|
import {User} from "@/api/system/user/model";
|
||||||
pageShopDealerApply,
|
import {getStoredInviteParams, handleInviteRelation} from "@/utils/invite";
|
||||||
updateShopDealerApply
|
import {addShopDealerUser} from "@/api/shop/shopDealerUser";
|
||||||
} from "@/api/shop/shopDealerApply";
|
import {listUserRole, updateUserRole} from "@/api/system/userRole";
|
||||||
import {getShopDealerUser} from "@/api/shop/shopDealerUser";
|
|
||||||
|
// 类型定义
|
||||||
|
interface ChooseAvatarEvent {
|
||||||
|
detail: {
|
||||||
|
avatarUrl: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InputEvent {
|
||||||
|
detail: {
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const AddUserAddress = () => {
|
const AddUserAddress = () => {
|
||||||
const {user} = useUser()
|
const {user, loginUser} = useUser()
|
||||||
const [loading, setLoading] = useState<boolean>(true)
|
const [loading, setLoading] = useState<boolean>(true)
|
||||||
const [FormData, setFormData] = useState<ShopDealerApply>()
|
const [FormData, setFormData] = useState<User>()
|
||||||
const formRef = useRef<any>(null)
|
const formRef = useRef<any>(null)
|
||||||
const [isEditMode, setIsEditMode] = useState<boolean>(false)
|
|
||||||
const [existingApply, setExistingApply] = useState<ShopDealerApply | null>(null)
|
|
||||||
|
|
||||||
// 获取审核状态文字
|
const reload = async () => {
|
||||||
const getApplyStatusText = (status?: number) => {
|
const inviteParams = getStoredInviteParams()
|
||||||
switch (status) {
|
if (inviteParams?.inviter) {
|
||||||
case 10:
|
setFormData({
|
||||||
return '待审核'
|
...user,
|
||||||
case 20:
|
refereeId: Number(inviteParams.inviter),
|
||||||
return '审核通过'
|
// 清空昵称,强制用户手动输入
|
||||||
case 30:
|
nickname: '',
|
||||||
return '驳回'
|
})
|
||||||
default:
|
} else {
|
||||||
return '未知状态'
|
// 如果没有邀请参数,也要确保昵称为空
|
||||||
|
setFormData({
|
||||||
|
...user,
|
||||||
|
nickname: '',
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const reload = async () => {
|
|
||||||
// 判断用户是否登录
|
const uploadAvatar = ({detail}: ChooseAvatarEvent) => {
|
||||||
if (!user?.userId) {
|
// 先更新本地显示的头像(临时显示)
|
||||||
return false;
|
const tempFormData = {
|
||||||
|
...FormData,
|
||||||
|
avatar: `${detail.avatarUrl}`,
|
||||||
}
|
}
|
||||||
// 查询当前用户ID是否已有申请记录
|
setFormData(tempFormData)
|
||||||
try {
|
|
||||||
const res = await pageShopDealerApply({userId: user?.userId});
|
Taro.uploadFile({
|
||||||
if (res && res.count > 0) {
|
url: 'https://server.websoft.top/api/oss/upload',
|
||||||
setIsEditMode(true);
|
filePath: detail.avatarUrl,
|
||||||
setExistingApply(res.list[0]);
|
name: 'file',
|
||||||
// 如果有记录,填充表单数据
|
header: {
|
||||||
setFormData(res.list[0]);
|
'content-type': 'application/json',
|
||||||
setLoading(false)
|
TenantId
|
||||||
} else {
|
},
|
||||||
setIsEditMode(false);
|
success: async (res) => {
|
||||||
setExistingApply(null);
|
const data = JSON.parse(res.data);
|
||||||
setLoading(false)
|
if (data.code === 0) {
|
||||||
|
const finalAvatarUrl = `${data.data.thumbnail}`
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用 useUser hook 的 updateUser 方法更新头像
|
||||||
|
await updateUser({
|
||||||
|
avatar: finalAvatarUrl
|
||||||
|
})
|
||||||
|
|
||||||
|
Taro.showToast({
|
||||||
|
title: '头像上传成功',
|
||||||
|
icon: 'success',
|
||||||
|
duration: 1500
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('更新用户头像失败:', error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无论用户信息更新是否成功,都要更新本地FormData
|
||||||
|
const finalFormData = {
|
||||||
|
...tempFormData,
|
||||||
|
avatar: finalAvatarUrl
|
||||||
|
}
|
||||||
|
setFormData(finalFormData)
|
||||||
|
|
||||||
|
// 同步更新表单字段
|
||||||
|
if (formRef.current) {
|
||||||
|
formRef.current.setFieldsValue({
|
||||||
|
avatar: finalAvatarUrl
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 上传失败,恢复原来的头像
|
||||||
|
setFormData({
|
||||||
|
...FormData,
|
||||||
|
avatar: user?.avatar || ''
|
||||||
|
})
|
||||||
|
Taro.showToast({
|
||||||
|
title: '上传失败',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
console.error('上传头像失败:', error)
|
||||||
|
Taro.showToast({
|
||||||
|
title: '上传失败',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
// 恢复原来的头像
|
||||||
|
setFormData({
|
||||||
|
...FormData,
|
||||||
|
avatar: user?.avatar || ''
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} catch (error) {
|
})
|
||||||
setLoading(true)
|
|
||||||
console.error('查询申请记录失败:', error);
|
|
||||||
setIsEditMode(false);
|
|
||||||
setExistingApply(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交表单
|
// 提交表单
|
||||||
const submitSucceed = async (values: any) => {
|
const submitSucceed = async (values: any) => {
|
||||||
try {
|
try {
|
||||||
|
// 验证必填字段
|
||||||
|
if (!values.phone && !FormData?.phone) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '请先获取手机号',
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证昵称:必须填写且不能是默认的微信昵称
|
||||||
|
const nickname = values.realName || FormData?.nickname || '';
|
||||||
|
if (!nickname || nickname.trim() === '') {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '请填写昵称',
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否为默认的微信昵称(常见的默认昵称)
|
||||||
|
const defaultNicknames = ['微信用户', 'WeChat User', '微信昵称'];
|
||||||
|
if (defaultNicknames.includes(nickname.trim())) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '请填写真实昵称,不能使用默认昵称',
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证昵称长度
|
||||||
|
if (nickname.trim().length < 2) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '昵称至少需要2个字符',
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!values.avatar && !FormData?.avatar) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '请上传头像',
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(values,FormData)
|
||||||
|
|
||||||
|
const roles = await listUserRole({userId: user?.userId})
|
||||||
|
console.log(roles, 'roles...')
|
||||||
|
|
||||||
// 准备提交的数据
|
// 准备提交的数据
|
||||||
const submitData = {
|
await updateUser({
|
||||||
...values,
|
userId: user?.userId,
|
||||||
realName: values.realName || user?.nickname,
|
nickname: values.realName || FormData?.nickname,
|
||||||
mobile: user?.phone,
|
phone: values.phone || FormData?.phone,
|
||||||
refereeId: values.refereeId || FormData?.refereeId,
|
avatar: values.avatar || FormData?.avatar,
|
||||||
applyStatus: 10,
|
refereeId: values.refereeId || FormData?.refereeId
|
||||||
auditTime: undefined
|
});
|
||||||
};
|
|
||||||
await getShopDealerUser(submitData.refereeId);
|
|
||||||
|
|
||||||
// 如果是编辑模式,添加现有申请的id
|
await addShopDealerUser({
|
||||||
if (isEditMode && existingApply?.applyId) {
|
userId: user?.userId,
|
||||||
submitData.applyId = existingApply.applyId;
|
realName: values.realName || FormData?.nickname,
|
||||||
|
mobile: values.phone || FormData?.phone,
|
||||||
|
refereeId: values.refereeId || FormData?.refereeId
|
||||||
|
})
|
||||||
|
|
||||||
|
if (roles.length > 0) {
|
||||||
|
await updateUserRole({
|
||||||
|
...roles[0],
|
||||||
|
roleId: 1848
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行新增或更新操作
|
|
||||||
if (isEditMode) {
|
|
||||||
await updateShopDealerApply(submitData);
|
|
||||||
} else {
|
|
||||||
await addShopDealerApply(submitData);
|
|
||||||
}
|
|
||||||
|
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: `${isEditMode ? '提交' : '提交'}成功`,
|
title: `注册成功`,
|
||||||
icon: 'success'
|
icon: 'success'
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -100,13 +214,130 @@ const AddUserAddress = () => {
|
|||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('验证邀请人失败:', error);
|
console.error('验证邀请人失败:', error);
|
||||||
return Taro.showToast({
|
|
||||||
title: '邀请人ID不存在',
|
|
||||||
icon: 'error'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取微信昵称
|
||||||
|
const getWxNickname = (nickname: string) => {
|
||||||
|
// 更新表单数据
|
||||||
|
const updatedFormData = {
|
||||||
|
...FormData,
|
||||||
|
nickname: nickname
|
||||||
|
}
|
||||||
|
setFormData(updatedFormData);
|
||||||
|
|
||||||
|
// 同步更新表单字段
|
||||||
|
if (formRef.current) {
|
||||||
|
formRef.current.setFieldsValue({
|
||||||
|
realName: nickname
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 获取用户手机号 */
|
||||||
|
const handleGetPhoneNumber = ({detail}: { detail: { code?: string, encryptedData?: string, iv?: string } }) => {
|
||||||
|
const {code, encryptedData, iv} = detail
|
||||||
|
Taro.login({
|
||||||
|
success: (loginRes) => {
|
||||||
|
if (code) {
|
||||||
|
Taro.request({
|
||||||
|
url: 'https://server.websoft.top/api/wx-login/loginByMpWxPhone',
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
authCode: loginRes.code,
|
||||||
|
code,
|
||||||
|
encryptedData,
|
||||||
|
iv,
|
||||||
|
notVerifyPhone: true,
|
||||||
|
refereeId: 0,
|
||||||
|
sceneType: 'save_referee',
|
||||||
|
tenantId: TenantId
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
'content-type': 'application/json',
|
||||||
|
TenantId
|
||||||
|
},
|
||||||
|
success: async function (res) {
|
||||||
|
if (res.data.code == 1) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: res.data.message,
|
||||||
|
icon: 'error',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 登录成功
|
||||||
|
const token = res.data.data.access_token;
|
||||||
|
const userData = res.data.data.user;
|
||||||
|
console.log(userData, 'userData...')
|
||||||
|
// 使用useUser Hook的loginUser方法更新状态
|
||||||
|
loginUser(token, userData);
|
||||||
|
|
||||||
|
if (userData.phone) {
|
||||||
|
console.log('手机号已获取', userData.phone)
|
||||||
|
const updatedFormData = {
|
||||||
|
...FormData,
|
||||||
|
phone: userData.phone,
|
||||||
|
// 不自动填充微信昵称,保持用户已输入的昵称
|
||||||
|
nickname: FormData?.nickname || '',
|
||||||
|
// 只在没有头像时才使用微信头像
|
||||||
|
avatar: FormData?.avatar || userData.avatar
|
||||||
|
}
|
||||||
|
setFormData(updatedFormData)
|
||||||
|
|
||||||
|
// 更新表单字段值
|
||||||
|
if (formRef.current) {
|
||||||
|
formRef.current.setFieldsValue({
|
||||||
|
phone: userData.phone,
|
||||||
|
// 不覆盖用户已输入的昵称
|
||||||
|
realName: FormData?.nickname || '',
|
||||||
|
avatar: FormData?.avatar || userData.avatar
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Taro.showToast({
|
||||||
|
title: '手机号获取成功',
|
||||||
|
icon: 'success',
|
||||||
|
duration: 1500
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 处理邀请关系
|
||||||
|
if (userData?.userId) {
|
||||||
|
try {
|
||||||
|
const inviteSuccess = await handleInviteRelation(userData.userId)
|
||||||
|
if (inviteSuccess) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '邀请关系建立成功',
|
||||||
|
icon: 'success',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('处理邀请关系失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示登录成功提示
|
||||||
|
// Taro.showToast({
|
||||||
|
// title: '注册成功',
|
||||||
|
// icon: 'success',
|
||||||
|
// duration: 1500
|
||||||
|
// })
|
||||||
|
|
||||||
|
// 不需要重新启动小程序,状态已经通过useUser更新
|
||||||
|
// 可以选择性地刷新当前页面数据
|
||||||
|
// await reload();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.log('登录失败!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 处理固定按钮点击事件
|
// 处理固定按钮点击事件
|
||||||
const handleFixedButtonClick = () => {
|
const handleFixedButtonClick = () => {
|
||||||
// 触发表单提交
|
// 触发表单提交
|
||||||
@@ -123,6 +354,18 @@ const AddUserAddress = () => {
|
|||||||
})
|
})
|
||||||
}, [user?.userId]); // 依赖用户ID,当用户变化时重新加载
|
}, [user?.userId]); // 依赖用户ID,当用户变化时重新加载
|
||||||
|
|
||||||
|
// 当FormData变化时,同步更新表单字段值
|
||||||
|
useEffect(() => {
|
||||||
|
if (formRef.current && FormData) {
|
||||||
|
formRef.current.setFieldsValue({
|
||||||
|
refereeId: FormData.refereeId,
|
||||||
|
phone: FormData.phone,
|
||||||
|
avatar: FormData.avatar,
|
||||||
|
realName: FormData.nickname
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [FormData]);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <Loading className={'px-2'}>加载中</Loading>
|
return <Loading className={'px-2'}>加载中</Loading>
|
||||||
}
|
}
|
||||||
@@ -139,50 +382,49 @@ const AddUserAddress = () => {
|
|||||||
>
|
>
|
||||||
<View className={'bg-gray-100 h-3'}></View>
|
<View className={'bg-gray-100 h-3'}></View>
|
||||||
<CellGroup style={{padding: '4px 0'}}>
|
<CellGroup style={{padding: '4px 0'}}>
|
||||||
<Form.Item name="realName" label="名称" initialValue={user?.nickname} required>
|
|
||||||
<Input placeholder="经销商名称" maxLength={10}/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="mobile" label="手机号" initialValue={user?.mobile} required>
|
|
||||||
<Input placeholder="请输入手机号" disabled={true} maxLength={11}/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="refereeId" label="邀请人ID" initialValue={FormData?.refereeId} required>
|
<Form.Item name="refereeId" label="邀请人ID" initialValue={FormData?.refereeId} required>
|
||||||
<Input placeholder="邀请人ID"/>
|
<Input placeholder="邀请人ID" disabled={true}/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="phone" label="手机号" initialValue={FormData?.phone} required>
|
||||||
|
<View className="flex items-center justify-between">
|
||||||
|
<Input
|
||||||
|
placeholder="请填写手机号"
|
||||||
|
disabled={true}
|
||||||
|
maxLength={11}
|
||||||
|
value={FormData?.phone || ''}
|
||||||
|
/>
|
||||||
|
<Button style={{color: '#ffffff'}} open-type="getPhoneNumber" onGetPhoneNumber={handleGetPhoneNumber}>
|
||||||
|
<Space>
|
||||||
|
<Button size="small">点击获取</Button>
|
||||||
|
</Space>
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</Form.Item>
|
||||||
|
{
|
||||||
|
FormData?.phone && <Form.Item name="avatar" label="头像" initialValue={FormData?.avatar} required>
|
||||||
|
<Button open-type="chooseAvatar" style={{height: '58px'}} onChooseAvatar={uploadAvatar}>
|
||||||
|
<Avatar src={FormData?.avatar || user?.avatar} size="54"/>
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
}
|
||||||
|
<Form.Item name="realName" label="昵称" initialValue="" required>
|
||||||
|
<Input
|
||||||
|
type="nickname"
|
||||||
|
className="info-content__input"
|
||||||
|
placeholder="请获取微信昵称"
|
||||||
|
value={FormData?.nickname || ''}
|
||||||
|
onInput={(e: InputEvent) => getWxNickname(e.detail.value)}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</CellGroup>
|
</CellGroup>
|
||||||
</Form>
|
</Form>
|
||||||
{/* 审核状态显示(仅在编辑模式下显示) */}
|
|
||||||
{isEditMode && (
|
|
||||||
<CellGroup>
|
|
||||||
<Cell
|
|
||||||
title={'审核状态'}
|
|
||||||
extra={
|
|
||||||
<span style={{
|
|
||||||
color: FormData?.applyStatus === 20 ? '#52c41a' :
|
|
||||||
FormData?.applyStatus === 30 ? '#ff4d4f' : '#faad14'
|
|
||||||
}}>
|
|
||||||
{getApplyStatusText(FormData?.applyStatus)}
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{FormData?.applyStatus === 20 && (
|
|
||||||
<Cell title={'审核时间'} extra={FormData?.auditTime || '无'}/>
|
|
||||||
)}
|
|
||||||
{FormData?.applyStatus === 30 && (
|
|
||||||
<Cell title={'驳回原因'} extra={FormData?.rejectReason || '无'}/>
|
|
||||||
)}
|
|
||||||
</CellGroup>
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
|
||||||
{/* 底部浮动按钮 */}
|
{/* 底部浮动按钮 */}
|
||||||
{(!isEditMode || FormData?.applyStatus === 10 || FormData?.applyStatus === 30) && (
|
<FixedButton
|
||||||
<FixedButton
|
icon={<Edit/>}
|
||||||
icon={<Edit/>}
|
text={'立即注册'}
|
||||||
text={isEditMode ? '保存修改' : '提交申请'}
|
onClick={handleFixedButtonClick}
|
||||||
disabled={FormData?.applyStatus === 10}
|
/>
|
||||||
onClick={handleFixedButtonClick}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export default definePageConfig({
|
export default definePageConfig({
|
||||||
navigationBarTitleText: '我的团队'
|
navigationBarTitleText: '邀请推广'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,33 +1,139 @@
|
|||||||
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 {Space, Avatar, Loading} from '@nutui/nutui-react-taro'
|
import {Phone, Edit, Message} from '@nutui/icons-react-taro'
|
||||||
import {User} from '@nutui/icons-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'
|
||||||
import {listShopDealerReferee} from '@/api/shop/shopDealerReferee'
|
import {listShopDealerReferee} from '@/api/shop/shopDealerReferee'
|
||||||
import {pageShopDealerOrder} from '@/api/shop/shopDealerOrder'
|
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 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;
|
||||||
|
alias?: string;
|
||||||
|
phone?: string;
|
||||||
orderCount?: number
|
orderCount?: number
|
||||||
commission?: string
|
commission?: string
|
||||||
status?: 'active' | 'inactive'
|
status?: 'active' | 'inactive'
|
||||||
subMembers?: number
|
subMembers?: number
|
||||||
joinTime?: string
|
joinTime?: string
|
||||||
|
dealerAvatar?: string;
|
||||||
|
dealerName?: string;
|
||||||
|
dealerPhone?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 层级信息接口
|
||||||
|
interface LevelInfo {
|
||||||
|
dealerId: number
|
||||||
|
dealerName?: string
|
||||||
|
level: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const DealerTeam: React.FC = () => {
|
const DealerTeam: React.FC = () => {
|
||||||
const [teamMembers, setTeamMembers] = useState<TeamMemberWithStats[]>([])
|
const [teamMembers, setTeamMembers] = useState<TeamMemberWithStats[]>([])
|
||||||
const {dealerUser} = useDealerUser()
|
const {dealerUser} = useDealerUser()
|
||||||
const [dealerId, setDealerId] = useState<number>()
|
const [dealerId, setDealerId] = useState<number>()
|
||||||
|
// 层级栈,用于支持返回上一层
|
||||||
|
const [levelStack, setLevelStack] = useState<LevelInfo[]>([])
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
// 当前查看的用户名称
|
||||||
|
const [currentDealerName, setCurrentDealerName] = useState<string>('')
|
||||||
|
|
||||||
|
// 异步加载成员统计数据
|
||||||
|
const loadMemberStats = async (members: TeamMemberWithStats[]) => {
|
||||||
|
// 分批处理,避免过多并发请求
|
||||||
|
const batchSize = 3
|
||||||
|
for (let i = 0; i < members.length; i += batchSize) {
|
||||||
|
const batch = members.slice(i, i + batchSize)
|
||||||
|
|
||||||
|
const batchStats = await Promise.all(
|
||||||
|
batch.map(async (member) => {
|
||||||
|
try {
|
||||||
|
// 并行获取订单统计和下级成员数量
|
||||||
|
const [orderResult, subMembersResult] = await Promise.all([
|
||||||
|
pageShopDealerOrder({
|
||||||
|
page: 1,
|
||||||
|
userId: member.userId
|
||||||
|
}),
|
||||||
|
listShopDealerReferee({
|
||||||
|
dealerId: member.userId,
|
||||||
|
deleted: 0
|
||||||
|
})
|
||||||
|
])
|
||||||
|
|
||||||
|
let orderCount = 0
|
||||||
|
let commission = '0.00'
|
||||||
|
let status: 'active' | 'inactive' = 'inactive'
|
||||||
|
|
||||||
|
if (orderResult?.list) {
|
||||||
|
const orders = orderResult.list
|
||||||
|
orderCount = orders.length
|
||||||
|
commission = orders.reduce((sum, order) => {
|
||||||
|
const levelCommission = member.level === 1 ? order.firstMoney :
|
||||||
|
member.level === 2 ? order.secondMoney :
|
||||||
|
order.thirdMoney
|
||||||
|
return sum + parseFloat(levelCommission || '0')
|
||||||
|
}, 0).toFixed(2)
|
||||||
|
|
||||||
|
// 判断活跃状态(30天内有订单为活跃)
|
||||||
|
const thirtyDaysAgo = new Date()
|
||||||
|
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30)
|
||||||
|
const hasRecentOrder = orders.some(order =>
|
||||||
|
new Date(order.createTime || '') > thirtyDaysAgo
|
||||||
|
)
|
||||||
|
status = hasRecentOrder ? 'active' : 'inactive'
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...member,
|
||||||
|
orderCount,
|
||||||
|
commission,
|
||||||
|
status,
|
||||||
|
subMembers: subMembersResult?.length || 0
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`获取成员${member.userId}数据失败:`, error)
|
||||||
|
return {
|
||||||
|
...member,
|
||||||
|
orderCount: 0,
|
||||||
|
commission: '0.00',
|
||||||
|
status: 'inactive' as const,
|
||||||
|
subMembers: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
// 更新这一批成员的数据
|
||||||
|
setTeamMembers(prevMembers => {
|
||||||
|
const updatedMembers = [...prevMembers]
|
||||||
|
batchStats.forEach(updatedMember => {
|
||||||
|
const index = updatedMembers.findIndex(m => m.userId === updatedMember.userId)
|
||||||
|
if (index !== -1) {
|
||||||
|
updatedMembers[index] = updatedMember
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return updatedMembers
|
||||||
|
})
|
||||||
|
|
||||||
|
// 添加小延迟,避免请求过于密集
|
||||||
|
if (i + batchSize < members.length) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取团队数据
|
// 获取团队数据
|
||||||
const fetchTeamData = useCallback(async () => {
|
const fetchTeamData = useCallback(async () => {
|
||||||
if (!dealerUser?.userId && !dealerId) return
|
if (!dealerUser?.userId && !dealerId) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
setLoading(true)
|
||||||
console.log(dealerId, 'dealerId>>>>>>>>>')
|
console.log(dealerId, 'dealerId>>>>>>>>>')
|
||||||
// 获取团队成员关系
|
// 获取团队成员关系
|
||||||
const refereeResult = await listShopDealerReferee({
|
const refereeResult = await listShopDealerReferee({
|
||||||
@@ -35,11 +141,11 @@ const DealerTeam: React.FC = () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (refereeResult) {
|
if (refereeResult) {
|
||||||
|
console.log('团队成员原始数据:', refereeResult)
|
||||||
// 处理团队成员数据
|
// 处理团队成员数据
|
||||||
const processedMembers: TeamMemberWithStats[] = refereeResult.map(member => ({
|
const processedMembers: TeamMemberWithStats[] = refereeResult.map(member => ({
|
||||||
...member,
|
...member,
|
||||||
name: `用户${member.userId}`,
|
name: `${member.userId}`,
|
||||||
avatar: '',
|
|
||||||
orderCount: 0,
|
orderCount: 0,
|
||||||
commission: '0.00',
|
commission: '0.00',
|
||||||
status: 'active' as const,
|
status: 'active' as const,
|
||||||
@@ -47,49 +153,12 @@ const DealerTeam: React.FC = () => {
|
|||||||
joinTime: member.createTime
|
joinTime: member.createTime
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// 并行获取每个成员的订单统计
|
// 先显示基础数据,然后异步加载详细统计
|
||||||
const memberStats = await Promise.all(
|
setTeamMembers(processedMembers)
|
||||||
processedMembers.map(async (member) => {
|
setLoading(false)
|
||||||
try {
|
|
||||||
const orderResult = await pageShopDealerOrder({
|
|
||||||
page: 1,
|
|
||||||
limit: 100,
|
|
||||||
userId: member.userId
|
|
||||||
})
|
|
||||||
|
|
||||||
if (orderResult?.list) {
|
// 异步加载每个成员的详细统计数据
|
||||||
const orders = orderResult.list
|
loadMemberStats(processedMembers)
|
||||||
const orderCount = orders.length
|
|
||||||
const commission = orders.reduce((sum, order) => {
|
|
||||||
const levelCommission = member.level === 1 ? order.firstMoney :
|
|
||||||
member.level === 2 ? order.secondMoney :
|
|
||||||
order.thirdMoney
|
|
||||||
return sum + parseFloat(levelCommission || '0')
|
|
||||||
}, 0).toFixed(2)
|
|
||||||
|
|
||||||
// 判断活跃状态(30天内有订单为活跃)
|
|
||||||
const thirtyDaysAgo = new Date()
|
|
||||||
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30)
|
|
||||||
const hasRecentOrder = orders.some(order =>
|
|
||||||
new Date(order.createTime || '') > thirtyDaysAgo
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
...member,
|
|
||||||
orderCount,
|
|
||||||
commission,
|
|
||||||
status: hasRecentOrder ? 'active' as const : 'inactive' as const
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return member
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`获取成员${member.userId}订单失败:`, error)
|
|
||||||
return member
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
setTeamMembers(memberStats)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -98,12 +167,106 @@ const DealerTeam: React.FC = () => {
|
|||||||
title: '获取团队数据失败',
|
title: '获取团队数据失败',
|
||||||
icon: 'error'
|
icon: 'error'
|
||||||
})
|
})
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}, [dealerUser?.userId, dealerId])
|
}, [dealerUser?.userId, dealerId])
|
||||||
|
|
||||||
|
// 查看下级成员
|
||||||
const getNextUser = (item: TeamMemberWithStats) => {
|
const getNextUser = (item: TeamMemberWithStats) => {
|
||||||
|
// 检查层级限制:最多只能查看2层(levelStack.length >= 1 表示已经是第2层了)
|
||||||
|
if (levelStack.length >= 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有下级成员,不允许点击
|
||||||
|
if (!item.subMembers || item.subMembers === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
console.log('点击用户:', item.userId, item.name)
|
console.log('点击用户:', item.userId, item.name)
|
||||||
|
|
||||||
|
// 将当前层级信息推入栈中
|
||||||
|
const currentLevel: LevelInfo = {
|
||||||
|
dealerId: dealerId || dealerUser?.userId || 0,
|
||||||
|
dealerName: currentDealerName || (dealerId ? '上级' : dealerUser?.realName || '我'),
|
||||||
|
level: levelStack.length
|
||||||
|
}
|
||||||
|
setLevelStack(prev => [...prev, currentLevel])
|
||||||
|
|
||||||
|
// 切换到下级
|
||||||
setDealerId(item.userId)
|
setDealerId(item.userId)
|
||||||
|
setCurrentDealerName(item.nickname || item.dealerName || `用户${item.userId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回上一层
|
||||||
|
const goBack = () => {
|
||||||
|
if (levelStack.length === 0) {
|
||||||
|
// 如果栈为空,返回首页或上一页
|
||||||
|
Taro.navigateBack()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从栈中弹出上一层信息
|
||||||
|
const prevLevel = levelStack[levelStack.length - 1]
|
||||||
|
setLevelStack(prev => prev.slice(0, -1))
|
||||||
|
|
||||||
|
if (prevLevel.dealerId === (dealerUser?.userId || 0)) {
|
||||||
|
// 返回到根层级
|
||||||
|
setDealerId(undefined)
|
||||||
|
setCurrentDealerName('')
|
||||||
|
} else {
|
||||||
|
setDealerId(prevLevel.dealerId)
|
||||||
|
setCurrentDealerName(prevLevel.dealerName || '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 一键拨打
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听数据变化,获取团队数据
|
// 监听数据变化,获取团队数据
|
||||||
@@ -113,81 +276,164 @@ const DealerTeam: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}, [fetchTeamData])
|
}, [fetchTeamData])
|
||||||
|
|
||||||
const renderMemberItem = (member: TeamMemberWithStats) => (
|
// 初始化当前用户名称
|
||||||
<View key={member.id} className="bg-white rounded-lg p-4 mb-3 shadow-sm" onClick={() => getNextUser(member)}>
|
useEffect(() => {
|
||||||
<View className="flex items-center mb-3">
|
if (!dealerId && dealerUser?.realName && !currentDealerName) {
|
||||||
<Avatar
|
setCurrentDealerName(dealerUser.realName)
|
||||||
size="40"
|
}
|
||||||
src={member.avatar}
|
}, [dealerUser, dealerId, currentDealerName])
|
||||||
icon={<User/>}
|
|
||||||
className="mr-3"
|
|
||||||
/>
|
|
||||||
<View className="flex-1">
|
|
||||||
<View className="flex items-center mb-1">
|
|
||||||
<Text className="font-semibold text-gray-800 mr-2">
|
|
||||||
{member.name}
|
|
||||||
</Text>
|
|
||||||
{/*{getLevelIcon(Number(member.level))}*/}
|
|
||||||
{/*<Text className="text-xs text-gray-500 ml-1">*/}
|
|
||||||
{/* {member.level}级*/}
|
|
||||||
{/*</Text>*/}
|
|
||||||
</View>
|
|
||||||
<Text className="text-xs text-gray-500">
|
|
||||||
加入时间:{member.joinTime}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
{/*<View className="text-right">*/}
|
|
||||||
{/* <Tag*/}
|
|
||||||
{/* type={member.status === 'active' ? 'success' : 'default'}*/}
|
|
||||||
{/* >*/}
|
|
||||||
{/* {member.status === 'active' ? '活跃' : '沉默'}*/}
|
|
||||||
{/* </Tag>*/}
|
|
||||||
{/*</View>*/}
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<View className="grid grid-cols-3 gap-4 text-center">
|
const renderMemberItem = (member: TeamMemberWithStats, index: number) => {
|
||||||
<Space>
|
// 判断是否可以点击:有下级成员且未达到层级限制
|
||||||
<Text className="text-xs text-gray-500">订单数</Text>
|
const canClick = member.subMembers && member.subMembers > 0 && levelStack.length < 1
|
||||||
<Text className="text-sm font-semibold text-blue-600">
|
// 判断是否显示手机号:只有本级(levelStack.length === 0)才显示
|
||||||
{member.orderCount}
|
const showPhone = levelStack.length === 0
|
||||||
</Text>
|
// 判断数据是否还在加载中(初始值都是0或'0.00')
|
||||||
</Space>
|
const isStatsLoading = member.orderCount === 0 && member.commission === '0.00' && member.subMembers === 0
|
||||||
<Space>
|
|
||||||
<Text className="text-xs text-gray-500">贡献佣金</Text>
|
|
||||||
<Text className="text-sm font-semibold text-green-600">
|
|
||||||
¥{member.commission}
|
|
||||||
</Text>
|
|
||||||
</Space>
|
|
||||||
<Space>
|
|
||||||
<Text className="text-xs text-gray-500">团队成员</Text>
|
|
||||||
<Text className="text-sm font-semibold text-purple-600">
|
|
||||||
{member.subMembers}
|
|
||||||
</Text>
|
|
||||||
</Space>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
|
|
||||||
const renderOverview = () => (
|
|
||||||
<View className="rounded-xl p-4">
|
|
||||||
{teamMembers.slice(0, 3).map(renderMemberItem)}
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!dealerUser) {
|
|
||||||
return (
|
return (
|
||||||
<View className="bg-gray-50 min-h-screen flex items-center justify-center">
|
<View
|
||||||
<Loading/>
|
key={member.id}
|
||||||
<Text className="text-gray-500 mt-2">加载中...</Text>
|
className={`bg-white rounded-lg p-4 mb-3 shadow-sm ${
|
||||||
|
canClick ? 'cursor-pointer' : 'cursor-default opacity-75'
|
||||||
|
}`}
|
||||||
|
onClick={() => getNextUser(member)}
|
||||||
|
>
|
||||||
|
<View className="flex items-center mb-3">
|
||||||
|
<Avatar
|
||||||
|
size="40"
|
||||||
|
src={member.avatar}
|
||||||
|
className="mr-3"
|
||||||
|
/>
|
||||||
|
<View className="flex-1">
|
||||||
|
<View className="flex items-center justify-between mb-1">
|
||||||
|
<View className="flex items-center">
|
||||||
|
<Space>
|
||||||
|
{member.alias ? <Text className="font-semibold text-blue-700 mr-2">{member.alias}</Text> :
|
||||||
|
<Text className="font-semibold text-gray-800 mr-2">{member.nickname}</Text>}
|
||||||
|
{/*别名备注*/}
|
||||||
|
<Edit size={16} className={'text-blue-500 mr-2'} onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
editAlias(member, index)
|
||||||
|
}}/>
|
||||||
|
{/*发送消息*/}
|
||||||
|
<Message size={16} className={'text-orange-500 mr-2'} onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
sendMessage(member)
|
||||||
|
}}/>
|
||||||
|
</Space>
|
||||||
|
</View>
|
||||||
|
{/* 显示手机号(仅本级可见) */}
|
||||||
|
{showPhone && member.phone && (
|
||||||
|
<Text className="text-sm text-gray-500" onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
makePhoneCall(member.phone || '');
|
||||||
|
}}>
|
||||||
|
{member.phone}
|
||||||
|
<Phone size={12} className="ml-1 text-green-500"/>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
<Text className="text-xs text-gray-500">
|
||||||
|
加入时间:{member.joinTime}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="grid grid-cols-3 gap-4 text-center">
|
||||||
|
<Space>
|
||||||
|
<Text className="text-xs text-gray-500">订单数</Text>
|
||||||
|
<Text className="text-sm font-semibold text-blue-600">
|
||||||
|
{isStatsLoading ? '-' : member.orderCount}
|
||||||
|
</Text>
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<Text className="text-xs text-gray-500">贡献佣金</Text>
|
||||||
|
<Text className="text-sm font-semibold text-green-600">
|
||||||
|
{isStatsLoading ? '-' : `¥${member.commission}`}
|
||||||
|
</Text>
|
||||||
|
</Space>
|
||||||
|
<Space>
|
||||||
|
<Text className="text-xs text-gray-500">团队成员</Text>
|
||||||
|
<Text className={`text-sm font-semibold ${
|
||||||
|
canClick ? 'text-purple-600' : 'text-gray-400'
|
||||||
|
}`}>
|
||||||
|
{isStatsLoading ? '-' : (member.subMembers || 0)}
|
||||||
|
</Text>
|
||||||
|
</Space>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const renderOverview = () => (
|
||||||
<View className="min-h-screen">
|
<View className="rounded-xl p-4">
|
||||||
{renderOverview()}
|
<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)}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 渲染顶部导航栏
|
||||||
|
const renderHeader = () => {
|
||||||
|
if (levelStack.length === 0) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="bg-white p-4 mb-3 shadow-sm">
|
||||||
|
<View className="flex items-center justify-between">
|
||||||
|
<View className="flex items-center">
|
||||||
|
<Text className="text-lg font-semibold">
|
||||||
|
{currentDealerName}的团队成员
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
onClick={goBack}
|
||||||
|
className="bg-blue-500"
|
||||||
|
>
|
||||||
|
返回上一层
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dealerUser) {
|
||||||
|
return (
|
||||||
|
<Space className="flex items-center justify-center">
|
||||||
|
<Empty description="您还不是业务人员" style={{
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
}} actions={[{text: '立即申请', onClick: () => navTo(`/dealer/apply/add`, true)}]}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{renderHeader()}
|
||||||
|
|
||||||
|
{loading ? (
|
||||||
|
<View className="flex items-center justify-center mt-20">
|
||||||
|
<Text className="text-gray-500">加载中...</Text>
|
||||||
|
</View>
|
||||||
|
) : teamMembers.length > 0 ? (
|
||||||
|
renderOverview()
|
||||||
|
) : (
|
||||||
|
<View className="flex items-center justify-center mt-20">
|
||||||
|
<Empty description="暂无成员" style={{
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
}}/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<FixedButton text={'立即邀请'} onClick={() => navTo(`/dealer/qrcode/index`, true)}/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DealerTeam
|
export default DealerTeam;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
Location,
|
Location,
|
||||||
Tips,
|
Tips,
|
||||||
Ask,
|
Ask,
|
||||||
Dongdong,
|
// Dongdong,
|
||||||
People,
|
People,
|
||||||
// AfterSaleService,
|
// AfterSaleService,
|
||||||
Logout,
|
Logout,
|
||||||
@@ -95,13 +95,13 @@ const UserCell = () => {
|
|||||||
</View>
|
</View>
|
||||||
</Grid.Item>
|
</Grid.Item>
|
||||||
|
|
||||||
<Grid.Item text={'我的邀请码'} onClick={() => navTo('/dealer/qrcode/index', true)}>
|
{/*<Grid.Item text={'我的邀请码'} onClick={() => navTo('/dealer/qrcode/index', true)}>*/}
|
||||||
<View className="text-center">
|
{/* <View className="text-center">*/}
|
||||||
<View className="w-12 h-12 bg-orange-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
{/* <View className="w-12 h-12 bg-orange-50 rounded-xl flex items-center justify-center mx-auto mb-2">*/}
|
||||||
<Dongdong color="#f59e0b" size="20"/>
|
{/* <Dongdong color="#f59e0b" size="20"/>*/}
|
||||||
</View>
|
{/* </View>*/}
|
||||||
</View>
|
{/* </View>*/}
|
||||||
</Grid.Item>
|
{/*</Grid.Item>*/}
|
||||||
|
|
||||||
{/*<Grid.Item text={'管理中心'} onClick={() => navTo('/admin/index', true)}>*/}
|
{/*<Grid.Item text={'管理中心'} onClick={() => navTo('/admin/index', true)}>*/}
|
||||||
{/* <View className="text-center">*/}
|
{/* <View className="text-center">*/}
|
||||||
|
|||||||
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