From c0954564a697c98496b69cb6a8f79942afe074ae 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, 6 Feb 2026 02:29:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(user):=20=E4=BC=98=E5=8C=96=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=9D=83=E9=99=90=E7=AE=A1=E7=90=86=E4=B8=8E=E6=89=AB?= =?UTF-8?q?=E7=A0=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 isAdmin 状态检查逻辑支持多种数据类型 (true/1/'1') - 实现统一扫码按钮的管理员权限控制,仅管理员可查看 - 集成 saveStorageByLoginUser 工具函数统一处理登录用户信息存储 - 优化扫码取消操作的错误处理,区分用户主动取消与实际错误 - 同步本地存储中的用户信息以便其他钩子读取管理员标识 --- src/hooks/useUnifiedQRScan.ts | 14 +++- src/hooks/useUser.ts | 7 +- src/pages/user/components/UserCard.tsx | 88 ++++++++++++++------------ 3 files changed, 66 insertions(+), 43 deletions(-) diff --git a/src/hooks/useUnifiedQRScan.ts b/src/hooks/useUnifiedQRScan.ts index 8e8692b..f2a2b8d 100644 --- a/src/hooks/useUnifiedQRScan.ts +++ b/src/hooks/useUnifiedQRScan.ts @@ -271,7 +271,14 @@ export function useUnifiedQRScan() { } }, fail: (err) => { - reject(new Error(err.errMsg || '扫码失败')); + const msg = (err as any)?.errMsg || ''; + // `scanCode:fail cancel` is a user-driven cancel; don't treat it as an error toast. + if (typeof msg === 'string' && msg.toLowerCase().includes('cancel')) { + cancelRef.current = true; + reject(new Error('取消扫码')); + return; + } + reject(new Error(msg || '扫码失败')); } }); }); @@ -323,6 +330,11 @@ export function useUnifiedQRScan() { return result; } catch (err: any) { + // User cancelled scanning (e.g. `scanCode:fail cancel`). + if (cancelRef.current) { + reset(); + return null; + } if (!cancelRef.current) { setState(UnifiedScanState.ERROR); const errorMessage = err.message || '处理失败'; diff --git a/src/hooks/useUser.ts b/src/hooks/useUser.ts index 479a0f3..4ad2069 100644 --- a/src/hooks/useUser.ts +++ b/src/hooks/useUser.ts @@ -280,11 +280,14 @@ export const useUser = () => { // 检查用户是否是管理员 const isAdmin = () => { - return user?.isAdmin === true; + // Some backends use `1/0` (or `1/2`) instead of boolean. + const v: any = (user as any)?.isAdmin; + return v === true || v === 1 || v === '1'; }; const isSuperAdmin = () => { - return user?.isSuperAdmin === true; + const v: any = (user as any)?.isSuperAdmin; + return v === true || v === 1 || v === '1'; }; // 获取用户余额 diff --git a/src/pages/user/components/UserCard.tsx b/src/pages/user/components/UserCard.tsx index cf88eac..d2a3f49 100644 --- a/src/pages/user/components/UserCard.tsx +++ b/src/pages/user/components/UserCard.tsx @@ -13,15 +13,20 @@ import UnifiedQRButton from "@/components/UnifiedQRButton"; import {useThemeStyles} from "@/hooks/useTheme"; import {getRootDomain} from "@/utils/domain"; import { getMyGltUserTicketTotal } from '@/api/glt/gltUserTicket' +import { saveStorageByLoginUser } from '@/utils/server' const UserCard = forwardRef((_, ref) => { const {data, refresh} = useUserData() - const {getDisplayName} = useUser(); + const {getDisplayName, isAdmin} = useUser(); const [IsLogin, setIsLogin] = useState(false) const [userInfo, setUserInfo] = useState() const [ticketTotal, setTicketTotal] = useState(0) const themeStyles = useThemeStyles(); + const canShowScanButton = (() => { + const v: any = (userInfo as any)?.isAdmin + return isAdmin() || v === true || v === 1 || v === '1' + })() // 角色名称:优先取用户 roles 数组的第一个角色名称 const getRoleName = () => { @@ -96,6 +101,8 @@ const UserCard = forwardRef((_, ref) => { if (data) { setUserInfo(data) setIsLogin(true); + // Keep local storage user info in sync so other hooks (e.g. unified scan) can read admin flags. + Taro.setStorageSync('User', data) Taro.setStorageSync('UserId', data.userId) // 登录态已就绪后刷新卡片统计(余额/积分/券/水票) refresh().then() @@ -193,8 +200,7 @@ const UserCard = forwardRef((_, ref) => { return false; } // 登录成功 - Taro.setStorageSync('access_token', res.data.data.access_token) - Taro.setStorageSync('UserId', res.data.data.user.userId) + saveStorageByLoginUser(res.data.data.access_token, res.data.data.user) setUserInfo(res.data.data.user) setIsLogin(true) // 登录态已就绪后刷新卡片统计(余额/积分/券/水票) @@ -249,47 +255,49 @@ const UserCard = forwardRef((_, ref) => { )} - - {/*统一扫码入口 - 支持登录和核销*/} - { - console.log('统一扫码成功:', result); - // 根据扫码类型给出不同的提示 - if (result.type === 'verification') { - const businessType = result?.data?.businessType; - if (businessType === 'gift' && result?.data?.gift) { - const gift = result.data.gift; + {/*统一扫码入口 - 仅管理员可见*/} + {canShowScanButton && ( + + { + console.log('统一扫码成功:', result); + // 根据扫码类型给出不同的提示 + if (result.type === 'verification') { + const businessType = result?.data?.businessType; + if (businessType === 'gift' && result?.data?.gift) { + const gift = result.data.gift; + Taro.showModal({ + title: '核销成功', + content: `已成功核销:${gift.goodsName || gift.name || '礼品'},面值¥${gift.faceValue}` + }); + return; + } + if (businessType === 'ticket' && result?.data?.ticket) { + const ticket = result.data.ticket; + const qty = result.data.qty || 1; + Taro.showModal({ + title: '核销成功', + content: `已成功核销:${ticket.templateName || '水票'},本次使用${qty}次,剩余可用${ticket.availableQty ?? 0}次` + }); + return; + } Taro.showModal({ title: '核销成功', - content: `已成功核销:${gift.goodsName || gift.name || '礼品'},面值¥${gift.faceValue}` + content: '已成功核销' }); - return; } - if (businessType === 'ticket' && result?.data?.ticket) { - const ticket = result.data.ticket; - const qty = result.data.qty || 1; - Taro.showModal({ - title: '核销成功', - content: `已成功核销:${ticket.templateName || '水票'},本次使用${qty}次,剩余可用${ticket.availableQty ?? 0}次` - }); - return; - } - Taro.showModal({ - title: '核销成功', - content: '已成功核销' - }); - } - }} - onError={(error) => { - console.error('统一扫码失败:', error); - }} - /> - + }} + onError={(error) => { + console.error('统一扫码失败:', error); + }} + /> + + )}