From fb6465104d89f8d35e797d29a16b6e29a842fb5b Mon Sep 17 00:00:00 2001 From: liuyihe <2513762445@qq.com> Date: Thu, 13 Aug 2026 17:12:05 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/http.ts | 9 + src/components.d.ts | 3 +- src/components/common/QueryToolbar.vue | 10 +- src/modules/first-leg/api/firstLeg.ts | 110 +++- src/modules/first-leg/api/unifiedOrders.ts | 9 + .../components/FirstLegOrderEditorShell.vue | 19 +- .../components/OrderProgressPanel.vue | 53 +- .../first-leg/store/firstLegOrders.test.mjs | 23 + src/modules/first-leg/store/firstLegOrders.ts | 106 ++-- src/modules/first-leg/types/firstLegOrder.ts | 6 + .../views/FirstLegOrdersView.test.mjs | 163 +++++- .../first-leg/views/FirstLegOrdersView.vue | 485 +++++++++++++++--- tmp/pdfs/backend-check.pdf | Bin 0 -> 5917 bytes 13 files changed, 776 insertions(+), 220 deletions(-) create mode 100644 tmp/pdfs/backend-check.pdf diff --git a/src/api/http.ts b/src/api/http.ts index 977ead7..6b7a93f 100644 --- a/src/api/http.ts +++ b/src/api/http.ts @@ -136,4 +136,13 @@ export async function apiBlobRequest(config: AxiosRequestConfig) { } } +export async function apiBlobResponseRequest(config: AxiosRequestConfig) { + try { + const response = await transport.request({ ...config, responseType: 'blob' }) + return { blob: response.data, headers: response.headers } + } catch (error) { + throw mapError(error) + } +} + export { ApiError } diff --git a/src/components.d.ts b/src/components.d.ts index ce3390f..90a9533 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -34,6 +34,7 @@ declare module 'vue' { ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] ElOption: typeof import('element-plus/es')['ElOption'] ElPagination: typeof import('element-plus/es')['ElPagination'] + ElPopover: typeof import('element-plus/es')['ElPopover'] ElRadio: typeof import('element-plus/es')['ElRadio'] ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] @@ -45,8 +46,6 @@ declare module 'vue' { ElTabPane: typeof import('element-plus/es')['ElTabPane'] ElTabs: typeof import('element-plus/es')['ElTabs'] ElTag: typeof import('element-plus/es')['ElTag'] - ElTimeline: typeof import('element-plus/es')['ElTimeline'] - ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem'] ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElUpload: typeof import('element-plus/es')['ElUpload'] LoadErrorState: typeof import('./components/common/LoadErrorState.vue')['default'] diff --git a/src/components/common/QueryToolbar.vue b/src/components/common/QueryToolbar.vue index 42f3356..d10e44b 100644 --- a/src/components/common/QueryToolbar.vue +++ b/src/components/common/QueryToolbar.vue @@ -15,6 +15,7 @@ export interface QueryOption { export interface QueryField { key: string + label?: string type: QueryFieldType placeholder?: string options?: QueryOption[] @@ -79,7 +80,8 @@ defineExpose({ search, reset }) :aria-label="ariaLabel" >
- +
{{ searchText }} @@ -126,8 +128,10 @@ defineExpose({ search, reset }) diff --git a/src/modules/first-leg/api/firstLeg.ts b/src/modules/first-leg/api/firstLeg.ts index 8ae9c56..940e921 100644 --- a/src/modules/first-leg/api/firstLeg.ts +++ b/src/modules/first-leg/api/firstLeg.ts @@ -1,4 +1,4 @@ -import { apiBlobRequest, apiRequest } from '@/api/http' +import { ApiError, apiBlobRequest, apiBlobResponseRequest, apiRequest } from '@/api/http' export type ApiFirstLegStatus = 'DRAFT' | 'SUBMITTED' | 'APPROVED' | 'ACCEPTED' | 'REJECTED' | 'CANCELLED' | 'PROCESSING' | 'COMPLETED' export type ApiTransportMode = 'SEA' | 'AIR' | 'RAIL' | 'TRUCK' @@ -205,6 +205,18 @@ export interface MerchantFirstLegOrder { referenceId: string | null expressCompany?: string | null expressTrackingNo?: string | null + skuNos?: string[] | null + totalPackageCount?: number | null + totalGrossWeightKg?: number | null + totalNetWeightKg?: number | null + totalVolumeCbm?: number | null + totalQuantity?: number | null + chargeableWeightKg?: number | null + chargeableVolumeCbm?: number | null + inboundCode?: string | null + inboundedAt?: string | null + businessDate?: string | null + internationalOrderNo?: string | null submittedAt?: string | null completedAt?: string | null version: number @@ -212,6 +224,40 @@ export interface MerchantFirstLegOrder { updatedAt: string } +export interface MerchantFirstLegOrderRow { + orderId: number | string + orderNo: string | null + packageCount: number | null + weightKg: number | null + inboundId: number | string | null + inboundCode: string | null + channelName: string | null + destinationType: ApiDestinationType | null + customerName: string | null + receivingCompanyName: string | null + specificationModel: string | null + status: ApiFirstLegStatus + totalQuantity: number | null + totalGrossWeightKg: number | null + totalNetWeightKg: number | null + totalVolumeCbm: number | null + trackingNo: string | null + internationalOrderNo: string | null + referenceId: string | null + remark: string | null + destinationWarehouseName: string | null + expressCompany: string | null + skuNos: string | string[] | null + chargeableWeightKg: number | null + chargeableVolumeCbm: number | null + billingUnitPrice: number | null + inboundedAt: string | null + businessDate: string | null + shipmentId: string | null + createdAt: string + cargoes?: Array<{ skuNo?: string | null }> +} + export interface MerchantFirstLegOrderCargo extends FirstLegOrderCargoPayload { id: number | string itemNo: number @@ -251,6 +297,66 @@ export function downloadMerchantProductImage(fileId: number | string) { return apiBlobRequest({ method: 'GET', url: `/merchant/v1/files/product-images/${fileId}` }) } +export async function downloadMerchantInboundCartonLabels(inboundId: number | string) { + return downloadMerchantCartonLabelPdf( + `/merchant/v1/first-leg/inbounds/${encodeURIComponent(String(inboundId))}/carton-label-pdf`, + ) +} + +export async function downloadMerchantOrderCartonLabels(orderId: number | string) { + return downloadMerchantCartonLabelPdf( + `/merchant/v1/first-leg/orders/${encodeURIComponent(String(orderId))}/carton-label-pdf`, + ) +} + +export async function downloadMerchantOrderCartonLabelsBatch(orderIds: Array) { + const response = await apiBlobResponseRequest({ + method: 'POST', + url: '/merchant/v1/first-leg/orders/carton-label-pdf', + data: orderIds.map(String), + timeout: 120_000, + }) + await requirePdfBlob(response.blob) + return { + blob: response.blob, + failureCount: Number(response.headers['x-carton-label-failure-count'] ?? 0), + failures: String(response.headers['x-carton-label-failures'] ?? ''), + } +} + +async function downloadMerchantCartonLabelPdf(url: string) { + const blob = await apiBlobRequest({ + method: 'POST', + url, + data: null, + timeout: 60_000, + }) + await requirePdfBlob(blob) + return blob +} + +async function requirePdfBlob(blob: Blob) { + const signature = new TextDecoder().decode(await blob.slice(0, 5).arrayBuffer()) + if (signature === '%PDF-') return + + const text = await blob.text() + try { + const error = JSON.parse(text) as { code?: unknown; message?: unknown; traceId?: unknown } + if (typeof error.message === 'string') { + throw new ApiError( + error.message || '箱唛生成失败', + typeof error.code === 'string' ? error.code : 'INVALID_PDF', + undefined, + undefined, + typeof error.traceId === 'string' ? error.traceId : null, + ) + } + } catch (error) { + if (error instanceof ApiError) throw error + } + throw new ApiError('箱唛服务未返回有效的 PDF 文件', 'INVALID_PDF') +} + export interface FirstLegOrderQuery { keyword?: string status?: ApiFirstLegStatus @@ -330,7 +436,7 @@ export function estimateMerchantFirstLegPrice(payload: MerchantPriceEstimateRequ } export function getMerchantFirstLegOrders(params: FirstLegOrderQuery = {}) { - return apiRequest>({ method: 'GET', url: '/merchant/v1/orders/first-leg', params: { page: 1, size: 200, ...params } }) + return apiRequest>({ method: 'GET', url: '/merchant/v1/orders/first-leg', params: { page: 1, size: 200, ...params } }) } export function getMerchantFirstLegOrder(id: number | string) { diff --git a/src/modules/first-leg/api/unifiedOrders.ts b/src/modules/first-leg/api/unifiedOrders.ts index 3f24b87..f5b6cb7 100644 --- a/src/modules/first-leg/api/unifiedOrders.ts +++ b/src/modules/first-leg/api/unifiedOrders.ts @@ -73,6 +73,15 @@ export interface MerchantUnifiedOrderRow { totalPackageCount: number | null actualGrossWeightKg: number | null actualVolumeCbm: number | null + skuNos?: string[] | null + expressCompany?: string | null + totalNetWeightKg?: number | null + totalQuantity?: number | null + chargeableVolumeCbm?: number | null + shipmentId?: string | null + referenceId?: string | null + inboundedAt?: string | null + businessDate?: string | null measurement: MerchantUnifiedMeasurementSummary | null preload: MerchantUnifiedPreloadSummary | null loading: MerchantUnifiedLoadingSummary | null diff --git a/src/modules/first-leg/components/FirstLegOrderEditorShell.vue b/src/modules/first-leg/components/FirstLegOrderEditorShell.vue index 5dffb91..9bf5a06 100644 --- a/src/modules/first-leg/components/FirstLegOrderEditorShell.vue +++ b/src/modules/first-leg/components/FirstLegOrderEditorShell.vue @@ -24,19 +24,21 @@ const emit = defineEmits<{
- - + diff --git a/src/modules/first-leg/components/OrderProgressPanel.vue b/src/modules/first-leg/components/OrderProgressPanel.vue index 05ea410..32198cc 100644 --- a/src/modules/first-leg/components/OrderProgressPanel.vue +++ b/src/modules/first-leg/components/OrderProgressPanel.vue @@ -1,6 +1,6 @@