From 409d8b3f57d48f81440f9b834a1d9f1b72639f40 Mon Sep 17 00:00:00 2001 From: liuyihe <2513762445@qq.com> Date: Wed, 23 Sep 2026 11:25:23 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B4=B4=E6=A0=87=E4=BC=98=E5=8C=960923?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../overseas-warehouse/api/lastMileOrders.ts | 9 ++++++++ .../overseas-warehouse/api/relabelOrders.ts | 6 ++++- .../pages/LastMileOrdersView.vue | 23 ++++++++++++++++++- .../pages/RelabelOrderDetailView.test.ts | 9 ++++++++ .../pages/RelabelOrderDetailView.vue | 18 ++++++++++++--- .../pages/RelabelOrdersView.vue | 6 +++-- src/modules/overseas-warehouse/types.ts | 1 + 7 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/modules/overseas-warehouse/api/lastMileOrders.ts b/src/modules/overseas-warehouse/api/lastMileOrders.ts index 76c1161e..d5136bee 100644 --- a/src/modules/overseas-warehouse/api/lastMileOrders.ts +++ b/src/modules/overseas-warehouse/api/lastMileOrders.ts @@ -374,6 +374,15 @@ export async function reviewReturnOrder(returnNo: string, customerId: string, de return unwrapApiResponse(response) } +export async function reviewDirectRelabelOrder(orderId: string, customerId: string, decision: 'APPROVE' | 'REJECT', remark?: string) { + const response = await overseasHttp.put, ApiEnvelope>( + `/platform/v1/last-leg/orders/${encodeURIComponent(orderId)}/relabel-review`, + { status: decision === 'APPROVE' ? 'PENDING_PROCESSING' : 'REJECTED', remark: remark?.trim() || undefined }, + { params: { customerId } }, + ) + return unwrapApiResponse(response) +} + export async function downloadLastMileOrders(query: Partial) { const { page: _page, size: _size, ...filters } = query const params = Object.fromEntries( diff --git a/src/modules/overseas-warehouse/api/relabelOrders.ts b/src/modules/overseas-warehouse/api/relabelOrders.ts index 1a5b747b..a79aa178 100644 --- a/src/modules/overseas-warehouse/api/relabelOrders.ts +++ b/src/modules/overseas-warehouse/api/relabelOrders.ts @@ -30,7 +30,9 @@ export interface CreateRelabelItemPayload { export interface CreateRelabelPayload { customerOrderNo: string - overseasWarehouseId: string + operationWarehouseId?: string + /** @deprecated compatibility alias for older platform fixtures */ + overseasWarehouseId?: string labelDeadline: string contactName: string contactPhone: string @@ -78,6 +80,8 @@ export interface RelabelSummaryDto { fulfillmentStatus?: string | null outboundOrderNo?: string | null customerConfirmedAt?: string | null + reviewChannel?: 'TAIL_ORDER' | 'RELABEL_ORDER' | null + reviewRemark?: string | null createdAt?: string | null } diff --git a/src/modules/overseas-warehouse/pages/LastMileOrdersView.vue b/src/modules/overseas-warehouse/pages/LastMileOrdersView.vue index d231dcea..71c1b291 100644 --- a/src/modules/overseas-warehouse/pages/LastMileOrdersView.vue +++ b/src/modules/overseas-warehouse/pages/LastMileOrdersView.vue @@ -15,7 +15,7 @@ import LoadErrorState from '@/shared/components/LoadErrorState.vue' import OperationErrorToast from '@/shared/components/OperationErrorToast.vue' import QueryToolbar, { type QueryField, type QueryModel } from '@/shared/components/QueryToolbar.vue' import RowActions, { type RowActionItem } from '@/shared/components/RowActions.vue' -import { changeContainerReceivingWarehouse, convertLastMileContainerToInboundPlans, createManualInboundPlan, downloadLastMileOrders, getContainerInboundPlanPreview, getContainerPlanProgress, getLastMileOrderManualNodes, listContainerPlanProgress, listLastMileOrders, reviewReturnOrder, transferLastMileOrders, updateLastMileOrderHold, updateLastMileOrderManualNodes, type ContainerInboundPlanPreviewDto, type ContainerInboundPlanPreviewGroupDto, type ContainerInboundPlanPreviewOrderDto, type ContainerPlanProgressDetailDto, type ContainerPlanProgressSummaryDto, type ManualNodeCode } from '../api/lastMileOrders' +import { changeContainerReceivingWarehouse, convertLastMileContainerToInboundPlans, createManualInboundPlan, downloadLastMileOrders, getContainerInboundPlanPreview, getContainerPlanProgress, getLastMileOrderManualNodes, listContainerPlanProgress, listLastMileOrders, reviewDirectRelabelOrder, reviewReturnOrder, transferLastMileOrders, updateLastMileOrderHold, updateLastMileOrderManualNodes, type ContainerInboundPlanPreviewDto, type ContainerInboundPlanPreviewGroupDto, type ContainerInboundPlanPreviewOrderDto, type ContainerPlanProgressDetailDto, type ContainerPlanProgressSummaryDto, type ManualNodeCode } from '../api/lastMileOrders' import OrderReferenceText from '../components/OrderReferenceText.vue' import WarehouseTransferReviewDialog from '../components/WarehouseTransferReviewDialog.vue' import { confirmRelabelCompletion, getRelabelReviewTask, returnRelabelForRework } from '../api/relabelOrders' @@ -806,6 +806,25 @@ async function reviewReturn(source: object) { } else if (error !== 'close') ElMessage.error(getApiErrorDetails(error, '退货预报审核失败').message) } } +async function reviewDirectRelabel(source: object) { + const row = source as LastMileOrderListItem + if (!row.id || !row.customerId) return + try { + await ElMessageBox.confirm('审核通过后,贴标单将进入海外仓换标作业。', '贴标单审核', { confirmButtonText: '审核通过', cancelButtonText: '驳回', distinguishCancelAndClose: true }) + await reviewDirectRelabelOrder(row.id, row.customerId, 'APPROVE') + ElMessage.success('贴标单已审核通过') + await load() + } catch (error) { + if (error === 'cancel') { + try { + const { value } = await ElMessageBox.prompt('请填写驳回原因,客户可在贴标单详情查看并修改后重新提交。', '驳回贴标单', { inputType: 'textarea', inputValidator: (input) => Boolean(input?.trim()) || '请填写驳回原因', confirmButtonText: '确认驳回', cancelButtonText: '取消' }) + await reviewDirectRelabelOrder(row.id, row.customerId, 'REJECT', value) + ElMessage.success('贴标单已驳回') + await load() + } catch (rejectError) { if (rejectError !== 'cancel' && rejectError !== 'close') ElMessage.error(getApiErrorDetails(rejectError, '贴标单审核失败').message) } + } else if (error !== 'close') ElMessage.error(getApiErrorDetails(error, '贴标单审核失败').message) + } +} function canConfirmRelabel(row: { relabelStatusCode?: string }) { return row.relabelStatusCode === 'CUSTOMER_CONFIRM_PENDING' } function canReworkRelabel(row: { relabelStatusCode?: string }) { return ['CUSTOMER_CONFIRM_PENDING', 'PROCESSING'].includes(row.relabelStatusCode ?? '') } function hasRelabelActions(row: LastMileOrderListItem) { @@ -830,6 +849,7 @@ function lastMileRowActions(row: LastMileOrderListItem): RowActionItem[] { const inboundPlanCreated = hasInboundPlan(row) return [ { key: 'detail', label: '查看', priority: 500 }, + { key: 'review-direct-relabel', label: '审核', priority: 470, tone: 'warning', visible: access.can(permission.lastMileOrderTransfer) && row.orderTypeCode === 'RELABEL_APPLICATION' && row.currentStatusCode === 'PENDING_REVIEW' }, { key: 'review-return', label: '审核', priority: 460, tone: 'warning', visible: access.can(permission.returnOrderReview) && row.orderTypeCode === 'RETURN_RELABEL' && row.currentStatusCode === 'PENDING_REVIEW' }, { key: 'review', label: '审核', priority: 450, tone: 'warning', visible: access.can(permission.warehouseTransferApprove) && row.orderTypeCode === 'WAREHOUSE_TRANSFER' && row.currentStatusCode === 'PENDING_REVIEW' }, { key: 'inbound-plan', label: '生成入库计划', priority: 400, visible: access.can(permission.inboundOrderCreate) && !inboundPlanCreated }, @@ -841,6 +861,7 @@ function lastMileRowActions(row: LastMileOrderListItem): RowActionItem[] { } function handleLastMileRowAction(command: string, row: LastMileOrderListItem) { if (command === 'detail') openDetail(row) + else if (command === 'review-direct-relabel') void reviewDirectRelabel(row) else if (command === 'review-return') void reviewReturn(row) else if (command === 'review') openTransferReview(row) else if (command === 'inbound-plan') openInboundPlan(row) diff --git a/src/modules/overseas-warehouse/pages/RelabelOrderDetailView.test.ts b/src/modules/overseas-warehouse/pages/RelabelOrderDetailView.test.ts index 595a3caa..b4d8dd92 100644 --- a/src/modules/overseas-warehouse/pages/RelabelOrderDetailView.test.ts +++ b/src/modules/overseas-warehouse/pages/RelabelOrderDetailView.test.ts @@ -19,4 +19,13 @@ describe('RelabelOrderDetailView', () => { expect(source).toContain('value?.application.customerOrderNo || value?.application.lastlegOrderNo') expect(source).toContain('item.destinationFbaWarehouseCode || item.destinationWarehouseId') }) + + it('lets platform operators confirm completion or return PDA work for rework', () => { + expect(source).toContain('@click="confirmCompletion"') + expect(source).toContain('确认换标完成') + expect(source).toContain('@click="returnForRework"') + expect(source).toContain('退回重新贴标') + expect(source).toContain('await confirmRelabelCompletion(orderId.value)') + expect(source).toContain('await returnRelabelForRework(orderId.value') + }) }) diff --git a/src/modules/overseas-warehouse/pages/RelabelOrderDetailView.vue b/src/modules/overseas-warehouse/pages/RelabelOrderDetailView.vue index 7ff83847..1cbd6500 100644 --- a/src/modules/overseas-warehouse/pages/RelabelOrderDetailView.vue +++ b/src/modules/overseas-warehouse/pages/RelabelOrderDetailView.vue @@ -1,11 +1,11 @@