@@ -49,6 +49,13 @@ const inboundPlanWarehouseLabel = computed(() => [actionTarget.value?.overseasWa
. filter ( ( value ) => value && value !== '—' )
. join ( ' · ' ) )
const inboundPlanMinimumPalletCount = ref ( 1 )
const inboundPlanSourcePalletCount = ref < number | null > ( null )
const inboundPlanPreviewLoading = ref ( false )
const inboundPlanPreviewError = ref ( '' )
const inboundPlanIsDropship = computed ( ( ) => actionTarget . value ? . orderTypeCode === 'ONE_CLICK_DROPSHIP' )
const inboundPlanSubmitDisabled = computed ( ( ) => inboundPlanIsDropship . value
&& ( inboundPlanPreviewLoading . value || inboundPlanSourcePalletCount . value == null
|| inboundPlanSourcePalletCount . value <= 0 ) )
const inboundPlanForm = reactive ( { plannedArrivalDate : '' , inboundCode : '' , palletCount : 1 , palletType : 'WOOD' as 'STANDARD' | 'WOOD' | 'PAPER' , assigned : false , mergeRequired : false , urgent : false , remark : '' } )
const holdOpen = ref ( false )
const holdForm = reactive ( { held : true , chargeable : true , reason : '' } )
@@ -481,6 +488,8 @@ function openInboundPlan(source?: object) {
const volumeMinimum = typeof target . volume === 'number' && target . volume > 0 ? Math . ceil ( target . volume / 2 ) : 1
inboundPlanMinimumPalletCount . value = Math . max ( 1 , volumeMinimum ,
typeof target . pallets === 'number' ? target . pallets : 0 )
inboundPlanSourcePalletCount . value = null
inboundPlanPreviewError . value = ''
Object . assign ( inboundPlanForm , { plannedArrivalDate : '' , inboundCode : target . inboundCode === '—' ? '' : target . inboundCode , palletCount : inboundPlanMinimumPalletCount . value , palletType : 'WOOD' , assigned : false , mergeRequired : false , urgent : false , remark : '' } )
actionError . value = null
inboundPlanOpen . value = true
@@ -488,20 +497,39 @@ function openInboundPlan(source?: object) {
}
async function refreshManualPalletMinimum ( target : LastMileOrderListItem ) {
if ( ! target . sourceContainerBookingId ) return
inboundPlanPreviewLoading . value = true
inboundPlanPreviewError . value = ''
try {
const preview = await getContainerInboundPlanPreview ( target . sourceContainerBookingId )
if ( actionTarget . value ? . id !== target . id || ! inboundPlanOpen . value ) return
const order = preview . groups . flatMap ( ( group ) => group . orders ) . find ( ( item ) => item . orderId === target . id )
if ( target . orderTypeCode === 'ONE_CLICK_DROPSHIP' ) {
const sourcePalletCount = Number ( order ? . sourcePalletCount || 0 )
if ( sourcePalletCount <= 0 ) throw new Error ( '一件代发订单缺少头程封托托盘快照' )
inboundPlanSourcePalletCount . value = sourcePalletCount
inboundPlanMinimumPalletCount . value = sourcePalletCount
inboundPlanForm . palletCount = sourcePalletCount
return
}
const suggested = Number ( order ? . suggestedPalletCount || 0 )
inboundPlanMinimumPalletCount . value = Math . max ( inboundPlanMinimumPalletCount . value , suggested )
inboundPlanForm . palletCount = Math . max ( inboundPlanForm . palletCount , inboundPlanMinimumPalletCount . value )
} catch {
// The backend repeats the same calculation and remains authoritative on submit.
} catch ( error ) {
if ( actionTarget . value ? . id === target . id && inboundPlanOpen . value ) {
inboundPlanPreviewError . value = getApiErrorDetails ( error , '柜级托盘预览加载失败,请重试' ) . message
}
} finally {
if ( actionTarget . value ? . id === target . id ) inboundPlanPreviewLoading . value = false
}
}
async function submitInboundPlan ( ) {
const row = actionTarget . value
const frozenInboundCode = String ( row ? . inboundCode || '' ) . trim ( )
if ( inboundPlanIsDropship . value && ( inboundPlanSourcePalletCount . value == null
|| inboundPlanForm . palletCount !== inboundPlanSourcePalletCount . value ) ) {
ElMessage . warning ( '一件代发必须按头程已封托托盘数整体生成入库计划' )
return
}
if ( ! row || ! row . overseasWarehouseId || ! inboundPlanForm . plannedArrivalDate || ! frozenInboundCode || frozenInboundCode === '—' || inboundPlanForm . palletCount < inboundPlanMinimumPalletCount . value ) {
ElMessage . warning ( '当前柜须已分配接柜仓,并完整填写预计到仓日期和托数' )
return
@@ -719,6 +747,7 @@ function blockerText(blockers: string[]) {
PLAN _ALREADY _STARTED : '已有计划已开始作业,不能再补生成托盘' ,
PLAN _PALLET _BINDING _INCOMPLETE : '已有计划只绑定了部分真实托盘,请联系管理员处理' ,
DROPSHIP _PALLET _SNAPSHOT _MISSING : '缺少一件代发托盘快照' ,
DROPSHIP _PALLET _COUNT _MISMATCH : '一件代发计划托数与头程已封托托盘数不一致,请重新走单' ,
PALLET _COUNT _NOT _READY : '计划托数暂未就绪' ,
}
return [ ... new Set ( blockers . map ( ( code ) => labels [ code ] || '计划数据暂未就绪,请刷新或联系管理员' ) ) ] . join ( '; ' ) || '—'
@@ -926,10 +955,11 @@ onMounted(() => { void load() })
< / LoadErrorState >
< / el-form >
< / AppDialog >
< AppDialog v-model = "inboundPlanOpen" title="入库计划" width="min(720px, calc(100vw - 28px))" confirm-text="保存并生成入库单" :confirm-loading="actionLoading " @confirm="submitInboundPlan" >
< AppDialog v-model = "inboundPlanOpen" title="入库计划" width="min(720px, calc(100vw - 28px))" confirm-text="保存并生成入库单" :loading="inboundPlanPreviewLoading" :confirm-loading="actionLoading" :confirm-disabled="inboundPlanSubmitDisabled " @confirm="submitInboundPlan" >
< el -alert title = "保存后生成入库单;清关完成后入库计划不可更改。" type = "info" :closable = "false" show -icon / >
< el-alert v-if = "inboundPlanPreviewError" class="container-convert-alert" :title="inboundPlanPreviewError" type="error" :closable="false" show -icon / >
< el-form class = "command-form" label -position = " top " >
< div class = "command-form__grid" > < el-form-item label = "计划入库仓" > < el-input :model-value = "inboundPlanWarehouseLabel" disabled placeholder = "当前柜接柜仓" / > < small > 接柜仓按柜统一 ; 如需变更 , 请在柜子工作台执行 “ 变更接柜仓 ” 。 < / small > < / el-form-item > < el-form-item label = "预计到仓日期" required > < el-date-picker v-model = "inboundPlanForm.plannedArrivalDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择日期" /></el-form-item><el-form-item label="入仓码"><el-input :model-value="inboundPlanForm.inboundCode" disabled placeholder="订单冻结入仓码" /></el-form-item><el-form-item label="托数" required><el-input-number v-model="inboundPlanForm.palletCount" :min="inboundPlanMinimumPalletCount" :max="999" controls-position="right" placeholder="请输入托数" /><small >按柜级预览至少 {{ inboundPlanMinimumPalletCount }} 托,可增加,不能减少。</small></el-form-item><el-form-item label="托盘类型"><el-select v-model="inboundPlanForm.palletType" placeholder="请选择托盘类型"><el-option label="标准托盘" value="STANDARD" /><el-option label="木托盘" value="WOOD" /><el-option label="纸托盘" value="PAPER" /></el-select></el-form-item><el-form-item label="是否分配"><el-switch v-model="inboundPlanForm.assigned" inline-prompt active-text="是" inactive-text="否" /></el-form-item><el-form-item label="允许合托"><el-switch v-model="inboundPlanForm.mergeRequired" inline-prompt active-text="是" inactive-text="否" /><small>仅作为现场可选提示,不强制工人合托。</small></el-form-item><el-form-item label="是否加急"><el-switch v-model="inboundPlanForm.urgent" inline-prompt active-text="是" inactive-text="否" / > < / el-form-item > < / div >
< div class = "command-form__grid" > < el-form-item label = "计划入库仓" > < el-input :model-value = "inboundPlanWarehouseLabel" disabled placeholder = "当前柜接柜仓" / > < small > 接柜仓按柜统一 ; 如需变更 , 请在柜子工作台执行 “ 变更接柜仓 ” 。 < / small > < / el-form-item > < el-form-item label = "预计到仓日期" required > < el-date-picker v-model = "inboundPlanForm.plannedArrivalDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择日期" /></el-form-item><el-form-item label="入仓码"><el-input :model-value="inboundPlanForm.inboundCode" disabled placeholder="订单冻结入仓码" /></el-form-item><el-form-item label="托数" required><el-input-number v-model="inboundPlanForm.palletCount" :min="inboundPlanMinimumPalletCount" :max="inboundPlanIsDropship ? inboundPlanMinimumPalletCount : 999" :disabled="inboundPlanIsDropship" controls-position="right" placeholder="请输入托数" /><small v-if="inboundPlanIsDropship">一件代发固定按头程已封托托盘数整体入库,不可增减。</small><small v-else >按柜级预览至少 {{ inboundPlanMinimumPalletCount }} 托,可增加,不能减少。</small></el-form-item><el-form-item label="托盘类型"><el-select v-model="inboundPlanForm.palletType" placeholder="请选择托盘类型"><el-option label="标准托盘" value="STANDARD" /><el-option label="木托盘" value="WOOD" /><el-option label="纸托盘" value="PAPER" /></el-select></el-form-item><el-form-item label="是否分配"><el-switch v-model="inboundPlanForm.assigned" inline-prompt active-text="是" inactive-text="否" /></el-form-item><el-form-item label="允许合托"><el-switch v-model="inboundPlanForm.mergeRequired" inline-prompt active-text="是" inactive-text="否" /><small>仅作为现场可选提示,不强制工人合托。</small></el-form-item><el-form-item label="是否加急"><el-switch v-model="inboundPlanForm.urgent" inline-prompt active-text="是" inactive-text="否" / > < / el-form-item > < / div >
< el-form-item label = "备注" > < el-input v-model = "inboundPlanForm.remark" type="textarea" :rows="3" maxlength="1000" show-word-limit placeholder="请输入备注(选填)" / > < / el-form-item >
< / el-form >
< / AppDialog >