|
|
@@ -0,0 +1,88 @@
|
|
|
+import { pinyin } from 'pinyin-pro';
|
|
|
+
|
|
|
+// ---- 类型 ----
|
|
|
+export interface PermGroup {
|
|
|
+ modelName: string;
|
|
|
+ apiPerms: API.PermItem[];
|
|
|
+ dataPerms: API.PermItem[]; // data:model:action(非字段)
|
|
|
+ fieldPerms: API.PermItem[]; // data:model:field:read|write
|
|
|
+}
|
|
|
+
|
|
|
+// ---- 排序 ----
|
|
|
+export const sortPerms = (perms: API.PermItem[]): API.PermItem[] =>
|
|
|
+ [...perms].sort((a, b) => {
|
|
|
+ const partsA = a.code.split(':');
|
|
|
+ const partsB = b.code.split(':');
|
|
|
+ const modelA = partsA[1] ?? '';
|
|
|
+ const modelB = partsB[1] ?? '';
|
|
|
+ if (modelA !== modelB) return modelA.localeCompare(modelB);
|
|
|
+ const isFieldA = partsA.length === 4;
|
|
|
+ const isFieldB = partsB.length === 4;
|
|
|
+ if (isFieldA !== isFieldB) return isFieldA ? 1 : -1;
|
|
|
+ return a.code.localeCompare(b.code);
|
|
|
+ });
|
|
|
+
|
|
|
+// ---- 分组 ----
|
|
|
+export const groupPerms = (perms: API.PermItem[]): PermGroup[] => {
|
|
|
+ const map = new Map<string, PermGroup>();
|
|
|
+ for (const p of perms) {
|
|
|
+ const parts = p.code.split(':');
|
|
|
+ const type = parts[0]; // api | data
|
|
|
+ const model = parts[1] ?? 'unknown';
|
|
|
+ if (!map.has(model)) {
|
|
|
+ map.set(model, { modelName: model, apiPerms: [], dataPerms: [], fieldPerms: [] });
|
|
|
+ }
|
|
|
+ const g = map.get(model)!;
|
|
|
+ if (type === 'api') {
|
|
|
+ g.apiPerms.push(p);
|
|
|
+ } else if (parts.length === 4) {
|
|
|
+ g.fieldPerms.push(p);
|
|
|
+ } else {
|
|
|
+ g.dataPerms.push(p);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return [...map.values()].sort((a, b) => a.modelName.localeCompare(b.modelName));
|
|
|
+};
|
|
|
+
|
|
|
+// ---- 搜索 ----
|
|
|
+const getPinyinStr = (text: string) => pinyin(text, { toneType: 'none', type: 'array' }).join('');
|
|
|
+
|
|
|
+const getPinyinInitials = (text: string) =>
|
|
|
+ pinyin(text, { pattern: 'first', toneType: 'none', type: 'array' }).join('');
|
|
|
+
|
|
|
+export const matchesPerm = (perm: API.PermItem, keyword: string): boolean => {
|
|
|
+ if (!keyword) return true;
|
|
|
+ const k = keyword.toLowerCase();
|
|
|
+ const code = perm.code.toLowerCase();
|
|
|
+ const name = perm.name.toLowerCase();
|
|
|
+ const namePy = getPinyinStr(perm.name).toLowerCase();
|
|
|
+ const nameInitials = getPinyinInitials(perm.name).toLowerCase();
|
|
|
+ return code.includes(k) || name.includes(k) || namePy.includes(k) || nameInitials.includes(k);
|
|
|
+};
|
|
|
+
|
|
|
+// ---- 用户权限 delta 计算 ----
|
|
|
+export const calcUserPermDelta = (
|
|
|
+ checkedIds: number[],
|
|
|
+ roleInheritedIds: number[],
|
|
|
+): API.UserPermItem[] => {
|
|
|
+ const checked = new Set(checkedIds);
|
|
|
+ const inherited = new Set(roleInheritedIds);
|
|
|
+ return [
|
|
|
+ ...checkedIds
|
|
|
+ .filter((id) => !inherited.has(id))
|
|
|
+ .map((id) => ({ permId: id, effect: 'ALLOW' as const })),
|
|
|
+ ...[...inherited]
|
|
|
+ .filter((id) => !checked.has(id))
|
|
|
+ .map((id) => ({ permId: id, effect: 'DENY' as const })),
|
|
|
+ ];
|
|
|
+};
|
|
|
+
|
|
|
+// ---- 获取权限对应的 pair id ----
|
|
|
+// api:model:action <-> data:model:action 互查
|
|
|
+export const getPairId = (perm: API.PermItem, allPerms: API.PermItem[]): number | undefined => {
|
|
|
+ const parts = perm.code.split(':');
|
|
|
+ if (parts.length !== 3) return undefined;
|
|
|
+ const [type, model, action] = parts;
|
|
|
+ const pairType = type === 'api' ? 'data' : 'api';
|
|
|
+ return allPerms.find((p) => p.code === `${pairType}:${model}:${action}`)?.id;
|
|
|
+};
|