diff --git a/web/src/components/admin/AdminSettingsModal.tsx b/web/src/components/admin/AdminSettingsModal.tsx new file mode 100644 index 00000000..21343c62 --- /dev/null +++ b/web/src/components/admin/AdminSettingsModal.tsx @@ -0,0 +1,1066 @@ +import React, { useState, useEffect } from 'react'; +import { + ShieldAlert, + Users, + UserCheck, + ToggleLeft, + ToggleRight, + Shield, + Server, + Plus, + Lock, + X, + Check, + Ban, + CheckCircle2, + AlertCircle, + Settings2, + Info, +} from 'lucide-react'; +import { UserProfile } from '../../types/docgov'; +import { RoleDefinition, PermissionKey } from '../../types/rbac'; +import { UserAccount, SystemAuthConfig } from '../../types/auth'; +import { authClient } from '../../api/auth'; + +export interface AdminSettingsModalProps { + isOpen: boolean; + onClose: () => void; + currentUser: UserProfile; + roles: RoleDefinition[]; + onSaveRoles: (roles: RoleDefinition[]) => void; + allUsers: UserProfile[]; + onRefreshUsers?: () => void; + env?: string; +} + +const ALL_PERMISSION_ITEMS: { key: PermissionKey; name: string; desc: string }[] = [ + { key: 'project:create', name: '创建新项目/立项', desc: '具备在协同工作台开启全新研发项目并指派阶段的权限' }, + { key: 'project:assign_lead', name: '设置文档责任人', desc: '在文档治理面板为交付物指派编制人、审核人与批准人' }, + { key: 'perm:manage', name: '系统权限与角色管理', desc: '增删改全局职位角色定义与权限矩阵' }, + { key: 'workflow:config_visibility', name: '配置工作流默认可见性', desc: '调整业务阶段中各受控交付物的基线查阅准入边界' }, + { key: 'doc:override_visibility', name: '配置单文档可见性覆盖', desc: '针对特定核心机密交付物设定独占白名单或责任人收紧' }, + { key: 'doc:export', name: '受控交付物导出', desc: '允许将医疗受控文档编译导出为受保护的 DOCX 或 XLSX' }, + { key: 'workflow:view_all', name: '默认查阅常规工作流文档', desc: '允许在全景大盘中查阅已就绪的常规项目协作文档' }, +]; + +export const AdminSettingsModal: React.FC = ({ + isOpen, + onClose, + currentUser, + roles, + onSaveRoles, + allUsers: _allUsers, + onRefreshUsers, + env = 'dev', +}) => { + const [activeTab, setActiveTab] = useState<'users' | 'approvals' | 'policy' | 'rbac' | 'system'>('users'); + + // Accounts state + const [userAccounts, setUserAccounts] = useState([]); + const [loadingUsers, setLoadingUsers] = useState(false); + const [feedbackTip, setFeedbackTip] = useState<{ type: 'success' | 'error'; msg: string } | null>(null); + + // System auth config state + const [systemConfig, setSystemConfig] = useState({ + allowRegistration: true, + requireApproval: false, + }); + + // Create User modal + const [showCreateModal, setShowCreateModal] = useState(false); + const [newUsername, setNewUsername] = useState(''); + const [newName, setNewName] = useState(''); + const [newInitialPassword, setNewInitialPassword] = useState('123456'); + const [newRoleId, setNewRoleId] = useState('role-engineer'); + const [newDepartment, setNewDepartment] = useState('研发中心'); + const [newEmployeeId, setNewEmployeeId] = useState(''); + + // RBAC dynamic role editing state + const [editRoles, setEditRoles] = useState(roles); + const [selectedRoleId, setSelectedRoleId] = useState(roles[0]?.id || 'role-admin'); + const [showNewRoleDialog, setShowNewRoleDialog] = useState(false); + const [newRoleName, setNewRoleName] = useState(''); + const [newRoleCode, setNewRoleCode] = useState(''); + const [newRoleDesc, setNewRoleDesc] = useState(''); + + // 1. 数据装载 + const loadAccounts = async () => { + setLoadingUsers(true); + try { + const list = await authClient.getAdminUsers(); + setUserAccounts(list); + const cfg = await authClient.getSystemAuthConfig(); + setSystemConfig(cfg); + } catch (e: any) { + console.warn('Failed to load admin accounts:', e); + } finally { + setLoadingUsers(false); + } + }; + + useEffect(() => { + if (isOpen) { + loadAccounts(); + setEditRoles(roles); + } + }, [isOpen, roles]); + + if (!isOpen) return null; + + // 严格入口防线:仅系统管理员独占访问 + const isAdmin = currentUser.roleId === 'role-admin' || currentUser.name.includes('管理员'); + if (!isAdmin) { + return ( +
+
+
+ +
+

403 访问被拒绝

+

+ 当前功能为系统管理控制台,仅系统管理员 (role-admin) 具备访问权限。 +

+ +
+
+ ); + } + + const showTip = (type: 'success' | 'error', msg: string) => { + setFeedbackTip({ type, msg }); + setTimeout(() => setFeedbackTip(null), 3000); + }; + + // 职位分配 + const handleAssignRole = async (targetUser: UserAccount, targetRoleId: string) => { + const roleDef = roles.find((r) => r.id === targetRoleId); + const positionName = roleDef ? roleDef.name : '方案工程师'; + try { + await authClient.updateUserRole(targetUser.id, targetRoleId, positionName); + await loadAccounts(); + onRefreshUsers?.(); + showTip('success', `已将 ${targetUser.name} 的职位更新为【${positionName}】`); + } catch (e: any) { + showTip('error', e.message || '更新职位失败'); + } + }; + + // 审批通过并指派职位 + const handleApproveUser = async (targetUser: UserAccount, targetRoleId: string) => { + const roleDef = roles.find((r) => r.id === targetRoleId); + const positionName = roleDef ? roleDef.name : '方案工程师'; + try { + await authClient.updateUserStatus(targetUser.id, 'active', targetRoleId, positionName); + await loadAccounts(); + onRefreshUsers?.(); + showTip('success', `已批准 ${targetUser.name} 入职并指派职位【${positionName}】`); + } catch (e: any) { + showTip('error', e.message || '审批处理失败'); + } + }; + + // 停用/启用账号 + const handleToggleUserStatus = async (targetUser: UserAccount) => { + const nextStatus = targetUser.status === 'active' ? 'disabled' : 'active'; + try { + await authClient.updateUserStatus(targetUser.id, nextStatus); + await loadAccounts(); + onRefreshUsers?.(); + showTip('success', `已将账号 ${targetUser.name} 设置为 ${nextStatus === 'active' ? '正常' : '已停用'}`); + } catch (e: any) { + showTip('error', e.message || '切换状态失败'); + } + }; + + // 管理员新建账号 + const handleCreateUser = async (e: React.FormEvent) => { + e.preventDefault(); + if (!newUsername.trim() || !newName.trim() || !newInitialPassword.trim()) { + showTip('error', '请填写必填字段'); + return; + } + const roleDef = roles.find((r) => r.id === newRoleId); + const positionName = roleDef ? roleDef.name : '方案工程师'; + + try { + await authClient.createAdminUser({ + username: newUsername.trim(), + name: newName.trim(), + initialPassword: newInitialPassword.trim(), + roleId: newRoleId, + position: positionName, + department: newDepartment.trim() || undefined, + employeeId: newEmployeeId.trim() || undefined, + }); + setShowCreateModal(false); + setNewUsername(''); + setNewName(''); + setNewInitialPassword('123456'); + setNewEmployeeId(''); + await loadAccounts(); + onRefreshUsers?.(); + showTip('success', `新员工账号创建成功!已标记为【首次登录必须改密】。`); + } catch (e: any) { + showTip('error', e.message || '创建账号失败'); + } + }; + + // 准入策略切换 + const handleTogglePolicy = async (key: keyof SystemAuthConfig) => { + const updated = { ...systemConfig, [key]: !systemConfig[key] }; + try { + await authClient.updateSystemAuthConfig(updated); + setSystemConfig(updated); + showTip('success', '准入治理策略已实时生效'); + } catch (e: any) { + showTip('error', e.message || '更新策略失败'); + } + }; + + // RBAC 权限勾选 + const handleTogglePermission = (roleId: string, permKey: PermissionKey) => { + setEditRoles((prev) => + prev.map((r) => { + if (r.id !== roleId) return r; + const exists = r.permissions.includes(permKey); + const nextPerms = exists + ? r.permissions.filter((p) => p !== permKey) + : [...r.permissions, permKey]; + return { ...r, permissions: nextPerms, updatedAt: new Date().toISOString() }; + }) + ); + }; + + // 保存 RBAC 角色 + const handleSaveRBAC = () => { + onSaveRoles(editRoles); + showTip('success', '全局角色与权限矩阵已保存并实时广播!'); + }; + + // 待审批列表 + const pendingUsers = userAccounts.filter((u) => u.status === 'pending_approval'); + + const selectedRole = editRoles.find((r) => r.id === selectedRoleId) || editRoles[0]; + + return ( +
+
+ {/* 顶部标题栏 */} +
+
+
+ +
+
+
+

