feat(web-admin): show brand qty on transport tab and highlight supplement actions

Expose driver per-product feedback on transport read, display site/driver
brand summaries in the transport tab, move supplement buttons to section
headers with primary highlight, and wire brand chips into IO/driver edit.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-07-31 05:35:42 +08:00
1 parent b7236329c2
commit 5c323f32c2
7 files changed
+510 -118

No files matched your search

+89 -11
View File
@@ -94,11 +94,12 @@ export function getPlanProductQuantity(product: {
inboundEstimateQty?: number | string | null
outboundEstimateQty?: number | string | null
}) {
const qty =
product.actualQty ??
product.quantity ??
product.inboundEstimateQty ??
product.outboundEstimateQty
const actual = product.actualQty
// 实际数量为空或为 0 时,展示合同/计划数量
if (actual !== undefined && actual !== null && actual !== '' && Number(actual) !== 0) {
return String(actual)
}
const qty = product.quantity ?? product.inboundEstimateQty ?? product.outboundEstimateQty
if (qty === undefined || qty === null || qty === '') return '—'
return String(qty)
}
@@ -146,6 +147,67 @@ export function formatPlanProductListText(
return `${name}${specPart},${qtyUnit}`
}
/** 入库单/出库单列表产品一行:名称 + 品牌/型号规格/单位/包装规格/送货方式 */
export function formatIoOrderProductListText(item: {
productName?: string | null
brandSummary?: string | null
brandName?: string | null
specification?: string | null
unit?: string | null
unitLabel?: string | null
packageSpec?: string | null
deliveryMethod?: string | null
}) {
const name = (item.productName || '').trim() || '—'
const bits: string[] = []
const brand = (item.brandSummary || item.brandName || '').trim()
if (brand) bits.push(`品牌 ${brand}`)
const spec = (item.specification || '').trim()
if (spec) bits.push(`型号/规格 ${spec}`)
const unit = (item.unitLabel || item.unit || '').trim()
if (unit) bits.push(`单位 ${unit}`)
const packageSpec = (item.packageSpec || '').trim()
if (packageSpec) bits.push(`包装规格 ${packageSpec}`)
const delivery = (item.deliveryMethod || '').trim()
if (delivery) {
const deliveryLabels: Record<string, string> = {
self_pick: '山峡自提',
one_ticket: '一票制',
customer_pick: '客户自提'
}
bits.push(`送货方式 ${deliveryLabels[delivery] || delivery}`)
}
return bits.length ? `${name},${bits.join(',')}` : name
}
/** 品牌数量摘要,如 `君正 16 / 红四方 12` */
export function formatBrandQtySummary(
rows?: Array<{ brandName?: string | null; qty?: number | string | null }> | null
): string {
if (!rows?.length) return ''
const parts = rows
.map(row => {
const qty = Number(row.qty ?? 0)
if (!Number.isFinite(qty) || qty <= 0) return ''
const name = (row.brandName || '').trim() || '未命名品牌'
return `${name} ${qty}`
})
.filter(Boolean)
return parts.join(' / ')
}
/** 现场入出库明细品牌摘要(优先 brandQtys,其次 brandSummary) */
export function resolveSiteBrandSummary(item: {
brandQtys?: Array<{ brandName?: string | null; qty?: number | string | null }> | null
brandSummary?: string | null
brandName?: string | null
}) {
const fromQtys = formatBrandQtySummary(item.brandQtys)
if (fromQtys) return fromQtys
const summary = (item.brandSummary || item.brandName || '').trim()
return summary || '—'
}
/** 司机公开页产品一行展示(含运输计划产品备注) */
export function formatPublicTransportProductLine(product: {
productName?: string
@@ -223,22 +285,38 @@ export function plPlanTypeFilterToTab(planType?: string): PlPlanTypeTab {
return 'all'
}
/** 运输计划跟进状态选项(筛选) */
export const PL_PLAN_FOLLOW_STATUS_OPTIONS = [
{ label: '等通知', value: 'wait_notice' },
{ label: '待安排', value: 'pending_arrange' },
{ label: '已安排', value: 'arranged' },
{ label: '已完成', value: 'completed' },
{ label: '已取消', value: 'cancelled' }
] as const
export function getPlPlanFollowStatusTagType(status?: string) {
const map: Record<string, string> = {
draft: 'info',
processing: 'warning',
wait_notice: 'warning',
pending_arrange: 'danger',
arranged: 'success',
completed: 'success',
cancelled: 'danger'
cancelled: 'info',
// 旧值兼容(迁移前)
draft: 'danger',
processing: 'warning'
}
return map[status || ''] || 'info'
}
export function getPlPlanFollowStatusLabel(status?: string) {
const map: Record<string, string> = {
draft: '草稿',
processing: '进行中',
wait_notice: '等通知',
pending_arrange: '待安排',
arranged: '已安排',
completed: '已完成',
cancelled: '已作废'
cancelled: '已取消',
draft: '待安排',
processing: '进行中'
}
return status ? map[status] || status : '—'
}