style(admin): unify dialog layouts and dense readonly presentation
This commit is contained in:
@@ -5,7 +5,7 @@ import SmartDispatchDialog from './SmartDispatchDialog.vue'
|
||||
import AppDialog from '@/shared/components/AppDialog.vue'
|
||||
import { listAllPlatformWarehouses } from '@/modules/warehouses'
|
||||
import * as api from '../api/dispatches'
|
||||
import { candidate, command, detail } from '../dispatchTestFixtures'
|
||||
import { candidate, command, detail, sharedPalletCandidates } from '../dispatchTestFixtures'
|
||||
import type { ConvertDraft, DispatchDraft, Version } from '../dispatchContracts'
|
||||
import { useDispatchCommandRecovery } from '../dispatchCommandRecovery'
|
||||
const identity = vi.hoisted(() => ({ id: 'smart-operator' }))
|
||||
@@ -17,6 +17,29 @@ vi.mock('@/modules/warehouses', () => ({ listAllPlatformWarehouses: vi.fn() }))
|
||||
vi.mock('./DispatchResourceFields.vue', () => ({ default: { template: '<div />' } }))
|
||||
vi.mock('../api/dispatches', () => ({ listDispatchDrafts: vi.fn(), listDispatchCandidates: vi.fn(), createDispatchDraft: vi.fn(), createDispatchDraftContainer: vi.fn(), lockDispatchDraftPallet: vi.fn(), unlockDispatchDraftPallet: vi.fn(), deleteDispatchDraftContainer: vi.fn(), releaseDispatchDraft: vi.fn(), convertDispatchDraft: vi.fn(), createDispatch: vi.fn(), getDispatch: vi.fn(), getDispatchCommand: vi.fn() }))
|
||||
const source = () => candidate(), draft = (): DispatchDraft => ({ draftToken: 'server-token', recordVersion: 0, status: 'ACTIVE', warehouseId: '321', containers: [{ draftContainerId: '100000000000000001', sequenceNo: 1, capacity: 30, sourceKeys: ['source-1'] }], sources: [source()], expiresAt: '2026-12-01T00:00:00Z' })
|
||||
it('converts a filtered relabel group with both authoritative sources and locks it through one HTTP command', async () => {
|
||||
const [first, second] = sharedPalletCandidates(), original = draft()
|
||||
original.sources = [first!]; original.containers[0]!.sourceKeys = [first!.sourceKey]
|
||||
vi.mocked(api.listDispatchCandidates).mockResolvedValue({ candidates: [first!], complete: true, snapshotAt: '' })
|
||||
vi.mocked(api.listDispatchDrafts).mockResolvedValue([original]); vi.mocked(api.lockDispatchDraftPallet).mockResolvedValue(original)
|
||||
vi.mocked(api.convertDispatchDraft).mockImplementation(async request => ({ draftToken: request.draftToken, replayed: false, vehicles: request.groups.map(group => ({ draftContainerId: group.draftContainerId, dispatch: command('CREATE', group.dispatch.idempotencyKey) })) }))
|
||||
const wrapper = mount(SmartDispatchDialog, options); await flushPromises()
|
||||
const state = wrapper.vm as unknown as { restoreDraft(token: string): void; draftAction(action: string, row: typeof first, container: string): Promise<void>; submit(): Promise<void> }
|
||||
state.restoreDraft('server-token'); await flushPromises(); await state.draftAction('LOCK', first, original.containers[0]!.draftContainerId)
|
||||
expect(api.lockDispatchDraftPallet).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({ source: first!.selection }))
|
||||
await state.submit(); expect(vi.mocked(api.convertDispatchDraft).mock.calls[0]![0].groups[0]!.dispatch.cargo).toEqual([first!.selection, second!.selection])
|
||||
wrapper.unmount()
|
||||
})
|
||||
it('allows one atomic removal of a restored group that is no longer selectable', async () => {
|
||||
const first = sharedPalletCandidates()[0]!, original = draft()
|
||||
first.selectionGroup = { ...first.selectionGroup, complete: false, blockReasons: ['来源待核齐'] }
|
||||
original.sources = [first]; original.containers[0]!.sourceKeys = [first.sourceKey]
|
||||
vi.mocked(api.listDispatchDrafts).mockResolvedValue([original]); vi.mocked(api.unlockDispatchDraftPallet).mockResolvedValue(original)
|
||||
const wrapper = mount(SmartDispatchDialog, options); await flushPromises()
|
||||
const state = wrapper.vm as unknown as { restoreDraft(token: string): void; draftAction(action: string, row: typeof first, container: string): Promise<void> }
|
||||
state.restoreDraft('server-token'); await flushPromises(); await state.draftAction('UNLOCK', first, original.containers[0]!.draftContainerId)
|
||||
expect(api.unlockDispatchDraftPallet).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({ source: first.selection })); wrapper.unmount()
|
||||
})
|
||||
const options = { props: { modelValue: true, orders: [] }, global: { plugins: [ElementPlus] } }
|
||||
beforeEach(() => { vi.clearAllMocks(); sessionStorage.clear(); identity.id = crypto.randomUUID(); vi.stubGlobal('ResizeObserver', class { observe() {} unobserve() {} disconnect() {} }); vi.mocked(listAllPlatformWarehouses).mockResolvedValue([{ id: '321', warehouseName: '洛杉矶仓' }] as Awaited<ReturnType<typeof listAllPlatformWarehouses>>); vi.mocked(api.listDispatchDrafts).mockResolvedValue([draft()]); vi.mocked(api.listDispatchCandidates).mockResolvedValue({ candidates: [source()], complete: true, snapshotAt: '2026-10-19T00:00:00Z' }); vi.mocked(api.getDispatch).mockResolvedValue(detail({ recordVersion: 1 })) })
|
||||
it.each([0, '0', '9007199254740993'])('transmits original authoritative batch version %s in complete draft conversion and exact retries', async recordVersion => {
|
||||
@@ -55,12 +78,12 @@ it('requires an explicit warehouse and allows saving a true empty dispatch with
|
||||
expect(wrapper.emitted('created')).toHaveLength(1); wrapper.unmount()
|
||||
})
|
||||
it('restores an explicit draft and submits complete server source versions in each container', async () => {
|
||||
const original = draft(); original.sources.push(candidate({ sourceKey: 'source-B', customerId: 'B', customerName: '客户乙' })); original.containers[0]!.sourceKeys.push('source-B')
|
||||
const original = draft(); original.sources.push(candidate({ sourceKey: 'source-B', customerId: 'B', customerName: '客户乙', selection: { ...source().selection, planPalletId: '22', warehousePalletId: '32' } })); original.containers[0]!.sourceKeys.push('source-B')
|
||||
vi.mocked(api.listDispatchDrafts).mockResolvedValue([original])
|
||||
vi.mocked(api.convertDispatchDraft).mockImplementation(async request => ({ draftToken: request.draftToken, replayed: false, vehicles: request.groups.map(group => ({ draftContainerId: group.draftContainerId, dispatch: command('CREATE', group.dispatch.idempotencyKey, 'PC/1') })) }))
|
||||
const wrapper = mount(SmartDispatchDialog, options); await flushPromises(); const state = wrapper.vm as unknown as { restoreDraft: (token: string) => void; submit: () => Promise<void> }
|
||||
state.restoreDraft('server-token'); await flushPromises(); await state.submit(); await flushPromises()
|
||||
expect(api.convertDispatchDraft).toHaveBeenCalledWith(expect.objectContaining({ recordVersion: 0, draftToken: 'server-token', groups: [expect.objectContaining({ draftContainerId: '100000000000000001', dispatch: expect.objectContaining({ cargo: [source().selection, source().selection] }) })] }))
|
||||
expect(api.convertDispatchDraft).toHaveBeenCalledWith(expect.objectContaining({ recordVersion: 0, draftToken: 'server-token', groups: [expect.objectContaining({ draftContainerId: '100000000000000001', dispatch: expect.objectContaining({ cargo: [source().selection, original.sources[1]!.selection] }) })] }))
|
||||
expect(wrapper.emitted('created')).toHaveLength(1); wrapper.unmount()
|
||||
})
|
||||
it('retains conversion payload and key after an unknown result instead of rebuilding from fresh versions', async () => {
|
||||
|
||||
Reference in New Issue
Block a user