import { useState, useCallback, useMemo } from 'react'; import { Table, Button, Modal, Form, Input, Select, Badge, message, Tag, DatePicker, } from 'antd'; import { CheckCircleOutlined, } from '@ant-design/icons'; import { pointsApi, type PointsOrder, } from '../../api/health/points'; import { AuthButton } from '../../components/AuthButton'; import { PageContainer } from '../../components/PageContainer'; import { usePaginatedData } from '../../hooks/usePaginatedData'; import { useHealthStore } from '../../stores/health'; import { formatDateTime } from '../../utils/format'; import type { Dayjs } from 'dayjs'; import { dayjs } from '../../utils/dayjs'; /** 订单状态映射 */ const STATUS_MAP: Record = { pending: { text: '待核销', color: 'orange' }, verified: { text: '已核销', color: 'green' }, cancelled: { text: '已取消', color: 'red' }, expired: { text: '已过期', color: 'default' }, }; /** 状态筛选选项 */ const STATUS_OPTIONS = Object.entries(STATUS_MAP).map(([value, { text }]) => ({ value, label: text, })); /** 截断 ID 显示 */ function truncateId(id: string): string { if (!id) return '-'; return id.length > 12 ? `${id.slice(0, 8)}...${id.slice(-4)}` : id; } interface OrderFilters { status: string | undefined; dateRange: [Dayjs, Dayjs] | undefined; } export default function PointsOrderList() { const [verifyModalOpen, setVerifyModalOpen] = useState(false); const [verifyForm] = Form.useForm(); const [verifying, setVerifying] = useState(false); const { batchResolvePatientNames, getPatientName } = useHealthStore(); const fetchOrders = useCallback( async (page: number, pageSize: number, filters: OrderFilters) => { const result = await pointsApi.listOrders({ page, page_size: pageSize, status: filters.status || undefined, }); const patientIds = result.data.map((o) => o.patient_id); batchResolvePatientNames(patientIds); return { data: result.data, total: result.total }; }, [batchResolvePatientNames], ); const { data, total, page, loading, filters, setFilters, refresh, } = usePaginatedData( fetchOrders, { pageSize: 20, defaultFilters: { status: undefined, dateRange: undefined } }, ); // ---- 核销 ---- const openVerifyModal = () => { verifyForm.resetFields(); setVerifyModalOpen(true); }; const handleVerify = async (values: { qr_code: string }) => { setVerifying(true); try { const order = await pointsApi.verifyOrder({ qr_code: values.qr_code }); message.success(`核销成功,订单 ${truncateId(order.id)} 已确认`); setVerifyModalOpen(false); verifyForm.resetFields(); refresh(page); } catch { message.error('核销失败,请检查二维码是否正确'); } finally { setVerifying(false); } }; const resetFilters = () => { setFilters({ status: undefined, dateRange: undefined }); }; // ---- 列定义 ---- const columns = useMemo(() => [ { title: '订单号', dataIndex: 'id', key: 'id', width: 140, render: (val: string) => ( {truncateId(val)} ), }, { title: '患者', dataIndex: 'patient_id', key: 'patient_id', width: 100, render: (id: string) => getPatientName(id), }, { title: '商品', dataIndex: 'product_name', key: 'product_name', width: 140, render: (name: string | null, record: PointsOrder) => name || truncateId(record.product_id), }, { title: '积分', dataIndex: 'points_cost', key: 'points_cost', width: 80, render: (val: number) => {val}, }, { title: '状态', dataIndex: 'status', key: 'status', width: 100, render: (val: string) => { const cfg = STATUS_MAP[val] || { text: val, color: 'default' }; return ; }, }, { title: '创建时间', dataIndex: 'created_at', key: 'created_at', width: 170, render: (val: string) => formatDateTime(val), }, { title: '核销时间', dataIndex: 'verified_at', key: 'verified_at', width: 170, render: (val: string) => formatDateTime(val), }, { title: '核销人', dataIndex: 'verified_by', key: 'verified_by', width: 100, render: (val: string | null) => val ? {truncateId(val)} : '-', }, { title: '过期时间', dataIndex: 'expires_at', key: 'expires_at', width: 170, render: (val: string | null) => { if (!val) return '-'; const isExpired = dayjs(val).isBefore(dayjs()); return ( {formatDateTime(val)} ); }, }, { title: '备注', dataIndex: 'notes', key: 'notes', width: 150, ellipsis: true, render: (val: string | null) => val || '-', }, ], [getPatientName]); return (