import React, { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Plus, Search, Edit, Trash2, CreditCard } from 'lucide-react'; import { format } from 'date-fns'; import { zhCN } from 'date-fns/locale'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { toast } from 'sonner'; 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 { 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 { Badge } from '@/client/components/ui/badge'; import { Skeleton } from '@/client/components/ui/skeleton'; import { DataTablePagination } from '@/client/admin/components/DataTablePagination'; import { userCardClient } from '@/client/api'; import type { InferRequestType, InferResponseType } from 'hono/client'; import { CreateUserCardDto, UpdateUserCardDto } from '@d8d/server/modules/user-cards/user-card.schema'; import { UserSelector } from '@/client/admin/components/UserSelector'; import { AgentSelector } from '@/client/admin/components/AgentSelector'; type CreateRequest = InferRequestType['json']; type UpdateRequest = InferRequestType['json']; type UserCardResponse = InferResponseType['data'][0]; const createFormSchema = CreateUserCardDto; const updateFormSchema = UpdateUserCardDto; export const UserCardsPage = () => { const queryClient = useQueryClient(); const [searchParams, setSearchParams] = useState({ page: 1, limit: 10, search: '' }); const [isModalOpen, setIsModalOpen] = useState(false); const [isCreateForm, setIsCreateForm] = useState(true); const [editingCard, setEditingCard] = useState(null); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [cardToDelete, setCardToDelete] = useState(null); // 表单实例 const createForm = useForm({ resolver: zodResolver(createFormSchema), defaultValues: { userId: 0, cardNo: '', password: '', balance: 0, state: 1, isDefault: 2 } }); const updateForm = useForm({ resolver: zodResolver(updateFormSchema), defaultValues: {} }); // 数据查询 const { data, isLoading, refetch } = useQuery({ queryKey: ['userCards', searchParams], queryFn: async () => { const res = await userCardClient.$get({ query: { page: searchParams.page, pageSize: searchParams.limit, keyword: searchParams.search } }); if (res.status !== 200) throw new Error('获取用户卡列表失败'); return await res.json(); } }); // 创建用户卡 const createMutation = useMutation({ mutationFn: async (data: CreateRequest) => { const res = await userCardClient.$post({ json: data }); if (res.status !== 201) throw new Error('创建用户卡失败'); return await res.json(); }, onSuccess: () => { toast.success('用户卡创建成功'); setIsModalOpen(false); createForm.reset(); refetch(); }, onError: (error) => { toast.error(error.message || '创建用户卡失败'); } }); // 更新用户卡 const updateMutation = useMutation({ mutationFn: async ({ id, data }: { id: number; data: UpdateRequest }) => { const res = await userCardClient[':id']['$put']({ param: { id: id.toString() }, json: data }); if (res.status !== 200) throw new Error('更新用户卡失败'); return await res.json(); }, onSuccess: () => { toast.success('用户卡更新成功'); setIsModalOpen(false); updateForm.reset(); refetch(); }, onError: (error) => { toast.error(error.message || '更新用户卡失败'); } }); // 删除用户卡 const deleteMutation = useMutation({ mutationFn: async (id: number) => { const res = await userCardClient[':id']['$delete']({ param: { id: id.toString() } }); if (res.status !== 204) throw new Error('删除用户卡失败'); return res; }, onSuccess: () => { toast.success('用户卡删除成功'); setDeleteDialogOpen(false); refetch(); }, onError: (error) => { toast.error(error.message || '删除用户卡失败'); } }); // 处理搜索 const handleSearch = (e: React.FormEvent) => { e.preventDefault(); setSearchParams(prev => ({ ...prev, page: 1 })); }; // 处理创建用户卡 const handleCreateCard = () => { setIsCreateForm(true); setEditingCard(null); createForm.reset(); setIsModalOpen(true); }; // 处理编辑用户卡 const handleEditCard = (card: UserCardResponse) => { setIsCreateForm(false); setEditingCard(card); updateForm.reset({ userId: card.userId, agentId: card.agentId || undefined, cardNo: card.cardNo, sjtCardNo: card.sjtCardNo || undefined, password: card.password, authCode: card.authCode || undefined, state: card.state, balance: card.balance, isDefault: card.isDefault }); setIsModalOpen(true); }; // 处理删除用户卡 const handleDeleteCard = (id: number) => { setCardToDelete(id); setDeleteDialogOpen(true); }; // 确认删除 const confirmDelete = () => { if (cardToDelete) { deleteMutation.mutate(cardToDelete); } }; // 加载状态 if (isLoading) { return (
{[...Array(5)].map((_, i) => (
))}
); } return (
{/* 页面标题 */}

用户卡管理

管理用户卡和余额信息

{/* 搜索区域 */} 用户卡列表 查看和管理所有用户卡
setSearchParams(prev => ({ ...prev, search: e.target.value }))} className="pl-8" />
{/* 数据表格 */} ID 用户 卡号 盛京通卡号 代理商 余额 状态 默认 创建时间 操作 {data?.data.map((card) => ( {card.id} {card.user?.name || card.user?.username || '-'} {card.cardNo} {card.sjtCardNo || '-'} {card.agent?.name || '-'} ¥{card.balance.toFixed(2)} {card.state === 1 ? '绑定' : '解绑'} {card.isDefault === 1 ? '是' : '否'} {format(new Date(card.createdAt), 'yyyy-MM-dd HH:mm', { locale: zhCN })}
))}
{data?.data.length === 0 && (

暂无用户卡数据

)}
{/* 分页 */} {data && data.data.length > 0 && ( setSearchParams(prev => ({ ...prev, page, limit }))} /> )} {/* 创建/编辑模态框 */} {isCreateForm ? '创建用户卡' : '编辑用户卡'} {isCreateForm ? '创建一个新的用户卡' : '编辑现有用户卡信息'} {isCreateForm ? (
createMutation.mutate(data))} className="space-y-4"> ( 用户 * )} /> ( 卡号 * )} /> ( 密码 * )} /> ( 代理商 )} /> ( 盛京通卡号 )} /> ( 初始余额 field.onChange(parseFloat(e.target.value))} /> )} /> ( 状态 )} /> ( 是否默认 )} /> ) : (
updateMutation.mutate({ id: editingCard!.id, data }))} className="space-y-4"> ( 用户 * )} /> ( 卡号 * )} /> ( 密码 * )} /> ( 代理商 )} /> ( 盛京通卡号 )} /> ( 余额 field.onChange(parseFloat(e.target.value))} /> )} /> ( 状态 )} /> ( 是否默认 )} /> )}
{/* 删除确认对话框 */} 确认删除 确定要删除这张用户卡吗?此操作将同时删除相关的余额记录。
); };