Files
shanxia2.0/web-admin/src/utils/pl-driver-summary.ts
T
wuzilongandCursor ab3a90c3f5 feat(web-admin): append pound QR links in copy-to-driver text
Include multi-line public pound QR URLs after the driver link when dibang slips exist.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-13 09:41:41 +08:00

285 lines
8.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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')
}