From f7328e7c607f409c6c92430646b26749c4235a02 Mon Sep 17 00:00:00 2001 From: liuyihe <2513762445@qq.com> Date: Mon, 31 Aug 2026 10:58:12 +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 --- .../overseas-warehouse/api/dispatches.ts | 33 +++++++++ .../overseas-warehouse/api/inboundOrders.ts | 1 + .../overseas-warehouse/api/inventory.ts | 6 +- .../overseas-warehouse/api/lastMileOrders.ts | 3 +- .../pages/DispatchDetailView.vue | 70 ++++++++++++++++++- .../pages/InboundOrdersView.vue | 11 ++- .../pages/InventoryTransactionsView.vue | 9 ++- .../pages/InventoryView.vue | 10 ++- .../pages/LastMileOrdersView.vue | 16 +++-- src/modules/overseas-warehouse/types.ts | 1 + 10 files changed, 140 insertions(+), 20 deletions(-) diff --git a/src/modules/overseas-warehouse/api/dispatches.ts b/src/modules/overseas-warehouse/api/dispatches.ts index 2812973b..98b64059 100644 --- a/src/modules/overseas-warehouse/api/dispatches.ts +++ b/src/modules/overseas-warehouse/api/dispatches.ts @@ -170,6 +170,39 @@ export async function releaseDispatchDraft(draftToken: string) { return unwrapApiResponse(response) } +export interface DispatchPdaPositionDto { id: number; balanceId?: number | null; availablePackages: number; allocatedPackages: number; pickedPackages: number } +export interface DispatchPdaItemDto { + dispatchItemId: number + palletNo?: string | null + destinationWarehouseCode?: string | null + destinationWarehouseName?: string | null + fbaWarehouseCode?: string | null + scheduledPackages: number + pickedPackages: number + positions: DispatchPdaPositionDto[] +} +export interface DispatchPdaTaskDto { id: number; taskNo: string; dispatchNo: string; vehiclePlate?: string | null; warehouseId?: string | null; status: string; items: DispatchPdaItemDto[] } +export interface DispatchPdaPushPayload { idempotencyKey: string; operatorId?: string; operatorName?: string; warehouseId?: string } +export interface DispatchPdaPickPayload { positionId: number; packages: number; productQuantity: number; idempotencyKey: string; remark?: string; warehouseId?: string } +export interface DispatchPdaCompletePayload { idempotencyKey: string; reason: string } + +export async function pushDispatchPda(dispatchNo: string, payload: DispatchPdaPushPayload) { + const response = await overseasHttp.post, ApiEnvelope>(`/pda/v1/last-leg/dispatches/${encodeURIComponent(dispatchNo)}/push`, payload) + return unwrapApiResponse(response) +} +export async function getDispatchPdaTask(dispatchNo: string) { + const response = await overseasHttp.get, ApiEnvelope>(`/pda/v1/last-leg/dispatches/${encodeURIComponent(dispatchNo)}`) + return unwrapApiResponse(response) +} +export async function pickDispatchPda(dispatchNo: string, itemId: number, payload: DispatchPdaPickPayload) { + const response = await overseasHttp.post, ApiEnvelope>(`/pda/v1/last-leg/dispatches/${encodeURIComponent(dispatchNo)}/items/${itemId}/pick`, payload) + return unwrapApiResponse(response) +} +export async function completeDispatchPda(dispatchNo: string, payload: DispatchPdaCompletePayload) { + const response = await overseasHttp.post, ApiEnvelope>(`/pda/v1/last-leg/dispatches/${encodeURIComponent(dispatchNo)}/complete`, payload) + return unwrapApiResponse(response) +} + export interface TransferDeliveryResult { dispatchNo: string; outboundNo: string; outboundStatus: string; deliveryNo: string; deliveryStatus: string; palletCount: number; packageCount: number; replayed: boolean } export interface TransferDeliveryPayload { idempotencyKey: string diff --git a/src/modules/overseas-warehouse/api/inboundOrders.ts b/src/modules/overseas-warehouse/api/inboundOrders.ts index 725a39ee..dfe433d1 100644 --- a/src/modules/overseas-warehouse/api/inboundOrders.ts +++ b/src/modules/overseas-warehouse/api/inboundOrders.ts @@ -40,6 +40,7 @@ export interface InboundOrderSummaryDto { expectedLoadingAt?: string | null dispatchStatus?: string | null createdAt?: string | null + version?: number | null } export interface BatchFbaAppointmentPayload { diff --git a/src/modules/overseas-warehouse/api/inventory.ts b/src/modules/overseas-warehouse/api/inventory.ts index 279fd444..8acf5105 100644 --- a/src/modules/overseas-warehouse/api/inventory.ts +++ b/src/modules/overseas-warehouse/api/inventory.ts @@ -117,7 +117,8 @@ export async function listInventoryBalances(query: InventoryBalanceQuery) { } export async function downloadInventoryBalances(query: Partial) { - return overseasHttp.get('/platform/v1/last-leg/inventory/balances/export', { params: query, responseType: 'blob' }) + const response = await overseasHttp.get('/platform/v1/last-leg/inventory/balances/export', { params: query, responseType: 'blob' }) + return response.data } export async function getInventorySummary(query: InventoryBalanceQuery) { @@ -165,5 +166,6 @@ export async function listInventoryLedger(query: InventoryLedgerQuery) { } export async function downloadInventoryLedger(query: Partial) { - return overseasHttp.get('/platform/v1/last-leg/inventory/ledger/export', { params: query, responseType: 'blob' }) + const response = await overseasHttp.get('/platform/v1/last-leg/inventory/ledger/export', { params: query, responseType: 'blob' }) + return response.data } diff --git a/src/modules/overseas-warehouse/api/lastMileOrders.ts b/src/modules/overseas-warehouse/api/lastMileOrders.ts index 44fbff2b..0f181d14 100644 --- a/src/modules/overseas-warehouse/api/lastMileOrders.ts +++ b/src/modules/overseas-warehouse/api/lastMileOrders.ts @@ -188,7 +188,8 @@ export async function getLastMileOrder(orderId: string) { } export async function downloadLastMileOrders(query: Partial) { - return overseasHttp.get('/platform/v1/last-leg/orders/export', { params: query, responseType: 'blob' }) + const response = await overseasHttp.get('/platform/v1/last-leg/orders/export', { params: query, responseType: 'blob' }) + return response.data } export async function createLastMileOrderFee(orderId: string, payload: CreateLastMileFeePayload) { diff --git a/src/modules/overseas-warehouse/pages/DispatchDetailView.vue b/src/modules/overseas-warehouse/pages/DispatchDetailView.vue index c4573990..b0cf39b6 100644 --- a/src/modules/overseas-warehouse/pages/DispatchDetailView.vue +++ b/src/modules/overseas-warehouse/pages/DispatchDetailView.vue @@ -1,11 +1,12 @@ @@ -139,6 +181,20 @@ onMounted(load)

单据信息

{{ field.label }}
{{ field.value }}

关联订单明细

+
+

PDA拣货与库存扣减

启动拣货刷新任务
+ +
+
任务号:{{ pdaTask.taskNo }} 状态:{{ dispatchStatus(pdaTask.status) }} 仓库:{{ text(pdaTask.warehouseId) }}
+ + + + +
拣货明细:{{ pickForm.itemId || '未选择' }}拣货并扣减库存
+
完成拣货
+
+ +
@@ -158,10 +214,22 @@ onMounted(load) .detail-fields dd { margin: 0; color: var(--cc-text); font-weight: 600; overflow-wrap: anywhere; } .order-section { overflow: hidden; border: 1px solid var(--cc-border); border-top: 0; background: #fff; } .order-section h2 { margin-right: 4px; margin-left: 4px; } +.pda-section { border: 1px solid var(--cc-border); border-top: 0; background: #fff; } +.pda-header { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; } +.pda-header h2 { margin-bottom: 0; } +.pda-actions, .pick-form, .pda-complete { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } +.pda-actions :deep(.el-input) { width: 230px; } +.pda-alert { margin: 12px 0; } +.pda-meta { margin: 12px 0; color: var(--cc-muted); } +.pick-form { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--cc-border); } +.pick-form :deep(.el-input), .pda-complete :deep(.el-input) { width: 220px; } +.pick-label { color: var(--cc-muted); } +.pda-complete { justify-content: flex-end; margin-top: 12px; } @media (max-width: 960px) { .summary-grid { grid-template-columns: 1fr; } .summary-grid > .detail-section, .summary-grid > .detail-section:nth-child(2) { border-right: 0; } .document-section { grid-column: auto; } .detail-fields > div { grid-template-columns: 132px minmax(0, 1fr); } + .pda-actions { width: 100%; } } diff --git a/src/modules/overseas-warehouse/pages/InboundOrdersView.vue b/src/modules/overseas-warehouse/pages/InboundOrdersView.vue index 9eccc863..d5c84244 100644 --- a/src/modules/overseas-warehouse/pages/InboundOrdersView.vue +++ b/src/modules/overseas-warehouse/pages/InboundOrdersView.vue @@ -14,7 +14,8 @@ 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 SmartDispatchDialog from '../components/SmartDispatchDialog.vue' -import { createFbaAppointment, getFbaAppointment, listInboundOrders, updateFbaAppointment, type InboundOrderSummaryDto } from '../api/inboundOrders' +import { createFbaAppointment, getFbaAppointment, listInboundOrders, updateFbaAppointment, updateInboundPlan, type InboundOrderSummaryDto } from '../api/inboundOrders' +import { listReceivingOverseasWarehouses, type ReceivingOverseasWarehouseDto } from '../api/pallets' import { overseasWarehousePermissions as permission } from '../permissions' import { formatOverseasDateTime, lastMileOrderTypeLabel } from '../lastMilePresentation' import type { InboundOrderListItem } from '../types' @@ -52,6 +53,7 @@ const appointmentSelectedCount = ref(0) const appointmentFbaWarehouse = ref('—') const appointmentForm = reactive({ appointmentNo: '', appointmentAt: '', expectedLoadingAt: '', appointmentStatus: 'SUCCESS' as 'PENDING' | 'SUCCESS' | 'FAILED' | 'CANCELLED', appointmentRemark: '' }) const appointmentTitle = computed(() => appointmentMode.value === 'edit' ? '编辑预约' : '预约') +const planEditOpen = ref(false); const planEditLoading = ref(false); const planEditSaving = ref(false); const planEditTarget = ref(null); const planEditForm = reactive({ warehouseId: '', warehouseCode: '', warehouseName: '', version: 0 }); const warehouses = ref([]) function selectableRow(source: object) { const row = source as InboundOrderListItem @@ -93,7 +95,7 @@ function toListItem(dto: InboundOrderSummaryDto): InboundOrderListItem { fbaWarehouse: text(dto.fbaWarehouseCode), owner: text(dto.customerName), cartons: number(dto.packageCount), pickedCartons: number(dto.pickedPackageCount), volume: number(dto.volumeCbm), pallets: number(dto.palletCount), status: planStatusLabel(dto.status), appointmentNo: text(dto.appointmentNo), appointmentStatus: appointmentStatusLabel(dto.appointmentStatus), appointmentAt: formatOverseasDateTime(dto.appointmentAt), expectedLoadingAt: formatOverseasDateTime(dto.expectedLoadingAt), dispatchStatus: dto.dispatchStatus === 'SCHEDULED' ? '已排车' : '未排车', - createdAt: formatOverseasDateTime(dto.createdAt), + createdAt: formatOverseasDateTime(dto.createdAt), version: number(dto.version), } } @@ -168,6 +170,8 @@ async function load(model: QueryModel = filters.value) { function search(model: QueryModel) { page.value = 1; void load(model) } function reset(model: QueryModel) { page.value = 1; void load(model) } function openDetail(source: object) { const row = source as InboundOrderListItem; void router.push({ name: 'overseas-inbound-order-detail', params: { orderId: row.orderNo }, query: { customerId: row.customerId } }) } +async function openPlanEdit(source: object) { const row = source as InboundOrderListItem; planEditTarget.value = row; planEditOpen.value = true; planEditLoading.value = true; mutationError.value = null; try { Object.assign(planEditForm, { warehouseId: row.warehouseId, warehouseCode: row.destinationWarehouseCode === '—' ? '' : row.destinationWarehouseCode, warehouseName: row.warehouse, version: row.version }); if (!warehouses.value.length) warehouses.value = (await listReceivingOverseasWarehouses()).records } catch (error) { planEditOpen.value = false; mutationError.value = getApiErrorDetails(error, '海外仓列表加载失败,请重试') } finally { planEditLoading.value = false } } +async function submitPlanEdit() { const target = planEditTarget.value; if (!target || !planEditForm.warehouseId) { ElMessage.warning('请选择目的仓'); return } planEditSaving.value = true; mutationError.value = null; try { await updateInboundPlan(target.orderNo, { customerId: target.customerId, overseasWarehouseId: planEditForm.warehouseId, warehouseCode: planEditForm.warehouseCode, warehouseName: planEditForm.warehouseName, version: planEditForm.version }); planEditOpen.value = false; ElMessage.success('入库单已更新'); await load() } catch (error) { mutationError.value = getApiErrorDetails(error, '入库单更新失败,请重试') } finally { planEditSaving.value = false } } onMounted(() => { void load() }) @@ -177,7 +181,7 @@ onMounted(() => { void load() })
- +
@@ -194,6 +198,7 @@ onMounted(() => { void load() }) +
diff --git a/src/modules/overseas-warehouse/pages/InventoryTransactionsView.vue b/src/modules/overseas-warehouse/pages/InventoryTransactionsView.vue index c8927269..b1905c9b 100644 --- a/src/modules/overseas-warehouse/pages/InventoryTransactionsView.vue +++ b/src/modules/overseas-warehouse/pages/InventoryTransactionsView.vue @@ -1,12 +1,14 @@ diff --git a/src/modules/overseas-warehouse/pages/InventoryView.vue b/src/modules/overseas-warehouse/pages/InventoryView.vue index 2b89eb20..29c35b9b 100644 --- a/src/modules/overseas-warehouse/pages/InventoryView.vue +++ b/src/modules/overseas-warehouse/pages/InventoryView.vue @@ -1,6 +1,6 @@ @@ -87,7 +91,7 @@ onMounted(() => { void loadWarehouses(); void load() })
- +
diff --git a/src/modules/overseas-warehouse/pages/LastMileOrdersView.vue b/src/modules/overseas-warehouse/pages/LastMileOrdersView.vue index 1d5f906e..203f9e6c 100644 --- a/src/modules/overseas-warehouse/pages/LastMileOrdersView.vue +++ b/src/modules/overseas-warehouse/pages/LastMileOrdersView.vue @@ -1,6 +1,6 @@
- +
- - - + diff --git a/src/modules/overseas-warehouse/types.ts b/src/modules/overseas-warehouse/types.ts index 9de832a2..36bb2044 100644 --- a/src/modules/overseas-warehouse/types.ts +++ b/src/modules/overseas-warehouse/types.ts @@ -69,6 +69,7 @@ export interface InboundOrderListItem { expectedLoadingAt: string dispatchStatus: string createdAt: string + version: number } export interface DispatchListItem {