优化
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
<template><view class="work-card"><text>本地合箱接口验收</text><textarea aria-label="验收请求 JSON" v-model="request" style="height:160px;width:100%;border:1px solid #aaa"/><button @tap="run">执行 PDA 验收请求</button><text id="acceptance-result" style="white-space:pre-wrap;user-select:text">{{ result }}</text></view></template>
|
||||
<script>
|
||||
import { overseasRequest } from '@/api/overseas.js'
|
||||
export default {data(){return {request:'',result:''}},methods:{async run(){this.result='执行中';try{const input=JSON.parse(this.request);const results=[];for(const item of Array.isArray(input)?input:[input])results.push(await overseasRequest(item.method||'GET',item.path,item.data));this.result=JSON.stringify(results,null,2)}catch(e){this.result=JSON.stringify({error:e.message,code:e.code},null,2)}}}}
|
||||
</script>
|
||||
|
||||
@@ -8,7 +8,17 @@
|
||||
<text class="hint">{{ mode === 'NEW' ? '从下方来源箱选择 SKU 和扣除数量,完成后生成新箱唛。' : '扫描来源箱,再扫描另一只箱作为目标;目标箱原有库存不扣除。' }}</text>
|
||||
<template v-if="mode">
|
||||
<ScanTargetInput name="source" v-model="sourceCode" label="来源箱唛" placeholder="扫描来源箱唛" :active="active === 'source'" @activate="active = 'source'" @submit="scanSource" />
|
||||
<button class="secondary" :disabled="busy || !mode || !sourceCode.trim()" @tap="scanSource(sourceCode)">{{ mode === 'EXISTING' && sources.length ? '确认更换来源箱' : '确认来源箱' }}</button>
|
||||
<button class="secondary" :disabled="busy || !mode || !sourceCode.trim()" @tap="scanSource(sourceCode)">{{ !plan && mode === 'EXISTING' && sources.length ? '确认更换来源箱' : '确认来源箱' }}</button>
|
||||
<view v-if="plan" class="plan-boxes section-gap">
|
||||
<text class="card-title">计划需扫描箱子</text>
|
||||
<view v-for="box in plannedSourceRows" :key="box.cartonMark" class="plan-box">
|
||||
<text class="business-code">{{ box.cartonMark }} · {{ box.participation === 'FULL' ? '整箱' : box.participation === 'PARTIAL' ? '部分' : '计划来源' }}</text>
|
||||
<text class="meta-line">来源退货单:{{ box.sourceReturnNo || '—' }}</text>
|
||||
<text v-for="item in box.items || []" :key="item.skuCode" class="meta-line">{{ item.skuCode }} · {{ item.quantity }} 件</text>
|
||||
<text v-for="item in box.originalItems || []" :key="'original-' + item.skuCode" class="meta-line">{{ item.skuCode }} · 原有 {{ item.quantity }} 件 · 计划后剩余 {{ target?.cartonMark === box.cartonMark ? item.quantity : Math.max(0, item.quantity - (box.items || []).filter(row => row.skuCode === item.skuCode).reduce((sum, row) => sum + row.quantity, 0)) }} 件</text>
|
||||
<text class="hint">{{ target?.cartonMark === box.cartonMark ? '目标箱 · 保留原有库存' : sources.some(source => source.cartonMark === box.cartonMark) ? '已扫描确认' : '待扫描' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="mode === 'EXISTING'" class="confirmed-sources">
|
||||
<view v-for="box in sources" :key="box.cartonMark" class="item-card section-gap">
|
||||
<view class="row-head"><text class="business-code">{{ box.cartonMark }}</text><button class="link-button" :disabled="busy" @tap="active = 'source'">更换来源箱</button></view>
|
||||
@@ -26,18 +36,10 @@
|
||||
<view v-if="target" class="item-card section-gap"><text class="business-code">{{ target.cartonMark }}</text><text v-for="item in target.items" :key="item.skuCode" class="meta-line">{{ item.skuCode }} · 原有 {{ item.quantity }} 件(不扣除)</text></view>
|
||||
</template>
|
||||
<template v-else><view class="dimensions"><label v-for="field in fields" :key="field.key"><text>{{ field.label }}</text><input class="small-input" type="digit" v-model="dimensions[field.key]" placeholder="请输入" /></label></view><text class="hint">体积:{{ volume }} m³</text></template>
|
||||
<view v-if="mode === 'EXISTING' && plan" class="plan-boxes section-gap">
|
||||
<text class="card-title">计划箱子明细</text>
|
||||
<view v-for="box in (plan.sources || []).filter(box => !sources.some(source => source.cartonMark === box.cartonMark))" :key="box.cartonMark" class="plan-box">
|
||||
<text class="business-code">{{ box.cartonMark }}</text>
|
||||
<text v-for="item in box.items || []" :key="item.skuCode" class="meta-line">{{ item.skuCode }} · {{ item.quantity }} 件</text>
|
||||
<text class="hint">{{ target?.cartonMark === box.cartonMark ? '目标箱 · 保留原有库存' : sources.some(source => source.cartonMark === box.cartonMark) ? '来源箱已确认 · 下方填写扣除数量' : '待扫描确认' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-for="box in sources" :key="box.cartonMark" class="item-card section-gap">
|
||||
<view class="row-head"><text class="business-code">{{ box.cartonMark }}</text><button v-if="!plan" class="link-button" @tap="remove(box.cartonMark)">移除</button></view>
|
||||
<template v-if="!plan"><button class="secondary" @tap="printSource(box.cartonMark)">补打包裹/箱标签</button><ReturnLabelConfirmation ref="sourceLabels" :carton-mark="box.cartonMark" /></template>
|
||||
<view v-for="item in box.items" :key="item.skuCode" class="source-quantity"><text class="meta-line">{{ item.skuCode }} · 可扣 {{ item.quantity }} 件</text><input class="small-input" type="number" :disabled="plan?.mode === 'SKU'" :value="deduction(box.cartonMark,item.skuCode)" placeholder="扣除数量" @input="setDeduction(box.cartonMark,item.skuCode,$event.detail.value)" /></view>
|
||||
<button v-if="!plan" class="secondary" @tap="printSource(box.cartonMark)">补打包裹/箱标签</button>
|
||||
<view v-for="item in box.items" :key="item.skuCode" class="source-quantity"><text class="meta-line">{{ item.skuCode }} · {{ plan ? '计划取出' : '可扣' }} {{ item.quantity }} 件</text><input class="small-input" type="number" :disabled="!!plan" :value="deduction(box.cartonMark,item.skuCode)" placeholder="扣除数量" @input="setDeduction(box.cartonMark,item.skuCode,$event.detail.value)" /></view>
|
||||
</view>
|
||||
<button class="primary" :disabled="!canComplete" @tap="complete">{{ busy ? '处理中…' : '确认合箱' }}</button>
|
||||
</view>
|
||||
@@ -46,15 +48,14 @@
|
||||
|
||||
<script>
|
||||
import ScanTargetInput from '@/components/ScanTargetInput.vue'
|
||||
import ReturnLabelConfirmation from '@/components/ReturnLabelConfirmation.vue'
|
||||
import { scanReturnCarton, completePackingPlan, mergeReturnBoxes, getReturnCartonLabel } from '@/api/return-packing.js'
|
||||
import { updateWarehouseWork, warehouseWork, warehouseIdempotencyKey, releaseWarehouseKey } from '@/stores/warehouse-work.js'
|
||||
export default {
|
||||
components: { ScanTargetInput, ReturnLabelConfirmation },
|
||||
components: { ScanTargetInput },
|
||||
props: { plan: { type: Object, default: null }, initialSource: { type: Object, default: null } },
|
||||
emits: ['completed', 'error', 'busy-change'],
|
||||
data() { return { mode:'', dimensions:{length:'',width:'',height:'',weight:''}, fields:[{key:'length',label:'长 cm'},{key:'width',label:'宽 cm'},{key:'height',label:'高 cm'},{key:'weight',label:'重量 kg'}], sourceCode: '', targetCode: '', sources: [], target: null, deductions: {}, active: 'source', busy: false } },
|
||||
computed: { volume() { const value = Number(this.dimensions.length) * Number(this.dimensions.width) * Number(this.dimensions.height) / 1000000; return Number.isFinite(value) && value > 0 ? value.toFixed(6) : '0.000000' }, canComplete() { return !this.busy && (!this.plan || this.plan.mode !== 'SKU' || this.plan.sourceCartonMarks.every(mark => this.sources.some(box => box.cartonMark === mark))) && this.plannedItems().length > 0 && (this.mode === 'NEW' ? this.fields.every(f => Number(this.dimensions[f.key]) > 0) : !!this.target && this.target.cartonMark === this.targetCode.trim() && this.sources[0]?.cartonMark === this.sourceCode.trim()) } },
|
||||
computed: { plannedSourceRows() { const listed = Array.isArray(this.plan?.sources) ? this.plan.sources : []; const marks = this.plan?.sourceCartonMarks?.length ? this.plan.sourceCartonMarks : listed.map(box => box.cartonMark).filter(Boolean); return marks.map(mark => listed.find(box => box.cartonMark === mark) || { cartonMark: mark, items: [] }) }, volume() { const value = Number(this.dimensions.length) * Number(this.dimensions.width) * Number(this.dimensions.height) / 1000000; return Number.isFinite(value) && value > 0 ? value.toFixed(6) : '0.000000' }, canComplete() { return !this.busy && (!this.plan || (this.plan.sourceCartonMarks || this.plan.sources.map(box => box.cartonMark)).every(mark => this.sources.some(box => box.cartonMark === mark) || this.target?.cartonMark === mark)) && this.plannedItems().length > 0 && (this.mode === 'NEW' ? this.fields.every(f => Number(this.dimensions[f.key]) > 0) : !!this.target && this.target.cartonMark === this.targetCode.trim() && (!!this.plan || this.sources[0]?.cartonMark === this.sourceCode.trim())) } },
|
||||
async mounted() {
|
||||
if (!this.plan && this.initialSource) { this.choose('NEW'); this.sources = [this.initialSource]; this.sourceCode = ''; this.active = 'source'; return }
|
||||
this.choose('NEW')
|
||||
@@ -79,16 +80,16 @@ export default {
|
||||
if (this.mode === 'EXISTING') { this.sourceCode = mark; this.active = 'target'; return }
|
||||
throw new Error('该来源箱已添加')
|
||||
}
|
||||
if (this.mode !== 'EXISTING' && this.target?.cartonMark === mark) throw new Error('来源箱不能与目标箱相同')
|
||||
if ((this.plan || this.mode !== 'EXISTING') && this.target?.cartonMark === mark) throw new Error('来源箱不能与目标箱相同')
|
||||
const planned = this.plan?.sources?.find(box => box.cartonMark === mark)
|
||||
if(this.plan && !planned) throw new Error('该来源箱不在合箱计划中')
|
||||
const box = planned ? {...planned,customerId:this.plan.customerId} : await scanReturnCarton(mark)
|
||||
const customer = this.sources[0]?.customerId || this.target?.customerId
|
||||
if (customer && customer !== box.customerId) throw new Error('不同客户的箱不能合并')
|
||||
if (this.mode === 'EXISTING') {
|
||||
if (this.mode === 'EXISTING' && !this.plan) {
|
||||
this.sources = [box]; this.sourceCode = mark; this.deductions = {}; this.target = null; this.targetCode = ''
|
||||
} else { this.sources.push(box); this.sourceCode = '' }
|
||||
if (this.plan?.mode === 'SKU') this.deductions = { ...this.deductions, [mark]: Object.fromEntries((box.items || []).map(item => [item.skuCode, Number(item.quantity)])) }
|
||||
if (this.plan) this.deductions = { ...this.deductions, [mark]: Object.fromEntries((box.items || []).map(item => [item.skuCode, Number(item.quantity)])) }
|
||||
this.save(); this.active = this.mode === 'NEW' ? 'source' : 'target'
|
||||
})
|
||||
},
|
||||
@@ -104,7 +105,7 @@ export default {
|
||||
// target. The remaining planned boxes are the source boxes.
|
||||
this.target = {...plannedSource,customerId:this.plan.customerId}
|
||||
this.targetCode = mark
|
||||
this.deductions = Object.fromEntries(this.sources.map(box => [box.cartonMark, Object.fromEntries((box.items || []).map(item => [item.skuCode, '']))]))
|
||||
this.deductions = Object.fromEntries(this.sources.map(box => [box.cartonMark, Object.fromEntries((box.items || []).map(item => [item.skuCode, Number(item.quantity)]))]))
|
||||
this.save()
|
||||
return
|
||||
}
|
||||
@@ -115,7 +116,7 @@ export default {
|
||||
this.target = box; this.save()
|
||||
})
|
||||
},
|
||||
acceptScan(value) { if (!this.plan && this.sources.some(box => box.cartonMark === String(value).trim())) { for (const label of [].concat(this.$refs?.sourceLabels || [])) label.acceptScan(value); return } if (!this.active) return; return this.active === 'source' ? this.scanSource(value) : this.scanTarget(value) },
|
||||
acceptScan(value) { if (!this.active) return; return this.active === 'source' ? this.scanSource(value) : this.scanTarget(value) },
|
||||
async printSource(mark) {
|
||||
// #ifdef H5
|
||||
const tab = window.open('', '_blank')
|
||||
@@ -129,6 +130,7 @@ export default {
|
||||
},
|
||||
deduction(mark, sku) { return this.deductions?.[mark]?.[sku] || '' },
|
||||
setDeduction(mark, sku, raw) {
|
||||
if (this.plan) return
|
||||
const box = this.sources.find(item => item.cartonMark === mark)
|
||||
const max = Number(box?.items?.find(item => item.skuCode === sku)?.quantity || 0)
|
||||
const value = Math.min(max, Math.max(0, Math.floor(Number(raw) || 0)))
|
||||
|
||||
Reference in New Issue
Block a user