forked from gxwebsoft/mp-10550
feat(user): 优化用户权限管理与扫码功能
- 添加 isAdmin 状态检查逻辑支持多种数据类型 (true/1/'1') - 实现统一扫码按钮的管理员权限控制,仅管理员可查看 - 集成 saveStorageByLoginUser 工具函数统一处理登录用户信息存储 - 优化扫码取消操作的错误处理,区分用户主动取消与实际错误 - 同步本地存储中的用户信息以便其他钩子读取管理员标识
This commit is contained in:
@@ -271,7 +271,14 @@ export function useUnifiedQRScan() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
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;
|
return result;
|
||||||
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
// User cancelled scanning (e.g. `scanCode:fail cancel`).
|
||||||
|
if (cancelRef.current) {
|
||||||
|
reset();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (!cancelRef.current) {
|
if (!cancelRef.current) {
|
||||||
setState(UnifiedScanState.ERROR);
|
setState(UnifiedScanState.ERROR);
|
||||||
const errorMessage = err.message || '处理失败';
|
const errorMessage = err.message || '处理失败';
|
||||||
|
|||||||
@@ -280,11 +280,14 @@ export const useUser = () => {
|
|||||||
|
|
||||||
// 检查用户是否是管理员
|
// 检查用户是否是管理员
|
||||||
const isAdmin = () => {
|
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 = () => {
|
const isSuperAdmin = () => {
|
||||||
return user?.isSuperAdmin === true;
|
const v: any = (user as any)?.isSuperAdmin;
|
||||||
|
return v === true || v === 1 || v === '1';
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取用户余额
|
// 获取用户余额
|
||||||
|
|||||||
@@ -13,15 +13,20 @@ import UnifiedQRButton from "@/components/UnifiedQRButton";
|
|||||||
import {useThemeStyles} from "@/hooks/useTheme";
|
import {useThemeStyles} from "@/hooks/useTheme";
|
||||||
import {getRootDomain} from "@/utils/domain";
|
import {getRootDomain} from "@/utils/domain";
|
||||||
import { getMyGltUserTicketTotal } from '@/api/glt/gltUserTicket'
|
import { getMyGltUserTicketTotal } from '@/api/glt/gltUserTicket'
|
||||||
|
import { saveStorageByLoginUser } from '@/utils/server'
|
||||||
|
|
||||||
const UserCard = forwardRef<any, any>((_, ref) => {
|
const UserCard = forwardRef<any, any>((_, ref) => {
|
||||||
const {data, refresh} = useUserData()
|
const {data, refresh} = useUserData()
|
||||||
const {getDisplayName} = useUser();
|
const {getDisplayName, isAdmin} = useUser();
|
||||||
const [IsLogin, setIsLogin] = useState<boolean>(false)
|
const [IsLogin, setIsLogin] = useState<boolean>(false)
|
||||||
const [userInfo, setUserInfo] = useState<User>()
|
const [userInfo, setUserInfo] = useState<User>()
|
||||||
const [ticketTotal, setTicketTotal] = useState<number>(0)
|
const [ticketTotal, setTicketTotal] = useState<number>(0)
|
||||||
|
|
||||||
const themeStyles = useThemeStyles();
|
const themeStyles = useThemeStyles();
|
||||||
|
const canShowScanButton = (() => {
|
||||||
|
const v: any = (userInfo as any)?.isAdmin
|
||||||
|
return isAdmin() || v === true || v === 1 || v === '1'
|
||||||
|
})()
|
||||||
|
|
||||||
// 角色名称:优先取用户 roles 数组的第一个角色名称
|
// 角色名称:优先取用户 roles 数组的第一个角色名称
|
||||||
const getRoleName = () => {
|
const getRoleName = () => {
|
||||||
@@ -96,6 +101,8 @@ const UserCard = forwardRef<any, any>((_, ref) => {
|
|||||||
if (data) {
|
if (data) {
|
||||||
setUserInfo(data)
|
setUserInfo(data)
|
||||||
setIsLogin(true);
|
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)
|
Taro.setStorageSync('UserId', data.userId)
|
||||||
// 登录态已就绪后刷新卡片统计(余额/积分/券/水票)
|
// 登录态已就绪后刷新卡片统计(余额/积分/券/水票)
|
||||||
refresh().then()
|
refresh().then()
|
||||||
@@ -193,8 +200,7 @@ const UserCard = forwardRef<any, any>((_, ref) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 登录成功
|
// 登录成功
|
||||||
Taro.setStorageSync('access_token', res.data.data.access_token)
|
saveStorageByLoginUser(res.data.data.access_token, res.data.data.user)
|
||||||
Taro.setStorageSync('UserId', res.data.data.user.userId)
|
|
||||||
setUserInfo(res.data.data.user)
|
setUserInfo(res.data.data.user)
|
||||||
setIsLogin(true)
|
setIsLogin(true)
|
||||||
// 登录态已就绪后刷新卡片统计(余额/积分/券/水票)
|
// 登录态已就绪后刷新卡片统计(余额/积分/券/水票)
|
||||||
@@ -249,47 +255,49 @@ const UserCard = forwardRef<any, any>((_, ref) => {
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
<Space style={{
|
{/*统一扫码入口 - 仅管理员可见*/}
|
||||||
marginTop: '30px',
|
{canShowScanButton && (
|
||||||
marginRight: '10px'
|
<Space style={{
|
||||||
}}>
|
marginTop: '30px',
|
||||||
{/*统一扫码入口 - 支持登录和核销*/}
|
marginRight: '10px'
|
||||||
<UnifiedQRButton
|
}}>
|
||||||
text="扫一扫"
|
<UnifiedQRButton
|
||||||
size="small"
|
text="扫一扫"
|
||||||
onSuccess={(result) => {
|
size="small"
|
||||||
console.log('统一扫码成功:', result);
|
onSuccess={(result) => {
|
||||||
// 根据扫码类型给出不同的提示
|
console.log('统一扫码成功:', result);
|
||||||
if (result.type === 'verification') {
|
// 根据扫码类型给出不同的提示
|
||||||
const businessType = result?.data?.businessType;
|
if (result.type === 'verification') {
|
||||||
if (businessType === 'gift' && result?.data?.gift) {
|
const businessType = result?.data?.businessType;
|
||||||
const gift = result.data.gift;
|
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({
|
Taro.showModal({
|
||||||
title: '核销成功',
|
title: '核销成功',
|
||||||
content: `已成功核销:${gift.goodsName || gift.name || '礼品'},面值¥${gift.faceValue}`
|
content: '已成功核销'
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (businessType === 'ticket' && result?.data?.ticket) {
|
}}
|
||||||
const ticket = result.data.ticket;
|
onError={(error) => {
|
||||||
const qty = result.data.qty || 1;
|
console.error('统一扫码失败:', error);
|
||||||
Taro.showModal({
|
}}
|
||||||
title: '核销成功',
|
/>
|
||||||
content: `已成功核销:${ticket.templateName || '水票'},本次使用${qty}次,剩余可用${ticket.availableQty ?? 0}次`
|
</Space>
|
||||||
});
|
)}
|
||||||
return;
|
|
||||||
}
|
|
||||||
Taro.showModal({
|
|
||||||
title: '核销成功',
|
|
||||||
content: '已成功核销'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onError={(error) => {
|
|
||||||
console.error('统一扫码失败:', error);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
</View>
|
</View>
|
||||||
<View className={'py-2'}>
|
<View className={'py-2'}>
|
||||||
<View className={'flex justify-around mt-1'}>
|
<View className={'flex justify-around mt-1'}>
|
||||||
|
|||||||
Reference in New Issue
Block a user