feat: split transport plan list columns into load and delivery product rows

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
wuzilongandCursor committed 2026-10-06 09:45:11 +08:00
1 parent c19318d0d0
commit 56776bb197
3 files changed
+466 -325

No files matched your search

@@ -28,6 +28,8 @@ export function usePlPlanMergeListTable(options: {
archivePairMode?: boolean | Ref<boolean> | ComputedRef<boolean>
loadCustomerKeywords?: Ref<string[] | null> | ComputedRef<string[] | null>
deliveryCustomerKeywords?: Ref<string[] | null> | ComputedRef<string[] | null>
/** 产品名称关键字:只保留命中产品行并重算 rowspan */
productKeywords?: Ref<string[] | null> | ComputedRef<string[] | null>
}) {
const displayRows = computed(() => {
const pairMode = !!unref(options.archivePairMode)
@@ -41,7 +43,8 @@ export function usePlPlanMergeListTable(options: {
: null,
customerKeywords: options.stopCustomerKeywords
? unref(options.stopCustomerKeywords)
: null
: null,
productKeywords: options.productKeywords ? unref(options.productKeywords) : null
})
}
return flattenPlanListRows(unref(options.planList), {
+44 -11
View File
@@ -54,7 +54,8 @@ export const plPlanColumnFilterDefs: ColumnFilterDef[] = [
type: 'textMulti',
queryKey: 'deliveryCustomers'
},
{ prop: 'stopAddressTime', label: '地址 / 时间', type: 'text', queryKey: 'stopAddress' },
{ prop: 'loadAddressTime', label: '装货地址/时间', type: 'text', queryKey: 'stopAddress' },
{ prop: 'deliveryAddressTime', label: '送货地址/时间', type: 'text', queryKey: 'stopAddress' },
{ prop: 'productName', label: '产品名称', type: 'text', queryKey: 'productName' },
{ prop: 'productSpec', label: '型号/规格', type: 'text', queryKey: 'productSpec' },
{
@@ -64,29 +65,61 @@ export const plPlanColumnFilterDefs: ColumnFilterDef[] = [
queryKey: 'packageSpec'
},
{ prop: 'productUnit', label: '单位', type: 'text', queryKey: 'productUnit' },
{ prop: 'productPlanQty', label: '计划数量', type: 'number', queryKeyPrefix: 'productPlanQty' },
{ prop: 'loadPlanQty', label: '装货计划量', type: 'number', queryKeyPrefix: 'productPlanQty' },
{ prop: 'deliveryPlanQty', label: '送货计划量', type: 'number', queryKeyPrefix: 'productPlanQty' },
{
prop: 'productActualQty',
label: '实际数量',
prop: 'loadActualQty',
label: '装货实际量',
type: 'number',
queryKeyPrefix: 'productActualQty'
},
{ prop: 'stopContract', label: '合同', type: 'text', queryKey: 'contractNo' },
{
prop: 'productInboundQty',
label: '入库数量',
prop: 'deliveryActualQty',
label: '送货实际量',
type: 'number',
queryKeyPrefix: 'productActualQty'
},
{ prop: 'loadContract', label: '装货合同', type: 'text', queryKey: 'contractNo' },
{ prop: 'deliveryContract', label: '送货合同', type: 'text', queryKey: 'contractNo' },
{
prop: 'loadInboundQty',
label: '装货入库',
type: 'number',
queryKeyPrefix: 'productInboundQty'
},
{
prop: 'productOutboundQty',
label: '出库数量',
prop: 'deliveryInboundQty',
label: '送货入库',
type: 'number',
queryKeyPrefix: 'productInboundQty'
},
{
prop: 'loadOutboundQty',
label: '装货出库',
type: 'number',
queryKeyPrefix: 'productOutboundQty'
},
{
prop: 'stopDibang',
label: '地磅',
prop: 'deliveryOutboundQty',
label: '送货出库',
type: 'number',
queryKeyPrefix: 'productOutboundQty'
},
{
prop: 'loadDibang',
label: '装货地磅',
type: 'enum',
queryKey: 'stopDibang',
enumOptions: [
{ value: 'outbound', label: '出库' },
{ value: 'inbound', label: '入库' },
{ value: 'none', label: '无' },
{ value: 'off', label: '未生成' }
]
},
{
prop: 'deliveryDibang',
label: '送货地磅',
type: 'enum',
queryKey: 'stopDibang',
enumOptions: [
+418 -313
View File
@@ -386,9 +386,9 @@
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('stopAddressTime')"
prop="stopAddressTime"
label="地址 / 时间"
v-if="isColumnVisible('loadAddressTime')"
prop="loadAddressTime"
label="装货地址/时间"
min-width="240"
align="left"
header-align="left"
@@ -397,42 +397,58 @@
>
<template #header>
<div class="col-header-with-filter">
<span class="col-header-with-filter__label is-left">地址 / 时间</span>
<span class="col-header-with-filter__label is-left">装货地址/时间</span>
<ColumnHeaderFilter
prop="stopAddressTime"
label="地址 / 时间"
prop="loadAddressTime"
label="装货地址/时间"
type="text"
:is-active="isFilterActive('stopAddressTime')"
:model-value="filters.stopAddressTime || null"
@update:model-value="(v) => setFilterState('stopAddressTime', v)"
:is-active="isFilterActive('loadAddressTime')"
:model-value="filters.loadAddressTime || null"
@update:model-value="(v) => setFilterState('loadAddressTime', v)"
/>
</div>
</template>
<template #default="{ row }">
<div class="pl-plan-pair-stack">
<div class="pl-plan-pair-stack__row pl-plan-pair-stack__row--load">
<span class="pl-plan-pair-stack__label pl-plan-pair-stack__label--load">装</span>
<div class="pl-plan-pair-stack__body">
<div class="pl-plan-pair-stack__name">
{{ formatStopAddressText(getPairLoadStop(row)) }}
</div>
<div class="pl-plan-pair-stack__sub">
{{ formatLoadTimeText(getPairLoadStop(row)) }}
</div>
</div>
<div class="pl-plan-stop-address-time">
<div class="pl-plan-stop-address-time__addr">
{{ formatStopAddressText(getPairLoadStop(row)) }}
</div>
<div class="pl-plan-pair-stack__row pl-plan-pair-stack__row--delivery">
<span class="pl-plan-pair-stack__label pl-plan-pair-stack__label--delivery"
>送</span
>
<div class="pl-plan-pair-stack__body">
<div class="pl-plan-pair-stack__name">
{{ formatStopAddressText(getPairDeliveryStop(row)) }}
</div>
<div class="pl-plan-pair-stack__sub">
{{ formatDeliveryTimeText(getPairDeliveryStop(row)) }}
</div>
</div>
<div class="pl-plan-stop-address-time__time">
{{ formatLoadTimeText(getPairLoadStop(row)) }}
</div>
</div>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('deliveryAddressTime')"
prop="deliveryAddressTime"
label="送货地址/时间"
min-width="240"
align="left"
header-align="left"
class-name="col-name-wrap pl-plan-stop-col"
label-class-name="col-name-wrap"
>
<template #header>
<div class="col-header-with-filter">
<span class="col-header-with-filter__label is-left">送货地址/时间</span>
<ColumnHeaderFilter
prop="deliveryAddressTime"
label="送货地址/时间"
type="text"
:is-active="isFilterActive('deliveryAddressTime')"
:model-value="filters.deliveryAddressTime || null"
@update:model-value="(v) => setFilterState('deliveryAddressTime', v)"
/>
</div>
</template>
<template #default="{ row }">
<div class="pl-plan-stop-address-time">
<div class="pl-plan-stop-address-time__addr">
{{ formatStopAddressText(getPairDeliveryStop(row)) }}
</div>
<div class="pl-plan-stop-address-time__time">
{{ formatDeliveryTimeText(getPairDeliveryStop(row)) }}
</div>
</div>
</template>
@@ -463,7 +479,7 @@
<template #default="{ row }">
<PlPlanListProductCell
:stop-products="getDeliveryProductsFromRow(row)"
:stop-products="row.deliveryProduct ? [row.deliveryProduct] : []"
:plan-products="row.plan.products"
field="name"
/>
@@ -494,7 +510,7 @@
</template>
<template #default="{ row }">
<PlPlanListProductCell
:stop-products="getDeliveryProductsFromRow(row)"
:stop-products="row.deliveryProduct ? [row.deliveryProduct] : []"
:plan-products="row.plan.products"
field="spec"
/>
@@ -525,7 +541,7 @@
</template>
<template #default="{ row }">
<PlPlanListProductCell
:stop-products="getDeliveryProductsFromRow(row)"
:stop-products="row.deliveryProduct ? [row.deliveryProduct] : []"
:plan-products="row.plan.products"
field="packageSpec"
/>
@@ -555,16 +571,16 @@
</template>
<template #default="{ row }">
<PlPlanListProductCell
:stop-products="getDeliveryProductsFromRow(row)"
:stop-products="row.deliveryProduct ? [row.deliveryProduct] : []"
:plan-products="row.plan.products"
field="unit"
/>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('productPlanQty')"
prop="productPlanQty"
label="计划数量"
v-if="isColumnVisible('loadPlanQty')"
prop="loadPlanQty"
label="装货计划量"
min-width="100"
align="right"
header-align="right"
@@ -572,48 +588,30 @@
>
<template #header>
<div class="col-header-with-filter">
<span class="col-header-with-filter__label is-right">计划数量</span>
<span class="col-header-with-filter__label is-right">装货计划量</span>
<ColumnHeaderFilter
prop="productPlanQty"
label="计划数量"
prop="loadPlanQty"
label="装货计划量"
type="number"
:is-active="isFilterActive('productPlanQty')"
:model-value="filters.productPlanQty || null"
@update:model-value="(v) => setFilterState('productPlanQty', v)"
:is-active="isFilterActive('loadPlanQty')"
:model-value="filters.loadPlanQty || null"
@update:model-value="(v) => setFilterState('loadPlanQty', v)"
/>
</div>
</template>
<template #default="{ row }">
<div class="pl-plan-pair-stack">
<div class="pl-plan-pair-stack__row pl-plan-pair-stack__row--load">
<span class="pl-plan-pair-stack__label pl-plan-pair-stack__label--load">装</span>
<div class="pl-plan-pair-stack__body">
<PlPlanListProductCell
stop-kind="load"
:stop-products="getLoadProductsFromRow(row)"
:plan-products="row.plan.products"
field="planQty"
/>
</div>
</div>
<div class="pl-plan-pair-stack__row pl-plan-pair-stack__row--delivery">
<span class="pl-plan-pair-stack__label pl-plan-pair-stack__label--delivery">送</span>
<div class="pl-plan-pair-stack__body">
<PlPlanListProductCell
stop-kind="delivery"
:stop-products="getDeliveryProductsFromRow(row)"
:plan-products="row.plan.products"
field="planQty"
/>
</div>
</div>
</div>
<PlPlanListProductCell
stop-kind="load"
:stop-products="row.loadProduct ? [row.loadProduct] : []"
:plan-products="row.plan.products"
field="planQty"
/>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('productActualQty')"
prop="productActualQty"
label="实际数量"
v-if="isColumnVisible('deliveryPlanQty')"
prop="deliveryPlanQty"
label="送货计划量"
min-width="100"
align="right"
header-align="right"
@@ -621,43 +619,87 @@
>
<template #header>
<div class="col-header-with-filter">
<span class="col-header-with-filter__label is-right">实际数量</span>
<span class="col-header-with-filter__label is-right">送货计划量</span>
<ColumnHeaderFilter
prop="productActualQty"
label="实际数量"
prop="deliveryPlanQty"
label="送货计划量"
type="number"
:is-active="isFilterActive('productActualQty')"
:model-value="filters.productActualQty || null"
@update:model-value="(v) => setFilterState('productActualQty', v)"
:is-active="isFilterActive('deliveryPlanQty')"
:model-value="filters.deliveryPlanQty || null"
@update:model-value="(v) => setFilterState('deliveryPlanQty', v)"
/>
</div>
</template>
<template #default="{ row }">
<div class="pl-plan-pair-stack">
<div class="pl-plan-pair-stack__row pl-plan-pair-stack__row--load">
<span class="pl-plan-pair-stack__label pl-plan-pair-stack__label--load">装</span>
<div class="pl-plan-pair-stack__body">
<PlPlanListProductCell
stop-kind="load"
:stop-products="getLoadProductsFromRow(row)"
:plan-products="row.plan.products"
field="actualQty"
/>
</div>
</div>
<div class="pl-plan-pair-stack__row pl-plan-pair-stack__row--delivery">
<span class="pl-plan-pair-stack__label pl-plan-pair-stack__label--delivery">送</span>
<div class="pl-plan-pair-stack__body">
<PlPlanListProductCell
stop-kind="delivery"
:stop-products="getDeliveryProductsFromRow(row)"
:plan-products="row.plan.products"
field="actualQty"
/>
</div>
</div>
<PlPlanListProductCell
stop-kind="delivery"
:stop-products="row.deliveryProduct ? [row.deliveryProduct] : []"
:plan-products="row.plan.products"
field="planQty"
/>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('loadActualQty')"
prop="loadActualQty"
label="装货实际量"
min-width="100"
align="right"
header-align="right"
class-name="pl-plan-stop-col"
>
<template #header>
<div class="col-header-with-filter">
<span class="col-header-with-filter__label is-right">装货实际量</span>
<ColumnHeaderFilter
prop="loadActualQty"
label="装货实际量"
type="number"
:is-active="isFilterActive('loadActualQty')"
:model-value="filters.loadActualQty || null"
@update:model-value="(v) => setFilterState('loadActualQty', v)"
/>
</div>
</template>
<template #default="{ row }">
<PlPlanListProductCell
stop-kind="load"
:stop-products="row.loadProduct ? [row.loadProduct] : []"
:plan-products="row.plan.products"
field="actualQty"
/>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('deliveryActualQty')"
prop="deliveryActualQty"
label="送货实际量"
min-width="100"
align="right"
header-align="right"
class-name="pl-plan-stop-col"
>
<template #header>
<div class="col-header-with-filter">
<span class="col-header-with-filter__label is-right">送货实际量</span>
<ColumnHeaderFilter
prop="deliveryActualQty"
label="送货实际量"
type="number"
:is-active="isFilterActive('deliveryActualQty')"
:model-value="filters.deliveryActualQty || null"
@update:model-value="(v) => setFilterState('deliveryActualQty', v)"
/>
</div>
</template>
<template #default="{ row }">
<PlPlanListProductCell
stop-kind="delivery"
:stop-products="row.deliveryProduct ? [row.deliveryProduct] : []"
:plan-products="row.plan.products"
field="actualQty"
/>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('planAttachments')"
@@ -669,16 +711,21 @@
>
<template #default="{ row }">
<PlPlanListCertCell
:products="resolveStopCertProducts(row.plan, getDeliveryProductsFromRow(row))"
:products="
resolveStopCertProducts(
row.plan,
row.deliveryProduct ? [row.deliveryProduct] : []
)
"
compact
hide-product
/>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('stopContract')"
prop="stopContract"
label="合同"
v-if="isColumnVisible('loadContract')"
prop="loadContract"
label="装货合同"
min-width="180"
align="left"
header-align="left"
@@ -687,44 +734,58 @@
>
<template #header>
<div class="col-header-with-filter">
<span class="col-header-with-filter__label is-left">合同</span>
<span class="col-header-with-filter__label is-left">装货合同</span>
<ColumnHeaderFilter
prop="stopContract"
label="合同"
prop="loadContract"
label="装货合同"
type="text"
:is-active="isFilterActive('stopContract')"
:model-value="filters.stopContract || null"
@update:model-value="(v) => setFilterState('stopContract', v)"
:is-active="isFilterActive('loadContract')"
:model-value="filters.loadContract || null"
@update:model-value="(v) => setFilterState('loadContract', v)"
/>
</div>
</template>
<template #default="{ row }">
<div class="pl-plan-pair-stack">
<div class="pl-plan-pair-stack__row pl-plan-pair-stack__row--load">
<span class="pl-plan-pair-stack__label pl-plan-pair-stack__label--load">装</span>
<div class="pl-plan-pair-stack__body pl-plan-pair-stack__contract">
<PlPlanContractCell
:lines="getLoadContractLinesFromRow(row)"
@open="openContractDetail"
/>
</div>
</div>
<div class="pl-plan-pair-stack__row pl-plan-pair-stack__row--delivery">
<span class="pl-plan-pair-stack__label pl-plan-pair-stack__label--delivery">送</span>
<div class="pl-plan-pair-stack__body pl-plan-pair-stack__contract">
<PlPlanContractCell
:lines="getDeliveryContractLinesFromRow(row)"
@open="openContractDetail"
/>
</div>
</div>
</div>
<PlPlanContractCell
:lines="getLoadContractLinesFromRow(row)"
@open="openContractDetail"
/>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('productInboundQty')"
prop="productInboundQty"
label="入库数量"
v-if="isColumnVisible('deliveryContract')"
prop="deliveryContract"
label="送货合同"
min-width="180"
align="left"
header-align="left"
class-name="col-name-wrap pl-plan-stop-col"
label-class-name="col-name-wrap"
>
<template #header>
<div class="col-header-with-filter">
<span class="col-header-with-filter__label is-left">送货合同</span>
<ColumnHeaderFilter
prop="deliveryContract"
label="送货合同"
type="text"
:is-active="isFilterActive('deliveryContract')"
:model-value="filters.deliveryContract || null"
@update:model-value="(v) => setFilterState('deliveryContract', v)"
/>
</div>
</template>
<template #default="{ row }">
<PlPlanContractCell
:lines="getDeliveryContractLinesFromRow(row)"
@open="openContractDetail"
/>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('loadInboundQty')"
prop="loadInboundQty"
label="装货入库"
min-width="100"
align="right"
header-align="right"
@@ -732,48 +793,31 @@
>
<template #header>
<div class="col-header-with-filter">
<span class="col-header-with-filter__label is-right">入库数量</span>
<span class="col-header-with-filter__label is-right">装货入库</span>
<ColumnHeaderFilter
prop="productInboundQty"
label="入库数量"
prop="loadInboundQty"
label="装货入库"
type="number"
:is-active="isFilterActive('productInboundQty')"
:model-value="filters.productInboundQty || null"
@update:model-value="(v) => setFilterState('productInboundQty', v)"
:is-active="isFilterActive('loadInboundQty')"
:model-value="filters.loadInboundQty || null"
@update:model-value="(v) => setFilterState('loadInboundQty', v)"
/>
</div>
</template>
<template #default="{ row }">
<div class="pl-plan-pair-stack">
<div class="pl-plan-pair-stack__row pl-plan-pair-stack__row--load">
<span class="pl-plan-pair-stack__label pl-plan-pair-stack__label--load">装</span>
<div class="pl-plan-pair-stack__body">
<PlPlanListProductCell
stop-kind="load"
:stop-products="getLoadProductsFromRow(row)"
:plan-products="row.plan.products"
field="inboundQty"
/>
</div>
</div>
<div class="pl-plan-pair-stack__row pl-plan-pair-stack__row--delivery">
<span class="pl-plan-pair-stack__label pl-plan-pair-stack__label--delivery">送</span>
<div class="pl-plan-pair-stack__body">
<PlPlanListProductCell
stop-kind="delivery"
:stop-products="getDeliveryProductsFromRow(row)"
:plan-products="row.plan.products"
field="inboundQty"
/>
</div>
</div>
</div>
<PlPlanListProductCell
stop-kind="load"
:stop-products="row.loadProduct ? [row.loadProduct] : []"
:plan-products="row.plan.products"
:stop-customer-name="getPairLoadStop(row)?.customerName"
field="inboundQty"
/>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('productOutboundQty')"
prop="productOutboundQty"
label="出库数量"
v-if="isColumnVisible('deliveryInboundQty')"
prop="deliveryInboundQty"
label="送货入库"
min-width="100"
align="right"
header-align="right"
@@ -781,96 +825,156 @@
>
<template #header>
<div class="col-header-with-filter">
<span class="col-header-with-filter__label is-right">出库数量</span>
<span class="col-header-with-filter__label is-right">送货入库</span>
<ColumnHeaderFilter
prop="productOutboundQty"
label="出库数量"
prop="deliveryInboundQty"
label="送货入库"
type="number"
:is-active="isFilterActive('productOutboundQty')"
:model-value="filters.productOutboundQty || null"
@update:model-value="(v) => setFilterState('productOutboundQty', v)"
:is-active="isFilterActive('deliveryInboundQty')"
:model-value="filters.deliveryInboundQty || null"
@update:model-value="(v) => setFilterState('deliveryInboundQty', v)"
/>
</div>
</template>
<template #default="{ row }">
<div class="pl-plan-pair-stack">
<div class="pl-plan-pair-stack__row pl-plan-pair-stack__row--load">
<span class="pl-plan-pair-stack__label pl-plan-pair-stack__label--load">装</span>
<div class="pl-plan-pair-stack__body">
<PlPlanListProductCell
stop-kind="load"
:stop-products="getLoadProductsFromRow(row)"
:plan-products="row.plan.products"
field="outboundQty"
/>
</div>
</div>
<div class="pl-plan-pair-stack__row pl-plan-pair-stack__row--delivery">
<span class="pl-plan-pair-stack__label pl-plan-pair-stack__label--delivery">送</span>
<div class="pl-plan-pair-stack__body">
<PlPlanListProductCell
stop-kind="delivery"
:stop-products="getDeliveryProductsFromRow(row)"
:plan-products="row.plan.products"
field="outboundQty"
/>
</div>
</div>
</div>
<PlPlanListProductCell
stop-kind="delivery"
:stop-products="row.deliveryProduct ? [row.deliveryProduct] : []"
:plan-products="row.plan.products"
:stop-customer-name="getPairDeliveryStop(row)?.customerName"
field="inboundQty"
/>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('stopDibang')"
prop="stopDibang"
label="地磅"
v-if="isColumnVisible('loadOutboundQty')"
prop="loadOutboundQty"
label="装货出库"
min-width="100"
align="right"
header-align="right"
class-name="pl-plan-stop-col"
>
<template #header>
<div class="col-header-with-filter">
<span class="col-header-with-filter__label is-right">装货出库</span>
<ColumnHeaderFilter
prop="loadOutboundQty"
label="装货出库"
type="number"
:is-active="isFilterActive('loadOutboundQty')"
:model-value="filters.loadOutboundQty || null"
@update:model-value="(v) => setFilterState('loadOutboundQty', v)"
/>
</div>
</template>
<template #default="{ row }">
<PlPlanListProductCell
stop-kind="load"
:stop-products="row.loadProduct ? [row.loadProduct] : []"
:plan-products="row.plan.products"
:stop-customer-name="getPairLoadStop(row)?.customerName"
field="outboundQty"
/>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('deliveryOutboundQty')"
prop="deliveryOutboundQty"
label="送货出库"
min-width="100"
align="right"
header-align="right"
class-name="pl-plan-stop-col"
>
<template #header>
<div class="col-header-with-filter">
<span class="col-header-with-filter__label is-right">送货出库</span>
<ColumnHeaderFilter
prop="deliveryOutboundQty"
label="送货出库"
type="number"
:is-active="isFilterActive('deliveryOutboundQty')"
:model-value="filters.deliveryOutboundQty || null"
@update:model-value="(v) => setFilterState('deliveryOutboundQty', v)"
/>
</div>
</template>
<template #default="{ row }">
<PlPlanListProductCell
stop-kind="delivery"
:stop-products="row.deliveryProduct ? [row.deliveryProduct] : []"
:plan-products="row.plan.products"
:stop-customer-name="getPairDeliveryStop(row)?.customerName"
field="outboundQty"
/>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('loadDibang')"
prop="loadDibang"
label="装货地磅"
min-width="120"
align="center"
class-name="pl-plan-stop-col"
>
<template #header>
<div class="col-header-with-filter">
<span class="col-header-with-filter__label">地磅</span>
<span class="col-header-with-filter__label">装货地磅</span>
<ColumnHeaderFilter
prop="stopDibang"
label="地磅"
prop="loadDibang"
label="装货地磅"
type="enum"
:is-active="isFilterActive('stopDibang')"
:model-value="filters.stopDibang || null"
:options="enumOptionsFor('stopDibang')"
@update:model-value="(v) => setFilterState('stopDibang', v)"
:is-active="isFilterActive('loadDibang')"
:model-value="filters.loadDibang || null"
:options="enumOptionsFor('loadDibang')"
@update:model-value="(v) => setFilterState('loadDibang', v)"
/>
</div>
</template>
<template #default="{ row }">
<div class="pl-plan-pair-stack">
<div class="pl-plan-pair-stack__row pl-plan-pair-stack__row--load">
<span class="pl-plan-pair-stack__label pl-plan-pair-stack__label--load">装</span>
<div class="pl-plan-pair-stack__body">
<el-tag
v-if="formatStopDibangLabel(getPairLoadStop(row)!)"
:type="getStopDibangTagType(getPairLoadStop(row)!)"
size="small"
>
{{ formatStopDibangLabel(getPairLoadStop(row)!) }}
</el-tag>
<span v-else class="muted">—</span>
</div>
</div>
<div class="pl-plan-pair-stack__row pl-plan-pair-stack__row--delivery">
<span class="pl-plan-pair-stack__label pl-plan-pair-stack__label--delivery">送</span>
<div class="pl-plan-pair-stack__body">
<el-tag
v-if="formatStopDibangLabel(getPairDeliveryStop(row)!)"
:type="getStopDibangTagType(getPairDeliveryStop(row)!)"
size="small"
>
{{ formatStopDibangLabel(getPairDeliveryStop(row)!) }}
</el-tag>
<span v-else class="muted">—</span>
</div>
</div>
<el-tag
v-if="formatStopDibangLabel(getPairLoadStop(row)!)"
:type="getStopDibangTagType(getPairLoadStop(row)!)"
size="small"
>
{{ formatStopDibangLabel(getPairLoadStop(row)!) }}
</el-tag>
<span v-else class="muted">—</span>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('deliveryDibang')"
prop="deliveryDibang"
label="送货地磅"
min-width="120"
align="center"
class-name="pl-plan-stop-col"
>
<template #header>
<div class="col-header-with-filter">
<span class="col-header-with-filter__label">送货地磅</span>
<ColumnHeaderFilter
prop="deliveryDibang"
label="送货地磅"
type="enum"
:is-active="isFilterActive('deliveryDibang')"
:model-value="filters.deliveryDibang || null"
:options="enumOptionsFor('deliveryDibang')"
@update:model-value="(v) => setFilterState('deliveryDibang', v)"
/>
</div>
</template>
<template #default="{ row }">
<el-tag
v-if="formatStopDibangLabel(getPairDeliveryStop(row)!)"
:type="getStopDibangTagType(getPairDeliveryStop(row)!)"
size="small"
>
{{ formatStopDibangLabel(getPairDeliveryStop(row)!) }}
</el-tag>
<span v-else class="muted">—</span>
</template>
</el-table-column>
</ListTable>
</div>
@@ -1147,18 +1251,17 @@
formatStopDibangLabel,
getDeliveryContractLinesFromRow,
getDeliveryCustomerNameFromRow,
getDeliveryProductsFromRow,
getLoadContractLinesFromRow,
getLoadCustomerNameFromRow,
getLoadProductsFromRow,
getPairDeliveryStop,
getPairLoadStop,
getPlanListDisplayIndex,
getPlPlanListRowClassName,
getRowSideIoQty,
getRowSidePlanQty,
getStopArchiveActualQty,
getStopDibangTagType,
resolveStopCertProducts,
sumStopArchiveActualQty,
sumStopProductsField,
type PlPlanListDisplayRow
} from '@/utils/pl-plan-list-display-rows'
import {
@@ -1227,22 +1330,29 @@
{ prop: 'arrangeCopy', label: '链接复制' },
{ prop: 'loadCustomer', label: '装货客户' },
{ prop: 'deliveryCustomer', label: '送货客户' },
{ prop: 'stopAddressTime', label: '地址 / 时间' },
{ prop: 'loadAddressTime', label: '装货地址/时间' },
{ prop: 'deliveryAddressTime', label: '送货地址/时间' },
{ prop: 'productName', label: '产品名称' },
{ prop: 'productSpec', label: '型号/规格' },
{ prop: 'productPackageSpec', label: '包装规格' },
{ prop: 'productUnit', label: '单位' },
{ prop: 'productPlanQty', label: '计划数量' },
{ prop: 'productActualQty', label: '实际数量' },
{ prop: 'loadPlanQty', label: '装货计划量' },
{ prop: 'deliveryPlanQty', label: '送货计划量' },
{ prop: 'loadActualQty', label: '装货实际量' },
{ prop: 'deliveryActualQty', label: '送货实际量' },
{ prop: 'planAttachments', label: '计划附件' },
{ prop: 'stopContract', label: '合同' },
{ prop: 'productInboundQty', label: '入库数量', defaultVisible: false },
{ prop: 'productOutboundQty', label: '出库数量', defaultVisible: false },
{ prop: 'stopDibang', label: '地磅' }
{ prop: 'loadContract', label: '装货合同' },
{ prop: 'deliveryContract', label: '送货合同' },
{ prop: 'loadInboundQty', label: '装货入库', defaultVisible: false },
{ prop: 'deliveryInboundQty', label: '送货入库', defaultVisible: false },
{ prop: 'loadOutboundQty', label: '装货出库', defaultVisible: false },
{ prop: 'deliveryOutboundQty', label: '送货出库', defaultVisible: false },
{ prop: 'loadDibang', label: '装货地磅' },
{ prop: 'deliveryDibang', label: '送货地磅' }
]
const { visibleColumns, checkAll, isIndeterminate, isColumnVisible, resetToDefault } =
usePersistedListColumns('pl-plan-list-columns-v6', listColumnDefs)
usePersistedListColumns('pl-plan-list-columns-v7', listColumnDefs)
const {
filters,
@@ -1298,6 +1408,34 @@
resetToInitial()
}
function textFilterKeywords(value: unknown): string[] | null {
if (value == null || value === '') return null
if (typeof value === 'string') {
const text = value.trim()
return text ? [text] : null
}
if (typeof value === 'object') {
const obj = value as { value?: unknown; keyword?: unknown; selected?: string[] }
if (Array.isArray(obj.selected)) return selectedKeywordsFromFilter(obj)
const text = String(obj.value ?? obj.keyword ?? '').trim()
return text ? [text] : null
}
return null
}
function isFirstLoadProductStatRow(row: PlPlanListDisplayRow): boolean {
const productKey = String(
row.loadProduct?.planProductId || row.loadProduct?.productId || ''
).trim()
if (!row.plan?.id || !productKey) return false
const first = displayRows.value.find((item) => {
if (item.plan?.id !== row.plan.id) return false
const key = String(item.loadProduct?.planProductId || item.loadProduct?.productId || '').trim()
return key === productKey
})
return first?.rowKey === row.rowKey
}
/** 装/送客户漏斗(配对行客户端过滤)。 */
const planLoadCustomerKeywords = computed(() =>
selectedKeywordsFromFilter(filters.loadCustomer as { selected?: string[] } | undefined)
@@ -1305,11 +1443,13 @@
const planDeliveryCustomerKeywords = computed(() =>
selectedKeywordsFromFilter(filters.deliveryCustomer as { selected?: string[] } | undefined)
)
const planProductKeywords = computed(() => textFilterKeywords(filters.productName))
const displayRows = computed(() =>
flattenPlanArchivePairRows(planList.value, {
loadCustomerKeywords: planLoadCustomerKeywords.value,
deliveryCustomerKeywords: planDeliveryCustomerKeywords.value
deliveryCustomerKeywords: planDeliveryCustomerKeywords.value,
productKeywords: planProductKeywords.value
})
)
@@ -1386,10 +1526,10 @@ tableEl: planTableWrapRef,
function getDisplayStatCellValue(row: PlPlanListDisplayRow, prop: string): unknown {
const plan = row.plan
const loadProducts = getLoadProductsFromRow(row)
const deliveryProducts = getDeliveryProductsFromRow(row)
const loadStop = getPairLoadStop(row)
const deliveryStop = getPairDeliveryStop(row)
const deliveryProduct = row.deliveryProduct
const countLoadQty = isFirstLoadProductStatRow(row)
if (prop === 'planNo') return plan.planNo
if (prop === 'followStatus') return getPlPlanFollowStatusLabel(plan.followStatus)
if (prop === 'planType') return getPlanTypeLabel(plan.planType)
@@ -1406,72 +1546,44 @@ tableEl: planTableWrapRef,
if (prop === 'followUserName') return plan.followUserName
if (prop === 'loadCustomer') return getLoadCustomerNameFromRow(row)
if (prop === 'deliveryCustomer') return getDeliveryCustomerNameFromRow(row)
if (prop === 'stopAddressTime') {
const loadPart = `${formatStopAddressText(loadStop)} ${formatLoadTimeText(loadStop)}`.trim()
const deliveryPart =
`${formatStopAddressText(deliveryStop)} ${formatDeliveryTimeText(deliveryStop)}`.trim()
return `装 ${loadPart} / 送 ${deliveryPart}`
if (prop === 'loadAddressTime') {
return `${formatStopAddressText(loadStop)} ${formatLoadTimeText(loadStop)}`.trim()
}
if (prop === 'stopDibang') {
return `装 ${formatStopDibangLabel(loadStop!) || '—'} / 送 ${formatStopDibangLabel(deliveryStop!) || '—'}`
if (prop === 'deliveryAddressTime') {
return `${formatStopAddressText(deliveryStop)} ${formatDeliveryTimeText(deliveryStop)}`.trim()
}
if (prop === 'stopContract') {
const loadLines = getLoadContractLinesFromRow(row)
.map((l) => `${l.side}${l.no}`)
.join(' ')
const deliveryLines = getDeliveryContractLinesFromRow(row)
.map((l) => `${l.side}${l.no}`)
.join(' ')
return `装 ${loadLines || '—'} / 送 ${deliveryLines || '—'}`
}
if (prop === 'productPlanQty') {
if (prop === 'loadDibang') return formatStopDibangLabel(loadStop) || '—'
if (prop === 'deliveryDibang') return formatStopDibangLabel(deliveryStop) || '—'
if (prop === 'loadContract') {
return (
sumStopProductsField(loadProducts, 'planQty') +
sumStopProductsField(deliveryProducts, 'planQty')
getLoadContractLinesFromRow(row)
.map((line) => `${line.side}${line.no}`)
.join(' ') || '—'
)
}
if (prop === 'productInboundQty') {
if (prop === 'deliveryContract') {
return (
sumStopProductsField(loadProducts, 'inboundQty') +
sumStopProductsField(deliveryProducts, 'inboundQty')
getDeliveryContractLinesFromRow(row)
.map((line) => `${line.side}${line.no}`)
.join(' ') || '—'
)
}
if (prop === 'productOutboundQty') {
return (
sumStopProductsField(loadProducts, 'outboundQty') +
sumStopProductsField(deliveryProducts, 'outboundQty')
)
if (prop === 'loadPlanQty') return countLoadQty ? getRowSidePlanQty(row, 'load') : 0
if (prop === 'deliveryPlanQty') return getRowSidePlanQty(row, 'delivery')
if (prop === 'loadActualQty') {
return countLoadQty && row.loadProduct ? getStopArchiveActualQty(row.loadProduct, 'load') : 0
}
if (prop === 'productActualQty') {
return (
sumStopArchiveActualQty(loadProducts, 'load') +
sumStopArchiveActualQty(deliveryProducts, 'delivery')
)
}
if (prop === 'productName') {
return deliveryProducts
.map((p) => p.productName || '')
.filter(Boolean)
.join('\n')
}
if (prop === 'productSpec') {
return deliveryProducts
.map((p) => p.model || p.packageSpec || '')
.filter(Boolean)
.join('\n')
}
if (prop === 'productPackageSpec') {
return deliveryProducts
.map((p) => p.packageSpec || '')
.filter(Boolean)
.join('\n')
}
if (prop === 'productUnit') {
return deliveryProducts
.map((p) => p.unit || '')
.filter(Boolean)
.join('\n')
if (prop === 'deliveryActualQty') {
return deliveryProduct ? getStopArchiveActualQty(deliveryProduct, 'delivery') : 0
}
if (prop === 'loadInboundQty') return countLoadQty ? getRowSideIoQty(row, 'load', 'inbound') : 0
if (prop === 'deliveryInboundQty') return getRowSideIoQty(row, 'delivery', 'inbound')
if (prop === 'loadOutboundQty') return countLoadQty ? getRowSideIoQty(row, 'load', 'outbound') : 0
if (prop === 'deliveryOutboundQty') return getRowSideIoQty(row, 'delivery', 'outbound')
if (prop === 'productName') return deliveryProduct?.productName || '—'
if (prop === 'productSpec') return deliveryProduct?.model || deliveryProduct?.packageSpec || '—'
if (prop === 'productPackageSpec') return deliveryProduct?.packageSpec || '—'
if (prop === 'productUnit') return deliveryProduct?.unit || '—'
if (prop === 'planAttachments' || prop === 'arrangeCopy') return ''
return ''
}
@@ -1543,14 +1655,7 @@ tableEl: planTableWrapRef,
}
const planExportColumns = computed(() => {
const skip = new Set([
'arrangeCopy',
'loadCustomer',
'deliveryCustomer',
'stopAddressTime',
'stopDibang',
'stopContract'
])
const skip = new Set(['arrangeCopy'])
const cols = [...visibleColumns.value].filter((col) => !skip.has(col))
if (!cols.includes('planNo')) cols.unshift('planNo')
return cols