import { useState, useEffect, useRef } from "react"; import { ClientLoaderFunctionArgs, ClientActionFunctionArgs } from "@remix-run/react"; import { Card } from "~/components/ui/Card"; import { Button } from "~/components/ui/Button"; import { Modal } from "~/components/ui/Modal"; import { toastService } from "~/components/ui/Toast"; import { getRoles, getRoutes, getRoleRoutePermissions, updateRoleRoutePermissions, getRoleUsers, getAllUsers, assignUserRoles, createRole, updateRole, deleteRole, revokeUserRole, getUserRoles, type RoleInfo, type RouteInfo, type UserInfo } from "~/api/role-permissions/role-permissions"; import rolePermissionsStyles from "~/styles/pages/role-permissions.css?url"; // 引入样式 export function links() { return [ { rel: "stylesheet", href: rolePermissionsStyles } ]; } // 页面元数据 export const meta = () => { return [ { title: "角色权限管理 - 中国烟草AI合同及卷宗审核系统" }, { name: "description", content: "管理系统角色和权限分配" } ]; }; // ==================== 辅助函数 ==================== /** * 数据范围转中文 */ function getDataScopeLabel(scope: string): string { const map: Record = { 'ALL': '全部数据', 'DEPT': '部门数据', 'SELF': '仅本人数据' }; return map[scope] || scope; } // ClientLoader - 加载初始数据 export async function clientLoader({ request }: ClientLoaderFunctionArgs) { // ==================== 权限校验 ==================== // 检查用户是否有provincial_admin权限 try { const userInfo = localStorage.getItem('user_info'); if (!userInfo) { // 未登录,重定向到登录页 window.location.href = '/login'; throw new Error('未登录'); } const user = JSON.parse(userInfo); // 检查角色或权限 // provincial_admin 角色拥有完整的RBAC管理权限 const hasPermission = user.role === 'provincial_admin' || user.role_key === 'provincial_admin' || (user.permissions && Array.isArray(user.permissions) && user.permissions.includes('system:rbac:manage')); if (!hasPermission) { // 无权限,显示错误提示 console.warn('⚠️ 权限不足:需要省级管理员权限或system:rbac:manage权限'); toastService.error('权限不足,需要省级管理员权限'); // 返回空数据,但不阻止页面渲染(可以显示友好的无权限提示) return { roles: [], routes: [], users: [], noPermission: true }; } } catch (error) { console.error('权限检查失败:', error); } // ==================== 加载数据 ==================== try { const [roles, routes, users] = await Promise.all([ getRoles(), getRoutes(), getAllUsers() ]); return { roles, routes, users, noPermission: false }; } catch (error) { console.error("加载数据失败:", error); return { roles: [], routes: [], users: [], noPermission: false }; } } // ClientAction - 处理用户操作 export async function clientAction({ request }: ClientActionFunctionArgs) { const formData = await request.formData(); const action = formData.get("action") as string; try { switch (action) { case "updatePermissions": { const roleId = parseInt(formData.get("roleId") as string); const routeIds = JSON.parse(formData.get("routeIds") as string); const result = await updateRoleRoutePermissions(roleId, routeIds); return result; } case "assignUserRoles": { const userId = parseInt(formData.get("userId") as string); const roleIds = JSON.parse(formData.get("roleIds") as string); const result = await assignUserRoles(userId, roleIds); return result; } case "createRole": { const roleData = JSON.parse(formData.get("roleData") as string); const result = await createRole(roleData); return result; } case "updateRole": { const roleId = parseInt(formData.get("roleId") as string); const roleData = JSON.parse(formData.get("roleData") as string); const result = await updateRole(roleId, roleData); return result; } case "deleteRole": { const roleId = parseInt(formData.get("roleId") as string); const result = await deleteRole(roleId); return result; } default: return { success: false, message: "未知操作" }; } } catch (error) { console.error("操作失败:", error); return { success: false, message: error instanceof Error ? error.message : "操作失败" }; } } // ==================== 创建角色模态框 ==================== interface CreateRoleModalProps { isOpen: boolean; onClose: () => void; onSuccess: () => void; } function CreateRoleModal({ isOpen, onClose, onSuccess }: CreateRoleModalProps) { const [formData, setFormData] = useState({ role_key: '', role_name: '', description: '', data_scope: 'SELF' as 'ALL' | 'DEPT' | 'SELF', priority: 10 }); const [loading, setLoading] = useState(false); const [errors, setErrors] = useState>({}); // 重置表单 const resetForm = () => { setFormData({ role_key: '', role_name: '', description: '', data_scope: 'SELF', priority: 10 }); setErrors({}); }; // 验证表单 const validateForm = (): boolean => { const newErrors: Record = {}; if (!formData.role_key.trim()) { newErrors.role_key = '角色标识不能为空'; } else if (!/^[a-z][a-z0-9_]*$/.test(formData.role_key)) { newErrors.role_key = '角色标识只能包含小写字母、数字、下划线,且必须以字母开头'; } if (!formData.role_name.trim()) { newErrors.role_name = '角色名称不能为空'; } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; // 提交表单 const handleSubmit = async (e?: React.FormEvent) => { if (e) e.preventDefault(); if (!validateForm()) { toastService.error('请填写正确的表单信息'); return; } setLoading(true); try { const result = await createRole({ role_key: formData.role_key, role_name: formData.role_name, description: formData.description, data_scope: formData.data_scope, priority: formData.priority, is_system_role: false }); if (result.success) { toastService.success(result.message); resetForm(); onSuccess(); onClose(); } else { toastService.error(result.message); } } catch (error) { toastService.error('创建角色失败'); console.error('创建角色失败:', error); } finally { setLoading(false); } }; // 关闭时重置表单 const handleClose = () => { resetForm(); onClose(); }; return ( } >
{ const value = e.target.value; setFormData({ ...formData, role_key: value }); // 实时验证 if (value && !/^[a-z][a-z0-9_]*$/.test(value)) { setErrors({ ...errors, role_key: '必须以字母开头,只能包含小写字母、数字、下划线' }); } else { setErrors({ ...errors, role_key: '' }); } }} disabled={loading} /> {errors.role_key && {errors.role_key}} 必须以字母开头,只能包含小写字母、数字、下划线,创建后不可修改
{ setFormData({ ...formData, role_name: e.target.value }); setErrors({ ...errors, role_name: '' }); }} disabled={loading} /> {errors.role_name && {errors.role_name}}