This commit is contained in:
2026-09-14 15:33:35 +08:00
parent 23d956ba15
commit b0de9c026f
30 changed files with 270 additions and 240 deletions
@@ -1,4 +1,5 @@
<script setup lang="ts">
import '@/styles/adminDensity.css'
import { computed } from 'vue'
import { ArrowLeft, Check, Pencil } from '@lucide/vue'
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
@@ -146,7 +147,7 @@ function formatNumber(value?: number | null, digits = 3) {
</script>
<template>
<section v-loading="loading" class="loading-detail-content" :aria-label="title" :aria-busy="loading">
<section v-loading="loading" class="loading-detail-content" :class="{ 'admin-density preload-density': mode === 'preload' }" :aria-label="title" :aria-busy="loading">
<section class="detail-overview" aria-label="清单概览">
<header class="detail-page-header" :class="{ 'detail-page-header--embedded': embedded }">
<el-button link type="primary" class="detail-page-header__back" @click="emit('back')"><ArrowLeft :size="16" />返回列表</el-button>
@@ -155,7 +156,7 @@ function formatNumber(value?: number | null, digits = 3) {
<p>{{ mode === 'preload' ? '核对运输信息、确认预装并维护逐条仓库备注' : '查看已完成装柜明细和柜次费用' }}</p>
</div>
<div v-if="detail && summary" class="detail-page-header__actions">
<PalletLabelPrintDialog v-if="summary.warehouseId" :plan-id="summary.planId" :warehouse-id="summary.warehouseId" :disabled="!summary.containerNo || summary.status === 'EDITING'" />
<PalletLabelPrintDialog :dense="mode === 'preload'" v-if="summary.warehouseId" :plan-id="summary.planId" :warehouse-id="summary.warehouseId" :disabled="!summary.containerNo || summary.status === 'EDITING'" />
<template v-if="canEditItemRemark">
<el-button v-if="canEditTransport" @click="emit('edit')"><Pencil :size="16" />{{ summary.containerNo ? '纠正集装箱号 / 航次' : '补录集装箱号 / 航次' }}</el-button>
<el-button type="primary" :loading="confirmSaving" :disabled="!preloadSummary?.readiness.canConfirm" @click="emit('confirmPreload')"><Check :size="16" />确认预装</el-button>
@@ -219,16 +220,13 @@ function formatNumber(value?: number | null, digits = 3) {
<section class="history-section" aria-label="操作历史">
<header><h2>操作历史</h2><span>{{ visibleHistories.length }} 条</span></header>
<el-empty v-if="visibleHistories.length === 0" description="暂无操作历史" :image-size="72" />
<el-timeline v-else>
<el-timeline-item v-for="history in visibleHistories" :key="historyEventKey(history) || history.historyId" :timestamp="formatDateTime(history.occurredAt)" placement="top">
<div class="history-entry">
<strong>{{ actionLabel(history.actionType) }}</strong>
<span v-if="history.fromStatus || history.toStatus">{{ loadingListStatusLabel(history.fromStatus) }} → {{ loadingListStatusLabel(history.toStatus) }}</span>
<p>{{ history.operatorName || '操作人信息缺失' }}<template v-if="history.operationReason"> · {{ history.operationReason }}</template></p>
</div>
</el-timeline-item>
</el-timeline>
<el-table :data="visibleHistories" border stripe empty-text="暂无操作历史" aria-label="清单操作历史">
<el-table-column label="时间" width="170"><template #default="{ row }">{{ formatDateTime(row.occurredAt) }}</template></el-table-column>
<el-table-column label="事件" width="140"><template #default="{ row }">{{ actionLabel(row.actionType) }}</template></el-table-column>
<el-table-column label="状态变化" min-width="200"><template #default="{ row }"><span v-if="row.fromStatus || row.toStatus">{{ loadingListStatusLabel(row.fromStatus) }} → {{ loadingListStatusLabel(row.toStatus) }}</span><span v-else>—</span></template></el-table-column>
<el-table-column label="操作人" min-width="160"><template #default="{ row }">{{ row.operatorName || '操作人信息缺失' }}</template></el-table-column>
<el-table-column label="原因 / 备注" min-width="240"><template #default="{ row }">{{ row.operationReason || '—' }}</template></el-table-column>
</el-table>
</section>
</DetailPageLayout>
</div>
@@ -273,7 +271,21 @@ function formatNumber(value?: number | null, digits = 3) {
.history-section header { display: flex; align-items: baseline; gap: 10px; margin-bottom: 18px; }
.history-section h2 { margin: 0; font-size: 16px; }
.history-section header span { color: var(--cc-muted); font-size: 12px; }
.history-entry { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.history-entry > span { color: var(--cc-muted); font-size: 12px; }
.history-entry p { width: 100%; margin: 5px 0 0; color: var(--cc-muted); font-size: 12px; }
.preload-density { gap: 12px; padding: 12px; border-radius: 4px; }
.preload-density .detail-page-header { gap: 12px; padding: 0 0 8px; }
.preload-density .detail-page-header__identity h1 { font-size: 16px; overflow-wrap: anywhere; }
.preload-density .detail-page-header__actions { flex-wrap: wrap; }
.preload-density .detail-summary { gap: 0; padding: 0; border-left: 1px solid var(--cc-border); }
.preload-density .detail-summary > div { gap: 0; align-items: stretch; min-height: 36px; border-bottom: 1px solid var(--cc-border); border-right: 1px solid var(--cc-border); }
.preload-density .detail-summary > div > span:first-child { display: flex; align-items: center; flex: 0 0 96px; padding: 6px 8px; background: #f8fafc; }
.preload-density .detail-summary > div > span:first-child::after { content: none; }
.preload-density .detail-summary strong, .preload-density .detail-summary :deep(.status-badge) { align-self: center; margin: 6px 8px; font-weight: 400; }
.preload-density .detail-summary__wide { grid-column: span 2; }
.preload-density .detail-layout :deep(.detail-page-layout__content) { gap: 12px; }
.preload-density .detail-section, .preload-density .history-section { padding-top: 8px; }
.preload-density .detail-section__heading h2 { font-size: 14px; }
.preload-density .detail-section__heading p, .preload-density small { font-size: 12px; }
.preload-density .readiness-note { align-items: baseline; }
.preload-density .readiness-note span { white-space: normal; overflow: visible; overflow-wrap: anywhere; }
@media (max-width: 720px) { .preload-density .detail-summary { grid-template-columns: 1fr; } .preload-density .detail-summary__wide { grid-column: auto; } }
</style>