feat(dealer): 优化业务员申请和团队管理功能
-强制用户手动输入昵称,清空默认微信昵称 - 添加昵称验证逻辑,禁止使用默认昵称 - 优化团队数据加载和展示逻辑 - 添加保存二维码到相册功能 - 调整提现金额门槛为100元
This commit is contained in:
@@ -48,6 +48,7 @@ export interface ShopDealerApply {
|
|||||||
export interface ShopDealerApplyParam extends PageParam {
|
export interface ShopDealerApplyParam extends PageParam {
|
||||||
applyId?: number;
|
applyId?: number;
|
||||||
type?: number;
|
type?: number;
|
||||||
|
dealerName?: string;
|
||||||
mobile?: string;
|
mobile?: string;
|
||||||
userId?: number;
|
userId?: number;
|
||||||
keywords?: string;
|
keywords?: string;
|
||||||
|
|||||||
@@ -37,6 +37,14 @@ const AddUserAddress = () => {
|
|||||||
setFormData({
|
setFormData({
|
||||||
...user,
|
...user,
|
||||||
refereeId: Number(inviteParams.inviter),
|
refereeId: Number(inviteParams.inviter),
|
||||||
|
// 清空昵称,强制用户手动输入
|
||||||
|
nickname: '',
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 如果没有邀请参数,也要确保昵称为空
|
||||||
|
setFormData({
|
||||||
|
...user,
|
||||||
|
nickname: '',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,7 +138,9 @@ const AddUserAddress = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!values.realName && !FormData?.nickname) {
|
// 验证昵称:必须填写且不能是默认的微信昵称
|
||||||
|
const nickname = values.realName || FormData?.nickname || '';
|
||||||
|
if (!nickname || nickname.trim() === '') {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '请填写昵称',
|
title: '请填写昵称',
|
||||||
icon: 'error'
|
icon: 'error'
|
||||||
@@ -138,6 +148,25 @@ const AddUserAddress = () => {
|
|||||||
return;
|
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) {
|
if (!values.avatar && !FormData?.avatar) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '请上传头像',
|
title: '请上传头像',
|
||||||
@@ -145,6 +174,7 @@ const AddUserAddress = () => {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
console.log(values,FormData)
|
||||||
|
|
||||||
const roles = await listUserRole({userId: user?.userId})
|
const roles = await listUserRole({userId: user?.userId})
|
||||||
console.log(roles, 'roles...')
|
console.log(roles, 'roles...')
|
||||||
@@ -247,8 +277,11 @@ const AddUserAddress = () => {
|
|||||||
console.log('手机号已获取', userData.phone)
|
console.log('手机号已获取', userData.phone)
|
||||||
const updatedFormData = {
|
const updatedFormData = {
|
||||||
...FormData,
|
...FormData,
|
||||||
...userData,
|
phone: userData.phone,
|
||||||
phone: userData.phone
|
// 不自动填充微信昵称,保持用户已输入的昵称
|
||||||
|
nickname: FormData?.nickname || '',
|
||||||
|
// 只在没有头像时才使用微信头像
|
||||||
|
avatar: FormData?.avatar || userData.avatar
|
||||||
}
|
}
|
||||||
setFormData(updatedFormData)
|
setFormData(updatedFormData)
|
||||||
|
|
||||||
@@ -256,8 +289,9 @@ const AddUserAddress = () => {
|
|||||||
if (formRef.current) {
|
if (formRef.current) {
|
||||||
formRef.current.setFieldsValue({
|
formRef.current.setFieldsValue({
|
||||||
phone: userData.phone,
|
phone: userData.phone,
|
||||||
realName: userData.nickname || FormData?.nickname,
|
// 不覆盖用户已输入的昵称
|
||||||
avatar: userData.avatar || FormData?.avatar
|
realName: FormData?.nickname || '',
|
||||||
|
avatar: FormData?.avatar || userData.avatar
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,11 +407,11 @@ const AddUserAddress = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
}
|
}
|
||||||
<Form.Item name="realName" label="昵称" initialValue={FormData?.nickname} required>
|
<Form.Item name="realName" label="昵称" initialValue="" required>
|
||||||
<Input
|
<Input
|
||||||
type="nickname"
|
type="nickname"
|
||||||
className="info-content__input"
|
className="info-content__input"
|
||||||
placeholder="请输入昵称"
|
placeholder="请获取微信昵称"
|
||||||
value={FormData?.nickname || ''}
|
value={FormData?.nickname || ''}
|
||||||
onInput={(e: InputEvent) => getWxNickname(e.detail.value)}
|
onInput={(e: InputEvent) => getWxNickname(e.detail.value)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
import {useEffect, useState, useRef} from "react";
|
import {useEffect, useState, useRef} from "react";
|
||||||
import {Loading, CellGroup, Cell, Input, Form, Calendar} from '@nutui/nutui-react-taro'
|
import {Loading, CellGroup, Cell, Input, Form, Calendar} from '@nutui/nutui-react-taro'
|
||||||
import {Edit, Calendar as CalendarIcon} from '@nutui/icons-react-taro'
|
import {Edit} from '@nutui/icons-react-taro'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import {useRouter} from '@tarojs/taro'
|
import {useRouter} from '@tarojs/taro'
|
||||||
import {View,Text} 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 {ShopDealerApply} from "@/api/shop/shopDealerApply/model";
|
||||||
import {
|
import {
|
||||||
addShopDealerApply, getShopDealerApply,
|
addShopDealerApply, getShopDealerApply, pageShopDealerApply,
|
||||||
updateShopDealerApply
|
updateShopDealerApply
|
||||||
} from "@/api/shop/shopDealerApply";
|
} from "@/api/shop/shopDealerApply";
|
||||||
import {
|
import {
|
||||||
formatDateForDatabase,
|
formatDateForDatabase,
|
||||||
extractDateForCalendar,
|
extractDateForCalendar
|
||||||
formatDateForDisplay
|
|
||||||
} from "@/utils/dateUtils";
|
} from "@/utils/dateUtils";
|
||||||
|
|
||||||
const AddShopDealerApply = () => {
|
const AddShopDealerApply = () => {
|
||||||
@@ -47,7 +46,6 @@ const AddShopDealerApply = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 处理签约时间选择
|
// 处理签约时间选择
|
||||||
const handleApplyTimeConfirm = (param: string) => {
|
const handleApplyTimeConfirm = (param: string) => {
|
||||||
const selectedDate = param[3] // 选中的日期字符串 (YYYY-M-D)
|
const selectedDate = param[3] // 选中的日期字符串 (YYYY-M-D)
|
||||||
@@ -79,13 +77,13 @@ const AddShopDealerApply = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const reload = async () => {
|
const reload = async () => {
|
||||||
if(!params.id){
|
if (!params.id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 查询当前用户ID是否已有申请记录
|
// 查询当前用户ID是否已有申请记录
|
||||||
try {
|
try {
|
||||||
const dealerApply = await getShopDealerApply(Number(params.id));
|
const dealerApply = await getShopDealerApply(Number(params.id));
|
||||||
if(dealerApply){
|
if (dealerApply) {
|
||||||
setFormData(dealerApply)
|
setFormData(dealerApply)
|
||||||
setIsEditMode(true);
|
setIsEditMode(true);
|
||||||
setExistingApply(dealerApply)
|
setExistingApply(dealerApply)
|
||||||
@@ -109,49 +107,60 @@ const AddShopDealerApply = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 提交表单
|
// 提交表单
|
||||||
const submitSucceed = async (values: any) => {
|
const submitSucceed = (values: any) => {
|
||||||
try {
|
|
||||||
// 准备提交的数据
|
|
||||||
const submitData = {
|
|
||||||
...values,
|
|
||||||
realName: values.realName || user?.nickname,
|
|
||||||
mobile: user?.phone,
|
|
||||||
refereeId: 33534,
|
|
||||||
applyStatus: 10,
|
|
||||||
auditTime: undefined,
|
|
||||||
// 确保日期数据正确提交(使用数据库格式)
|
|
||||||
applyTime: values.applyTime || (applyTime ? formatDateForDatabase(applyTime) : ''),
|
|
||||||
contractTime: values.contractTime || (contractTime ? formatDateForDatabase(contractTime) : '')
|
|
||||||
};
|
|
||||||
|
|
||||||
// 如果是编辑模式,添加现有申请的id
|
pageShopDealerApply({dealerName: values.dealerName, type: 4}).then((res) => {
|
||||||
if (isEditMode && existingApply?.applyId) {
|
if (res && res?.count > 0) {
|
||||||
submitData.applyId = existingApply.applyId;
|
Taro.showToast({
|
||||||
|
title: '该客户已存在',
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
// 准备提交的数据
|
||||||
|
const submitData = {
|
||||||
|
...values,
|
||||||
|
type: 4,
|
||||||
|
realName: values.realName || user?.nickname,
|
||||||
|
mobile: user?.phone,
|
||||||
|
refereeId: 33534,
|
||||||
|
applyStatus: 10,
|
||||||
|
auditTime: undefined,
|
||||||
|
// 确保日期数据正确提交(使用数据库格式)
|
||||||
|
applyTime: values.applyTime || (applyTime ? formatDateForDatabase(applyTime) : ''),
|
||||||
|
contractTime: values.contractTime || (contractTime ? formatDateForDatabase(contractTime) : '')
|
||||||
|
};
|
||||||
|
|
||||||
// 执行新增或更新操作
|
// 如果是编辑模式,添加现有申请的id
|
||||||
if (isEditMode) {
|
if (isEditMode && existingApply?.applyId) {
|
||||||
await updateShopDealerApply(submitData);
|
submitData.applyId = existingApply.applyId;
|
||||||
} else {
|
}
|
||||||
await addShopDealerApply(submitData);
|
|
||||||
|
// 执行新增或更新操作
|
||||||
|
if (isEditMode) {
|
||||||
|
updateShopDealerApply(submitData);
|
||||||
|
} else {
|
||||||
|
addShopDealerApply(submitData);
|
||||||
|
}
|
||||||
|
|
||||||
|
Taro.showToast({
|
||||||
|
title: `${isEditMode ? '提交' : '提交'}成功`,
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
Taro.navigateBack();
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('验证邀请人失败:', error);
|
||||||
|
return Taro.showToast({
|
||||||
|
title: '邀请人ID不存在',
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
})
|
||||||
Taro.showToast({
|
|
||||||
title: `${isEditMode ? '提交' : '提交'}成功`,
|
|
||||||
icon: 'success'
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
Taro.navigateBack();
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('验证邀请人失败:', error);
|
|
||||||
return Taro.showToast({
|
|
||||||
title: '邀请人ID不存在',
|
|
||||||
icon: 'error'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理固定按钮点击事件
|
// 处理固定按钮点击事件
|
||||||
@@ -201,43 +210,43 @@ const AddShopDealerApply = () => {
|
|||||||
<Form.Item name="dealerCode" label="户号" initialValue={FormData?.dealerCode} required>
|
<Form.Item name="dealerCode" label="户号" initialValue={FormData?.dealerCode} required>
|
||||||
<Input placeholder="请填写户号" disabled={isEditMode}/>
|
<Input placeholder="请填写户号" disabled={isEditMode}/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="money" label="签约价格" initialValue={FormData?.money} required>
|
{/*<Form.Item name="money" label="签约价格" initialValue={FormData?.money} required>*/}
|
||||||
<Input placeholder="(元/兆瓦时)" disabled={false}/>
|
{/* <Input placeholder="(元/兆瓦时)" disabled={false}/>*/}
|
||||||
</Form.Item>
|
{/*</Form.Item>*/}
|
||||||
<Form.Item name="applyTime" label="签约时间" initialValue={FormData?.applyTime} required>
|
{/*<Form.Item name="applyTime" label="签约时间" initialValue={FormData?.applyTime} required>*/}
|
||||||
<View
|
{/* <View*/}
|
||||||
className="flex items-center justify-between py-2"
|
{/* className="flex items-center justify-between py-2"*/}
|
||||||
onClick={() => !isEditMode && setShowApplyTimePicker(true)}
|
{/* onClick={() => !isEditMode && setShowApplyTimePicker(true)}*/}
|
||||||
style={{
|
{/* style={{*/}
|
||||||
cursor: isEditMode ? 'not-allowed' : 'pointer',
|
{/* cursor: isEditMode ? 'not-allowed' : 'pointer',*/}
|
||||||
opacity: isEditMode ? 0.6 : 1
|
{/* opacity: isEditMode ? 0.6 : 1*/}
|
||||||
}}
|
{/* }}*/}
|
||||||
>
|
{/* >*/}
|
||||||
<View className="flex items-center">
|
{/* <View className="flex items-center">*/}
|
||||||
<CalendarIcon size={16} color="#999" className="mr-2" />
|
{/* <CalendarIcon size={16} color="#999" className="mr-2" />*/}
|
||||||
<Text style={{ color: applyTime ? '#333' : '#999' }}>
|
{/* <Text style={{ color: applyTime ? '#333' : '#999' }}>*/}
|
||||||
{applyTime ? formatDateForDisplay(applyTime) : '请选择签约时间'}
|
{/* {applyTime ? formatDateForDisplay(applyTime) : '请选择签约时间'}*/}
|
||||||
</Text>
|
{/* </Text>*/}
|
||||||
</View>
|
{/* </View>*/}
|
||||||
</View>
|
{/* </View>*/}
|
||||||
</Form.Item>
|
{/*</Form.Item>*/}
|
||||||
<Form.Item name="contractTime" label="合同日期" initialValue={FormData?.contractTime} required>
|
{/*<Form.Item name="contractTime" label="合同日期" initialValue={FormData?.contractTime} required>*/}
|
||||||
<View
|
{/* <View*/}
|
||||||
className="flex items-center justify-between py-2"
|
{/* className="flex items-center justify-between py-2"*/}
|
||||||
onClick={() => !isEditMode && setShowContractTimePicker(true)}
|
{/* onClick={() => !isEditMode && setShowContractTimePicker(true)}*/}
|
||||||
style={{
|
{/* style={{*/}
|
||||||
cursor: isEditMode ? 'not-allowed' : 'pointer',
|
{/* cursor: isEditMode ? 'not-allowed' : 'pointer',*/}
|
||||||
opacity: isEditMode ? 0.6 : 1
|
{/* opacity: isEditMode ? 0.6 : 1*/}
|
||||||
}}
|
{/* }}*/}
|
||||||
>
|
{/* >*/}
|
||||||
<View className="flex items-center">
|
{/* <View className="flex items-center">*/}
|
||||||
<CalendarIcon size={16} color="#999" className="mr-2" />
|
{/* <CalendarIcon size={16} color="#999" className="mr-2" />*/}
|
||||||
<Text style={{ color: contractTime ? '#333' : '#999' }}>
|
{/* <Text style={{ color: contractTime ? '#333' : '#999' }}>*/}
|
||||||
{contractTime ? formatDateForDisplay(contractTime) : '请选择合同生效起止时间'}
|
{/* {contractTime ? formatDateForDisplay(contractTime) : '请选择合同生效起止时间'}*/}
|
||||||
</Text>
|
{/* </Text>*/}
|
||||||
</View>
|
{/* </View>*/}
|
||||||
</View>
|
{/* </View>*/}
|
||||||
</Form.Item>
|
{/*</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"/>*/}
|
||||||
{/*</Form.Item>*/}
|
{/*</Form.Item>*/}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ const CustomerIndex = () => {
|
|||||||
|
|
||||||
// 构建API参数,根据状态筛选
|
// 构建API参数,根据状态筛选
|
||||||
const params: any = {
|
const params: any = {
|
||||||
type: 0,
|
type: 4,
|
||||||
page: currentPage
|
page: currentPage
|
||||||
};
|
};
|
||||||
const applyStatus = mapCustomerStatusToApplyStatus(statusFilter || activeTab);
|
const applyStatus = mapCustomerStatusToApplyStatus(statusFilter || activeTab);
|
||||||
@@ -126,10 +126,10 @@ const CustomerIndex = () => {
|
|||||||
try {
|
try {
|
||||||
// 并行获取各状态的数量
|
// 并行获取各状态的数量
|
||||||
const [allRes, pendingRes, signedRes, cancelledRes] = await Promise.all([
|
const [allRes, pendingRes, signedRes, cancelledRes] = await Promise.all([
|
||||||
pageShopDealerApply({type: 0}), // 全部
|
pageShopDealerApply({type: 4}), // 全部
|
||||||
pageShopDealerApply({applyStatus: 10, type: 0}), // 跟进中
|
pageShopDealerApply({applyStatus: 10, type: 4}), // 跟进中
|
||||||
pageShopDealerApply({applyStatus: 20, type: 0}), // 已签约
|
pageShopDealerApply({applyStatus: 20, type: 4}), // 已签约
|
||||||
pageShopDealerApply({applyStatus: 30, type: 0}) // 已取消
|
pageShopDealerApply({applyStatus: 30, type: 4}) // 已取消
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setStatusCounts({
|
setStatusCounts({
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
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,Empty, Avatar, Button} from '@nutui/nutui-react-taro'
|
import {Phone} 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'
|
||||||
@@ -41,6 +42,90 @@ const DealerTeam: React.FC = () => {
|
|||||||
// 当前查看的用户名称
|
// 当前查看的用户名称
|
||||||
const [currentDealerName, setCurrentDealerName] = useState<string>('')
|
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
|
||||||
@@ -54,6 +139,7 @@ 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,
|
||||||
@@ -65,60 +151,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,
|
|
||||||
userId: member.userId
|
|
||||||
})
|
|
||||||
|
|
||||||
// 获取下级成员数量
|
// 异步加载每个成员的详细统计数据
|
||||||
const subMembersResult = await listShopDealerReferee({
|
loadMemberStats(processedMembers)
|
||||||
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
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
setTeamMembers(memberStats)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -198,6 +236,10 @@ const DealerTeam: React.FC = () => {
|
|||||||
const renderMemberItem = (member: TeamMemberWithStats) => {
|
const renderMemberItem = (member: TeamMemberWithStats) => {
|
||||||
// 判断是否可以点击:有下级成员且未达到层级限制
|
// 判断是否可以点击:有下级成员且未达到层级限制
|
||||||
const canClick = member.subMembers && member.subMembers > 0 && levelStack.length < 1
|
const canClick = member.subMembers && member.subMembers > 0 && levelStack.length < 1
|
||||||
|
// 判断是否显示手机号:只有本级(levelStack.length === 0)才显示
|
||||||
|
const showPhone = levelStack.length === 0
|
||||||
|
// 判断数据是否还在加载中(初始值都是0或'0.00')
|
||||||
|
const isStatsLoading = member.orderCount === 0 && member.commission === '0.00' && member.subMembers === 0
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
@@ -207,47 +249,56 @@ const DealerTeam: React.FC = () => {
|
|||||||
}`}
|
}`}
|
||||||
onClick={() => getNextUser(member)}
|
onClick={() => getNextUser(member)}
|
||||||
>
|
>
|
||||||
<View className="flex items-center mb-3">
|
<View className="flex items-center mb-3">
|
||||||
<Avatar
|
<Avatar
|
||||||
size="40"
|
size="40"
|
||||||
src={member.avatar}
|
src={member.avatar}
|
||||||
className="mr-3"
|
className="mr-3"
|
||||||
/>
|
/>
|
||||||
<View className="flex-1">
|
<View className="flex-1">
|
||||||
<View className="flex items-center mb-1">
|
<View className="flex items-center justify-between mb-1">
|
||||||
<Text className="font-semibold text-gray-800 mr-2">
|
<View className="flex items-center">
|
||||||
{member.nickname}
|
<Text className="font-semibold text-gray-800 mr-2">
|
||||||
|
{member.nickname}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
{/* 显示手机号(仅本级可见) */}
|
||||||
|
{showPhone && member.phone && (
|
||||||
|
<Text className="text-sm text-gray-500">
|
||||||
|
{member.phone}
|
||||||
|
<Phone size={12} className="ml-1 text-green-500"/>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
<Text className="text-xs text-gray-500">
|
||||||
|
加入时间:{member.joinTime}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text className="text-xs text-gray-500">
|
</View>
|
||||||
加入时间:{member.joinTime}
|
|
||||||
</Text>
|
<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>
|
</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">
|
|
||||||
{member.orderCount}
|
|
||||||
</Text>
|
|
||||||
</Space>
|
|
||||||
<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 ${
|
|
||||||
canClick ? 'text-purple-600' : 'text-gray-400'
|
|
||||||
}`}>
|
|
||||||
{member.subMembers || 0}
|
|
||||||
</Text>
|
|
||||||
</Space>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,7 +338,7 @@ const DealerTeam: React.FC = () => {
|
|||||||
<Space className="flex items-center justify-center">
|
<Space className="flex items-center justify-center">
|
||||||
<Empty description="您还不是业务人员" style={{
|
<Empty description="您还不是业务人员" style={{
|
||||||
backgroundColor: 'transparent'
|
backgroundColor: 'transparent'
|
||||||
}} actions={[{ text: '立即申请', onClick: () => navTo(`/dealer/apply/add`,true)}]}
|
}} actions={[{text: '立即申请', onClick: () => navTo(`/dealer/apply/add`, true)}]}
|
||||||
/>
|
/>
|
||||||
</Space>
|
</Space>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {useEffect, useState} from 'react'
|
import {useEffect, useState} from 'react'
|
||||||
import {View, Text, Image} from '@tarojs/components'
|
import {View, Text, Image} from '@tarojs/components'
|
||||||
import {Tabs} from '@nutui/nutui-react-taro'
|
import {Tabs} from '@nutui/nutui-react-taro'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
import {listCmsWebsiteField} from "@/api/cms/cmsWebsiteField";
|
import {listCmsWebsiteField} from "@/api/cms/cmsWebsiteField";
|
||||||
import {CmsWebsiteField} from "@/api/cms/cmsWebsiteField/model";
|
import {CmsWebsiteField} from "@/api/cms/cmsWebsiteField/model";
|
||||||
@@ -9,15 +10,74 @@ const WechatService = () => {
|
|||||||
const [activeTab, setActiveTab] = useState('0')
|
const [activeTab, setActiveTab] = useState('0')
|
||||||
const [codes, setCodes] = useState<CmsWebsiteField[]>([])
|
const [codes, setCodes] = useState<CmsWebsiteField[]>([])
|
||||||
|
|
||||||
|
// 长按保存二维码到相册
|
||||||
|
const saveQRCodeToAlbum = (imageUrl: string) => {
|
||||||
|
// 首先下载图片到本地
|
||||||
|
Taro.downloadFile({
|
||||||
|
url: imageUrl,
|
||||||
|
success: (res) => {
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
// 保存图片到相册
|
||||||
|
Taro.saveImageToPhotosAlbum({
|
||||||
|
filePath: res.tempFilePath,
|
||||||
|
success: () => {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '保存成功',
|
||||||
|
icon: 'success',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
console.error('保存失败:', error)
|
||||||
|
if (error.errMsg.includes('auth deny')) {
|
||||||
|
Taro.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '需要您授权保存图片到相册',
|
||||||
|
showCancel: true,
|
||||||
|
cancelText: '取消',
|
||||||
|
confirmText: '去设置',
|
||||||
|
success: (modalRes) => {
|
||||||
|
if (modalRes.confirm) {
|
||||||
|
Taro.openSetting()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '保存失败',
|
||||||
|
icon: 'error',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '图片下载失败',
|
||||||
|
icon: 'error',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '图片下载失败',
|
||||||
|
icon: 'error',
|
||||||
|
duration: 2000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const renderQRCode = (data: typeof codes[0]) => (
|
const renderQRCode = (data: typeof codes[0]) => (
|
||||||
<View className="qr-container">
|
<View className="qr-container">
|
||||||
|
|
||||||
<View className="qr-content">
|
<View className="qr-content">
|
||||||
<View className="qr-code-wrapper">
|
<View className="qr-code-wrapper">
|
||||||
<Image
|
<Image
|
||||||
src={`${data.value}`}
|
src={`${data.value}`}
|
||||||
className="qr-code-image"
|
className="qr-code-image"
|
||||||
mode="aspectFit"
|
mode="aspectFit"
|
||||||
|
onLongPress={() => saveQRCodeToAlbum(`${data.value}`)}
|
||||||
/>
|
/>
|
||||||
{data.style && <Text className="wechat-id">联系电话:{data.style}</Text>}
|
{data.style && <Text className="wechat-id">联系电话:{data.style}</Text>}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -204,9 +204,9 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (amount < 10) {
|
if (amount < 100) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '最低提现金额为10元',
|
title: '最低提现金额为100元',
|
||||||
icon: 'error'
|
icon: 'error'
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
@@ -316,7 +316,7 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
borderTop: '1px solid rgba(255, 255, 255, 0.3)'
|
borderTop: '1px solid rgba(255, 255, 255, 0.3)'
|
||||||
}}>
|
}}>
|
||||||
<Text className="text-white text-opacity-80 text-xs">
|
<Text className="text-white text-opacity-80 text-xs">
|
||||||
最低提现金额:¥10
|
最低提现金额:¥100
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -193,6 +193,18 @@ const Header = (props: any) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isLoggedIn && user) {
|
if (isLoggedIn && user) {
|
||||||
console.log('用户信息已更新:', user);
|
console.log('用户信息已更新:', user);
|
||||||
|
// 检查是否设置头像和昵称
|
||||||
|
if(user.nickname === '微信用户'){
|
||||||
|
Taro.showToast({
|
||||||
|
title: '请设置头像和昵称',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
Taro.navigateTo({
|
||||||
|
url: '/user/profile/profile'
|
||||||
|
});
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [user, isLoggedIn])
|
}, [user, isLoggedIn])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user