From ccf16b823b6fba5681d20b7a523b17aa3dc323bd Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Sat, 5 Sep 2026 22:26:37 +0800 Subject: [PATCH 1/3] feat: show overseas funding and pallet summaries --- .../pages/FinanceCustomerAccountsView.vue | 2 +- .../api/platformOverseasBusiness.ts | 14 ++++++- .../overseas-business/contract.test.ts | 2 +- .../pages/OverseasBusinessViews.test.ts | 4 +- .../pages/PurchaseOrderDetailView.vue | 38 ++++++++++++++++--- src/modules/overseas-business/types.ts | 3 ++ src/modules/overseas-warehouse/api/pallets.ts | 6 +++ .../api/relabelOrders.test.ts | 2 +- .../overseas-warehouse/api/relabelOrders.ts | 5 +++ .../pages/PalletsView.test.ts | 5 ++- .../overseas-warehouse/pages/PalletsView.vue | 12 ++++-- .../pages/RelabelOrderDetailView.vue | 9 +++-- .../pages/RelabelOrdersView.test.ts | 1 + .../pages/RelabelOrdersView.vue | 11 ++++-- .../pages/prototypeContract.test.ts | 2 +- src/modules/overseas-warehouse/types.ts | 2 + .../pallets/api/platformPallets.ts | 5 +++ .../pallets/pages/PalletsView.test.ts | 3 ++ .../pallets/pages/PalletsView.vue | 18 ++++++++- 19 files changed, 116 insertions(+), 28 deletions(-) diff --git a/src/modules/finance/pages/FinanceCustomerAccountsView.vue b/src/modules/finance/pages/FinanceCustomerAccountsView.vue index 5123245d..cb87ca4b 100644 --- a/src/modules/finance/pages/FinanceCustomerAccountsView.vue +++ b/src/modules/finance/pages/FinanceCustomerAccountsView.vue @@ -445,7 +445,7 @@ onBeforeUnmount(clearVoucherPreview) - + {{ reviewRequest.requestNo }}{{ money(reviewRequest.amount) }}{{ paymentMethodLabel(reviewRequest.paymentMethod) }}{{ reviewRequest.voucherFileName || '—' }} diff --git a/src/modules/overseas-business/api/platformOverseasBusiness.ts b/src/modules/overseas-business/api/platformOverseasBusiness.ts index 93cf9bbb..f5a1ce97 100644 --- a/src/modules/overseas-business/api/platformOverseasBusiness.ts +++ b/src/modules/overseas-business/api/platformOverseasBusiness.ts @@ -6,6 +6,7 @@ import type { OverseasBusinessHistory, OverseasBusinessPage, OverseasPurchaseCustomerOption, OverseasPurchaseDetail, OverseasPurchaseQuery, OverseasPurchaseSummary, OverseasPurchaseWarehouseOption, UpdateLocalLooseboardOrderRequest, UpdateLocalTransportOrderRequest, UpdateOverseasPurchaseRequest, OverseasPurchaseOutboundDetail, + OverseasPurchaseFile, OverseasPurchaseFee, } from '../types' const procurementBasePath = '/platform/v1/last-leg/overseas-procurements' @@ -19,7 +20,7 @@ interface ProcurementDetailPayload { contactName?: string | null; contactPhone?: string | null; domesticCarrier?: string | null; domesticTrackingNo?: string | null overseasCarrier?: string | null; overseasTrackingNo?: string | null; estimatedArrivalAt?: string | null; remark?: string | null createdBy?: string | null; reviewedBy?: string | null; reviewRemark?: string | null; reviewedAt?: string | null - histories?: OverseasBusinessHistory[]; outboundDetails?: OverseasPurchaseOutboundDetail[] + histories?: OverseasBusinessHistory[]; outboundDetails?: OverseasPurchaseOutboundDetail[]; files?: any[]; fees?: any[] } interface TruckSummaryPayload extends Omit { id: Numeric; budgetAmount?: Numeric | null; totalWeightKg: Numeric; totalVolumeCbm: Numeric } interface TruckPalletPayload extends Omit { id: Numeric; warehousePalletId?: Numeric | null } @@ -40,7 +41,7 @@ function procurementSummary(value: ProcurementSummaryPayload): OverseasPurchaseS return { ...value, id: String(value.id), purchaseWarehouseId: value.purchaseWarehouseId == null ? null : String(value.purchaseWarehouseId), budgetUnitPrice: value.budgetUnitPrice == null ? null : String(value.budgetUnitPrice) } } function procurementDetail(value: ProcurementDetailPayload): OverseasPurchaseDetail { - return { ...procurementSummary(value.summary), productUrl: value.productUrl || null, receivingAddress: value.receivingAddress || null, contactName: value.contactName || null, contactPhone: value.contactPhone || null, domesticCarrier: value.domesticCarrier || null, domesticTrackingNo: value.domesticTrackingNo || null, overseasCarrier: value.overseasCarrier || null, overseasTrackingNo: value.overseasTrackingNo || null, estimatedArrivalAt: value.estimatedArrivalAt || null, remark: value.remark || null, createdBy: value.createdBy || null, reviewedBy: value.reviewedBy || null, reviewRemark: value.reviewRemark || null, reviewedAt: value.reviewedAt || null, histories: value.histories || [], outboundDetails: (value.outboundDetails || []).map((item) => ({ ...item, id: String(item.id), quantity: Number(item.quantity) })) } + return { ...procurementSummary(value.summary), productUrl: value.productUrl || null, receivingAddress: value.receivingAddress || null, contactName: value.contactName || null, contactPhone: value.contactPhone || null, domesticCarrier: value.domesticCarrier || null, domesticTrackingNo: value.domesticTrackingNo || null, overseasCarrier: value.overseasCarrier || null, overseasTrackingNo: value.overseasTrackingNo || null, estimatedArrivalAt: value.estimatedArrivalAt || null, remark: value.remark || null, createdBy: value.createdBy || null, reviewedBy: value.reviewedBy || null, reviewRemark: value.reviewRemark || null, reviewedAt: value.reviewedAt || null, histories: value.histories || [], outboundDetails: (value.outboundDetails || []).map((item) => ({ ...item, id: String(item.id), quantity: Number(item.quantity) })), files: (value.files || []).map((item) => ({ ...item, id: String(item.id), fileId: String(item.fileId), sizeBytes: Number(item.sizeBytes) })), fees: (value.fees || []).map((item) => ({ ...item, id: String(item.id), voucherFileId: String(item.voucherFileId), amount: String(item.amount) })) } } function truckSummary(value: TruckSummaryPayload): LocalTransportOrder { return { ...value, id: String(value.id), totalWeightKg: String(value.totalWeightKg), totalVolumeCbm: String(value.totalVolumeCbm), budgetAmount: value.budgetAmount == null ? null : String(value.budgetAmount) } @@ -75,6 +76,15 @@ export async function updateOverseasPurchase(orderNo: string, customerId: string const response = await overseasHttp.patch, ApiEnvelope>(`${procurementBasePath}/${encodeURIComponent(orderNo)}`, payload, { params: { customerId } }) return procurementDetail(unwrapApiResponse(response)) } +export async function uploadOverseasPurchaseFile(orderNo: string, customerId: string, category: 'RECEIPT' | 'VOUCHER', file: File) { + const body = new FormData(); body.append('file', file) + const response = await overseasHttp.post, ApiEnvelope>(`${procurementBasePath}/${encodeURIComponent(orderNo)}/files/${category}`, body, { params: { customerId } }) + return unwrapApiResponse(response) +} +export async function createOverseasPurchaseFee(orderNo: string, customerId: string, payload: { feeItemCode: string; feeItemName: string; amount: number; voucherFileId: string; remark?: string }) { + const response = await overseasHttp.post, ApiEnvelope>(`${procurementBasePath}/${encodeURIComponent(orderNo)}/fees`, payload, { params: { customerId } }) + return unwrapApiResponse(response) +} export async function listUsTruckOrders(query: LocalTransportQuery = {}) { const response = await overseasHttp.get>, ApiEnvelope>>(truckBasePath, { params: { ...query, orderNo: query.orderNo?.trim() || undefined, routeKeyword: query.routeKeyword?.trim() || undefined, status: query.status || undefined, transportType: query.transportType || undefined, customerId: query.customerId || undefined } }) return normalizePage(unwrapApiResponse(response), truckSummary) diff --git a/src/modules/overseas-business/contract.test.ts b/src/modules/overseas-business/contract.test.ts index e6117a30..55d60820 100644 --- a/src/modules/overseas-business/contract.test.ts +++ b/src/modules/overseas-business/contract.test.ts @@ -33,7 +33,7 @@ describe('overseas business real contract pages', () => { expect(api).toContain('updateUsTruckOrder') expect(api).toContain('createLocalLooseboardOrder') expect(api).toContain('updateLocalLooseboardOrder') - expect(api).not.toContain('createOverseasPurchase') + expect(api).not.toMatch(/export async function createOverseasPurchase\s*\(/) expect(api).not.toMatch(/\/merchant\/|receive-pallets|\/status/) expect(list).toContain('listLocalLooseboardOrders') expect(form).toContain('listPlatformTenants') diff --git a/src/modules/overseas-business/pages/OverseasBusinessViews.test.ts b/src/modules/overseas-business/pages/OverseasBusinessViews.test.ts index db746f37..c886441d 100644 --- a/src/modules/overseas-business/pages/OverseasBusinessViews.test.ts +++ b/src/modules/overseas-business/pages/OverseasBusinessViews.test.ts @@ -22,7 +22,7 @@ const routing = vi.hoisted(() => ({ push: vi.fn(), route: { params: {} as Record vi.mock('vue-router', () => ({ useRoute: () => routing.route, useRouter: () => ({ push: routing.push }) })) vi.mock('@/modules/iam', () => ({ listPlatformTenants: vi.fn() })) vi.mock('../api/platformOverseasBusiness', () => ({ - listOverseasPurchases: vi.fn(), getOverseasPurchase: vi.fn(), updateOverseasPurchase: vi.fn(), listOverseasPurchaseCustomerOptions: vi.fn(), listOverseasPurchaseWarehouseOptions: vi.fn(), + listOverseasPurchases: vi.fn(), getOverseasPurchase: vi.fn(), updateOverseasPurchase: vi.fn(), uploadOverseasPurchaseFile: vi.fn(), createOverseasPurchaseFee: vi.fn(), listOverseasPurchaseCustomerOptions: vi.fn(), listOverseasPurchaseWarehouseOptions: vi.fn(), listUsTruckOrders: vi.fn(), getUsTruckOrder: vi.fn(), createUsTruckOrder: vi.fn(), updateUsTruckOrder: vi.fn(), listLocalLooseboardOrders: vi.fn(), getLocalLooseboardOrder: vi.fn(), createLocalLooseboardOrder: vi.fn(), updateLocalLooseboardOrder: vi.fn(), })) @@ -36,7 +36,7 @@ const purchase: OverseasPurchaseDetail = { purchaseQuantity: 20, budgetUnitPrice: '12.50', currency: 'USD', purchaseWarehouseId: '20001', purchaseWarehouseName: '洛杉矶采购仓', inboundQuantity: 10, outboundQuantity: 0, inventoryQuantity: 10, status: 'PENDING_OUTBOUND', version: 3, createdAt: '2026-08-01T10:00:00', updatedAt: '2026-08-02T10:00:00', receivingAddress: 'LA', contactName: 'Alice', contactPhone: '10086', domesticCarrier: null, domesticTrackingNo: null, overseasCarrier: 'UPS', overseasTrackingNo: 'UPS-1', estimatedArrivalAt: '2026-08-25T12:00:00', remark: '采购备注', createdBy: '7', reviewedBy: '8', reviewRemark: null, reviewedAt: '2026-08-02T09:00:00', - histories: [{ operationType: 'UPDATE', fromStatus: 'APPROVED', toStatus: 'APPROVED', operatorId: '7', changeDetail: '{"remark":"采购备注"}', remark: null, createdAt: '2026-08-02T10:00:00' }], + histories: [{ operationType: 'UPDATE', fromStatus: 'APPROVED', toStatus: 'APPROVED', operatorId: '7', changeDetail: '{"remark":"采购备注"}', remark: null, createdAt: '2026-08-02T10:00:00' }], files: [], fees: [], } const truck: LocalTransportOrder = { id: '9007199254740995', customerId: '10002', orderNo: 'TK-200', transportType: 'PORT_PICKUP', origin: 'Long Beach', destination: 'Ontario', containerType: '40HQ', palletCount: 2, packageCount: 20, receivedPalletCount: 0, allocatedPalletCount: 0, shippedPalletCount: 0, totalWeightKg: '1200.000', totalVolumeCbm: '16.500', budgetAmount: '850.00', currency: 'USD', contactName: 'Bob', contactInfo: '10010', requestedDate: '2026-08-28', status: 'PENDING_REVIEW', version: 1, createdAt: '2026-08-01T10:00:00', updatedAt: '2026-08-02T10:00:00' } const truckDetail: LocalTransportDetail = { ...truck, originAddress: 'Pier A', destinationAddress: 'Warehouse B', remark: null, createdBy: '7', pallets: [], histories: [] } diff --git a/src/modules/overseas-business/pages/PurchaseOrderDetailView.vue b/src/modules/overseas-business/pages/PurchaseOrderDetailView.vue index 11ca8764..2901426d 100644 --- a/src/modules/overseas-business/pages/PurchaseOrderDetailView.vue +++ b/src/modules/overseas-business/pages/PurchaseOrderDetailView.vue @@ -1,17 +1,22 @@ diff --git a/src/modules/overseas-warehouse/pages/RelabelOrderDetailView.vue b/src/modules/overseas-warehouse/pages/RelabelOrderDetailView.vue index da38e0f1..c507db58 100644 --- a/src/modules/overseas-warehouse/pages/RelabelOrderDetailView.vue +++ b/src/modules/overseas-warehouse/pages/RelabelOrderDetailView.vue @@ -15,16 +15,17 @@ const detail = ref(null) const loading = ref(false) const loadError = ref(null) const groups = [ - { title: '单据信息', fields: ['换标单号', '关联订单', '状态', '创建时间', '更新时间'] }, + { title: '单据信息', fields: ['换标单号', '关联订单', '状态', '资金状态', '创建时间', '更新时间'] }, { title: '商品信息', fields: ['商品SKU', '中文品名', '箱数', '预算总价'] }, { title: '作业要求', fields: ['贴标要求', '上传文件', '备注'] }, - { title: '费用明细', fields: ['开箱费', '挑货费', '换标费', '换箱唛标费', '换标费总计'] }, + { title: '费用明细', fields: ['换标费总计', '资金异常说明'] }, ] function text(value: unknown) { return String(value ?? '').trim() || '—' } function status(value: unknown) { const labels: Record = { PENDING_PROCESSING: '待处理', PROCESSING: '处理中', COMPLETED: '已完成', REJECTED: '已驳回', CANCELLED: '已取消' } return labels[String(value || '')] || text(value) } +function walletStatus(value: unknown) { return ({ FREEZE_PENDING: '冻结中', FROZEN: '已冻结', SETTLE_PENDING: '结算中', SETTLED: '已结算', RELEASE_PENDING: '释放中', RELEASED: '已释放', FREEZE_FAILED: '冻结失败', SETTLE_FAILED: '结算失败', RELEASE_FAILED: '释放失败' } as Record)[String(value || '')] || text(value) } const referenceValue = computed(() => text(detail.value?.application.relabelNo || '换标单号待补充')) const fieldValues = computed>(() => { const value = detail.value @@ -33,6 +34,7 @@ const fieldValues = computed>(() => { 换标单号: text(value?.application.relabelNo), 关联订单: text(value?.application.lastlegOrderNo || value?.application.customerOrderNo), 状态: status(value?.application.status), + 资金状态: walletStatus(value?.application.walletStatus), 创建时间: formatOverseasDateTime(value?.application.createdAt), 更新时间: value?.history.length ? formatOverseasDateTime(value.history[value.history.length - 1]?.createdAt) : '—', 商品SKU: items.map((item) => text(item.skuCode)).join('、') || '—', @@ -42,7 +44,8 @@ const fieldValues = computed>(() => { 贴标要求: items.map((item) => text(item.labelRequirement)).join(';') || '—', 上传文件: items.map((item) => text(item.relabelFileName)).filter((item) => item !== '—').join('、') || '—', 备注: text(value?.remark), - 开箱费: '—', 挑货费: '—', 换标费: '—', 换箱唛标费: '—', 换标费总计: '—', + 换标费总计: value?.application.chargeAmount == null ? '—' : `${Number(value.application.chargeAmount).toFixed(2)} ${value.application.chargeCurrency || 'USD'}`, + 资金异常说明: text(value?.application.walletErrorMessage), } }) async function load() { diff --git a/src/modules/overseas-warehouse/pages/RelabelOrdersView.test.ts b/src/modules/overseas-warehouse/pages/RelabelOrdersView.test.ts index 34d77c72..4b5cfcd3 100644 --- a/src/modules/overseas-warehouse/pages/RelabelOrdersView.test.ts +++ b/src/modules/overseas-warehouse/pages/RelabelOrdersView.test.ts @@ -82,6 +82,7 @@ describe('RelabelOrdersView', () => { formItem(wrapper, '商品SKU').findComponent({ name: 'ElInput' }).vm.$emit('update:modelValue', 'SKU-1') formItem(wrapper, '中文品名').findComponent({ name: 'ElInput' }).vm.$emit('update:modelValue', '商品一') formItem(wrapper, '箱数').findComponent({ name: 'ElInputNumber' }).vm.$emit('update:modelValue', 2) + formItem(wrapper, '换标费用(CNY)').findComponent({ name: 'ElInputNumber' }).vm.$emit('update:modelValue', 20) formItem(wrapper, '截止时间').findComponent({ name: 'ElDatePicker' }).vm.$emit('update:modelValue', '2026-08-30T10:00:00') formItem(wrapper, '联系人').findComponent({ name: 'ElInput' }).vm.$emit('update:modelValue', '张三') formItem(wrapper, '联系电话').findComponent({ name: 'ElInput' }).vm.$emit('update:modelValue', '13800000000') diff --git a/src/modules/overseas-warehouse/pages/RelabelOrdersView.vue b/src/modules/overseas-warehouse/pages/RelabelOrdersView.vue index 5843a8eb..eaf1188f 100644 --- a/src/modules/overseas-warehouse/pages/RelabelOrdersView.vue +++ b/src/modules/overseas-warehouse/pages/RelabelOrdersView.vue @@ -43,6 +43,8 @@ const columns: DataTableColumn[] = [ { key: 'productName', label: '中文品名', minWidth: 180 }, { key: 'cartons', label: '箱数', width: 90, align: 'right' }, { key: 'budgetTotal', label: '预算总价', width: 120, align: 'right' }, + { key: 'relabelCost', label: '换标费用', width: 120, align: 'right' }, + { key: 'walletStatus', label: '资金状态', width: 120 }, { key: 'labelRequirement', label: '贴标要求', minWidth: 150 }, { key: 'fileName', label: '上传文件', minWidth: 160 }, { key: 'status', label: '状态', width: 110 }, @@ -115,12 +117,12 @@ async function submitForm() { try { if (dialogMode.value === 'create') { if (!form.customerId || !form.warehouseId) { ElMessage.warning('请选择客户和公共海外收货仓'); return } - if (!form.relatedOrderNo.trim() || !form.sku.trim() || !form.productName.trim() || !form.cartons || !form.labelDeadline || !form.contactName.trim() || !form.contactPhone.trim() || !form.shipmentNo.trim() || !form.newSkuCode.trim() || !form.labelRequirement) { + if (!form.relatedOrderNo.trim() || !form.sku.trim() || !form.productName.trim() || !form.cartons || !form.relabelCost || form.relabelCost <= 0 || !form.labelDeadline || !form.contactName.trim() || !form.contactPhone.trim() || !form.shipmentNo.trim() || !form.newSkuCode.trim() || !form.labelRequirement) { ElMessage.warning('请完整填写换标单必填信息') return } submitting.value = true - await createRelabelOrder(form.customerId, { customerOrderNo: form.relatedOrderNo.trim(), overseasWarehouseId: form.warehouseId, labelDeadline: form.labelDeadline, contactName: form.contactName.trim(), contactPhone: form.contactPhone.trim(), remark: form.remark.trim() || undefined, idempotencyKey: createIdempotencyKey.value, items: [{ skuCode: form.sku.trim(), productNameZh: form.productName.trim(), totalPackageCount: Number(form.cartons), notifiedPackageCount: Number(form.cartons), shipmentNo: form.shipmentNo.trim(), newSkuCode: form.newSkuCode.trim(), labelRequirement: form.labelRequirement }] }) + await createRelabelOrder(form.customerId, { customerOrderNo: form.relatedOrderNo.trim(), overseasWarehouseId: form.warehouseId, labelDeadline: form.labelDeadline, contactName: form.contactName.trim(), contactPhone: form.contactPhone.trim(), chargeAmount: Number(form.relabelCost), remark: form.remark.trim() || undefined, idempotencyKey: createIdempotencyKey.value, items: [{ skuCode: form.sku.trim(), productNameZh: form.productName.trim(), totalPackageCount: Number(form.cartons), notifiedPackageCount: Number(form.cartons), shipmentNo: form.shipmentNo.trim(), newSkuCode: form.newSkuCode.trim(), labelRequirement: form.labelRequirement }] }) ElMessage.success('换标单已创建') } else { if (!allowedStatusOptions.value.includes(form.status)) { ElMessage.warning('当前状态没有可提交的审核流转'); return } @@ -148,6 +150,7 @@ function relabelStatusCode(value: unknown) { const codes: Record = { 待处理: 'PENDING_PROCESSING', 处理中: 'PROCESSING', 已完成: 'COMPLETED', 已驳回: 'REJECTED', 已取消: 'CANCELLED' } return codes[String(value || '')] || String(value || 'PENDING_PROCESSING') } +function walletStatus(value: unknown) { return ({ FREEZE_PENDING: '冻结中', FROZEN: '已冻结', SETTLE_PENDING: '结算中', SETTLED: '已结算', RELEASE_PENDING: '释放中', RELEASED: '已释放', FREEZE_FAILED: '冻结失败', SETTLE_FAILED: '结算失败', RELEASE_FAILED: '释放失败' } as Record)[String(value || '')] || String(value || '—') } function canAudit(source: object) { const row = source as RelabelOrderListItem return ['PENDING_PROCESSING', 'PROCESSING'].includes(relabelStatusCode(row.status)) @@ -159,7 +162,7 @@ async function load(model: QueryModel = filters.value) { const result = await listRelabelOrders({ page: page.value, size: pageSize.value, relabelNo: String(filters.value.relabelNo || '').trim() || undefined, skuCode: String(filters.value.sku || '').trim() || undefined, status: String(filters.value.status || '').trim() || undefined }) rows.value = result.records.map((item) => ({ customerId: '', orderNo: String(item.relabelNo), relatedOrderNo: String(item.lastlegOrderNo || item.customerOrderNo || '—'), sku: String(item.skuCodes || '—'), - productName: '—', cartons: Number(item.totalPackageCount || 0), budgetTotal: null, labelRequirement: '—', fileName: '—', status: relabelStatus(item.status), remark: '', + productName: '—', cartons: Number(item.totalPackageCount || 0), budgetTotal: null, relabelCost: Number(item.chargeAmount || 0), walletStatus: walletStatus(item.walletStatus), walletErrorMessage: item.walletErrorMessage || '', labelRequirement: '—', fileName: '—', status: relabelStatus(item.status), remark: '', createdAt: formatOverseasDateTime(item.createdAt), })) total.value = result.total @@ -199,7 +202,7 @@ watch(dialogOpen, (open) => { if (!open) createIdempotencyKey.value = '' }) - + diff --git a/src/modules/overseas-warehouse/pages/prototypeContract.test.ts b/src/modules/overseas-warehouse/pages/prototypeContract.test.ts index 000987a0..9b4aa871 100644 --- a/src/modules/overseas-warehouse/pages/prototypeContract.test.ts +++ b/src/modules/overseas-warehouse/pages/prototypeContract.test.ts @@ -357,7 +357,7 @@ describe('overseas warehouse first-batch prototype contract', () => { 'inTransit', 'updatedAt', ]) expect(columnKeys('RelabelOrdersView.vue')).toEqual([ - 'actions', 'orderNo', 'sku', 'productName', 'cartons', 'budgetTotal', 'labelRequirement', 'fileName', 'status', 'createdAt', + 'actions', 'orderNo', 'sku', 'productName', 'cartons', 'budgetTotal', 'relabelCost', 'walletStatus', 'labelRequirement', 'fileName', 'status', 'createdAt', ]) expect(columnKeys('PalletsView.vue')).toEqual([ 'actions', 'code', 'type', 'warehouse', 'volume', 'purchasePrice', 'fbaWarehouse', 'status', 'remark', diff --git a/src/modules/overseas-warehouse/types.ts b/src/modules/overseas-warehouse/types.ts index 41090933..57f5465e 100644 --- a/src/modules/overseas-warehouse/types.ts +++ b/src/modules/overseas-warehouse/types.ts @@ -124,6 +124,8 @@ export interface RelabelOrderListItem { budgetTotal: number | null labelRequirement: string relabelCost?: number + walletStatus?: string + walletErrorMessage?: string fileName: string status: string remark: string diff --git a/src/modules/warehouse-resources/pallets/api/platformPallets.ts b/src/modules/warehouse-resources/pallets/api/platformPallets.ts index 76d0b00b..3a60da25 100644 --- a/src/modules/warehouse-resources/pallets/api/platformPallets.ts +++ b/src/modules/warehouse-resources/pallets/api/platformPallets.ts @@ -21,6 +21,7 @@ export interface PlatformPalletPage { total: number pages: number } +export interface PlatformPalletSummary { totalCount: number; idleCount: number; occupiedCount: number } export interface PlatformPalletQuery { page: number @@ -61,6 +62,10 @@ export async function listPlatformPallets(query: PlatformPalletQuery) { pages: Number(result.pages), } } +export async function getPlatformPalletSummary(query: PlatformPalletQuery) { + const response = await http.get, ApiEnvelope>(`${basePath}/summary`, { params: { tenantId: platformResourceScope, warehouseId: query.warehouseId || undefined, palletCode: query.palletCode?.trim() || undefined, palletType: query.palletType?.trim() || undefined, page: 1, size: 1 } }) + const value = unwrapApiResponse(response); return { totalCount: Number(value.totalCount), idleCount: Number(value.idleCount), occupiedCount: Number(value.occupiedCount) } +} export async function getPlatformPallet( warehouseId: number | string, diff --git a/src/modules/warehouse-resources/pallets/pages/PalletsView.test.ts b/src/modules/warehouse-resources/pallets/pages/PalletsView.test.ts index d31f7406..d805e525 100644 --- a/src/modules/warehouse-resources/pallets/pages/PalletsView.test.ts +++ b/src/modules/warehouse-resources/pallets/pages/PalletsView.test.ts @@ -12,6 +12,7 @@ import QueryToolbar from '@/shared/components/QueryToolbar.vue' import { createPlatformPallet, getPlatformPallet, + getPlatformPalletSummary, listPlatformPallets, type PlatformPalletResource, } from '../api/platformPallets' @@ -22,6 +23,7 @@ const palletsSource = readFileSync(resolve(process.cwd(), 'src/modules/warehouse vi.mock('@/modules/warehouses', () => ({ listAllPlatformWarehouses: vi.fn() })) vi.mock('../api/platformPallets', () => ({ listPlatformPallets: vi.fn(), + getPlatformPalletSummary: vi.fn(), getPlatformPallet: vi.fn(), createPlatformPallet: vi.fn(), updatePlatformPallet: vi.fn(), @@ -134,6 +136,7 @@ describe('PalletsView', () => { document.body.innerHTML = '' vi.mocked(listAllPlatformWarehouses).mockReset().mockResolvedValue([warehouse, fbaWarehouse, overseasWarehouse]) vi.mocked(listPlatformPallets).mockReset().mockResolvedValue({ records: [pallet], page: 1, size: 20, total: 1, pages: 1 }) + vi.mocked(getPlatformPalletSummary).mockReset().mockResolvedValue({ totalCount: 1, idleCount: 1, occupiedCount: 0 }) vi.mocked(getPlatformPallet).mockReset().mockResolvedValue(pallet) vi.mocked(createPlatformPallet).mockReset().mockResolvedValue('20002') }) diff --git a/src/modules/warehouse-resources/pallets/pages/PalletsView.vue b/src/modules/warehouse-resources/pallets/pages/PalletsView.vue index c4bbe293..e5f0cc7e 100644 --- a/src/modules/warehouse-resources/pallets/pages/PalletsView.vue +++ b/src/modules/warehouse-resources/pallets/pages/PalletsView.vue @@ -16,9 +16,11 @@ import { createPlatformPallet, deletePlatformPallet, getPlatformPallet, + getPlatformPalletSummary, listPlatformPallets, updatePlatformPallet, type PlatformPalletResource, + type PlatformPalletSummary, type SavePlatformPalletRequest, } from '../api/platformPallets' @@ -32,6 +34,7 @@ const loadError = ref(null) const page = ref(1) const pageSize = ref(20) const total = ref(0) +const summary = ref({ totalCount: 0, idleCount: 0, occupiedCount: 0 }) const editorOpen = ref(false) const saving = ref(false) const deletingIds = ref(new Set()) @@ -187,16 +190,18 @@ async function loadPallets() { loadError.value = null loading.value = true try { - const result = await listPlatformPallets({ + const query = { page: page.value, size: pageSize.value, ...(appliedFilters.value.warehouseId ? { warehouseId: apiId(appliedFilters.value.warehouseId) } : {}), ...(appliedFilters.value.palletCode ? { palletCode: appliedFilters.value.palletCode } : {}), ...(appliedFilters.value.palletType ? { palletType: appliedFilters.value.palletType } : {}), - }) + } + const [result, summaryResult] = await Promise.all([listPlatformPallets(query), getPlatformPalletSummary(query)]) if (sequence !== palletSequence) return rows.value = result.records total.value = result.total + summary.value = summaryResult } catch (error) { if (sequence !== palletSequence) return loadError.value = getApiErrorDetails(error, '托盘列表加载失败,请稍后重试') @@ -339,6 +344,11 @@ onMounted(() => {