Files
makemd/dashboard/src/pages/Settings/RoleManagement.tsx

412 lines
12 KiB
TypeScript
Raw Normal View History

import { useState, useEffect, FC } from '@/imports';
import {
Card,
Table,
Button,
Modal,
Form,
Input,
Select,
Checkbox,
Tree,
message,
Space,
Tag,
Tooltip,
Alert,
Typography,
Divider,
Collapse,
} from 'antd';
import { PlusOutlined, EditOutlined, DeleteOutlined, SaveOutlined, CloseOutlined, KeyOutlined, LockOutlined, DownOutlined, UpOutlined, LineChartOutlined } from '@ant-design/icons';
import { useRequest } from 'umi';
import {
createRole as createRoleAPI,
updateRole as updateRoleAPI,
deleteRole as deleteRoleAPI,
getAllRoles as getAllRolesAPI,
getPermissionDefinitions as getPermissionsAPI,
} from '@/services/roleDataSource';
const { Title, Text } = Typography;
const { Option } = Select;
const { TreeNode } = Tree;
const { Panel } = Collapse;
const RoleManagement: FC = () => {
const [roles, setRoles] = useState<any[]>([]);
const [permissions, setPermissions] = useState<any[]>([]);
const [selectedRole, setSelectedRole] = useState<any>(null);
const [isModalVisible, setIsModalVisible] = useState(false);
const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
const [form] = Form.useForm();
const [confirmLoading, setConfirmLoading] = useState(false);
const [deletingRole, setDeletingRole] = useState<string>('');
// 加载角色列表
const { run: loadRoles, loading: loadingRoles } = useRequest(getAllRolesAPI, {
onSuccess: (data) => {
setRoles(data);
},
});
// 加载权限列表
const { run: loadPermissions, loading: loadingPermissions } = useRequest(getPermissionsAPI, {
onSuccess: (data) => {
setPermissions(data);
},
});
useEffect(() => {
loadRoles();
loadPermissions();
}, []);
// 打开创建/编辑角色模态框
const showModal = (role?: any) => {
if (role) {
setSelectedRole(role);
form.setFieldsValue({
roleCode: role.code,
roleName: role.name,
permissions: role.permissions || [],
});
} else {
setSelectedRole(null);
form.resetFields();
}
setIsModalVisible(true);
};
// 关闭模态框
const handleCancel = () => {
setIsModalVisible(false);
setSelectedRole(null);
form.resetFields();
};
// 提交角色表单
const handleSubmit = async () => {
try {
const values = await form.validateFields();
setConfirmLoading(true);
if (selectedRole) {
// 更新角色
await updateRoleAPI(values.roleCode, values.roleName, values.permissions);
message.success('Role updated successfully');
} else {
// 创建角色
await createRoleAPI(values.roleCode, values.roleName, values.permissions);
message.success('Role created successfully');
}
setIsModalVisible(false);
form.resetFields();
loadRoles();
} catch (error) {
message.error('Failed to save role');
} finally {
setConfirmLoading(false);
}
};
// 打开删除确认模态框
const showDeleteConfirm = (roleCode: string) => {
setDeletingRole(roleCode);
setIsDeleteModalVisible(true);
};
// 确认删除角色
const handleDelete = async () => {
try {
setConfirmLoading(true);
await deleteRoleAPI(deletingRole);
message.success('Role deleted successfully');
setIsDeleteModalVisible(false);
loadRoles();
} catch (error) {
message.error('Failed to delete role');
} finally {
setConfirmLoading(false);
}
};
// 构建权限树
const buildPermissionTree = () => {
const modules = new Map<string, any[]>();
// 按模块分组权限
permissions.forEach(permission => {
if (!modules.has(permission.module)) {
modules.set(permission.module, []);
}
modules.get(permission.module)?.push(permission);
});
return Array.from(modules.entries()).map(([module, perms]) => (
<TreeNode key={module} title={module}>
{perms.map(perm => (
<TreeNode
key={perm.id}
title={
<Space size="small">
<Text>{perm.name}</Text>
<Tooltip title={perm.description}>
<Tag size="small" color="blue">{perm.action}</Tag>
</Tooltip>
</Space>
}
/>
))}
</TreeNode>
));
};
// 检查角色是否为系统预设角色
const isSystemRole = (roleCode: string) => {
const systemRoles = ['ADMIN', 'MANAGER', 'OPERATOR', 'FINANCE', 'SOURCING', 'LOGISTICS', 'ANALYST'];
return systemRoles.includes(roleCode);
};
// 计算权限继承关系
const calculatePermissionInheritance = (role: any) => {
const inheritedPermissions = new Set<string>();
const roleHierarchy = {
'ADMIN': [],
'MANAGER': ['OPERATOR'],
'OPERATOR': [],
'FINANCE': [],
'SOURCING': [],
'LOGISTICS': [],
'ANALYST': [],
};
// 递归计算继承的权限
const calculateInheritance = (roleCode: string) => {
const childRoles = roleHierarchy[roleCode as keyof typeof roleHierarchy] || [];
childRoles.forEach(childRoleCode => {
const childRole = roles.find(r => r.code === childRoleCode);
if (childRole) {
childRole.permissions?.forEach((perm: string) => {
inheritedPermissions.add(perm);
});
calculateInheritance(childRoleCode);
}
});
};
calculateInheritance(role.code);
return Array.from(inheritedPermissions);
};
const columns = [
{
title: 'Role Code',
dataIndex: 'code',
key: 'code',
render: (code: string) => (
<Space>
<KeyOutlined />
<Text strong>{code}</Text>
{isSystemRole(code) && <Tag color="green">System</Tag>}
</Space>
),
},
{
title: 'Role Name',
dataIndex: 'name',
key: 'name',
},
{
title: 'Permissions',
dataIndex: 'permissions',
key: 'permissions',
render: (permissions: string[]) => (
<Tag color="blue">{permissions?.length || 0} permissions</Tag>
),
},
{
title: 'Actions',
key: 'actions',
render: (_: any, record: any) => (
<Space size="middle">
<Button
type="primary"
icon={<EditOutlined />}
onClick={() => showModal(record)}
disabled={isSystemRole(record.code)}
>
Edit
</Button>
<Button
danger
icon={<DeleteOutlined />}
onClick={() => showDeleteConfirm(record.code)}
disabled={isSystemRole(record.code)}
>
Delete
</Button>
</Space>
),
},
];
return (
<div style={{ padding: 24 }}>
<Card
title={
<Space>
<LockOutlined />
<Title level={4}>Role Management</Title>
</Space>
}
extra={
<Button
type="primary"
icon={<PlusOutlined />}
onClick={() => showModal()}
>
Create Role
</Button>
}
>
<Alert
message="Role Management"
description="Create and manage custom roles with specific permissions. System roles cannot be modified."
type="info"
style={{ marginBottom: 24 }}
/>
<Table
columns={columns}
dataSource={roles}
rowKey="code"
loading={loadingRoles}
pagination={{ pageSize: 10 }}
expandable={{
expandedRowRender: (record) => (
<div style={{ padding: 20 }}>
<Collapse defaultActiveKey={['permissions', 'inheritance']}>
<Panel
header={
<Space>
<LockOutlined />
<Text strong>Assigned Permissions</Text>
</Space>
}
key="permissions"
>
<Space wrap>
{record.permissions?.map((perm: string) => {
const permInfo = permissions.find(p => p.id === perm);
return (
<Tag key={perm} color="blue">
{permInfo?.name || perm}
</Tag>
);
})}
</Space>
</Panel>
<Panel
header={
<Space>
<LineChartOutlined />
<Text strong>Permission Inheritance</Text>
</Space>
}
key="inheritance"
>
<div>
<Text>Role Hierarchy:</Text>
<div style={{ marginLeft: 20, marginTop: 10 }}>
{calculatePermissionInheritance(record).length > 0 ? (
<Space wrap>
{calculatePermissionInheritance(record).map((perm: string) => {
const permInfo = permissions.find(p => p.id === perm);
return (
<Tag key={perm} color="green">
{permInfo?.name || perm}
</Tag>
);
})}
</Space>
) : (
<Text type="secondary">No inherited permissions</Text>
)}
</div>
</div>
</Panel>
</Collapse>
</div>
),
}}
/>
{/* Create/Edit Role Modal */}
<Modal
title={selectedRole ? 'Edit Role' : 'Create Role'}
open={isModalVisible}
onOk={handleSubmit}
onCancel={handleCancel}
confirmLoading={confirmLoading}
width={800}
>
<Form
form={form}
layout="vertical"
>
<Form.Item
name="roleCode"
label="Role Code"
rules={[
{ required: true, message: 'Please enter role code' },
{ pattern: /^[A-Z_]+$/, message: 'Role code must be uppercase with underscores' },
]}
>
<Input placeholder="e.g., SALES_MANAGER" disabled={!!selectedRole} />
</Form.Item>
<Form.Item
name="roleName"
label="Role Name"
rules={[{ required: true, message: 'Please enter role name' }]}
>
<Input placeholder="e.g., Sales Manager" />
</Form.Item>
<Form.Item
name="permissions"
label="Permissions"
rules={[{ required: true, message: 'Please select permissions' }]}
>
<Tree
checkable
treeData={buildPermissionTree()}
placeholder="Select permissions"
/>
</Form.Item>
</Form>
</Modal>
{/* Delete Role Modal */}
<Modal
title="Delete Role"
open={isDeleteModalVisible}
onOk={handleDelete}
onCancel={() => setIsDeleteModalVisible(false)}
confirmLoading={confirmLoading}
okText="Delete"
cancelText="Cancel"
okType="danger"
>
<p>Are you sure you want to delete the role <strong>{deletingRole}</strong>?</p>
<p>All users assigned to this role will lose these permissions.</p>
</Modal>
</Card>
</div>
);
};
export default RoleManagement;