import React, { useState, useMemo, useCallback } from 'react'; import { useQuery } from '@tanstack/react-query'; import { format } from 'date-fns'; import { Search, Filter, X, Download } from 'lucide-react'; import { passengerClient } from '@/client/api'; import type { InferResponseType } from 'hono/client'; import * as XLSX from 'xlsx'; 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 { DataTablePagination } from '@/client/admin/components/DataTablePagination'; import { Skeleton } from '@/client/components/ui/skeleton'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/client/components/ui/select'; import { toast } from 'sonner'; // 使用RPC方式提取类型 type PassengerResponse = InferResponseType['data'][0]; export const PassengersPage = () => { const [searchParams, setSearchParams] = useState({ page: 1, limit: 10, keyword: '' }); const [filters, setFilters] = useState({ userId: undefined as number | undefined, idType: undefined as string | undefined }); const [showFilters, setShowFilters] = useState(false); const { data: passengersData, isLoading } = useQuery({ queryKey: ['passengers', searchParams, filters], queryFn: async () => { const filterParams: Record = {}; if (filters.userId !== undefined) { filterParams.userId = filters.userId; } if (filters.idType !== undefined) { filterParams.idType = filters.idType; } const res = await passengerClient.$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 passengers = passengersData?.data || []; const totalCount = passengersData?.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({ userId: undefined, idType: undefined }); setSearchParams(prev => ({ ...prev, page: 1 })); }; // 检查是否有活跃的过滤条件 const hasActiveFilters = useMemo(() => { return filters.userId !== undefined || filters.idType !== undefined; }, [filters]); // 导出乘客数据 const handleExport = async () => { try { const res = await passengerClient.$get({ query: { page: 1, pageSize: totalCount, keyword: searchParams.keyword, filters: hasActiveFilters ? JSON.stringify(filters) : undefined } }); if (res.status !== 200) { throw new Error('获取导出数据失败'); } const data = await res.json(); const passengers = data.data; // 准备Excel数据 const excelData = passengers.map((passenger: PassengerResponse) => ({ '姓名': passenger.name, '证件类型': passenger.idType, '证件号码': passenger.idNumber, '手机号': passenger.phone, '默认乘客': passenger.isDefault ? '是' : '否', '所属用户': passenger.user?.username || '未知用户', '创建时间': format(new Date(passenger.createdAt), 'yyyy-MM-dd HH:mm') })); // 创建工作簿 const wb = XLSX.utils.book_new(); // 创建工作表 const ws = XLSX.utils.json_to_sheet(excelData); // 设置列宽 const colWidths = [ { wch: 12 }, // 姓名 { wch: 12 }, // 证件类型 { wch: 20 }, // 证件号码 { wch: 15 }, // 手机号 { wch: 10 }, // 默认乘客 { wch: 15 }, // 所属用户 { wch: 20 } // 创建时间 ]; ws['!cols'] = colWidths; // 添加工作表到工作簿 XLSX.utils.book_append_sheet(wb, ws, '乘客信息'); // 生成Excel文件并下载 const fileName = `乘客信息_${format(new Date(), 'yyyy-MM-dd_HH-mm')}.xlsx`; XLSX.writeFile(wb, fileName); toast.success('导出成功'); } catch (error) { console.error('导出失败:', error); toast.error('导出失败,请重试'); } }; // 渲染表格部分的骨架屏 const renderTableSkeleton = () => (
{Array.from({ length: 5 }).map((_, index) => (
))}
); return (

乘客信息管理

乘客列表 管理系统中的所有乘客信息,共 {totalCount} 位乘客
{hasActiveFilters && ( )}
{showFilters && (
{/* 证件类型筛选 */}
{/* 用户筛选 */}
handleFilterChange({ userId: e.target.value ? parseInt(e.target.value) : undefined }) } />
)} {/* 过滤条件标签 */} {hasActiveFilters && (
{filters.idType && ( 证件类型: {filters.idType} handleFilterChange({ idType: undefined })} /> )} {filters.userId !== undefined && ( 用户ID: {filters.userId} handleFilterChange({ userId: undefined })} /> )}
)}
姓名 证件类型 证件号码 手机号 默认乘客 所属用户 创建时间 {isLoading ? ( // 显示表格骨架屏 {renderTableSkeleton()} ) : ( // 显示实际乘客数据 passengers.map((passenger) => ( {passenger.name} {passenger.idType} {passenger.idNumber} {passenger.phone} {passenger.isDefault ? '是' : '否'} {passenger.user?.username || '未知用户'} {format(new Date(passenger.createdAt), 'yyyy-MM-dd HH:mm')} )) )}
); };