From ceea66242042fff7c96176d1afecc012824e3781 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com>
Date: Sun, 1 Mar 2026 12:19:02 +0800
Subject: [PATCH] =?UTF-8?q?feat(customer):=20=E4=BC=98=E5=8C=96=E6=8A=A5?=
=?UTF-8?q?=E5=A4=87=E4=BA=BA=E7=AE=A1=E7=90=86=E5=92=8C=E6=9D=83=E9=99=90?=
=?UTF-8?q?=E6=8E=A7=E5=88=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 移除页面分页查询,仅保留精确查询接口调用
- 添加当前登录用户ID获取和报备人规范化处理逻辑
- 实现管理员可查看全部客户,普通分销商仅查看自己的权限控制
- 集成用户角色显示功能,在用户卡片组件中展示角色标签
- 修复角色名称获取逻辑,支持多种数据
---
src/dealer/customer/add.tsx | 58 +++++++++++++++++---------
src/dealer/customer/index.tsx | 30 +++++++++----
src/hooks/useUser.ts | 4 +-
src/pages/user/components/UserCard.tsx | 10 ++---
4 files changed, 67 insertions(+), 35 deletions(-)
diff --git a/src/dealer/customer/add.tsx b/src/dealer/customer/add.tsx
index 6711f46..147faf4 100644
--- a/src/dealer/customer/add.tsx
+++ b/src/dealer/customer/add.tsx
@@ -15,7 +15,7 @@ import {
extractDateForCalendar, formatDateForDisplay
} from "@/utils/dateUtils";
import {ShopDealerUser} from "@/api/shop/shopDealerUser/model";
-import {getShopDealerUser, pageShopDealerUser} from "@/api/shop/shopDealerUser";
+import {getShopDealerUser} from "@/api/shop/shopDealerUser";
const AddShopDealerApply = () => {
const {params} = useRouter();
@@ -286,6 +286,8 @@ const AddShopDealerApply = () => {
const submitSucceed = async (values: any) => {
try {
+ const currentUserId = Number(Taro.getStorageSync('UserId')) || 0;
+
// 房号相关必填校验
if (!values.address || values.address.trim() === '') {
Taro.showToast({title: '请选择/填写小区', icon: 'error'});
@@ -323,18 +325,34 @@ const AddShopDealerApply = () => {
return;
}
- // 验证报备人是否存在
- if (values.userId > 0) {
- const isExist = await pageShopDealerUser({userId: Number(values.userId)});
- if(isExist && isExist.count == 0){
- Taro.showToast({
- title: '报备人不存在',
- icon: 'error'
- });
+ // 规范化报备人:留空=自己报备(当前登录用户)
+ const rawUserId = normalizeText(values.userId);
+ const submitUserId = rawUserId
+ ? Number(rawUserId)
+ : (isEditMode ? (existingApply?.userId || currentUserId) : currentUserId);
+ if (!Number.isFinite(submitUserId) || submitUserId <= 0) {
+ Taro.showToast({title: '请填写正确的报备人ID', icon: 'error'});
+ return;
+ }
+
+ // 报备人存在性校验 + 获取该报备人的推荐人(用于后端展示链路)
+ let reporterDealerUser: ShopDealerUser | undefined = undefined;
+ if (submitUserId === currentUserId) {
+ reporterDealerUser = referee;
+ } else {
+ try {
+ reporterDealerUser = await getShopDealerUser(submitUserId);
+ } catch {
+ Taro.showToast({title: '报备人不存在', icon: 'error'});
return;
}
}
+ // 后端常用 0 表示“无推荐人”,避免传空值触发“推荐人不存在”
+ const submitRefereeId = (reporterDealerUser?.refereeId && reporterDealerUser.refereeId > 0)
+ ? reporterDealerUser.refereeId
+ : undefined;
+
const houseKeyRaw = buildHouseKey(values.address, values.buildingNo, values.unitNo, values.roomNo);
const houseKeyNormalized = buildHouseKeyNormalized(values.address, values.buildingNo, values.unitNo, values.roomNo);
const houseKey = houseKeyNormalized || houseKeyRaw;
@@ -416,7 +434,10 @@ const AddShopDealerApply = () => {
// 客户姓名/手机号
realName: values.realName,
mobile: values.mobile,
- refereeId: referee?.refereeId,
+ // 报备人(留空时用当前登录用户)
+ // userId: submitUserId,
+ // 推荐人(报备人的上级;无则传 0)
+ refereeId: submitRefereeId,
applyStatus: isEditMode ? 20 : 10,
auditTime: undefined,
// 设置保护期过期时间(15天后)
@@ -496,8 +517,7 @@ const AddShopDealerApply = () => {
unitNo: parsed.unitNo,
roomNo: parsed.roomNo,
realName: FormData.realName,
- mobile: FormData.mobile,
- userId: FormData.userId
+ mobile: FormData.mobile
});
}, [FormData]);
@@ -571,13 +591,13 @@ const AddShopDealerApply = () => {
{/**/}
>
)}
-
-
-
+ {/**/}
+ {/* */}
+ {/**/}
diff --git a/src/dealer/customer/index.tsx b/src/dealer/customer/index.tsx
index 70b3377..9848b3e 100644
--- a/src/dealer/customer/index.tsx
+++ b/src/dealer/customer/index.tsx
@@ -34,8 +34,10 @@ const CustomerIndex = () => {
const [hasMore, setHasMore] = useState(true)
// 非分销商不允许查看客户列表
- const {hasRole, loading: userLoading} = useUser()
- const canView = hasRole('dealer')
+ const {user, hasRole, loading: userLoading} = useUser()
+ // 管理员允许查看全部;普通分销商仅查看自己
+ const isAdminUser = user?.isAdmin === true
+ const canView = hasRole('dealer') || isAdminUser
const roleCheckFinished = !userLoading
const noPermissionShownRef = useRef(false)
@@ -196,12 +198,17 @@ const CustomerIndex = () => {
setLoading(true);
try {
const currentPage = resetPage ? 1 : (targetPage || page);
+ const currentUserId = Number(Taro.getStorageSync('UserId')) || user?.userId || 0;
// 构建API参数,根据状态筛选
const params: any = {
type: 4,
page: currentPage
};
+ // 非管理员:只看自己添加的客户
+ if (!isAdminUser && currentUserId > 0) {
+ params.userId = currentUserId;
+ }
const applyStatus = mapCustomerStatusToApplyStatus(statusFilter || activeTab);
if (applyStatus !== undefined) {
params.applyStatus = applyStatus;
@@ -244,7 +251,7 @@ const CustomerIndex = () => {
} finally {
setLoading(false);
}
- }, [activeTab, page]);
+ }, [activeTab, page, isAdminUser, user?.userId]);
const reloadMore = async () => {
if (loading || !hasMore) return; // 防止重复加载
@@ -292,12 +299,19 @@ const CustomerIndex = () => {
// 获取所有状态的统计数量
const fetchStatusCounts = useCallback(async () => {
try {
+ const currentUserId = Number(Taro.getStorageSync('UserId')) || user?.userId || 0;
+ const baseParams: any = {type: 4};
+ // 非管理员:只统计自己添加的客户
+ if (!isAdminUser && currentUserId > 0) {
+ baseParams.userId = currentUserId;
+ }
+
// 并行获取各状态的数量
const [allRes, pendingRes, signedRes, cancelledRes] = await Promise.all([
- pageShopDealerApply({type: 4}), // 全部
- pageShopDealerApply({applyStatus: 10, type: 4}), // 跟进中
- pageShopDealerApply({applyStatus: 20, type: 4}), // 已签约
- pageShopDealerApply({applyStatus: 30, type: 4}) // 已取消
+ pageShopDealerApply({...baseParams}), // 全部
+ pageShopDealerApply({...baseParams, applyStatus: 10}), // 跟进中
+ pageShopDealerApply({...baseParams, applyStatus: 20}), // 已签约
+ pageShopDealerApply({...baseParams, applyStatus: 30}) // 已取消
]);
setStatusCounts({
@@ -309,7 +323,7 @@ const CustomerIndex = () => {
} catch (error) {
console.error('获取状态统计失败:', error);
}
- }, []);
+ }, [isAdminUser, user?.userId]);
const getStatusCounts = () => statusCounts;
diff --git a/src/hooks/useUser.ts b/src/hooks/useUser.ts
index 7b7b1b2..ccf3481 100644
--- a/src/hooks/useUser.ts
+++ b/src/hooks/useUser.ts
@@ -10,7 +10,6 @@ export const useUser = () => {
const [user, setUser] = useState(null);
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [loading, setLoading] = useState(true);
- const [userInfo] = useState()
// 自动登录(通过OpenID)
const autoLoginByOpenId = async () => {
@@ -259,7 +258,8 @@ export const useUser = () => {
// 角色名称:优先取用户 roles 数组的第一个角色名称
const getRoleName = () => {
- return userInfo?.roles?.[0]?.roleName || userInfo?.roleName || '业务员'
+ // Some APIs return `roles`, some return single `roleName`.
+ return user?.roles?.[0]?.roleName || user?.roleName || '注册用户'
}
// 检查用户是否已实名认证
const isCertified = () => {
diff --git a/src/pages/user/components/UserCard.tsx b/src/pages/user/components/UserCard.tsx
index 9f4c950..259307f 100644
--- a/src/pages/user/components/UserCard.tsx
+++ b/src/pages/user/components/UserCard.tsx
@@ -1,5 +1,4 @@
-import {Button} from '@nutui/nutui-react-taro'
-import {Avatar} from '@nutui/nutui-react-taro'
+import {Avatar, Button, Tag} from '@nutui/nutui-react-taro'
import {View} from '@tarojs/components'
import {Scan} from '@nutui/icons-react-taro';
import {getWxOpenId} from '@/api/layout';
@@ -16,7 +15,8 @@ function UserCard() {
isLoggedIn,
loginUser,
fetchUserInfo,
- getDisplayName
+ getDisplayName,
+ getRoleName
} = useUser();
useEffect(() => {
@@ -178,9 +178,7 @@ function UserCard() {
{getDisplayName()}
{isLoggedIn ? (
- {/**/}
- {/* {getRoleName()}*/}
- {/**/}
+ {getRoleName()}
) : ''}