This commit is contained in:
2026-09-04 12:18:39 +08:00
parent 72520225d9
commit 4eda56acf1
8 changed files with 381 additions and 12 deletions
@@ -18,6 +18,7 @@ import {
listSmartLoadingCandidates,
listSmartLoadingWorkbenchContainers,
recommendSmartLoading,
adjustSmartLoadingItem,
removeAllUnpickedSmartLoadingItems,
removeSmartLoadingGroup,
removeSmartLoadingItem,
@@ -525,11 +526,32 @@ describe('GlobalSmartLoadingView', () => {
it('keeps the package count editor inside its table cell without overflow ellipsis', () => {
const source = readFileSync(resolve(process.cwd(), 'src/modules/container-loading/pages/GlobalSmartLoadingView.vue'), 'utf8')
expect(source).toContain('<div v-else class="package-count-editor"><el-input-number')
expect(source).toContain('@change="adjust(row)"')
expect(source).toContain(':controls="false"')
expect(source).toContain('@blur="adjust(row)" @keyup.enter="adjust(row)"')
expect(source).not.toContain('<Save')
expect(source).toContain('.plan-pane :deep(.package-count-editor .el-input-number) { width: 100%; min-width: 0; }')
})
it('saves a typed package count through the adjustment API when editing finishes', async () => {
vi.mocked(adjustSmartLoadingItem).mockResolvedValue({
...plan,
version: 3,
items: [{ ...plan.items[0], packageCount: 4, version: 2 }],
})
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
const quantityInput = wrapper.findAllComponents(ElInputNumber).find((input) => input.props('modelValue') === 6)
expect(quantityInput?.props('controls')).toBe(false)
quantityInput?.vm.$emit('update:modelValue', 4)
await flushPromises()
quantityInput?.vm.$emit('blur')
await flushPromises()
expect(adjustSmartLoadingItem).toHaveBeenCalledTimes(1)
expect(adjustSmartLoadingItem).toHaveBeenCalledWith(plan.id, plan.items[0].id, 4, plan.version, plan.items[0].version)
})
it('rounds capacity percentages and hides column settings in embedded tables', async () => {
vi.mocked(getSmartLoadingPlanByBooking).mockResolvedValue({
...plan,
@@ -527,8 +527,9 @@ async function recommend() {
}
async function adjust(source: object) {
const item = itemRow(source)
if (!plan.value) return
if (!plan.value || mutating.value) return
const count = Number(quantityDrafts[item.id])
if (count === item.packageCount) return
if (!Number.isInteger(count) || count < item.minPackageCount) {
ElMessage.warning(`分配箱数不能低于已拣下限 ${item.minPackageCount} 箱`)
return
@@ -760,7 +761,7 @@ onMounted(() => { void load(); void loadCustomers() })
<template #actions="{ row }"><div class="row-actions"><el-button v-if="canDelete && editable && row.canRemove" size="small" type="danger" text :disabled="mutating" @click="remove(row)"><Trash2 :size="15" />删除</el-button></div></template>
<template #customerName="{ row }"><LineClampText :content="row.customerName" /></template>
<template #coLoadGroup="{ row }"><div v-if="row.coLoadGroup" class="candidate-group"><el-tag size="small" type="warning"><LineClampText :content="`${row.coLoadGroup.groupNo} · ${groupMemberLabel(row.coLoadGroup)}`" /></el-tag><LineClampText :content="row.memberOrderSummaries.join(';')" /><LineClampText :content="`固定 ${row.coLoadGroup.fullPackageCount} 箱 · ${row.coLoadGroup.lockedPlanId ? '已锁定' : '未锁定'}`" /></div><LineClampText v-else content="普通单票" /></template>
<template #packageCount="{ row }"><LineClampText v-if="row.coLoadGroup" :content="`${row.packageCount} 箱(整组固定)`" /><div v-else class="package-count-editor"><el-input-number v-model="quantityDrafts[row.id]" :min="row.minPackageCount" :max="adjustmentMax(row)" :step="1" :disabled="mutating || !canAssign || !editable || !row.canAdjust" controls-position="right" @change="adjust(row)" /><LineClampText v-if="row.detailCount > 1 && editable" content="多规格,需删除后重加" /><LineClampText v-if="row.pickedPackageCount > 0" :content="`已拣 ${row.pickedPackageCount} 箱`" /></div></template>
<template #packageCount="{ row }"><LineClampText v-if="row.coLoadGroup" :content="`${row.packageCount} 箱(整组固定)`" /><div v-else class="package-count-editor"><el-input-number v-model="quantityDrafts[row.id]" :min="row.minPackageCount" :max="adjustmentMax(row)" :step="1" :controls="false" :disabled="mutating || !canAssign || !editable || !row.canAdjust" @blur="adjust(row)" @keyup.enter="adjust(row)" /><LineClampText v-if="row.detailCount > 1 && editable" content="多规格,需删除后重加" /><LineClampText v-if="row.pickedPackageCount > 0" :content="`已拣 ${row.pickedPackageCount} 箱`" /></div></template>
<template #weight="{ row }">{{ formatNumber(Number(row.grossWeightKg || 0), 1) }} kg</template>
<template #volume="{ row }">{{ formatNumber(Number(row.volumeCbm || 0)) }} m³</template>
</DataTable>
@@ -132,7 +132,8 @@ describe('SmartLoadingWorkbenchView', () => {
it('keeps the package count editor inside its table cell without overflow ellipsis', () => {
const source = readFileSync(resolve(process.cwd(), 'src/modules/container-loading/pages/SmartLoadingWorkbenchView.vue'), 'utf8')
expect(source).toContain('<div v-else class="package-count-editor"><el-input-number')
expect(source).toContain('@change="adjust(row)"')
expect(source).toContain(':controls="false"')
expect(source).toContain('@blur="adjust(row)" @keyup.enter="adjust(row)"')
expect(source).not.toContain('<Save')
expect(source).toContain('.workbench :deep(.package-count-editor .el-input-number) { width: 100%; min-width: 0; }')
})
@@ -262,8 +262,9 @@ async function recommend() {
}
async function adjust(source: object) {
const item = itemRow(source)
if (!plan.value) return
if (!plan.value || mutating.value) return
const count = Number(quantityDrafts[item.id])
if (count === item.packageCount) return
if (!Number.isInteger(count) || count < item.minPackageCount) { ElMessage.warning(`分配箱数不能低于已拣下限 ${item.minPackageCount} 箱`); return }
mutating.value = true
mutationError.value = null
@@ -363,7 +364,7 @@ onMounted(load)
<template #customerName="{ row }"><LineClampText :content="row.customerName" /></template>
<template #coLoadGroup="{ row }"><div v-if="row.coLoadGroup" class="candidate-group"><el-tag size="small" type="warning"><LineClampText :content="`${row.coLoadGroup.groupNo} · ${groupMemberLabel(row.coLoadGroup)}`" /></el-tag><LineClampText :content="row.memberOrderSummaries.join(';')" /><LineClampText :content="`全部入仓片段联动,固定 ${row.coLoadGroup.fullPackageCount} 箱`" /></div><LineClampText v-else content="普通单票" /></template>
<template #goods="{ row }"><strong>{{ row.productNameZh || '未命名货物' }}</strong><small>{{ row.skuCode || '无 SKU' }}</small></template>
<template #packageCount="{ row }"><LineClampText v-if="row.coLoadGroup" :content="`${row.packageCount} 箱(整组固定)`" /><div v-else class="package-count-editor"><el-input-number v-model="quantityDrafts[row.id]" :min="row.minPackageCount" :step="1" :disabled="mutating || !canAssign || !editable || !row.canAdjust" controls-position="right" @change="adjust(row)" /><LineClampText v-if="row.detailCount > 1 && editable" :content="`含 ${row.detailCount} 个规格,需删除后重加`" /><LineClampText v-if="row.pickedPackageCount > 0" :content="`已拣 ${row.pickedPackageCount} 箱`" /></div></template>
<template #packageCount="{ row }"><LineClampText v-if="row.coLoadGroup" :content="`${row.packageCount} 箱(整组固定)`" /><div v-else class="package-count-editor"><el-input-number v-model="quantityDrafts[row.id]" :min="row.minPackageCount" :step="1" :controls="false" :disabled="mutating || !canAssign || !editable || !row.canAdjust" @blur="adjust(row)" @keyup.enter="adjust(row)" /><LineClampText v-if="row.detailCount > 1 && editable" :content="`含 ${row.detailCount} 个规格,需删除后重加`" /><LineClampText v-if="row.pickedPackageCount > 0" :content="`已拣 ${row.pickedPackageCount} 箱`" /></div></template>
<template #volume="{ row }">{{ formatNumber(Number(row.volumeCbm || 0)) }} m³</template><template #weight="{ row }">{{ formatNumber(Number(row.grossWeightKg || 0),1) }} kg</template>
</DataTable>
<DataTable :data="displayedCandidates" :columns="candidateColumns" :loading="loading || mutating" stripe preference-key="smart-loading-candidates" column-settings-mode="hide" empty-text="当前仓库暂无可排柜货物">