import React, { useState, useMemo, useCallback } from 'react'; import { useQuery } from '@tanstack/react-query'; import { format } from 'date-fns'; import { Plus, Search, Edit, Trash2, Filter, X } from 'lucide-react'; import { userClient } from '@/client/api'; import type { InferRequestType, InferResponseType } from 'hono/client'; import { Button } from '@/client/components/ui/button'; import { Input } from '@/client/components/ui/input'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/client/components/ui/card'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/client/components/ui/table'; import { Badge } from '@/client/components/ui/badge'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/client/components/ui/dialog'; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/client/components/ui/form'; import { DataTablePagination } from '@/client/admin/components/DataTablePagination'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { toast } from 'sonner'; import { Skeleton } from '@/client/components/ui/skeleton'; import { Switch } from '@/client/components/ui/switch'; import { DisabledStatus } from '@/share/types'; import { CreateUserDto, UpdateUserDto } from '@/server/modules/users/user.schema'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/client/components/ui/select'; import { Popover, PopoverContent, PopoverTrigger } from '@/client/components/ui/popover'; import { Calendar } from '@/client/components/ui/calendar'; import { cn } from '@/client/lib/utils'; // 使用RPC方式提取类型 type CreateUserRequest = InferRequestType['json']; type UpdateUserRequest = InferRequestType['json']; type UserResponse = InferResponseType['data'][0]; // 直接使用后端定义的 schema const createUserFormSchema = CreateUserDto; const updateUserFormSchema = UpdateUserDto; type CreateUserFormData = CreateUserRequest; type UpdateUserFormData = UpdateUserRequest; export const UsersPage = () => { const [searchParams, setSearchParams] = useState({ page: 1, limit: 10, keyword: '' }); const [filters, setFilters] = useState({ isDisabled: undefined as number | undefined, roleIds: [] as number[], createdAt: undefined as { gte?: string; lte?: string } | undefined }); const [showFilters, setShowFilters] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false); const [editingUser, setEditingUser] = useState(null); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [userToDelete, setUserToDelete] = useState(null); const [isCreateForm, setIsCreateForm] = useState(true); const createForm = useForm({ resolver: zodResolver(createUserFormSchema), defaultValues: { username: '', nickname: undefined, email: null, phone: null, name: null, password: '', isDisabled: DisabledStatus.ENABLED, }, }); const updateForm = useForm({ resolver: zodResolver(updateUserFormSchema), defaultValues: { username: undefined, nickname: undefined, email: null, phone: null, name: null, password: undefined, isDisabled: undefined, }, }); const { data: usersData, isLoading, refetch } = useQuery({ queryKey: ['users', searchParams, filters], queryFn: async () => { const filterParams: Record = {}; if (filters.isDisabled !== undefined) { filterParams.isDisabled = filters.isDisabled; } if (filters.roleIds.length > 0) { filterParams['roles.id'] = filters.roleIds; } if (filters.createdAt) { filterParams.createdAt = filters.createdAt; } const res = await userClient.$get({ query: { page: searchParams.page, pageSize: searchParams.limit, keyword: searchParams.keyword, filters: Object.keys(filterParams).length > 0 ? JSON.stringify(filterParams) : undefined } }); if (res.status !== 200) { throw new Error('获取用户列表失败'); } return await res.json(); } }); const users = usersData?.data || []; const totalCount = usersData?.pagination?.total || 0; // 防抖搜索函数 const debounce = (func: Function, delay: number) => { let timeoutId: NodeJS.Timeout; return (...args: any[]) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func(...args), delay); }; }; // 使用useCallback包装防抖搜索 const debouncedSearch = useCallback( debounce((keyword: string) => { setSearchParams(prev => ({ ...prev, keyword, page: 1 })); }, 300), [] ); // 处理搜索输入变化 const handleSearchChange = (e: React.ChangeEvent) => { const keyword = e.target.value; setSearchParams(prev => ({ ...prev, keyword })); debouncedSearch(keyword); }; // 处理搜索表单提交 const handleSearch = (e: React.FormEvent) => { e.preventDefault(); setSearchParams(prev => ({ ...prev, page: 1 })); }; // 处理分页 const handlePageChange = (page: number, limit: number) => { setSearchParams(prev => ({ ...prev, page, limit })); }; // 处理过滤条件变化 const handleFilterChange = (newFilters: Partial) => { setFilters(prev => ({ ...prev, ...newFilters })); setSearchParams(prev => ({ ...prev, page: 1 })); }; // 重置所有过滤条件 const resetFilters = () => { setFilters({ isDisabled: undefined, roleIds: [], createdAt: undefined }); setSearchParams(prev => ({ ...prev, page: 1 })); }; // 检查是否有活跃的过滤条件 const hasActiveFilters = useMemo(() => { return filters.isDisabled !== undefined || filters.roleIds.length > 0 || filters.createdAt !== undefined; }, [filters]); // 打开创建用户对话框 const handleCreateUser = () => { setEditingUser(null); setIsCreateForm(true); createForm.reset({ username: '', nickname: null, email: null, phone: null, name: null, password: '', isDisabled: DisabledStatus.ENABLED, }); setIsModalOpen(true); }; // 打开编辑用户对话框 const handleEditUser = (user: UserResponse) => { setEditingUser(user); setIsCreateForm(false); updateForm.reset({ username: user.username, nickname: user.nickname, email: user.email, phone: user.phone, name: user.name, isDisabled: user.isDisabled, }); setIsModalOpen(true); }; // 处理创建表单提交 const handleCreateSubmit = async (data: CreateUserFormData) => { try { const res = await userClient.$post({ json: data }); if (res.status !== 201) { throw new Error('创建用户失败'); } toast.success('用户创建成功'); setIsModalOpen(false); refetch(); } catch { toast.error('创建失败,请重试'); } }; // 处理更新表单提交 const handleUpdateSubmit = async (data: UpdateUserFormData) => { if (!editingUser) return; try { const res = await userClient[':id']['$put']({ param: { id: editingUser.id }, json: data }); if (res.status !== 200) { throw new Error('更新用户失败'); } toast.success('用户更新成功'); setIsModalOpen(false); refetch(); } catch { toast.error('更新失败,请重试'); } }; // 处理删除用户 const handleDeleteUser = (id: number) => { setUserToDelete(id); setDeleteDialogOpen(true); }; const confirmDelete = async () => { if (!userToDelete) return; try { const res = await userClient[':id']['$delete']({ param: { id: userToDelete } }); if (res.status !== 204) { throw new Error('删除用户失败'); } toast.success('用户删除成功'); refetch(); } catch { toast.error('删除失败,请重试'); } finally { setDeleteDialogOpen(false); setUserToDelete(null); } }; // 渲染加载骨架 if (isLoading) { return (

