fix(dispatch): 展示审核预约时间并简化装车确认

This commit is contained in:
2026-10-21 19:36:58 +08:00
parent 875d2b2b04
commit dbd7c473c2
6 changed files with 38 additions and 12 deletions
@@ -147,3 +147,13 @@ it('rejects missing stable-reference arrays instead of silently falling back to
await expect(getTrip(trip.tripNo)).rejects.toThrow()
}
})
it('keeps unknown actual load measurements unknown when reading the trip', async () => {
const unknown = structuredClone(confirmed) as unknown as { pallets: Array<{ weightKg: unknown; volumeCbm: unknown }>; segments: Array<{ occupied: { weightKg: unknown; volumeCbm: unknown }; remaining: { weightKg: unknown; volumeCbm: unknown } }> }
unknown.pallets.forEach(row => { row.weightKg = null; row.volumeCbm = null })
unknown.segments.forEach(row => { row.occupied.weightKg = null; row.occupied.volumeCbm = null; row.remaining.weightKg = null; row.remaining.volumeCbm = null })
vi.mocked(http.get).mockResolvedValueOnce(envelope({ tripNo: trip.tripNo, recordVersion: '3', cargo: [cargo], manifestHash: confirmed.manifestHash, confirmedLoad: unknown, blockReason: null }))
const result = await getTripLoading(trip.tripNo)
expect(result.confirmedLoad?.pallets[0]?.weightKg).toBeNull()
expect(result.confirmedLoad?.segments[0]?.remaining.volumeCbm).toBeNull()
})
@@ -19,7 +19,7 @@ type WirePreviousDispatch = Omit<TripPreviousDispatch, 'recordVersion'> & { reco
type WireDetail = Omit<TripDetail, 'trip' | 'dispatches' | 'loadingReview' | 'previousDispatches'> & { trip: WireTrip; dispatches: WireBinding[]; loadingReview: WireReview; previousDispatches: WirePreviousDispatch[] }
type WirePage<T> = { records: T[]; page: Numeric; size: Numeric; total: Numeric; pages: Numeric }
type WireCargo = Omit<TripCargo, 'packages'> & { packages: Numeric }
type WireLoad = { [K in keyof TripLoad]: Numeric }
type WireLoad = { palletSpaces: Numeric; weightKg: Numeric | null; volumeCbm: Numeric | null }
type WireMeasurement = Omit<TripMeasurement, 'palletSpaces' | 'weightKg' | 'volumeCbm' | 'deliveryIndex'> & WireLoad & { deliveryIndex: Numeric }
type WireConfirmed = Omit<TripConfirmedLoad, 'cargo' | 'pallets' | 'segments'> & {
cargo: WireCargo[]; pallets: WireMeasurement[]
@@ -56,7 +56,7 @@ export const normalizeTripDetail = (value: WireDetail): TripDetail => ({ ...valu
const cargo = (value: WireCargo): TripCargo => ({ ...value, packages: integer(value.packages) })
const outcome = (value: WireOutcome): TripOutcome => ({ ...value, packages: integer(value.packages),
signedPackages: integer(value.signedPackages), returnedPackages: integer(value.returnedPackages), remainingPackages: integer(value.remainingPackages) })
const load = (value: WireLoad): TripLoad => ({ palletSpaces: integer(value.palletSpaces), weightKg: number(value.weightKg), volumeCbm: number(value.volumeCbm) })
const load = (value: WireLoad): TripLoad => ({ palletSpaces: integer(value.palletSpaces), weightKg: value.weightKg == null ? null : number(value.weightKg), volumeCbm: value.volumeCbm == null ? null : number(value.volumeCbm) })
function confirmed(value: WireConfirmed | null): TripConfirmedLoad | null {
return value == null ? null : { ...value, cargo: value.cargo.map(cargo),
pallets: value.pallets.map(row => ({ ...row, ...load(row), deliveryIndex: integer(row.deliveryIndex) })),
@@ -104,7 +104,7 @@ onBeforeUnmount(() => { sequence++ })
<template #stop="{ row }"><span v-if="matchingStops(row as PalletRow).length === 1">{{ matchingStops(row as PalletRow)[0]?.name }}</span><el-select v-else v-model="row.deliveryIndex" :disabled="!editable || busy || loading || !matchingStops(row as PalletRow).length" placeholder="请先匹配货物目的地"><el-option v-for="stop in matchingStops(row as PalletRow)" :key="stop.index" :value="stop.index" :label="stop.name" /></el-select></template>
<template v-if="!embedded" #toolbar-actions><el-button @click="load">{{ planning ? '重新读取容量计划' : '重新读取装载' }}</el-button><el-button v-if="editable" type="primary" :loading="busy" :disabled="loading || !!info?.blockReason || !!destinationIssue || !rows.length" @click="save">{{ planning ? '确认计划容量' : '确认实际装载' }}</el-button></template>
</DataTable></div>
<table v-if="savedLoad && !stalePlan" class="density-table"><thead><tr><th>行驶区间</th><th>占用托位</th><th>重量(kg)</th><th>体积(m³)</th><th v-if="planning">剩余托位 / kg / m³</th></tr></thead><tbody><tr v-for="segment in segments" :key="segment.fromIndex"><td>{{ trip.stops[segment.fromIndex]?.name }} → {{ trip.stops[segment.toIndex]?.name }}</td><td>{{ segment.occupied.palletSpaces }} / {{ trip.maxPalletSpaces }}</td><td>{{ segment.occupied.weightKg }} / {{ trip.maxLoadKg }}</td><td>{{ segment.occupied.volumeCbm }} / {{ trip.maxVolumeCbm }}</td><td v-if="planning">{{ segment.remaining.palletSpaces }} / {{ segment.remaining.weightKg }} / {{ segment.remaining.volumeCbm }}</td></tr></tbody></table>
<table v-if="savedLoad && !stalePlan" class="density-table"><thead><tr><th>行驶区间</th><th>占用托位</th><th>重量(kg)</th><th>体积(m³)</th><th v-if="planning">剩余托位 / kg / m³</th></tr></thead><tbody><tr v-for="segment in segments" :key="segment.fromIndex"><td>{{ trip.stops[segment.fromIndex]?.name }} → {{ trip.stops[segment.toIndex]?.name }}</td><td>{{ segment.occupied.palletSpaces }} / {{ trip.maxPalletSpaces }}</td><td>{{ segment.occupied.weightKg ?? '—' }} / {{ trip.maxLoadKg }}</td><td>{{ segment.occupied.volumeCbm ?? '—' }} / {{ trip.maxVolumeCbm }}</td><td v-if="planning">{{ segment.remaining.palletSpaces }} / {{ segment.remaining.weightKg }} / {{ segment.remaining.volumeCbm }}</td></tr></tbody></table>
</div>
</template>
@@ -47,8 +47,8 @@ export interface TripCargo {
dispatchNo: string; customerId: string; dispatchItemId: string; warehousePalletId: string | null
palletNo: string | null; packages: number; destinationKey: string | null; destinationLabel: string | null
}
export interface TripMeasurement { warehousePalletId: string; palletSpaces: number; weightKg: number; volumeCbm: number; deliveryIndex: number }
export interface TripLoad { palletSpaces: number; weightKg: number; volumeCbm: number }
export interface TripMeasurement { warehousePalletId: string; palletSpaces: number; weightKg: number | null; volumeCbm: number | null; deliveryIndex: number }
export interface TripLoad { palletSpaces: number; weightKg: number | null; volumeCbm: number | null }
export interface TripSegment { fromIndex: number; toIndex: number; occupied: TripLoad; remaining: TripLoad }
export interface TripConfirmedLoad { manifestHash: string; cargo: TripCargo[]; pallets: TripMeasurement[]; segments: TripSegment[]; operatorId: string; confirmedAt: string }
export interface TripPlannedLoad { manifestHash: string; cargo: TripCargo[]; pallets: TripMeasurement[]; segments: TripSegment[]; operatorId: string; confirmedAt: string }