From 86516a833479b788745afc453a0b66495a0b46b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com>
Date: Fri, 12 Sep 2025 13:09:41 +0800
Subject: [PATCH] =?UTF-8?q?feat(dealer):=20=E4=BC=98=E5=8C=96=E4=B8=9A?=
=?UTF-8?q?=E5=8A=A1=E5=91=98=E7=94=B3=E8=AF=B7=E5=92=8C=E5=9B=A2=E9=98=9F?=
=?UTF-8?q?=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
-强制用户手动输入昵称,清空默认微信昵称
- 添加昵称验证逻辑,禁止使用默认昵称
- 优化团队数据加载和展示逻辑
- 添加保存二维码到相册功能
- 调整提现金额门槛为100元
---
src/api/shop/shopDealerApply/model/index.ts | 1 +
src/dealer/apply/add.tsx | 48 +++-
src/dealer/customer/add.tsx | 177 ++++++++-------
src/dealer/customer/index.tsx | 10 +-
src/dealer/team/index.tsx | 235 ++++++++++++--------
src/dealer/wechat/index.tsx | 62 +++++-
src/dealer/withdraw/index.tsx | 6 +-
src/pages/index/Header.tsx | 12 +
8 files changed, 359 insertions(+), 192 deletions(-)
diff --git a/src/api/shop/shopDealerApply/model/index.ts b/src/api/shop/shopDealerApply/model/index.ts
index f5c4e3a..1d7c0d4 100644
--- a/src/api/shop/shopDealerApply/model/index.ts
+++ b/src/api/shop/shopDealerApply/model/index.ts
@@ -48,6 +48,7 @@ export interface ShopDealerApply {
export interface ShopDealerApplyParam extends PageParam {
applyId?: number;
type?: number;
+ dealerName?: string;
mobile?: string;
userId?: number;
keywords?: string;
diff --git a/src/dealer/apply/add.tsx b/src/dealer/apply/add.tsx
index 13473ae..f862bf3 100644
--- a/src/dealer/apply/add.tsx
+++ b/src/dealer/apply/add.tsx
@@ -37,6 +37,14 @@ const AddUserAddress = () => {
setFormData({
...user,
refereeId: Number(inviteParams.inviter),
+ // 清空昵称,强制用户手动输入
+ nickname: '',
+ })
+ } else {
+ // 如果没有邀请参数,也要确保昵称为空
+ setFormData({
+ ...user,
+ nickname: '',
})
}
}
@@ -130,7 +138,9 @@ const AddUserAddress = () => {
return;
}
- if (!values.realName && !FormData?.nickname) {
+ // 验证昵称:必须填写且不能是默认的微信昵称
+ const nickname = values.realName || FormData?.nickname || '';
+ if (!nickname || nickname.trim() === '') {
Taro.showToast({
title: '请填写昵称',
icon: 'error'
@@ -138,6 +148,25 @@ const AddUserAddress = () => {
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: '请上传头像',
@@ -145,6 +174,7 @@ const AddUserAddress = () => {
});
return;
}
+ console.log(values,FormData)
const roles = await listUserRole({userId: user?.userId})
console.log(roles, 'roles...')
@@ -247,8 +277,11 @@ const AddUserAddress = () => {
console.log('手机号已获取', userData.phone)
const updatedFormData = {
...FormData,
- ...userData,
- phone: userData.phone
+ phone: userData.phone,
+ // 不自动填充微信昵称,保持用户已输入的昵称
+ nickname: FormData?.nickname || '',
+ // 只在没有头像时才使用微信头像
+ avatar: FormData?.avatar || userData.avatar
}
setFormData(updatedFormData)
@@ -256,8 +289,9 @@ const AddUserAddress = () => {
if (formRef.current) {
formRef.current.setFieldsValue({
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 = () => {
}
-
+
getWxNickname(e.detail.value)}
/>
diff --git a/src/dealer/customer/add.tsx b/src/dealer/customer/add.tsx
index e922291..f9b9ba7 100644
--- a/src/dealer/customer/add.tsx
+++ b/src/dealer/customer/add.tsx
@@ -1,20 +1,19 @@
import {useEffect, useState, useRef} from "react";
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 {useRouter} from '@tarojs/taro'
-import {View,Text} from '@tarojs/components'
+import {View} from '@tarojs/components'
import FixedButton from "@/components/FixedButton";
import {useUser} from "@/hooks/useUser";
import {ShopDealerApply} from "@/api/shop/shopDealerApply/model";
import {
- addShopDealerApply, getShopDealerApply,
+ addShopDealerApply, getShopDealerApply, pageShopDealerApply,
updateShopDealerApply
} from "@/api/shop/shopDealerApply";
import {
formatDateForDatabase,
- extractDateForCalendar,
- formatDateForDisplay
+ extractDateForCalendar
} from "@/utils/dateUtils";
const AddShopDealerApply = () => {
@@ -47,7 +46,6 @@ const AddShopDealerApply = () => {
}
-
// 处理签约时间选择
const handleApplyTimeConfirm = (param: string) => {
const selectedDate = param[3] // 选中的日期字符串 (YYYY-M-D)
@@ -79,13 +77,13 @@ const AddShopDealerApply = () => {
}
const reload = async () => {
- if(!params.id){
+ if (!params.id) {
return false;
}
// 查询当前用户ID是否已有申请记录
try {
const dealerApply = await getShopDealerApply(Number(params.id));
- if(dealerApply){
+ if (dealerApply) {
setFormData(dealerApply)
setIsEditMode(true);
setExistingApply(dealerApply)
@@ -109,49 +107,60 @@ const AddShopDealerApply = () => {
}
// 提交表单
- const submitSucceed = async (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) : '')
- };
+ const submitSucceed = (values: any) => {
- // 如果是编辑模式,添加现有申请的id
- if (isEditMode && existingApply?.applyId) {
- submitData.applyId = existingApply.applyId;
+ pageShopDealerApply({dealerName: values.dealerName, type: 4}).then((res) => {
+ if (res && res?.count > 0) {
+ 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) : '')
+ };
- // 执行新增或更新操作
- if (isEditMode) {
- await updateShopDealerApply(submitData);
- } else {
- await addShopDealerApply(submitData);
+ // 如果是编辑模式,添加现有申请的id
+ if (isEditMode && existingApply?.applyId) {
+ submitData.applyId = existingApply.applyId;
+ }
+
+ // 执行新增或更新操作
+ 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 = () => {
-
-
-
-
- !isEditMode && setShowApplyTimePicker(true)}
- style={{
- cursor: isEditMode ? 'not-allowed' : 'pointer',
- opacity: isEditMode ? 0.6 : 1
- }}
- >
-
-
-
- {applyTime ? formatDateForDisplay(applyTime) : '请选择签约时间'}
-
-
-
-
-
- !isEditMode && setShowContractTimePicker(true)}
- style={{
- cursor: isEditMode ? 'not-allowed' : 'pointer',
- opacity: isEditMode ? 0.6 : 1
- }}
- >
-
-
-
- {contractTime ? formatDateForDisplay(contractTime) : '请选择合同生效起止时间'}
-
-
-
-
+ {/**/}
+ {/* */}
+ {/**/}
+ {/**/}
+ {/* !isEditMode && setShowApplyTimePicker(true)}*/}
+ {/* style={{*/}
+ {/* cursor: isEditMode ? 'not-allowed' : 'pointer',*/}
+ {/* opacity: isEditMode ? 0.6 : 1*/}
+ {/* }}*/}
+ {/* >*/}
+ {/* */}
+ {/* */}
+ {/* */}
+ {/* {applyTime ? formatDateForDisplay(applyTime) : '请选择签约时间'}*/}
+ {/* */}
+ {/* */}
+ {/* */}
+ {/**/}
+ {/**/}
+ {/* !isEditMode && setShowContractTimePicker(true)}*/}
+ {/* style={{*/}
+ {/* cursor: isEditMode ? 'not-allowed' : 'pointer',*/}
+ {/* opacity: isEditMode ? 0.6 : 1*/}
+ {/* }}*/}
+ {/* >*/}
+ {/* */}
+ {/* */}
+ {/* */}
+ {/* {contractTime ? formatDateForDisplay(contractTime) : '请选择合同生效起止时间'}*/}
+ {/* */}
+ {/* */}
+ {/* */}
+ {/**/}
{/**/}
{/* */}
{/**/}
diff --git a/src/dealer/customer/index.tsx b/src/dealer/customer/index.tsx
index f4dfb2c..31075b9 100644
--- a/src/dealer/customer/index.tsx
+++ b/src/dealer/customer/index.tsx
@@ -41,7 +41,7 @@ const CustomerIndex = () => {
// 构建API参数,根据状态筛选
const params: any = {
- type: 0,
+ type: 4,
page: currentPage
};
const applyStatus = mapCustomerStatusToApplyStatus(statusFilter || activeTab);
@@ -126,10 +126,10 @@ const CustomerIndex = () => {
try {
// 并行获取各状态的数量
const [allRes, pendingRes, signedRes, cancelledRes] = await Promise.all([
- pageShopDealerApply({type: 0}), // 全部
- pageShopDealerApply({applyStatus: 10, type: 0}), // 跟进中
- pageShopDealerApply({applyStatus: 20, type: 0}), // 已签约
- pageShopDealerApply({applyStatus: 30, type: 0}) // 已取消
+ pageShopDealerApply({type: 4}), // 全部
+ pageShopDealerApply({applyStatus: 10, type: 4}), // 跟进中
+ pageShopDealerApply({applyStatus: 20, type: 4}), // 已签约
+ pageShopDealerApply({applyStatus: 30, type: 4}) // 已取消
]);
setStatusCounts({
diff --git a/src/dealer/team/index.tsx b/src/dealer/team/index.tsx
index 2e95611..e6cb1ec 100644
--- a/src/dealer/team/index.tsx
+++ b/src/dealer/team/index.tsx
@@ -1,6 +1,7 @@
import React, {useState, useEffect, useCallback} from 'react'
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 {useDealerUser} from '@/hooks/useDealerUser'
import {listShopDealerReferee} from '@/api/shop/shopDealerReferee'
@@ -41,6 +42,90 @@ const DealerTeam: React.FC = () => {
// 当前查看的用户名称
const [currentDealerName, setCurrentDealerName] = useState('')
+ // 异步加载成员统计数据
+ 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 () => {
if (!dealerUser?.userId && !dealerId) return
@@ -54,6 +139,7 @@ const DealerTeam: React.FC = () => {
})
if (refereeResult) {
+ console.log('团队成员原始数据:', refereeResult)
// 处理团队成员数据
const processedMembers: TeamMemberWithStats[] = refereeResult.map(member => ({
...member,
@@ -65,60 +151,12 @@ const DealerTeam: React.FC = () => {
joinTime: member.createTime
}))
- // 并行获取每个成员的订单统计和下级成员数量
- const memberStats = await Promise.all(
- processedMembers.map(async (member) => {
- try {
- // 获取订单统计
- const orderResult = await pageShopDealerOrder({
- page: 1,
- userId: member.userId
- })
+ // 先显示基础数据,然后异步加载详细统计
+ setTeamMembers(processedMembers)
+ setLoading(false)
- // 获取下级成员数量
- const subMembersResult = await 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
- }
- })
- )
-
- setTeamMembers(memberStats)
+ // 异步加载每个成员的详细统计数据
+ loadMemberStats(processedMembers)
}
} catch (error) {
@@ -198,6 +236,10 @@ const DealerTeam: React.FC = () => {
const renderMemberItem = (member: TeamMemberWithStats) => {
// 判断是否可以点击:有下级成员且未达到层级限制
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 (
{
}`}
onClick={() => getNextUser(member)}
>
-
-
-
-
-
- {member.nickname}
+
+
+
+
+
+
+ {member.nickname}
+
+
+ {/* 显示手机号(仅本级可见) */}
+ {showPhone && member.phone && (
+
+ {member.phone}
+
+
+ )}
+
+
+ 加入时间:{member.joinTime}
-
- 加入时间:{member.joinTime}
-
+
+
+
+
+ 订单数
+
+ {isStatsLoading ? '-' : member.orderCount}
+
+
+
+ 贡献佣金
+
+ {isStatsLoading ? '-' : `¥${member.commission}`}
+
+
+
+ 团队成员
+
+ {isStatsLoading ? '-' : (member.subMembers || 0)}
+
+
-
-
-
- 订单数
-
- {member.orderCount}
-
-
-
- 贡献佣金
-
- ¥{member.commission}
-
-
-
- 团队成员
-
- {member.subMembers || 0}
-
-
-
-
)
}
@@ -287,7 +338,7 @@ const DealerTeam: React.FC = () => {
navTo(`/dealer/apply/add`,true)}]}
+ }} actions={[{text: '立即申请', onClick: () => navTo(`/dealer/apply/add`, true)}]}
/>
)
diff --git a/src/dealer/wechat/index.tsx b/src/dealer/wechat/index.tsx
index 4098de3..1132e6e 100644
--- a/src/dealer/wechat/index.tsx
+++ b/src/dealer/wechat/index.tsx
@@ -1,6 +1,7 @@
import {useEffect, useState} from 'react'
import {View, Text, Image} from '@tarojs/components'
import {Tabs} from '@nutui/nutui-react-taro'
+import Taro from '@tarojs/taro'
import './index.scss'
import {listCmsWebsiteField} from "@/api/cms/cmsWebsiteField";
import {CmsWebsiteField} from "@/api/cms/cmsWebsiteField/model";
@@ -9,15 +10,74 @@ const WechatService = () => {
const [activeTab, setActiveTab] = useState('0')
const [codes, setCodes] = useState([])
+ // 长按保存二维码到相册
+ 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]) => (
-
saveQRCodeToAlbum(`${data.value}`)}
/>
{data.style && 联系电话:{data.style}}
diff --git a/src/dealer/withdraw/index.tsx b/src/dealer/withdraw/index.tsx
index fd49672..e8f6cc1 100644
--- a/src/dealer/withdraw/index.tsx
+++ b/src/dealer/withdraw/index.tsx
@@ -204,9 +204,9 @@ const DealerWithdraw: React.FC = () => {
return
}
- if (amount < 10) {
+ if (amount < 100) {
Taro.showToast({
- title: '最低提现金额为10元',
+ title: '最低提现金额为100元',
icon: 'error'
})
return
@@ -316,7 +316,7 @@ const DealerWithdraw: React.FC = () => {
borderTop: '1px solid rgba(255, 255, 255, 0.3)'
}}>
- 最低提现金额:¥10
+ 最低提现金额:¥100
diff --git a/src/pages/index/Header.tsx b/src/pages/index/Header.tsx
index a5afb84..d4fcdb7 100644
--- a/src/pages/index/Header.tsx
+++ b/src/pages/index/Header.tsx
@@ -193,6 +193,18 @@ const Header = (props: any) => {
useEffect(() => {
if (isLoggedIn && user) {
console.log('用户信息已更新:', user);
+ // 检查是否设置头像和昵称
+ if(user.nickname === '微信用户'){
+ Taro.showToast({
+ title: '请设置头像和昵称',
+ icon: 'none'
+ })
+ setTimeout(() => {
+ Taro.navigateTo({
+ url: '/user/profile/profile'
+ });
+ }, 2000)
+ }
}
}, [user, isLoggedIn])