/** * 权限检查Hook * * 基于RBAC(基于角色的访问控制)模型,提供细粒度的权限检查功能。 * * 权限键格式:module:resource:action * 例如:prompt_template:create:write * * 使用示例: * ```typescript * const { hasPermission, canCreate, canEdit } = usePermission(); * * // 检查单个权限 * if (hasPermission('prompt_template:create:write')) { * // 显示创建按钮 * } * * // 使用便捷方法 * if (canCreate('prompt_template')) { * // 显示创建按钮 * } * ``` */ import { useRouteLoaderData, useLocation } from "@remix-run/react"; interface RootLoaderData { permissions?: string[]; permissionMap?: Record; // ✅ 新增:权限映射表 userRole: string; userInfo?: { role_id?: number; role_key?: string; role_name?: string; }; } /** * 交叉评查模块默认权限配置 * 当 permissionMap 中没有配置时,使用此默认配置 */ const CROSS_CHECKING_DEFAULT_PERMISSIONS: Record = { '/cross-checking': [ 'cross_review:task:read', // 'cross_review:task:create', // 'cross_review:document:complete', 'cross_review:progress:view', 'cross_review:proposal:create', 'cross_review:proposal:delete', 'cross_review:proposal:read', // 'cross_review:proposal:vote' ], '/cross-checking/upload': [ // 'cross_review:task:create' ], '/cross-checking/result': [ // 'cross_review:document:complete', 'cross_review:progress:view', 'cross_review:proposal:create', 'cross_review:proposal:delete', 'cross_review:proposal:read', // 'cross_review:proposal:vote' ] }; export function usePermission() { const rootData = useRouteLoaderData("root") as RootLoaderData; const location = useLocation(); // 从root loader获取权限映射表 const permissionMap = rootData?.permissionMap || {}; const userRole = rootData?.userRole || 'common'; // 🔑 根据当前路由获取权限列表 const currentPath = location.pathname; // console.log('currentPath', currentPath) // 获取当前路由的权限:优先使用 permissionMap,否则使用交叉评查默认配置 const getCrossCheckingPermissions = (): string[] => { // 检查是否是交叉评查相关路由 if (currentPath.startsWith('/cross-checking')) { // 精确匹配 if (CROSS_CHECKING_DEFAULT_PERMISSIONS[currentPath]) { return CROSS_CHECKING_DEFAULT_PERMISSIONS[currentPath]; } // 处理带参数的路由,如 /cross-checking/result?id=xxx const basePath = currentPath.split('?')[0]; if (CROSS_CHECKING_DEFAULT_PERMISSIONS[basePath]) { return CROSS_CHECKING_DEFAULT_PERMISSIONS[basePath]; } } return []; }; // 优先使用 permissionMap 中的权限,如果没有则使用交叉评查默认权限 const currentPermissions = permissionMap[currentPath]?.length > 0 ? permissionMap[currentPath] : getCrossCheckingPermissions(); // 向后兼容:如果存在旧的permissions数组,也要支持 const legacyPermissions = rootData?.permissions || []; /** * 检查是否有指定权限 * @param permissionKey 权限键,如 "prompt_template:create:write" * @returns boolean */ const hasPermission = (permissionKey: string): boolean => { // 优先使用当前路由的权限列表 if (currentPermissions.length > 0) { return currentPermissions.includes(permissionKey); } // 向后兼容:支持旧的permissions数组 if (legacyPermissions.length > 0) { return legacyPermissions.includes(permissionKey); } // 降级方案:如果没有权限数据,使用userRole判断(兼容现有系统) // 包含'provin'的角色拥有所有权限 if (userRole.toLowerCase().includes('provin')) { return true; } // 默认只有查看权限 if (permissionKey.includes(':read')) { return true; } return false; }; /** * 检查是否有指定路由的权限 * @param path 路由路径,如 "/prompts" * @param permissionKey 权限键 * @returns boolean */ const hasRoutePermission = (path: string, permissionKey: string): boolean => { const routePermissions = permissionMap[path] || []; return routePermissions.includes(permissionKey); }; /** * 获取当前路由的所有权限 * @returns 权限列表 */ const getCurrentPermissions = (): string[] => { return currentPermissions; }; /** * 获取指定路由的所有权限 * @param path 路由路径 * @returns 权限列表 */ const getRoutePermissions = (path: string): string[] => { return permissionMap[path] || []; }; /** * 检查是否有指定模块的任意权限 * @param module 模块名,如 "prompt_template" * @returns boolean */ const hasModulePermission = (module: string): boolean => { if (currentPermissions.length > 0) { return currentPermissions.some(p => p.startsWith(`${module}:`)); } if (legacyPermissions.length > 0) { return legacyPermissions.some(p => p.startsWith(`${module}:`)); } // 降级方案 return userRole.toLowerCase().includes('provin'); }; /** * 检查是否有指定资源和动作的权限 * @param module 模块名,如 "prompt_template" * @param resource 资源名,如 "create", "list", "detail" * @param action 动作,如 "read", "write", "delete" * @returns boolean */ const hasResourcePermission = (module: string, resource: string, action: string): boolean => { const permissionKey = `${module}:${resource}:${action}`; return hasPermission(permissionKey); }; /** * 批量检查权限(需要全部满足) * @param permissionKeys 权限键数组 * @returns boolean */ const hasAllPermissions = (permissionKeys: string[]): boolean => { return permissionKeys.every(key => hasPermission(key)); }; /** * 批量检查权限(满足任意一个即可) * @param permissionKeys 权限键数组 * @returns boolean */ const hasAnyPermission = (permissionKeys: string[]): boolean => { return permissionKeys.some(key => hasPermission(key)); }; // 便捷方法:检查常见操作权限 const canCreate = (module: string): boolean => { return hasResourcePermission(module, 'create', 'write'); }; const canRead = (module: string, resource: string = 'list'): boolean => { return hasResourcePermission(module, resource, 'read'); }; const canUpdate = (module: string): boolean => { return hasResourcePermission(module, 'update', 'write'); }; const canDelete = (module: string): boolean => { return hasResourcePermission(module, 'delete', 'delete'); }; const canList = (module: string): boolean => { return hasResourcePermission(module, 'list', 'read'); }; const canView = (module: string): boolean => { return hasResourcePermission(module, 'detail', 'read'); }; /** * 检查是否有批量操作权限 * @param module 模块名,如 "evaluation_group" * @returns boolean - 检查是否有 module:batch:write 权限 */ const canBatch = (module: string): boolean => { return hasResourcePermission(module, 'batch', 'write'); }; return { // 原始权限数据 permissions: currentPermissions, // ✅ 返回当前路由的权限 permissionMap, // ✅ 返回完整的权限映射表 userRole, // 基础检查方法 hasPermission, hasModulePermission, hasResourcePermission, hasAllPermissions, hasAnyPermission, // ✅ 新增:路由权限查询方法 hasRoutePermission, getCurrentPermissions, getRoutePermissions, // 便捷方法 canCreate, canRead, canUpdate, canDelete, canList, canView, canBatch // ✅ 新增:批量操作权限检查 }; } /** * 权限组件包装器 * * 根据权限控制子组件的显示/隐藏 * * 使用示例: * ```typescript * * * * ``` */ interface PermissionGuardProps { permission?: string; permissions?: string[]; requireAll?: boolean; // true=需要全部权限,false=任意一个即可 fallback?: React.ReactNode; // 无权限时显示的内容 children: React.ReactNode; } export function PermissionGuard({ permission, permissions: permissionList, requireAll = false, fallback = null, children }: PermissionGuardProps) { const { hasPermission, hasAllPermissions, hasAnyPermission } = usePermission(); let hasAccess = false; if (permission) { // 单个权限检查 hasAccess = hasPermission(permission); } else if (permissionList && permissionList.length > 0) { // 多个权限检查 hasAccess = requireAll ? hasAllPermissions(permissionList) : hasAnyPermission(permissionList); } else { // 没有指定权限,默认允许访问 hasAccess = true; } if (!hasAccess) { return <>{fallback}; } return <>{children}; }