统一履约状态和节点日志展示并整理装柜与尾程界面

This commit is contained in:
2026-10-28 16:56:32 +08:00
parent a52a6620fc
commit d990637b38
20 changed files with 270 additions and 43 deletions
@@ -95,6 +95,12 @@ function nodeText(value?: CarrierScheduleStandardNode | null) { return ({ DEPART
function nodeLocation(item?: CarrierScheduleNodeFact | null) { return item ? [item.locationCode, item.locationName].filter(Boolean).join(' · ') || '—' : '—' }
function exceptionLocation(item: CarrierScheduleExceptionFact) { return [item.locationCode, item.locationName].filter(Boolean).join(' · ') || '—' }
function nodeTimeText(item: CarrierScheduleNodeFact) { return item.actualAt ? `实际 ${formatTime(item.actualAt)}` : item.estimatedAt ? `预计 ${formatTime(item.estimatedAt)}` : `观测 ${formatTime(item.observedAt)}` }
function nodeSourceText(item: CarrierScheduleNodeFact) {
const operatorName = nonBlank(item.operatorName)
if (operatorName) return operatorName
if (/MANUAL|PLATFORM/i.test(item.providerCode) || item.providerCode === 'FIRSTLEG') return '操作人未记录'
return [item.providerCode, item.sourceReference].filter(Boolean).join(' · ') || '—'
}
function exceptionText(value: CarrierScheduleExceptionFact['exceptionCode']) { return ({ CUSTOMS_HOLD: '海关扣留', TERMINAL_HOLD: '码头扣留', DOCUMENT_HOLD: '单证扣留', UNKNOWN_HOLD: '其他扣留' } as Record<string, string>)[value] || value }
function providerErrorText(value?: string | null) {
const message = nonBlank(value)
@@ -189,7 +195,7 @@ const fulfillmentEvents = computed<FulfillmentEvent[]>(() => {
const timing = actual ? `实际 ${formatTime(item.actualAt)}` : item.estimatedAt ? `预计 ${formatTime(item.estimatedAt)}` : `观测 ${formatTime(item.observedAt)}`
const expected = actual && item.estimatedAt ? ` · 原预计 ${formatTime(item.estimatedAt)}` : ''
const type = manual ? '人工补录' : item.standardNode === 'DEPARTED_WAREHOUSE' && actual ? '确认离仓' : '船运节点'
values.push({ key: `node-${key}`, time, type, title: nodeText(item.standardNode), summary: `${timing}${expected} · ${nodeLocation(item)}`, source: [item.providerCode, item.sourceReference].filter(Boolean).join(' · ') || '—', actual })
values.push({ key: `node-${key}`, time, type, title: nodeText(item.standardNode), summary: `${timing}${expected} · ${nodeLocation(item)}`, source: nodeSourceText(item), actual })
}
for (const item of exceptions.value) values.push({ key: `exception-${item.id}`, time: item.observedAt, type: '异常', title: `${exceptionText(item.exceptionCode)} · ${item.active ? '发生' : '已解除'}`, summary: `${exceptionLocation(item)} · ${item.providerStatus}`, source: `船运跟踪 · ${[item.providerCode, item.sourceReference].filter(Boolean).join(' · ')}`, actual: true })
return values.sort((left, right) => {
@@ -285,7 +291,6 @@ watch(manualDialogOpen, (open) => { if (!open && !manualSaving.value) { manualId
<AppDialog class="container-node-dialog admin-workbench is-dialog" :class="{ 'admin-density-dialog': dense }" v-model="manualDialogOpen" :title="manualDialogTitle" :width="dense ? 'min(780px, calc(100vw - 28px))' : 'min(620px, calc(100vw - 28px))'" :fullscreenable="dense" append-to-body confirm-text="确认补录" :confirm-loading="manualSaving" @confirm="submitManualNode">
<el-alert v-if="manualError" type="error" :closable="false" show-icon title="补录未成功,已保留当前填写内容"><template #default><ApiErrorMeta :details="manualError" /></template></el-alert>
<el-alert v-if="manualValidationError" type="warning" :closable="false" show-icon :title="manualValidationError" />
<el-alert type="info" :closable="false" title="观测时间按提交时刻记录;预计事实不推进实际进度,既有事实不可编辑或删除。" />
<section v-if="dense" class="density-dialog-section">
<el-form class="density-form">
<div class="workbench-evidence"><table class="density-table manual-editor-table" aria-label="人工补录船节点"><tbody>