// ============================================================ // 账号管理 // ============================================================ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { Button, message, Tag, Modal, Form, Input, Select, Popconfirm, Space } from 'antd' import { PlusOutlined } from '@ant-design/icons' import type { ProColumns } from '@ant-design/pro-components' import { ProTable } from '@ant-design/pro-components' import { accountService } from '@/services/accounts' import type { AccountPublic } from '@/types' const roleLabels: Record = { super_admin: '超级管理员', admin: '管理员', user: '用户', } const roleColors: Record = { super_admin: 'red', admin: 'blue', user: 'default', } const statusLabels: Record = { active: '正常', disabled: '已禁用', suspended: '已封禁', } const statusColors: Record = { active: 'green', disabled: 'default', suspended: 'red', } export default function Accounts() { const queryClient = useQueryClient() const [form] = Form.useForm() const [modalOpen, setModalOpen] = useState(false) const [editingId, setEditingId] = useState(null) const { data, isLoading } = useQuery({ queryKey: ['accounts'], queryFn: ({ signal }) => accountService.list(signal), }) const updateMutation = useMutation({ mutationFn: ({ id, data }: { id: string; data: Partial }) => accountService.update(id, data), onSuccess: () => { message.success('更新成功') queryClient.invalidateQueries({ queryKey: ['accounts'] }) setModalOpen(false) }, onError: (err: Error) => message.error(err.message || '更新失败'), }) const statusMutation = useMutation({ mutationFn: ({ id, status }: { id: string; status: AccountPublic['status'] }) => accountService.updateStatus(id, { status }), onSuccess: () => { message.success('状态更新成功') queryClient.invalidateQueries({ queryKey: ['accounts'] }) }, onError: (err: Error) => message.error(err.message || '状态更新失败'), }) const columns: ProColumns[] = [ { title: '用户名', dataIndex: 'username', width: 120 }, { title: '显示名', dataIndex: 'display_name', width: 120 }, { title: '邮箱', dataIndex: 'email', width: 180 }, { title: '角色', dataIndex: 'role', width: 120, render: (_, record) => {roleLabels[record.role] || record.role}, }, { title: '状态', dataIndex: 'status', width: 100, render: (_, record) => {statusLabels[record.status] || record.status}, }, { title: '2FA', dataIndex: 'totp_enabled', width: 80, render: (_, record) => record.totp_enabled ? 已启用 : 未启用, }, { title: '最后登录', dataIndex: 'last_login_at', width: 180, render: (_, record) => record.last_login_at ? new Date(record.last_login_at).toLocaleString('zh-CN') : '-', }, { title: '操作', width: 200, render: (_, record) => ( {record.status === 'active' ? ( statusMutation.mutate({ id: record.id, status: 'disabled' })}> ) : ( statusMutation.mutate({ id: record.id, status: 'active' })}> )} ), }, ] const handleSave = async () => { const values = await form.validateFields() if (editingId) { updateMutation.mutate({ id: editingId, data: values }) } } return (
columns={columns} dataSource={data?.items ?? []} loading={isLoading} rowKey="id" search={false} toolBarRender={() => []} pagination={{ total: data?.total ?? 0, pageSize: data?.page_size ?? 20, current: data?.page ?? 1, showSizeChanger: false, }} /> { setModalOpen(false); setEditingId(null); form.resetFields() }} confirmLoading={updateMutation.isPending} >