42 lines
4.2 KiB
Vue
42 lines
4.2 KiB
Vue
<template>
|
||
<view class="section-gap">
|
||
<text class="card-title">换标商品独立合箱</text><text class="hint">部分换标须装入独立新箱;来源余量留在原包裹/箱。所有来源登记成功后才可生成新箱。</text>
|
||
<view v-for="row in deductions" :key="`${row.cartonMark}:${row.skuCode}`" class="meta-line">{{ row.cartonMark }} · {{ row.skuCode }} · {{ row.quantity }} 件</view>
|
||
<template v-if="!facts.length"><view class="dimensions"><label v-for="field in fields" :key="field.key"><text>{{ field.label }}</text><input v-model="dimensions[field.key]" type="digit" placeholder="请输入" /></label></view><button class="primary" :disabled="!ready" @tap="pack">{{ busy ? '生成中…' : '生成独立新箱及标签' }}</button></template>
|
||
<view v-for="fact in facts" :key="fact.cartonMark"><text class="business-code">新箱:{{ fact.cartonMark }} · {{ fact.quantity }} 件</text><button class="secondary" @tap="print(fact.label)">查看 / 打印新箱识别标签</button><text class="hint">此标签用于识别仓库包裹/箱;商品目标 SKU 标签请按上方换标明细核对。</text></view>
|
||
</view>
|
||
</template>
|
||
<script>
|
||
import { packReturnRelabelCartons } from '@/api/relabel.js'
|
||
import { requiresReturnPack, returnDeductions, returnPackFacts, returnSelections } from '@/utils/return-relabel.js'
|
||
import { warehouseIdempotencyKey, releaseWarehouseKey } from '@/stores/warehouse-work.js'
|
||
export default {
|
||
props: { task: { type: Object, required: true }, locked: { type: Boolean, default: false } }, emits: ['saved', 'error', 'busy', 'refresh'],
|
||
data() { return { busy: false, dimensions: { length: '', width: '', height: '', weight: '' }, fields: [{ key: 'length', label: '长 cm' }, { key: 'width', label: '宽 cm' }, { key: 'height', label: '高 cm' }, { key: 'weight', label: '重量 kg' }] } },
|
||
computed: {
|
||
deductions() { return returnDeductions(this.task) }, facts() { return returnPackFacts(this.task) },
|
||
ready() { const items = returnSelections(this.task); return !this.locked && !this.busy && requiresReturnPack(this.task) && !this.facts.length && items.length === this.task.items.length && items.length > 0 && items.every(item => Number(item.relabelledPackages) === Number(item.expectedPackages) && !Number(item.skippedPackages) && !Number(item.exceptionPackages)) && this.deductions.every(row => row.cartonMark && row.skuCode && Number.isSafeInteger(row.quantity) && row.quantity > 0) && this.fields.every(field => Number.isFinite(Number(this.dimensions[field.key])) && Number(this.dimensions[field.key]) > 0) }
|
||
},
|
||
methods: {
|
||
async pack() {
|
||
if (!this.ready) return
|
||
const payload = { sourceCartonMarks: [...new Set(this.deductions.map(row => row.cartonMark))], targetCartonMark: null, sourceDeductions: this.deductions, ...Object.fromEntries(this.fields.map(field => [field.key, Number(this.dimensions[field.key])])) }
|
||
const key = warehouseIdempotencyKey('relabel', 'return-pack', { relabelNo: this.task.relabelNo, round: this.task.reviewedAt || 'INITIAL', ...payload })
|
||
this.busy = true; this.$emit('busy', true)
|
||
try { const result = await packReturnRelabelCartons(this.task.relabelNo, { ...payload, idempotencyKey: key.value }); releaseWarehouseKey('relabel', key.signature); this.$emit('saved', result) } catch (error) { if (['409', 'A0409', 'B0409'].includes(String(error?.code || ''))) { releaseWarehouseKey('relabel', key.signature); this.$emit('refresh') } this.$emit('error', error) } finally { this.busy = false; this.$emit('busy', false) }
|
||
},
|
||
print(label) {
|
||
// #ifdef H5
|
||
if (!label?.printableHtml) return this.$emit('error', new Error('服务端未返回可打印的新箱标签'))
|
||
const tab = window.open('', '_blank'); if (!tab) return this.$emit('error', new Error('请允许打开标签打印窗口'))
|
||
tab.document.write(label.printableHtml); tab.document.close(); tab.focus(); tab.print()
|
||
// #endif
|
||
// #ifndef H5
|
||
this.$emit('error', new Error('当前设备未配置打印服务,生成标签不代表打印完成'))
|
||
// #endif
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
<style scoped>.dimensions{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin:12px 0}.dimensions input{border:1px solid #cbd8e8;padding:8px}.hint,.meta-line{display:block;margin:8px 0}</style>
|