This commit is contained in:
2026-08-12 18:50:03 +08:00
parent 41a131a33b
commit 2c5f53ec00
29 changed files with 513 additions and 283 deletions
@@ -48,6 +48,12 @@ describe('OceanBookingDetailView interaction contract', () => {
expect(source).toContain('v-model="container.capacityCbm"')
expect(source).toContain('v-model="container.maxWeightKg"')
expect(source).toContain('releaseOceanBooking(id.value, releaseRequest)')
expect(source).toContain('capacityCbm: null')
expect(source).toContain('maxWeightKg: null')
expect(source).toContain('function validateContainerRequirements()')
expect(source).toContain('容量、限重和数量必须大于 0')
expect(source).not.toContain('capacityCbm: 68')
expect(source).not.toContain('maxWeightKg: 28000')
})
it('keeps view, create and edit on the formal detail route with a return action', () => {
@@ -85,7 +85,7 @@ function blankParties(): OceanBookingParty[] {
function blankData(): OceanBookingDraftData {
return {
customerId: '', customerName: '', channelCode: '', channelName: '', warehouseId: '', warehouseName: '', parties: blankParties(),
containerRequirements: [{ containerType: '40HC', containerKind: 'FCL', capacityCbm: 68, maxWeightKg: 28000, quantity: 1 }],
containerRequirements: [{ containerType: '40HC', containerKind: 'FCL', capacityCbm: null, maxWeightKg: null, quantity: 1 }],
}
}
const form = reactive<OceanBookingSaveRequest>({ expectedVersion: 0, data: blankData() })
@@ -151,7 +151,7 @@ function applyDetail(value: OceanBookingDetail) {
estimatedSailingAt: value.estimatedSailingAt, estimatedArrivalAt: value.estimatedArrivalAt,
remark: value.remark,
parties: value.parties.length ? value.parties.map(({ partyType, partyName, contactName, contactPhone, address }) => ({ partyType, partyName, contactName, contactPhone, address })) : blankParties(),
containerRequirements: value.containerRequirements.length ? value.containerRequirements.map(({ containerType, containerKind, capacityCbm, maxWeightKg, quantity }) => ({ containerType, containerKind, capacityCbm, maxWeightKg, quantity })) : [{ containerType: '40HC', containerKind: 'FCL', capacityCbm: 68, maxWeightKg: 28000, quantity: 1 }],
containerRequirements: value.containerRequirements.length ? value.containerRequirements.map(({ containerType, containerKind, capacityCbm, maxWeightKg, quantity }) => ({ containerType, containerKind, capacityCbm: capacityCbm ?? null, maxWeightKg: maxWeightKg ?? null, quantity: quantity ?? null })) : [{ containerType: '40HC', containerKind: 'FCL', capacityCbm: null, maxWeightKg: null, quantity: 1 }],
}
}
async function load() {
@@ -169,6 +169,7 @@ async function save() {
actionError.value = { message: '发货人、收货人和通知人名称均为必填项' }
return
}
if (!validateContainerRequirements()) return
saving.value = true
actionError.value = null
try {
@@ -214,6 +215,7 @@ async function submitChange() {
async function runAction(action: OceanBookingAction) {
if (!detail.value || action === 'change-request') { if (action === 'change-request') openChangeDialog(); return }
actionError.value = null
if (!validateContainerRequirements()) return
try {
const reason = await requiredReason(action)
await ElMessageBox.confirm(`确认执行“${actionLabels[action]}”吗?`, actionLabels[action], { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' })
@@ -227,7 +229,15 @@ async function runAction(action: OceanBookingAction) {
if (cause !== 'cancel' && !(cause instanceof Error && cause.message === 'cancel')) actionError.value = getApiErrorDetails(cause)
} finally { actionSaving.value = false }
}
function addContainer() { form.data.containerRequirements.push({ containerType: '40HC', containerKind: 'FCL', capacityCbm: 68, maxWeightKg: 28000, quantity: 1 }) }
function validateContainerRequirements() {
const invalid = form.data.containerRequirements.find((container) => !(Number(container.capacityCbm) > 0) || !(Number(container.maxWeightKg) > 0) || !(Number(container.quantity) > 0))
if (invalid) {
actionError.value = { message: `箱型 ${invalid.containerType || '未命名'} 的容量、限重和数量必须大于 0,请补充后再提交` }
return false
}
return true
}
function addContainer() { form.data.containerRequirements.push({ containerType: '40HC', containerKind: 'FCL', capacityCbm: null, maxWeightKg: null, quantity: 1 }) }
function removeContainer(index: number) { if (form.data.containerRequirements.length > 1) form.data.containerRequirements.splice(index, 1) }
onMounted(() => { void load(); void loadOptions() })
+1 -1
View File
@@ -1,7 +1,7 @@
export type OceanBookingStatus = 'DRAFT' | 'SUBMITTED' | 'CONFIRMED' | 'RELEASED' | 'FAILED' | 'CHANGE_PENDING' | 'CANCELLED'
export interface OceanBookingParty { id?: string; partyType: string; partyName: string; contactName?: string | null; contactPhone?: string | null; address?: string | null }
export interface OceanBookingContainer { id?: string; containerType: string; quantity: number; containerKind?: string | null; capacityCbm: number; maxWeightKg: number }
export interface OceanBookingContainer { id?: string; containerType: string; quantity: number | null; containerKind?: string | null; capacityCbm: number | null; maxWeightKg: number | null }
export interface OceanBookingContainerAllocation extends OceanBookingContainer { allocatedQuantity?: number | null }
export interface OceanBookingDraftData {
customerId: string; customerName: string; parties: OceanBookingParty[]; containerRequirements: OceanBookingContainer[]