From 672a995c59e10bd0b48e90a2953155dcbc8af0c7 Mon Sep 17 00:00:00 2001 From: jacy <18049821889@163.com> Date: Wed, 5 Aug 2026 23:41:31 +0800 Subject: [PATCH] fix(mini-user): split order-logistics utils for weapp bundle stability Co-authored-by: Cursor --- .../src/components/LogisticsRichText.tsx | 2 +- .../src/lib/order-logistics-utils.ts | 114 +++++++++++++++++ apps/mini-user/src/lib/order-logistics.ts | 115 +----------------- .../src/pages/order-detail/index.tsx | 4 +- .../src/pages/order-logistics/index.tsx | 9 +- 5 files changed, 125 insertions(+), 119 deletions(-) create mode 100644 apps/mini-user/src/lib/order-logistics-utils.ts diff --git a/apps/mini-user/src/components/LogisticsRichText.tsx b/apps/mini-user/src/components/LogisticsRichText.tsx index 3854a2b..752a70f 100644 --- a/apps/mini-user/src/components/LogisticsRichText.tsx +++ b/apps/mini-user/src/components/LogisticsRichText.tsx @@ -1,6 +1,6 @@ import { Text } from '@tarojs/components'; import Taro from '@tarojs/taro'; -import { splitLogisticsText } from '../lib/order-logistics'; +import { splitLogisticsText } from '../lib/order-logistics-utils'; import { toast } from '../lib/api'; type LogisticsRichTextProps = { diff --git a/apps/mini-user/src/lib/order-logistics-utils.ts b/apps/mini-user/src/lib/order-logistics-utils.ts new file mode 100644 index 0000000..3771fe1 --- /dev/null +++ b/apps/mini-user/src/lib/order-logistics-utils.ts @@ -0,0 +1,114 @@ +import type { OrderTrackDto } from '@dukang/shared-types'; +import { isXfxProviderCode } from '@dukang/shared-types'; +import { formatShanghaiDateTime } from './datetime'; + +export type OrderDelivery = { + provider?: string; + trackingNo?: string; + logisticsCompany?: string; + manualQueryUrl?: string; +}; + +export type OrderTrackNode = { + trackInfo?: string; + createTime?: string; + createdAt?: string; + statusName?: string; +}; + +export type OrderTrackEstimatedArrival = { + arriveTime: string; + siteName?: string; +}; + +export type OrderTrackResponse = OrderTrackDto; + +/** 订单详情页展示物流摘要的状态(配送中) */ +export const ORDER_LOGISTICS_PREVIEW_STATUSES = ['SHIPPING', 'SHIPPED'] as const; + +const PHONE_PATTERN = /(1[3-9]\d{9}|\d{3,4}-\d{7,8})/g; + +function compareTrackNodeTime(a: OrderTrackNode, b: OrderTrackNode) { + const ta = new Date(a.createTime || a.createdAt || 0).getTime(); + const tb = new Date(b.createTime || b.createdAt || 0).getTime(); + if (Number.isNaN(ta) && Number.isNaN(tb)) return 0; + if (Number.isNaN(ta)) return 1; + if (Number.isNaN(tb)) return -1; + return ta - tb; +} + +export function deliveryProviderLabel(provider?: string, company?: string) { + if (company) return company; + if (!provider || provider === 'MOCK' || isXfxProviderCode(provider)) { + return '小飞侠配送'; + } + if (provider === 'LOGISTICS') return '快递配送'; + return provider; +} + +export function shouldLoadOrderTrack(delivery?: OrderDelivery | null) { + if (!delivery) return false; + if (delivery.trackingNo) return true; + return isXfxProviderCode(delivery.provider || ''); +} + +export function formatTrackNodeTime(node: OrderTrackNode) { + return formatShanghaiDateTime(node.createTime || node.createdAt); +} + +export function sortTrackNodesNewestFirst(nodes: OrderTrackNode[]): OrderTrackNode[] { + return [...nodes].sort((a, b) => -compareTrackNodeTime(a, b)); +} + +export function sortTrackNodesOldestFirst(nodes: OrderTrackNode[]): OrderTrackNode[] { + return [...nodes].sort(compareTrackNodeTime); +} + +export function latestTrackNode(nodes: OrderTrackNode[]) { + if (!nodes.length) return null; + let latest = nodes[0]; + for (let i = 1; i < nodes.length; i += 1) { + if (compareTrackNodeTime(nodes[i], latest) > 0) { + latest = nodes[i]; + } + } + return latest; +} + +export function isLogisticsNotArrived(nodes: OrderTrackNode[], orderStatus?: string) { + if (['PENDING_RECEIVE', 'DELIVERED', 'COMPLETED'].includes(orderStatus || '')) { + return false; + } + if (nodes.length === 0) return true; + return !nodes.some( + (node) => node.statusName?.includes('签收') || node.trackInfo?.includes('签收'), + ); +} + +export function formatEstimatedArrival(eta?: OrderTrackEstimatedArrival | null) { + if (!eta?.arriveTime) return ''; + const time = formatShanghaiDateTime(eta.arriveTime); + if (eta.siteName) return `预计送达:${time}(${eta.siteName})`; + return `预计送达:${time}`; +} + +export type LogisticsTextSegment = { type: 'text' | 'phone'; value: string }; + +export function splitLogisticsText(text: string): LogisticsTextSegment[] { + if (!text) return []; + const segments: LogisticsTextSegment[] = []; + let lastIndex = 0; + const regex = new RegExp(PHONE_PATTERN.source, 'g'); + let match: RegExpExecArray | null; + while ((match = regex.exec(text)) !== null) { + if (match.index > lastIndex) { + segments.push({ type: 'text', value: text.slice(lastIndex, match.index) }); + } + segments.push({ type: 'phone', value: match[0] }); + lastIndex = match.index + match[0].length; + } + if (lastIndex < text.length) { + segments.push({ type: 'text', value: text.slice(lastIndex) }); + } + return segments.length ? segments : [{ type: 'text', value: text }]; +} diff --git a/apps/mini-user/src/lib/order-logistics.ts b/apps/mini-user/src/lib/order-logistics.ts index 361fa54..695130a 100644 --- a/apps/mini-user/src/lib/order-logistics.ts +++ b/apps/mini-user/src/lib/order-logistics.ts @@ -1,118 +1,7 @@ -import type { OrderTrackDto } from '@dukang/shared-types'; -import { isXfxProviderCode } from '@dukang/shared-types'; -import { formatShanghaiDateTime } from './datetime'; +export * from './order-logistics-utils'; +import type { OrderTrackResponse } from './order-logistics-utils'; import { request } from './api'; -export type OrderDelivery = { - provider?: string; - trackingNo?: string; - logisticsCompany?: string; - manualQueryUrl?: string; -}; - -export type OrderTrackNode = { - trackInfo?: string; - createTime?: string; - createdAt?: string; - statusName?: string; -}; - -export type OrderTrackEstimatedArrival = { - arriveTime: string; - siteName?: string; -}; - -export type OrderTrackResponse = OrderTrackDto; - -/** 订单详情页展示物流摘要的状态(配送中) */ -export const ORDER_LOGISTICS_PREVIEW_STATUSES = ['SHIPPING', 'SHIPPED'] as const; - -const PHONE_PATTERN = /(1[3-9]\d{9}|\d{3,4}-\d{7,8})/g; - -export function deliveryProviderLabel(provider?: string, company?: string) { - if (company) return company; - if (!provider || provider === 'MOCK' || isXfxProviderCode(provider)) { - return '小飞侠配送'; - } - if (provider === 'LOGISTICS') return '快递配送'; - return provider; -} - -export function shouldLoadOrderTrack(delivery?: OrderDelivery | null) { - if (!delivery) return false; - if (delivery.trackingNo) return true; - return isXfxProviderCode(delivery.provider || ''); -} - -export function formatTrackNodeTime(node: OrderTrackNode) { - return formatShanghaiDateTime(node.createTime || node.createdAt); -} - -export function sortTrackNodesNewestFirst(nodes: OrderTrackNode[]) { - return [...nodes].sort((a, b) => { - const ta = new Date(a.createTime || a.createdAt || 0).getTime(); - const tb = new Date(b.createTime || b.createdAt || 0).getTime(); - if (Number.isNaN(ta) && Number.isNaN(tb)) return 0; - if (Number.isNaN(ta)) return 1; - if (Number.isNaN(tb)) return -1; - return tb - ta; - }); -} - -export function sortTrackNodesOldestFirst(nodes: OrderTrackNode[]) { - return [...nodes].sort((a, b) => { - const ta = new Date(a.createTime || a.createdAt || 0).getTime(); - const tb = new Date(b.createTime || b.createdAt || 0).getTime(); - if (Number.isNaN(ta) && Number.isNaN(tb)) return 0; - if (Number.isNaN(ta)) return 1; - if (Number.isNaN(tb)) return -1; - return ta - tb; - }); -} - -export function latestTrackNode(nodes: OrderTrackNode[]) { - const sorted = sortTrackNodesNewestFirst(nodes); - return sorted[0] ?? null; -} - -export function isLogisticsNotArrived(nodes: OrderTrackNode[], orderStatus?: string) { - if (['PENDING_RECEIVE', 'DELIVERED', 'COMPLETED'].includes(orderStatus || '')) { - return false; - } - if (nodes.length === 0) return true; - return !nodes.some( - (node) => node.statusName?.includes('签收') || node.trackInfo?.includes('签收'), - ); -} - -export function formatEstimatedArrival(eta?: OrderTrackEstimatedArrival | null) { - if (!eta?.arriveTime) return ''; - const time = formatShanghaiDateTime(eta.arriveTime); - if (eta.siteName) return `预计送达:${time}(${eta.siteName})`; - return `预计送达:${time}`; -} - -export type LogisticsTextSegment = { type: 'text' | 'phone'; value: string }; - -export function splitLogisticsText(text: string): LogisticsTextSegment[] { - if (!text) return []; - const segments: LogisticsTextSegment[] = []; - let lastIndex = 0; - const regex = new RegExp(PHONE_PATTERN.source, 'g'); - let match: RegExpExecArray | null; - while ((match = regex.exec(text)) !== null) { - if (match.index > lastIndex) { - segments.push({ type: 'text', value: text.slice(lastIndex, match.index) }); - } - segments.push({ type: 'phone', value: match[0] }); - lastIndex = match.index + match[0].length; - } - if (lastIndex < text.length) { - segments.push({ type: 'text', value: text.slice(lastIndex) }); - } - return segments.length ? segments : [{ type: 'text', value: text }]; -} - export async function fetchOrderTrack(orderId: string) { return request(`/trade/orders/${orderId}/track`); } diff --git a/apps/mini-user/src/pages/order-detail/index.tsx b/apps/mini-user/src/pages/order-detail/index.tsx index 4e7185d..25c6c22 100644 --- a/apps/mini-user/src/pages/order-detail/index.tsx +++ b/apps/mini-user/src/pages/order-detail/index.tsx @@ -10,7 +10,6 @@ import LogisticsRichText from '../../components/LogisticsRichText'; import { request, toast } from '../../lib/api'; import { buildPayUrl } from '../../lib/checkout-nav'; import { - fetchOrderTrack, formatEstimatedArrival, isLogisticsNotArrived, latestTrackNode, @@ -18,7 +17,8 @@ import { shouldLoadOrderTrack, type OrderTrackEstimatedArrival, type OrderTrackNode, -} from '../../lib/order-logistics'; +} from '../../lib/order-logistics-utils'; +import { fetchOrderTrack } from '../../lib/order-logistics'; import { maskPhone } from '../../lib/phone'; import { confirmOrderReceive, type WechatConfirmPayload } from '../../lib/wechat-order-confirm'; import { diff --git a/apps/mini-user/src/pages/order-logistics/index.tsx b/apps/mini-user/src/pages/order-logistics/index.tsx index 1f68298..6cc9ccf 100644 --- a/apps/mini-user/src/pages/order-logistics/index.tsx +++ b/apps/mini-user/src/pages/order-logistics/index.tsx @@ -7,13 +7,13 @@ import LogisticsRichText from '../../components/LogisticsRichText'; import { request, toast } from '../../lib/api'; import { deliveryProviderLabel, - fetchOrderTrack, formatTrackNodeTime, shouldLoadOrderTrack, sortTrackNodesOldestFirst, type OrderDelivery, type OrderTrackNode, -} from '../../lib/order-logistics'; +} from '../../lib/order-logistics-utils'; +import { fetchOrderTrack } from '../../lib/order-logistics'; import { usePageView } from '../../lib/usePageView'; type OrderDetail = { @@ -32,7 +32,10 @@ export default function OrderLogisticsPage() { const [signPhotoUrls, setSignPhotoUrls] = useState([]); const [trackLoading, setTrackLoading] = useState(false); - const sortedTrackNodes = useMemo(() => sortTrackNodesOldestFirst(trackNodes), [trackNodes]); + const sortedTrackNodes = useMemo( + () => (Array.isArray(trackNodes) ? sortTrackNodesOldestFirst(trackNodes) : []), + [trackNodes], + ); const timelineComplete = ['PENDING_RECEIVE', 'DELIVERED', 'COMPLETED'].includes( order?.status || '', );