Files
aurak/web/src/hooks/usePermissions.ts
T
Developer ba33d517c1 feat: 分层 RBAC 权限管理系统
后端:
- 新增 Role / RolePermission 实体(自动 seed 系统角色)
- PermissionService——通过 isAdmin / TenantMember 链路解析用户权限
- @Permission() 装饰器 + PermissionsGuard 守卫
- /api/permissions 和 /api/roles REST API
- UserController 内联 role 检查迁移到 @Permission()
- PermissionModule 全局注册

前端:
- usePermissions hook——获取当前用户权限集
- PermissionGate 组件级门控
- PermissionSettingsView——角色列表+权限矩阵编辑页面
- SettingsView 新增「权限管理」Tab(仅 admin 可见)
- 权限预览(26 项,7 分类)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 23:25:22 +08:00

87 lines
2.1 KiB
TypeScript

import { useState, useEffect, useCallback } from 'react';
import { useAuth } from '../contexts/AuthContext';
/**
* 前端权限 hook
* 获取当前用户在活动租户下的权限集,提供便捷的检查方法
*
* @example
* ```tsx
* const { hasPermission, hasAnyPermission, isLoading } = usePermissions();
*
* if (hasPermission('user:create')) {
* // 渲染创建用户按钮
* }
*
* {hasAnyPermission('user:edit', 'user:delete') && <AdminActions />}
* ```
*/
export function usePermissions() {
const { apiKey, activeTenant } = useAuth();
const [permissions, setPermissions] = useState<string[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchPermissions = useCallback(async () => {
if (!apiKey || !activeTenant) {
setPermissions([]);
setIsLoading(false);
return;
}
try {
setIsLoading(true);
const res = await fetch('/api/permissions/mine', {
headers: {
'x-api-key': apiKey,
'x-tenant-id': activeTenant.tenantId,
},
});
if (res.ok) {
const data = await res.json();
setPermissions(data.permissions || []);
setError(null);
} else {
setPermissions([]);
}
} catch (err: any) {
console.error('Failed to fetch permissions:', err);
setError(err.message);
setPermissions([]);
} finally {
setIsLoading(false);
}
}, [apiKey, activeTenant?.tenantId]);
// 获取权限
useEffect(() => {
fetchPermissions();
}, [fetchPermissions]);
const hasPermission = useCallback(
(key: string) => permissions.includes(key),
[permissions],
);
const hasAnyPermission = useCallback(
(...keys: string[]) => keys.some(k => permissions.includes(k)),
[permissions],
);
const hasAllPermissions = useCallback(
(...keys: string[]) => keys.every(k => permissions.includes(k)),
[permissions],
);
return {
permissions,
isLoading,
error,
hasPermission,
hasAnyPermission,
hasAllPermissions,
refresh: fetchPermissions,
};
}