This commit is contained in:
zlwu committed 2026-08-31 20:53:46 +08:00
1 parent 69fa5b1fa8
commit ba21c40058
5 files changed
+221 -193

No files matched your search

+1 -1
View File
@@ -1 +1 @@
4878
80580
@@ -122,7 +122,7 @@
</PlPlanContractMultiSelect>
<div class="form-grid-2">
<el-form-item label="送货时间">
<el-form-item label="送货时间" required>
<el-date-picker
:model-value="delivery.deliveryTime"
type="date"
@@ -213,7 +213,6 @@
:editable="!readonly"
:compact="compactProducts"
:allow-remove="!readonly"
:shared-readonly="true"
:contract-id="deliveryContractId(delivery)"
:exclude-plan-id="excludePlanId"
@remove="(productIndex) => removeStopProduct(index, productIndex)"
@@ -1828,6 +1828,10 @@
ElMessage.warning('请选择装货客户')
return false
}
if (!String(loadForm.value.loadTime || '').trim()) {
ElMessage.warning('请选择装货时间')
return false
}
if (!deliveryForms.value.length) {
ElMessage.warning('请至少添加一段送货信息')
return false
@@ -1838,6 +1842,10 @@
ElMessage.warning(`送货段#${i + 1}请选择客户`)
return false
}
if (!String(row.deliveryTime || '').trim()) {
ElMessage.warning(`送货段#${i + 1}请选择送货时间`)
return false
}
if (
loadForm.value.customerId &&
row.customerId &&
@@ -70,7 +70,7 @@
</PlPlanContractMultiSelect>
<div class="form-grid-2 pl-plan-load-section__grid">
<el-form-item label="装货时间">
<el-form-item label="装货时间" required>
<el-date-picker
:model-value="modelValue.loadTime"
type="date"
@@ -20,6 +20,118 @@
:product="{ ...row, productName: row.standardProductName || row.productName }"
/>
</div>
<div class="pl-stop-product-dual-item__field">
<span class="pl-stop-product-dual-item__label">规格型号</span>
<span class="pl-stop-product-dual-item__readonly">{{ row.model || '—' }}</span>
</div>
<div class="pl-stop-product-dual-item__field">
<span class="pl-stop-product-dual-item__label"
>单位<span class="pl-stop-field-required" aria-hidden="true">*</span></span
>
<DictSelect
v-if="sharedEditable"
v-model="row.unit"
class="pl-plan-product-cell-control pl-stop-product-dual-dict"
dict-type="crm_product_unit"
placeholder="单位"
/>
<template v-else>
<span class="pl-stop-product-dual-item__readonly">{{
resolveDictLabel('crm_product_unit', row.unit)
}}</span>
</template>
</div>
<div class="pl-stop-product-dual-item__field">
<span class="pl-stop-product-dual-item__label"
>包装规格<span class="pl-stop-field-required" aria-hidden="true">*</span></span
>
<DictSelect
v-if="sharedEditable"
v-model="row.packageSpec"
class="pl-plan-product-cell-control pl-stop-product-dual-dict"
dict-type="crm_product_gg"
placeholder="包装"
/>
<span v-else class="pl-stop-product-dual-item__readonly">{{
resolveDictLabel('crm_product_gg', row.packageSpec)
}}</span>
</div>
<div class="pl-stop-product-dual-item__field">
<span class="pl-stop-product-dual-item__label"
>送货方式<span class="pl-stop-field-required" aria-hidden="true">*</span></span
>
<DictSelect
v-if="sharedEditable"
v-model="row.deliveryMethod"
class="pl-plan-product-cell-control pl-stop-product-dual-dict"
dict-type="crm_shfs"
placeholder="送货"
/>
<span v-else class="pl-stop-product-dual-item__readonly">{{
row.deliveryMethod ? getDeliveryMethodDisplay(row.deliveryMethod) : '—'
}}</span>
</div>
</div>
<div class="pl-stop-product-dual-item__row2">
<!-- 可安排数量仅运输计划新增/编辑提醒用,详情弹窗不展示 -->
<div v-if="showAvailableQty" class="pl-stop-product-dual-item__field">
<span class="pl-stop-product-dual-item__label">可安排数量</span>
<div class="pl-stop-avail-wrap">
<span class="pl-stop-avail-num">{{ formatAvailableDisplay(row) }}</span>
<el-button
class="pl-stop-avail-help"
:disabled="!canOpenArrangeQty(row)"
circle
size="small"
@click="openArrangeQtyDialog(row)"
>
?
</el-button>
</div>
</div>
<div class="pl-stop-product-dual-item__field">
<span class="pl-stop-product-dual-item__label"
>计划数量<span class="pl-stop-field-required" aria-hidden="true">*</span></span
>
<el-input-number
v-if="editable"
v-model="row.planQty"
:min="0"
:precision="3"
:controls="false"
class="pl-plan-product-cell-control pl-stop-product-dual-qty"
:class="{ 'is-over-available': isPlanQtyOverAvailable(row) }"
/>
<span
v-else
class="pl-stop-product-dual-item__readonly pl-stop-product-dual-item__qty-text pl-stop-plan-qty"
>
{{ row.planQty ?? '—' }}
</span>
<p v-if="editable && isPlanQtyOverAvailable(row)" class="pl-stop-over-tip">
已超过可安排数量,安排时将被拦截
</p>
</div>
<div v-if="showStopActualQty" class="pl-stop-product-dual-item__field">
<span class="pl-stop-product-dual-item__label">实际数量</span>
<span
class="pl-stop-product-dual-item__readonly pl-stop-product-dual-item__qty-text"
>
{{ formatStopQty(resolveStopActualQty(row, stopArchiveMode)) }}
</span>
</div>
<div class="pl-stop-product-dual-item__field pl-stop-product-dual-item__field--remark">
<span class="pl-stop-product-dual-item__label">备注</span>
<el-input
v-if="editable"
v-model="row.remark"
class="pl-plan-product-cell-control"
placeholder="备注"
/>
<span v-else class="pl-stop-product-dual-item__readonly">{{
row.remark || '—'
}}</span>
</div>
<div class="pl-stop-product-dual-item__field">
<span class="pl-stop-product-dual-item__label">别名</span>
<div v-if="sharedEditable" class="line-meta-cell">
@@ -51,6 +163,20 @@
<el-icon><Plus /></el-icon>
</el-button>
</div>
<div v-else-if="aliasBrandManageable" class="line-meta-cell">
<span class="pl-stop-product-dual-item__readonly line-meta-cell__control">{{
row.aliasName || '—'
}}</span>
<el-button
v-if="row.productId"
link
type="primary"
class="line-meta-cell__btn"
@click="openAliasBrandManage(row, 'alias')"
>
<el-icon><Plus /></el-icon>
</el-button>
</div>
<span v-else class="pl-stop-product-dual-item__readonly">{{
row.aliasName || '—'
}}</span>
@@ -86,130 +212,22 @@
<el-icon><Plus /></el-icon>
</el-button>
</div>
<span v-else class="pl-stop-product-dual-item__readonly">
{{ formatBrandConstraintLabel(row.brandMode, row.brandName) }}
</span>
</div>
<div class="pl-stop-product-dual-item__field">
<span class="pl-stop-product-dual-item__label">规格型号</span>
<span class="pl-stop-product-dual-item__readonly">{{ row.model || '—' }}</span>
</div>
<div class="pl-stop-product-dual-item__field">
<span class="pl-stop-product-dual-item__label"
>单位<span class="pl-stop-field-required" aria-hidden="true">*</span></span
>
<DictSelect
v-if="sharedEditable"
v-model="row.unit"
class="pl-plan-product-cell-control pl-stop-product-dual-dict"
dict-type="crm_product_unit"
placeholder="单位"
/>
<template v-else>
<span class="pl-stop-product-dual-item__readonly">{{
resolveDictLabel('crm_product_unit', row.unit)
}}</span>
</template>
</div>
</div>
<div class="pl-stop-product-dual-item__row2">
<!-- 可安排数量仅运输计划新增/编辑提醒用,详情弹窗不展示 -->
<div v-if="showAvailableQty" class="pl-stop-product-dual-item__field">
<span class="pl-stop-product-dual-item__label">可安排数量</span>
<div class="pl-stop-avail-wrap">
<span class="pl-stop-avail-num">{{ formatAvailableDisplay(row) }}</span>
<div v-else-if="aliasBrandManageable" class="line-meta-cell">
<span class="pl-stop-product-dual-item__readonly line-meta-cell__control">
{{ formatBrandConstraintLabel(row.brandMode, row.brandName) }}
</span>
<el-button
class="pl-stop-avail-help"
:disabled="!canOpenArrangeQty(row)"
circle
size="small"
@click="openArrangeQtyDialog(row)"
v-if="row.productId"
link
type="primary"
class="line-meta-cell__btn"
@click="openAliasBrandManage(row, 'brand')"
>
?
<el-icon><Plus /></el-icon>
</el-button>
</div>
</div>
<div class="pl-stop-product-dual-item__field">
<span class="pl-stop-product-dual-item__label"
>计划数量<span class="pl-stop-field-required" aria-hidden="true">*</span></span
>
<el-input-number
v-if="editable"
v-model="row.planQty"
:min="0"
:precision="3"
:controls="false"
class="pl-plan-product-cell-control pl-stop-product-dual-qty"
:class="{ 'is-over-available': isPlanQtyOverAvailable(row) }"
/>
<span
v-else
class="pl-stop-product-dual-item__readonly pl-stop-product-dual-item__qty-text pl-stop-plan-qty"
>
{{ row.planQty ?? '—' }}
</span>
<p v-if="editable && isPlanQtyOverAvailable(row)" class="pl-stop-over-tip">
已超过可安排数量,安排时将被拦截
</p>
</div>
<div class="pl-stop-product-dual-item__field">
<span class="pl-stop-product-dual-item__label"
>包装规格<span class="pl-stop-field-required" aria-hidden="true">*</span></span
>
<DictSelect
v-if="sharedEditable"
v-model="row.packageSpec"
class="pl-plan-product-cell-control pl-stop-product-dual-dict"
dict-type="crm_product_gg"
placeholder="包装"
/>
<span v-else class="pl-stop-product-dual-item__readonly">{{
resolveDictLabel('crm_product_gg', row.packageSpec)
}}</span>
</div>
<div class="pl-stop-product-dual-item__field">
<span class="pl-stop-product-dual-item__label"
>送货方式<span class="pl-stop-field-required" aria-hidden="true">*</span></span
>
<DictSelect
v-if="sharedEditable"
v-model="row.deliveryMethod"
class="pl-plan-product-cell-control pl-stop-product-dual-dict"
dict-type="crm_shfs"
placeholder="送货"
/>
<span v-else class="pl-stop-product-dual-item__readonly">{{
row.deliveryMethod ? getDeliveryMethodDisplay(row.deliveryMethod) : '—'
}}</span>
</div>
<div class="pl-stop-product-dual-item__field pl-stop-product-dual-item__field--remark">
<span class="pl-stop-product-dual-item__label">备注</span>
<el-input
v-if="editable"
v-model="row.remark"
class="pl-plan-product-cell-control"
placeholder="备注"
/>
<span v-else class="pl-stop-product-dual-item__readonly">{{
row.remark || '—'
}}</span>
</div>
<div v-if="canRemove" class="pl-stop-product-dual-item__act">
<el-button link type="danger" @click="handleRemove(index)">
<el-icon><Delete /></el-icon>
</el-button>
</div>
</div>
<div
v-if="showStopActualQty || showStopArchivePrice"
class="pl-stop-product-dual-item__row3"
>
<div v-if="showStopActualQty" class="pl-stop-product-dual-item__field">
<span class="pl-stop-product-dual-item__label">实际数量</span>
<span
class="pl-stop-product-dual-item__readonly pl-stop-product-dual-item__qty-text"
>
{{ formatStopQty(resolveStopActualQty(row, stopArchiveMode)) }}
<span v-else class="pl-stop-product-dual-item__readonly">
{{ formatBrandConstraintLabel(row.brandMode, row.brandName) }}
</span>
</div>
<div v-if="showStopArchivePrice" class="pl-stop-product-dual-item__field">
@@ -224,6 +242,11 @@
{{ formatStopArchiveAmount(row, stopArchiveMode) }}
</span>
</div>
<div v-if="canRemove" class="pl-stop-product-dual-item__act">
<el-button link type="danger" @click="handleRemove(index)">
<el-icon><Delete /></el-icon>
</el-button>
</div>
</div>
</article>
</div>
@@ -243,16 +266,6 @@
/>
</template>
</el-table-column>
<el-table-column label="别名" min-width="140" align="center" show-overflow-tooltip>
<template #default="{ row }">
{{ row.aliasName || '—' }}
</template>
</el-table-column>
<el-table-column label="品牌" min-width="140" align="center" show-overflow-tooltip>
<template #default="{ row }">
{{ formatBrandConstraintLabel(row.brandMode, row.brandName) }}
</template>
</el-table-column>
<el-table-column label="型号/规格" min-width="120" show-overflow-tooltip>
<template #default="{ row }">
{{ row.model || '—' }}
@@ -277,6 +290,46 @@
</template>
</template>
</el-table-column>
<el-table-column prop="packageSpec" min-width="100" align="center">
<template #header>
<span>包装规格<span class="pl-stop-field-required" aria-hidden="true">*</span></span>
</template>
<template #default="{ row }">
<DictSelect
v-if="sharedEditable"
v-model="row.packageSpec"
class="pl-plan-product-cell-control"
dict-type="crm_product_gg"
placeholder="请选择包装规格"
style="width: 100%"
/>
<template v-else>
<DictTag v-if="row.packageSpec" dict-type="crm_product_gg" :value="row.packageSpec" />
<span v-else>—</span>
</template>
</template>
</el-table-column>
<el-table-column min-width="100" align="center">
<template #header>
<span>送货方式<span class="pl-stop-field-required" aria-hidden="true">*</span></span>
</template>
<template #default="{ row }">
<DictSelect
v-if="sharedEditable"
v-model="row.deliveryMethod"
class="pl-plan-product-cell-control"
dict-type="crm_shfs"
placeholder="请选择送货方式"
style="width: 100%"
/>
<template v-else>
<el-tag v-if="row.deliveryMethod" size="small" type="info">
{{ getDeliveryMethodDisplay(row.deliveryMethod) }}
</el-tag>
<span v-else>—</span>
</template>
</template>
</el-table-column>
<el-table-column
v-if="showAvailableQty"
label="可安排数量"
@@ -328,6 +381,26 @@
{{ formatStopQty(resolveStopActualQty(row, stopArchiveMode)) }}
</template>
</el-table-column>
<el-table-column v-if="editable" label="备注" min-width="140">
<template #default="{ row }">
<el-input
v-model="row.remark"
class="pl-plan-product-cell-control"
placeholder="备注"
/>
</template>
</el-table-column>
<el-table-column v-else prop="remark" label="备注" min-width="120" show-overflow-tooltip />
<el-table-column label="别名" min-width="140" align="center" show-overflow-tooltip>
<template #default="{ row }">
{{ row.aliasName || '—' }}
</template>
</el-table-column>
<el-table-column label="品牌" min-width="140" align="center" show-overflow-tooltip>
<template #default="{ row }">
{{ formatBrandConstraintLabel(row.brandMode, row.brandName) }}
</template>
</el-table-column>
<el-table-column
v-if="showStopArchivePrice"
label="单价(元)"
@@ -348,56 +421,6 @@
{{ formatStopArchiveAmount(row, stopArchiveMode) }}
</template>
</el-table-column>
<el-table-column prop="packageSpec" min-width="100" align="center">
<template #header>
<span>包装规格<span class="pl-stop-field-required" aria-hidden="true">*</span></span>
</template>
<template #default="{ row }">
<DictSelect
v-if="sharedEditable"
v-model="row.packageSpec"
class="pl-plan-product-cell-control"
dict-type="crm_product_gg"
placeholder="请选择包装规格"
style="width: 100%"
/>
<template v-else>
<DictTag v-if="row.packageSpec" dict-type="crm_product_gg" :value="row.packageSpec" />
<span v-else>—</span>
</template>
</template>
</el-table-column>
<el-table-column min-width="100" align="center">
<template #header>
<span>送货方式<span class="pl-stop-field-required" aria-hidden="true">*</span></span>
</template>
<template #default="{ row }">
<DictSelect
v-if="sharedEditable"
v-model="row.deliveryMethod"
class="pl-plan-product-cell-control"
dict-type="crm_shfs"
placeholder="请选择送货方式"
style="width: 100%"
/>
<template v-else>
<el-tag v-if="row.deliveryMethod" size="small" type="info">
{{ getDeliveryMethodDisplay(row.deliveryMethod) }}
</el-tag>
<span v-else>—</span>
</template>
</template>
</el-table-column>
<el-table-column v-if="editable" label="备注" min-width="140">
<template #default="{ row }">
<el-input
v-model="row.remark"
class="pl-plan-product-cell-control"
placeholder="备注"
/>
</template>
</el-table-column>
<el-table-column v-else prop="remark" label="备注" min-width="120" show-overflow-tooltip />
<el-table-column
v-if="canRemove"
label="操作"
@@ -467,7 +490,7 @@
compact?: boolean
/** 是否允许删除行,默认跟 editable */
allowRemove?: boolean
/** 共享字段只读(送货段:别名/品牌/单位/包装/送货方式跟装货) */
/** 共享字段只读(别名/品牌/单位/包装/送货方式不可编辑;送货段已放开,默认 false) */
sharedReadonly?: boolean
/** 查看态:展示段级实际数量(装货 inbound / 送货 outbound) */
showStopActualQty?: boolean
@@ -497,6 +520,8 @@
}>()
const sharedEditable = computed(() => !!props.editable && !props.sharedReadonly)
/** 共享只读但仍可维护别名/品牌主数据(加号);送货段已放开编辑时走 sharedEditable */
const aliasBrandManageable = computed(() => !!props.editable && props.sharedReadonly)
const canRemove = computed(
() => !!props.editable && (props.allowRemove === undefined ? true : props.allowRemove)
)
@@ -755,10 +780,9 @@
}
.pl-stop-product-dual-item__row1,
.pl-stop-product-dual-item__row2,
.pl-stop-product-dual-item__row3 {
.pl-stop-product-dual-item__row2 {
display: grid;
/* 上下共用同一套 5 列轨道,保证第一列左右对齐;下行仅 4 个字段占前 4 列 */
/* 5 列轨道;字段超出时自动换行到下一行 */
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 8px 12px;
padding: 10px 12px;
@@ -770,11 +794,7 @@
border-bottom: 1px solid #eef2f0;
}
.pl-stop-product-dual-item__row3 {
border-top: 1px solid #eef2f0;
}
/* 可编辑:末列留给删除按钮(上下同行宽,删除键只在第二行) */
/* 可编辑:末列留给删除按钮(固定首行右侧) */
.pl-plan-stop-product-table-wrap.is-compact.is-editable {
.pl-stop-product-dual-item__row1,
.pl-stop-product-dual-item__row2 {
@@ -783,6 +803,7 @@
.pl-stop-product-dual-item__act {
grid-column: 6;
grid-row: 1;
}
}