fix(admin): show empty customs state for unloaded plan
This commit is contained in:
@@ -4,7 +4,7 @@ import { completeAllBuyContainerCustoms, downloadContainerCustomsCompletion, dow
|
|||||||
|
|
||||||
const customs = {
|
const customs = {
|
||||||
aggregateId: 2092438843630391401n, loadingPlanId: 2092438843630391402n, containerBookingId: 2092438843630391403n,
|
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 }],
|
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 }],
|
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,
|
startedAt: null, completedAt: null,
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export interface ContainerCustomsView {
|
|||||||
cabinetNo: string
|
cabinetNo: string
|
||||||
containerNo: string | null
|
containerNo: string | null
|
||||||
status: ContainerCustomsStatus
|
status: ContainerCustomsStatus
|
||||||
|
hasActualCargo: boolean
|
||||||
version: number
|
version: number
|
||||||
taxRefundCount: number
|
taxRefundCount: number
|
||||||
coveredTaxRefundCount: number
|
coveredTaxRefundCount: number
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import ContainerCustomsPanel from './ContainerCustomsPanel.vue'
|
|||||||
function customs(status: ContainerCustomsStatus, hasCompletion = false): ContainerCustomsView {
|
function customs(status: ContainerCustomsStatus, hasCompletion = false): ContainerCustomsView {
|
||||||
return {
|
return {
|
||||||
aggregateId: '2092438843630391401', loadingPlanId: '2092438843630391402', containerBookingId: '2092438843630391403',
|
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 }],
|
inbounds: [{ inboundId: '2092438843630391404', orderId: '2092438843630391405', orderNo: 'O-1', inboundCode: 'RK-1', tenantId: '2092438843630391406', customerName: '客户一', taxMode: 'TAX_REFUND', covered: true }],
|
||||||
documents: [{
|
documents: [{
|
||||||
documentId: '2092438843630391407', tenantId: '2092438843630391406',
|
documentId: '2092438843630391407', tenantId: '2092438843630391406',
|
||||||
@@ -78,6 +78,20 @@ describe('ContainerCustomsPanel', () => {
|
|||||||
expect(buttonTexts(wrapper)).not.toContain('提交')
|
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 () => {
|
it('allows completion upload and overwrite only while DECLARING with manage permission', async () => {
|
||||||
let wrapper = await render('DECLARING', true, false)
|
let wrapper = await render('DECLARING', true, false)
|
||||||
expect(wrapper.text()).toContain('上传报关完成文件')
|
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 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 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 missingCompletionCount = computed(() => customs.value?.documents.filter((item) => !item.completion).length || 0)
|
||||||
const statusText: Record<ContainerCustomsStatus, string> = {
|
const statusText: Record<ContainerCustomsStatus, string> = {
|
||||||
WAITING_CUSTOMER_DOCUMENTS: '待客户补齐退税资料', READY_TO_START: '可开始报关', DECLARING: '报关中', COMPLETED: '报关已完成',
|
WAITING_CUSTOMER_DOCUMENTS: '待客户补齐退税资料', READY_TO_START: '可开始报关', DECLARING: '报关中', COMPLETED: '报关已完成',
|
||||||
@@ -156,9 +156,9 @@ onMounted(load)
|
|||||||
<header class="customs-header">
|
<header class="customs-header">
|
||||||
<div><h2>整柜报关</h2><p>客户退税资料与平台买单事实按柜冻结;报关完成和国内离仓分别展示。</p></div>
|
<div><h2>整柜报关</h2><p>客户退税资料与平台买单事实按柜冻结;报关完成和国内离仓分别展示。</p></div>
|
||||||
<div v-if="customs" class="customs-actions">
|
<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 && 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>
|
<el-button v-if="canManage && allBuy && customs.status === 'DECLARING'" type="primary" :loading="actionLoading" @click="completeAllBuy">确认报关完成</el-button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</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>
|
<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>
|
<div v-else-if="loading"><el-skeleton :rows="3" animated /></div>
|
||||||
<template v-else-if="customs">
|
<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 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">
|
<div v-if="missingInbounds.length" class="compact-table-wrap">
|
||||||
<h3>缺退税资料清单</h3>
|
<h3>缺退税资料清单</h3>
|
||||||
@@ -182,6 +184,7 @@ onMounted(load)
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody></table>
|
</tbody></table>
|
||||||
</div>
|
</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>
|
<div v-if="customs.status === 'COMPLETED'" class="departure-state"><strong>国内离仓状态:</strong><el-tag :type="departureState === 'DEPARTED' ? 'success' : 'warning'">{{ departureState === 'DEPARTED' ? '已自动离仓' : '报关已完成,待其他离仓条件' }}</el-tag></div>
|
||||||
</template>
|
</template>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Reference in New Issue
Block a user