pms-front-react/src/pages/system/RolePage.tsx

780 lines
28 KiB
TypeScript
Raw Normal View History

2026-03-17 07:18:07 +00:00
import React, { useState, useEffect, useCallback } from 'react';
import {
2026-03-17 07:18:56 +00:00
App, Table, Form, Input, Select, Button, Modal, Space, DatePicker, Switch, Dropdown, InputNumber, Tree, Tooltip, Checkbox, Popconfirm, Row, Col
2026-03-17 07:18:07 +00:00
} from 'antd';
2026-03-17 07:18:56 +00:00
import type { TableColumnsType } from 'antd';
2026-03-17 07:18:07 +00:00
import {
SearchOutlined, ReloadOutlined, PlusOutlined, EditOutlined, DeleteOutlined, DownloadOutlined,
2026-03-17 07:18:56 +00:00
CheckCircleOutlined, DownOutlined, ExclamationCircleOutlined, QuestionCircleOutlined, UserOutlined
2026-03-17 07:18:07 +00:00
} from '@ant-design/icons';
import {
listRole, getRole, addRole, updateRole, dataScope, changeRoleStatus, delRole, deptTreeSelect
} from '../../api/system/role';
import { treeselect as menuTreeselect, roleMenuTreeselect } from '../../api/system/menu';
import { saveAs } from 'file-saver';
import dayjs from 'dayjs';
import { parseTime } from '../../utils/ruoyi'; // Custom utility
2026-03-17 07:18:56 +00:00
import Permission from '@/components/Permission';
import ReadonlyAction from '@/components/Permission/ReadonlyAction';
2026-04-07 08:44:06 +00:00
import { useNavigate } from 'react-router-dom';
import './system-admin.css';
2026-03-17 07:18:07 +00:00
const { RangePicker } = DatePicker;
// Mock Dictionaries
const sysNormalDisableDict = [
{ value: '0', label: '正常' },
{ value: '1', label: '停用' },
];
const dataScopeOptions = [
{ value: '1', label: '全部数据权限' },
{ value: '2', label: '自定数据权限' },
{ value: '3', label: '本部门数据权限' },
{ value: '4', label: '本部门及以下数据权限' },
{ value: '5', label: '仅本人数据权限' },
];
2026-03-17 07:18:56 +00:00
type RoleQueryParams = {
pageNum?: number;
pageSize?: number;
roleName?: string;
roleKey?: string;
status?: string;
beginTime?: string;
endTime?: string;
};
type RoleRecord = {
roleId?: string | number;
roleName?: string;
roleKey?: string;
roleSort?: string | number;
status?: string;
createTime?: string | number | Date;
[key: string]: unknown;
};
2026-03-17 07:18:07 +00:00
const RolePage: React.FC = () => {
2026-03-17 07:18:56 +00:00
const { message, modal } = App.useApp();
2026-04-07 08:44:06 +00:00
const navigate = useNavigate();
2026-03-17 07:18:07 +00:00
const [queryForm] = Form.useForm();
const [roleForm] = Form.useForm();
const [dataScopeForm] = Form.useForm();
const [loading, setLoading] = useState(false);
2026-03-17 07:18:56 +00:00
const [roleList, setRoleList] = useState<RoleRecord[]>([]);
2026-03-17 07:18:07 +00:00
const [total, setTotal] = useState(0);
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
2026-03-17 07:18:56 +00:00
const [dateRange, setDateRange] = useState<[dayjs.Dayjs | null, dayjs.Dayjs | null] | null>(null);
2026-03-17 07:18:07 +00:00
const [addEditModalVisible, setAddEditModalVisible] = useState(false);
const [addEditModalTitle, setAddEditModalTitle] = useState('');
const [formType, setFormType] = useState<'add' | 'edit'>('add');
const [dataScopeModalVisible, setDataScopeModalVisible] = useState(false);
const [dataScopeModalTitle, setDataScopeModalTitle] = useState('');
const [menuOptions, setMenuOptions] = useState<any[]>([]);
const [deptOptions, setDeptOptions] = useState<any[]>([]);
const [menuExpand, setMenuExpand] = useState(false);
const [menuNodeAll, setMenuNodeAll] = useState(false);
const [deptExpand, setDeptExpand] = useState(false);
const [deptNodeAll, setDeptNodeAll] = useState(false);
const [menuExpandedKeys, setMenuExpandedKeys] = useState<React.Key[]>([]);
const [deptExpandedKeys, setDeptExpandedKeys] = useState<React.Key[]>([]);
const [menuCheckedKeys, setMenuCheckedKeys] = useState<React.Key[]>([]);
const [menuHalfCheckedKeys, setMenuHalfCheckedKeys] = useState<React.Key[]>([]);
const [deptCheckedKeys, setDeptCheckedKeys] = useState<React.Key[]>([]);
const [deptHalfCheckedKeys, setDeptHalfCheckedKeys] = useState<React.Key[]>([]);
const [menuCheckStrictly, setMenuCheckStrictly] = useState(true); // For menu tree linkage
const [deptCheckStrictly, setDeptCheckStrictly] = useState(true); // For dept tree linkage
2026-03-17 07:18:56 +00:00
const [currentRole, setCurrentRole] = useState<RoleRecord>({}); // For storing current role in modals
2026-03-17 07:18:07 +00:00
2026-03-17 07:18:56 +00:00
const [queryParams, setQueryParams] = useState<RoleQueryParams>({
2026-03-17 07:18:07 +00:00
pageNum: 1,
pageSize: 10,
roleName: undefined,
roleKey: undefined,
status: undefined,
});
const extractResponseData = (response: any) => {
if (response && typeof response === 'object' && 'data' in response) {
return response.data ?? {};
}
return response ?? {};
};
const extractTreeNodes = (response: any, key: 'menus' | 'depts') => {
if (!response || typeof response !== 'object') {
return [];
}
if (Array.isArray(response[key])) {
return response[key];
}
if (Array.isArray(response.data)) {
return response.data;
}
if (response.data && typeof response.data === 'object' && Array.isArray(response.data[key])) {
return response.data[key];
}
return [];
};
const extractCheckedKeys = (response: any): React.Key[] => {
if (!response || typeof response !== 'object') {
return [];
}
if (Array.isArray(response.checkedKeys)) {
return response.checkedKeys;
}
if (response.data && typeof response.data === 'object' && Array.isArray(response.data.checkedKeys)) {
return response.data.checkedKeys;
}
return [];
};
const collectTreeKeys = (nodes: any[]): React.Key[] => {
const keys: React.Key[] = [];
const visit = (list: any[]) => {
list.forEach((node) => {
const key = node?.id ?? node?.key;
if (key !== undefined) {
keys.push(key);
}
if (Array.isArray(node?.children) && node.children.length > 0) {
visit(node.children);
}
});
};
visit(nodes ?? []);
return keys;
};
const getList = useCallback(async () => {
setLoading(true);
try {
2026-03-17 07:18:56 +00:00
const formattedQueryParams: RoleQueryParams = { ...queryParams };
if (dateRange && dateRange[0] && dateRange[1]) {
formattedQueryParams.beginTime = dateRange[0].format('YYYY-MM-DD');
formattedQueryParams.endTime = dateRange[1].format('YYYY-MM-DD');
2026-03-17 07:18:07 +00:00
} else {
2026-03-17 07:18:56 +00:00
formattedQueryParams.beginTime = undefined;
formattedQueryParams.endTime = undefined;
2026-03-17 07:18:07 +00:00
}
2026-03-17 07:18:56 +00:00
const response = await listRole(formattedQueryParams) as { rows?: RoleRecord[]; total?: number };
setRoleList(response.rows ?? []);
setTotal(response.total ?? 0);
2026-03-17 07:18:07 +00:00
} catch (error) {
console.error('Failed to fetch role list:', error);
message.error('获取角色列表失败');
} finally {
setLoading(false);
}
}, [queryParams, dateRange]);
const getMenuTreeselect = useCallback(async (roleId?: string | number) => {
try {
const response = roleId !== undefined ? await roleMenuTreeselect(roleId) : await menuTreeselect();
setMenuOptions(extractTreeNodes(response, 'menus'));
return response;
} catch (error) {
console.error('Failed to fetch menu tree:', error);
message.error('获取菜单树失败');
return null;
}
}, []);
const getDeptTreeselect = useCallback(async (roleId?: string | number) => {
try {
2026-03-17 07:18:56 +00:00
const response = await deptTreeSelect(roleId ?? '');
2026-03-17 07:18:07 +00:00
setDeptOptions(extractTreeNodes(response, 'depts'));
return response;
} catch (error) {
console.error('Failed to fetch department tree:', error);
message.error('获取部门树失败');
return null;
}
}, []);
useEffect(() => {
getList();
}, [getList]);
2026-03-17 07:18:56 +00:00
const handleQuery = (values?: Partial<RoleQueryParams>) => {
const formValues = values ?? queryForm.getFieldsValue();
setQueryParams((prev) => ({
...prev,
pageNum: 1,
roleName: formValues.roleName || undefined,
roleKey: formValues.roleKey || undefined,
status: formValues.status || undefined,
}));
2026-03-17 07:18:07 +00:00
};
const resetQuery = () => {
queryForm.resetFields();
setDateRange(null);
setQueryParams({
pageNum: 1,
pageSize: 10,
roleName: undefined,
roleKey: undefined,
status: undefined,
});
};
2026-03-17 07:18:56 +00:00
const handleSelectionChange = (selectedKeys: React.Key[]) => {
2026-03-17 07:18:07 +00:00
setSelectedRowKeys(selectedKeys);
};
const handleStatusChange = async (record: any) => {
const newStatus = record.status === '0' ? '1' : '0';
const text = record.status === '0' ? '停用' : '启用';
2026-03-17 07:18:56 +00:00
modal.confirm({
2026-03-17 07:18:07 +00:00
title: '确认操作',
icon: <ExclamationCircleOutlined />,
content: `确认要"${text}"角色"${record.roleName}"吗?`,
onOk: async () => {
try {
await changeRoleStatus(record.roleId, newStatus);
message.success(`${text}成功`);
getList();
} catch (error) {
message.error(`${text}失败`);
setRoleList(prev => prev.map(role => role.roleId === record.roleId ? { ...role, status: record.status } : role));
}
},
onCancel() {
setRoleList(prev => prev.map(role => role.roleId === record.roleId ? { ...role, status: record.status } : role));
},
});
};
const resetRoleForm = () => {
roleForm.resetFields();
dataScopeForm.resetFields();
setMenuOptions([]); // Clear menu options
setDeptOptions([]); // Clear dept options
setCurrentRole({}); // Clear current role
setMenuExpand(false);
setMenuNodeAll(false);
setDeptExpand(false);
setDeptNodeAll(false);
setMenuExpandedKeys([]);
setDeptExpandedKeys([]);
setMenuCheckedKeys([]);
setMenuHalfCheckedKeys([]);
setDeptCheckedKeys([]);
setDeptHalfCheckedKeys([]);
setMenuCheckStrictly(true);
setDeptCheckStrictly(true);
};
const handleAdd = async () => {
resetRoleForm();
await getMenuTreeselect(); // Get full menu tree
setAddEditModalTitle('添加角色');
setFormType('add');
setAddEditModalVisible(true);
roleForm.setFieldsValue({
roleSort: 0,
status: '0',
menuCheckStrictly: true,
deptCheckStrictly: true,
});
};
const handleUpdate = async (record?: any) => {
resetRoleForm();
const roleId = record?.roleId || selectedRowKeys[0];
if (roleId === undefined) {
message.warning('请选择要修改的角色');
return;
}
try {
const roleMenuResponse = await getMenuTreeselect(roleId); // Get menu tree with selected keys
const roleResponse = await getRole(roleId);
const roleData = extractResponseData(roleResponse);
setCurrentRole(roleData);
setMenuCheckStrictly(roleData.menuCheckStrictly === undefined ? true : !!roleData.menuCheckStrictly);
setDeptCheckStrictly(roleData.deptCheckStrictly === undefined ? true : !!roleData.deptCheckStrictly);
2026-03-17 07:18:56 +00:00
setMenuCheckedKeys(extractCheckedKeys(roleMenuResponse));
2026-03-17 07:18:07 +00:00
setMenuHalfCheckedKeys([]);
setAddEditModalTitle('修改角色');
setFormType('edit');
roleForm.setFieldsValue({
...roleData,
status: roleData.status?.toString() ?? '0',
});
setAddEditModalVisible(true);
} catch (error) {
message.error('获取角色详情失败');
}
};
const handleDelete = (record?: any) => {
const roleIds = record?.roleId ? [record.roleId] : selectedRowKeys;
if (roleIds.length === 0) {
message.warning('请选择要删除的角色');
return;
}
2026-03-17 07:18:56 +00:00
modal.confirm({
2026-03-17 07:18:07 +00:00
title: '确认删除',
icon: <ExclamationCircleOutlined />,
content: `是否确认删除角色编号为"${roleIds.join(',')}"的数据项?`,
onOk: async () => {
try {
await delRole(roleIds.join(','));
message.success('删除成功');
setSelectedRowKeys([]);
getList();
} catch (error) {
message.error('删除失败');
}
},
});
};
const handleExport = async () => {
const hide = message.loading('正在导出数据...', 0);
try {
2026-03-17 07:18:56 +00:00
const response = await listRole({ ...queryParams, pageNum: undefined, pageSize: undefined }) as { rows?: RoleRecord[] };
2026-03-17 07:18:07 +00:00
const header = ['角色编号', '角色名称', '权限字符', '显示顺序', '状态', '创建时间'];
2026-03-17 07:18:56 +00:00
const data = (response.rows ?? []).map((role) => [
2026-03-17 07:18:07 +00:00
role.roleId, role.roleName, role.roleKey, role.roleSort,
sysNormalDisableDict.find((d) => d.value === String(role.status ?? ''))?.label ?? '',
parseTime(role.createTime),
]);
const csvContent = [header, ...data]
2026-03-17 07:18:56 +00:00
.map((row) => row.map((cell: unknown) => `"${String(cell).replace(/"/g, '""')}"`).join(','))
2026-03-17 07:18:07 +00:00
.join('\n');
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
saveAs(blob, `role_${dayjs().format('YYYYMMDDHHmmss')}.csv`);
hide();
message.success('导出成功');
} catch {
hide();
message.error('导出失败');
}
};
2026-03-17 07:18:56 +00:00
const handleCommand = (key: string, record: any) => {
2026-03-17 07:18:07 +00:00
switch (key) {
case 'handleDataScope':
handleDataScope(record);
break;
case 'handleAuthUser':
handleAuthUser(record);
break;
default:
break;
}
};
const handleDataScope = async (record: any) => {
resetRoleForm();
const roleId = record.roleId;
try {
const deptTreeResponse = await getDeptTreeselect(roleId); // Get dept tree with selected keys
const roleResponse = await getRole(roleId);
const roleData = extractResponseData(roleResponse);
setCurrentRole(roleData);
setDataScopeModalTitle('分配数据权限');
setDataScopeModalVisible(true);
dataScopeForm.setFieldsValue({
...roleData,
dataScope: roleData.dataScope?.toString(),
});
setDeptCheckedKeys(extractCheckedKeys(deptTreeResponse));
setDeptHalfCheckedKeys([]);
} catch (error) {
message.error('获取数据权限信息失败');
}
};
const handleAuthUser = (record: any) => {
2026-04-07 08:44:06 +00:00
navigate(`/system/role-auth/user/${record.roleId}`);
2026-03-17 07:18:07 +00:00
};
const submitRoleForm = async (values: any) => {
try {
const menuIds = [...menuCheckedKeys, ...menuHalfCheckedKeys];
const roleData = { ...currentRole, ...values, menuIds, menuCheckStrictly };
if (formType === 'edit') {
await updateRole(roleData);
message.success('修改成功');
} else {
await addRole(roleData);
message.success('新增成功');
}
setAddEditModalVisible(false);
getList();
} catch (error) {
console.error('Submit role form failed:', error);
message.error('操作失败');
}
};
const submitDataScopeForm = async (values: any) => {
try {
let deptIds: React.Key[] = [];
if (values.dataScope === '2') {
deptIds = [...deptCheckedKeys, ...deptHalfCheckedKeys];
}
const roleData = { ...currentRole, ...values, deptIds, deptCheckStrictly };
await dataScope(roleData);
message.success('分配数据权限成功');
setDataScopeModalVisible(false);
getList();
} catch (error) {
message.error('分配数据权限失败');
}
};
const dataScopeSelectChange = (value: string) => {
dataScopeForm.setFieldValue('dataScope', value);
if (value !== '2') {
setDeptCheckedKeys([]);
setDeptHalfCheckedKeys([]);
}
};
const handleCheckedTreeExpand = (value: boolean, type: 'menu' | 'dept') => {
if (type === 'menu') {
setMenuExpand(value);
setMenuExpandedKeys(value ? collectTreeKeys(menuOptions) : []);
return;
}
setDeptExpand(value);
setDeptExpandedKeys(value ? collectTreeKeys(deptOptions) : []);
};
const handleCheckedTreeNodeAll = (value: boolean, type: 'menu' | 'dept') => {
if (type === 'menu') {
const keys = value ? collectTreeKeys(menuOptions) : [];
setMenuCheckedKeys(keys);
setMenuHalfCheckedKeys([]);
setMenuNodeAll(value);
return;
}
const keys = value ? collectTreeKeys(deptOptions) : [];
setDeptCheckedKeys(keys);
setDeptHalfCheckedKeys([]);
setDeptNodeAll(value);
};
const handleMenuCheck = (checked: any, info: any) => {
if (Array.isArray(checked)) {
setMenuCheckedKeys(checked);
setMenuHalfCheckedKeys(info?.halfCheckedKeys ?? []);
return;
}
setMenuCheckedKeys(checked?.checked ?? []);
setMenuHalfCheckedKeys(checked?.halfChecked ?? []);
};
const handleDeptCheck = (checked: any, info: any) => {
if (Array.isArray(checked)) {
setDeptCheckedKeys(checked);
setDeptHalfCheckedKeys(info?.halfCheckedKeys ?? []);
return;
}
setDeptCheckedKeys(checked?.checked ?? []);
setDeptHalfCheckedKeys(checked?.halfChecked ?? []);
};
const handleCheckedTreeConnect = (value: boolean, type: 'menu' | 'dept') => {
if (type === 'menu') setMenuCheckStrictly(value);
if (type === 'dept') setDeptCheckStrictly(value);
};
const columns: TableColumnsType<any> = [
Table.SELECTION_COLUMN,
{ title: '角色编号', dataIndex: 'roleId', align: 'center', width: 100 },
{ title: '角色名称', dataIndex: 'roleName', align: 'center', ellipsis: true },
{ title: '权限字符', dataIndex: 'roleKey', align: 'center', ellipsis: true },
{ title: '显示顺序', dataIndex: 'roleSort', align: 'center', width: 80 },
{
title: '状态', dataIndex: 'status', align: 'center', width: 100,
2026-03-17 07:18:56 +00:00
render: (text: string, record: RoleRecord) => (
<Permission
permissions="system:role:edit"
fallback={
<Switch
checked={text === '0'}
checkedChildren="正常"
unCheckedChildren="停用"
disabled
/>
}
>
<Switch
checked={text === '0'}
checkedChildren="正常"
unCheckedChildren="停用"
onChange={() => handleStatusChange(record)}
disabled={record.roleId === 1}
/>
</Permission>
2026-03-17 07:18:07 +00:00
),
},
{ title: '创建时间', dataIndex: 'createTime', align: 'center', width: 180, render: (text: string) => parseTime(text) },
{
title: '操作', key: 'operation', align: 'center', width: 220,
render: (_, record) => (
<Space size="small">
2026-03-17 07:18:56 +00:00
<Permission permissions="system:role:edit" fallback={<ReadonlyAction icon={<EditOutlined />}></ReadonlyAction>}>
<Button type="link" icon={<EditOutlined />} onClick={() => handleUpdate(record)} disabled={record.roleId === 1}></Button>
</Permission>
<Permission permissions="system:role:remove" fallback={<ReadonlyAction icon={<DeleteOutlined />} danger></ReadonlyAction>}>
<Popconfirm
title="是否确认删除该角色?"
onConfirm={() => handleDelete(record)}
okText="是"
cancelText="否"
disabled={record.roleId === 1}
>
<Button type="link" danger icon={<DeleteOutlined />} disabled={record.roleId === 1}></Button>
</Popconfirm>
</Permission>
<Permission
permissions="system:role:edit"
mode="or"
fallback={<ReadonlyAction icon={<DownOutlined />}></ReadonlyAction>}
2026-03-17 07:18:07 +00:00
>
2026-03-17 07:18:56 +00:00
<Dropdown
menu={{
items: [
{ key: 'handleDataScope', icon: <CheckCircleOutlined />, label: '数据权限' },
{ key: 'handleAuthUser', icon: <UserOutlined />, label: '分配用户' },
],
onClick: ({ key }) => handleCommand(String(key), record),
}}
trigger={['click']}
disabled={record.roleId === 1}
>
<Button type="link" icon={<DownOutlined />} disabled={record.roleId === 1}></Button>
</Dropdown>
</Permission>
2026-03-17 07:18:07 +00:00
</Space>
),
},
];
return (
2026-03-17 07:18:56 +00:00
<div className="app-container role-page-container">
2026-03-17 07:18:07 +00:00
<Form form={queryForm} layout="inline" className="search-form" onFinish={handleQuery} initialValues={queryParams}>
<Form.Item label="角色名称" name="roleName">
2026-03-17 07:18:56 +00:00
<Input placeholder="请输入角色名称" allowClear onPressEnter={() => queryForm.submit()} style={{ width: 240 }} />
2026-03-17 07:18:07 +00:00
</Form.Item>
<Form.Item label="权限字符" name="roleKey">
2026-03-17 07:18:56 +00:00
<Input placeholder="请输入权限字符" allowClear onPressEnter={() => queryForm.submit()} style={{ width: 240 }} />
2026-03-17 07:18:07 +00:00
</Form.Item>
<Form.Item label="状态" name="status">
<Select placeholder="角色状态" allowClear style={{ width: 240 }}>
{sysNormalDisableDict.map(dict => (
<Select.Option key={dict.value} value={dict.value}>{dict.label}</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label="创建时间">
<RangePicker
value={dateRange}
onChange={(dates) => setDateRange(dates)}
format="YYYY-MM-DD"
style={{ width: 240 }}
/>
</Form.Item>
<Form.Item>
<Button type="primary" icon={<SearchOutlined />} htmlType="submit"></Button>
<Button icon={<ReloadOutlined />} onClick={resetQuery} style={{ marginLeft: 8 }}></Button>
</Form.Item>
</Form>
<Space className="mb8">
2026-03-17 07:18:56 +00:00
<Permission permissions="system:role:add">
<Button type="primary" ghost icon={<PlusOutlined />} onClick={handleAdd}></Button>
</Permission>
<Permission permissions="system:role:edit">
<Button type="primary" ghost icon={<EditOutlined />} disabled={selectedRowKeys.length !== 1} onClick={() => handleUpdate()}></Button>
</Permission>
<Permission permissions="system:role:remove">
<Button danger ghost icon={<DeleteOutlined />} disabled={selectedRowKeys.length === 0} onClick={() => handleDelete()}></Button>
</Permission>
<Permission permissions="system:role:export">
<Button ghost icon={<DownloadOutlined />} onClick={handleExport}></Button>
</Permission>
2026-03-17 07:18:07 +00:00
</Space>
<Table
columns={columns}
dataSource={roleList}
rowKey="roleId"
loading={loading}
rowSelection={{
selectedRowKeys,
onChange: handleSelectionChange,
}}
pagination={{
current: queryParams.pageNum,
pageSize: queryParams.pageSize,
total: total,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total) => `${total}`,
onChange: (page, pageSize) => {
setQueryParams(prev => ({ ...prev, pageNum: page, pageSize: pageSize }));
},
}}
/>
{/* Add/Edit Role Modal */}
<Modal
2026-03-17 07:18:56 +00:00
className="system-admin-modal"
2026-03-17 07:18:07 +00:00
title={addEditModalTitle}
open={addEditModalVisible}
onOk={roleForm.submit}
onCancel={() => setAddEditModalVisible(false)}
width={500}
forceRender
>
<Form form={roleForm} labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} onFinish={submitRoleForm} initialValues={currentRole}>
<Form.Item label="角色名称" name="roleName" rules={[{ required: true, message: '请输入角色名称' }]}>
<Input placeholder="请输入角色名称" />
</Form.Item>
<Form.Item
label={
<Space>
<Tooltip title="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasRole('admin')`)">
<QuestionCircleOutlined />
</Tooltip>
</Space>
}
name="roleKey"
rules={[{ required: true, message: '请输入权限字符' }]} // Corrected escaping for "
>
<Input placeholder="请输入权限字符" />
</Form.Item>
<Form.Item label="角色顺序" name="roleSort" rules={[{ required: true, message: '请输入角色顺序' }]}>
<InputNumber min={0} controls={false} style={{ width: '100%' }} />
</Form.Item>
<Form.Item label="状态" name="status">
<Select>
{sysNormalDisableDict.map(dict => (
<Select.Option key={dict.value} value={dict.value}>{dict.label}</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label="菜单权限">
<Row gutter={[8, 8]}>
<Col>
<Checkbox checked={menuExpand} onChange={(e) => handleCheckedTreeExpand(e.target.checked, 'menu')}>/</Checkbox>
</Col>
<Col>
<Checkbox checked={menuNodeAll} onChange={(e) => handleCheckedTreeNodeAll(e.target.checked, 'menu')}>/</Checkbox>
</Col>
<Col>
<Checkbox checked={menuCheckStrictly} onChange={(e) => handleCheckedTreeConnect(e.target.checked, 'menu')}></Checkbox>
</Col>
</Row>
<Tree
2026-03-17 07:18:56 +00:00
className="system-tree-panel"
2026-03-17 07:18:07 +00:00
checkable
treeData={menuOptions}
fieldNames={{ title: 'label', key: 'id' }}
checkStrictly={!menuCheckStrictly}
checkedKeys={
!menuCheckStrictly
? { checked: menuCheckedKeys, halfChecked: menuHalfCheckedKeys }
: menuCheckedKeys
}
onCheck={handleMenuCheck}
expandedKeys={menuExpandedKeys}
onExpand={(expandedKeys) => setMenuExpandedKeys(expandedKeys)}
/>
</Form.Item>
<Form.Item label="备注" name="remark">
<Input.TextArea placeholder="请输入内容" />
</Form.Item>
</Form>
</Modal>
{/* Assign Data Scope Modal */}
<Modal
2026-03-17 07:18:56 +00:00
className="system-admin-modal"
2026-03-17 07:18:07 +00:00
title={dataScopeModalTitle}
open={dataScopeModalVisible}
onOk={dataScopeForm.submit}
onCancel={() => setDataScopeModalVisible(false)}
width={500}
forceRender
>
<Form form={dataScopeForm} labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} onFinish={submitDataScopeForm} initialValues={currentRole}>
<Form.Item label="角色名称">
<Input value={currentRole?.roleName ?? ''} disabled />
</Form.Item>
<Form.Item label="权限字符">
<Input value={currentRole?.roleKey ?? ''} disabled />
</Form.Item>
<Form.Item label="权限范围" name="dataScope">
<Select onChange={dataScopeSelectChange}>
{dataScopeOptions.map(option => (
<Select.Option key={option.value} value={option.value}>{option.label}</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item noStyle shouldUpdate={(prevValues, currentValues) => prevValues.dataScope !== currentValues.dataScope}>
{({ getFieldValue }) =>
getFieldValue('dataScope') === '2' ? (
<Form.Item label="数据权限">
<Row gutter={[8, 8]}>
<Col>
<Checkbox checked={deptExpand} onChange={(e) => handleCheckedTreeExpand(e.target.checked, 'dept')}>/</Checkbox>
</Col>
<Col>
<Checkbox checked={deptNodeAll} onChange={(e) => handleCheckedTreeNodeAll(e.target.checked, 'dept')}>/</Checkbox>
</Col>
<Col>
<Checkbox checked={deptCheckStrictly} onChange={(e) => handleCheckedTreeConnect(e.target.checked, 'dept')}></Checkbox>
</Col>
</Row>
<Tree
2026-03-17 07:18:56 +00:00
className="system-tree-panel"
2026-03-17 07:18:07 +00:00
checkable
treeData={deptOptions}
fieldNames={{ title: 'label', key: 'id' }}
checkStrictly={!deptCheckStrictly}
checkedKeys={
!deptCheckStrictly
? { checked: deptCheckedKeys, halfChecked: deptHalfCheckedKeys }
: deptCheckedKeys
}
onCheck={handleDeptCheck}
expandedKeys={deptExpandedKeys}
onExpand={(expandedKeys) => setDeptExpandedKeys(expandedKeys)}
/>
</Form.Item>
) : null
}
</Form.Item>
</Form>
</Modal>
</div>
);
};
export default RolePage;