feat(appointment): complete customer receiving and trip continuation workflows

This commit is contained in:
2026-10-16 15:41:56 +08:00
parent 148085286d
commit 45f662ecf2
45 changed files with 2042 additions and 131 deletions
@@ -22,7 +22,7 @@ const trip: FleetTrip = { tripNo: 'TR-TEST', status: 'PLANNED', warehouseId: '92
plannedDepartureAt: '2026-10-03T00:00:00Z', expectedDriverEndAt: '2026-10-04T00:00:00Z', departedAt: null, completedAt: null,
stops: [{ name: '起点' }, { name: 'ONT8', destinationKey: 'FBA:ONT8' }], remark: null, recordVersion: 3, updatedAt: '2026-10-02T00:00:00Z', vehicleRelease: null, driverRelease: null }
const reviewState = { status: 'PENDING' as const, reason: null, reviewerId: null, reviewedAt: null, recordVersion: null }
const detail: TripDetail = { trip, driverQualification: null, dispatches: [], loadingReview: reviewState }
const detail: TripDetail = { trip, driverQualification: null, previousDispatches: [], appointmentHistoryUnavailableReason: null, dispatches: [], loadingReview: reviewState }
const layout = { template: '<div><slot name="header"/><slot/></div>' }
const input = { name: 'ElInput', props: ['modelValue'], emits: ['update:modelValue'], template: '<input :value="modelValue" @input="$emit(\'update:modelValue\',$event.target.value)" />' }
const dialog = { name: 'AppDialog', props: ['modelValue'], emits: ['confirm'], template: '<div v-if="modelValue"><slot/><button @click="$emit(\'confirm\')">保存计划</button></div>' }
@@ -53,6 +53,18 @@ it('returns from a trip to the exact appointment work context and preserves it w
const page = mount(TripDetailView); await flushPromises(); await button(page, '返回预约工作').trigger('click')
expect(state.push).toHaveBeenLastCalledWith(tripQuery.appointmentReturn)
})
it('loads customer appointment cards only on request and navigates using a resolved work key', async () => {
const previousDispatches = [{ dispatchNo: 'DS-REMOVED', customerId: 'A', recordVersion: 2, recordedAt: trip.updatedAt, appointmentBatches: [] }]
vi.mocked(api.getTrip).mockResolvedValue({ ...structuredClone(detail), previousDispatches, appointmentHistoryUnavailableReason: '历史证据不完整' })
const page = mount(TripDetailView); await flushPromises()
expect(page.findComponent({ name: 'TripAppointmentPanel' }).exists()).toBe(false)
await button(page, '核对客户预约与后送去向').trigger('click'); await flushPromises()
const panel = page.getComponent({ name: 'TripAppointmentPanel' })
expect(panel.props()).toMatchObject({ previousDispatches, historyUnavailableReason: '历史证据不完整' })
expect(panel.props('dispatches')).toEqual([])
panel.vm.$emit('work', 'real-child-work'); await flushPromises()
expect(state.push).toHaveBeenLastCalledWith({ path: '/appointment-calendar', query: { workItem: 'real-child-work' } })
})
it('shows mixed signature and return quantities from one lazy completion read', async () => {
vi.mocked(api.getTrip).mockResolvedValue({ ...structuredClone(detail), trip: { ...trip, status: 'IN_TRANSIT' } })
@@ -129,7 +141,7 @@ it('counts a shared physical pallet once and submits only operator-entered measu
})
it('submits a specific dispatch review with optional approval notes and a reason when rejecting', async () => {
const pending: TripDetail = { ...structuredClone(detail), dispatches: [{ dispatchNo: 'DP-A', customerId: 'A', packageCount: 10, boundBy: '1', boundAt: trip.updatedAt, review: reviewState }] }
const pending: TripDetail = { ...structuredClone(detail), dispatches: [{ appointmentBatches: [], dispatchNo: 'DP-A', customerId: 'A', packageCount: 10, boundBy: '1', boundAt: trip.updatedAt, review: reviewState }] }
vi.mocked(api.getTrip).mockResolvedValue(pending)
vi.mocked(api.reviewTripDispatch).mockResolvedValue({ ...pending, trip: { ...trip, recordVersion: 4 } })
const wrapper = mount(TripDetailView); await flushPromises()