refactor(dealer): 重构经销商申请和团队管理功能

-优化了经销商申请流程,简化了表单和提交逻辑
-重新设计了团队管理页面,优化了成员展示和统计功能
- 移除了不必要的功能和冗余代码,提高了代码可维护性- 调整了 API接口调用,确保数据处理的正确性和一致性
This commit is contained in:
2025-09-09 21:02:05 +08:00
parent bfab2b190a
commit b2d79ab052
4 changed files with 217 additions and 177 deletions

View File

@@ -36,5 +36,6 @@ export interface ShopDealerReferee {
export interface ShopDealerRefereeParam extends PageParam {
id?: number;
dealerId?: number;
deleted?: number;
keywords?: string;
}

View File

@@ -1,21 +1,16 @@
import {useEffect, useState, useRef} from "react";
import {Loading, CellGroup, Cell, Input, Form, Avatar, Button, Space} 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 Taro 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,
pageShopDealerApply,
updateShopDealerApply
} from "@/api/shop/shopDealerApply";
import {getShopDealerUser} from "@/api/shop/shopDealerUser";
import {TenantId} from "@/config/app";
import {updateUser} from "@/api/system/user";
import {User} from "@/api/system/user/model";
import {getStoredInviteParams, handleInviteRelation} from "@/utils/invite";
import {addShopDealerUser} from "@/api/shop/shopDealerUser";
import {listUserRole, updateUserRole} from "@/api/system/userRole";
// 类型定义
interface ChooseAvatarEvent {
@@ -35,22 +30,6 @@ const AddUserAddress = () => {
const [loading, setLoading] = useState<boolean>(true)
const [FormData, setFormData] = useState<User>()
const formRef = useRef<any>(null)
const [isEditMode, setIsEditMode] = useState<boolean>(false)
const [existingApply, setExistingApply] = useState<ShopDealerApply | null>(null)
// 获取审核状态文字
const getApplyStatusText = (status?: number) => {
switch (status) {
case 10:
return '待审核'
case 20:
return '审核通过'
case 30:
return '驳回'
default:
return '未知状态'
}
}
const reload = async () => {
const inviteParams = getStoredInviteParams()
@@ -60,39 +39,16 @@ const AddUserAddress = () => {
refereeId: Number(inviteParams.inviter),
})
}
// 判断用户是否登录
if (!user?.userId) {
return false;
}
// 查询当前用户ID是否已有申请记录
try {
const res = await pageShopDealerApply({userId: user?.userId});
if (res && res.count > 0) {
setIsEditMode(true);
setExistingApply(res.list[0]);
// 如果有记录,填充表单数据
setFormData(res.list[0]);
setLoading(false)
} else {
setIsEditMode(false);
setExistingApply(null);
setLoading(false)
}
} catch (error) {
setLoading(true)
console.error('查询申请记录失败:', error);
setIsEditMode(false);
setExistingApply(null);
}
}
const uploadAvatar = ({detail}: ChooseAvatarEvent) => {
// 先更新本地显示的头像
setFormData({
// 先更新本地显示的头像(临时显示)
const tempFormData = {
...FormData,
avatar: `${detail.avatarUrl}`,
})
}
setFormData(tempFormData)
Taro.uploadFile({
url: 'https://server.websoft.top/api/oss/upload',
@@ -105,20 +61,46 @@ const AddUserAddress = () => {
success: async (res) => {
const data = JSON.parse(res.data);
if (data.code === 0) {
const finalAvatarUrl = `${data.data.thumbnail}`
try {
// 使用 useUser hook 的 updateUser 方法更新头像
await updateUser({
avatar: `${data.data.thumbnail}`
avatar: finalAvatarUrl
})
Taro.showToast({
title: '头像上传成功',
icon: 'success',
duration: 1500
})
// 由于 useEffect 监听了 user 变化FormData 会自动同步更新
} catch (error) {
console.error('更新头像失败:', error)
// 如果更新失败,恢复原来的头像
setFormData({
...FormData,
avatar: user?.avatar || ''
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) => {
@@ -139,32 +121,60 @@ const AddUserAddress = () => {
// 提交表单
const submitSucceed = async (values: any) => {
try {
// 验证必填字段
if (!values.phone && !FormData?.phone) {
Taro.showToast({
title: '请先获取手机号',
icon: 'error'
});
return;
}
if (!values.realName && !FormData?.nickname) {
Taro.showToast({
title: '请填写昵称',
icon: 'error'
});
return;
}
if (!values.avatar && !FormData?.avatar) {
Taro.showToast({
title: '请上传头像',
icon: 'error'
});
return;
}
const roles = await listUserRole({userId: user?.userId})
console.log(roles, 'roles...')
// 准备提交的数据
const submitData = {
...values,
realName: values.realName || user?.nickname,
mobile: user?.phone,
refereeId: values.refereeId || FormData?.refereeId,
applyStatus: 10,
auditTime: undefined
};
await getShopDealerUser(submitData.refereeId);
await updateUser({
userId: user?.userId,
nickname: values.realName || FormData?.nickname,
phone: values.phone || FormData?.phone,
avatar: values.avatar || FormData?.avatar,
refereeId: values.refereeId || FormData?.refereeId
});
// 如果是编辑模式添加现有申请的id
if (isEditMode && existingApply?.applyId) {
submitData.applyId = existingApply.applyId;
await addShopDealerUser({
userId: user?.userId,
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({
title: `${isEditMode ? '提交' : '提交'}成功`,
title: `注册成功`,
icon: 'success'
});
@@ -174,20 +184,24 @@ const AddUserAddress = () => {
} catch (error) {
console.error('验证邀请人失败:', error);
return Taro.showToast({
title: '邀请人ID不存在',
icon: 'error'
});
}
}
// 获取微信昵称
const getWxNickname = (nickname: string) => {
// 更新表单数据
setFormData({
const updatedFormData = {
...FormData,
nickname: nickname
});
}
setFormData(updatedFormData);
// 同步更新表单字段
if (formRef.current) {
formRef.current.setFieldsValue({
realName: nickname
})
}
}
/* 获取用户手机号 */
@@ -225,20 +239,36 @@ const AddUserAddress = () => {
// 登录成功
const token = res.data.data.access_token;
const userData = res.data.data.user;
console.log(userData,'userData...')
console.log(userData, 'userData...')
// 使用useUser Hook的loginUser方法更新状态
loginUser(token, userData);
if(userData.phone){
console.log('手机号已获取',userData.phone)
setFormData({
if (userData.phone) {
console.log('手机号已获取', userData.phone)
const updatedFormData = {
...FormData,
...userData,
phone: userData.phone
}
setFormData(updatedFormData)
// 更新表单字段值
if (formRef.current) {
formRef.current.setFieldsValue({
phone: userData.phone,
realName: userData.nickname || FormData?.nickname,
avatar: userData.avatar || FormData?.avatar
})
}
Taro.showToast({
title: '手机号获取成功',
icon: 'success',
duration: 1500
})
}
// 处理邀请关系
if (userData?.userId) {
try {
@@ -290,6 +320,18 @@ const AddUserAddress = () => {
})
}, [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) {
return <Loading className={'px-2'}></Loading>
}
@@ -311,7 +353,12 @@ const AddUserAddress = () => {
</Form.Item>
<Form.Item name="phone" label="手机号" initialValue={FormData?.phone} required>
<View className="flex items-center justify-between">
<Input placeholder="请填写手机号" disabled={true} maxLength={11}/>
<Input
placeholder="请填写手机号"
disabled={true}
maxLength={11}
value={FormData?.phone || ''}
/>
<Button style={{color: '#ffffff'}} open-type="getPhoneNumber" onGetPhoneNumber={handleGetPhoneNumber}>
<Space>
<Button size="small"></Button>
@@ -320,18 +367,18 @@ const AddUserAddress = () => {
</View>
</Form.Item>
{
FormData?.phone && <Form.Item name="avatar" label="头像" initialValue={user?.avatar} required>
FormData?.phone && <Form.Item name="avatar" label="头像" initialValue={FormData?.avatar} required>
<Button open-type="chooseAvatar" style={{height: '58px'}} onChooseAvatar={uploadAvatar}>
<Avatar src={FormData?.avatar} size="54"/>
<Avatar src={FormData?.avatar || user?.avatar} size="54"/>
</Button>
</Form.Item>
}
<Form.Item name="realName" label="昵称" initialValue={user?.nickname} required>
<Form.Item name="realName" label="昵称" initialValue={FormData?.nickname} required>
<Input
type="nickname"
className="info-content__input"
placeholder="请输入昵称"
value={FormData?.nickname}
value={FormData?.nickname || ''}
onInput={(e: InputEvent) => getWxNickname(e.detail.value)}
/>
</Form.Item>
@@ -339,13 +386,11 @@ const AddUserAddress = () => {
</Form>
{/* 底部浮动按钮 */}
{(!isEditMode) && (
<FixedButton
icon={<Edit/>}
text={isEditMode ? '保存修改' : '提交申请'}
onClick={handleFixedButtonClick}
/>
)}
<FixedButton
icon={<Edit/>}
text={'立即注册'}
onClick={handleFixedButtonClick}
/>
</>
);

View File

@@ -1,7 +1,7 @@
import React, {useState, useEffect} from 'react'
import {View, Text, Image} from '@tarojs/components'
import {Button, Loading} from '@nutui/nutui-react-taro'
import {Share, Download, Copy, QrCode} from '@nutui/icons-react-taro'
import {Download, QrCode} from '@nutui/icons-react-taro'
import Taro from '@tarojs/taro'
import {useDealerUser} from '@/hooks/useDealerUser'
import {generateInviteCode} from '@/api/invite'
@@ -115,52 +115,52 @@ const DealerQrcode: React.FC = () => {
}
// 复制邀请信息
const copyInviteInfo = () => {
if (!dealerUser?.userId) {
Taro.showToast({
title: '用户信息未加载',
icon: 'error'
})
return
}
const inviteText = `🎉 邀请您加入我的团队!
扫描小程序码或搜索"九云售电云"小程序,即可享受优质商品和服务!
💰 成为我的团队成员,一起赚取丰厚佣金
🎁 新用户专享优惠等你来拿
邀请码:${dealerUser.userId}
快来加入我们吧!`
Taro.setClipboardData({
data: inviteText,
success: () => {
Taro.showToast({
title: '邀请信息已复制',
icon: 'success'
})
}
})
}
// const copyInviteInfo = () => {
// if (!dealerUser?.userId) {
// Taro.showToast({
// title: '用户信息未加载',
// icon: 'error'
// })
// return
// }
//
// const inviteText = `🎉 邀请您加入我的团队!
//
// 扫描小程序码或搜索"九云售电云"小程序,即可享受优质商品和服务!
//
// 💰 成为我的团队成员,一起赚取丰厚佣金
// 🎁 新用户专享优惠等你来拿
//
// 邀请码:${dealerUser.userId}
// 快来加入我们吧!`
//
// Taro.setClipboardData({
// data: inviteText,
// success: () => {
// Taro.showToast({
// title: '邀请信息已复制',
// icon: 'success'
// })
// }
// })
// }
// 分享小程序码
const shareMiniProgramCode = () => {
if (!dealerUser?.userId) {
Taro.showToast({
title: '用户信息未加载',
icon: 'error'
})
return
}
// 小程序分享
Taro.showShareMenu({
withShareTicket: true,
showShareItems: ['shareAppMessage']
})
}
// const shareMiniProgramCode = () => {
// if (!dealerUser?.userId) {
// Taro.showToast({
// title: '用户信息未加载',
// icon: 'error'
// })
// return
// }
//
// // 小程序分享
// Taro.showShareMenu({
// withShareTicket: true,
// showShareItems: ['shareAppMessage']
// })
// }
if (!dealerUser) {
return (
@@ -263,29 +263,29 @@ const DealerQrcode: React.FC = () => {
</Button>
</View>
<View className={'my-2 bg-white'}>
<Button
size="large"
block
icon={<Copy/>}
onClick={copyInviteInfo}
disabled={!dealerUser?.userId || loading}
>
</Button>
</View>
<View className={'my-2 bg-white'}>
<Button
size="large"
block
fill="outline"
icon={<Share/>}
onClick={shareMiniProgramCode}
disabled={!dealerUser?.userId || loading}
>
</Button>
</View>
{/*<View className={'my-2 bg-white'}>*/}
{/* <Button*/}
{/* size="large"*/}
{/* block*/}
{/* icon={<Copy/>}*/}
{/* onClick={copyInviteInfo}*/}
{/* disabled={!dealerUser?.userId || loading}*/}
{/* >*/}
{/* 复制邀请信息*/}
{/* </Button>*/}
{/*</View>*/}
{/*<View className={'my-2 bg-white'}>*/}
{/* <Button*/}
{/* size="large"*/}
{/* block*/}
{/* fill="outline"*/}
{/* icon={<Share/>}*/}
{/* onClick={shareMiniProgramCode}*/}
{/* disabled={!dealerUser?.userId || loading}*/}
{/* >*/}
{/* 分享给好友*/}
{/* </Button>*/}
{/*</View>*/}
</View>
{/* 推广说明 */}

View File

@@ -72,13 +72,13 @@ const DealerTeam: React.FC = () => {
// 获取订单统计
const orderResult = await pageShopDealerOrder({
page: 1,
limit: 100,
userId: member.userId
})
// 获取下级成员数量
const subMembersResult = await listShopDealerReferee({
dealerId: member.userId
dealerId: member.userId,
deleted: 0
})
let orderCount = 0
@@ -218,12 +218,6 @@ const DealerTeam: React.FC = () => {
<Text className="font-semibold text-gray-800 mr-2">
{member.nickname}
</Text>
{canClick && (
<Text className="text-xs text-blue-500"></Text>
)}
{!canClick && member.subMembers && member.subMembers > 0 && levelStack.length >= 1 && (
<Text className="text-xs text-red-500"></Text>
)}
</View>
<Text className="text-xs text-gray-500">
{member.joinTime}