2026-08-28 16:54:02 +08:00
|
|
|
import { readFileSync } from 'node:fs'
|
|
|
|
|
import { resolve } from 'node:path'
|
|
|
|
|
import { describe, expect, it } from 'vitest'
|
2026-08-18 17:40:40 +08:00
|
|
|
|
2026-08-28 16:54:02 +08:00
|
|
|
const source = readFileSync(resolve(process.cwd(), 'src/modules/container-loading/pages/ContainerDetailView.vue'), 'utf8')
|
|
|
|
|
const overviewSource = readFileSync(resolve(process.cwd(), 'src/modules/container-loading/components/ContainerOverviewPanel.vue'), 'utf8')
|
2026-09-08 20:55:30 +08:00
|
|
|
const routeSource = readFileSync(resolve(process.cwd(), 'src/modules/container-loading/routes.ts'), 'utf8')
|
|
|
|
|
const loadingRouteSource = readFileSync(resolve(process.cwd(), 'src/modules/loading-lists/routes.ts'), 'utf8')
|
|
|
|
|
const navigationSource = readFileSync(resolve(process.cwd(), 'src/core/navigation/adminNavigation.ts'), 'utf8')
|
2026-09-08 22:03:03 +08:00
|
|
|
const lastLegFeesSource = readFileSync(resolve(process.cwd(), 'src/modules/container-loading/components/ContainerLastLegFeesPanel.vue'), 'utf8')
|
2026-08-26 11:10:48 +08:00
|
|
|
|
2026-08-28 16:54:02 +08:00
|
|
|
describe('ContainerDetailView customs integration', () => {
|
2026-08-31 12:12:51 +08:00
|
|
|
it('renders the requested detail sections in the confirmed business order', () => {
|
|
|
|
|
const headings = [
|
|
|
|
|
'container-information-heading">货柜信息',
|
|
|
|
|
'container-voyage-heading">船名航次信息',
|
2026-09-08 19:57:42 +08:00
|
|
|
'container-customs-heading">{{ isDomestic',
|
|
|
|
|
'container-receiving-heading">装柜作业与国内出仓',
|
2026-08-31 12:12:51 +08:00
|
|
|
]
|
|
|
|
|
let previous = -1
|
|
|
|
|
for (const heading of headings) {
|
|
|
|
|
const current = source.indexOf(heading)
|
|
|
|
|
expect(current, heading).toBeGreaterThan(previous)
|
|
|
|
|
previous = current
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('reuses the current schedule, customs, receipt and container-fee capabilities', () => {
|
|
|
|
|
expect(source).toContain('<CarrierScheduleQueryPanel')
|
|
|
|
|
expect(source).toContain(':sections="[\'documents\']"')
|
|
|
|
|
expect(source).toContain(':sections="[\'pda\']"')
|
2026-09-08 19:57:42 +08:00
|
|
|
expect(source).toContain('<LoadingListDetailDrawer')
|
2026-08-31 12:12:51 +08:00
|
|
|
expect(source).toContain('getConfirmedLoadingList')
|
|
|
|
|
expect(source).toContain('getPreloadPlan')
|
2026-09-08 19:57:42 +08:00
|
|
|
expect(source).toContain('label="装柜清单"')
|
|
|
|
|
expect(source).toContain('打印装柜清单')
|
|
|
|
|
expect(source).toContain(':sections="[\'pda\']"')
|
2026-08-31 12:12:51 +08:00
|
|
|
})
|
|
|
|
|
|
2026-09-08 20:55:30 +08:00
|
|
|
it('keeps the confirmed loading list inside the domestic container detail only', () => {
|
|
|
|
|
expect(routeSource).toContain("name: 'domestic-container-detail'")
|
|
|
|
|
expect(routeSource).toContain("name: 'overseas-container-detail'")
|
|
|
|
|
expect(loadingRouteSource).not.toContain("name: 'loading-lists'")
|
|
|
|
|
expect(navigationSource).not.toContain("title: '装柜清单'")
|
2026-09-08 22:03:03 +08:00
|
|
|
expect(source).toContain('<el-tabs v-model="activeTab"')
|
2026-09-08 20:55:30 +08:00
|
|
|
expect(source).toContain("v-if=\"isDomestic && activeTab === 'loading-list'\"")
|
|
|
|
|
expect(source).toContain("loadingDetail.value = await getConfirmedLoadingList(aggregate.loadingPlanId, resource.warehouseId)")
|
|
|
|
|
expect(source).toContain("getContainer(containerId.value, props.workbench), getContainerOverview(containerId.value)")
|
|
|
|
|
expect(source).toContain(':can-edit="false"')
|
|
|
|
|
})
|
|
|
|
|
|
2026-09-08 22:03:03 +08:00
|
|
|
it('separates domestic first-leg fees and connects the overseas fee lifecycle lazily', () => {
|
|
|
|
|
expect(source).toContain('label="头程计费" name="first-leg-fees"')
|
|
|
|
|
expect(source).toContain('label="尾程计费" name="last-leg-fees"')
|
|
|
|
|
expect(source).toContain('v-if="isDomestic && firstLegFeesOpened"')
|
|
|
|
|
expect(source).toContain('v-show="activeTab === \'first-leg-fees\'"')
|
|
|
|
|
expect(source).toContain('title="头程计费" :plan-id="overview.loadingPlanId" :container-booking-id="detail.id"')
|
|
|
|
|
expect(source).toContain('v-if="isOverseas && lastLegFeesOpened"')
|
|
|
|
|
expect(source).toContain('<ContainerLastLegFeesPanel :container-booking-id="detail.id" :container-no="detail.containerNo" />')
|
|
|
|
|
expect(source).toContain("if (isDomestic.value && activeTab.value === 'loading-list') await loadLoadingDetail(resource, aggregate)")
|
|
|
|
|
expect(source).toContain("tabName === 'loading-list' || tabName === 'first-leg-fees'")
|
|
|
|
|
expect(lastLegFeesSource.indexOf('label="操作"')).toBeLessThan(lastLegFeesSource.indexOf('label="客户"'))
|
|
|
|
|
expect(lastLegFeesSource).toContain('show-overflow-tooltip')
|
|
|
|
|
expect(lastLegFeesSource).toContain('尾程计费加载失败')
|
|
|
|
|
expect(lastLegFeesSource).toContain('empty-text="当前货柜暂无尾程费用"')
|
|
|
|
|
expect(lastLegFeesSource).toContain('v-loading="loading"')
|
|
|
|
|
expect(lastLegFeesSource).toContain('Promise.all([listOverseasContainerFees(props.containerBookingId), listOverseasContainerCustomers(props.containerBookingId), listContainerFeeSupplierOptions(null)])')
|
|
|
|
|
expect(lastLegFeesSource).toContain("form.settlementMode === 'DIRECT_PAID'")
|
|
|
|
|
expect(lastLegFeesSource).toContain('uploadContainerFeePaymentVoucher(form.customerId, file.raw)')
|
|
|
|
|
expect(lastLegFeesSource).toContain("access.can('platform:finance:fee-source:edit')")
|
|
|
|
|
expect(lastLegFeesSource).toContain("access.can('platform:finance:fee-source:review')")
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-28 16:54:02 +08:00
|
|
|
it('mounts the new container customs panel only after loading completes', () => {
|
|
|
|
|
expect(source).toContain("detail.planStatus === 'COMPLETED' && overview?.loadingPlanId && canListCustoms")
|
|
|
|
|
expect(source).toContain('<ContainerCustomsPanel')
|
|
|
|
|
expect(source).toContain(':loading-plan-id="overview.loadingPlanId"')
|
|
|
|
|
expect(source).toContain(':can-manage="canManageCustoms"')
|
2026-08-18 17:40:40 +08:00
|
|
|
})
|
|
|
|
|
|
2026-08-31 12:12:51 +08:00
|
|
|
it('allows the overview panel to render only the section owned by each detail group', () => {
|
|
|
|
|
expect(overviewSource).toContain("sections?: Array<'exception' | 'documents' | 'pda'>")
|
|
|
|
|
expect(overviewSource).toContain("visibleSections.has('exception')")
|
|
|
|
|
expect(overviewSource).toContain("visibleSections.has('documents')")
|
|
|
|
|
expect(overviewSource).toContain("visibleSections.has('pda')")
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-28 16:54:02 +08:00
|
|
|
it('removes the retired loading-list customs UI and calls', () => {
|
|
|
|
|
for (const retired of ['getLoadingDeclaration', 'uploadLoadingDeclaration', 'completeLoadingDeclaration', 'downloadLoadingDeclaration', 'CUSTOMS_DECLARATION']) {
|
|
|
|
|
expect(source).not.toContain(retired)
|
|
|
|
|
expect(overviewSource).not.toContain(retired)
|
|
|
|
|
}
|
2026-08-26 12:36:01 +08:00
|
|
|
})
|
2026-08-31 12:40:01 +08:00
|
|
|
|
|
|
|
|
it('offers the released-booking link workflow only for an eligible unlinked simulation container', () => {
|
|
|
|
|
expect(source).toContain("detail.value?.sourceType === 'SIMULATION'")
|
|
|
|
|
expect(source).toContain("!detail.value.oceanBookingId")
|
|
|
|
|
expect(source).toContain("['WAIT_LOADING', 'EDITING'].includes(detail.value.planStatus)")
|
|
|
|
|
expect(source).toContain('关联已放舱订舱')
|
|
|
|
|
expect(source).toContain("query: { linkContainerId: detail.value.id }")
|
|
|
|
|
})
|
2026-08-18 17:40:40 +08:00
|
|
|
})
|