This commit is contained in:
2026-10-16 17:43:42 +08:00
parent af3cacd2db
commit fbaa48337d
5 changed files with 96 additions and 23 deletions
@@ -48,7 +48,7 @@ export interface TransferCreate {
deliveryAddress?: string; packageCount: number; volumeCbm: number; deliveryMethod?: string; estimatedArrivalAt?: string; estimatedShipAt?: string
fbaShipmentNo?: string; trackingNo?: string; fbaBoxNos?: string; remark?: string; needRelabel: boolean; idempotencyKey: string
labelDeadline?: string; pickupContactName?: string; pickupContactPhone?: string; relabelContactName?: string; relabelContactPhone?: string; relabelChargeAmount?: number; relabelDeliveryMethod?: RelabelDeliveryMethod
items: Array<{ skuCode?: string; productNameZh?: string; quantity: number; packageCount: number; shippingMark?: string }>
items: Array<{ skuCode?: string; productNameZh?: string; quantity: number; packageCount: number; shippingMark?: string; cartonMarks?: string[] }>
relabelItems?: Array<{ operationType: 'CARTON_LABEL' | 'SKU_LABEL' | 'CARTON_AND_SKU'; customerCartonMarks: string[]; originalSku?: string; newSku?: string; quantity: number; fbaCartonMark?: string; destinationWarehouseCode?: string; remark?: string }>
}
export interface RelabelCreate {
@@ -0,0 +1,47 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import ts from 'typescript'
const source = readFileSync(new URL('./transferCartonPrinting.ts', import.meta.url), 'utf8')
const js = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS } }).outputText
const exports = {}
new Function('exports', js)(exports)
const prepare = exports.prepareTransferCartonLabels
test('prints forecast counts independently of quantities or order fields and escapes cargo', () => {
const rows = [{ sku: 'A<script>', name: '货物 & 品名', expectedPackageCount: 12 }]
let next = 0
const html = prepare(rows, () => `BX-${++next}`)
assert.equal((html.match(/<section /g) || []).length, 12)
assert.match(html, /12 \/ 12/)
assert.match(html, /A&lt;script&gt;/)
assert.equal(rows[0].generatedCartonMarks.length, 12)
assert.equal(prepare(rows, () => { throw new Error('must retain labels') }), html)
})
test('adding boxes or rows keeps previously printed identities', () => {
const rows = [{ sku: 'A', name: '', expectedPackageCount: 2 }]
let next = 0
prepare(rows, () => `BX-${++next}`)
rows[0].expectedPackageCount = 3
rows.push({ sku: 'B', name: '', expectedPackageCount: 1 })
const html = prepare(rows, () => `BX-${++next}`)
assert.deepEqual(rows[0].generatedCartonMarks, ['BX-1', 'BX-2', 'BX-3'])
assert.match(html, /BX-4/)
})
test('invalid or excessive forecasts do not generate any labels', () => {
for (const count of [0, -1, 1.5, undefined, 5001]) {
const row = { sku: '', name: '', expectedPackageCount: count }
assert.throws(() => prepare([row]), /预计箱数/)
assert.equal(row.generatedCartonMarks, undefined)
}
})
test('print button cannot create, update or submit an order', () => {
const view = readFileSync(new URL('../views/WarehouseServiceView.vue', import.meta.url), 'utf8')
const handler = view.slice(view.indexOf('function printForecastCartons()'), view.indexOf('async function submit('))
assert.match(handler, /prepareTransferCartonLabels\(cargoRows.value\)/)
assert.doesNotMatch(handler, /createTransfer|updateTransfer|submitTransfer|getTransferCartonLabel|form\./)
assert.match(view, /cartonMarks: item.generatedCartonMarks/)
})
@@ -0,0 +1,27 @@
export interface PrintableCargoRow {
sku: string
name: string
expectedPackageCount?: number
generatedCartonMarks?: string[]
}
const escapeHtml = (value: string) => value.replace(/[&<>"']/g, char => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[char]!)
/** Generate labels in memory only. The later order request carries these same identities. */
export function prepareTransferCartonLabels(rows: PrintableCargoRow[], newMark: () => string = () => {
const bytes = crypto.getRandomValues(new Uint8Array(16))
return `BX-${Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('').toUpperCase()}`
}) {
const total = rows.reduce((sum, row) => sum + Number(row.expectedPackageCount || 0), 0)
if (!rows.length || rows.some(row => !Number.isSafeInteger(row.expectedPackageCount) || Number(row.expectedPackageCount) < 1) || total > 5000) {
throw new Error('请填写货物的预计箱数(正整数),单次最多打印 5000 箱')
}
let index = 0
const labels = rows.flatMap(row => {
const count = Number(row.expectedPackageCount)
const marks = row.generatedCartonMarks ||= []
while (marks.length < count) marks.push(newMark())
return marks.slice(0, count).map(mark => `<section class="label"><div class="title">仓储转运箱唛</div><div class="mark">${escapeHtml(mark)}</div><div>${++index} / ${total}</div><div>SKU:${escapeHtml(row.sku)}</div><div>${escapeHtml(row.name)}</div></section>`)
})
return `<!doctype html><html><head><meta charset="utf-8"><title>仓储转运箱唛</title><style>@page{size:100mm 100mm;margin:0}body{margin:0;font-family:Arial,sans-serif}.label{box-sizing:border-box;width:100mm;height:100mm;padding:10mm;break-after:page;display:flex;flex-direction:column;justify-content:center;gap:6mm;text-align:center;overflow-wrap:anywhere}.label:last-child{break-after:auto}.title{font-size:22px;font-weight:bold}.mark{font-size:23px;font-weight:bold}</style></head><body>${labels.join('')}</body></html>`
}
@@ -7,6 +7,7 @@ import QueryToolbar, { type QueryField, type QueryModel } from '@/components/com
import DataTable, { type DataTableColumn } from '@/components/common/DataTable.vue'
import type { TableAction } from '@/components/common/tableActions'
import { useAuthStore } from '@/modules/auth/public'
import { prepareTransferCartonLabels } from '../utils/transferCartonPrinting'
import CompletionConfirmationDialog from '../components/CompletionConfirmationDialog.vue'
import { returnRelabelCandidate } from '../utils/returnRelabelCandidates'
import {
@@ -35,7 +36,7 @@ type ServiceType = 'labeling' | 'transfer' | 'disposal' | 'relabel'
type PageMode = 'list' | 'create' | 'edit' | 'detail'
type LabelingMode = 'carton' | 'sku' | 'carton_and_sku'
type RelabelTemplateType = 'carton' | 'sku' | 'mixed'
interface CargoRow { discardCartonIds?: string[]; id: number; included?: boolean; sku: string; name: string; quantity: number; expectedPackageCount?: number; availableQuantity?: number; sourceOrderId?: string; sourceType?: 'DROPSHIP' | 'RETURN_CARTON' | 'RETURN_PARCEL' | 'INVENTORY'; sourceInventoryBalanceId?: string; sourceInventoryPositionId?: string; sourceInboundOrderId?: string; returnCartonId?: string; sourceInboundOrderNo?: string; sourceInboundCode?: string; sourceWarehouseName?: string; palletNo?: string; warehouseLocationCode?: string; fnsku: string; shipmentNo: string; newSkuCode: string; shippingMark: string; customerCartonMarks: string; cartonMark: string; fbaCartonMark: string; destinationWarehouseId: string; remark: string }
interface CargoRow { generatedCartonMarks?: string[]; discardCartonIds?: string[]; id: number; included?: boolean; sku: string; name: string; quantity: number; expectedPackageCount?: number; availableQuantity?: number; sourceOrderId?: string; sourceType?: 'DROPSHIP' | 'RETURN_CARTON' | 'RETURN_PARCEL' | 'INVENTORY'; sourceInventoryBalanceId?: string; sourceInventoryPositionId?: string; sourceInboundOrderId?: string; returnCartonId?: string; sourceInboundOrderNo?: string; sourceInboundCode?: string; sourceWarehouseName?: string; palletNo?: string; warehouseLocationCode?: string; fnsku: string; shipmentNo: string; newSkuCode: string; shippingMark: string; customerCartonMarks: string; cartonMark: string; fbaCartonMark: string; destinationWarehouseId: string; remark: string }
interface SkuRelabelRow { id: number; sourceRowId: number | null; cartonMark: string; originalSku: string; newSku: string; quantity: number; fbaCartonMark: string; shipmentNo: string; destinationWarehouseId: string; remark: string }
interface ImportedRelabelBinding extends SkuRelabelRow { templateType: RelabelTemplateType; selected: boolean; shipmentNo: string }
interface ServiceConfig { title: string; createTitle: string; numberLabel: string; emptyText: string; columns: string[] }
@@ -588,6 +589,7 @@ async function openEdit(transferNo: string) {
...createCargoRow(), sku: item.skuCode || '', name: item.productNameZh || '',
quantity: Number(item.quantity || 0), expectedPackageCount: Number(item.packageCount || 0), shippingMark: item.shippingMark || '',
customerCartonMarks: item.shippingMark || '',
generatedCartonMarks: (detail.cartons || []).filter(box => String(box.transferItemId) === String(item.id)).map(box => box.cartonMark),
}))
const bindings = detail.relabelBindings || []
if (bindings.length) {
@@ -1156,7 +1158,18 @@ watch([page, pageSize], ([nextPage, nextSize], [previousPage, previousSize]) =>
if (nextSize !== previousSize && nextPage !== 1) { page.value = 1; return }
void load()
})
async function submit(action: 'draft' | 'submit' | 'print' = 'submit') {
function printForecastCartons() {
if (saving.value) return
let html: string
try { html = prepareTransferCartonLabels(cargoRows.value) }
catch (error) { ElMessage.warning(error instanceof Error ? error.message : '箱唛生成失败'); return }
const printWindow = window.open('', '_blank')
if (!printWindow) { ElMessage.warning('请允许浏览器打开打印窗口'); return }
printWindow.opener = null
printWindow.document.open(); printWindow.document.write(html); printWindow.document.close()
printWindow.focus(); printWindow.print()
}
async function submit(action: 'draft' | 'submit' = 'submit') {
if (saving.value) return
const primaryWarehouse = props.service === 'relabel' ? form.sourceWarehouse : form.warehouse
const relabelRows = labelingMode.value === 'carton' ? cartonDetailRows.value : skuRelabelRows.value
@@ -1236,9 +1249,6 @@ async function submit(action: 'draft' | 'submit' | 'print' = 'submit') {
if (props.service !== 'transfer' || action === 'submit') {
try { await ElMessageBox.confirm(`确认提交${config.value.title}吗?`, '提交确认', { confirmButtonText: '提交', cancelButtonText: '取消' }) } catch { return }
}
const draftPrintWindow = action === 'print' ? window.open('', '_blank') : null
if (action === 'print' && !draftPrintWindow) { ElMessage.warning('请允许浏览器打开打印窗口'); return }
if (draftPrintWindow) draftPrintWindow.opener = null
saving.value = true
try {
const idempotencyKey = createIdempotencyKey()
@@ -1282,7 +1292,7 @@ async function submit(action: 'draft' | 'submit' | 'print' = 'submit') {
const transferRemark = [form.remark, form.relabel && form.requirement ? `换标要求:${form.requirement}` : ''].filter(Boolean).join(';')
const operationType = labelingMode.value === 'carton' ? 'CARTON_LABEL' : labelingMode.value === 'sku' ? 'SKU_LABEL' : 'CARTON_AND_SKU'
const selectedFba = fbaWarehouses.value.find((warehouse) => warehouse.warehouseCode === form.destination)
const payload: TransferCreate = { operatingWarehouseId: primaryWarehouse, destinationType: transferDestinationType.value, fbaWarehouseCode: transferDestinationType.value === 'FBA' ? form.destination || undefined : undefined, destinationWarehouseId: transferDestinationType.value === 'FBA' ? String(selectedFba?.id || '') || undefined : undefined, destinationWarehouseCode: transferDestinationType.value === 'FBA' ? form.destination || undefined : undefined, destinationWarehouseName: transferDestinationType.value === 'FBA' ? selectedFba?.warehouseName : undefined, privateDestinationAddress: transferDestinationType.value === 'PRIVATE' ? form.privateDestinationAddress : undefined, privateDestinationPostalCode: transferDestinationType.value === 'PRIVATE' ? form.privateDestinationPostalCode : undefined, privateDestinationContactName: transferDestinationType.value === 'PRIVATE' ? form.privateDestinationContact : undefined, privateDestinationContactPhone: transferDestinationType.value === 'PRIVATE' ? form.privateDestinationPhone : undefined, deliveryAddress: inventoryTransfer.value ? undefined : form.address, packageCount, volumeCbm: 0, deliveryMethod: inventoryTransfer.value ? undefined : form.deliveryMethod, estimatedArrivalAt: inventoryTransfer.value ? undefined : form.estimatedArrivalAt, estimatedShipAt: form.estimatedShipAt || undefined, fbaShipmentNo: transferDestinationType.value === 'FBA' ? form.fbaShipmentNo || undefined : undefined, trackingNo: !inventoryTransfer.value && form.deliveryMethod === 'CUSTOMER_DELIVERY' ? form.trackingNo || undefined : undefined, fbaBoxNos: customerCartonMarks.length ? customerCartonMarks.join(',') : undefined, remark: transferRemark || undefined, needRelabel: props.service === 'relabel' || form.relabel, labelDeadline: form.relabel ? form.labelDeadline : undefined, pickupContactName: !inventoryTransfer.value && form.deliveryMethod === 'WAREHOUSE_PICKUP' ? form.pickupContact : undefined, pickupContactPhone: !inventoryTransfer.value && form.deliveryMethod === 'WAREHOUSE_PICKUP' ? form.pickupPhone : undefined, relabelContactName: form.relabel ? form.relabelContact : undefined, relabelContactPhone: form.relabel ? form.relabelPhone : undefined, relabelDeliveryMethod: form.relabel && labelingMode.value !== 'sku' ? form.relabelDeliveryMethod as 'TRUCK' | 'EXPRESS' | 'AMAZON_PICKUP' | 'CUSTOMER_PICKUP' : undefined, idempotencyKey, items: cargoRows.value.map((item) => ({ skuCode: item.sku, productNameZh: item.name, quantity: item.quantity, packageCount: Number(item.expectedPackageCount), shippingMark: transferCustomerMarks(item).join(',') || undefined })), relabelItems: form.relabel ? transferRelabelRows.value.map((item) => ({ operationType, customerCartonMarks: transferCustomerMarks(item), originalSku: item.sku, newSku: item.newSkuCode || undefined, quantity: item.quantity, fbaCartonMark: item.fbaCartonMark || undefined, destinationWarehouseCode: transferRelabelDefaultDestination.value || undefined, remark: item.remark || undefined })) : undefined }
const payload: TransferCreate = { operatingWarehouseId: primaryWarehouse, destinationType: transferDestinationType.value, fbaWarehouseCode: transferDestinationType.value === 'FBA' ? form.destination || undefined : undefined, destinationWarehouseId: transferDestinationType.value === 'FBA' ? String(selectedFba?.id || '') || undefined : undefined, destinationWarehouseCode: transferDestinationType.value === 'FBA' ? form.destination || undefined : undefined, destinationWarehouseName: transferDestinationType.value === 'FBA' ? selectedFba?.warehouseName : undefined, privateDestinationAddress: transferDestinationType.value === 'PRIVATE' ? form.privateDestinationAddress : undefined, privateDestinationPostalCode: transferDestinationType.value === 'PRIVATE' ? form.privateDestinationPostalCode : undefined, privateDestinationContactName: transferDestinationType.value === 'PRIVATE' ? form.privateDestinationContact : undefined, privateDestinationContactPhone: transferDestinationType.value === 'PRIVATE' ? form.privateDestinationPhone : undefined, deliveryAddress: inventoryTransfer.value ? undefined : form.address, packageCount, volumeCbm: 0, deliveryMethod: inventoryTransfer.value ? undefined : form.deliveryMethod, estimatedArrivalAt: inventoryTransfer.value ? undefined : form.estimatedArrivalAt, estimatedShipAt: form.estimatedShipAt || undefined, fbaShipmentNo: transferDestinationType.value === 'FBA' ? form.fbaShipmentNo || undefined : undefined, trackingNo: !inventoryTransfer.value && form.deliveryMethod === 'CUSTOMER_DELIVERY' ? form.trackingNo || undefined : undefined, fbaBoxNos: customerCartonMarks.length ? customerCartonMarks.join(',') : undefined, remark: transferRemark || undefined, needRelabel: props.service === 'relabel' || form.relabel, labelDeadline: form.relabel ? form.labelDeadline : undefined, pickupContactName: !inventoryTransfer.value && form.deliveryMethod === 'WAREHOUSE_PICKUP' ? form.pickupContact : undefined, pickupContactPhone: !inventoryTransfer.value && form.deliveryMethod === 'WAREHOUSE_PICKUP' ? form.pickupPhone : undefined, relabelContactName: form.relabel ? form.relabelContact : undefined, relabelContactPhone: form.relabel ? form.relabelPhone : undefined, relabelDeliveryMethod: form.relabel && labelingMode.value !== 'sku' ? form.relabelDeliveryMethod as 'TRUCK' | 'EXPRESS' | 'AMAZON_PICKUP' | 'CUSTOMER_PICKUP' : undefined, idempotencyKey, items: cargoRows.value.map((item) => ({ skuCode: item.sku, productNameZh: item.name, quantity: item.quantity, packageCount: Number(item.expectedPackageCount), shippingMark: transferCustomerMarks(item).join(',') || undefined, cartonMarks: item.generatedCartonMarks?.slice(0, Number(item.expectedPackageCount)) })), relabelItems: form.relabel ? transferRelabelRows.value.map((item) => ({ operationType, customerCartonMarks: transferCustomerMarks(item), originalSku: item.sku, newSku: item.newSkuCode || undefined, quantity: item.quantity, fbaCartonMark: item.fbaCartonMark || undefined, destinationWarehouseCode: transferRelabelDefaultDestination.value || undefined, remark: item.remark || undefined })) : undefined }
if (props.service === 'transfer' && selectedTransferInventory.value) {
;(payload as TransferCreate).inboundCode = selectedTransferInventory.value
;(payload as TransferCreate).sourceLastlegOrderIds = selectedTransferSourceOrderIds.value
@@ -1292,17 +1302,6 @@ async function submit(action: 'draft' | 'submit' | 'print' = 'submit') {
: await createTransfer(payload)
const transferNo = String(created.transferNo || '')
if (!transferNo) throw new Error('转运草稿已保存,但未返回转运单号')
if (action === 'print') {
// 保存服务端草稿后打印同一套箱唛,后续编辑及正式提交保留原号。
editingTransferNo.value = transferNo
const label = await getTransferCartonLabel(transferNo)
if (draftPrintWindow) {
draftPrintWindow.document.open(); draftPrintWindow.document.write(label.printableHtml); draftPrintWindow.document.close()
draftPrintWindow.focus(); draftPrintWindow.print()
}
ElMessage.success('草稿已保存,正在打印箱唛;请继续填写并提交订单')
return
}
if (action === 'submit') {
const submitted = await submitTransfer(transferNo)
if (form.relabel) {
@@ -1329,7 +1328,7 @@ async function submit(action: 'draft' | 'submit' | 'print' = 'submit') {
}
ElMessage.success(props.service === 'transfer' ? '转运单草稿保存成功' : `${config.value.title}提交成功`)
await goToServiceList()
} catch (error) { draftPrintWindow?.close(); ElMessage.error(error instanceof Error ? error.message : '提交失败') }
} catch (error) { ElMessage.error(error instanceof Error ? error.message : '提交失败') }
finally { saving.value = false }
}
async function openDetail(value: Record<string, string>) {
@@ -1807,7 +1806,7 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
</tbody></table></div>
<div v-if="!transferSourceLoading && !transferSourceError && !transferSourceCartons.length" class="warehouse-table-scroll"><table class="warehouse-edit-table"><thead><tr><th>商品 SKU</th><th>中文品名</th><th>库存箱数</th><th>原目的仓</th></tr></thead><tbody><tr v-for="row in cargoRows" :key="row.id"><td>{{ row.sku || '—' }}</td><td>{{ row.name || '—' }}</td><td>{{ row.expectedPackageCount }}</td><td>{{ transferInventoryCandidates.filter(item => item.inboundCode === selectedTransferInventory && item.skuCode === row.sku).map(item => item.destinationFbaWarehouseCode || item.destinationFbaWarehouseName).filter(Boolean).join(' / ') || '未指定' }}</td></tr></tbody></table></div>
</section>
<div v-if="!inventoryTransfer && (props.service !== 'disposal' || !discardSelectedCandidate?.cartonSelectionSupported)" class="warehouse-section-heading" :class="{ 'warehouse-step-heading': props.service === 'transfer' }"><div><h3><span v-if="props.service === 'transfer'" class="warehouse-step-number">1</span>货物明细</h3></div><div v-if="props.service !== 'disposal'" class="warehouse-heading-actions"><el-button v-if="props.service === 'transfer'" size="small" :loading="saving" @click="submit('print')"><Printer :size="15" />打印唛头</el-button><el-button type="primary" size="small" @click="addCargoRow"><Plus :size="15" />添加行</el-button><el-button size="small" @click="cargoRows = []">清空</el-button></div></div>
<div v-if="!inventoryTransfer && (props.service !== 'disposal' || !discardSelectedCandidate?.cartonSelectionSupported)" class="warehouse-section-heading" :class="{ 'warehouse-step-heading': props.service === 'transfer' }"><div><h3><span v-if="props.service === 'transfer'" class="warehouse-step-number">1</span>货物明细</h3></div><div v-if="props.service !== 'disposal'" class="warehouse-heading-actions"><el-button v-if="props.service === 'transfer'" size="small" :loading="saving" @click="printForecastCartons"><Printer :size="15" />打印唛头</el-button><el-button type="primary" size="small" @click="addCargoRow"><Plus :size="15" />添加行</el-button><el-button size="small" @click="cargoRows = []">清空</el-button></div></div>
<div v-if="!inventoryTransfer && (props.service !== 'disposal' || !discardSelectedCandidate?.cartonSelectionSupported)" class="warehouse-table-scroll"><table class="warehouse-edit-table"><thead><tr><th>商品 SKU{{ props.service === 'transfer' ? '' : ' *' }}</th><th>中文品名{{ props.service === 'transfer' ? '' : ' *' }}</th><th v-if="props.service === 'disposal'">可用箱数</th><th v-if="props.service === 'relabel'">FNSKU / 箱唛</th><th v-if="props.service === 'transfer'">SKU 数量(件)*</th><th>{{ props.service === 'disposal' ? '本次废弃箱数' : '预计箱数' }} *</th><th v-if="props.service !== 'disposal'">操作</th></tr></thead><tbody><tr v-for="row in cargoRows" :key="row.id"><td><el-input v-model="row.sku" :readonly="props.service === 'disposal'" :placeholder="props.service === 'transfer' ? '选填' : '请输入商品 SKU'" /></td><td><el-input v-model="row.name" :readonly="props.service === 'disposal'" :placeholder="props.service === 'transfer' ? '选填' : '请输入中文品名'" /></td><td v-if="props.service === 'disposal'"><strong>{{ row.availableQuantity }} 箱</strong></td><td v-if="props.service === 'relabel'"><el-input v-model="row.shippingMark" placeholder="请输入FNSKU或箱唛" /></td><td v-if="props.service === 'transfer'"><el-input-number v-model="row.quantity" :min="1" :precision="0" controls-position="right" placeholder="请输入件数" /></td><td v-if="props.service === 'transfer'"><el-input-number v-model="row.expectedPackageCount" :min="1" :precision="0" controls-position="right" placeholder="请输入预计箱数" /></td><td v-else><el-input-number v-model="row.quantity" :disabled="props.service === 'disposal'" :min="props.service === 'disposal' ? 0 : 1" :max="row.availableQuantity" controls-position="right" placeholder="请输入箱数" /></td><td v-if="props.service !== 'disposal'"><button type="button" class="warehouse-delete" title="删除" @click="removeCargoRow(row.id)"><Trash2 :size="16" />删除</button></td></tr><tr v-if="cargoRows.length === 0"><td :colspan="props.service === 'disposal' ? 4 : props.service === 'relabel' || props.service === 'transfer' ? 5 : 4"><div class="warehouse-empty">{{ props.service === 'disposal' ? '请先选择入库单,系统将自动带出 SKU 和可用箱数' : '暂无货物明细,请点击“添加行”' }}</div></td></tr></tbody></table></div>
</template>
</div>
@@ -102,12 +102,12 @@ test('仓储转运不要求客户填写内部入仓号和我方箱唛,但换
assert.doesNotMatch(view, /<th v-if="props\.service === 'transfer'">FNSKU \/ 箱唛<\/th>/)
})
test('仓储转运草稿及提交后统一打印服务端预报箱唛', () => {
test('仓储转运填写货物即可打印,保存或提交保留已打印箱号', () => {
assert.doesNotMatch(view, /printTransferDraftCartonMarks|CT-\$\{batch\}/)
assert.match(view, /getTransferCartonLabel\(transferNo\)/)
assert.match(view, /打印预报箱唛/)
assert.match(view, /@click="submit\('print'\)"/)
assert.match(view, /editingTransferNo.value = transferNo/)
assert.match(view, /@click="printForecastCartons"/)
assert.match(view, /cartonMarks: item.generatedCartonMarks/)
})
test('仓储转运换标资料按步骤分组展示', () => {