import { useEffect, useMemo, useState } from 'react'; import { Link, useNavigate, useParams } from 'react-router-dom'; import AppImage from '@dukang/shared-ui/AppImage'; import AppToast from '../components/AppToast'; import { request } from '../lib/api'; import { buildOrderAddressSelectUrl } from '../lib/navigation'; import { STITCH_ORDER_PRODUCT_IMAGE } from '../lib/order-images'; import { handleShareButtonClick } from '../lib/wechat-share'; import ContactCustomerSheet from '../components/ContactCustomerSheet'; import { usePageView } from '../lib/usePageView'; type OrderItem = { productName: string; productSpec: string; productImage: string; unitPrice: number; quantity: number; }; type OrderDelivery = { provider?: string; trackingNo?: string; logisticsCompany?: string; manualQueryUrl?: string; providerOrderNo?: string; }; type TrackNode = { trackInfo?: string; createdAt?: string; }; type OrderPayment = { status?: string; }; type Order = { id: string; orderNo: string; orderType?: string; isProxyOrder?: boolean; proxyPartnerName?: string | null; status: string; originOrderId?: string | null; remark?: string | null; receiverName: string; receiverPhone: string; receiverProvince?: string; receiverCity?: string; receiverDistrict?: string; receiverAddress: string; productAmount?: number; freightAmount?: number; payAmount: number; benefitAmount: number; createdAt: string; items?: OrderItem[]; delivery?: OrderDelivery | null; payment?: OrderPayment | null; }; const PROGRESS_STEPS = [ { label: '下单成功' }, { label: '出库中' }, { label: '配送中' }, { label: '待签收' }, { label: '完成' }, ] as const; const STATUS_BANNER: Record = { PENDING_PAY: { title: '待付款', subtitle: '请尽快完成支付' }, PENDING_SHIP: { title: '待发货', subtitle: '商家正在备货,请耐心等待' }, OUT_WAREHOUSE: { title: '出库中', subtitle: '商品正在出库打包' }, SHIPPING: { title: '配送中', subtitle: '您的佳酿正在赶往您的餐桌,请保持电话畅通' }, PENDING_RECEIVE: { title: '待签收', subtitle: '包裹已送达,请确认签收' }, COMPLETED: { title: '已完成', subtitle: '感谢您的购买,期待再次光临' }, RESHIP: { title: '补发中', subtitle: '包裹正在揽收,请耐心等待' }, }; const EDITABLE_STATUSES = new Set(['PENDING_PAY', 'PENDING_SHIP', 'OUT_WAREHOUSE']); function maskPhone(phone: string) { return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'); } function formatMoney(amount: number) { return amount.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } function formatDateTime(value: string) { const d = new Date(value); if (Number.isNaN(d.getTime())) return value; const pad = (n: number) => String(n).padStart(2, '0'); return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`; } function progressActiveIndex(status: string) { switch (status) { case 'PENDING_PAY': return 0; case 'PENDING_SHIP': return 1; case 'OUT_WAREHOUSE': return 1; case 'SHIPPING': return 2; case 'PENDING_RECEIVE': return 3; case 'COMPLETED': return 4; default: return 0; } } function deliveryProviderLabel(provider?: string, company?: string) { if (company) return company; if (!provider || provider === 'MOCK' || provider === 'XFX' || provider === 'XIAOFEIXIA') return '小飞侠配送'; if (provider === 'LOGISTICS') return '快递配送'; return provider; } function fullReceiverAddress(order: Order) { const region = [order.receiverProvince, order.receiverCity, order.receiverDistrict] .filter(Boolean) .join(''); if (region && order.receiverAddress.startsWith(region)) { return order.receiverAddress; } return `${region}${order.receiverAddress}`; } export default function OrderDetailPage() { const { id } = useParams(); usePageView('order_detail_view', { orderId: id }); const navigate = useNavigate(); const [order, setOrder] = useState(null); const [showCs, setShowCs] = useState(false); const [copyHint, setCopyHint] = useState(''); const [shareToast, setShareToast] = useState(''); const [confirming, setConfirming] = useState(false); const [trackNodes, setTrackNodes] = useState([]); const isReship = order?.orderType === 'RESHIPMENT'; const isProxy = order?.orderType === 'PROXY' || order?.isProxyOrder === true; async function loadTrack() { if (!id) return; try { const data = await request<{ nodes?: TrackNode[] }>('USER_H5', `/trade/orders/${id}/track`); setTrackNodes(data.nodes ?? []); } catch { setTrackNodes([]); } } async function loadOrder() { if (!id) return; const data = await request('USER_H5', `/trade/orders/${id}`); setOrder(data); if (data.delivery?.trackingNo || data.delivery?.provider === 'XFX') { void loadTrack(); } } useEffect(() => { loadOrder().catch(() => {}); }, [id]); const banner = useMemo(() => { if (!order) return { title: '', subtitle: '' }; if (isReship) return STATUS_BANNER.RESHIP; return STATUS_BANNER[order.status] ?? { title: order.status, subtitle: '' }; }, [order, isReship]); const activeProgress = order ? progressActiveIndex(order.status) : 0; const item = order?.items?.[0]; const productImage = item?.productImage || STITCH_ORDER_PRODUCT_IMAGE; const canEditAddress = order ? EDITABLE_STATUSES.has(order.status) : false; const canConfirmReceive = order?.status === 'PENDING_RECEIVE' && !isReship; const canPay = order?.status === 'PENDING_PAY' && !isReship; const canRefund = order && !canPay && ['PENDING_SHIP', 'PENDING_RECEIVE', 'COMPLETED', 'OUT_WAREHOUSE', 'SHIPPING'].includes(order.status); const canInvoice = order?.status === 'COMPLETED'; const productTotal = Number(order?.productAmount ?? order?.payAmount ?? 0); const freightTotal = Number(order?.freightAmount ?? 0); async function copyOrderNo() { if (!order) return; try { await navigator.clipboard.writeText(order.orderNo); setCopyHint('已复制'); window.setTimeout(() => setCopyHint(''), 2000); } catch { setCopyHint('复制失败'); } } async function confirmReceive() { if (!id || !canConfirmReceive) return; setConfirming(true); try { const updated = await request('USER_H5', `/trade/orders/${id}/confirm-receive`, { method: 'POST', }); setOrder(updated); } catch (e) { window.alert(e instanceof Error ? e.message : '确认收货失败'); } finally { setConfirming(false); } } if (!order) return
加载中...
; return (

杜康好客

local_shipping
local_shipping

{banner.title}

{banner.subtitle}

{isProxy && order?.proxyPartnerName ? (

代下单 · 由合伙人 {order.proxyPartnerName} 代下

) : null}
{!isReship && (
{PROGRESS_STEPS.map((step, index) => { const done = index < activeProgress; const active = index === activeProgress; const pending = index > activeProgress; return (
{index < PROGRESS_STEPS.length - 1 && ( )}
{done && check} {active && } {pending && }
{step.label}
); })}
)} {isReship && (
补发原因 {order.remark || '商品破损'}
关联原订单 {order.originOrderId ? ( 查看原订单 ) : ( )}
)} {item && (

{item.productName}

规格:{item.productSpec}

¥ {formatMoney(isReship ? 0 : Number(item.unitPrice)).replace('.00', '')} x{item.quantity}
{Number(order.benefitAmount) > 0 && !isReship && (
confirmation_number
¥{Math.round(Number(order.benefitAmount))} 好客权益

全城100+餐厅通用

)}
)}

location_on 收货信息

{canEditAddress && id && ( )}
收货人 {order.receiverName} {maskPhone(order.receiverPhone)}
收货地址 {fullReceiverAddress(order)}
配送方式 {deliveryProviderLabel(order.delivery?.provider, order.delivery?.logisticsCompany)}
{order.delivery?.trackingNo && (
运单号 {order.delivery.trackingNo}
)} {order.delivery?.manualQueryUrl && (
物流查询 查看物流
)}
{trackNodes.length > 0 && (

timeline 物流动态

{trackNodes.map((node, index) => (
{node.createdAt ? formatDateTime(node.createdAt) : '—'} {node.trackInfo || '—'}
))}
)} {!isReship && (

info 订单信息

订单编号 {order.orderNo}
创建时间 {formatDateTime(order.createdAt)}
支付方式 {order.payment?.status === 'SUCCESS' ? '微信支付' : '—'}
商品总额 ¥{formatMoney(productTotal)}
运费 ¥{formatMoney(freightTotal)}
实付金额 ¥{formatMoney(Number(order.payAmount))}
)}
{canRefund && order?.status !== 'REFUNDING' && order?.status !== 'REFUNDED' && ( )} {canInvoice && ( )} {canPay && ( )} {canConfirmReceive && ( )}
{showCs && ( setShowCs(false)} /> )}
); }