This commit is contained in:
2026-10-19 14:53:57 +08:00
parent f7dd39c7ed
commit 9a24e0c83d
6 changed files with 46 additions and 11 deletions
@@ -78,8 +78,8 @@ test('新建退货预报使用独立路由页面,编辑仍保留加宽弹框',
assert.match(view, /class="return-create-actions"[\s\S]*?<el-button type="primary" :loading="creating" @click="submit">提交预报<\/el-button>/)
})
test('新建预报居左限宽,联系字段与数量输入保持紧凑', () => {
assert.match(view, /return-create-surface \{ width:calc\(100% - 24px\);[^}]*max-width:1120px;[^}]*margin:0 0 0 24px; justify-self:start;/)
test('新建预报铺满工作区,联系字段与数量输入保持紧凑', () => {
assert.match(view, /return-create-surface \{ width:100%; min-width:0;[^}]*margin:0;/)
assert.match(view, /<el-form label-width="130px" class="return-create-form">/)
assert.match(view, /return-create-form :deep\(\.el-input\) \{ max-width:320px;/)
assert.match(view, /return-create-form :deep\(\.el-select\) \{ max-width:520px;/)
@@ -487,7 +487,8 @@ onBeforeUnmount(clearPhotoUrls)
@media(max-width:1200px) { .return-query.query-toolbar--grid { grid-template-columns:repeat(2,minmax(0,320px)); } }
.relabel-suborder { display: flex; align-items: center; gap: 8px; min-height: 30px; white-space: nowrap; }
.return-orders-page { display:grid; gap:12px; } .return-create-form { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); column-gap:20px; } .return-form-wide { grid-column:1 / -1; } .items-title { display:flex; justify-content:space-between; align-items:center; margin:16px 0 4px; font-weight:600; } .items-hint { margin-bottom:10px; color:var(--el-text-color-secondary); font-size:12px; } h3 { margin:24px 0 10px; font-size:16px; } h4 { margin:16px 0 8px; font-size:14px; } .remark { white-space:pre-wrap; } .warehouse-error, .duplicate-sku-error { margin-top:6px; color:var(--el-color-danger); font-size:12px; } :deep(.duplicate-sku-input .el-input__wrapper) { box-shadow:0 0 0 1px var(--el-color-danger) inset; } :deep(.el-dialog .el-select) { width:100%; }
.return-create-surface { width:calc(100% - 24px); max-width:1120px; min-width:0; margin:0 0 0 24px; justify-self:start; box-sizing:border-box; overflow:hidden; border:1px solid var(--client-border); border-radius:var(--client-radius); background:#fff; }
.return-create-surface { width:100%; min-width:0; min-height:calc(100dvh - 198px); margin:0; box-sizing:border-box; overflow:hidden; border:1px solid var(--client-border); border-radius:var(--client-radius); background:#fff; }
.return-create-header, .return-create-body, .return-create-actions { width:calc(100% - 24px); max-width:1118px; margin-left:24px; box-sizing:border-box; }
.return-create-header { display:flex; min-height:68px; align-items:center; justify-content:space-between; gap:20px; padding:12px 20px; border-bottom:1px solid var(--client-border); }
.return-create-header h2 { margin:0; color:#1f2d45; font-size:20px; }
.return-create-header p { margin:4px 0 0; color:#7c899c; font-size:12px; }
@@ -332,6 +332,7 @@ const availableBoxesCollapsed = ref(false)
const availableSkusCollapsed = ref(false)
const relabelCandidatesLoading = ref(false)
const transferInventoryCandidates = ref<RelabelInventoryCandidate[]>([])
const transferAvailableSourceCounts = ref<Record<string, number>>({})
const selectedTransferInventory = ref('')
const inventoryTransfer = computed(() => props.service === 'transfer' && Boolean(selectedTransferInventory.value))
// 来源库存发往FBA时默认贴标,私人仓和外来货保留原有选择。
@@ -1179,6 +1180,10 @@ function printForecastCartons() {
}
async function submit(action: 'draft' | 'submit' = 'submit') {
if (saving.value) return
if (props.service === 'transfer' && inventoryTransfer.value && action === 'draft') {
ElMessage.warning('选择库存来源后不能保存草稿,请直接提交订单')
return
}
if (transferRelabelRequired.value) form.relabel = true
const primaryWarehouse = props.service === 'relabel' ? form.sourceWarehouse : form.warehouse
const relabelRows = labelingMode.value === 'carton' ? cartonDetailRows.value : skuRelabelRows.value
@@ -1475,6 +1480,7 @@ async function loadWarehouses() {
function warehouseLabel(value: MerchantWarehouse) { return `${value.warehouseName} · ${value.warehouseCode}` }
async function loadTransferInventoryCandidates(warehouseId: string) {
transferInventoryCandidates.value = []
transferAvailableSourceCounts.value = {}
if (!warehouseId) return
relabelCandidatesLoading.value = true
try {
@@ -1483,6 +1489,18 @@ async function loadTransferInventoryCandidates(warehouseId: string) {
item.sourceType === 'RETURN_CARTON' || item.sourceType === 'RETURN_PARCEL'
? Number(item.cartonAvailablePackages || 0) > 0
: Number(item.availablePackages || 0) > 0)
const sourceValues = [...new Set(transferInventoryCandidates.value.map(item => {
const returnNo = String(item.inboundOrderNo || '').trim()
return item.sourceType === 'RETURN_CARTON' || item.sourceType === 'RETURN_PARCEL'
? (returnNo ? `RETURN_ORDER:${returnNo}` : '') : String(item.inboundCode || '')
}).filter(Boolean))]
const results = await Promise.all(sourceValues.map(async (value) => {
try {
const cartons = await getTransferSourceCartons(warehouseId, value)
return [value, cartons.length] as const
} catch { return [value, 0] as const }
}))
transferAvailableSourceCounts.value = Object.fromEntries(results)
} catch (error) {
ElMessage.error(error instanceof Error ? error.message : '当前海外仓库存来源加载失败')
} finally { relabelCandidatesLoading.value = false }
@@ -1544,7 +1562,7 @@ const transferInventoryOptions = computed(() => {
: inventoryQuantity(item.availablePackages)
const current = grouped.get(value)
if (current) current.packages += packages
else grouped.set(value, { value, label: value.startsWith('RETURN_ORDER:') ? `退货单|${returnNo}` : `${item.inboundOrderNo || value}|${value}`, packages })
else if ((transferAvailableSourceCounts.value[value] ?? 1) > 0) grouped.set(value, { value, label: value.startsWith('RETURN_ORDER:') ? `退货单|${returnNo}` : `${item.inboundOrderNo || value}|${value}`, packages })
}
return [...grouped.values()]
})
@@ -1850,7 +1868,7 @@ function addReturnBoxBinding(source: RelabelInventoryCandidate) {
<div v-else-if="labelingMode === 'sku'" class="warehouse-table-scroll"><table class="warehouse-edit-table warehouse-transfer-mark-table"><thead><tr><th>仓库唛头 *</th><th>旧 SKU *</th><th>新 SKU *</th><th>换标数量(件)*</th><th>备注</th><th>操作</th></tr></thead><tbody><tr v-for="row in transferRelabelRows" :key="row.id"><td><el-input v-model="row.customerCartonMarks" placeholder="请输入仓库唛头" /></td><td><el-input :model-value="row.sku" readonly /></td><td><el-input v-model="row.newSkuCode" placeholder="请输入新 SKU" /></td><td><el-input-number v-model="row.quantity" :min="1" :precision="0" controls-position="right" placeholder="请输入箱数" /></td><td><el-input v-model="row.remark" type="textarea" :rows="2" placeholder="请输入备注" /></td><td><button type="button" class="warehouse-delete" title="删除该明细" @click="transferRelabelRows = transferRelabelRows.filter((item) => item.id !== row.id)"><Trash2 :size="16" />删除</button></td></tr><tr v-if="transferRelabelRows.length === 0"><td colspan="6"><div class="warehouse-empty">暂无绑定明细,请点击“添加 SKU 明细”</div></td></tr></tbody></table></div>
<div v-else class="warehouse-table-scroll"><table class="warehouse-edit-table warehouse-transfer-mark-table"><thead><tr><th>仓库唛头 *</th><th>旧 SKU *</th><th>新 SKU *</th><th>换标数量(件)*</th><th>新 FBA 箱唛 *</th><th>备注</th><th>操作</th></tr></thead><tbody><tr v-for="row in transferRelabelRows" :key="row.id"><td><el-input v-model="row.customerCartonMarks" placeholder="请输入仓库唛头" /></td><td><el-input :model-value="row.sku" readonly /></td><td><el-input v-model="row.newSkuCode" placeholder="请输入新 SKU" /></td><td><el-input-number v-model="row.quantity" :min="1" :precision="0" controls-position="right" placeholder="请输入数量" /></td><td><el-input v-model="row.fbaCartonMark" placeholder="请输入新的 FBA 箱唛" /></td><td><el-input v-model="row.remark" type="textarea" :rows="2" placeholder="请输入备注" /></td><td><button type="button" class="warehouse-delete" title="删除该明细" @click="transferRelabelRows = transferRelabelRows.filter((item) => item.id !== row.id)"><Trash2 :size="16" />删除</button></td></tr><tr v-if="transferRelabelRows.length === 0"><td colspan="7"><div class="warehouse-empty">暂无绑定明细,请点击“添加 SKU 明细”</div></td></tr></tbody></table></div>
</div>
<div class="warehouse-submit-bar warehouse-transfer-submit-bar"><el-button v-if="mode === 'create'" native-type="button" @click="resetForm">重置</el-button><el-button v-if="props.service === 'transfer'" native-type="button" :loading="saving" @click="submit('draft')">保存草稿</el-button><el-button v-if="props.service === 'transfer'" type="primary" native-type="submit" :loading="saving">提交订单</el-button><el-button v-if="props.service !== 'transfer'" type="primary" native-type="submit" :loading="saving">提交{{ config.title }}</el-button></div>
<div class="warehouse-submit-bar warehouse-transfer-submit-bar"><el-button v-if="mode === 'create'" native-type="button" @click="resetForm">重置</el-button><el-button v-if="props.service === 'transfer' && !inventoryTransfer" native-type="button" :loading="saving" @click="submit('draft')">保存草稿</el-button><el-button v-if="props.service === 'transfer'" type="primary" native-type="submit" :loading="saving">提交订单</el-button><el-button v-if="props.service !== 'transfer'" type="primary" native-type="submit" :loading="saving">提交{{ config.title }}</el-button></div>
</form>
</template>
@@ -31,6 +31,13 @@ test('退货箱明细与来源下拉使用同一份逐箱库存,不请求普
assert.equal(ctx.transferSourceLoading.value, false)
})
test('选择库存来源时隐藏保存草稿并阻止草稿动作', () => {
assert.match(view, /props\.service === 'transfer' && !inventoryTransfer/)
assert.match(view, /保存草稿/)
assert.match(view, /props\.service === 'transfer' && inventoryTransfer\.value && action === 'draft'/)
assert.match(view, /选择库存来源后不能保存草稿,请直接提交订单/)
})
test('来源库存发往FBA默认开启贴标,清除来源或切换私人仓后恢复选项', () => {
const source = ref(''), destination = ref('NONE'), form = reactive({ relabel: false })
const inventoryTransfer = computed(() => Boolean(source.value))