diff --git a/src/modules/container-loading/pages/ContainerDetailView.vue b/src/modules/container-loading/pages/ContainerDetailView.vue index cbd5496b..dc3aee27 100644 --- a/src/modules/container-loading/pages/ContainerDetailView.vue +++ b/src/modules/container-loading/pages/ContainerDetailView.vue @@ -11,7 +11,7 @@ import AppDialog from '@/shared/components/AppDialog.vue' import LoadErrorState from '@/shared/components/LoadErrorState.vue' import StatusBadge from '@/shared/components/StatusBadge.vue' import { CarrierScheduleQueryPanel } from '@/modules/port-schedules' -import { ContainerFeesPanel, getConfirmedLoadingList, getPreloadPlan, LoadingListDetailDrawer, type LoadingDetail } from '@/modules/loading-lists' +import { ContainerFeesPanel, getConfirmedLoadingList, getPreloadPlan, LoadingListDetailDrawer, PalletLabelPrintDialog, type LoadingDetail } from '@/modules/loading-lists' import { confirmContainerOutboundDispatch, getContainerOutboundDispatch, @@ -496,7 +496,7 @@ onMounted(async () => { await load(); if (isOverseas.value && route.query.action
-

货柜信息

+

货柜信息

真实订舱
{{ detail.oceanBookingId ? (detail.billOfLadingNo || '已关联') : '未关联' }}关联已放舱订舱
异常状态
{{ detail.exceptionActive ? '异常' : '正常' }}
目的仓
{{ detail.destinationWarehouseName || detail.destinationWarehouseCode || '—' }}
拖车公司
{{ detail.terminalTruckingCompany || '—' }}
diff --git a/src/modules/loading-lists/api/platformLoadingLists.test.ts b/src/modules/loading-lists/api/platformLoadingLists.test.ts index 77046e85..791cd082 100644 --- a/src/modules/loading-lists/api/platformLoadingLists.test.ts +++ b/src/modules/loading-lists/api/platformLoadingLists.test.ts @@ -6,6 +6,8 @@ import { getPreloadPlan, listConfirmedLoadingLists, listPreloadPlans, + preparePalletLabels, + printPalletLabels, updatePreloadTransport, updateLoadingItemWarehouseRemark, } from './platformLoadingLists' @@ -158,4 +160,18 @@ describe('platform loading lists API', () => { }) }) + it('prepares and prints pallet labels by FBA warehouse', async () => { + const post = vi.spyOn(http, 'post') + .mockResolvedValueOnce({ code: '200', message: 'ok', data: { planId: 10, planNo: 'LP-1', containerNo: 'MSCU6639870', totalCount: 2, groups: [{ destinationWarehouseCode: 'ONT8', destinationWarehouseName: 'ONT8', totalCount: 2, labels: [{ labelId: 1, itemId: 2, accountId: 3, inboundId: 4, inboundCode: 'RK-1', containerNo: 'MSCU6639870', destinationWarehouseCode: 'ONT8', destinationWarehouseName: 'ONT8', palletIndex: 1, palletCode: 'MSCU6639870-ONT8-001' }] }] } }) + .mockResolvedValueOnce(new Blob(['pdf'], { type: 'application/pdf' })) + + const manifest = await preparePalletLabels('10', '20') + const pdf = await printPalletLabels('10', '20', 'ONT8', 1) + + expect(manifest).toMatchObject({ planId: '10', groups: [{ labels: [{ labelId: '1', itemId: '2', accountId: '3', inboundId: '4' }] }] }) + expect(pdf.type).toBe('application/pdf') + expect(post).toHaveBeenNthCalledWith(1, '/platform/v1/first-leg/loading-plans/10/pallet-labels/prepare', null, { params: { warehouseId: '20' } }) + expect(post).toHaveBeenNthCalledWith(2, '/platform/v1/first-leg/loading-plans/10/pallet-labels/print', { destinationWarehouseCode: 'ONT8', count: 1 }, { params: { warehouseId: '20' }, responseType: 'blob', timeout: 60_000 }) + }) + }) diff --git a/src/modules/loading-lists/api/platformLoadingLists.ts b/src/modules/loading-lists/api/platformLoadingLists.ts index 32ff36cd..3a8e3e7f 100644 --- a/src/modules/loading-lists/api/platformLoadingLists.ts +++ b/src/modules/loading-lists/api/platformLoadingLists.ts @@ -256,6 +256,34 @@ function normalizeHistory(raw: LoadingHistory): LoadingHistory { } } +export interface PalletLabel { + labelId: string + itemId: string + accountId: string + inboundId: string + inboundCode: string + containerNo: string + destinationWarehouseCode: string + destinationWarehouseName: string | null + palletIndex: number + palletCode: string +} + +export interface PalletLabelGroup { + destinationWarehouseCode: string + destinationWarehouseName: string | null + totalCount: number + labels: PalletLabel[] +} + +export interface PalletLabelManifest { + planId: string + planNo: string + containerNo: string + totalCount: number + groups: PalletLabelGroup[] +} + function normalizePage(raw: RawPage, normalize: (row: T) => T): LoadingListPage { return { records: (raw.records || []).map(normalize), @@ -353,3 +381,26 @@ export async function getConfirmedLoadingList(planId: string, warehouseId: strin histories: (result.histories || []).map(normalizeHistory), } } + +export async function preparePalletLabels(planId: string, warehouseId: string) { + const response = await http.post, ApiEnvelope>( + `${basePath}/loading-plans/${encodeURIComponent(planId)}/pallet-labels/prepare`, null, { params: { warehouseId } }, + ) + const result = unwrapApiResponse(response) + return { + ...result, + planId: requiredId(result.planId), + groups: (result.groups || []).map((group) => ({ + ...group, + labels: (group.labels || []).map((label) => ({ ...label, labelId: requiredId(label.labelId), itemId: requiredId(label.itemId), accountId: requiredId(label.accountId), inboundId: requiredId(label.inboundId) })), + })), + } +} + +export function printPalletLabels(planId: string, warehouseId: string, destinationWarehouseCode?: string, count?: number) { + return http.post( + `${basePath}/loading-plans/${encodeURIComponent(planId)}/pallet-labels/print`, + { destinationWarehouseCode: destinationWarehouseCode || null, count: count || null }, + { params: { warehouseId }, responseType: 'blob', timeout: 60_000 }, + ) +} diff --git a/src/modules/loading-lists/components/LoadingListDetailDrawer.vue b/src/modules/loading-lists/components/LoadingListDetailDrawer.vue index 11e8ef9e..4e354a13 100644 --- a/src/modules/loading-lists/components/LoadingListDetailDrawer.vue +++ b/src/modules/loading-lists/components/LoadingListDetailDrawer.vue @@ -6,6 +6,7 @@ import DataTable, { type DataTableColumn } from '@/shared/components/DataTable.v import DetailPageLayout from '@/shared/components/DetailPageLayout.vue' import LoadErrorState from '@/shared/components/LoadErrorState.vue' import StatusBadge from '@/shared/components/StatusBadge.vue' +import PalletLabelPrintDialog from './PalletLabelPrintDialog.vue' import { loadingListStatusLabel } from '../status' import type { ApiErrorDetails } from '@/shared/api/http' import type { LoadingDetail, LoadingHistory, LoadingItem, LoadingListMode, LoadingListSummary, PreloadSummary } from '../api/platformLoadingLists' @@ -151,6 +152,7 @@ function formatNumber(value?: number | null, digits = 3) {

{{ mode === 'preload' ? '核对运输信息、确认预装并维护逐条仓库备注' : '查看已完成装柜明细和柜次费用' }}

+