import { useEffect, useState, useCallback } from 'react'; import { Table, Button, Space, Modal, Form, Input, Select, Badge, message, Card, Row, Col, Tag, } from 'antd'; import { CheckCircleOutlined, } from '@ant-design/icons'; import dayjs from 'dayjs'; import { pointsApi, type PointsOrder, } from '../../api/health/points'; import { patientApi } from '../../api/health/patients'; import { AuthButton } from '../../components/AuthButton'; /** 订单状态映射 */ 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; } export default function PointsOrderList() { const [data, setData] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(20); const [loading, setLoading] = useState(false); const [statusFilter, setStatusFilter] = useState(undefined); const [verifyModalOpen, setVerifyModalOpen] = useState(false); const [verifyForm] = Form.useForm(); const [verifying, setVerifying] = useState(false); // 名称缓存 const [nameCache, setNameCache] = useState>({}); // ---- 数据获取 ---- const fetchData = useCallback(async (p = page, ps = pageSize) => { setLoading(true); try { const result = await pointsApi.listOrders({ page: p, page_size: ps, status: statusFilter || undefined, }); setData(result.data); setTotal(result.total); // 批量解析患者名称 const patientIds = [...new Set(result.data.map((o) => o.patient_id))]; const missingIds = patientIds.filter((id) => !nameCache[id]); if (missingIds.length > 0) { const newNames: Record = {}; await Promise.all( missingIds.map(async (id) => { try { const detail = await patientApi.get(id); newNames[id] = detail.name; } catch { newNames[id] = id.slice(0, 8); } }), ); setNameCache((prev) => ({ ...prev, ...newNames })); } } catch { message.error('加载订单列表失败'); } finally { setLoading(false); } }, [page, pageSize, statusFilter, nameCache]); useEffect(() => { fetchData(); }, [fetchData]); // ---- 核销 ---- 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(); fetchData(page, pageSize); } catch { message.error('核销失败,请检查二维码是否正确'); } finally { setVerifying(false); } }; // ---- 列定义 ---- const columns = [ { title: '订单号', dataIndex: 'id', key: 'id', width: 140, render: (val: string) => ( {truncateId(val)} ), }, { title: '患者', dataIndex: 'patient_id', key: 'patient_id', width: 100, render: (id: string) => nameCache[id] || id.slice(0, 8), }, { 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) => (val ? dayjs(val).format('YYYY-MM-DD HH:mm') : '-'), }, { title: '核销时间', dataIndex: 'verified_at', key: 'verified_at', width: 170, render: (val: string) => (val ? dayjs(val).format('YYYY-MM-DD HH:mm') : '-'), }, { title: '核销人', dataIndex: 'verified_by', key: 'verified_by', width: 100, render: (val: string | null) => val ? {nameCache[val] || val.slice(0, 8)} : '-', }, { title: '过期时间', dataIndex: 'expires_at', key: 'expires_at', width: 170, render: (val: string | null) => { if (!val) return '-'; const isExpired = dayjs(val).isBefore(dayjs()); return ( {dayjs(val).format('YYYY-MM-DD HH:mm')} ); }, }, { title: '备注', dataIndex: 'notes', key: 'notes', width: 150, ellipsis: true, render: (val: string | null) => val || '-', }, ]; return ( {/* 筛选栏 */}