系统管理与治理控制台

+ + 管理员特权独占 + + + ENV: {env.toUpperCase()} + +
+

+ 人员账号审核、受控职位指派、准入开关及全局 RBAC 权限矩阵 +

+
+
+ +
+ {feedbackTip && ( +
+ {feedbackTip.type === 'success' ? ( + + ) : ( + + )} + {feedbackTip.msg} +
+ )} + + +
+
+ + {/* 主体布局 */} +
+ {/* 左侧导航 Tab */} +
+ + + + + + + + + +
+ + {/* 右侧主工作区 */} +
+ {/* 1. 人员与职位治理 */} + {activeTab === 'users' && ( +
+
+
+

研发中心全员账号与职位指派

+

+ 职位对应系统角色,决定其在研发协同工作台中的立项、文档责任人分配与受控操作权限 +

+
+ +
+ + {loadingUsers ? ( +
正在同步系统用户列表...
+ ) : ( +
+ + + + + + + + + + + + {userAccounts.map((u) => { + const isSuperAdmin = u.username === 'admin' || u.roleId === 'role-admin'; + return ( + + + + + + + + + + + + ); + })} + +
姓名 / 用户名部门 / 工号受控职位 (指派角色)账号状态管理操作
+
+ {u.name} + {u.mustChangePassword && ( + + 待首次改密 + + )} +
+
{u.username}
+
+
{u.department || '未录入'}
+
{u.employeeId || '-'}
+
+ {isSuperAdmin ? ( + + 系统管理员 + + ) : ( + + )} + + {u.status === 'active' && ( + + 正常 + + )} + {u.status === 'pending_approval' && ( + + 待审核 + + )} + {u.status === 'disabled' && ( + + 已停用 + + )} + + {!isSuperAdmin && ( +
+ +
+ )} +
+
+ )} +
+ )} + + {/* 2. 注册审批中心 */} + {activeTab === 'approvals' && ( +
+
+

新注册员工审核与职位指派

+

+ 当系统准入策略开启“新员工需管理员审核”时,自主注册的申请人将在此汇聚,待指派正式职位后激活 +

+
+ + {pendingUsers.length === 0 ? ( +
+ +

当前没有待审核的注册申请

+

+ 所有注册员工均已审核激活,或当前准入策略已设置为自动激活 +

+
+ ) : ( +
+ {pendingUsers.map((pu) => { + return ( +
+
+
+ {pu.name} + ({pu.username}) + + 待审申请 + +
+
+ 部门: {pu.department || '未填写'} + 工号: {pu.employeeId || '未填写'} + 申请时间: {pu.createdAt || '近期'} +
+
+ +
+
+ 指派职位: + +
+ + + + +
+
+ ); + })} +
+ )} +
+ )} + + {/* 3. 系统准入策略 */} + {activeTab === 'policy' && ( +
+
+

系统账号准入与注册安全策略

+

+ 控制外部人员能否自行在登录界面注册,以及新用户是否必须经过管理员审批与受控职位授权 +

+
+ +
+ {/* 开关 1: 开放自主注册 */} +
+
+
+ 允许员工自主注册账号 + + {systemConfig.allowRegistration ? '已开启' : '已暂停'} + +
+

+ 关闭后,登录主页将彻底隐藏注册入口,仅允许系统管理员在后台手动添加开通账号。 +

+
+ + +
+ + {/* 开关 2: 注册需审批 */} +
+
+
+ 新注册账号必须经过管理员审批 + + {systemConfig.requireApproval ? '强制审批' : '免审激活'} + +
+

+ 开启后,自主注册的账号处于待审核状态无法登录,必须由管理员在“新用户注册审核”中心指派职位并激活。 +

+
+ + +
+
+
+ )} + + {/* 4. 全局角色与权限矩阵 (RBAC) */} + {activeTab === 'rbac' && ( +
+
+
+

全局动态角色与 7 项原子权限矩阵

+

+ 支持动态增删角色,定制各职位默认拥有的全局业务权限能力 +

+
+
+ + +
+
+ + {/* 角色卡片选择 */} +
+ {editRoles.map((r) => { + const isSelected = r.id === selectedRoleId; + return ( +
setSelectedRoleId(r.id)} + className={`p-3 rounded-xl border cursor-pointer transition ${ + isSelected + ? 'bg-amber-500/10 border-amber-500/50 text-white shadow-sm' + : 'bg-slate-950/60 border-slate-800 text-slate-400 hover:border-slate-700' + }`} + > +
+ {r.name} + {r.isSystem && ( + + 内置 + + )} +
+
{r.code}
+

{r.description}

+
+ ); + })} +
+ + {/* 选定角色的权限勾选矩阵 */} + {selectedRole && ( +
+
+
+ + 配置【{selectedRole.name}】的授权项 + + + (已选 {selectedRole.permissions.length} 项) + +
+ {selectedRole.isSystem && selectedRole.code === 'ADMIN' && ( + + 系统管理员拥有全部底层通配权限 + + )} +
+ +
+ {ALL_PERMISSION_ITEMS.map((perm) => { + const checked = + selectedRole.code === 'ADMIN' || selectedRole.permissions.includes(perm.key); + const disabled = selectedRole.code === 'ADMIN'; + + return ( + + ); + })} +
+
+ )} +
+ )} + + {/* 5. 环境与运行状态 */} + {activeTab === 'system' && ( +
+
+

系统运行架构与环境隔离状态

+

+ 监控开发、测试与生产环境三套隔离状态,以及知识库共享与大模型后端运行健康度 +

+
+ +
+
+ + + 运行环境 (ENV) + +
{env.toUpperCase()}
+

+ {env === 'dev' + ? '开发环境:支持测试人员调试快捷身份切换,数据隔离在 data/dev' + : '测试/生产环境:严格禁用身份切换,仅允许真实登出与登录'} +

+
+ +
+ + + 超级管理员凭据 (.env) + +
+ ADMIN_USERNAME: admin +
+
+ ADMIN_PASSWORD: •••••••••••••• +
+

+ 系统管理员凭据严格从运行环境环境变量动态注入,具备防死锁兜底 +

+
+
+ +
+ 跨环境隔离合规准则摘要 +
+

• 知识库共享:共享合规法规数据库(端口 5432,数据库 regulations)。

+

• 对话与业务库隔离(红线):各环境对话历史、草稿与受控文档库物理隔离在各自目录,互不可见。

+

• 身份切换安全隔离(红线):真实测试(test)与发布(main)环境禁止一切快捷身份切换,必须凭密码登录。

+
+
+
+ )} +
+
+
+ + {/* 新建员工账号弹窗 */} + {showCreateModal && ( +
+
+
+

+ + 管理员开通新员工账号 +

+ +
+ +
+
+
+ + setNewName(e.target.value)} + placeholder="例如: 赵工" + className="w-full bg-slate-950 border border-slate-800 rounded-xl px-3 py-1.5 text-xs text-slate-100 focus:outline-none focus:border-amber-500" + /> +
+
+ + setNewUsername(e.target.value)} + placeholder="如: zhaogong" + className="w-full bg-slate-950 border border-slate-800 rounded-xl px-3 py-1.5 text-xs text-slate-100 focus:outline-none focus:border-amber-500" + /> +
+
+ +
+ + setNewInitialPassword(e.target.value)} + placeholder="默认 123456" + className="w-full bg-slate-950 border border-slate-800 rounded-xl px-3 py-1.5 text-xs text-slate-100 focus:outline-none focus:border-amber-500 font-mono" + /> +
+ +
+ + +
+ +
+
+ + setNewDepartment(e.target.value)} + placeholder="如: 研发中心" + className="w-full bg-slate-950 border border-slate-800 rounded-xl px-3 py-1.5 text-xs text-slate-200" + /> +
+
+ + setNewEmployeeId(e.target.value)} + placeholder="如: RD-099" + className="w-full bg-slate-950 border border-slate-800 rounded-xl px-3 py-1.5 text-xs text-slate-200" + /> +
+
+ +
+ + 核心约束:由管理员创建的账号,首次登录系统时将被强制要求修改密码。 +
+ +
+ + +
+
+
+
+ )} + + {/* 新增自定义角色对话框 */} + {showNewRoleDialog && ( +
+
+

新增自定义受控角色

+
+
+ + setNewRoleName(e.target.value)} + placeholder="如: 合规审计专家" + className="w-full bg-slate-950 border border-slate-800 rounded-xl px-3 py-1.5 text-xs text-slate-100" + /> +
+
+ + setNewRoleCode(e.target.value.toUpperCase())} + placeholder="如: AUDITOR" + className="w-full bg-slate-950 border border-slate-800 rounded-xl px-3 py-1.5 text-xs text-slate-100 font-mono" + /> +
+
+ +