import { useEffect, useState, useCallback, useRef } from 'react'; import { Table, Button, Space, Modal, Form, Input, Tag, Popconfirm, Checkbox, message, theme, } from 'antd'; import { PlusOutlined, SearchOutlined, EditOutlined, DeleteOutlined, UserOutlined, SafetyCertificateOutlined, StopOutlined, CheckCircleOutlined, } from '@ant-design/icons'; import { listUsers, createUser, updateUser, deleteUser, assignRoles, type CreateUserRequest, type UpdateUserRequest, } from '../api/users'; import { listRoles, type RoleInfo } from '../api/roles'; import type { UserInfo } from '../api/auth'; const STATUS_COLOR_MAP: Record = { active: '#059669', disabled: '#dc2626', locked: '#d97706', }; const STATUS_BG_MAP: Record = { active: '#ECFDF5', disabled: '#FEF2F2', locked: '#FFFBEB', }; const STATUS_LABEL_MAP: Record = { active: '正常', disabled: '禁用', locked: '锁定', }; export default function Users() { const [users, setUsers] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [loading, setLoading] = useState(false); const [searchText, setSearchText] = useState(''); const [createModalOpen, setCreateModalOpen] = useState(false); const [editUser, setEditUser] = useState(null); const [roleModalOpen, setRoleModalOpen] = useState(false); const [selectedUser, setSelectedUser] = useState(null); const [allRoles, setAllRoles] = useState([]); const [selectedRoleIds, setSelectedRoleIds] = useState([]); const [form] = Form.useForm(); const { token } = theme.useToken(); const isDark = token.colorBgContainer === '#111827' || token.colorBgContainer === 'rgb(17, 24, 39)'; const fetchUsers = useCallback(async (p = page) => { setLoading(true); try { const result = await listUsers(p, 20, searchText); setUsers(result.data); setTotal(result.total); } catch { message.error('加载用户列表失败'); } setLoading(false); }, [page, searchText]); // 搜索防抖:输入后 300ms 才触发查询 const debounceTimer = useRef | null>(null); const debouncedSearch = useCallback((_text: string) => { if (debounceTimer.current) clearTimeout(debounceTimer.current); debounceTimer.current = setTimeout(() => { setPage(1); }, 300); }, []); const fetchRoles = useCallback(async () => { try { const result = await listRoles(); setAllRoles(result.data); } catch { // 静默处理 } }, []); useEffect(() => { fetchUsers(); fetchRoles(); }, [fetchUsers, fetchRoles]); const handleCreateOrEdit = async (values: { username: string; password?: string; display_name?: string; email?: string; phone?: string; }) => { try { if (editUser) { const req: UpdateUserRequest = { display_name: values.display_name, email: values.email, phone: values.phone, version: editUser.version, }; await updateUser(editUser.id, req); message.success('用户更新成功'); } else { const req: CreateUserRequest = { username: values.username, password: values.password ?? '', display_name: values.display_name, email: values.email, phone: values.phone, }; await createUser(req); message.success('用户创建成功'); } setCreateModalOpen(false); setEditUser(null); form.resetFields(); fetchUsers(); } catch (err: unknown) { const errorMsg = (err as { response?: { data?: { message?: string } } })?.response?.data?.message || '操作失败'; message.error(errorMsg); } }; const handleDelete = async (id: string) => { try { await deleteUser(id); message.success('用户已删除'); fetchUsers(); } catch { message.error('删除失败'); } }; const handleToggleStatus = async (id: string, status: string) => { try { const user = users.find(u => u.id === id); if (!user) return; await updateUser(id, { status, version: user.version }); message.success(status === 'disabled' ? '用户已禁用' : '用户已启用'); fetchUsers(); } catch { message.error('状态更新失败'); } }; const handleAssignRoles = async () => { if (!selectedUser) return; try { await assignRoles(selectedUser.id, selectedRoleIds); message.success('角色分配成功'); setRoleModalOpen(false); fetchUsers(); } catch { message.error('角色分配失败'); } }; const openCreateModal = () => { setEditUser(null); form.resetFields(); setCreateModalOpen(true); }; const openEditModal = (user: UserInfo) => { setEditUser(user); form.setFieldsValue({ username: user.username, display_name: user.display_name, email: user.email, phone: user.phone, }); setCreateModalOpen(true); }; const closeCreateModal = () => { setCreateModalOpen(false); setEditUser(null); form.resetFields(); }; const openRoleModal = (user: UserInfo) => { setSelectedUser(user); setSelectedRoleIds(user.roles.map((r) => r.id)); setRoleModalOpen(true); }; const filteredUsers = users; const columns = [ { title: '用户', dataIndex: 'username', key: 'username', render: (v: string, record: UserInfo) => (
{(record.display_name?.[0] || v?.[0] || 'U').toUpperCase()}
{v}
{record.display_name && (
{record.display_name}
)}
), }, { title: '邮箱', dataIndex: 'email', key: 'email', render: (v: string | undefined) => v || '-', }, { title: '电话', dataIndex: 'phone', key: 'phone', render: (v: string | undefined) => v || '-', }, { title: '状态', dataIndex: 'status', key: 'status', width: 100, render: (status: string) => ( {STATUS_LABEL_MAP[status] || status} ), }, { title: '角色', dataIndex: 'roles', key: 'roles', render: (roles: RoleInfo[]) => roles.length > 0 ? roles.map((r) => ( {r.name} )) : -, }, { title: '操作', key: 'actions', width: 240, render: (_: unknown, record: UserInfo) => ( {/* 表格容器 */}
{ setPage(p); fetchUsers(p); }, showTotal: (t) => `共 ${t} 条记录`, style: { padding: '12px 16px', margin: 0 }, }} /> {/* 新建/编辑用户弹窗 */} form.submit()} width={480} >
} disabled={!!editUser} /> {!editUser && ( )}
{/* 角色分配弹窗 */} setRoleModalOpen(false)} onOk={handleAssignRoles} width={480} >
setSelectedRoleIds(values as string[])} style={{ display: 'flex', flexDirection: 'column', gap: 12 }} > {allRoles.map((r) => (
{r.name} {r.code}
))}
); }