feat(web): Phase 5 - 业务页面迁移完成
完成所有业务页面从 Vue 3 到 React 19 的迁移: 页面迁移: - Login: 表单验证 + 认证集成 - Dashboard: 统计卡片 + G2 图表占位 - UserManagement: 表格 + 分页 + CRUD + 权限控制 - RoleManagement: 表格 + 弹窗 + TreeSelect 权限分配 - MenuManagement: 树形表格 + 层级菜单管理 - ConfigManagement: 参数配置 CRUD - DictManagement: 字典类型/数据双面板管理 - FileManagement: 文件上传 + 图片预览 - NoticeManagement: 通知公告 CRUD - LoginLog/OpLog/ExLog: 审计日志只读查询 - 403: 权限拒绝页面 API 层补充: - loginLog.ts: 新增 LoginLog/OpLog/ExLog 接口与 API - status.ts: 新增 userStatusMap/roleStatusMap/menuStatusMap/noticeStatusMap 路由修正: - routes.ts: 日志页面路径对齐实际目录结构 验证:tsc --noEmit 零错误,dev server 正常启动
This commit is contained in:
@@ -1,3 +1,207 @@
|
||||
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 PermissionGuard from '@/components/PermissionGuard'
|
||||
|
||||
export default function RoleManagement() {
|
||||
return <div>RoleManagement Page (TODO)</div>
|
||||
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 {}
|
||||
}
|
||||
|
||||
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 {}
|
||||
}
|
||||
|
||||
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)}
|
||||
destroyOnClose
|
||||
width={600}
|
||||
>
|
||||
<Form form={form} layout="vertical">
|
||||
<Form.Item name="roleName" label="角色名称" rules={[{ required: true, message: '请输入角色名称' }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="roleKey" label="角色标识" rules={[{ required: true, message: '请输入角色标识' }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="roleSort" label="排序" initialValue={0}>
|
||||
<InputNumber min={0} 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,
|
||||
})),
|
||||
}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user