fix(dispatch): reject stale loading document upload rounds

This commit is contained in:
2026-10-26 19:21:13 +08:00
parent a9dc19a86b
commit 0137fd3a4c
3 changed files with 28 additions and 10 deletions
@@ -10,7 +10,7 @@ vi.mock('@/core/auth/auth', () => ({ useAuthStore: () => ({ user: { id: 'operato
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can }) }))
vi.mock('../api/dispatchLoadingDocuments', async importOriginal => ({ ...await importOriginal<typeof api>(), getLoadingDocuments: vi.fn(), uploadLoadingPhoto: vi.fn(), recoverLoadingPhoto: vi.fn(), bindLoadingPhoto: vi.fn(), readLoadingPhoto: vi.fn() }))
const photo: api.LoadingPhoto = { slot: 'LOADING_SCENE', fileId: '101', fileName: 'loading.png', contentType: 'image/png', savedAt: null }
const data = (extra: Partial<api.LoadingDocuments> = {}): api.LoadingDocuments => ({ dispatchNo: 'PC/1', recordVersion: '0', dispatchRecordVersion: '3', stage: 'AFTER_LOADING', editable: true, complete: false, photos: [], ...extra })
const data = (extra: Partial<api.LoadingDocuments> = {}): api.LoadingDocuments => ({ dispatchNo: 'PC/1', recordVersion: '0', dispatchRecordVersion: '3', reviewRound: '1', stage: 'AFTER_LOADING', editable: true, complete: false, photos: [], ...extra })
function setup() { return mount(DispatchLoadingDocuments, { props: { dispatchNo: 'PC/1' }, global: { plugins: [ElementPlus] } }) }
async function upload(wrapper: ReturnType<typeof setup>, files = [new File(['image'], 'loading.png', { type: 'image/png' })]) {
const input = wrapper.get('input[aria-label="上传司机驾驶证"]')
@@ -51,13 +51,28 @@ it('offers five whole-dispatch slots and only reports saved after binding succee
expect(table.findAll('thead th').map(cell => cell.text())).toEqual(['操作', '照片类别', '文件名称', '图片预览', '保存时间'])
expect(table.findAll('tbody tr').map(row => row.findAll('td')[1]!.text())).toEqual(Object.values(api.loadingPhotoLabels).map(label => `${label}0/5 张`))
await upload(wrapper)
expect(api.uploadLoadingPhoto).toHaveBeenCalledWith('PC/1', 'LOADING_SCENE', expect.any(File), expect.any(String))
expect(api.uploadLoadingPhoto).toHaveBeenCalledWith('PC/1', 'LOADING_SCENE', expect.any(File), expect.any(String), '1')
expect(api.bindLoadingPhoto).toHaveBeenCalledWith('PC/1', 'LOADING_SCENE', expect.objectContaining({ recordVersion: '0', fileId: '101' }))
expect(vi.mocked(api.bindLoadingPhoto).mock.calls[0]![2].uploadKey).toBe(vi.mocked(api.uploadLoadingPhoto).mock.calls[0]![3])
expect(wrapper.emitted('saved')).toHaveLength(1)
expect(wrapper.text()).toContain('图片读取失败')
expect(table.get('tbody tr').text()).toContain('loading.png')
wrapper.unmount()
})
it('stops a batch when re-review changes the source during upload instead of rebasing old files', async () => {
const wrapper = setup(); await flushPromises()
vi.mocked(api.uploadLoadingPhoto).mockImplementationOnce(async () => {
vi.mocked(api.getLoadingDocuments).mockResolvedValue(data({ recordVersion: '8', dispatchRecordVersion: '12', reviewRound: '2' }))
return photo
})
await upload(wrapper, [new File(['a'], 'old-driver.png', { type: 'image/png' }), new File(['b'], 'old-driver-2.png', { type: 'image/png' })])
expect(api.bindLoadingPhoto).not.toHaveBeenCalled()
expect(api.uploadLoadingPhoto).toHaveBeenCalledTimes(1)
expect(vi.mocked(api.uploadLoadingPhoto).mock.calls[0]![4]).toBe('1')
expect(wrapper.text()).toContain('审核轮次已变化')
expect(wrapper.emitted('saved')).toHaveLength(1)
wrapper.unmount()
})
it('shows an imported PDF license as an authenticated document and accepts an exact PDF replacement', async () => {
vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:license')
vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => {})