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,198 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Table, Button, Modal, Form, Input, Select, Tag, Space, message, Popconfirm } from 'antd'
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined, ReloadOutlined } from '@ant-design/icons'
|
||||
import type { ColumnsType } from 'antd/es/table'
|
||||
import { userApi } from '@/api/user.api'
|
||||
import { roleApi } from '@/api/role.api'
|
||||
import type { User, CreateUserRequest, UpdateUserRequest, UserPageRequest } from '@/api/user.api'
|
||||
import type { Role } from '@/api/role.api'
|
||||
import type { PageResponse } from '@/api/user.api'
|
||||
import { UserStatus, userStatusMap } from '@/constants/status'
|
||||
import PermissionGuard from '@/components/PermissionGuard'
|
||||
|
||||
export default function UserManagement() {
|
||||
return <div>UserManagement Page (TODO)</div>
|
||||
const [users, setUsers] = useState<User[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [modalOpen, setModalOpen] = useState(false)
|
||||
const [editingUser, setEditingUser] = useState<User | null>(null)
|
||||
const [roles, setRoles] = useState<Role[]>([])
|
||||
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 })
|
||||
const [form] = Form.useForm()
|
||||
|
||||
useEffect(() => {
|
||||
loadUsers()
|
||||
loadRoles()
|
||||
}, [])
|
||||
|
||||
async function loadUsers() {
|
||||
setLoading(true)
|
||||
try {
|
||||
const params: UserPageRequest = { page: pagination.current - 1, size: pagination.pageSize }
|
||||
const res = await userApi.getPage(params)
|
||||
const data = res as unknown as PageResponse<User>
|
||||
setUsers(data.content)
|
||||
setPagination((prev) => ({ ...prev, total: data.totalElements }))
|
||||
} catch {
|
||||
message.error('加载用户列表失败')
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
async function loadRoles() {
|
||||
try {
|
||||
const res = await roleApi.getAll()
|
||||
setRoles(Array.isArray(res) ? res : [])
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
setEditingUser(null)
|
||||
form.resetFields()
|
||||
setModalOpen(true)
|
||||
}
|
||||
|
||||
function handleEdit(record: User) {
|
||||
setEditingUser(record)
|
||||
form.setFieldsValue({
|
||||
nickname: record.nickname,
|
||||
email: record.email,
|
||||
phone: record.phone,
|
||||
roles: record.roles,
|
||||
status: record.status,
|
||||
})
|
||||
setModalOpen(true)
|
||||
}
|
||||
|
||||
async function handleDelete(id: string) {
|
||||
try {
|
||||
await userApi.delete(id)
|
||||
message.success('删除成功')
|
||||
loadUsers()
|
||||
} catch {
|
||||
message.error('删除失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
const values = await form.validateFields()
|
||||
if (editingUser) {
|
||||
const data: UpdateUserRequest = { ...values }
|
||||
await userApi.update(editingUser.id, data)
|
||||
message.success('更新成功')
|
||||
} else {
|
||||
const data: CreateUserRequest = { ...values }
|
||||
await userApi.create(data)
|
||||
message.success('创建成功')
|
||||
}
|
||||
setModalOpen(false)
|
||||
loadUsers()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const columns: ColumnsType<User> = [
|
||||
{ title: '用户名', dataIndex: 'username', key: 'username' },
|
||||
{ title: '昵称', dataIndex: 'nickname', key: 'nickname' },
|
||||
{ title: '邮箱', dataIndex: 'email', key: 'email' },
|
||||
{ title: '手机', dataIndex: 'phone', key: 'phone' },
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
render: (status: UserStatus) => {
|
||||
const info = userStatusMap[status]
|
||||
return <Tag color={info?.color || 'default'}>{info?.label || status}</Tag>
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '角色',
|
||||
dataIndex: 'roles',
|
||||
key: 'roles',
|
||||
render: (roles: string[]) => roles?.map((r) => <Tag key={r}>{r}</Tag>),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
render: (_, record) => (
|
||||
<Space>
|
||||
<PermissionGuard permission="system:user:edit">
|
||||
<Button type="link" icon={<EditOutlined />} onClick={() => handleEdit(record)} />
|
||||
</PermissionGuard>
|
||||
<PermissionGuard permission="system:user: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, display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Space>
|
||||
<PermissionGuard permission="system:user:add">
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>新增用户</Button>
|
||||
</PermissionGuard>
|
||||
<Button icon={<ReloadOutlined />} onClick={loadUsers}>刷新</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<Table<User>
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={users}
|
||||
loading={loading}
|
||||
pagination={{
|
||||
...pagination,
|
||||
showSizeChanger: true,
|
||||
showTotal: (total) => `共 ${total} 条`,
|
||||
onChange: (page, pageSize) => {
|
||||
setPagination((prev) => ({ ...prev, current: page, pageSize }))
|
||||
setTimeout(loadUsers, 0)
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
title={editingUser ? '编辑用户' : '新增用户'}
|
||||
open={modalOpen}
|
||||
onOk={handleSubmit}
|
||||
onCancel={() => setModalOpen(false)}
|
||||
destroyOnClose
|
||||
>
|
||||
<Form form={form} layout="vertical">
|
||||
{!editingUser && (
|
||||
<Form.Item name="username" label="用户名" rules={[{ required: true, message: '请输入用户名' }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
)}
|
||||
{!editingUser && (
|
||||
<Form.Item name="password" label="密码" rules={[{ required: true, message: '请输入密码' }]}>
|
||||
<Input.Password />
|
||||
</Form.Item>
|
||||
)}
|
||||
<Form.Item name="nickname" label="昵称">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="email" label="邮箱">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="phone" label="手机">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="roles" label="角色">
|
||||
<Select mode="multiple" options={roles.map((r) => ({ label: r.roleName, value: r.roleKey }))} />
|
||||
</Form.Item>
|
||||
{editingUser && (
|
||||
<Form.Item name="status" label="状态">
|
||||
<Select options={Object.entries(userStatusMap).map(([value, info]) => ({ label: info.label, value }))} />
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user