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 }]; }