import { useEffect, useMemo, useState } from 'react'; import { View, Text, Image } from '@tarojs/components'; import Taro, { useRouter } from '@tarojs/taro'; import PageShell from '../../components/PageShell'; import SubPageHeader from '../../components/SubPageHeader'; import LogisticsRichText from '../../components/LogisticsRichText'; import { request, toast } from '../../lib/api'; import { deliveryProviderLabel, formatTrackNodeTime, shouldLoadOrderTrack, sortTrackNodesOldestFirst, type OrderDelivery, type OrderTrackNode, } from '../../lib/order-logistics-utils'; import { fetchOrderTrack } from '../../lib/order-logistics'; import { usePageView } from '../../lib/usePageView'; type OrderDetail = { id: string; orderNo?: string; status?: string; delivery?: OrderDelivery | null; }; export default function OrderLogisticsPage() { const router = useRouter(); const orderId = router.params.id ?? ''; usePageView('order_logistics_view', orderId ? { orderId } : undefined); const [order, setOrder] = useState(null); const [trackNodes, setTrackNodes] = useState([]); const [signPhotoUrls, setSignPhotoUrls] = useState([]); const [trackLoading, setTrackLoading] = useState(false); const sortedTrackNodes = useMemo( () => (Array.isArray(trackNodes) ? sortTrackNodesOldestFirst(trackNodes) : []), [trackNodes], ); const timelineComplete = ['PENDING_RECEIVE', 'DELIVERED', 'COMPLETED'].includes( order?.status || '', ); useEffect(() => { if (!orderId) return; let cancelled = false; (async () => { try { const data = await request(`/trade/orders/${orderId}`); if (cancelled) return; setOrder(data); if (!shouldLoadOrderTrack(data.delivery)) { setTrackNodes([]); setSignPhotoUrls([]); return; } setTrackLoading(true); try { const track = await fetchOrderTrack(orderId); if (!cancelled) { setTrackNodes(track.nodes ?? []); setSignPhotoUrls(track.signPhotoUrls ?? []); } } catch { if (!cancelled) { setTrackNodes([]); setSignPhotoUrls([]); } } finally { if (!cancelled) setTrackLoading(false); } } catch (e) { if (!cancelled) toast(e instanceof Error ? e.message : '加载失败'); } })(); return () => { cancelled = true; }; }, [orderId]); function openManualQuery() { const url = order?.delivery?.manualQueryUrl?.trim(); if (!url) { toast('暂无物流查询链接'); return; } if (process.env.TARO_ENV === 'h5' && typeof window !== 'undefined') { window.open(url, '_blank'); return; } Taro.setClipboardData({ data: url }) .then(() => toast('查询链接已复制,请在浏览器中打开')) .catch(() => toast('无法打开物流查询')); } function previewSignPhotos(index: number) { if (signPhotoUrls.length === 0) return; Taro.previewImage({ current: signPhotoUrls[index], urls: signPhotoUrls }).catch(() => toast('无法预览图片'), ); } const delivery = order?.delivery; return ( {!order ? ( 加载中… ) : ( <> 物流信息 配送方式 {deliveryProviderLabel(delivery?.provider, delivery?.logisticsCompany)} {delivery?.trackingNo ? ( 运单号 {delivery.trackingNo} ) : null} {delivery?.manualQueryUrl ? ( 物流查询 查看物流 ) : null} {!delivery?.trackingNo && !delivery?.manualQueryUrl ? ( 物流信息待更新,请稍后查看 ) : null} {signPhotoUrls.length > 0 ? ( 签收照片 {signPhotoUrls.map((url, index) => ( previewSignPhotos(index)} /> ))} ) : null} {trackLoading ? ( 物流动态 加载中… ) : sortedTrackNodes.length > 0 ? ( 物流动态 {sortedTrackNodes.map((node, index) => { const isLatest = index === sortedTrackNodes.length - 1; return ( {node.statusName ? ( {node.statusName} ) : null} {formatTrackNodeTime(node)} ); })} ) : shouldLoadOrderTrack(delivery) ? ( 物流动态 暂无路由信息,请稍后刷新 ) : null} )} ); }