fix(admin): show empty customs state for unloaded plan
This commit is contained in:
@@ -4,7 +4,7 @@ import { completeAllBuyContainerCustoms, downloadContainerCustomsCompletion, dow
|
||||
|
||||
const customs = {
|
||||
aggregateId: 2092438843630391401n, loadingPlanId: 2092438843630391402n, containerBookingId: 2092438843630391403n,
|
||||
planNo: 'LP-1', cabinetNo: 'CB-1', containerNo: 'MSCU-1', status: 'READY_TO_START', version: '3', taxRefundCount: 1, coveredTaxRefundCount: 1,
|
||||
planNo: 'LP-1', cabinetNo: 'CB-1', containerNo: 'MSCU-1', status: 'READY_TO_START', hasActualCargo: true, version: '3', taxRefundCount: 1, coveredTaxRefundCount: 1,
|
||||
inbounds: [{ inboundId: 2092438843630391404n, orderId: 2092438843630391405n, orderNo: 'O-1', inboundCode: 'RK-1', tenantId: 2092438843630391406n, customerName: '客户一', taxMode: 'TAX_REFUND', covered: true }],
|
||||
documents: [{ documentId: 2092438843630391407n, tenantId: 2092438843630391406n, source: { fileId: '2092438843630391408', fileName: 'source.pdf', fileHash: 'hash', sizeBytes: 1, version: 2, uploadedAt: '2026-08-28T10:00:00' }, inboundIds: ['2092438843630391404'], inboundCodes: ['RK-1'], frozen: true, completion: null }],
|
||||
startedAt: null, completedAt: null,
|
||||
|
||||
@@ -41,6 +41,7 @@ export interface ContainerCustomsView {
|
||||
cabinetNo: string
|
||||
containerNo: string | null
|
||||
status: ContainerCustomsStatus
|
||||
hasActualCargo: boolean
|
||||
version: number
|
||||
taxRefundCount: number
|
||||
coveredTaxRefundCount: number
|
||||
|
||||
@@ -28,7 +28,7 @@ import ContainerCustomsPanel from './ContainerCustomsPanel.vue'
|
||||
function customs(status: ContainerCustomsStatus, hasCompletion = false): ContainerCustomsView {
|
||||
return {
|
||||
aggregateId: '2092438843630391401', loadingPlanId: '2092438843630391402', containerBookingId: '2092438843630391403',
|
||||
planNo: 'LP-1', cabinetNo: 'MCR08290001', containerNo: 'MSCU-1', status, version: 3, taxRefundCount: 1, coveredTaxRefundCount: 1,
|
||||
planNo: 'LP-1', cabinetNo: 'MCR08290001', containerNo: 'MSCU-1', status, hasActualCargo: true, version: 3, taxRefundCount: 1, coveredTaxRefundCount: 1,
|
||||
inbounds: [{ inboundId: '2092438843630391404', orderId: '2092438843630391405', orderNo: 'O-1', inboundCode: 'RK-1', tenantId: '2092438843630391406', customerName: '客户一', taxMode: 'TAX_REFUND', covered: true }],
|
||||
documents: [{
|
||||
documentId: '2092438843630391407', tenantId: '2092438843630391406',
|
||||
@@ -78,6 +78,20 @@ describe('ContainerCustomsPanel', () => {
|
||||
expect(buttonTexts(wrapper)).not.toContain('提交')
|
||||
})
|
||||
|
||||
it('shows a successful empty state and hides customs actions when the completed plan has no actual cargo', async () => {
|
||||
api.get.mockResolvedValue({ ...customs('READY_TO_START'), hasActualCargo: false, taxRefundCount: 0, coveredTaxRefundCount: 0, inbounds: [], documents: [] })
|
||||
const wrapper = mount(ContainerCustomsPanel, {
|
||||
props: { loadingPlanId: '2092438843630391402', canList: true, canManage: true, departureState: 'WAITING_OTHER_GATES' },
|
||||
global: { plugins: [ElementPlus] },
|
||||
})
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.text()).toContain('当前装柜计划没有实际装柜货物,无需报关')
|
||||
expect(wrapper.text()).toContain('无需报关')
|
||||
expect(buttonTexts(wrapper)).not.toContain('开始报关')
|
||||
expect(buttonTexts(wrapper)).not.toContain('导出买单 XLSX')
|
||||
})
|
||||
|
||||
it('allows completion upload and overwrite only while DECLARING with manage permission', async () => {
|
||||
let wrapper = await render('DECLARING', true, false)
|
||||
expect(wrapper.text()).toContain('上传报关完成文件')
|
||||
|
||||
@@ -37,7 +37,7 @@ const completionFiles = reactive<Record<string, File | null>>({})
|
||||
|
||||
const missingInbounds = computed(() => customs.value?.inbounds.filter((item) => item.taxMode === 'TAX_REFUND' && !item.covered) || [])
|
||||
const missingCoverageCount = computed(() => Math.max(0, (customs.value?.taxRefundCount || 0) - (customs.value?.coveredTaxRefundCount || 0)))
|
||||
const allBuy = computed(() => customs.value?.taxRefundCount === 0)
|
||||
const allBuy = computed(() => customs.value?.hasActualCargo === true && customs.value.taxRefundCount === 0)
|
||||
const missingCompletionCount = computed(() => customs.value?.documents.filter((item) => !item.completion).length || 0)
|
||||
const statusText: Record<ContainerCustomsStatus, string> = {
|
||||
WAITING_CUSTOMER_DOCUMENTS: '待客户补齐退税资料', READY_TO_START: '可开始报关', DECLARING: '报关中', COMPLETED: '报关已完成',
|
||||
@@ -156,9 +156,9 @@ onMounted(load)
|
||||
<header class="customs-header">
|
||||
<div><h2>整柜报关</h2><p>客户退税资料与平台买单事实按柜冻结;报关完成和国内离仓分别展示。</p></div>
|
||||
<div v-if="customs" class="customs-actions">
|
||||
<el-tag :type="customs.status === 'COMPLETED' ? 'success' : customs.status === 'WAITING_CUSTOMER_DOCUMENTS' ? 'danger' : 'warning'">{{ statusText[customs.status] }}</el-tag>
|
||||
<el-tag :type="!customs.hasActualCargo ? 'info' : customs.status === 'COMPLETED' ? 'success' : customs.status === 'WAITING_CUSTOMER_DOCUMENTS' ? 'danger' : 'warning'">{{ customs.hasActualCargo ? statusText[customs.status] : '无需报关' }}</el-tag>
|
||||
<el-button v-if="canManage && allBuy" plain :loading="actionLoading" @click="exportAllBuy">导出买单 XLSX</el-button>
|
||||
<el-button v-if="canManage && customs.status === 'READY_TO_START'" type="primary" :loading="actionLoading" @click="start">开始报关</el-button>
|
||||
<el-button v-if="canManage && customs.hasActualCargo && customs.status === 'READY_TO_START'" type="primary" :loading="actionLoading" @click="start">开始报关</el-button>
|
||||
<el-button v-if="canManage && allBuy && customs.status === 'DECLARING'" type="primary" :loading="actionLoading" @click="completeAllBuy">确认报关完成</el-button>
|
||||
</div>
|
||||
</header>
|
||||
@@ -166,6 +166,8 @@ onMounted(load)
|
||||
<LoadErrorState v-if="loadError" compact title="整柜报关加载失败" :message="loadError.message" :loading="loading" @retry="load"><template #details><ApiErrorMeta :details="loadError" /></template></LoadErrorState>
|
||||
<div v-else-if="loading"><el-skeleton :rows="3" animated /></div>
|
||||
<template v-else-if="customs">
|
||||
<el-empty v-if="!customs.hasActualCargo" description="当前装柜计划没有实际装柜货物,无需报关" :image-size="56" />
|
||||
<template v-else>
|
||||
<div class="customs-metrics"><span>退税总票 <strong>{{ customs.taxRefundCount }}</strong></span><span>已覆盖 <strong>{{ customs.coveredTaxRefundCount }}</strong></span><span :class="{ danger: missingCoverageCount }">缺失 <strong>{{ missingCoverageCount }}</strong></span></div>
|
||||
<div v-if="missingInbounds.length" class="compact-table-wrap">
|
||||
<h3>缺退税资料清单</h3>
|
||||
@@ -182,6 +184,7 @@ onMounted(load)
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
</template>
|
||||
<div v-if="customs.status === 'COMPLETED'" class="departure-state"><strong>国内离仓状态:</strong><el-tag :type="departureState === 'DEPARTED' ? 'success' : 'warning'">{{ departureState === 'DEPARTED' ? '已自动离仓' : '报关已完成,待其他离仓条件' }}</el-tag></div>
|
||||
</template>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user