feat: 修复部分功能(健康信息、VIP、医生端健康信息导入)
This commit is contained in:
@@ -1,9 +1,23 @@
|
||||
/**
|
||||
* VIP 权限判断工具(前端口子)
|
||||
* 默认从登录态 userInfo.vip 读取;在线复诊等场景可传入挂号履约诊所的 vip
|
||||
* VIP 权限判断(全局方法)
|
||||
* - 默认读登录态 userInfo.vip
|
||||
* - 接诊/复诊等场景务必传入「履约诊所」vip,避免用错门店权益
|
||||
* - 模板显隐优先用 <VipGate code="xxx" :vip="storeVip" />
|
||||
*/
|
||||
import { computed, type ComputedRef, type Ref } from 'vue';
|
||||
|
||||
import { useUserStore } from '@vben/stores';
|
||||
|
||||
/** 与 xk_vip_feature.code / 等级 permissions 对齐的常用功能码 */
|
||||
export const VIP_FEATURE = {
|
||||
MEDICAL_RECORD: 'medical_record',
|
||||
AI_MEDICAL_RECORD: 'ai_medical_record',
|
||||
AI_PRESCRIPTION: 'ai_prescription',
|
||||
GOLDEN_FORMULA: 'golden_formula',
|
||||
} as const;
|
||||
|
||||
export type VipFeatureCode = (typeof VIP_FEATURE)[keyof typeof VIP_FEATURE] | string;
|
||||
|
||||
export interface StoreVipInfo {
|
||||
level_code?: string;
|
||||
level_name?: string;
|
||||
@@ -24,29 +38,56 @@ export function getCurrentVip(): StoreVipInfo | null {
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否拥有指定权限码
|
||||
* @param code 权限码
|
||||
* @param vipOrPermissions 可选:挂号侧 vip 对象,或 permissions 字符串数组;不传则用登录态
|
||||
* 从 get-current-store-type / 同类接口响应中取出 vip 对象
|
||||
* 兼容 result / data / 已解包
|
||||
*/
|
||||
export function pickVipFromApiPayload(payload: unknown): StoreVipInfo | null {
|
||||
if (!payload || typeof payload !== 'object') return null;
|
||||
const raw = payload as Record<string, any>;
|
||||
const data = raw.result ?? raw.data ?? raw;
|
||||
if (!data || typeof data !== 'object') return null;
|
||||
const vip = (data as any).vip;
|
||||
return vip && typeof vip === 'object' ? (vip as StoreVipInfo) : null;
|
||||
}
|
||||
|
||||
/** 规范化 permissions 列表 */
|
||||
export function normalizeVipPermissions(
|
||||
vipOrPermissions?: StoreVipInfo | string[] | null,
|
||||
): string[] {
|
||||
if (Array.isArray(vipOrPermissions)) {
|
||||
return vipOrPermissions.filter((x) => typeof x === 'string' && x);
|
||||
}
|
||||
if (vipOrPermissions && typeof vipOrPermissions === 'object') {
|
||||
const list = vipOrPermissions.permissions;
|
||||
return Array.isArray(list) ? list.filter((x) => typeof x === 'string' && x) : [];
|
||||
}
|
||||
const cur = getCurrentVip()?.permissions;
|
||||
return Array.isArray(cur) ? cur.filter((x) => typeof x === 'string' && x) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否拥有指定权限码(全局判断方法)
|
||||
* @param code 权限码,如 VIP_FEATURE.MEDICAL_RECORD
|
||||
* @param vipOrPermissions 挂号侧 vip / permissions;
|
||||
* - 不传:用登录态 userInfo.vip
|
||||
* - 显式传 null:视为无履约诊所权益(不回落登录态,避免接诊未就绪时误放行)
|
||||
*/
|
||||
export function hasVipPermission(
|
||||
code: string,
|
||||
vipOrPermissions?: StoreVipInfo | string[] | null,
|
||||
): boolean {
|
||||
if (!code) return false;
|
||||
let list: string[] | undefined;
|
||||
if (Array.isArray(vipOrPermissions)) {
|
||||
list = vipOrPermissions;
|
||||
} else if (vipOrPermissions && typeof vipOrPermissions === 'object') {
|
||||
list = vipOrPermissions.permissions;
|
||||
} else {
|
||||
list = getCurrentVip()?.permissions;
|
||||
if (arguments.length < 2) {
|
||||
return normalizeVipPermissions().includes(code);
|
||||
}
|
||||
return Array.isArray(list) && list.includes(code);
|
||||
if (vipOrPermissions == null) {
|
||||
return false;
|
||||
}
|
||||
return normalizeVipPermissions(vipOrPermissions).includes(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 等级是否不低于目标(按 level_weight;若无 weight 则仅精确匹配编码时返回 true)
|
||||
* 简化:仅当当前 level_code 与 minCode 相同,或 weight 足够时通过
|
||||
*/
|
||||
export function levelAtLeast(minCode: string, vip?: StoreVipInfo | null): boolean {
|
||||
const current = vip ?? getCurrentVip();
|
||||
@@ -70,11 +111,36 @@ function parseLevelWeight(code: string): number {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** 组合式:供组件调用 */
|
||||
export function useVipGate() {
|
||||
/**
|
||||
* 组合式:基于可选的门店 vip ref 做权限判断(接诊台推荐)
|
||||
* @example
|
||||
* const { has, canUseMedicalRecord } = useVipPermission(receptionVip)
|
||||
* const ok = has(VIP_FEATURE.MEDICAL_RECORD)
|
||||
*/
|
||||
export function useVipPermission(
|
||||
vipSource?: Ref<StoreVipInfo | null | undefined> | ComputedRef<StoreVipInfo | null | undefined>,
|
||||
) {
|
||||
/** 传入了 vipSource 时始终走履约诊所分支(含 null=无权益) */
|
||||
const has = (code: string) => {
|
||||
if (vipSource) {
|
||||
return hasVipPermission(code, vipSource.value ?? null);
|
||||
}
|
||||
return hasVipPermission(code);
|
||||
};
|
||||
return {
|
||||
vip: getCurrentVip(),
|
||||
hasVipPermission,
|
||||
levelAtLeast,
|
||||
vip: computed(() => (vipSource ? vipSource.value : getCurrentVip())),
|
||||
has,
|
||||
hasVipPermission: has,
|
||||
levelAtLeast: (minCode: string) =>
|
||||
levelAtLeast(minCode, vipSource ? vipSource.value ?? null : getCurrentVip()),
|
||||
canUseMedicalRecord: computed(() => has(VIP_FEATURE.MEDICAL_RECORD)),
|
||||
canUseAiMedicalRecord: computed(() => has(VIP_FEATURE.AI_MEDICAL_RECORD)),
|
||||
canUseAiPrescription: computed(() => has(VIP_FEATURE.AI_PRESCRIPTION)),
|
||||
canUseGoldenFormula: computed(() => has(VIP_FEATURE.GOLDEN_FORMULA)),
|
||||
};
|
||||
}
|
||||
|
||||
/** @deprecated 请用 useVipPermission;保留兼容旧调用 */
|
||||
export function useVipGate() {
|
||||
return useVipPermission();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user