import React, { useState, useEffect } from 'react'; import { Shield, Plus, Save, X, Edit2, Trash2, Loader2, Check, Users, Key, } from 'lucide-react'; import { useAuth } from '../../src/contexts/AuthContext'; import { useConfirm } from '../../contexts/ConfirmContext'; import { useToast } from '../../contexts/ToastContext'; import { useLanguage } from '../../contexts/LanguageContext'; import { cn } from '../../src/utils/cn'; interface PermissionMeta { key: string; category: string; label: string; description: string; } interface Role { id: string; name: string; description?: string; isSystem: boolean; baseRole?: string; tenantId?: string; } /** 按分类分组的权限 */ interface PermissionsByCategory { [category: string]: PermissionMeta[]; } export const PermissionSettingsView: React.FC = () => { const { apiKey, activeTenant } = useAuth(); const { confirm } = useConfirm(); const { showError, showSuccess } = useToast(); const { t } = useLanguage(); const [roles, setRoles] = useState([]); const [allPermissions, setAllPermissions] = useState({}); const [selectedRoleId, setSelectedRoleId] = useState(null); const [rolePermissions, setRolePermissions] = useState>(new Set()); const [isLoadingRoles, setIsLoadingRoles] = useState(true); const [isLoadingPerms, setIsLoadingPerms] = useState(false); const [isSaving, setIsSaving] = useState(false); const [showCreateRole, setShowCreateRole] = useState(false); const [newRoleName, setNewRoleName] = useState(''); const [newRoleDesc, setNewRoleDesc] = useState(''); const [editingPermissions, setEditingPermissions] = useState>(new Set()); const [hasChanges, setHasChanges] = useState(false); const headers = { 'x-api-key': apiKey, 'x-tenant-id': activeTenant?.tenantId || '', }; // 加载角色列表 const fetchRoles = async () => { try { setIsLoadingRoles(true); const res = await fetch('/api/roles', { headers }); if (res.ok) { const data = await res.json(); setRoles(data); } } catch (err: any) { console.error('Failed to fetch roles:', err); } finally { setIsLoadingRoles(false); } }; // 加载权限元数据 const fetchPermissions = async () => { try { const res = await fetch('/api/permissions', { headers }); if (res.ok) { const data = await res.json(); setAllPermissions(data); } } catch (err: any) { console.error('Failed to fetch permissions:', err); } }; // 加载选中角色的权限 const fetchRolePermissions = async (roleId: string) => { try { setIsLoadingPerms(true); const res = await fetch(`/api/roles/${roleId}/permissions`, { headers }); if (res.ok) { const data = await res.json(); const permSet = new Set(data.permissions || []); setRolePermissions(permSet); setEditingPermissions(new Set(permSet)); } } catch (err: any) { console.error('Failed to fetch role permissions:', err); } finally { setIsLoadingPerms(false); } }; useEffect(() => { fetchRoles(); fetchPermissions(); }, [apiKey, activeTenant]); useEffect(() => { if (selectedRoleId) { fetchRolePermissions(selectedRoleId); } }, [selectedRoleId]); // 切换权限 const togglePermission = (key: string) => { setEditingPermissions(prev => { const next = new Set(prev); if (next.has(key)) { next.delete(key); } else { next.add(key); } setHasChanges(true); return next; }); }; // 全选/取消分类 const toggleCategory = (category: string, perms: PermissionMeta[]) => { const keys = perms.map(p => p.key); const allChecked = keys.every(k => editingPermissions.has(k)); setEditingPermissions(prev => { const next = new Set(prev); keys.forEach(k => { if (allChecked) next.delete(k); else next.add(k); }); setHasChanges(true); return next; }); }; // 保存权限 const savePermissions = async () => { if (!selectedRoleId) return; try { setIsSaving(true); const res = await fetch(`/api/roles/${selectedRoleId}/permissions`, { method: 'PUT', headers: { ...headers, 'Content-Type': 'application/json' }, body: JSON.stringify({ permissions: [...editingPermissions] }), }); if (res.ok) { setRolePermissions(new Set(editingPermissions)); setHasChanges(false); showSuccess?.('权限已保存'); } else { const err = await res.json(); throw new Error(err.message || '保存失败'); } } catch (err: any) { showError?.(err.message); } finally { setIsSaving(false); } }; // 创建角色 const createRole = async () => { if (!newRoleName.trim()) return; try { const res = await fetch('/api/roles', { method: 'POST', headers: { ...headers, 'Content-Type': 'application/json' }, body: JSON.stringify({ name: newRoleName.trim(), description: newRoleDesc.trim() }), }); if (res.ok) { await fetchRoles(); setShowCreateRole(false); setNewRoleName(''); setNewRoleDesc(''); showSuccess?.('角色已创建'); } else { const err = await res.json(); throw new Error(err.message || '创建失败'); } } catch (err: any) { showError?.(err.message); } }; // 删除角色 const deleteRole = async (role: Role) => { const confirmed = await confirm?.(`确定删除角色"${role.name}"?`); if (!confirmed) return; try { const res = await fetch(`/api/roles/${role.id}`, { method: 'DELETE', headers }); if (res.ok) { if (selectedRoleId === role.id) setSelectedRoleId(null); await fetchRoles(); showSuccess?.('角色已删除'); } else { const err = await res.json(); throw new Error(err.message || '删除失败'); } } catch (err: any) { showError?.(err.message); } }; const selectedRole = roles.find(r => r.id === selectedRoleId); return (
{/* 左:角色列表 */}

角色

{isLoadingRoles ? (
) : (
{roles.map(role => ( )} ))}
)} {/* 创建角色弹窗 */} {showCreateRole && (
setNewRoleName(e.target.value)} placeholder="角色名称" className="w-full px-3 py-2 text-sm bg-white border border-slate-200 rounded-lg focus:ring-2 focus:ring-indigo-500/20 focus:border-indigo-500 outline-none" /> setNewRoleDesc(e.target.value)} placeholder="角色描述(可选)" className="w-full px-3 py-2 text-sm bg-white border border-slate-200 rounded-lg focus:ring-2 focus:ring-indigo-500/20 focus:border-indigo-500 outline-none" />
)}
{/* 右:权限矩阵 */}
{!selectedRole ? (

选择一个角色查看权限

) : isLoadingPerms ? (
) : (
{/* 角色标题 */}

{selectedRole.name} {selectedRole.isSystem && ( 系统角色 )}

{selectedRole.description && (

{selectedRole.description}

)}
{selectedRole.isSystem && (
系统角色的权限不可修改。可以创建自定义角色,然后分配所需权限。
)} {/* 权限矩阵 */}
{Object.entries(allPermissions).map(([category, perms]) => { const allChecked = perms.every(p => editingPermissions.has(p.key)); const someChecked = perms.some(p => editingPermissions.has(p.key)); return (
{/* 分类标题 */} {/* 权限列表 */}
{perms.map(perm => ( ))}
); })}
)}
); };