用户管理

); } return (

用户管理

用户列表 管理系统中的所有用户,共 {totalCount} 位用户
{hasActiveFilters && ( )}
{showFilters && (
{/* 状态筛选 */}
{/* 角色筛选 */}
{filters.roleIds.length > 0 && (
{filters.roleIds.map(roleId => ( {roleId === 1 ? '管理员' : '普通用户'} handleFilterChange({ roleIds: filters.roleIds.filter(id => id !== roleId) })} /> ))}
)}
{/* 创建时间筛选 */}
{ handleFilterChange({ createdAt: range?.from && range?.to ? { gte: format(range.from, 'yyyy-MM-dd'), lte: format(range.to, 'yyyy-MM-dd') } : undefined }); }} initialFocus />
)} {/* 过滤条件标签 */} {hasActiveFilters && (
{filters.isDisabled !== undefined && ( 状态: {filters.isDisabled === 0 ? '启用' : '禁用'} handleFilterChange({ isDisabled: undefined })} /> )} {filters.roleIds.map(roleId => ( 角色: {roleId === 1 ? '管理员' : '普通用户'} handleFilterChange({ roleIds: filters.roleIds.filter(id => id !== roleId) })} /> ))} {filters.createdAt && ( 创建时间: {filters.createdAt.gte || ''} 至 {filters.createdAt.lte || ''} handleFilterChange({ createdAt: undefined })} /> )}
)}
用户名 昵称 邮箱 真实姓名 角色 状态 创建时间 操作 {users.map((user) => ( {user.username} {user.nickname || '-'} {user.email || '-'} {user.name || '-'} role.name === 'admin') ? 'destructive' : 'default'} className="capitalize" > {user.roles?.some((role) => role.name === 'admin') ? '管理员' : '普通用户'} {user.isDisabled === 1 ? '禁用' : '启用'} {format(new Date(user.createdAt), 'yyyy-MM-dd HH:mm')}
))}
{/* 创建/编辑用户对话框 */} {editingUser ? '编辑用户' : '创建用户'} {editingUser ? '编辑现有用户信息' : '创建一个新的用户账户'} {isCreateForm ? (
( 用户名 * )} /> ( 昵称 )} /> ( 邮箱 )} /> ( 手机号 )} /> ( 真实姓名 )} /> ( 密码 * )} /> (
用户状态 禁用后用户将无法登录系统
field.onChange(checked ? 1 : 0)} />
)} /> ) : (
( 用户名 * )} /> ( 昵称 )} /> ( 邮箱 )} /> ( 手机号 )} /> ( 真实姓名 )} /> ( 新密码 )} /> (
用户状态 禁用后用户将无法登录系统
field.onChange(checked ? 1 : 0)} />
)} /> )}
{/* 删除确认对话框 */} 确认删除 确定要删除这个用户吗?此操作无法撤销。
); };