From d45cd19361175f2ac31280e9688667b4618e1f71 Mon Sep 17 00:00:00 2001
From: liuyihe <2513762445@qq.com>
Date: Tue, 29 Sep 2026 20:52:34 +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/returnOrders.ts | 8 +-
.../views/ReturnOrdersView.vue | 101 +++++++++++++-----
2 files changed, 79 insertions(+), 30 deletions(-)
diff --git a/src/modules/overseas-warehouse/api/returnOrders.ts b/src/modules/overseas-warehouse/api/returnOrders.ts
index 5374106..45655aa 100644
--- a/src/modules/overseas-warehouse/api/returnOrders.ts
+++ b/src/modules/overseas-warehouse/api/returnOrders.ts
@@ -10,10 +10,12 @@ export interface ReturnOrderItem {
skuCode: string; productNameZh?: string; expectedQuantity: number; receivedQuantity: number
}
export interface ReturnReceipt {
- receiptNo: string; receiptBatchNo: string; palletCode: string; trackingNo?: string; startedBy: 'RETURN_NO' | 'SKU'
- receivedQuantity: number; operatorName?: string; signedAt: string
+ receiptNo: string; receiptBatchNo: string; palletCode?: string; trackingNo?: string; startedBy: 'RETURN_NO' | 'SKU'
+ receivedQuantity: number; operatorName?: string; signedAt: string; unitType: 'PALLET' | 'BOX' | 'PARCEL'; unitCode?: string; boxCount: number
+ items: Array<{ skuCode: string; productNameZh?: string; quantity: number; boxCount: number }>; cartonMark?: string; printableHtml?: string
+ labels?: Array<{ cartonMark: string; printableHtml: string }>
}
-export interface ReturnPallet { palletCode: string; skuTypes: number; totalQuantity: number; items: Array<{ skuCode: string; productNameZh?: string; quantity: number }> }
+export interface ReturnPallet { palletCode: string; unitType: 'PALLET' | 'BOX'; unitCode: string; skuTypes: number; totalQuantity: number; totalBoxCount: number; items: Array<{ skuCode: string; productNameZh?: string; quantity: number; boxCount: number }> }
export interface ReturnPhoto { fileId: number; fileName: string; contentType: string; sizeBytes: number }
export interface ReturnOrderDetail { order: ReturnOrderSummary; remark?: string; items: ReturnOrderItem[]; receipts: ReturnReceipt[]; pallets: ReturnPallet[]; photos: ReturnPhoto[] }
export interface ReturnOrderCreate {
diff --git a/src/modules/overseas-warehouse/views/ReturnOrdersView.vue b/src/modules/overseas-warehouse/views/ReturnOrdersView.vue
index cda9654..8a049cf 100644
--- a/src/modules/overseas-warehouse/views/ReturnOrdersView.vue
+++ b/src/modules/overseas-warehouse/views/ReturnOrdersView.vue
@@ -10,6 +10,7 @@ import { useAuthStore } from '@/modules/auth/public'
import { getMerchantWarehouses, type MerchantWarehouse } from '@/modules/first-leg/public'
import { createIdempotencyKey } from '@/utils/idempotencyKey'
import { createReturnOrder, downloadReturnPhoto, getReturnOrder, listReturnOrders, updateReturnOrder, type ReturnOrderDetail, type ReturnOrderSummary, type ReturnReceipt } from '../api/returnOrders'
+import { getReturnInventoryLabel } from '../api/returnInventory'
const props = withDefaults(defineProps<{ createPage?: boolean; detailPage?: boolean; returnNo?: string }>(), { createPage: false, detailPage: false, returnNo: '' })
const router = useRouter()
@@ -54,19 +55,21 @@ const tableColumns: DataTableColumn[] = [
{ key: 'createdAt', label: '创建时间', slotName: 'createdAt', width: 180 },
]
const itemColumns: DataTableColumn[] = [
- { prop: 'skuCode', label: 'SKU', minWidth: 190 },
- { prop: 'productNameZh', label: '中文品名', minWidth: 220 },
- { prop: 'expectedQuantity', label: '预报数量', width: 110 },
- { prop: 'receivedQuantity', label: '实收数量', width: 110 },
+ { prop: 'skuCode', label: 'SKU', minWidth: 150 },
+ { prop: 'productNameZh', label: '中文品名', minWidth: 180 },
+ { prop: 'expectedQuantity', label: '预报数量', width: 100 },
+ { prop: 'receivedQuantity', label: '实收数量', width: 100 },
]
const receiptColumns: DataTableColumn[] = [
- { prop: 'receiptBatchNo', label: '批次号', minWidth: 170 },
- { prop: 'receiptNo', label: '签收单号', minWidth: 170 },
- { prop: 'palletCode', label: '托盘码', minWidth: 190 },
- { key: 'trackingNo', label: '快递单号', slotName: 'trackingNo', minWidth: 160 },
- { key: 'startedBy', label: '开始方式', slotName: 'startedBy', width: 110 },
- { prop: 'receivedQuantity', label: '实收', width: 90 },
- { key: 'signedAt', label: '签收时间', slotName: 'signedAt', minWidth: 180 },
+ { prop: 'receiptBatchNo', label: '批次号', minWidth: 145 },
+ { prop: 'receiptNo', label: '签收单号', minWidth: 145 },
+ { key: 'unitType', label: '实物类型', slotName: 'unitType', width: 90 },
+ { prop: 'unitCode', label: '实物编码', minWidth: 160 },
+ { key: 'trackingNo', label: '快递单号', slotName: 'trackingNo', minWidth: 140 },
+ { key: 'startedBy', label: '开始方式', slotName: 'startedBy', width: 100 },
+ { prop: 'receivedQuantity', label: '实收', width: 80 },
+ { prop: 'boxCount', label: '箱数', width: 70 },
+ { key: 'signedAt', label: '签收时间', slotName: 'signedAt', minWidth: 165 },
]
interface SkuDraft { id: number; skuCode: string; productNameZh: string; expectedQuantity: number }
let skuSequence = 0
@@ -172,6 +175,35 @@ function previewPhoto(fileId: string | number) {
const url = photoUrls[String(fileId)]
if (url) window.open(url, '_blank', 'noopener')
}
+async function printReturnLabel(receipt: ReturnReceipt) {
+ if (!receipt.cartonMark) return
+ const popup = window.open('', '_blank')
+ if (!popup) { ElMessage.warning('请允许浏览器打开打印窗口'); return }
+ popup.opener = null
+ try {
+ const labels = receipt.labels?.length
+ ? receipt.labels
+ : [{ cartonMark: receipt.cartonMark, printableHtml: receipt.printableHtml || (await getReturnInventoryLabel(receipt.cartonMark)).printableHtml }]
+ const printableHtml = labels.length === 1
+ ? labels[0].printableHtml
+ : `
退货箱唛${labels.map((label) => {
+ const html = label.printableHtml || ''
+ const bodyStart = html.indexOf('', bodyStart) + 1
+ const bodyEnd = html.lastIndexOf('')
+ const content = contentStart > 0 && bodyEnd > contentStart ? html.slice(contentStart, bodyEnd) : html
+ return ``
+ }).join('')}`
+ popup.document.open()
+ popup.document.write(printableHtml)
+ popup.document.close()
+ popup.focus()
+ popup.print()
+ } catch (error) {
+ popup.close()
+ ElMessage.error(error instanceof Error ? error.message : '箱唛加载失败')
+ }
+}
function openDetail(returnNo: string) { void router.push({ name: 'overseas-return-relabel-detail', params: { returnNo } }) }
function returnToList() { void router.push({ name: 'overseas-return-relabel' }) }
function cancelCreate() { resetForm(); void router.push({ name: 'overseas-return-relabel' }) }
@@ -187,7 +219,8 @@ async function submit() {
creating.value = true
try {
const payload = { overseasWarehouseId: form.overseasWarehouseId, warehouseContactName: form.warehouseContactName.trim(), warehouseContactPhone: form.warehouseContactPhone.trim(), customerReferenceNo: form.customerReferenceNo || undefined, remark: form.remark || undefined, needRelabel: form.needRelabel, idempotencyKey: createIdempotencyKey(), items: items.map(item => ({ ...item, productNameZh: item.productNameZh || undefined })) }
- await (editingReturnNo.value ? updateReturnOrder(editingReturnNo.value, payload) : createReturnOrder(payload))
+ if (editingReturnNo.value) await updateReturnOrder(editingReturnNo.value, payload)
+ else await createReturnOrder(payload)
ElMessage.success(editingReturnNo.value ? '退货预报已更新' : '退货预报已创建'); showCreate.value = false; editingReturnNo.value = ''; resetForm(); await router.push({ name: 'overseas-return-relabel' })
} finally { creating.value = false }
}
@@ -315,9 +348,9 @@ onBeforeUnmount(clearPhotoUrls)
换标子单({{ selected.order.relabelApplications?.length || 0 }})
-
- {{ relabelStatusLabel(row.status) }}
-
+
+ {{ relabelStatusLabel(row.status) }}
+
@@ -325,14 +358,15 @@ onBeforeUnmount(clearPhotoUrls)
- 打托明细({{ selected.pallets?.length || 0 }} 托)
+ 实物明细({{ selected.pallets?.length || 0 }} 个托盘/箱)打印全部箱唛({{ receipt.labels?.length || receipt.boxCount || 1 }} 箱)
- {{ pallet.palletCode }}{{ pallet.skuTypes }} 种 SKU · {{ pallet.totalQuantity }} 件
+ {{ pallet.unitType === 'BOX' ? '箱子' : '托盘' }} {{ pallet.unitCode || pallet.palletCode }}{{ pallet.skuTypes }} 种 SKU · {{ pallet.totalQuantity }} 件 · {{ pallet.totalBoxCount }} 箱
-
- {{ row.productNameZh || '—' }}
-
+
+ {{ row.productNameZh || '—' }}
+
+
@@ -352,10 +386,15 @@ onBeforeUnmount(clearPhotoUrls)
签收批次
+ {{ row.unitType === 'PALLET' ? '托盘' : row.unitType === 'BOX' ? '箱子' : '包裹' }}
{{ row.trackingNo || '无快递单号' }}
{{ startedByLabel(row.startedBy) }}
{{ formatDateTime(row.signedAt) }}
+
+ 包裹 {{ receipt.unitCode || receipt.trackingNo || receipt.receiptNo }} SKU 明细
+ {{ item.skuCode }} × {{ item.quantity }}
+
@@ -388,7 +427,7 @@ onBeforeUnmount(clearPhotoUrls)
.return-pagination-total { color:#526176; font-size:14px; }
.return-page-size-select { width:112px; margin-left:8px; }
.return-page-size-select :deep(.el-input__inner), .return-page-size-select :deep(.el-select__selected-item) { color:#526176 !important; opacity:1 !important; -webkit-text-fill-color:#526176; }
-.return-detail-surface { overflow:hidden; min-height:240px; border:1px solid var(--client-border); border-radius:var(--client-radius); background:#fff; }
+.return-detail-surface { width:min(100%, 1560px); margin:0; overflow:hidden; min-height:240px; border:1px solid var(--client-border); border-radius:8px; background:#fff; box-shadow:0 2px 10px rgba(31,45,69,.04); }
.return-detail-header { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center; gap:14px; min-height:64px; padding:10px 16px; border-bottom:1px solid var(--client-border); }
.return-back { display:inline-flex; align-items:center; gap:5px; padding:5px 0; border:0; background:transparent; color:var(--el-color-primary); cursor:pointer; }
.return-detail-title { display:flex; min-width:0; flex-direction:column; gap:3px; }
@@ -396,14 +435,22 @@ onBeforeUnmount(clearPhotoUrls)
.return-detail-title span { color:#7c899c; font-size:12px; }
.return-detail-actions { display:flex; align-items:center; gap:8px; }
.return-detail-section + .return-detail-section { border-top:1px solid var(--client-border); }
-.return-detail-section > h3 { min-height:40px; margin:0; padding:11px 16px 9px; color:#26364f; font-size:14px; }
+.return-detail-section > h3 { min-height:40px; margin:0; padding:9px 14px; color:#26364f; font-size:13px; }
+.section-heading-with-action { display:flex; align-items:center; justify-content:space-between; gap:12px; }
+.return-label-actions { display:flex; align-items:center; gap:6px; }
+.return-label-actions :deep(.el-button) { height:28px; margin:0; padding:0 12px; border-radius:6px; font-size:12px; box-shadow:0 2px 5px rgba(33,103,232,.14); }
.return-detail-grid { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); border-top:1px solid #edf1f5; }
-.return-detail-grid div { display:flex; min-width:0; min-height:48px; align-items:center; gap:10px; padding:8px 14px; border-right:1px solid #edf1f5; border-bottom:1px solid #edf1f5; }
-.return-detail-grid span,.return-detail-remark span { flex:0 0 92px; color:#8490a1; font-size:12px; }
-.return-detail-grid strong,.return-detail-remark strong { min-width:0; overflow-wrap:anywhere; color:#2c3c55; font-size:13px; }
-.return-detail-remark { display:flex; align-items:flex-start; gap:10px; padding:10px 14px; background:#f8fafc; }
+.return-detail-grid div { display:flex; min-width:0; min-height:40px; align-items:center; gap:6px; padding:6px 10px; border-right:1px solid #edf1f5; border-bottom:1px solid #edf1f5; }
+.return-detail-grid span,.return-detail-remark span { flex:0 0 82px; color:#8490a1; font-size:12px; }
+.return-detail-grid strong,.return-detail-remark strong { min-width:0; overflow-wrap:anywhere; color:#2c3c55; font-size:12px; }
+.return-detail-remark { display:flex; align-items:flex-start; gap:6px; padding:8px 10px; background:#f8fafc; }
.return-detail-section :deep(.data-table) { border:0; border-radius:0; }
-.return-pallet-list { padding:0 16px 12px; }
+.return-detail-section :deep(.el-table) { font-size:12px; }
+.return-detail-section :deep(.el-table th.el-table__cell) { height:34px; padding:0; }
+.return-detail-section :deep(.el-table td.el-table__cell) { height:36px; padding:0; }
+.return-detail-section :deep(.el-table .cell) { padding:0 8px; line-height:18px; }
+.return-detail-section :deep(.el-table .el-tag) { font-size:12px; }
+.return-pallet-list { padding:0 14px 10px; }
.return-pallet-summary { margin-left:12px; color:#7c899c; font-size:12px; font-weight:400; }
.return-photo-grid { display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:12px; padding:0 16px 16px; }
.return-photo { min-width:0; overflow:hidden; padding:0; border:1px solid #dfe6ef; border-radius:6px; color:#526176; background:#fff; cursor:pointer; }