- shadcn/ui 标准化:废弃纸感令牌,统一 bg-background/text-foreground 等 - Tailwind v4 + @theme inline,移除 tailwind.config.js - React 19 use() + Suspense 流式渲染,首屏骨架秒出 - 三级错误边界:Route → Section → Widget 层层兜底 - 错误上报:useErrorReport → sendBeacon → /api/log mock 端点 - 三层安全边界:L1 角色门禁 / L2 权限点门禁 / L3 数据范围 - 权限位图 base36 压缩:67 权限点 → ~14 字符,JWT 体积减少 ≥ 99% - notify 统一 Toast 封装,禁止业务直接 import sonner - PluginBoundary 替代 PluginLoader(错误边界 + Suspense + Skeleton 三件套) 验证:typecheck 0 错误 / lint 0 错误 / build 6 路由生成成功
77 lines
2.3 KiB
TypeScript
77 lines
2.3 KiB
TypeScript
import { useCallback, useMemo } from "react";
|
||
import type { PermissionContext } from "./types";
|
||
|
||
/**
|
||
* usePermission - 权限查询 Hook
|
||
*
|
||
* 用途:L2 路由级 + L3 组件级视口控制。
|
||
* 权限点命名遵循 F7 裁决:`<RESOURCE>_<ACTION>`(如 `EXAM_READ`)。
|
||
* 数据范围用后缀 `_OWN`/`_CHILD`(如 `GRADE_READ_CHILD`)。
|
||
*
|
||
* 注意:权限数据由调用方注入(apps/* 从 iam /iam/permissions/effective 获取后传入)。
|
||
* 本 hook 不直接调 API,保持纯客户端逻辑。
|
||
*
|
||
* @example
|
||
* const { hasPermission, hasAny, hasAll } = usePermission({ permissions, dataScope, roles });
|
||
*
|
||
* if (hasPermission("EXAM_CREATE")) { ... }
|
||
* if (hasPermission("GRADE_READ_CHILD")) { ... } // 数据范围后缀
|
||
*/
|
||
|
||
export interface UsePermissionProps {
|
||
/** 权限上下文(从 iam /iam/permissions/effective 获取) */
|
||
context: PermissionContext | null;
|
||
}
|
||
|
||
export interface UsePermissionReturn {
|
||
/** 检查是否拥有指定权限点 */
|
||
hasPermission: (perm: string) => boolean;
|
||
/** 检查是否拥有任一权限点 */
|
||
hasAny: (...perms: string[]) => boolean;
|
||
/** 检查是否拥有全部权限点 */
|
||
hasAll: (...perms: string[]) => boolean;
|
||
/** 检查是否拥有指定角色 */
|
||
hasRole: (role: string) => boolean;
|
||
/** 当前数据范围 */
|
||
dataScope: PermissionContext["dataScope"] | null;
|
||
}
|
||
|
||
export function usePermission({
|
||
context,
|
||
}: UsePermissionProps): UsePermissionReturn {
|
||
const permissions = context?.permissions ?? [];
|
||
const roles = context?.roles ?? [];
|
||
const dataScope = context?.dataScope ?? null;
|
||
|
||
const permissionSet = useMemo(() => new Set(permissions), [permissions]);
|
||
const roleSet = useMemo(() => new Set(roles), [roles]);
|
||
|
||
const hasPermission = useCallback(
|
||
(perm: string): boolean => permissionSet.has(perm),
|
||
[permissionSet],
|
||
);
|
||
|
||
const hasAny = useCallback(
|
||
(...perms: string[]): boolean => perms.some((p) => permissionSet.has(p)),
|
||
[permissionSet],
|
||
);
|
||
|
||
const hasAll = useCallback(
|
||
(...perms: string[]): boolean => perms.every((p) => permissionSet.has(p)),
|
||
[permissionSet],
|
||
);
|
||
|
||
const hasRole = useCallback(
|
||
(role: string): boolean => roleSet.has(role),
|
||
[roleSet],
|
||
);
|
||
|
||
return {
|
||
hasPermission,
|
||
hasAny,
|
||
hasAll,
|
||
hasRole,
|
||
dataScope,
|
||
};
|
||
}
|