diff --git a/src/modules/container-loading/api/platformContainerLoading.ts b/src/modules/container-loading/api/platformContainerLoading.ts index 37aa7861..c9f08f86 100644 --- a/src/modules/container-loading/api/platformContainerLoading.ts +++ b/src/modules/container-loading/api/platformContainerLoading.ts @@ -69,6 +69,7 @@ function container(raw: ContainerBookingResource): ContainerBookingResource { ...raw, id: id(raw.id), planId: optionalId(raw.planId), + planNo: raw.planNo || null, mergedIntoContainerBookingId: optionalId(raw.mergedIntoContainerBookingId), warehouseId: id(raw.warehouseId), oceanBookingId: optionalId(raw.oceanBookingId), @@ -78,6 +79,7 @@ function container(raw: ContainerBookingResource): ContainerBookingResource { version: numberValue(raw.version), capacityCbm: raw.capacityCbm == null ? null : numberValue(raw.capacityCbm), maxWeightKg: raw.maxWeightKg == null ? null : numberValue(raw.maxWeightKg), + exceptionActive: Boolean(raw.exceptionActive), clearanceDocument: document(raw.clearanceDocument), } } diff --git a/src/modules/container-loading/pages/ContainersView.vue b/src/modules/container-loading/pages/ContainersView.vue index 86743514..0bc5eef2 100644 --- a/src/modules/container-loading/pages/ContainersView.vue +++ b/src/modules/container-loading/pages/ContainersView.vue @@ -21,7 +21,7 @@ import { CONTAINER_SPECIFICATIONS, applyContainerSpecification, containerSpecifi import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http' import ContainerOverviewPanel from '../components/ContainerOverviewPanel.vue' import { cancelContainerException, completeContainerClearance, createContainerSimulation, downloadContainerDocument, downloadContainerPdaPhoto, getContainer, getContainerClearance, getContainerOverview, linkContainerOceanBooking, listContainers, markContainerException, unlinkContainerOceanBooking, updateContainer, updateContainerTransport, uploadContainerClearance, uploadContainerDocument } from '../api/platformContainerLoading' -import type { ContainerBookingResource, ContainerDocumentType, ContainerOverview, ContainerOverviewDocument, ContainerPdaPhoto, ContainerPlanStatus, ContainerSaveRequest, ContainerSimulationCreateRequest } from '../types' +import type { ContainerBookingResource, ContainerDocumentStatus, ContainerDocumentType, ContainerOverview, ContainerOverviewDocument, ContainerPdaPhoto, ContainerPlanStatus, ContainerSaveRequest, ContainerSimulationCreateRequest } from '../types' type Filters = QueryModel & { customerId: string; keyword: string; containerType: string; status: ContainerPlanStatus | ''; sailingRange: string[] } type ContainerForm = ContainerSaveRequest & { id: string; version: number } @@ -169,10 +169,16 @@ const queryFields = computed(() => [ ]) const columns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 310, fixed: 'left', slotName: 'actions' }, - { key: 'cabinetNo', label: '柜次号', minWidth: 180, slotName: 'cabinet' }, - { key: 'oceanBooking', label: '真实订舱', minWidth: 170, slotName: 'oceanBooking' }, { key: 'containerNo', label: '集装箱号', minWidth: 150, slotName: 'containerNo' }, + { key: 'planNo', prop: 'planNo', label: '装柜清单号', minWidth: 165, formatter: (row: ContainerBookingResource) => row.planNo || '—' }, { key: 'planStatus', label: '状态', width: 110, slotName: 'status' }, + { key: 'exceptionActive', label: '异常', width: 90, formatter: (row: ContainerBookingResource) => row.exceptionActive ? '异常' : '正常' }, + { key: 'cabinetNo', label: '柜次号', minWidth: 180, slotName: 'cabinet' }, + { key: 'destinationClearanceDocumentStatus', label: '清关资料', width: 110, formatter: (row: ContainerBookingResource) => documentStatusText(row.destinationClearanceDocumentStatus) }, + { key: 'customsDeclarationDocumentStatus', label: '报关资料', width: 110, formatter: (row: ContainerBookingResource) => documentStatusText(row.customsDeclarationDocumentStatus) }, + { key: 'deliveryOrderDocumentStatus', label: 'DO资料', width: 100, formatter: (row: ContainerBookingResource) => documentStatusText(row.deliveryOrderDocumentStatus) }, + { key: 'pickupOrderDocumentStatus', label: 'PU资料', width: 100, formatter: (row: ContainerBookingResource) => documentStatusText(row.pickupOrderDocumentStatus) }, + { key: 'oceanBooking', label: '真实订舱', minWidth: 170, slotName: 'oceanBooking' }, { key: 'warehouseName', prop: 'warehouseName', label: '装柜仓库', minWidth: 160 }, { key: 'terminalTruckingCompany', prop: 'terminalTruckingCompany', label: '码头车队', minWidth: 160 }, { key: 'billOfLadingNo', prop: 'billOfLadingNo', label: '提单号', minWidth: 150 }, @@ -190,6 +196,7 @@ const columns: DataTableColumn[] = [ function optional(value: string | null) { return value?.trim() || null } function statusText(status: ContainerPlanStatus) { return ({ WAIT_LOADING: '待装柜', EDITING: '预装柜中', LOADING: '拣货中', COMPLETED: '装柜完成' })[status] } +function documentStatusText(status: ContainerDocumentStatus | null | undefined) { return status ? ({ MISSING: '未上传', UPLOADED: '已上传', COMPLETED: '已完成' })[status] : '—' } function formatTime(value: string | null | undefined) { return value?.replace('T', ' ').slice(0, 16) || '—' } function containerViewLabel(row: ContainerBookingResource) { const reference = [row.cabinetNo, row.containerNo, row.bookingNo] @@ -615,7 +622,7 @@ onMounted(() => { void Promise.all([loadCustomers(), loadWarehouses(), loadBusin - + 选择文件 diff --git a/src/modules/container-loading/types.ts b/src/modules/container-loading/types.ts index a1529530..d5923133 100644 --- a/src/modules/container-loading/types.ts +++ b/src/modules/container-loading/types.ts @@ -3,6 +3,7 @@ export type ContainerPlanStatus = 'WAIT_LOADING' | 'EDITING' | 'LOADING' | 'COMP export interface ContainerBookingResource { id: string planId: string | null + planNo?: string | null bookingNo: string containerNo: string | null cabinetNo: string @@ -40,6 +41,11 @@ export interface ContainerBookingResource { estimatedWarehouseArrivalAt: string | null actualWarehouseArrivalAt: string | null planStatus: ContainerPlanStatus + exceptionActive?: boolean + destinationClearanceDocumentStatus?: ContainerDocumentStatus + customsDeclarationDocumentStatus?: ContainerDocumentStatus + deliveryOrderDocumentStatus?: ContainerDocumentStatus + pickupOrderDocumentStatus?: ContainerDocumentStatus version: number createdAt?: string | null updatedAt?: string | null @@ -75,6 +81,7 @@ export interface BusinessDocument { } export type ContainerDocumentType = 'DESTINATION_CLEARANCE' | 'CUSTOMS_DECLARATION' | 'DELIVERY_ORDER' | 'PICKUP_ORDER' +export type ContainerDocumentStatus = 'MISSING' | 'UPLOADED' | 'COMPLETED' export interface ContainerOverviewDocument { id: string diff --git a/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.test.ts b/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.test.ts index abc4da85..c41aaf8c 100644 --- a/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.test.ts +++ b/src/modules/fba-pricing/carrier-rates/pages/CarrierRatesView.test.ts @@ -48,6 +48,17 @@ describe('CarrierRatesView price-book source contract', () => { expect(source).not.toContain('草稿') }) + it('shows all source-backed prototype price metadata without inventing cost breakdowns', () => { + for (const label of ['渠道', '参考时效', '最晚进仓时间', '船期', '附加费', '备注', '生效状态']) { + expect(source).toContain(`label="${label}"`) + } + for (const field of ['referenceTransit', 'latestWarehouseEntry', 'estimatedDepartureText', 'surchargeDescription', 'remark']) { + expect(source).toContain(`selectedChannel?.${field}`) + } + expect(source).not.toContain('AMS+换单费') + expect(source).not.toContain('建议报价') + }) + it('maps only active platform public domestic and overseas warehouses before confirmation', () => { expect(source).toContain('listAllPlatformWarehouses()') expect(source).toContain("warehouse.warehouseType === 'DOMESTIC'") diff --git a/src/modules/loading-lists/api/platformLoadingLists.ts b/src/modules/loading-lists/api/platformLoadingLists.ts index e24f8ee3..95ba3d6b 100644 --- a/src/modules/loading-lists/api/platformLoadingLists.ts +++ b/src/modules/loading-lists/api/platformLoadingLists.ts @@ -82,9 +82,12 @@ export interface LoadingItem { itemId: string accountId: string customerName: string | null + orderId: string + orderNo: string inboundId: string inboundDetailId: string inboundCode: string | null + containerNo: string | null skuCode: string | null destinationWarehouse: string | null chineseName: string | null @@ -237,6 +240,7 @@ function normalizeItem(raw: LoadingItem): LoadingItem { ...raw, itemId: requiredId(raw.itemId), accountId: requiredId(raw.accountId), + orderId: requiredId(raw.orderId), inboundId: requiredId(raw.inboundId), inboundDetailId: requiredId(raw.inboundDetailId), productQuantity: String(raw.productQuantity ?? '0'), diff --git a/src/modules/loading-lists/components/LoadingListDetailDrawer.vue b/src/modules/loading-lists/components/LoadingListDetailDrawer.vue index ec0f9c59..b2a967cc 100644 --- a/src/modules/loading-lists/components/LoadingListDetailDrawer.vue +++ b/src/modules/loading-lists/components/LoadingListDetailDrawer.vue @@ -27,6 +27,8 @@ const emit = defineEmits<{ retry: []; edit: []; confirmPreload: []; attachCustom const itemColumns: DataTableColumn[] = [ { key: 'sequence', prop: 'loadSequence', label: '装柜顺序', fixed: 'left', width: 92, hideable: false, movable: false }, { key: 'inboundCode', prop: 'inboundCode', label: '入仓码', minWidth: 150 }, + { key: 'orderNo', prop: 'orderNo', label: '订单号', minWidth: 150 }, + { key: 'containerNo', prop: 'containerNo', label: '集装箱号', minWidth: 145 }, { key: 'skuCode', prop: 'skuCode', label: 'SKU', minWidth: 130 }, { key: 'destinationWarehouse', prop: 'destinationWarehouse', label: '目的仓', minWidth: 130 }, { key: 'customerName', prop: 'customerName', label: '客户', minWidth: 150 }, diff --git a/src/modules/ocean-bookings/api/platformOceanBookings.ts b/src/modules/ocean-bookings/api/platformOceanBookings.ts index ccfd6a7a..2f3731e7 100644 --- a/src/modules/ocean-bookings/api/platformOceanBookings.ts +++ b/src/modules/ocean-bookings/api/platformOceanBookings.ts @@ -17,6 +17,7 @@ function booking(raw: OceanBookingResource): OceanBookingResource { customerId: raw.customerId == null ? null : String(raw.customerId), shippingCompanyId: raw.shippingCompanyId == null ? null : String(raw.shippingCompanyId), bookingAgentId: raw.bookingAgentId == null ? null : String(raw.bookingAgentId), + containerCount: Number(raw.containerCount ?? 0), pol: raw.pol ?? raw.departurePortName ?? null, pod: raw.pod ?? raw.destinationPortName ?? null, carrierName: raw.carrierName ?? raw.shippingCompanyName ?? null, diff --git a/src/modules/ocean-bookings/pages/OceanBookingsView.vue b/src/modules/ocean-bookings/pages/OceanBookingsView.vue index cbf584e1..69227961 100644 --- a/src/modules/ocean-bookings/pages/OceanBookingsView.vue +++ b/src/modules/ocean-bookings/pages/OceanBookingsView.vue @@ -40,6 +40,9 @@ const columns = [ { prop: 'bookingNo', label: '订舱号', minWidth: 150 }, { prop: 'customerName', label: '客户', minWidth: 160 }, { prop: 'billOfLadingNo', label: '提单号', minWidth: 155 }, + { prop: 'shipperName', label: '提单发货人', minWidth: 160 }, + { prop: 'consigneeName', label: '提单收货人', minWidth: 160 }, + { prop: 'containerCount', label: '柜量', width: 90, align: 'right' }, { prop: 'carrierName', label: '船公司', minWidth: 130 }, { prop: 'bookingAgent', label: '订舱代理', minWidth: 140 }, { prop: 'vesselName', label: '船名 / 航次', minWidth: 175, slotName: 'vessel' }, diff --git a/src/modules/ocean-bookings/types.ts b/src/modules/ocean-bookings/types.ts index 9b55c06c..d9aa894e 100644 --- a/src/modules/ocean-bookings/types.ts +++ b/src/modules/ocean-bookings/types.ts @@ -21,6 +21,7 @@ export interface OceanBookingDraftData { export interface OceanBookingResource { id: string; bookingNo: string | null; status: OceanBookingStatus; version: number customerId: string | null; customerName: string | null; billOfLadingNo: string | null + shipperName: string | null; consigneeName: string | null; containerCount: number channelCode: string | null; channelName: string | null; warehouseId: string | null; warehouseName: string | null shippingCompanyId: string | null; shippingCompanyName: string | null; bookingAgentId: string | null; bookingAgentName: string | null vesselName: string | null; voyageNo: string | null; departurePortCode: string | null; departurePortName: string | null diff --git a/src/modules/outbound-dispatch/pages/OutboundDispatchesView.vue b/src/modules/outbound-dispatch/pages/OutboundDispatchesView.vue index 633d7483..ca218a19 100644 --- a/src/modules/outbound-dispatch/pages/OutboundDispatchesView.vue +++ b/src/modules/outbound-dispatch/pages/OutboundDispatchesView.vue @@ -48,7 +48,8 @@ const columns = [ { key: 'actions', label: '操作', fixed: 'left', width: 240, slotName: 'actions', hideable: false, movable: false }, { prop: 'inboundCode', label: '入仓码', minWidth: 145 }, { prop: 'customerName', label: '客户', minWidth: 140 }, - { prop: 'containerNo', label: '柜号', minWidth: 130 }, + { prop: 'cabinetNo', label: '柜次号', minWidth: 145 }, + { prop: 'containerNo', label: '集装箱号', minWidth: 145 }, { prop: 'oceanBookingStatus', label: '订舱', width: 105, slotName: 'booking' }, { prop: 'truckAppointmentStatus', label: '拖车', width: 105, slotName: 'truck' }, { prop: 'pickTaskStatus', label: '拣货', width: 105, slotName: 'pick' }, @@ -209,7 +210,7 @@ onBeforeUnmount(() => { disposed = true; listRequestId += 1; optionRequestId +=
- +
diff --git a/src/modules/outbound-dispatch/types.ts b/src/modules/outbound-dispatch/types.ts index 4a9d5b18..ff2de734 100644 --- a/src/modules/outbound-dispatch/types.ts +++ b/src/modules/outbound-dispatch/types.ts @@ -1,5 +1,5 @@ export interface OutboundGateItem { code: string; name: string; passed: boolean; actual?: string | null; requirement?: string | null } export interface OutboundGateResult { passed: boolean; items: OutboundGateItem[]; checkedAt: string } -export interface OutboundDispatchResource { orderId: string; orderNo: string; inboundId: string; inboundCode: string; customerId?: string | null; customerName?: string | null; inboundStatus?: string | null; containerBookingId?: string | null; containerNo?: string | null; loadingPlanStatus?: string | null; pickTaskStatus?: string | null; oceanBookingStatus?: string | null; truckAppointmentStatus?: string | null; dispatchStatus?: string | null; version: number; gate?: OutboundGateResult | null } +export interface OutboundDispatchResource { orderId: string; orderNo: string; inboundId: string; inboundCode: string; customerId?: string | null; customerName?: string | null; inboundStatus?: string | null; containerBookingId?: string | null; cabinetNo?: string | null; containerNo?: string | null; loadingPlanStatus?: string | null; pickTaskStatus?: string | null; oceanBookingStatus?: string | null; truckAppointmentStatus?: string | null; dispatchStatus?: string | null; version: number; gate?: OutboundGateResult | null } export interface OutboundDispatchDetail { aggregate: OutboundDispatchResource; timeline: Array<{ module: string; action: string; fromStatus?: string | null; toStatus?: string | null; operatorName?: string | null; remark?: string | null; occurredAt: string }> } export interface OutboundDispatchQuery { orderId?: string; inboundId?: string; containerBookingId?: string } diff --git a/src/modules/trucking/api/platformTrucking.ts b/src/modules/trucking/api/platformTrucking.ts index 331467f9..12f5bb53 100644 --- a/src/modules/trucking/api/platformTrucking.ts +++ b/src/modules/trucking/api/platformTrucking.ts @@ -32,11 +32,12 @@ function normalizePage(payload: PagePayload): TruckAppointmentPage { } function normalizeResource(resource: TruckAppointmentResource): TruckAppointmentResource { - const idKeys = ['id', 'outboundOrderId', 'oceanBookingId', 'containerBookingId', 'loadingPlanId', 'warehouseId', 'customerId', 'carrierId'] + const idKeys = ['id', 'outboundOrderId', 'oceanBookingId', 'containerBookingId', 'loadingPlanId', 'warehouseId', 'customerId', 'shippingCompanyId', 'bookingAgentId', 'carrierId'] const normalized = { ...resource } as Record for (const key of idKeys) { if (normalized[key] !== null && normalized[key] !== undefined) normalized[key] = String(normalized[key]) } + normalized.attachmentCount = Number(normalized.attachmentCount ?? 0) return normalized as unknown as TruckAppointmentResource } diff --git a/src/modules/trucking/pages/TruckAppointmentsView.vue b/src/modules/trucking/pages/TruckAppointmentsView.vue index 2b6b855b..88f0f2ad 100644 --- a/src/modules/trucking/pages/TruckAppointmentsView.vue +++ b/src/modules/trucking/pages/TruckAppointmentsView.vue @@ -30,8 +30,13 @@ const fields = computed(() => [ const columns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 150, fixed: 'left', slotName: 'actions' }, { key: 'appointmentNo', prop: 'appointmentNo', label: '派车单号', minWidth: 170 }, - { key: 'containerNo', prop: 'containerNo', label: '集装箱号', minWidth: 145, formatter: (row: TruckAppointmentResource) => row.containerNo || (row.containerBookingId ? '已关联柜次' : '—') }, + { key: 'oceanBookingNo', prop: 'oceanBookingNo', label: '订舱号', minWidth: 150, formatter: (row: TruckAppointmentResource) => row.oceanBookingNo || '—' }, + { key: 'cabinetNo', prop: 'cabinetNo', label: '柜次号', minWidth: 150, formatter: (row: TruckAppointmentResource) => row.cabinetNo || '—' }, + { key: 'containerNo', prop: 'containerNo', label: '集装箱号', minWidth: 145, formatter: (row: TruckAppointmentResource) => row.containerNo || '—' }, { key: 'customerName', prop: 'customerName', label: '客户', minWidth: 150, formatter: (row: TruckAppointmentResource) => row.customerName || '客户信息缺失' }, + { key: 'warehouseName', prop: 'warehouseName', label: '装柜仓库', minWidth: 150, formatter: (row: TruckAppointmentResource) => row.warehouseName || '—' }, + { key: 'shippingCompanyName', prop: 'shippingCompanyName', label: '船公司', minWidth: 150, formatter: (row: TruckAppointmentResource) => row.shippingCompanyName || '—' }, + { key: 'bookingAgentName', prop: 'bookingAgentName', label: '订舱代理', minWidth: 150, formatter: (row: TruckAppointmentResource) => row.bookingAgentName || '—' }, { key: 'company', label: '运输公司', minWidth: 160, slotName: 'company' }, { key: 'driver', label: '司机', minWidth: 130, slotName: 'driver' }, { key: 'vehicleNo', prop: 'vehicleNo', label: '车头号', minWidth: 120, formatter: (row: TruckAppointmentResource) => row.vehicleNo || '—' }, @@ -40,7 +45,7 @@ const columns: DataTableColumn[] = [ { key: 'receivableAmount', label: '应收拖车费', minWidth: 130, align: 'right', formatter: (row: TruckAppointmentResource) => formatAmount(row.receivableAmount, row.currency) }, { key: 'plannedLoadingAt', label: '预计装箱日期', minWidth: 170, formatter: (row: TruckAppointmentResource) => formatDateTime(row.plannedLoadingAt) }, { key: 'status', label: '状态', width: 130, slotName: 'status' }, - { key: 'attachments', label: '附件', width: 90, formatter: (row: TruckAppointmentResource) => String(row.attachments?.length || 0) }, + { key: 'attachmentCount', label: '附件', width: 90, align: 'right', formatter: (row: TruckAppointmentResource) => String(Number(row.attachmentCount ?? 0)) }, { key: 'updatedAt', label: '更新时间', minWidth: 160, formatter: (row: TruckAppointmentResource) => formatDateTime(row.updatedAt) }, ] function formatDateTime(value?: string | null) { return value ? value.replace('T', ' ').slice(0, 16) : '—' } diff --git a/src/modules/trucking/types.ts b/src/modules/trucking/types.ts index 862094a5..be32a96c 100644 --- a/src/modules/trucking/types.ts +++ b/src/modules/trucking/types.ts @@ -86,11 +86,18 @@ export interface TruckAppointmentResource { revision?: number | null outboundOrderId?: string | null oceanBookingId?: string | null + oceanBookingNo?: string | null containerBookingId?: string | null + cabinetNo?: string | null loadingPlanId?: string | null warehouseId?: string | null + warehouseName?: string | null customerId?: string | null customerName?: string | null + shippingCompanyId?: string | null + shippingCompanyName?: string | null + bookingAgentId?: string | null + bookingAgentName?: string | null carrierId?: string | null carrierName?: string | null fleetContact?: string | null @@ -123,6 +130,7 @@ export interface TruckAppointmentResource { relatedContainerType?: string | null entrustingPartyName?: string | null attachments?: TruckAttachmentReference[] | null + attachmentCount?: number | string | null statusHistory?: TruckAppointmentStatusHistory[] | null createdAt?: string | null updatedAt?: string | null diff --git a/src/modules/warehouses/pages/WarehousesView.test.ts b/src/modules/warehouses/pages/WarehousesView.test.ts index b377a11a..6e599b90 100644 --- a/src/modules/warehouses/pages/WarehousesView.test.ts +++ b/src/modules/warehouses/pages/WarehousesView.test.ts @@ -182,6 +182,7 @@ describe('WarehousesView', () => { expect(warehousesSource).toMatch(/size="small"\s+plain\s+:type=/) expect(warehousesSource).toContain('') expect(warehousesSource).toContain('') + for (const label of ['仓库名称', '仓库编码', '州/省', '城市', '邮编']) expect(warehousesSource).toContain(`label: '${label}'`) expect(warehousesSource).toContain('@media (max-width: 720px)') }) it('uses the prototype-sized centered warehouse editor with a fixed footer', () => {