This commit is contained in:
2026-08-28 16:54:02 +08:00
parent 94f003153d
commit 813763a02a
16 changed files with 413 additions and 883 deletions
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { computed, onBeforeUnmount, reactive, watch } from 'vue'
import { Check, Download, FileUp, TriangleAlert } from '@lucide/vue'
import { Download, FileUp, TriangleAlert } from '@lucide/vue'
import { getApiErrorDetails } from '@/shared/api/http'
import StatusBadge from '@/shared/components/StatusBadge.vue'
import type { ContainerOverview, ContainerOverviewDocument, ContainerPdaPhoto } from '../types'
@@ -10,36 +10,30 @@ const props = defineProps<{
overview: ContainerOverview
canManageDocuments: boolean
canManageCustoms: boolean
canDownloadCustoms?: boolean
canManageException: boolean
documentBusy?: boolean
customsCompleting?: boolean
}>()
const emit = defineEmits<{
uploadDocument: [documentType: string]
downloadDocument: [document: ContainerOverviewDocument]
completeCustoms: [document: ContainerOverviewDocument]
manageException: []
}>()
const documentTypes = [
{ type: 'DESTINATION_CLEARANCE', label: '目的港清关资料' },
{ type: 'CUSTOMS_DECLARATION', label: '国内出口报关附件' },
{ type: 'DELIVERY_ORDER', label: 'DO 资料' },
{ type: 'PICKUP_ORDER', label: 'PU 资料' },
] as const
const documentsByType = computed(() => new Map(props.overview.documents.map((item) => [item.documentType, item])))
const containerDownloadableTypes = new Set(['DELIVERY_ORDER', 'PICKUP_ORDER'])
const customsTypes = new Set(['DESTINATION_CLEARANCE', 'CUSTOMS_DECLARATION'])
function canDownloadDocument(documentType: string) {
return containerDownloadableTypes.has(documentType)
|| (documentType === 'CUSTOMS_DECLARATION' && props.canDownloadCustoms)
|| (documentType === 'DESTINATION_CLEARANCE' && props.canManageCustoms)
}
function canUploadDocument(documentType: string) {
return containerDownloadableTypes.has(documentType) ? props.canManageDocuments : customsTypes.has(documentType) && props.canManageCustoms
return containerDownloadableTypes.has(documentType) ? props.canManageDocuments : documentType === 'DESTINATION_CLEARANCE' && props.canManageCustoms
}
function hasDocumentAction(documentType: string) {
const document = documentsByType.value.get(documentType)
@@ -47,9 +41,7 @@ function hasDocumentAction(documentType: string) {
}
function documentStatusText(documentType: string) {
const document = documentsByType.value.get(documentType)
if (documentType !== 'CUSTOMS_DECLARATION') return document ? formatTime(document.uploadedAt) : '等待上传'
if (!document) return '第一步上传附件,第二步确认报关完成'
return document.completed ? '已确认报关完成' : '附件已上传,待确认报关完成'
return document ? formatTime(document.uploadedAt) : '等待上传'
}
function documentStatusLabel(documentType: string) {
const document = documentsByType.value.get(documentType)
@@ -129,8 +121,8 @@ onBeforeUnmount(() => { photoGeneration += 1; revokePhotoUrls() })
<table class="document-table">
<thead><tr><th class="document-table__actions">操作</th><th>资料类型</th><th>文件名</th><th>上传人</th><th>上传时间</th><th>状态</th></tr></thead>
<tbody><tr v-for="item in documentTypes" :key="item.type">
<td class="document-table__actions"><div class="document-actions"><el-button v-if="documentsByType.get(item.type) && canDownloadDocument(item.type)" size="small" :loading="documentBusy" @click="emit('downloadDocument', documentsByType.get(item.type)!)"><Download :size="15" />下载</el-button><el-button v-if="canUploadDocument(item.type) && !documentsByType.get(item.type)?.completed" size="small" @click="emit('uploadDocument', item.type)"><FileUp :size="15" />{{ documentsByType.get(item.type) ? '更新附件' : '上传附件' }}</el-button><el-button v-if="item.type === 'CUSTOMS_DECLARATION' && canManageCustoms && documentsByType.get(item.type) && !documentsByType.get(item.type)?.completed" size="small" type="primary" :loading="customsCompleting" @click="emit('completeCustoms', documentsByType.get(item.type)!)"><Check :size="15" />确认报关完成</el-button><span v-if="!hasDocumentAction(item.type)" class="no-action">—</span></div></td>
<td><strong>{{ item.label }}</strong><small v-if="item.type === 'CUSTOMS_DECLARATION'">{{ documentStatusText(item.type) }}</small></td>
<td class="document-table__actions"><div class="document-actions"><el-button v-if="documentsByType.get(item.type) && canDownloadDocument(item.type)" size="small" :loading="documentBusy" @click="emit('downloadDocument', documentsByType.get(item.type)!)"><Download :size="15" />下载</el-button><el-button v-if="canUploadDocument(item.type) && !documentsByType.get(item.type)?.completed" size="small" @click="emit('uploadDocument', item.type)"><FileUp :size="15" />{{ documentsByType.get(item.type) ? '更新附件' : '上传附件' }}</el-button><span v-if="!hasDocumentAction(item.type)" class="no-action">—</span></div></td>
<td><strong>{{ item.label }}</strong></td>
<td><span class="document-file-name" :title="documentsByType.get(item.type)?.fileName || undefined">{{ documentsByType.get(item.type)?.fileName || '—' }}</span></td>
<td>{{ documentsByType.get(item.type)?.uploadedByName || '—' }}</td>
<td>{{ documentsByType.get(item.type) ? formatTime(documentsByType.get(item.type)?.uploadedAt) : '—' }}</td>