feat(client): show customs clearance results
This commit is contained in:
@@ -58,6 +58,24 @@ export interface MerchantCustomsContainer {
|
||||
completedAt?: string | null
|
||||
}
|
||||
|
||||
export type MerchantClearanceResultType = 'INSPECTION' | 'SUPPLEMENT_REQUIRED' | 'RELEASED' | 'DETAINED' | 'OTHER'
|
||||
export type MerchantClearanceResultStatus = 'OPEN' | 'RESOLVED' | 'CANCELLED'
|
||||
|
||||
export interface MerchantClearanceResult {
|
||||
id: string
|
||||
declarationBatchId: string
|
||||
customsContainerId: string
|
||||
resultType: MerchantClearanceResultType
|
||||
resultStatus: MerchantClearanceResultStatus
|
||||
customsEntryNo: string | null
|
||||
occurredAt: string
|
||||
resolution: string | null
|
||||
evidenceFileId: string | null
|
||||
recordedByName: string
|
||||
recordedAt: string
|
||||
previousResultId: string | null
|
||||
}
|
||||
|
||||
export interface ApiErrorMeta {
|
||||
code: string
|
||||
requestId: string | null
|
||||
@@ -105,6 +123,13 @@ export function getMerchantCustomsForOrder(orderId: string) {
|
||||
})
|
||||
}
|
||||
|
||||
export function getMerchantClearanceResults(orderId: string) {
|
||||
return apiRequest<MerchantClearanceResult[]>({
|
||||
method: 'GET',
|
||||
url: `/merchant/v1/customs/orders/${encodedId(orderId)}/clearance-results`,
|
||||
})
|
||||
}
|
||||
|
||||
export function uploadMerchantCustomsDocument(planId: string, inboundIds: string[], file: File) {
|
||||
validateMerchantCustomsFile(file)
|
||||
return apiRequest<MerchantCustomsDocument>({
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
downloadMerchantCustomsFinal,
|
||||
downloadMerchantCustomsSource,
|
||||
getMerchantCustomsForOrder,
|
||||
getMerchantClearanceResults,
|
||||
merchantCustomsErrorMessage,
|
||||
replaceMerchantCustomsDocument,
|
||||
rejectMerchantCustomsReceipt,
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
validateMerchantCustomsFile,
|
||||
type MerchantCustomsContainer,
|
||||
type MerchantCustomsDocument,
|
||||
type MerchantClearanceResult,
|
||||
} from '@/modules/first-leg/api/customs'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -43,6 +45,7 @@ const uploadInput = ref<HTMLInputElement>()
|
||||
const replaceInput = ref<HTMLInputElement>()
|
||||
const mutating = ref(false)
|
||||
const downloadingKey = ref('')
|
||||
const clearanceResults = ref<MerchantClearanceResult[]>([])
|
||||
|
||||
const taxRefundInbounds = computed(() => detail.value?.inbounds.filter((item) => item.taxMode === 'TAX_REFUND') ?? [])
|
||||
const locked = computed(() => Boolean(detail.value?.startedAt) || detail.value?.status === 'DECLARING' || detail.value?.status === 'COMPLETED')
|
||||
@@ -71,6 +74,7 @@ async function loadDetail() {
|
||||
const result = await getMerchantCustomsForOrder(props.orderId)
|
||||
detail.value = result
|
||||
initializeSelection(result)
|
||||
try { clearanceResults.value = await getMerchantClearanceResults(props.orderId) } catch { clearanceResults.value = [] }
|
||||
emit('updated', result)
|
||||
} catch (error) {
|
||||
detail.value = null
|
||||
@@ -247,6 +251,14 @@ function formatSize(bytes: number) {
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
|
||||
return `${(bytes / 1024 / 1024).toFixed(1)} MB`
|
||||
}
|
||||
|
||||
function clearanceResultLabel(value: MerchantClearanceResult) {
|
||||
return ({ INSPECTION: '查验', SUPPLEMENT_REQUIRED: '补充资料', RELEASED: '放行', DETAINED: '扣留', OTHER: '其他' } as Record<string, string>)[value.resultType] || value.resultType
|
||||
}
|
||||
|
||||
function clearanceResultStatusLabel(value: MerchantClearanceResult) {
|
||||
return value.resultStatus === 'RESOLVED' ? '已处理' : value.resultStatus === 'CANCELLED' ? '已取消' : '待处理'
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -302,6 +314,20 @@ function formatSize(bytes: number) {
|
||||
<input ref="replaceInput" class="customs-hidden-input" type="file" @change="handleReplacementFile">
|
||||
</section>
|
||||
|
||||
<section class="customs-section clearance-results-section">
|
||||
<div class="customs-section-title">
|
||||
<div><h3>海关申报结果</h3><p>平台完成正式申报后,客户可查看与本订单相关的查验、补充资料和放行结果。</p></div>
|
||||
</div>
|
||||
<el-empty v-if="!clearanceResults.length" description="暂无海关结果" :image-size="48" />
|
||||
<div v-else class="clearance-result-list">
|
||||
<div v-for="item in clearanceResults" :key="item.id" class="clearance-result-row">
|
||||
<div><strong>{{ clearanceResultLabel(item) }}</strong><span>{{ formatDateTime(item.occurredAt) }} · 报关单号 {{ item.customsEntryNo || '—' }}</span></div>
|
||||
<el-tag size="small" :type="item.resultStatus === 'RESOLVED' ? 'success' : 'warning'">{{ clearanceResultStatusLabel(item) }}</el-tag>
|
||||
<p v-if="item.resolution">{{ item.resolution }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="customs-section">
|
||||
<div class="customs-section-title">
|
||||
<div><h3>资料版本历史</h3><p>每份当前资料展示服务端版本、关联票和上传时间;覆盖及删除操作由服务端保留不可变历史。</p></div>
|
||||
@@ -358,6 +384,11 @@ function formatSize(bytes: number) {
|
||||
.customs-locked strong { color: var(--navy); font-size: 12px; }
|
||||
.customs-locked span { margin-top: 3px; font-size: 11px; }
|
||||
.customs-section { margin-top: 18px; }
|
||||
.clearance-result-list { display:grid; gap:10px; }
|
||||
.clearance-result-row { display:grid; grid-template-columns:minmax(0,1fr) auto; gap:6px 14px; padding:12px 14px; border:1px solid var(--border); background:#fbfcfe; }
|
||||
.clearance-result-row strong,.clearance-result-row span { display:block; }
|
||||
.clearance-result-row span { margin-top:4px; color:var(--muted); font-size:11px; }
|
||||
.clearance-result-row p { grid-column:1 / -1; margin:0; color:var(--navy); font-size:12px; }
|
||||
.customs-section-title { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; margin-bottom: 12px; }
|
||||
.customs-section-title h3 { margin: 0; color: var(--navy); font-size: 14px; }
|
||||
.customs-section-title p { margin: 5px 0 0; color: var(--muted); font-size: 11px; line-height: 1.55; }
|
||||
|
||||
Reference in New Issue
Block a user