import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { Button, Descriptions, Drawer, Form, Input, Space, Table, Typography, message, } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import OrderTrackDrawer from '../components/OrderTrackDrawer'; import { request } from '../lib/api'; import { DELIVERY_PROVIDER_LABELS, DELIVERY_TYPE_LABELS, ORDER_STATUS_LABELS, fmtTime, } from '../lib/constants'; import { useAdminList } from '../lib/useAdminList'; import { useAdminListColumns } from '../lib/useAdminListColumns'; import { AdminListHeader } from '../components/AdminListHeader'; import { AdminPrimaryLink } from '../components/AdminPrimaryLink'; type DeliveryOrder = { id: string; orderNo: string; status: string; deliveryType: string; productName: string; productSpec?: string | null; barcode69?: string | null; quantity: number; saleUnit?: string; bottlesPerUnit?: number; payAmount?: number; receiverName: string; receiverPhone: string; receiverAddress?: string; receiverProvince?: string; receiverCity?: string; receiverDistrict?: string; user?: { id: string; userNo: string; phone: string | null; nickname: string | null }; imageResource?: { url: string } | null; }; type Row = { id: string; orderId: string; provider: string; trackingNo: string | null; providerOrderNo: string | null; updatedAt: string; logisticsFee?: number | null; order?: DeliveryOrder; }; function formatQty(order?: DeliveryOrder | null) { if (!order || order.quantity == null) return '—'; const unit = order.saleUnit === 'BOX' ? '箱' : '瓶'; const bottles = order.saleUnit === 'BOX' && order.bottlesPerUnit && order.bottlesPerUnit > 1 ? `(${order.quantity * order.bottlesPerUnit}瓶)` : ''; return `${order.quantity}${unit}${bottles}`; } function formatAddress(order?: DeliveryOrder | null) { if (!order) return '—'; if (order.deliveryType === 'ON_SITE_PICKUP') return '现场取货'; const region = [order.receiverProvince, order.receiverCity, order.receiverDistrict] .filter(Boolean) .join(''); const detail = (order.receiverAddress || '').trim(); if (!region) return detail || '—'; if (!detail || detail.startsWith(region)) return detail || region; return `${region}${detail}`; } export default function DeliveriesPage() { const navigate = useNavigate(); const [form] = Form.useForm(); const [editForm] = Form.useForm(); const [filters, setFilters] = useState>({}); const { data, loading, page, pageSize, setPage, setPageSize, reload } = useAdminList( '/admin/deliveries', () => { const qs = new URLSearchParams(); if (filters.provider) qs.set('provider', filters.provider); if (filters.trackingNo) qs.set('trackingNo', filters.trackingNo); if (filters.orderNo) qs.set('orderNo', filters.orderNo); return qs; }, [filters], ); const [detail, setDetail] = useState(null); const [drawerOpen, setDrawerOpen] = useState(false); const [trackOpen, setTrackOpen] = useState(false); const [trackOrderId, setTrackOrderId] = useState(null); const [trackOrderNo, setTrackOrderNo] = useState(null); const [trackOrderStatus, setTrackOrderStatus] = useState(null); function openTrack(row: Row) { const orderId = row.orderId || row.order?.id; if (!orderId) { message.warning('缺少关联订单,无法查询路由'); return; } setTrackOrderId(orderId); setTrackOrderNo(row.order?.orderNo ?? null); setTrackOrderStatus(row.order?.status ?? null); setTrackOpen(true); } async function openDetail(row: Row) { const d = await request(`/admin/deliveries/${row.id}`); setDetail(d); editForm.setFieldsValue({ provider: d.provider, trackingNo: d.trackingNo, providerOrderNo: d.providerOrderNo, }); setDrawerOpen(true); } const baseColumns: ColumnsType = [ { title: '订单号', dataIndex: ['order', 'orderNo'], width: 170, render: (v, row) => ( void openDetail(row)}>{v} ), }, { title: '用户', key: 'user', width: 140, render: (_, row) => { const user = row.order?.user; if (!user) return '—'; const label = user.userNo || user.phone || '—'; return (
{user.id ? ( navigate('/users', { state: { openUserId: String(user.id) } })} > {label} ) : ( label )} {user.phone && user.userNo ? ( {user.phone} ) : null}
); }, }, { title: '商品', key: 'product', width: 200, render: (_, row) => { const order = row.order; if (!order?.productName) return '—'; return (
{order.productName} {order.productSpec ? ( {order.productSpec} ) : null}
); }, }, { title: '数量', key: 'quantity', width: 90, render: (_, row) => formatQty(row.order), }, { title: '配送方式', dataIndex: ['order', 'deliveryType'], width: 90, render: (v: string | undefined) => DELIVERY_TYPE_LABELS[v ?? ''] || v || '—', }, { title: '承运商', dataIndex: 'provider', width: 90, render: (v: string) => DELIVERY_PROVIDER_LABELS[v] || v || '—', }, { title: '运单号', dataIndex: 'trackingNo', width: 140, render: (v) => v || '—' }, { title: '运费', dataIndex: 'logisticsFee', width: 90, render: (v: number | null | undefined) => (v == null ? '—' : `¥${Number(v).toFixed(2)}`), }, { title: '第三方单号', dataIndex: 'providerOrderNo', width: 140, render: (v) => v || '—' }, { title: '订单状态', dataIndex: ['order', 'status'], width: 100, render: (s) => ORDER_STATUS_LABELS[s] || s, }, { title: '收货人', dataIndex: ['order', 'receiverName'], width: 90 }, { title: '收货电话', dataIndex: ['order', 'receiverPhone'], width: 120, render: (v) => v || '—', }, { title: '配送地址', key: 'address', width: 280, ellipsis: true, render: (_, row) => formatAddress(row.order), }, { title: '更新', dataIndex: 'updatedAt', width: 160, render: fmtTime }, { title: '操作', width: 140, render: (_, row) => ( ), }, ]; const { columns, settingsButton, settingsModal } = useAdminListColumns('deliveries', baseColumns, { page, pageSize }); const order = detail?.order; return (
{settingsModal}
{ setFilters(v); setPage(1); }}>
{ setPage(p); setPageSize(ps); } }} /> setDrawerOpen(false)} extra={ }> {detail && ( <> {order?.orderNo || '—'} {order?.user ? ( {order.user.id ? ( navigate('/users', { state: { openUserId: String(order.user!.id) } })} > {order.user.userNo} ) : ( order.user.userNo || '—' )} {[order.user.nickname, order.user.phone].filter(Boolean).join(' / ') || ''} ) : '—'} {order?.imageResource?.url ? ( ) : null} {order?.productName || '—'} {order?.productSpec ? ( {order.productSpec} ) : null} {order?.barcode69 ? ( {order.barcode69} ) : null} {formatQty(order)} {DELIVERY_TYPE_LABELS[order?.deliveryType ?? ''] || order?.deliveryType || '—'} {[order?.receiverName, order?.receiverPhone].filter(Boolean).join(' ') || '—'} {formatAddress(order)} {detail.logisticsFee == null ? '—' : `¥${Number(detail.logisticsFee).toFixed(2)}`}
)}
setTrackOpen(false)} /> ); }