diff --git a/src/modules/overseas-warehouse/api/returnOrders.ts b/src/modules/overseas-warehouse/api/returnOrders.ts index c6fd930..5374106 100644 --- a/src/modules/overseas-warehouse/api/returnOrders.ts +++ b/src/modules/overseas-warehouse/api/returnOrders.ts @@ -2,6 +2,7 @@ import { overseasApiRequest, overseasBlobRequest } from '@/api/overseasHttp' import type { PageResult } from './warehouseServices' export interface ReturnOrderSummary { + relabelApplications?: Array<{ relabelNo: string; status: string; quantity: number; createdAt: string }> returnNo: string; overseasWarehouseId: string; warehouseContactName?: string; warehouseContactPhone?: string; customerReferenceNo?: string; status: string skuTypes: number; expectedQuantity: number; receivedQuantity: number; needRelabel: boolean; relabelNo?: string; relabelStatus?: string; createdAt: string } diff --git a/src/modules/overseas-warehouse/api/warehouseServices.ts b/src/modules/overseas-warehouse/api/warehouseServices.ts index c9e4656..c7b5122 100644 --- a/src/modules/overseas-warehouse/api/warehouseServices.ts +++ b/src/modules/overseas-warehouse/api/warehouseServices.ts @@ -32,7 +32,7 @@ export interface RelabelInventoryCandidate { availablePackages: number | string; version: number positions: Array<{ positionId: string; palletNo?: string; batchNo?: string; warehouseLocationCode?: string; availablePackages: number }> sourceType?: 'INBOUND' | 'RETURN_CARTON'; returnCartonId?: string; cartonMark?: string - skuItems?: Array<{ skuCode: string; newSkuCode?: string; quantity: number; remark?: string }> + skuItems?: Array<{ skuCode: string; newSkuCode?: string; quantity: number; remark?: string }>; relabelledPackages?: number } export interface TransferCreate { diff --git a/src/modules/overseas-warehouse/permissions.test.mjs b/src/modules/overseas-warehouse/permissions.test.mjs index cd405e3..da167aa 100644 --- a/src/modules/overseas-warehouse/permissions.test.mjs +++ b/src/modules/overseas-warehouse/permissions.test.mjs @@ -30,7 +30,7 @@ test('opens warehouse service and return receipt details as dedicated protected assert.match(routes, /path: 'overseas-warehouse\/return-relabel\/:returnNo\/detail'[\s\S]*?name: 'overseas-return-relabel-detail'[\s\S]*?props: \{ detailPage: true \}[\s\S]*?permission: 'merchant:overseas-warehouse:return-order:list'/) assert.match(routes, /path: 'overseas-warehouse\/storage-transfer\/:detailNo\/detail'[\s\S]*?name: 'overseas-storage-transfer-detail'[\s\S]*?props: \{ service: 'transfer', detailPage: true \}/) assert.match(routes, /path: 'overseas-warehouse\/cargo-disposal\/:detailNo\/detail'[\s\S]*?name: 'overseas-cargo-disposal-detail'[\s\S]*?props: \{ service: 'disposal', detailPage: true \}/) - assert.match(services, /name: 'overseas-labeling-detail'/) + assert.match(services, /labeling: 'overseas-labeling-detail'/) assert.match(services, /transfer: 'overseas-storage-transfer-detail'/) assert.match(services, /disposal: 'overseas-cargo-disposal-detail'/) assert.match(services, /class="warehouse-detail-error"/) @@ -38,6 +38,11 @@ test('opens warehouse service and return receipt details as dedicated protected assert.doesNotMatch(returns, /title="退货签收详情"/) }) +test('returns to the labeling list after a labeling application is submitted', () => { + assert.match(services, /ElMessage\.success\(`\$\{config\.value\.title\}提交成功`\)[\s\S]*?router\.push\(\{ name: 'overseas-labeling' \}\)/) + assert.match(services, /uploadReturnRelabelFile\([\s\S]*?router\.push\(\{ name: 'overseas-labeling' \}\)/) +}) + test('keeps overseas warehouse writes behind operation-specific button permissions', () => { for (const permission of [ 'merchant:overseas-warehouse:relabel:create', diff --git a/src/modules/overseas-warehouse/views/ReturnOrdersView.test.mjs b/src/modules/overseas-warehouse/views/ReturnOrdersView.test.mjs index 22d2073..be73e93 100644 --- a/src/modules/overseas-warehouse/views/ReturnOrdersView.test.mjs +++ b/src/modules/overseas-warehouse/views/ReturnOrdersView.test.mjs @@ -38,7 +38,7 @@ test('退货列表和详情使用客户可读仓库、状态与时间', () => { assert.match(view, /return warehouse \? warehouseLabel\(warehouse\) : '未关联'/) assert.match(view, /PENDING_RECEIPT: '待签收'/) assert.match(view, /PARTIALLY_RECEIVED: '部分签收'/) - assert.match(view, /return needRelabel \? '已完成入库\/待换标' : '已完成入库'/) + assert.match(view, /if \(status === 'COMPLETED'\) return '已完成入库'/) assert.match(view, /function formatDateTime\(value\?: string \| null\)/) assert.match(view, /\{\{ operationWarehouseLabel\(row\.overseasWarehouseId\) \}\}/) assert.match(view, /\{\{ statusLabel\(row\.status, row\.needRelabel\) \}\}/) @@ -72,9 +72,9 @@ test('退货签收详情使用独立路由和紧凑连续页面', () => { test('待审核退货可编辑,完成入库后从独立新增页发起贴标', () => { assert.match(view, /row\.status === 'PENDING_REVIEW'.*key: 'edit'/) assert.match(view, /await updateReturnOrder\(editingReturnNo\.value, payload\)/) - assert.match(view, /order\.status === 'COMPLETED' && order\.needRelabel === true && !order\.relabelNo/) + assert.match(view, /order\.status === 'COMPLETED' && order\.needRelabel === true/) assert.match(view, /name: 'overseas-labeling-create'/) assert.match(view, /query: \{ returnNo: order\.returnNo, warehouseId: String\(order\.overseasWarehouseId\) \}/) - assert.match(view, /prop: 'relabelNo', label: '换标单号'/) - assert.match(view, /label: '换标单状态'/) + assert.match(view, /v-for="application in row\.relabelApplications \|\| \[\]"/) + assert.match(view, /relabelStatusLabel\(application\.status\)/) }) diff --git a/src/modules/overseas-warehouse/views/ReturnOrdersView.vue b/src/modules/overseas-warehouse/views/ReturnOrdersView.vue index 01de759..e3bd54d 100644 --- a/src/modules/overseas-warehouse/views/ReturnOrdersView.vue +++ b/src/modules/overseas-warehouse/views/ReturnOrdersView.vue @@ -49,7 +49,7 @@ const tableColumns: DataTableColumn[] = [ { key: 'warehouse', label: '操作仓', slotName: 'warehouse', minWidth: 200 }, { key: 'relabel', label: '换标', slotName: 'relabel', width: 100 }, { prop: 'skuTypes', label: 'SKU 种类', width: 105 }, { prop: 'expectedQuantity', label: '预报数量', width: 110 }, { prop: 'receivedQuantity', label: '实收数量', width: 110 }, { key: 'status', label: '状态', slotName: 'status', width: 150 }, - { prop: 'relabelNo', label: '换标单号', minWidth: 190 }, { key: 'relabelStatus', label: '换标单状态', slotName: 'relabelStatus', width: 130 }, + { key: 'relabelApplications', label: '换标子单', slotName: 'relabelApplications', minWidth: 340 }, { key: 'createdAt', label: '创建时间', slotName: 'createdAt', width: 180 }, ] const itemColumns: DataTableColumn[] = [ @@ -176,7 +176,7 @@ function previewPhoto(fileId: string | number) { } function openDetail(returnNo: string) { void router.push({ name: 'overseas-return-relabel-detail', params: { returnNo } }) } function returnToList() { void router.push({ name: 'overseas-return-relabel' }) } -function canApplyRelabel(order: { status?: unknown; needRelabel?: unknown; relabelNo?: unknown }) { return order.status === 'COMPLETED' && order.needRelabel === true && !order.relabelNo } +function canApplyRelabel(order: { status?: unknown; needRelabel?: unknown; relabelNo?: unknown }) { return order.status === 'COMPLETED' && order.needRelabel === true } function openRelabel(order: Pick) { void router.push({ name: 'overseas-labeling-create', query: { returnNo: order.returnNo, warehouseId: String(order.overseasWarehouseId) } }) } @@ -193,15 +193,15 @@ async function submit() { } finally { creating.value = false } } function statusType(status: string, needRelabel = false) { - if (status === 'COMPLETED') return needRelabel ? 'warning' : 'success' + if (status === 'COMPLETED') return 'success' return ({ PENDING_REVIEW: 'warning', PENDING_RECEIPT: 'primary', PARTIALLY_RECEIVED: 'warning', REJECTED: 'danger', CANCELLED: 'info' } as Record)[status] || 'info' } function statusClass(status: string, needRelabel = false) { - if (status === 'COMPLETED') return needRelabel ? 'return-status--relabel' : 'return-status--completed' + if (status === 'COMPLETED') return 'return-status--completed' return `return-status--${status.toLowerCase().replaceAll('_', '-')}` } function statusLabel(status: string, needRelabel = false) { - if (status === 'COMPLETED') return needRelabel ? '已完成入库/待换标' : '已完成入库' + if (status === 'COMPLETED') return '已完成入库' return ({ PENDING_REVIEW: '待审核', PENDING_RECEIPT: '待签收', PARTIALLY_RECEIVED: '部分签收', REJECTED: '已驳回', CANCELLED: '已取消' } as Record)[status] ?? (status || '未知状态') } function relabelStatusType(status?: string) { @@ -238,7 +238,7 @@ onBeforeUnmount(clearPhotoUrls) - + @@ -277,8 +277,6 @@ onBeforeUnmount(clearPhotoUrls)
实收 / 预报{{ selected.order.receivedQuantity }} / {{ selected.order.expectedQuantity }}
SKU 种类{{ selected.order.skuTypes }}
是否换标{{ selected.order.needRelabel ? '需要换标' : '否' }}
-
换标单号{{ selected.order.relabelNo || '—' }}
-
换标单状态{{ relabelStatusLabel(selected.order.relabelStatus) }}
客户参考号{{ selected.order.customerReferenceNo || '—' }}
海外仓联系人{{ selected.order.warehouseContactName || '—' }}
联系电话{{ selected.order.warehouseContactPhone || '—' }}
@@ -286,6 +284,14 @@ onBeforeUnmount(clearPhotoUrls)
备注{{ selected.remark }}
+
+

换标子单({{ selected.order.relabelApplications?.length || 0 }})

+ + + + + +

预报与实收

@@ -329,6 +335,7 @@ onBeforeUnmount(clearPhotoUrls)