优化
This commit is contained in:
@@ -8,7 +8,7 @@ import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam'
|
||||
import { listAllPlatformWarehouses, type PlatformWarehouseSummaryResource } from '@/modules/warehouses'
|
||||
import { listPlatformPartners, type PlatformPartnerResource } from '@/modules/master-data'
|
||||
import { listSuppliers, type Supplier } from '@/modules/suppliers'
|
||||
import { getOceanBooking, listOceanBookings, listWarehouseLogisticsChannels, type OceanBookingResource, type OceanLogisticsChannel } from '@/modules/ocean-bookings'
|
||||
import { listOceanBookingLinkCandidates, listOceanBookings, listWarehouseLogisticsChannels, type OceanBookingResource, type OceanLogisticsChannel } from '@/modules/ocean-bookings'
|
||||
import { uploadLoadingDeclaration } from '@/modules/loading-lists'
|
||||
import AdminPage from '@/shared/components/AdminPage.vue'
|
||||
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
|
||||
@@ -92,8 +92,10 @@ const linkCandidateKey = ref('')
|
||||
const linkContainerNo = ref('')
|
||||
const linkCandidates = ref<Array<{ key: string; oceanBookingId: string; requirementId: string; label: string; disabled: boolean }>>([])
|
||||
const selectedTruckingSupplierId = ref('')
|
||||
const unresolvedTruckingSupplierName = ref<string | null>(null)
|
||||
let listSequence = 0
|
||||
let detailSequence = 0
|
||||
let linkSequence = 0
|
||||
|
||||
function emptyForm(): ContainerForm {
|
||||
return {
|
||||
@@ -101,7 +103,7 @@ function emptyForm(): ContainerForm {
|
||||
billOfLadingNo: null, vesselName: null, voyageNo: null, containerType: '40HC', shippingCompanyId: null,
|
||||
shippingCompanyName: null, departurePortCode: null, departurePortName: null, destinationPortCode: null,
|
||||
destinationPortName: null, destinationWarehouseCode: null, destinationWarehouseName: null,
|
||||
capacityCbm: 76, maxWeightKg: 28000, terminalTruckingCompany: null, loadingAt: null,
|
||||
capacityCbm: 76, maxWeightKg: 28000, terminalTruckingCompanyId: null, terminalTruckingCompany: null, loadingAt: null,
|
||||
estimatedSailingAt: null, actualSailingAt: null, estimatedPortArrivalAt: null, actualPortArrivalAt: null,
|
||||
estimatedWarehouseArrivalAt: null, actualWarehouseArrivalAt: null,
|
||||
}
|
||||
@@ -110,7 +112,7 @@ const form = reactive<ContainerForm>(emptyForm())
|
||||
const rules: FormRules<ContainerForm> = {
|
||||
warehouseId: [{ required: true, message: '请选择装柜仓库', trigger: 'change' }],
|
||||
containerType: [{ required: true, message: '请选择柜型', trigger: 'change' }],
|
||||
terminalTruckingCompany: [{ validator: (_rule, value, callback) => callback(!value || selectedTruckingSupplierId.value ? undefined : new Error('请选择有效的合作中拖车供应商')), trigger: 'change' }],
|
||||
terminalTruckingCompany: [{ validator: (_rule, value, callback) => callback(unresolvedTruckingSupplierName.value || (value && !selectedTruckingSupplierId.value) ? new Error('请选择有效的合作中拖车供应商') : undefined), trigger: 'change' }],
|
||||
}
|
||||
|
||||
const canView = computed(() => access.can('platform:firstleg:container:list'))
|
||||
@@ -205,10 +207,30 @@ function replaceForm(source: ContainerBookingResource | null) {
|
||||
Object.assign(form, emptyForm(), source || {})
|
||||
const specification = containerSpecification(form.containerType)
|
||||
if (specification) applyContainerSpecification(form, specification.type)
|
||||
selectedTruckingSupplierId.value = truckingCompanies.value.find((supplier) => supplier.name === form.terminalTruckingCompany)?.id || ''
|
||||
reconcileTruckingSupplier()
|
||||
}
|
||||
function reconcileTruckingSupplier() {
|
||||
const supplierId = optional(form.terminalTruckingCompanyId)
|
||||
const supplierName = optional(form.terminalTruckingCompany) || unresolvedTruckingSupplierName.value
|
||||
const supplier = supplierId
|
||||
? truckingCompanies.value.find((item) => item.id === supplierId)
|
||||
: truckingCompanies.value.find((item) => item.name === supplierName)
|
||||
selectedTruckingSupplierId.value = supplier?.id || ''
|
||||
unresolvedTruckingSupplierName.value = supplier ? null : supplierName
|
||||
if (supplier) {
|
||||
form.terminalTruckingCompanyId = supplier.id
|
||||
form.terminalTruckingCompany = supplier.name
|
||||
} else if (!supplierName) {
|
||||
form.terminalTruckingCompanyId = null
|
||||
form.terminalTruckingCompany = null
|
||||
} else {
|
||||
form.terminalTruckingCompanyId = null
|
||||
form.terminalTruckingCompany = supplierName
|
||||
}
|
||||
}
|
||||
function payload(): ContainerSaveRequest {
|
||||
const selectedWarehouse = warehouses.value.find((warehouse) => String(warehouse.id) === form.warehouseId)
|
||||
const selectedTruckingSupplier = truckingCompanies.value.find((supplier) => supplier.id === selectedTruckingSupplierId.value)
|
||||
return {
|
||||
containerNo: optional(form.containerNo),
|
||||
warehouseId: form.warehouseId,
|
||||
@@ -219,7 +241,9 @@ function payload(): ContainerSaveRequest {
|
||||
departurePortCode: optional(form.departurePortCode), departurePortName: optional(form.departurePortName),
|
||||
destinationPortCode: optional(form.destinationPortCode), destinationPortName: optional(form.destinationPortName),
|
||||
destinationWarehouseCode: optional(form.destinationWarehouseCode), destinationWarehouseName: optional(form.destinationWarehouseName),
|
||||
capacityCbm: form.capacityCbm, maxWeightKg: form.maxWeightKg, terminalTruckingCompany: optional(form.terminalTruckingCompany),
|
||||
capacityCbm: form.capacityCbm, maxWeightKg: form.maxWeightKg,
|
||||
terminalTruckingCompanyId: selectedTruckingSupplier?.id || null,
|
||||
terminalTruckingCompany: selectedTruckingSupplier?.name || null,
|
||||
loadingAt: form.loadingAt, estimatedSailingAt: form.estimatedSailingAt, actualSailingAt: form.actualSailingAt,
|
||||
estimatedPortArrivalAt: form.estimatedPortArrivalAt, actualPortArrivalAt: form.actualPortArrivalAt,
|
||||
estimatedWarehouseArrivalAt: form.estimatedWarehouseArrivalAt, actualWarehouseArrivalAt: form.actualWarehouseArrivalAt,
|
||||
@@ -230,8 +254,11 @@ function partnerLabel(partner: PlatformPartnerResource) {
|
||||
return `${partner.partnerName}${partner.partnerCode ? `(${partner.partnerCode})` : ''}`
|
||||
}
|
||||
function selectTruckingSupplier(supplierId: string) {
|
||||
if (!supplierId && unresolvedTruckingSupplierName.value) return
|
||||
const supplier = truckingCompanies.value.find((item) => item.id === supplierId)
|
||||
selectedTruckingSupplierId.value = supplier?.id || ''
|
||||
unresolvedTruckingSupplierName.value = null
|
||||
form.terminalTruckingCompanyId = supplier?.id || null
|
||||
form.terminalTruckingCompany = supplier?.name || null
|
||||
}
|
||||
function portLabel(port: PortOption) {
|
||||
@@ -300,7 +327,10 @@ async function loadBusinessOptions() {
|
||||
])
|
||||
if (partnerResult.status === 'fulfilled') partners.value = partnerResult.value
|
||||
else errors.push(getApiErrorDetails(partnerResult.reason, '船公司与拖车公司加载失败').message)
|
||||
if (supplierResult.status === 'fulfilled') truckingSuppliers.value = supplierResult.value.records.filter((supplier) => supplier.category === 'TRUCKING' && supplier.status === 'ACTIVE')
|
||||
if (supplierResult.status === 'fulfilled') {
|
||||
truckingSuppliers.value = supplierResult.value.records.filter((supplier) => supplier.category === 'TRUCKING' && supplier.status === 'ACTIVE')
|
||||
if (form.id) reconcileTruckingSupplier()
|
||||
}
|
||||
else errors.push(getApiErrorDetails(supplierResult.reason, '拖车供应商加载失败').message)
|
||||
if (bookingResult.status === 'fulfilled') oceanBookings.value = bookingResult.value
|
||||
else errors.push(getApiErrorDetails(bookingResult.reason, '港口候选加载失败').message)
|
||||
@@ -351,14 +381,19 @@ async function saveSimulation() {
|
||||
} catch (error) { simulationError.value = getApiErrorDetails(error, '模拟柜创建失败') }
|
||||
finally { simulationSaving.value = false }
|
||||
}
|
||||
async function loadReleasedBookings() {
|
||||
const first = await listOceanBookings({ page: 1, size: 200, status: 'RELEASED' })
|
||||
if (first.pages <= 1) return first.records
|
||||
const rest = await Promise.all(Array.from({ length: first.pages - 1 }, (_, index) => listOceanBookings({ page: index + 2, size: 200, status: 'RELEASED' })))
|
||||
return [first, ...rest].flatMap((result) => result.records)
|
||||
}
|
||||
async function openLink(source: object) {
|
||||
if (linkLoading.value) return
|
||||
const row = source as ContainerBookingResource
|
||||
const warehouseId = optional(row.warehouseId)
|
||||
const channelCode = optional(row.channelCode)
|
||||
if (!warehouseId || !channelCode || row.capacityCbm == null || row.maxWeightKg == null) {
|
||||
linkTarget.value = row
|
||||
linkCandidates.value = []
|
||||
linkError.value = { message: '当前模拟柜缺少仓库、渠道或固定柜型规格,无法查询可关联订舱' }
|
||||
linkOpen.value = true
|
||||
return
|
||||
}
|
||||
const sequence = ++linkSequence
|
||||
linkTarget.value = row
|
||||
linkCandidateKey.value = ''
|
||||
linkContainerNo.value = ''
|
||||
@@ -367,27 +402,24 @@ async function openLink(source: object) {
|
||||
linkOpen.value = true
|
||||
linkLoading.value = true
|
||||
try {
|
||||
const bookings = await loadReleasedBookings()
|
||||
const details = await Promise.all(bookings.map((booking) => getOceanBooking(booking.id)))
|
||||
linkCandidates.value = details.flatMap((booking) => booking.containerRequirements
|
||||
.map((requirement) => ({ ...requirement, remainingQuantity: Math.max(0, Number(requirement.quantity) - booking.containers.filter((container) => container.requirementId === requirement.requirementId).length) }))
|
||||
.filter((requirement) => Boolean(requirement.requirementId) && Number(requirement.remainingQuantity) > 0)
|
||||
.map((requirement) => {
|
||||
const specificationMatches = booking.warehouseId === row.warehouseId
|
||||
&& booking.channelCode === row.channelCode
|
||||
&& requirement.containerType === row.containerType
|
||||
&& Number(requirement.capacityCbm) === Number(row.capacityCbm)
|
||||
&& Number(requirement.maxWeightKg) === Number(row.maxWeightKg)
|
||||
return {
|
||||
key: `${booking.id}:${requirement.requirementId}`,
|
||||
oceanBookingId: booking.id,
|
||||
requirementId: String(requirement.requirementId),
|
||||
disabled: !specificationMatches,
|
||||
label: `${booking.bookingNo || '订舱号待补'} · ${requirement.containerType} × ${requirement.remainingQuantity} · ${booking.customerName || '未关联客户'} · ${booking.warehouseName || '仓库待补'} · ${booking.channelName || '渠道待补'}${specificationMatches ? '' : '(与当前柜规格不一致)'}`,
|
||||
}
|
||||
}))
|
||||
} catch (error) { linkError.value = getApiErrorDetails(error, '已放舱订舱候选加载失败') }
|
||||
finally { linkLoading.value = false }
|
||||
const result = await listOceanBookingLinkCandidates({
|
||||
warehouseId, channelCode, containerType: row.containerType,
|
||||
capacityCbm: row.capacityCbm, maxWeightKg: row.maxWeightKg,
|
||||
page: 1, size: 200,
|
||||
})
|
||||
if (sequence !== linkSequence || linkTarget.value?.id !== row.id) return
|
||||
linkCandidates.value = result.records.map((candidate) => ({
|
||||
key: `${candidate.oceanBookingId}:${candidate.requirementId}`,
|
||||
oceanBookingId: candidate.oceanBookingId,
|
||||
requirementId: candidate.requirementId,
|
||||
disabled: false,
|
||||
label: `${candidate.bookingNo} · ${candidate.containerType} × ${candidate.remainingQuantity} · ${candidate.customerName} · ${candidate.warehouseName} · ${candidate.channelName}`,
|
||||
}))
|
||||
} catch (error) {
|
||||
if (sequence === linkSequence) linkError.value = getApiErrorDetails(error, '已放舱订舱候选加载失败')
|
||||
} finally {
|
||||
if (sequence === linkSequence) linkLoading.value = false
|
||||
}
|
||||
}
|
||||
async function saveLink() {
|
||||
const target = linkTarget.value
|
||||
@@ -521,6 +553,10 @@ async function openEdit(source: object) {
|
||||
catch (error) { ElMessage.error(getApiErrorDetails(error, '货柜详情加载失败').message) }
|
||||
}
|
||||
async function save() {
|
||||
if (unresolvedTruckingSupplierName.value) {
|
||||
editorError.value = { message: '原拖车供应商已不可用,请重新选择合作中的拖车供应商' }
|
||||
return
|
||||
}
|
||||
if (!(await formRef.value?.validate().catch(() => false))) return
|
||||
saving.value = true
|
||||
editorError.value = null
|
||||
@@ -564,6 +600,11 @@ function goContainerProcess(source: object) {
|
||||
watch([page, pageSize], ([nextPage, nextSize], [previousPage, previousSize]) => {
|
||||
if (nextPage !== previousPage || nextSize !== previousSize) void loadRows()
|
||||
})
|
||||
watch(linkOpen, (open) => {
|
||||
if (open) return
|
||||
linkSequence += 1
|
||||
linkLoading.value = false
|
||||
})
|
||||
onMounted(() => { void Promise.all([loadCustomers(), loadWarehouses(), loadBusinessOptions(), loadRows()]) })
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user