import React, { useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { format } from 'date-fns'; import { Plus, Search, Edit, Trash2 } 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-shadcn/components/DataTablePagination'; import AvatarSelector from '@/client/admin-shadcn/components/AvatarSelector'; 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'; // 使用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, search: '' }); const [isModalOpen, setIsModalOpen] = useState(false); const [editingUser, setEditingUser] = useState(null); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [userToDelete, setUserToDelete] = useState(null); // Avatar selector is now integrated, no separate state needed 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], queryFn: async () => { const res = await userClient.$get({ query: { page: searchParams.page, pageSize: searchParams.limit, keyword: searchParams.search } }); if (res.status !== 200) { throw new Error('获取用户列表失败'); } return await res.json(); } }); const users = usersData?.data || []; const totalCount = usersData?.pagination?.total || 0; // 处理搜索 const handleSearch = (e: React.FormEvent) => { e.preventDefault(); setSearchParams(prev => ({ ...prev, page: 1 })); }; // 处理分页 const handlePageChange = (page: number, limit: number) => { setSearchParams(prev => ({ ...prev, page, limit })); }; // 打开创建用户对话框 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, avatarFileId: user.avatarFileId, 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 (error) { console.error('创建用户失败:', error); 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 (error) { console.error('更新用户失败:', error); 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 (error) { console.error('删除用户失败:', error); toast.error('删除失败,请重试'); } finally { setDeleteDialogOpen(false); setUserToDelete(null); } }; // 渲染加载骨架 if (isLoading) { return (

用户管理

); } return (

用户管理

用户列表 管理系统中的所有用户,共 {totalCount} 位用户
setSearchParams(prev => ({ ...prev, search: e.target.value }))} className="pl-8" />
头像 用户名 昵称 邮箱 真实姓名 角色 状态 创建时间 操作 {users.map((user) => (
{user.avatarFile?.fullUrl ? ( {user.username} ) : (
{user.username?.charAt(0)?.toUpperCase() || 'U'}
)}
{user.username} {user.nickname || '-'} {user.email || '-'} {user.name || '-'} role.name === 'admin') ? 'destructive' : 'default'} className="capitalize" > {user.roles?.some((role: any) => role.name === 'admin') ? '管理员' : '普通用户'} {user.isDisabled === 1 ? '禁用' : '启用'} {format(new Date(user.createdAt), 'yyyy-MM-dd HH:mm')}
))}
{/* 创建/编辑用户对话框 */} {editingUser ? '编辑用户' : '创建用户'} {editingUser ? '编辑现有用户信息' : '创建一个新的用户账户'} {isCreateForm ? (
( 用户名 * )} /> ( 昵称 )} /> ( 邮箱 )} /> ( 手机号 )} /> ( 真实姓名 )} /> ( 密码 * )} /> ( 头像 field.onChange(value)} maxSize={2} uploadPath="/avatars" uploadButtonText="上传头像" previewSize="medium" placeholder="选择头像" /> )} /> (
用户状态 禁用后用户将无法登录系统
field.onChange(checked ? 1 : 0)} />
)} /> ) : (
( 用户名 * )} /> ( 昵称 )} /> ( 邮箱 )} /> ( 手机号 )} /> ( 真实姓名 )} /> ( 新密码 )} /> ( 头像 field.onChange(value)} maxSize={2} uploadPath="/avatars" uploadButtonText="上传头像" previewSize="medium" placeholder="选择头像" /> )} /> (
用户状态 禁用后用户将无法登录系统
field.onChange(checked ? 1 : 0)} />
)} /> )}
{/* Avatar selector is now integrated within the form */} {/* 删除确认对话框 */} 确认删除 确定要删除这个用户吗?此操作无法撤销。
); };