1c5145f1a5
用户名添加长度(3-50)和格式校验,密码添加长度(8-20)和强度校验, 邮箱添加格式和长度校验,手机添加格式校验,昵称添加长度校验。
200 lines
6.4 KiB
TypeScript
200 lines
6.4 KiB
TypeScript
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 { VALIDATION } from '@/constants/validation-rules'
|
|
import PermissionGuard from '@/components/PermissionGuard'
|
|
|
|
export default function UserManagement() {
|
|
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 { /* ignored */ }
|
|
}
|
|
|
|
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 { /* ignored */ }
|
|
}
|
|
|
|
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)}
|
|
destroyOnHidden
|
|
>
|
|
<Form form={form} layout="vertical">
|
|
{!editingUser && (
|
|
<Form.Item name="username" label="用户名" rules={VALIDATION.username.rules}>
|
|
<Input />
|
|
</Form.Item>
|
|
)}
|
|
{!editingUser && (
|
|
<Form.Item name="password" label="密码" rules={VALIDATION.password.rules}>
|
|
<Input.Password />
|
|
</Form.Item>
|
|
)}
|
|
<Form.Item name="nickname" label="昵称" rules={VALIDATION.nickname.rules}>
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item name="email" label="邮箱" rules={VALIDATION.email.rules}>
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item name="phone" label="手机" rules={VALIDATION.phone.rules}>
|
|
<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>
|
|
)
|
|
}
|