Files
novalon-manage-system/novalon-manage-web/src/pages/system/role/index.tsx
T
张翔 6b5f7a517b refactor(role): 应用统一验证规则到角色管理表单
角色名称添加长度(2-50)校验,角色标识添加长度和格式校验,
排序使用 VALIDATION 常量的 initialValue 和 rules。
2026-05-06 15:36:27 +08:00

209 lines
6.8 KiB
TypeScript

import { useState, useEffect } from 'react'
import { Table, Button, Modal, Form, Input, InputNumber, Select, Tag, Space, message, Popconfirm, TreeSelect } from 'antd'
import { PlusOutlined, EditOutlined, DeleteOutlined, ReloadOutlined } from '@ant-design/icons'
import type { ColumnsType } from 'antd/es/table'
import { roleApi } from '@/api/role.api'
import type { Role, CreateRoleRequest, UpdateRoleRequest, RolePageRequest, Permission } from '@/api/role.api'
import type { PageResponse } from '@/api/user.api'
import { RoleStatus, roleStatusMap } from '@/constants/status'
import { VALIDATION } from '@/constants/validation-rules'
import PermissionGuard from '@/components/PermissionGuard'
export default function RoleManagement() {
const [roles, setRoles] = useState<Role[]>([])
const [loading, setLoading] = useState(false)
const [modalOpen, setModalOpen] = useState(false)
const [editingRole, setEditingRole] = useState<Role | null>(null)
const [permissions, setPermissions] = useState<Permission[]>([])
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 })
const [form] = Form.useForm()
useEffect(() => {
loadRoles()
loadPermissions()
}, [])
async function loadRoles() {
setLoading(true)
try {
const params: RolePageRequest = { page: pagination.current - 1, size: pagination.pageSize }
const res = await roleApi.getPage(params)
const data = res as unknown as PageResponse<Role>
setRoles(data.content)
setPagination((prev) => ({ ...prev, total: data.totalElements }))
} catch {
message.error('加载角色列表失败')
} finally {
setLoading(false)
}
}
async function loadPermissions() {
try {
const res = await roleApi.getAllPermissions()
setPermissions(Array.isArray(res) ? res : [])
} catch { /* ignored */ }
}
function handleAdd() {
setEditingRole(null)
form.resetFields()
setModalOpen(true)
}
function handleEdit(record: Role) {
setEditingRole(record)
form.setFieldsValue({
roleName: record.roleName,
roleKey: record.roleKey,
roleSort: record.roleSort,
status: record.status,
permissions: record.permissions?.map((p) => p.id),
})
setModalOpen(true)
}
async function handleDelete(id: number) {
try {
await roleApi.delete(id)
message.success('删除成功')
loadRoles()
} catch {
message.error('删除失败')
}
}
async function handleSubmit() {
try {
const values = await form.validateFields()
if (editingRole) {
const data: UpdateRoleRequest = { ...values }
await roleApi.update(editingRole.id, data)
message.success('更新成功')
} else {
const data: CreateRoleRequest = { ...values }
await roleApi.create(data)
message.success('创建成功')
}
setModalOpen(false)
loadRoles()
} catch { /* ignored */ }
}
const permissionTreeData = buildPermissionTree(permissions)
const columns: ColumnsType<Role> = [
{ title: '角色名称', dataIndex: 'roleName', key: 'roleName' },
{ title: '角色标识', dataIndex: 'roleKey', key: 'roleKey' },
{ title: '排序', dataIndex: 'roleSort', key: 'roleSort', width: 80 },
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: (status: RoleStatus) => {
const info = roleStatusMap[status]
return <Tag color={info?.color || 'default'}>{info?.label || status}</Tag>
},
},
{
title: '操作',
key: 'action',
render: (_, record) => (
<Space>
<PermissionGuard permission="system:role:edit">
<Button type="link" icon={<EditOutlined />} onClick={() => handleEdit(record)} />
</PermissionGuard>
<PermissionGuard permission="system:role:delete">
<Popconfirm title="确认删除?" onConfirm={() => handleDelete(record.id)}>
<Button type="link" danger icon={<DeleteOutlined />} />
</Popconfirm>
</PermissionGuard>
</Space>
),
},
]
return (
<div style={{ padding: 24 }}>
<div style={{ marginBottom: 16 }}>
<Space>
<PermissionGuard permission="system:role:add">
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}></Button>
</PermissionGuard>
<Button icon={<ReloadOutlined />} onClick={loadRoles}></Button>
</Space>
</div>
<Table<Role>
rowKey="id"
columns={columns}
dataSource={roles}
loading={loading}
pagination={{
...pagination,
showSizeChanger: true,
showTotal: (total) => `${total}`,
onChange: (page, pageSize) => {
setPagination((prev) => ({ ...prev, current: page, pageSize }))
setTimeout(loadRoles, 0)
},
}}
/>
<Modal
title={editingRole ? '编辑角色' : '新增角色'}
open={modalOpen}
onOk={handleSubmit}
onCancel={() => setModalOpen(false)}
destroyOnHidden
width={600}
>
<Form form={form} layout="vertical">
<Form.Item name="roleName" label="角色名称" rules={VALIDATION.roleName.rules}>
<Input />
</Form.Item>
<Form.Item name="roleKey" label="角色标识" rules={VALIDATION.roleKey.rules}>
<Input />
</Form.Item>
<Form.Item name="roleSort" label="排序" initialValue={VALIDATION.roleSort.initialValue} rules={VALIDATION.roleSort.rules}>
<InputNumber min={1} style={{ width: '100%' }} />
</Form.Item>
<Form.Item name="status" label="状态" initialValue={RoleStatus.ACTIVE}>
<Select options={Object.entries(roleStatusMap).map(([value, info]) => ({ label: info.label, value }))} />
</Form.Item>
<Form.Item name="permissions" label="权限">
<TreeSelect
treeData={permissionTreeData}
multiple
treeCheckable
showCheckedStrategy={TreeSelect.SHOW_CHILD}
placeholder="请选择权限"
style={{ width: '100%' }}
/>
</Form.Item>
</Form>
</Modal>
</div>
)
}
function buildPermissionTree(permissions: Permission[]) {
const grouped: Record<string, Permission[]> = {}
for (const p of permissions) {
const key = p.resource
if (!grouped[key]) grouped[key] = []
grouped[key].push(p)
}
return Object.entries(grouped).map(([resource, items]) => ({
title: resource,
value: `resource-${resource}`,
key: `resource-${resource}`,
children: items.map((item) => ({
title: `${item.name} (${item.action})`,
value: item.id,
key: item.id,
})),
}))
}