This commit is contained in:
2026-10-21 18:11:48 +08:00
parent c367fadf09
commit 872d197531
9 changed files with 168 additions and 39 deletions
@@ -2,19 +2,74 @@ import { mount, flushPromises } from '@vue/test-utils'
import ElementPlus from 'element-plus'
import { beforeEach, expect, it, vi } from 'vitest'
import TransportExecutionPanel from './TransportExecutionPanel.vue'
import { commandTransport, getTransport, uploadTransport, type TransportDetail } from '../api/transportTasks'
import { assignTransport, commandTransport, getTransport, uploadTransport, type TransportDetail } from '../api/transportTasks'
import { getDriver } from '@/modules/overseas-master-data'
import DispatchEvidenceFiles from './DispatchEvidenceFiles.vue'
vi.mock('vue-router', () => ({ onBeforeRouteLeave: vi.fn() }))
vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({ can: () => true }) }))
vi.mock('@/modules/overseas-master-data', () => ({ listVehicles: vi.fn(), listDrivers: vi.fn(), pickupStatusLabels: { ASSIGNED: '待取货', PICKED: '已取货', ARRIVED: '已到仓' } }))
vi.mock('@/modules/overseas-master-data', () => ({ listVehicles: vi.fn(), listDrivers: vi.fn(), getDriver: vi.fn(), downloadDriverLicense: vi.fn(), pickupStatusLabels: { ASSIGNED: '待取货', PICKED: '已取货', ARRIVED: '已到仓' } }))
vi.mock('../api/transportTasks', () => ({ getTransport: vi.fn(), assignTransport: vi.fn(), transportOccupancy: vi.fn(), cancelTransport: vi.fn(), releaseTransport: vi.fn(), commandTransport: vi.fn(), uploadTransport: vi.fn(), downloadTransportEvidence: vi.fn(), transportLabels: { PICKUP: '到仓取货', LOCAL_TRUCK: '本土卡车' } }))
const facts = (arriving = false): TransportDetail => ({ dispatchNo: 'PC1', taskType: 'PICKUP', execution: { trip: { status: arriving ? 'IN_TRANSIT' : 'PLANNED', recordVersion: 4, warehouseName: '洛杉矶仓', vehicleId: '1', vehiclePlate: 'CA-1', driverId: '2', driverName: '司机甲', driverPhone: '555' } }, pickups: [{ customerId: 'C1', transferNo: 'WT1', packageCount: 5, actualPackages: arriving ? 5 : null, status: arriving ? 'PICKED' : 'ASSIGNED', evidence: [] }], completionEvidence: [] }) as unknown as TransportDetail
const options = { global: { plugins: [ElementPlus] } }
beforeEach(() => {
vi.clearAllMocks(); vi.stubGlobal('ResizeObserver', class { observe() {} unobserve() {} disconnect() {} })
vi.mocked(getTransport).mockResolvedValue(facts())
vi.mocked(getDriver).mockResolvedValue({ id: '2', licenseFileId: '33' } as Awaited<ReturnType<typeof getDriver>>)
vi.mocked(uploadTransport).mockResolvedValue({ fileId: '88', fileName: '现场.jpg', contentType: 'image/jpeg', sizeBytes: 10 })
})
const photoOptions = { global: { plugins: [ElementPlus], stubs: { DispatchEvidenceFiles: true } } }
it('refreshes the selected driver master license when reopening resource maintenance', async () => {
const value = facts()
value.resources = { vehicle: null, driver: { driverId: '2', evidence: { photoFileIds: ['22'], fileIds: ['30'], masterLicenseFileId: '30' } } } as TransportDetail['resources']
vi.mocked(getTransport).mockResolvedValue(value)
const wrapper = mount(TransportExecutionPanel, { ...photoOptions, props: { dispatchNo: 'PC1', editResources: true } }); await flushPromises()
const license = wrapper.findAllComponents(DispatchEvidenceFiles)[2]!
expect(license.props('referenceIds')).toEqual(['33'])
expect(license.props('modelValue')).toEqual([])
wrapper.unmount()
})
it('requires three categories and sends photos with both current versions for review', async () => {
const value = facts(); value.dispatchRecordVersion = 9
vi.mocked(getTransport).mockResolvedValue(value)
vi.mocked(assignTransport).mockResolvedValue(value)
const wrapper = mount(TransportExecutionPanel, { ...photoOptions, props: { dispatchNo: 'PC1', editResources: true } }); await flushPromises()
const save = wrapper.findAll('button').find(button => button.text() === '保存车辆司机')!
expect(save.attributes('disabled')).toBeDefined()
const cards = wrapper.findAllComponents(DispatchEvidenceFiles)
expect(cards.map(card => card.props('label'))).toEqual(['车辆图片', '司机照片', '驾驶证照片'])
expect(cards[2]!.props('referenceIds')).toEqual(['33'])
cards[0]!.vm.$emit('update:modelValue', ['21']); cards[1]!.vm.$emit('update:modelValue', ['22']); await flushPromises()
expect(save.attributes('disabled')).toBeUndefined()
await save.trigger('click'); await flushPromises()
expect(assignTransport).toHaveBeenCalledWith('PC1', expect.objectContaining({ recordVersion: 4, dispatchRecordVersion: 9, vehicleId: '1', driverId: '2', masterLicenseFileId: '33', reviewPhotos: { vehicleFileIds: ['21'], driverPhotoFileIds: ['22'], licenseFileIds: [] } }))
wrapper.unmount()
})
it('restores saved photos and retains uploads after a failed save', async () => {
const value = facts(); value.dispatchRecordVersion = 9
value.resources = { vehicle: { vehicleId: '1', evidenceFileIds: ['21'] }, driver: { driverId: '2', evidence: { photoFileIds: ['22'], fileIds: ['23'] } } } as TransportDetail['resources']
vi.mocked(getTransport).mockResolvedValue(value); vi.mocked(assignTransport).mockRejectedValue(new Error('保存失败'))
const wrapper = mount(TransportExecutionPanel, { ...photoOptions, props: { dispatchNo: 'PC1', editResources: true } }); await flushPromises()
const cards = wrapper.findAllComponents(DispatchEvidenceFiles)
expect(cards.map(card => card.props('modelValue'))).toEqual([['21'], ['22'], ['23']])
expect(getDriver).not.toHaveBeenCalled()
cards[0]!.vm.$emit('update:modelValue', ['24']); await flushPromises()
await wrapper.findAll('button').find(button => button.text() === '保存车辆司机')!.trigger('click'); await flushPromises()
expect(cards[0]!.props('modelValue')).toEqual(['24'])
expect(wrapper.emitted('saved')).toBeUndefined(); wrapper.unmount()
})
it('clears old photos when selecting another vehicle or driver and ignores late license responses', async () => {
let resolve!: (value: Awaited<ReturnType<typeof getDriver>>) => void
vi.mocked(getDriver).mockImplementationOnce(() => new Promise(done => { resolve = done })).mockResolvedValue({ id: '4', licenseFileId: '44' } as Awaited<ReturnType<typeof getDriver>>)
const wrapper = mount(TransportExecutionPanel, { ...photoOptions, props: { dispatchNo: 'PC1', editResources: true } }); await flushPromises()
const cards = wrapper.findAllComponents(DispatchEvidenceFiles)
cards[0]!.vm.$emit('update:modelValue', ['21']); cards[1]!.vm.$emit('update:modelValue', ['22']); cards[2]!.vm.$emit('update:modelValue', ['23'])
const selectors = wrapper.findAllComponents({ name: 'ElSelect' })
selectors[0]!.vm.$emit('update:modelValue', '3'); selectors[1]!.vm.$emit('update:modelValue', '4'); await flushPromises()
resolve({ id: '2', licenseFileId: '33' } as Awaited<ReturnType<typeof getDriver>>); await flushPromises()
expect(cards.map(card => card.props('modelValue'))).toEqual([[], [], []])
expect(cards[2]!.props('referenceIds')).toEqual(['44']); wrapper.unmount()
})
it.each([false, true])('confirms the %s photo stage with its own evidence and current transport version', async arriving => {
vi.mocked(getTransport).mockResolvedValue(facts(arriving))
const wrapper = mount(TransportExecutionPanel, { ...options, props: { dispatchNo: 'PC1', pickupApproved: true } }); await flushPromises()