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 @@
-
+
+ 托盘总数{{ summary.totalCount }}闲置托盘{{ summary.idleCount }}占用托盘{{ summary.occupiedCount }}已报废{{ summary.scrappedCount }}
+
@@ -146,6 +151,7 @@ onMounted(loadWarehouses)
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(() => {
+
+ 托盘总数{{ summary.totalCount }}
+ 闲置托盘{{ summary.idleCount }}
+ 占用托盘{{ summary.occupiedCount }}
+
{