2026-08-04 14:53:11 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* VIP 权限判断工具(前端口子)
|
2026-08-07 20:24:49 +08:00
|
|
|
|
* 默认从登录态 userInfo.vip 读取;在线复诊等场景可传入挂号履约诊所的 vip
|
2026-08-04 14:53:11 +08:00
|
|
|
|
*/
|
|
|
|
|
|
import { useUserStore } from '@vben/stores';
|
|
|
|
|
|
|
|
|
|
|
|
export interface StoreVipInfo {
|
|
|
|
|
|
level_code?: string;
|
|
|
|
|
|
level_name?: string;
|
|
|
|
|
|
level_weight?: number;
|
|
|
|
|
|
badge_url?: string;
|
|
|
|
|
|
duration_type?: string;
|
|
|
|
|
|
duration_label?: string;
|
|
|
|
|
|
duration_badge_url?: string;
|
|
|
|
|
|
permissions?: string[];
|
|
|
|
|
|
expire_at?: number;
|
|
|
|
|
|
is_lifetime?: boolean;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 读取当前登录用户 VIP 信息 */
|
|
|
|
|
|
export function getCurrentVip(): StoreVipInfo | null {
|
|
|
|
|
|
const userStore = useUserStore();
|
|
|
|
|
|
return ((userStore.userInfo as any)?.vip as StoreVipInfo) || null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-07 20:24:49 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 是否拥有指定权限码
|
|
|
|
|
|
* @param code 权限码
|
|
|
|
|
|
* @param vipOrPermissions 可选:挂号侧 vip 对象,或 permissions 字符串数组;不传则用登录态
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function hasVipPermission(
|
|
|
|
|
|
code: string,
|
|
|
|
|
|
vipOrPermissions?: StoreVipInfo | string[] | null,
|
|
|
|
|
|
): boolean {
|
2026-08-04 14:53:11 +08:00
|
|
|
|
if (!code) return false;
|
2026-08-07 20:24:49 +08:00
|
|
|
|
let list: string[] | undefined;
|
|
|
|
|
|
if (Array.isArray(vipOrPermissions)) {
|
|
|
|
|
|
list = vipOrPermissions;
|
|
|
|
|
|
} else if (vipOrPermissions && typeof vipOrPermissions === 'object') {
|
|
|
|
|
|
list = vipOrPermissions.permissions;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
list = getCurrentVip()?.permissions;
|
|
|
|
|
|
}
|
2026-08-04 14:53:11 +08:00
|
|
|
|
return Array.isArray(list) && list.includes(code);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 等级是否不低于目标(按 level_weight;若无 weight 则仅精确匹配编码时返回 true)
|
|
|
|
|
|
* 简化:仅当当前 level_code 与 minCode 相同,或 weight 足够时通过
|
|
|
|
|
|
*/
|
2026-08-07 20:24:49 +08:00
|
|
|
|
export function levelAtLeast(minCode: string, vip?: StoreVipInfo | null): boolean {
|
|
|
|
|
|
const current = vip ?? getCurrentVip();
|
|
|
|
|
|
if (!current?.level_code) return false;
|
|
|
|
|
|
if (current.level_code === minCode) return true;
|
|
|
|
|
|
const weight = Number(current.level_weight ?? 0);
|
2026-08-04 14:53:11 +08:00
|
|
|
|
const minWeight = parseLevelWeight(minCode);
|
|
|
|
|
|
if (minWeight >= 0) {
|
|
|
|
|
|
return weight >= minWeight;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 粗略解析 V0-V11 / BLACK_GOLD / CUSTOM 权重 */
|
|
|
|
|
|
function parseLevelWeight(code: string): number {
|
|
|
|
|
|
if (/^V\d+$/i.test(code)) {
|
|
|
|
|
|
return Number(code.slice(1));
|
|
|
|
|
|
}
|
|
|
|
|
|
if (code === 'BLACK_GOLD') return 12;
|
|
|
|
|
|
if (code === 'CUSTOM') return 13;
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 组合式:供组件调用 */
|
|
|
|
|
|
export function useVipGate() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
vip: getCurrentVip(),
|
|
|
|
|
|
hasVipPermission,
|
|
|
|
|
|
levelAtLeast,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|