This commit is contained in:
2026-10-20 23:49:50 +08:00
parent e60f1b300a
commit 1c771066ea
7 changed files with 121 additions and 4 deletions
@@ -891,6 +891,41 @@ describe('GlobalSmartLoadingView', () => {
expect(listSmartLoadingCandidates).toHaveBeenCalledWith({ channelGroupCodes: ['MATSON_CLX'], page: 1, size: 20 })
})
it('keeps assignment failures and diagnostic IDs inside the open dialog without losing the quantity', async () => {
vi.spyOn(ElMessage, 'error').mockImplementation(() => undefined as never)
vi.mocked(assignSmartLoadingItem).mockRejectedValueOnce(new AxiosError(
'forbidden', 'ERR_BAD_REQUEST', undefined, undefined,
{ data: { code: 'A0403', message: 'Access denied', requestId: 'assign-request', traceId: 'assign-trace' }, status: 403, statusText: 'Forbidden', headers: {}, config: {} as never },
))
const wrapper = mount(GlobalSmartLoadingView, { attachTo: document.body, global: { plugins: [ElementPlus] } })
await flushPromises()
await wrapper.get('.candidate-pane .el-table__body-wrapper').findAll('button').find(button => button.text() === '加入')!.trigger('click')
await flushPromises()
const popup = wrapper.getComponent(AppDialog)
const input = popup.getComponent(ElInputNumber)
input.vm.$emit('update:modelValue', 7)
await flushPromises()
// 箱数与上方资料使用同一标签列,保留输入名称与原范围。
const label = popup.get('label[for="global-loading-assignment-count"]')
expect(label.element.parentElement?.tagName).toBe('TH')
expect(label.element.parentElement?.nextElementSibling?.getAttribute('colspan')).toBe('3')
expect(popup.find('input#global-loading-assignment-count').exists()).toBe(true)
expect(input.props('max')).toBe(candidate.availablePackageCount)
popup.vm.$emit('confirm')
await flushPromises()
expect(assignSmartLoadingItem).toHaveBeenCalledWith(plan.id, { inboundId: candidate.inboundId, packageCount: 7, expectedVersion: plan.version })
expect(popup.props('modelValue')).toBe(true)
expect(input.props('modelValue')).toBe(7)
expect(popup.text()).toContain('加入当前柜次失败')
expect(popup.text()).toContain('当前账号无权访问该数据')
expect(popup.text()).toContain('assign-request')
expect(popup.text()).toContain('assign-trace')
expect(wrapper.get('.global-loading-page').findAll('.load-error-state')).toHaveLength(1)
wrapper.unmount()
})
it('refreshes the latest plan after A0409 without losing the selected container', async () => {
const warningMessage = vi.spyOn(ElMessage, 'warning').mockImplementation(() => undefined as never)
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
@@ -494,6 +494,7 @@ function openAssign(source: object) {
const candidate = candidateRow(source)
if (candidateBlocked(candidate)) return
selectedCandidate.value = candidate
mutationError.value = null
assignForm.packageCount = candidate.coLoadGroupId
? Number(candidate.groupTotalBoxes || candidate.availablePackageCount)
: candidate.availablePackageCount
@@ -716,7 +717,7 @@ onMounted(() => { void load(); void loadCustomers() })
<template #details><ApiErrorMeta :details="loadError" /></template>
</LoadErrorState>
<template v-else>
<LoadErrorState v-if="mutationError" title="装柜操作失败" :message="mutationError.message" compact retry-text="刷新工作台" @retry="loadWorkbenchData()">
<LoadErrorState v-if="mutationError && !assignOpen" title="装柜操作失败" :message="mutationError.message" compact retry-text="刷新工作台" @retry="loadWorkbenchData()">
<template #details><ApiErrorMeta :details="mutationError" /></template>
</LoadErrorState>
<section class="dispatch-grid" aria-label="智能装柜调度台">
@@ -839,6 +840,9 @@ onMounted(() => { void load(); void loadCustomers() })
</section>
<AppDialog class="admin-density-dialog admin-workbench is-dialog" v-model="assignOpen" :title="assignmentTitle" width="min(920px, calc(100vw - 28px))" fullscreenable :confirm-loading="mutating" :confirm-disabled="Boolean(selectedCandidate?.blockReason || selectedCandidate?.locked)" :confirm-text="selectedCandidate?.unitType === 'SEALED_PALLET' ? '确认整托分配' : selectedCandidate?.coLoadGroupId ? '确认整组分配' : '确认加入'" @confirm="assign">
<LoadErrorState v-if="mutationError" title="加入当前柜次失败" :message="mutationError.message" compact :show-retry="false">
<template #details><ApiErrorMeta :details="mutationError" /></template>
</LoadErrorState>
<div v-if="selectedCandidate" class="density-dialog-sections">
<section class="density-dialog-section" data-tone="source">
<div class="workbench-evidence"><table class="density-table assignment-context-table" aria-label="全局装柜分配来源"><tbody>
@@ -856,7 +860,7 @@ onMounted(() => { void load(); void loadCustomers() })
<template v-else>
<tr><th>入仓码</th><td>{{ selectedCandidate.inboundCode || '—' }}</td><th>可用箱数</th><td>{{ selectedCandidate.availablePackageCount }} 箱</td></tr>
<tr><th>平均单箱毛重</th><td>{{ formatMeasurement(selectedCandidate.unitGrossWeightKg, 'kg', 1) }}</td><th>平均单箱体积</th><td>{{ formatMeasurement(selectedCandidate.averageUnitVolumeCbm, 'm³') }}</td></tr>
<tr><td colspan="4"><el-form-item label="本次分配箱数" label-position="left" label-width="128px" required><el-input-number v-model="assignForm.packageCount" :min="1" :max="selectedCandidate.availablePackageCount" :step="1" controls-position="right" placeholder="请输入本次分配箱数" /></el-form-item></td></tr>
<tr><th><label for="global-loading-assignment-count"><span class="assignment-required" aria-hidden="true">*</span>本次分配箱数</label></th><td colspan="3"><el-form-item><el-input-number id="global-loading-assignment-count" v-model="assignForm.packageCount" aria-label="本次分配箱数" aria-required="true" :min="1" :max="selectedCandidate.availablePackageCount" :step="1" controls-position="right" placeholder="请输入本次分配箱数" /></el-form-item></td></tr>
</template>
</tbody></table></div>
</el-form>
@@ -880,6 +884,7 @@ onMounted(() => { void load(); void loadCustomers() })
<style scoped>
.assignment-context-table { table-layout: fixed; }
.assignment-context-table th { width: 120px; }
.assignment-required { margin-right: var(--admin-space-1); color: var(--el-color-danger); }
.global-loading-page { display: flex; min-width: 0; min-height: 0; flex-direction: column; gap: 12px; }
.embedded-smart-loading { display: flex; width: 100%; min-width: 0; min-height: 0; flex: 1; overflow: hidden; }
.embedded-smart-loading .global-loading-page { width: 100%; min-height: 0; flex: 1; overflow: hidden; }