From 47cd09d1e5cd0f3199d0b2ccafe9259de02376fe Mon Sep 17 00:00:00 2001 From: wuzilong <704541015@qq.com> Date: Tue, 6 Oct 2026 09:32:01 +0800 Subject: [PATCH] feat: span transport plan list by plan and delivery column props Co-authored-by: Cursor --- .../utils/pl-plan-list-display-rows.test.ts | 98 ++++++++++++++++++- .../src/utils/pl-plan-list-display-rows.ts | 69 ++++++++++--- 2 files changed, 155 insertions(+), 12 deletions(-) diff --git a/web-admin/src/utils/pl-plan-list-display-rows.test.ts b/web-admin/src/utils/pl-plan-list-display-rows.test.ts index cc2a3fa85..03883e763 100644 --- a/web-admin/src/utils/pl-plan-list-display-rows.test.ts +++ b/web-admin/src/utils/pl-plan-list-display-rows.test.ts @@ -2,8 +2,12 @@ import assert from 'node:assert/strict' import { describe, it } from 'node:test' import type { Plan, PlanStopProduct } from '@/api/pl' import { + buildPlPlanListSpanMethod, flattenPlanArchivePairRows, - matchLoadProduct + flattenPlanListRows, + matchLoadProduct, + PL_DELIVERY_SPAN_PROPS, + PL_PLAN_SPAN_PROPS } from './pl-plan-list-display-rows' function sp(partial: Partial & Pick): PlanStopProduct { @@ -93,3 +97,95 @@ describe('flattenPlanArchivePairRows product rows', () => { assert.equal(rows[0].deliveryRowSpan, 1) }) }) + +describe('buildPlPlanListSpanMethod two-level', () => { + it('merges plan and delivery columns independently', () => { + const rows = flattenPlanArchivePairRows([samplePlan()]) + const span = buildPlPlanListSpanMethod() + const planFirst = span({ + row: rows[0], + column: { property: 'loadCustomer' }, + rowIndex: 0, + columnIndex: 0 + }) + assert.deepEqual(planFirst, { rowspan: 3, colspan: 1 }) + const planSecond = span({ + row: rows[1], + column: { property: 'loadCustomer' }, + rowIndex: 1, + columnIndex: 0 + }) + assert.deepEqual(planSecond, { rowspan: 0, colspan: 0 }) + const delFirst = span({ + row: rows[0], + column: { property: 'deliveryCustomer' }, + rowIndex: 0, + columnIndex: 0 + }) + assert.deepEqual(delFirst, { rowspan: 2, colspan: 1 }) + const delThird = span({ + row: rows[2], + column: { property: 'deliveryCustomer' }, + rowIndex: 2, + columnIndex: 0 + }) + assert.deepEqual(delThird, { rowspan: 1, colspan: 1 }) + const leaf = span({ + row: rows[0], + column: { property: 'productName' }, + rowIndex: 0, + columnIndex: 0 + }) + assert.equal(leaf, undefined) + const sel = span({ + row: rows[0], + column: { type: 'selection' }, + rowIndex: 0, + columnIndex: 0 + }) + assert.deepEqual(sel, { rowspan: 3, colspan: 1 }) + assert.equal(PL_PLAN_SPAN_PROPS.has('loadAddressTime'), true) + assert.equal(PL_DELIVERY_SPAN_PROPS.has('deliveryContract'), true) + }) + + it('non-pair rows merge only plan columns by planRowSpan', () => { + const rows = flattenPlanListRows([samplePlan()]) + const span = buildPlPlanListSpanMethod([], 3) + assert.deepEqual( + span({ + row: rows[0], + column: { property: 'planDate' }, + rowIndex: 0, + columnIndex: 0 + }), + { rowspan: 3, colspan: 1 } + ) + assert.deepEqual( + span({ + row: rows[1], + column: { property: 'planDate' }, + rowIndex: 1, + columnIndex: 0 + }), + { rowspan: 0, colspan: 0 } + ) + assert.equal( + span({ + row: rows[1], + column: { property: 'deliveryCustomer' }, + rowIndex: 1, + columnIndex: 0 + }), + undefined + ) + assert.equal( + span({ + row: rows[2], + column: { property: 'deliveryContract' }, + rowIndex: 2, + columnIndex: 0 + }), + undefined + ) + }) +}) diff --git a/web-admin/src/utils/pl-plan-list-display-rows.ts b/web-admin/src/utils/pl-plan-list-display-rows.ts index cb8db0e78..75d2ce04f 100644 --- a/web-admin/src/utils/pl-plan-list-display-rows.ts +++ b/web-admin/src/utils/pl-plan-list-display-rows.ts @@ -18,7 +18,7 @@ export interface PlPlanListDisplayRow { stop: PlanLoad | PlanDelivery deliverySeq?: number /** - * 归档配对行:一行 = 装货 × 一送货。 + * 归档配对行:一行 = 送货段 × 一产品。 * 为 true 时 stop 为送货段,pairLoad 为装货段;kind 固定为 delivery。 */ isPairRow?: boolean @@ -177,7 +177,7 @@ const EMPTY_DELIVERY_STOP: PlanDelivery = { } /** - * 归档列表配对展平:一行 = 装货 × 一个送货。 + * 归档列表配对展平:一行 = 送货段 × 一个产品(带配对装货产品)。 * 无送货段时仍输出一行(送货侧为空),避免有装无送时整单消失。 */ export function flattenPlanArchivePairRows( @@ -469,23 +469,70 @@ export function isPlPlanListPlanMergeColumn( return planColIndex >= 0 && planColIndex < visiblePlanProps.length } +export const PL_PLAN_SPAN_PROPS = new Set([ + 'planNo', + 'followStatus', + 'planDate', + 'deliveryTimes', + 'planType', + 'licensePlate', + 'personnelSummary', + 'followUserName', + 'arrangeCopy', + 'archiveStatus', + 'documentAttachments', + 'erpEntered', + 'planAttachments', + 'carrierName', + 'hazardCertReady', + 'loadCustomer', + 'loadAddressTime', + 'loadContract', + 'loadDibang', + 'loadContractGap' +]) + +export const PL_DELIVERY_SPAN_PROPS = new Set([ + 'deliveryCustomer', + 'deliveryAddressTime', + 'deliveryContract', + 'deliveryDibang', + 'deliveryContractGap' +]) + export function buildPlPlanListSpanMethod( - visiblePlanProps: string[], - fixedMergeColCount: number = PLAN_FIXED_MERGE_COL_COUNT + _visiblePlanProps?: string[], + _fixedMergeColCount?: number ): (args: { row: PlPlanListDisplayRow column: { type?: string; property?: string } rowIndex: number columnIndex: number }) => { rowspan: number; colspan: number } | undefined { - const fixed = Math.max(0, fixedMergeColCount) - const planMergeCount = fixed + visiblePlanProps.length - return ({ row, columnIndex }) => { - if (columnIndex >= planMergeCount) return undefined - if (row.isPlanFirstRow) { - return { rowspan: row.planRowSpan, colspan: 1 } + return ({ row, column }) => { + const type = column?.type + const prop = column?.property || '' + const isPlanCol = + type === 'selection' || type === 'index' || PL_PLAN_SPAN_PROPS.has(prop) + const isDeliveryCol = PL_DELIVERY_SPAN_PROPS.has(prop) + + if (!row.isPairRow) { + if (isPlanCol) { + if (row.isPlanFirstRow) return { rowspan: row.planRowSpan, colspan: 1 } + return { rowspan: 0, colspan: 0 } + } + return undefined } - return { rowspan: 0, colspan: 0 } + + if (isPlanCol) { + if (row.isPlanFirstRow) return { rowspan: row.planRowSpan, colspan: 1 } + return { rowspan: 0, colspan: 0 } + } + if (isDeliveryCol) { + if (row.isDeliveryFirstRow) return { rowspan: row.deliveryRowSpan || 1, colspan: 1 } + return { rowspan: 0, colspan: 0 } + } + return undefined } }