import React, { useState, useMemo, useCallback } from 'react'; import { useQuery } from '@tanstack/react-query'; import { format } from 'date-fns'; import { Search, Filter, X, Eye } from 'lucide-react'; import { orderClient } from '@/client/api'; import type { 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 { 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 { OrderStatus, PaymentStatus } from '@d8d/server/share/order.types'; // 使用RPC方式提取类型 type OrderResponse = InferResponseType['data'][0]; export const OrdersPage = () => { const [searchParams, setSearchParams] = useState({ page: 1, pageSize: 10, search: '' }); const [filters, setFilters] = useState({ status: undefined as OrderStatus | undefined, paymentStatus: undefined as PaymentStatus | undefined }); const [showFilters, setShowFilters] = useState(false); const [detailDialogOpen, setDetailDialogOpen] = useState(false); const [selectedOrder, setSelectedOrder] = useState(null); // 获取订单列表 const { data: ordersData, isLoading } = useQuery({ queryKey: ['orders', searchParams, filters], queryFn: async () => { const res = await orderClient.$get({ query: { page: searchParams.page, pageSize: searchParams.pageSize, keyword: searchParams.search, filters: JSON.stringify({ status: filters.status, paymentStatus: filters.paymentStatus }) } }); if (res.status !== 200) { throw new Error('获取订单列表失败'); } return await res.json(); } }); // 获取订单统计 const { data: statsData } = useQuery({ queryKey: ['order-stats'], queryFn: async () => { const res = await orderClient.stats.$get(); if (res.status !== 200) { throw new Error('获取订单统计失败'); } return await res.json(); } }); const orders = ordersData?.data || []; const totalCount = ordersData?.pagination?.total || 0; const stats = statsData || { total: 0, pendingPayment: 0, waitingDeparture: 0, inProgress: 0, completed: 0, cancelled: 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((search: string) => { setSearchParams(prev => ({ ...prev, search, page: 1 })); }, 300), [] ); // 处理搜索输入变化 const handleSearchChange = (e: React.ChangeEvent) => { const search = e.target.value; setSearchParams(prev => ({ ...prev, search })); debouncedSearch(search); }; // 处理搜索表单提交 const handleSearch = (e: React.FormEvent) => { e.preventDefault(); setSearchParams(prev => ({ ...prev, page: 1 })); }; // 处理分页 const handlePageChange = (page: number, pageSize: number) => { setSearchParams(prev => ({ ...prev, page, pageSize })); }; // 处理过滤条件变化 const handleFilterChange = (newFilters: Partial) => { setFilters(prev => ({ ...prev, ...newFilters })); setSearchParams(prev => ({ ...prev, page: 1 })); }; // 重置所有过滤条件 const resetFilters = () => { setFilters({ status: undefined, paymentStatus: undefined }); setSearchParams(prev => ({ ...prev, page: 1 })); }; // 检查是否有活跃的过滤条件 const hasActiveFilters = useMemo(() => { return filters.status !== undefined || filters.paymentStatus !== undefined; }, [filters]); // 打开订单详情对话框 const handleViewOrder = (order: OrderResponse) => { setSelectedOrder(order); setDetailDialogOpen(true); }; // 获取订单状态对应的颜色 const getStatusColor = (status: OrderStatus) => { switch (status) { case OrderStatus.PENDING_PAYMENT: return 'secondary'; case OrderStatus.WAITING_DEPARTURE: return 'default'; case OrderStatus.IN_PROGRESS: return 'default'; case OrderStatus.COMPLETED: return 'default'; case OrderStatus.CANCELLED: return 'destructive'; default: return 'default'; } }; // 获取支付状态对应的颜色 const getPaymentStatusColor = (status: PaymentStatus) => { switch (status) { case PaymentStatus.PENDING: return 'secondary'; case PaymentStatus.PAID: return 'default'; case PaymentStatus.FAILED: return 'destructive'; case PaymentStatus.REFUNDED: return 'secondary'; default: return 'default'; } }; // 渲染表格部分的骨架屏 const renderTableSkeleton = () => (
{Array.from({ length: 5 }).map((_, index) => (
))}
); return (

订单管理

{/* 订单统计面板 */}
总订单数 {stats.total} 待支付 {stats.pendingPayment} 待出发 {stats.waitingDeparture} 行程中 {stats.inProgress} 已完成 {stats.completed} 已取消 {stats.cancelled}
订单列表 管理系统中的所有订单,共 {totalCount} 个订单
{hasActiveFilters && ( )}
{showFilters && (
{/* 订单状态筛选 */}
{/* 支付状态筛选 */}
)} {/* 过滤条件标签 */} {hasActiveFilters && (
{filters.status && ( 订单状态: {filters.status} handleFilterChange({ status: undefined })} /> )} {filters.paymentStatus && ( 支付状态: {filters.paymentStatus} handleFilterChange({ paymentStatus: undefined })} /> )}
)}
订单号 用户 路线 乘客数量 订单金额 订单状态 支付状态 创建时间 操作 {isLoading ? ( // 显示表格骨架屏 {renderTableSkeleton()} ) : ( // 显示实际订单数据 orders.map((order) => ( #{order.id} {order.user?.username || '未知用户'} {order.user?.phone && ` (${order.user.phone})`} {order.route?.name || '未知路线'} {order.passengerCount} ¥{order.totalAmount} {order.status} {order.paymentStatus} {format(new Date(order.createdAt), 'yyyy-MM-dd HH:mm')}
)) )}
{/* 订单详情对话框 */} 订单详情 查看订单的详细信息 {selectedOrder && (

订单信息

订单号: #{selectedOrder.id}
订单状态: {selectedOrder.status}
支付状态: {selectedOrder.paymentStatus}
创建时间: {format(new Date(selectedOrder.createdAt), 'yyyy-MM-dd HH:mm:ss')}

用户信息

用户名: {selectedOrder.user?.username || '未知'}
手机号: {selectedOrder.user?.phone || '未知'}

路线信息

路线名称: {selectedOrder.route?.name || '未知'}
路线描述: {selectedOrder.route?.description || '无描述'}

订单详情

乘客数量: {selectedOrder.passengerCount}
订单金额: ¥{selectedOrder.totalAmount}
{selectedOrder.passengerSnapshots && selectedOrder.passengerSnapshots.length > 0 && (

乘客信息

{selectedOrder.passengerSnapshots.map((passenger: any, index: number) => (
乘客 {index + 1}: {passenger.name || '未知'}
身份证: {passenger.idNumber || '未知'}
手机号: {passenger.phone || '未知'}
))}
)}
)}
); };