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 `
${content}
` + }).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 }})

- - - + + +
@@ -325,14 +358,15 @@ onBeforeUnmount(clearPhotoUrls)
-

打托明细({{ selected.pallets?.length || 0 }} 托)

+

实物明细({{ selected.pallets?.length || 0 }} 个托盘/箱)打印全部箱唛({{ receipt.labels?.length || receipt.boxCount || 1 }} 箱)

- + - - - + + + + @@ -352,10 +386,15 @@ onBeforeUnmount(clearPhotoUrls)

签收批次

+ +
+ 包裹 {{ 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; }