feat(admin): use smart-loading groups for simulations
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue'
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { Plus } from '@lucide/vue'
|
||||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
|
||||
import { listAllPlatformWarehouses, type PlatformWarehouseSummaryResource } from '@/modules/warehouses'
|
||||
@@ -11,6 +11,7 @@ import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||||
import { CONTAINER_SPECIFICATIONS, applyContainerSpecification, containerSpecification } from '@/shared/domain/containerSpecifications'
|
||||
import { createContainerSimulation } from '../api/platformContainerLoading'
|
||||
import type { ContainerSimulationCreateRequest } from '../types'
|
||||
import { SMART_LOADING_CHANNEL_GROUPS } from '../smartLoadingChannelGroups'
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
loadWarehouseOptions?: () => Promise<PlatformWarehouseSummaryResource[]>
|
||||
@@ -27,15 +28,19 @@ const simulationError = ref<ApiErrorDetails | null>(null)
|
||||
const simulationChannels = ref<OceanLogisticsChannel[]>([])
|
||||
const simulationChannelsLoading = ref(false)
|
||||
const simulationFormRef = ref<FormInstance>()
|
||||
const simulationForm = reactive<ContainerSimulationCreateRequest>({ channelCode: '', warehouseId: '', containerType: '40HC', capacityCbm: 76, maxWeightKg: 28000 })
|
||||
const simulationForm = reactive<ContainerSimulationCreateRequest>({ channelGroupCode: '', warehouseId: '', containerType: '40HC', capacityCbm: 76, maxWeightKg: 28000 })
|
||||
const simulationChannelGroups = computed(() => {
|
||||
const activeCodes = new Set(simulationChannels.value.map((channel) => channel.channelCode))
|
||||
return SMART_LOADING_CHANNEL_GROUPS.filter((group) => group.channelCodes.some((code) => activeCodes.has(code)))
|
||||
})
|
||||
const simulationRules: FormRules<ContainerSimulationCreateRequest> = {
|
||||
warehouseId: [{ required: true, message: '请选择装柜仓库', trigger: 'change' }],
|
||||
channelCode: [{ required: true, message: '请选择物流渠道', trigger: 'change' }],
|
||||
channelGroupCode: [{ required: true, message: '请选择物流渠道', trigger: 'change' }],
|
||||
containerType: [{ required: true, message: '请选择柜型', trigger: 'change' }],
|
||||
}
|
||||
|
||||
async function openSimulation() {
|
||||
Object.assign(simulationForm, { channelCode: '', warehouseId: '', containerType: '40HC', capacityCbm: 76, maxWeightKg: 28000 })
|
||||
Object.assign(simulationForm, { channelGroupCode: '', warehouseId: '', containerType: '40HC', capacityCbm: 76, maxWeightKg: 28000 })
|
||||
simulationChannels.value = []
|
||||
simulationError.value = null
|
||||
simulationOpen.value = true
|
||||
@@ -59,7 +64,7 @@ async function selectSimulationWarehouse(value: unknown) {
|
||||
const sequence = ++channelSequence
|
||||
simulationChannelsLoading.value = false
|
||||
simulationForm.warehouseId = String(value || '')
|
||||
simulationForm.channelCode = ''
|
||||
simulationForm.channelGroupCode = ''
|
||||
simulationChannels.value = []
|
||||
simulationError.value = null
|
||||
if (!simulationForm.warehouseId) return
|
||||
@@ -92,7 +97,7 @@ async function saveSimulation() {
|
||||
<el-alert title="模拟柜用于先排货,服务端会生成稳定的柜次号;此处不填写集装箱号和船期。" type="info" :closable="false" />
|
||||
<el-form ref="simulationFormRef" :model="simulationForm" :rules="simulationRules" label-position="top" class="simulation-form">
|
||||
<el-form-item label="装柜仓库" prop="warehouseId"><el-select :model-value="simulationForm.warehouseId" filterable placeholder="请选择国内装柜仓库" :loading="warehouseLoading" @update:model-value="selectSimulationWarehouse"><el-option v-for="warehouse in warehouses" :key="String(warehouse.id)" :label="warehouse.warehouseName" :value="String(warehouse.id)" /></el-select></el-form-item>
|
||||
<el-form-item label="物流渠道" prop="channelCode"><el-select v-model="simulationForm.channelCode" filterable :disabled="!simulationForm.warehouseId || simulationChannelsLoading" :loading="simulationChannelsLoading" :placeholder="simulationForm.warehouseId ? '请选择物流渠道' : '请先选择装柜仓库'" no-data-text="当前仓库暂无有效渠道"><el-option v-for="channel in simulationChannels" :key="channel.channelCode" :label="channel.channelName" :value="channel.channelCode" /></el-select></el-form-item>
|
||||
<el-form-item label="物流渠道" prop="channelGroupCode"><el-select v-model="simulationForm.channelGroupCode" filterable :disabled="!simulationForm.warehouseId || simulationChannelsLoading" :loading="simulationChannelsLoading" :placeholder="simulationForm.warehouseId ? '请选择物流渠道' : '请先选择装柜仓库'" no-data-text="当前仓库暂无有效渠道"><el-option v-for="group in simulationChannelGroups" :key="group.code" :label="group.name" :value="group.code" /></el-select></el-form-item>
|
||||
<el-form-item label="柜型" prop="containerType"><el-select :model-value="simulationForm.containerType" @update:model-value="selectSimulationContainerType(String($event || ''))"><el-option v-for="item in CONTAINER_SPECIFICATIONS" :key="item.type" :label="item.type" :value="item.type" /></el-select></el-form-item>
|
||||
<el-form-item label="固定规格"><el-input :model-value="`${containerSpecification(simulationForm.containerType)?.capacityCbm || '—'} m³ / ${containerSpecification(simulationForm.containerType)?.maxWeightKg || '—'} kg`" disabled /></el-form-item>
|
||||
</el-form>
|
||||
|
||||
@@ -11,7 +11,7 @@ vi.mock('../api/platformContainerLoading', () => ({ linkContainerOceanBooking: v
|
||||
|
||||
const container = {
|
||||
id: '9001', cabinetNo: 'CAB-1', bookingNo: 'CB-1', containerNo: null, planId: '7001', planStatus: 'EDITING',
|
||||
sourceType: 'SIMULATION', warehouseId: '8', warehouseName: '义乌仓', channelCode: 'ZBYHK', channelName: '美森正班',
|
||||
sourceType: 'SIMULATION', warehouseId: '8', warehouseName: '义乌仓', channelGroupCode: 'MATSON_CLX', channelCode: null, channelName: null,
|
||||
oceanBookingId: null, billOfLadingNo: null, vesselName: null, voyageNo: null, containerType: '40HC',
|
||||
shippingCompanyId: null, shippingCompanyName: null, departurePortCode: null, departurePortName: null,
|
||||
destinationPortCode: null, destinationPortName: null, destinationWarehouseCode: null, destinationWarehouseName: null,
|
||||
@@ -35,7 +35,7 @@ describe('OceanBookingLinkDialog', () => {
|
||||
it('loads matching released-booking requirements when opened', async () => {
|
||||
const wrapper = mount(OceanBookingLinkDialog, { props: { modelValue: true, container }, global: { plugins: [ElementPlus] } })
|
||||
await flushPromises()
|
||||
expect(listOceanBookingLinkCandidates).toHaveBeenCalledExactlyOnceWith({ warehouseId: '8', channelCode: 'ZBYHK', containerType: '40HC', page: 1, size: 200 })
|
||||
expect(listOceanBookingLinkCandidates).toHaveBeenCalledExactlyOnceWith({ warehouseId: '8', channelGroupCode: 'MATSON_CLX', containerType: '40HC', page: 1, size: 200 })
|
||||
expect((wrapper.vm as unknown as { candidates: Array<{ label: string }> }).candidates[0]?.label).toContain('OB-101 · 40HC × 1')
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
@@ -35,16 +35,16 @@ async function loadCandidates() {
|
||||
error.value = null
|
||||
if (!target) return
|
||||
const warehouseId = optional(target.warehouseId)
|
||||
const channelCode = optional(target.channelCode)
|
||||
const channelGroupCode = optional(target.channelGroupCode)
|
||||
const containerType = optional(target.containerType)
|
||||
if (!warehouseId || !channelCode || !containerType) {
|
||||
error.value = { message: '当前模拟柜缺少仓库、渠道或柜型,无法查询可关联订舱' }
|
||||
if (!warehouseId || !channelGroupCode || !containerType) {
|
||||
error.value = { message: '当前模拟柜缺少仓库、计划渠道或柜型,无法查询可关联订舱' }
|
||||
return
|
||||
}
|
||||
const sequence = ++requestSequence
|
||||
loading.value = true
|
||||
try {
|
||||
const result = await listOceanBookingLinkCandidates({ warehouseId, channelCode, containerType, page: 1, size: 200 })
|
||||
const result = await listOceanBookingLinkCandidates({ warehouseId, channelGroupCode, containerType, page: 1, size: 200 })
|
||||
if (sequence !== requestSequence || !props.modelValue || props.container?.id !== target.id) return
|
||||
candidates.value = result.records.map((candidate) => ({
|
||||
key: `${candidate.oceanBookingId}:${candidate.requirementId}`,
|
||||
|
||||
Reference in New Issue
Block a user