Release/test #1
@@ -24,24 +24,31 @@
|
||||
</view>
|
||||
|
||||
<view v-if="task.taskStatus === 'CLAIMED'" class="section-card">
|
||||
<text class="section-title">1. 箱码清点与归托</text>
|
||||
<text class="section-desc">先扫箱:核对实际件数并确认清点;已选择托盘时本箱直接归托。也可先扫托盘,再连续扫箱清点归托。</text>
|
||||
<text class="section-title">1. 扫箱清点</text>
|
||||
<text class="section-desc">扫描箱码,确认 SKU 后点数。</text>
|
||||
<template v-if="palletSetupReady">
|
||||
<ScanTargetInput v-model="boxCode" name="boxCode" label="箱码" placeholder="扫描或输入箱唛箱码" :active="activeScan === 'boxCode'" @activate="activeScan = 'boxCode'" @submit="resolveCarton" />
|
||||
<view v-if="!carton" class="count-entry">
|
||||
<text>扫描箱码后核对计划件数与实际件数,再确认完成本箱清点。</text>
|
||||
<button class="primary-button count-button" :disabled="!canResolveCarton" @tap="resolveCarton()">开始清点</button>
|
||||
<text>请扫描箱码</text>
|
||||
<button class="primary-button count-button" :disabled="!canResolveCarton" @tap="resolveCarton()">读取 SKU</button>
|
||||
</view>
|
||||
<view v-if="carton" class="carton-card">
|
||||
<view class="carton-head"><text>{{ carton.boxCode }}</text><text>{{ carton.countStatus === 'UNCOUNTED' ? '待清点' : '已处理' }}</text></view>
|
||||
<text class="carton-line">{{ carton.skuCode || 'SKU —' }} · {{ carton.cargoName || '品名 —' }}</text>
|
||||
<text class="carton-line">计划 {{ carton.expectedSkuQuantity || 0 }} 件</text>
|
||||
<view class="field"><text>实际件数</text><input v-model="actualQuantity" type="number" aria-label="本箱实际件数" /></view>
|
||||
<text class="sku-label">SKU</text>
|
||||
<text class="sku-code">{{ carton.skuCode || '—' }}</text>
|
||||
<view class="quantity-row">
|
||||
<text>清点数量</text>
|
||||
<view class="quantity-stepper">
|
||||
<button aria-label="减少清点数量" @tap="adjustQuantity(-1)">−</button>
|
||||
<input v-model="actualQuantity" type="number" aria-label="清点数量" />
|
||||
<button aria-label="增加清点数量" @tap="adjustQuantity(1)">+</button>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="quantityDiffers" class="field stacked"><text>差异说明</text><textarea v-model="discrepancyRemark" maxlength="500" placeholder="数量不一致时必填" /></view>
|
||||
<button class="primary-button" :disabled="!canCount || submitting" @tap="countCarton">{{ currentPallet ? `确认清点并归入 ${currentPallet.palletNo}` : '确认清点并进入待归托区' }}</button>
|
||||
<text v-if="currentPallet" class="pallet-hint">归入 {{ currentPallet.palletNo }}</text>
|
||||
<button class="primary-button" :disabled="!canCount || submitting" @tap="countCarton">确认清点</button>
|
||||
</view>
|
||||
<view class="pallet-scan-block">
|
||||
<text class="field-label">托盘归托(可先扫或后扫)</text>
|
||||
<text class="field-label">扫托盘(可选)</text>
|
||||
<ScanTargetInput v-model="palletCode" name="palletCode" label="托盘码" placeholder="扫描或输入已生成的 DP 托盘码" :active="activeScan === 'palletCode'" @activate="activeScan = 'palletCode'" @submit="selectPallet" />
|
||||
</view>
|
||||
</template>
|
||||
@@ -112,6 +119,7 @@
|
||||
const result = await this.runMutation(() => countDropshipCarton(this.taskId, { warehouseId: this.warehouseId, boxCode: this.carton.boxCode, palletNo: this.currentPallet?.palletNo || null, actualSkuQuantity: Number(this.actualQuantity), discrepancyAction: this.quantityDiffers ? 'ACCEPT_VARIANCE' : 'MATCH', discrepancyRemark: this.quantityDiffers ? this.discrepancyRemark.trim() : null }), this.currentPallet ? '本箱已清点并归托' : '本箱已完成入库清点')
|
||||
if (result) { this.boxCode = ''; this.carton = null; this.actualQuantity = ''; this.discrepancyRemark = '' }
|
||||
},
|
||||
adjustQuantity(step) { this.actualQuantity = String(Math.max(1, Number(this.actualQuantity || 0) + step)) },
|
||||
async selectPallet(value) {
|
||||
if (!this.palletSetupReady) { this.error = this.palletSetupMessage; return }
|
||||
const code = String(value || this.palletCode || '').trim().toUpperCase(); const pallet = (this.task?.pallets || []).find((item) => item.palletNo === code)
|
||||
@@ -170,7 +178,8 @@
|
||||
.section-title { display: block; color: #12233f; font-size: 16px; font-weight: 700; }.section-desc { display: block; margin: 3px 0 11px; color: #667085; font-size: 11px; line-height: 17px; }.section-heading > view { min-width: 0; }
|
||||
.primary-button, .secondary-button { box-sizing: border-box; width: 100%; height: 46px; margin-top: 10px; border-radius: 9px; font-size: 14px; font-weight: 700; line-height: 44px; }.primary-button { border: 0; color: #fff; background: #1a73e8; }.secondary-button { border: 1px solid #8ab8ef; color: #155eef; background: #f6f9ff; }.primary-button[disabled],.secondary-button[disabled] { opacity: .45; }
|
||||
.count-entry { margin-top: 9px; padding: 10px; border: 1px solid #dbe8f8; border-radius: 9px; color: #52657a; font-size: 11px; line-height: 17px; background: #f6f9ff; }.count-button { margin-top: 8px; }.count-unavailable { padding: 18px 10px; border-radius: 9px; color: #b42318; font-size: 12px; line-height: 18px; text-align: center; background: #fff1f0; }.pallet-scan-block { margin-top: 13px; padding-top: 12px; border-top: 1px solid #e6edf5; }.field-label { display: block; margin-bottom: 7px; color: #344054; font-size: 12px; font-weight: 700; }
|
||||
.carton-card, .pallet-card { margin-top: 11px; padding: 11px; border: 1px solid #e3eaf2; border-radius: 9px; background: #f8fbff; }.carton-head { color: #12233f; font-size: 13px; font-weight: 700; }.carton-line, .pallet-meta { display: block; margin-top: 5px; color: #667085; font-size: 11px; line-height: 17px; }
|
||||
.carton-card, .pallet-card { margin-top: 11px; padding: 11px; border: 1px solid #e3eaf2; border-radius: 9px; background: #f8fbff; }.sku-label { display: block; color: #667085; font-size: 10px; }.sku-code { display: block; margin-top: 3px; color: #12233f; font-size: 17px; font-weight: 700; overflow-wrap: anywhere; }.pallet-meta { display: block; margin-top: 5px; color: #667085; font-size: 11px; line-height: 17px; }
|
||||
.quantity-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 14px; color: #344054; font-size: 13px; font-weight: 700; }.quantity-stepper { display: grid; grid-template-columns: 44px 72px 44px; overflow: hidden; border: 1px solid #b8c6d8; border-radius: 9px; background: #fff; }.quantity-stepper button,.quantity-stepper input { box-sizing: border-box; height: 44px; margin: 0; border: 0; border-radius: 0; text-align: center; background: #fff; }.quantity-stepper button { color: #155eef; font-size: 21px; line-height: 44px; }.quantity-stepper input { width: 72px; border-right: 1px solid #dce4ed; border-left: 1px solid #dce4ed; color: #12233f; font-size: 16px; }.pallet-hint { display: block; margin-top: 10px; color: #155eef; font-size: 11px; text-align: center; }
|
||||
.field { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 10px; color: #344054; font-size: 12px; }.field input { box-sizing: border-box; width: 120px; height: 42px; padding: 0 10px; border: 1px solid #cfd8e3; border-radius: 8px; background: #fff; }.field.stacked { align-items: stretch; flex-direction: column; gap: 6px; }.field textarea { box-sizing: border-box; width: 100%; height: 70px; padding: 9px; border: 1px solid #cfd8e3; border-radius: 8px; background: #fff; }
|
||||
.pallet-card { background: #fff; }.pallet-code { color: #12233f; font-size: 15px; font-weight: 700; overflow-wrap: anywhere; }.text-button { min-width: 58px; height: 40px; border: 0; color: #155eef; font-size: 12px; line-height: 40px; background: transparent; }.empty { padding: 18px 0 8px; color: #98a2b3; font-size: 12px; text-align: center; }
|
||||
</style>
|
||||
|
||||
@@ -43,7 +43,9 @@ test('palletization UI accepts carton and pre-generated pallet scans in either o
|
||||
assert.match(tasks, /\.search-card\s*\{[^}]*display:\s*flex;[^}]*box-shadow:\s*none;/)
|
||||
assert.match(tasks, /\.task-list\s*\{[^}]*overflow:\s*hidden;[^}]*border-radius:\s*7px;/)
|
||||
assert.match(tasks, /\.task-card\s*\{[^}]*margin:\s*0;[^}]*border-radius:\s*0;[^}]*box-shadow:\s*none;/)
|
||||
for (const text of ['一件代发入库', '领取并开始入库', '箱码清点与归托', '开始清点', '确认清点', '托盘归托(可先扫或后扫)', '差异说明', '预生成托盘码', '打印托盘码', '封托']) assert.match(work, new RegExp(text))
|
||||
for (const text of ['一件代发入库', '领取并开始入库', '扫箱清点', '读取 SKU', '确认清点', '扫托盘(可选)', '差异说明', '预生成托盘码', '打印托盘码', '封托']) assert.match(work, new RegExp(text))
|
||||
for (const text of ['sku-label', 'sku-code', 'quantity-stepper', '减少清点数量', '增加清点数量', 'adjustQuantity']) assert.match(work, new RegExp(text))
|
||||
for (const text of ['计划 {{ carton.expectedSkuQuantity', "carton.cargoName", "carton.countStatus === 'UNCOUNTED'"]) assert.doesNotMatch(work, new RegExp(text))
|
||||
assert.match(work, /canResolveCarton\(\)[\s\S]*?boxCode[\s\S]*?!this\.loading[\s\S]*?!this\.submitting/)
|
||||
for (const text of ['expectedPalletCount', 'generatedPalletCount', 'palletSetupReady', 'palletSetupMessage']) assert.match(work, new RegExp(text))
|
||||
assert.match(work, /<template v-if="palletSetupReady">[\s\S]*?ScanTargetInput[\s\S]*?count-unavailable/)
|
||||
|
||||
Reference in New Issue
Block a user