ba33d517c1
后端: - 新增 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>
87 lines
2.1 KiB
TypeScript
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,
|
|
};
|
|
}
|