Include multi-line public pound QR URLs after the driver link when dibang slips exist. Co-authored-by: Cursor <cursoragent@cursor.com>
285 lines
8.3 KiB
TypeScript
285 lines
8.3 KiB
TypeScript
import type { PlanDelivery, PlanLoad, PlanProduct, PlanStopProduct } from '@/api/pl'
|
||
import {
|
||
appendAmapNavLine,
|
||
buildAmapNavigationUrlWithFallback
|
||
} from '@/utils/amap/navigation'
|
||
import { formatDate } from '@/utils/format'
|
||
import { formatPlanProductListText } from '@/utils/pl-display'
|
||
|
||
export type DriverSummaryStopBlock = {
|
||
label: string
|
||
customerName?: string
|
||
stopTime?: string
|
||
address?: string
|
||
lng?: number | null
|
||
lat?: number | null
|
||
contact?: string
|
||
notes?: string
|
||
productLines: string[]
|
||
}
|
||
|
||
type DriverSummaryPlan = {
|
||
planNo?: string
|
||
licensePlate?: string
|
||
pickupTime?: string
|
||
planDate?: string
|
||
personnelSummary?: string
|
||
pickupAddress?: string
|
||
pickupLng?: number | null
|
||
pickupLat?: number | null
|
||
pickupContact?: string
|
||
pickupNotes?: string
|
||
destinationAddress?: string
|
||
destinationName?: string
|
||
destinationLng?: number | null
|
||
destinationLat?: number | null
|
||
deliveryContact?: string
|
||
destinationNotes?: string
|
||
startPoint?: string
|
||
endPoint?: string
|
||
load?: PlanLoad | null
|
||
deliveries?: PlanDelivery[] | null
|
||
}
|
||
|
||
function formatDriverStopProductLine(
|
||
product: PlanStopProduct,
|
||
getUnitLabel?: (unit: string) => string
|
||
) {
|
||
return formatPlanProductListText(
|
||
{
|
||
productName: product.productName,
|
||
model: product.model,
|
||
packageSpec: product.packageSpec,
|
||
unit: product.unit,
|
||
quantity: product.planQty
|
||
},
|
||
getUnitLabel,
|
||
{ usePlannedQuantity: true }
|
||
)
|
||
}
|
||
|
||
function formatPlanLevelProductLines(
|
||
products: PlanProduct[],
|
||
getUnitLabel?: (unit: string) => string
|
||
) {
|
||
return products
|
||
.map((product) => formatPlanProductListText(product, getUnitLabel, { usePlannedQuantity: true }))
|
||
.filter(Boolean)
|
||
}
|
||
|
||
function appendDriverStopBlockLines(lines: string[], stop: DriverSummaryStopBlock) {
|
||
lines.push(`【${stop.label} · ${stop.customerName || '—'}】`)
|
||
if (stop.stopTime) {
|
||
lines.push(`时间:${stop.stopTime}`)
|
||
}
|
||
if (stop.address) {
|
||
lines.push(`地址:${stop.address}`)
|
||
}
|
||
appendAmapNavLine(
|
||
lines,
|
||
'导航',
|
||
buildAmapNavigationUrlWithFallback({
|
||
lng: stop.lng,
|
||
lat: stop.lat,
|
||
address: stop.address,
|
||
name: stop.label
|
||
})
|
||
)
|
||
if (stop.contact) {
|
||
lines.push(`联系方式:${stop.contact}`)
|
||
}
|
||
if (stop.notes) {
|
||
lines.push(`注意事项:${stop.notes}`)
|
||
}
|
||
if (stop.productLines.length) {
|
||
stop.productLines.forEach((line) => lines.push(`产品:${line}`))
|
||
} else {
|
||
lines.push('产品:—')
|
||
}
|
||
}
|
||
|
||
export function buildDriverStopBlocksFromPlan(
|
||
plan?: DriverSummaryPlan | null,
|
||
getUnitLabel?: (unit: string) => string
|
||
): DriverSummaryStopBlock[] {
|
||
if (!plan) return []
|
||
const blocks: DriverSummaryStopBlock[] = []
|
||
const load = plan.load
|
||
if (load) {
|
||
blocks.push({
|
||
label: '装货',
|
||
customerName: load.customerName || undefined,
|
||
stopTime: formatDate(load.loadTime) || undefined,
|
||
address: load.address || undefined,
|
||
lng: load.lng,
|
||
lat: load.lat,
|
||
contact: load.contact || undefined,
|
||
notes: load.notes || undefined,
|
||
productLines: (load.products || []).map((product) =>
|
||
formatDriverStopProductLine(product, getUnitLabel)
|
||
)
|
||
})
|
||
}
|
||
for (const delivery of plan.deliveries || []) {
|
||
const seq = delivery.seq != null ? Number(delivery.seq) : 0
|
||
blocks.push({
|
||
label: seq > 0 ? `送货 #${seq}` : '送货',
|
||
customerName: delivery.customerName || undefined,
|
||
stopTime: formatDate(delivery.deliveryTime) || undefined,
|
||
address: delivery.address || undefined,
|
||
lng: delivery.lng,
|
||
lat: delivery.lat,
|
||
contact: delivery.contact || undefined,
|
||
notes: delivery.notes || undefined,
|
||
productLines: (delivery.products || []).map((product) =>
|
||
formatDriverStopProductLine(product, getUnitLabel)
|
||
)
|
||
})
|
||
}
|
||
return blocks
|
||
}
|
||
|
||
function buildLegacyDriverStopBlocks(
|
||
plan: DriverSummaryPlan,
|
||
overrides: {
|
||
pickupAddress?: string
|
||
pickupLng?: number | null
|
||
pickupLat?: number | null
|
||
pickupContact?: string
|
||
pickupNotes?: string
|
||
destinationAddress?: string
|
||
destinationLng?: number | null
|
||
destinationLat?: number | null
|
||
deliveryContact?: string
|
||
destinationNotes?: string
|
||
startPoint?: string
|
||
endPoint?: string
|
||
},
|
||
productLines: string[]
|
||
): DriverSummaryStopBlock[] {
|
||
const pickupAddress =
|
||
overrides.pickupAddress || plan.pickupAddress || overrides.startPoint || plan.startPoint
|
||
const destinationAddress =
|
||
overrides.destinationAddress ||
|
||
plan.destinationAddress ||
|
||
plan.destinationName ||
|
||
overrides.endPoint ||
|
||
plan.endPoint
|
||
const blocks: DriverSummaryStopBlock[] = []
|
||
if (pickupAddress) {
|
||
blocks.push({
|
||
label: '取货',
|
||
customerName: plan.load?.customerName || undefined,
|
||
stopTime: formatDate(plan.pickupTime) || undefined,
|
||
address: pickupAddress,
|
||
lng: overrides.pickupLng ?? plan.pickupLng,
|
||
lat: overrides.pickupLat ?? plan.pickupLat,
|
||
contact: overrides.pickupContact || plan.pickupContact || undefined,
|
||
notes: overrides.pickupNotes || plan.pickupNotes || undefined,
|
||
productLines: productLines.length ? productLines : []
|
||
})
|
||
}
|
||
if (destinationAddress) {
|
||
blocks.push({
|
||
label: '送货',
|
||
customerName: undefined,
|
||
stopTime: undefined,
|
||
address: destinationAddress,
|
||
lng: overrides.destinationLng ?? plan.destinationLng,
|
||
lat: overrides.destinationLat ?? plan.destinationLat,
|
||
contact: overrides.deliveryContact || plan.deliveryContact || undefined,
|
||
notes: overrides.destinationNotes || plan.destinationNotes || undefined,
|
||
productLines: []
|
||
})
|
||
}
|
||
return blocks
|
||
}
|
||
|
||
export function buildPoundQrcodePublicUrl(inventoryId: string, origin = window.location.origin) {
|
||
return `${origin}/#/public/pl/pound-qrcode/${encodeURIComponent(inventoryId)}`
|
||
}
|
||
|
||
export function buildPoundQrcodeLinksFromInventoryRecords(
|
||
records: {
|
||
dbInventoryId: string
|
||
dbPurchasePlanId?: string
|
||
businessType?: string
|
||
partnerCompany?: string
|
||
}[],
|
||
origin = window.location.origin
|
||
) {
|
||
return records
|
||
.filter((r) => r.dbPurchasePlanId?.trim() && r.dbInventoryId?.trim())
|
||
.map((r) => {
|
||
const bt = (r.businessType || '').trim()
|
||
const pc = (r.partnerCompany || '').trim()
|
||
const label = bt && pc ? `${bt} · ${pc}` : bt || pc || '磅单'
|
||
return { label, url: buildPoundQrcodePublicUrl(r.dbInventoryId, origin) }
|
||
})
|
||
}
|
||
|
||
export function buildDriverSummaryFromPlan(options: {
|
||
plan?: DriverSummaryPlan | null
|
||
planNo?: string
|
||
licensePlate?: string
|
||
pickupTime?: string
|
||
planDate?: string
|
||
personnelSummary?: string
|
||
driverUrl?: string
|
||
products?: PlanProduct[]
|
||
stopBlocks?: DriverSummaryStopBlock[]
|
||
getUnitLabel?: (unit: string) => string
|
||
pickupAddress?: string
|
||
pickupLng?: number | null
|
||
pickupLat?: number | null
|
||
destinationAddress?: string
|
||
destinationLng?: number | null
|
||
destinationLat?: number | null
|
||
pickupContact?: string
|
||
pickupNotes?: string
|
||
deliveryContact?: string
|
||
destinationNotes?: string
|
||
startPoint?: string
|
||
endPoint?: string
|
||
poundQrcodeLinks?: { label: string; url: string }[]
|
||
}) {
|
||
const plan = options.plan || {}
|
||
const plate = options.licensePlate || plan.licensePlate || '—'
|
||
const lines = [`【${plate} - 运输计划】`]
|
||
lines.push(`计划号:${options.planNo || plan.planNo || '—'}`)
|
||
lines.push(`车牌:${plate}`)
|
||
const time = formatDate(options.pickupTime || plan.pickupTime) || options.planDate || plan.planDate
|
||
if (time) lines.push(`时间:${time}`)
|
||
|
||
const productLines = formatPlanLevelProductLines(options.products || [], options.getUnitLabel)
|
||
let stopBlocks =
|
||
options.stopBlocks ||
|
||
buildDriverStopBlocksFromPlan(plan, options.getUnitLabel)
|
||
if (!stopBlocks.length) {
|
||
stopBlocks = buildLegacyDriverStopBlocks(plan, options, productLines)
|
||
}
|
||
|
||
if (stopBlocks.length) {
|
||
lines.push('')
|
||
stopBlocks.forEach((stop, index) => {
|
||
if (index > 0) lines.push('')
|
||
appendDriverStopBlockLines(lines, stop)
|
||
})
|
||
} else if (productLines.length) {
|
||
lines.push('')
|
||
lines.push(`产品:${productLines.join(';')}`)
|
||
}
|
||
|
||
lines.push('')
|
||
lines.push(`司机链接:${options.driverUrl || '—'}`)
|
||
const links = (options.poundQrcodeLinks || []).filter((x) => x?.url)
|
||
if (links.length) {
|
||
lines.push('')
|
||
lines.push('磅单二维码链接:')
|
||
for (const item of links) {
|
||
lines.push(`${item.label}:${item.url}`)
|
||
}
|
||
}
|
||
return lines.join('\n')
|
||
}
|