|
|
@@ -23,11 +23,15 @@ export interface PlPlanListDisplayRow {
|
|
|
*/
|
|
|
isPairRow?: boolean
|
|
|
pairLoad?: PlanLoad
|
|
|
+ deliveryProduct?: PlanStopProduct
|
|
|
+ loadProduct?: PlanStopProduct
|
|
|
/** 当前页内计划序号(0-based),用于整单斑马纹 */
|
|
|
planListIndex: number
|
|
|
isPlanFirstRow: boolean
|
|
|
planRowSpan: number
|
|
|
isPlanLastRow: boolean
|
|
|
+ deliveryRowSpan: number
|
|
|
+ isDeliveryFirstRow: boolean
|
|
|
}
|
|
|
|
|
|
const PLAN_FIXED_MERGE_COL_COUNT = 3
|
|
|
@@ -79,6 +83,31 @@ export function stopCustomerMatchesKeywords(
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+function productNameMatchesKeywords(
|
|
|
+ name: string | null | undefined,
|
|
|
+ keywords: string[]
|
|
|
+): boolean {
|
|
|
+ const text = String(name || '').trim().toLowerCase()
|
|
|
+ if (!text) return false
|
|
|
+ return keywords.some((k) => text.includes(k.trim().toLowerCase()))
|
|
|
+}
|
|
|
+
|
|
|
+export function matchLoadProduct(
|
|
|
+ loadProducts: PlanStopProduct[] | undefined,
|
|
|
+ deliveryProduct: PlanStopProduct | null | undefined
|
|
|
+): PlanStopProduct | undefined {
|
|
|
+ if (!deliveryProduct) return undefined
|
|
|
+ const list = loadProducts || []
|
|
|
+ const ppid = String(deliveryProduct.planProductId || '').trim()
|
|
|
+ if (ppid) {
|
|
|
+ const hit = list.find((p) => String(p.planProductId || '').trim() === ppid)
|
|
|
+ if (hit) return hit
|
|
|
+ }
|
|
|
+ const pid = String(deliveryProduct.productId || '').trim()
|
|
|
+ if (!pid) return undefined
|
|
|
+ return list.find((p) => String(p.productId || '').trim() === pid)
|
|
|
+}
|
|
|
+
|
|
|
export function flattenPlanListRows(
|
|
|
plans: Plan[],
|
|
|
options?: { kinds?: PlPlanListDisplayRowKind[] | null; customerKeywords?: string[] | null }
|
|
|
@@ -130,7 +159,9 @@ export function flattenPlanListRows(
|
|
|
planListIndex: rowPlanIndex,
|
|
|
isPlanFirstRow: index === 0,
|
|
|
planRowSpan,
|
|
|
- isPlanLastRow: index === slots.length - 1
|
|
|
+ isPlanLastRow: index === slots.length - 1,
|
|
|
+ deliveryRowSpan: 1,
|
|
|
+ isDeliveryFirstRow: true
|
|
|
})
|
|
|
})
|
|
|
if (narrowed) visiblePlanIndex += 1
|
|
|
@@ -156,11 +187,13 @@ export function flattenPlanArchivePairRows(
|
|
|
deliveryCustomerKeywords?: string[] | null
|
|
|
/** 任一侧客户包含关键字即保留(兼容旧 stopCustomers) */
|
|
|
customerKeywords?: string[] | null
|
|
|
+ productKeywords?: string[] | null
|
|
|
}
|
|
|
): PlPlanListDisplayRow[] {
|
|
|
const loadKeywords = normalizeStopCustomerKeywords(options?.loadCustomerKeywords)
|
|
|
const deliveryKeywords = normalizeStopCustomerKeywords(options?.deliveryCustomerKeywords)
|
|
|
const eitherKeywords = normalizeStopCustomerKeywords(options?.customerKeywords)
|
|
|
+ const productKeywords = normalizeStopCustomerKeywords(options?.productKeywords)
|
|
|
const rows: PlPlanListDisplayRow[] = []
|
|
|
let visiblePlanIndex = 0
|
|
|
|
|
|
@@ -195,22 +228,61 @@ export function flattenPlanArchivePairRows(
|
|
|
})
|
|
|
|
|
|
if (!slots.length) return
|
|
|
- const narrowed = !!(loadKeywords || deliveryKeywords || eitherKeywords)
|
|
|
+
|
|
|
+ type ProductSlot = {
|
|
|
+ delivery: PlanDelivery
|
|
|
+ deliverySeq: number
|
|
|
+ deliveryProduct?: PlanStopProduct
|
|
|
+ loadProduct?: PlanStopProduct
|
|
|
+ }
|
|
|
+ const productSlots: ProductSlot[] = []
|
|
|
+ for (const slot of slots) {
|
|
|
+ const products = slot.delivery.products || []
|
|
|
+ const picked =
|
|
|
+ products.length > 0
|
|
|
+ ? products.filter((p) =>
|
|
|
+ productKeywords
|
|
|
+ ? productNameMatchesKeywords(p.productName, productKeywords)
|
|
|
+ : true
|
|
|
+ )
|
|
|
+ : productKeywords
|
|
|
+ ? []
|
|
|
+ : [undefined]
|
|
|
+ for (const deliveryProduct of picked) {
|
|
|
+ productSlots.push({
|
|
|
+ delivery: slot.delivery,
|
|
|
+ deliverySeq: slot.deliverySeq,
|
|
|
+ deliveryProduct,
|
|
|
+ loadProduct: matchLoadProduct(load.products, deliveryProduct)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!productSlots.length) return
|
|
|
+ const narrowed = !!(loadKeywords || deliveryKeywords || eitherKeywords || productKeywords)
|
|
|
const rowPlanIndex = narrowed ? visiblePlanIndex : planListIndex
|
|
|
- const planRowSpan = slots.length
|
|
|
- slots.forEach((slot, index) => {
|
|
|
+ const planRowSpan = productSlots.length
|
|
|
+ productSlots.forEach((slot, index) => {
|
|
|
+ const same = productSlots.filter((s) => s.deliverySeq === slot.deliverySeq)
|
|
|
+ const firstIndex = productSlots.findIndex((s) => s.deliverySeq === slot.deliverySeq)
|
|
|
+ const productKey =
|
|
|
+ slot.deliveryProduct?.planProductId || slot.deliveryProduct?.productId || 'empty'
|
|
|
rows.push({
|
|
|
- rowKey: `${plan.id}-pair-${slot.deliverySeq}`,
|
|
|
+ rowKey: `${plan.id}-pair-${slot.deliverySeq}-${productKey}`,
|
|
|
plan,
|
|
|
kind: 'delivery',
|
|
|
stop: slot.delivery,
|
|
|
deliverySeq: slot.deliverySeq,
|
|
|
isPairRow: true,
|
|
|
pairLoad: load,
|
|
|
+ deliveryProduct: slot.deliveryProduct,
|
|
|
+ loadProduct: slot.loadProduct,
|
|
|
planListIndex: rowPlanIndex,
|
|
|
isPlanFirstRow: index === 0,
|
|
|
planRowSpan,
|
|
|
- isPlanLastRow: index === slots.length - 1
|
|
|
+ isPlanLastRow: index === productSlots.length - 1,
|
|
|
+ deliveryRowSpan: same.length,
|
|
|
+ isDeliveryFirstRow: index === firstIndex
|
|
|
})
|
|
|
})
|
|
|
if (narrowed) visiblePlanIndex += 1
|
|
|
@@ -282,6 +354,14 @@ export function getDeliveryProductsFromRow(row: PlPlanListDisplayRow): PlanStopP
|
|
|
return getPairDeliveryStop(row)?.products || []
|
|
|
}
|
|
|
|
|
|
+export function getLoadContractsFromRow(row: PlPlanListDisplayRow) {
|
|
|
+ return getPairLoadStop(row)?.contracts || []
|
|
|
+}
|
|
|
+
|
|
|
+export function getDeliveryContractsFromRow(row: PlPlanListDisplayRow) {
|
|
|
+ return getPairDeliveryStop(row)?.contracts || []
|
|
|
+}
|
|
|
+
|
|
|
function stopHasProductId(
|
|
|
stop: PlanLoad | PlanDelivery | null | undefined,
|
|
|
productId: string
|
|
|
@@ -355,7 +435,9 @@ export function flattenPlanListRowsForProduct(
|
|
|
planListIndex,
|
|
|
isPlanFirstRow: index === 0,
|
|
|
planRowSpan,
|
|
|
- isPlanLastRow: index === stops.length - 1
|
|
|
+ isPlanLastRow: index === stops.length - 1,
|
|
|
+ deliveryRowSpan: 1,
|
|
|
+ isDeliveryFirstRow: true
|
|
|
})
|
|
|
})
|
|
|
planListIndex += 1
|