feat(client): show customs clearance results
This commit is contained in:
@@ -58,6 +58,24 @@ export interface MerchantCustomsContainer {
|
|||||||
completedAt?: string | null
|
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 {
|
export interface ApiErrorMeta {
|
||||||
code: string
|
code: string
|
||||||
requestId: string | null
|
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) {
|
export function uploadMerchantCustomsDocument(planId: string, inboundIds: string[], file: File) {
|
||||||
validateMerchantCustomsFile(file)
|
validateMerchantCustomsFile(file)
|
||||||
return apiRequest<MerchantCustomsDocument>({
|
return apiRequest<MerchantCustomsDocument>({
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
downloadMerchantCustomsFinal,
|
downloadMerchantCustomsFinal,
|
||||||
downloadMerchantCustomsSource,
|
downloadMerchantCustomsSource,
|
||||||
getMerchantCustomsForOrder,
|
getMerchantCustomsForOrder,
|
||||||
|
getMerchantClearanceResults,
|
||||||
merchantCustomsErrorMessage,
|
merchantCustomsErrorMessage,
|
||||||
replaceMerchantCustomsDocument,
|
replaceMerchantCustomsDocument,
|
||||||
rejectMerchantCustomsReceipt,
|
rejectMerchantCustomsReceipt,
|
||||||
@@ -18,6 +19,7 @@ import {
|
|||||||
validateMerchantCustomsFile,
|
validateMerchantCustomsFile,
|
||||||
type MerchantCustomsContainer,
|
type MerchantCustomsContainer,
|
||||||
type MerchantCustomsDocument,
|
type MerchantCustomsDocument,
|
||||||
|
type MerchantClearanceResult,
|
||||||
} from '@/modules/first-leg/api/customs'
|
} from '@/modules/first-leg/api/customs'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -43,6 +45,7 @@ const uploadInput = ref<HTMLInputElement>()
|
|||||||
const replaceInput = ref<HTMLInputElement>()
|
const replaceInput = ref<HTMLInputElement>()
|
||||||
const mutating = ref(false)
|
const mutating = ref(false)
|
||||||
const downloadingKey = ref('')
|
const downloadingKey = ref('')
|
||||||
|
const clearanceResults = ref<MerchantClearanceResult[]>([])
|
||||||
|
|
||||||
const taxRefundInbounds = computed(() => detail.value?.inbounds.filter((item) => item.taxMode === 'TAX_REFUND') ?? [])
|
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')
|
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)
|
const result = await getMerchantCustomsForOrder(props.orderId)
|
||||||
detail.value = result
|
detail.value = result
|
||||||
initializeSelection(result)
|
initializeSelection(result)
|
||||||
|
try { clearanceResults.value = await getMerchantClearanceResults(props.orderId) } catch { clearanceResults.value = [] }
|
||||||
emit('updated', result)
|
emit('updated', result)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
detail.value = null
|
detail.value = null
|
||||||
@@ -247,6 +251,14 @@ function formatSize(bytes: number) {
|
|||||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
|
||||||
return `${(bytes / 1024 / 1024).toFixed(1)} MB`
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -302,6 +314,20 @@ function formatSize(bytes: number) {
|
|||||||
<input ref="replaceInput" class="customs-hidden-input" type="file" @change="handleReplacementFile">
|
<input ref="replaceInput" class="customs-hidden-input" type="file" @change="handleReplacementFile">
|
||||||
</section>
|
</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">
|
<section class="customs-section">
|
||||||
<div class="customs-section-title">
|
<div class="customs-section-title">
|
||||||
<div><h3>资料版本历史</h3><p>每份当前资料展示服务端版本、关联票和上传时间;覆盖及删除操作由服务端保留不可变历史。</p></div>
|
<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 strong { color: var(--navy); font-size: 12px; }
|
||||||
.customs-locked span { margin-top: 3px; font-size: 11px; }
|
.customs-locked span { margin-top: 3px; font-size: 11px; }
|
||||||
.customs-section { margin-top: 18px; }
|
.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 { 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 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; }
|
.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