@@ -394,16 +394,16 @@ async function openLink(source: object) {
async function saveLink ( ) {
const target = linkTarget . value
const candidate = linkCandidates . value . find ( ( item ) => item . key === linkCandidateKey . value )
const containerNo = normalizedContainerNo ( linkContainerNo . value )
linkContainerNo . value = containerNo
const normalized = normalizedContainerNo ( linkContainerNo . value )
const containerNo = normalized || null
linkContainerNo . value = normalized
if ( ! target || ! candidate || candidate . disabled ) { linkError . value = { message : '请选择规格一致且仍有剩余数量的柜型需求' } ; return }
if ( ! containerNo ) { linkError . value = { message : '请输入港口取得的真实集装箱号' } ; return }
linkSaving . value = true
linkError . value = null
try {
await linkContainerOceanBooking ( target . id , { oceanBookingId : candidate . oceanBookingId , requirementId : candidate . requirementId , containerNo , expectedVersion : target . version } )
linkOpen . value = false
ElMessage . success ( '订舱与真实集装箱号已关联,柜次号、装柜计划和明细保持不变' )
ElMessage . success ( containerNo ? '订舱与真实集装箱号已关联,柜次号、装柜计划和明细保持不变' : '订舱已关联,真实集装箱号待司机提柜时录入' )
await loadRows ( )
} catch ( error ) { linkError . value = getApiErrorDetails ( error , '关联真实订舱柜失败' ) }
finally { linkSaving . value = false }
@@ -538,8 +538,8 @@ onMounted(async () => {
< template # toolbar -actions > < el-button v-if = "canCreate" type="primary" @click="openSimulation"><Plus :size="16" / > 新增模拟柜 < / el-button > < / template >
< template # actions = "{ row }" > < div class = "row-actions" > < el-button v-if = "canView" size="small" type="primary" plain :aria-label="containerViewLabel(row as ContainerBookingResource)" @click="openDetail(row)"><Eye :size="15" />查看</el-button><el-button v-if="canView && canEdit && ['WAIT_LOADING', 'EDITING'].includes(row.planStatus)" size="small" @click="openEdit(row)"><Pencil :size="15" />编辑运输信息</el-button><el-button v-if="row.planStatus === 'EDITING' && canSmartLoading" size="small" type="primary" @click="goContainerProcess(row)"><Sparkles :size="15" />排柜</el-button><el-button v-else-if="row.planStatus === 'LOADING' && canViewPreload" size="small" type="primary" plain @click="goContainerProcess(row)"><ListChecks :size="15" />预装清单</el-button><el-button v-else-if="row.planStatus === 'COMPLETED' && canViewLoadingList" size="small" type="primary" plain @click="goContainerProcess(row)"><ListChecks :size="15" />装柜清单</el-button><el-dropdown v-if="canView" trigger="click" @command="handleMoreAction($event, row)"><el-button size="small" plain aria-label="更多货柜操作" title="更多货柜操作"><MoreHorizontal :size="16" /></el-button><template #dropdown><el-dropdown-menu><el-dropdown-item command="overview"><FileText :size="15" />轨迹与日志</el-dropdown-item><el-dropdown-item command="documents"><FileUp :size="15" />DO / PU 资料</el-dropdown-item><el-dropdown-item v-if="canEdit && row.sourceType === 'SIMULATION' && !row.oceanBookingId && ['WAIT_LOADING','EDITING'].includes(row.planStatus)" command="link"><Link2 :size="15" />关联已放舱订舱</el-dropdown-item><el-dropdown-item v-if="canEdit" command="exception" divided><TriangleAlert :size="15" / > 异常处理 < / el-dropdown-item > < / el-dropdown-menu > < / template > < / el-dropdown > < / div > < / template >
< template # cabinet = "{ row }" > < strong > { { row . cabinetNo } } < / strong > < small > 系统生成 < / small > < / template >
< template # oceanBooking = "{ row }" > < span v-if = "row.oceanBookingId" > < strong > { { row . billOfLadingNo || '已关联订舱' } } < / strong > < small > { { row . containerNo ? ` 集装箱号: ${ row . containerNo } ` : '集装箱号待录入 ' } } < / small > < / span > < span v-else > 未关联 < / span > < / template >
< template # containerNo = "{ row }" > { { row . containerNo || '— ' } } < / template >
< template # oceanBooking = "{ row }" > < span v-if = "row.oceanBookingId" > < strong > { { row . billOfLadingNo || '已关联订舱' } } < / strong > < small > { { row . containerNo ? ` 集装箱号: ${ row . containerNo } ` : '集装箱号:待补录 ' } } < / small > < / span > < span v-else > 未关联 < / span > < / template >
< template # containerNo = "{ row }" > { { row . containerNo || '待补录 ' } } < / template >
< template # status = "{ row }" > < el-tag : type = "row.planStatus === 'COMPLETED' ? 'success' : row.planStatus === 'LOADING' ? 'warning' : 'info'" > { { statusText ( row . planStatus ) } } < / el-tag > < / template >
< template # route = "{ row }" > { { row . departurePortName || row . departurePortCode || '—' } } → { { row . destinationPortName || row . destinationPortCode || '—' } } < / template >
< template # capacity = "{ row }" > { { row . capacityCbm ? ` ${ row . capacityCbm } m³ ` : '—' } } / { { row . maxWeightKg ? ` ${ row . maxWeightKg } kg ` : '—' } } < / template >
@@ -564,9 +564,9 @@ onMounted(async () => {
< LoadErrorState v-if = "simulationError" compact title="模拟柜创建失败" :message="simulationError.message" :show-retry="false"><template #details><ApiErrorMeta :details="simulationError" / > < / template > < / LoadErrorState >
< / AppDialog >
< AppDialog v-model = "linkOpen" title="关联已放舱订舱" width="min(680px, calc(100vw - 28px))" confirm-text="确认关联" cancel-text="取消" :confirm-loading="linkSaving" :confirm-disabled="!linkCandidateKey || !linkContainerNo.trim() " @confirm="saveLink" >
< el -alert title = "关联后保留当前柜次号、装柜计划和明细,仅 补充订舱快照及港口取得的真实集装箱号 。" type = "info" :closable = "false" / >
< el-form label -position = " top " > < el-form-item label = "柜次号" > < el-input : model -value = " linkTarget ? .cabinetNo | | ' ' " disabled / > < / el-form-item > < el-form-item label = "已放舱订舱及柜型需求" required > < el-select v-model = "linkCandidateKey" filterable :loading="linkLoading" placeholder="请选择仍有剩余数量的柜型需求" no-data-text="暂无符合当前装柜仓库、物流渠道和柜型的已放舱需求"><el-option v-for="candidate in linkCandidates" :key="candidate.key" :label="candidate.label" :value="candidate.key" :disabled="candidate.disabled" /></el-select></el-form-item><el-form-item label="真实集装箱号" required ><el-input v-model="linkContainerNo" maxlength="64" placeholder="请输入港口取得的真实集装箱号 " @blur="linkContainerNo = normalizedContainerNo(linkContainerNo)" / > < / el-form-item > < / el-form >
< AppDialog v-model = "linkOpen" title="关联已放舱订舱" width="min(680px, calc(100vw - 28px))" confirm-text="确认关联" cancel-text="取消" :confirm-loading="linkSaving" :confirm-disabled="!linkCandidateKey" @confirm="saveLink" >
< el -alert title = "关联后保留当前柜次号、装柜计划和明细,并 补充订舱快照;真实集装箱号可待司机提柜时录入 。" type = "info" :closable = "false" / >
< el-form label -position = " top " > < el-form-item label = "柜次号" > < el-input : model -value = " linkTarget ? .cabinetNo | | ' ' " disabled / > < / el-form-item > < el-form-item label = "已放舱订舱及柜型需求" required > < el-select v-model = "linkCandidateKey" filterable :loading="linkLoading" placeholder="请选择仍有剩余数量的柜型需求" no-data-text="暂无符合当前装柜仓库、物流渠道和柜型的已放舱需求"><el-option v-for="candidate in linkCandidates" :key="candidate.key" :label="candidate.label" :value="candidate.key" :disabled="candidate.disabled" /></el-select></el-form-item><el-form-item label="真实集装箱号(可选) "><el-input v-model="linkContainerNo" maxlength="64" placeholder="如未取得可留空,后续显示待补录 " @blur="linkContainerNo = normalizedContainerNo(linkContainerNo)" / > < / el-form-item > < / el-form >
< p class = "dialog-hint" > 仓库 、 渠道和柜型必须与当前模拟柜一致 ; 容量和限重仅展示柜型固定快照 , 不影响关联资格 。 < / p >
< LoadErrorState v-if = "linkError" compact title="关联失败" :message="linkError.message" :show-retry="false"><template #details><ApiErrorMeta :details="linkError" / > < / template > < / LoadErrorState >
< / AppDialog >
@@ -576,7 +576,7 @@ onMounted(async () => {
< LoadErrorState v-if = "warehouseError" title="装柜仓库加载失败" :message="warehouseError.message" compact @retry="loadWarehouses"><template #details><ApiErrorMeta :details="warehouseError" / > < / template > < / LoadErrorState >
< el-alert v-if = "businessOptionsError" type="warning" :closable="false" show-icon :title="businessOptionsError"><el-button link type="warning" :loading="businessOptionsLoading" @click="loadBusinessOptions" > 重新加载业务选项 < / el -button > < / el-alert >
< el-form v-if = "!warehouseError" ref="formRef" :model="form" :rules="rules" label-position="top" class="form-grid" >
< el -form -item label = "真实集装箱号" > < el-input : model -value = " form.containerNo | | ' 待录入 ' " disabled / > < small > 初次录入请使用 “ 关联已放舱订舱 ” ; 后续纠错请进入预装清单 。 < / small > < / el-form-item >
< el -form -item label = "真实集装箱号" > < el-input : model -value = " form.containerNo | | ' 待补 录 ' " disabled / > < small > 货柜基础资料不提供柜号写入 ; 请在拖车提柜或预装详情中补录 , 后续纠错也在预装详情完成 。 < / small > < / el-form-item >
< el-form-item label = "柜次号" > < el-input :model-value = "form.cabinetNo" disabled placeholder = "系统生成" / > < / el-form-item >
< el-form-item label = "装柜仓库" prop = "warehouseId" > < el-select v-model = "form.warehouseId" filterable placeholder="请选择装柜仓库" :loading="warehouseLoading" loading-text="仓库加载中" no-data-text="暂无可用国内仓库"><el-option v-for="warehouse in warehouses" :key="String(warehouse.id)" :label="`${warehouse.warehouseName}( ${warehouse.warehouseCode}) `" :value="String(warehouse.id)" / > < / el-select > < / el-form-item >
< el-form-item label = "提单号" > < el-input v-model = "form.billOfLadingNo" maxlength="128" placeholder="请输入提单号(可选)" / > < / el-form-item >