修复智能装柜订单状态多选

This commit is contained in:
2026-09-03 22:15:25 +08:00
parent 38b435a7ac
commit 475b2a08e2
5 changed files with 35 additions and 24 deletions
@@ -68,7 +68,10 @@ describe('platform container loading API', () => {
.mockResolvedValueOnce({ code: '200', data: container })
.mockResolvedValueOnce({ code: '200', data: { records: [{ accountId: 1, customerId: 1, inboundId: 2, orderId: 4, orderNo: 'ORD-1', inboundCode: 'IN-1', orderStatusSnapshot: 'WAIT_SPLIT', expectedArrivalAt: '2026-09-10T09:30:00', warehouseId: 5, destinationWarehouseId: 6, channelCode: 'NY_EXX', channelName: '纽约EXX海铁', customerName: '示例企业', goodsSummary: '服装、鞋类', availablePackageCount: '7', unitGrossWeightKg: '12.5', unitNetWeightKg: '11.8', averageUnitVolumeCbm: '0.187', availableVolumeCbm: '1.309', coLoadGroupId: 9007199254740999n, groupNo: 'CLG-9007199254740999', memberCount: '3', groupTotalBoxes: '21', locked: false, blockReason: null, groupMembers: [] }], page: '1', size: '20', total: '1', pages: '1' } })
expect((await getContainer('9')).id).toBe(String(container.id))
expect(await listSmartLoadingCandidates({ channelGroupCodes: ['EXX', 'HEDE'], warehouseId: '5', page: 1, size: 20 })).toMatchObject({ records: [{ accountId: '1', customerId: '1', inboundId: '2', inboundCode: 'IN-1', orderStatusSnapshot: 'WAIT_SPLIT', expectedArrivalAt: '2026-09-10T09:30:00', destinationWarehouseId: '6', channelCode: 'NY_EXX', channelName: '纽约EXX海铁', goodsSummary: '服装、鞋类', availablePackageCount: 7, unitGrossWeightKg: 12.5, unitNetWeightKg: 11.8, averageUnitVolumeCbm: 0.187, availableVolumeCbm: 1.309, coLoadGroupId: '9007199254740999', groupNo: 'CLG-9007199254740999', memberCount: 3, groupTotalBoxes: 21, locked: false, blockReason: null }], total: 1 })
expect(await listSmartLoadingCandidates({ channelGroupCodes: ['EXX', 'HEDE'], warehouseId: '5', orderStatuses: ['PENDING_REVIEW', 'WAIT_MEASURE'], page: 1, size: 20 })).toMatchObject({ records: [{ accountId: '1', customerId: '1', inboundId: '2', inboundCode: 'IN-1', orderStatusSnapshot: 'WAIT_SPLIT', expectedArrivalAt: '2026-09-10T09:30:00', destinationWarehouseId: '6', channelCode: 'NY_EXX', channelName: '纽约EXX海铁', goodsSummary: '服装、鞋类', availablePackageCount: 7, unitGrossWeightKg: 12.5, unitNetWeightKg: 11.8, averageUnitVolumeCbm: 0.187, availableVolumeCbm: 1.309, coLoadGroupId: '9007199254740999', groupNo: 'CLG-9007199254740999', memberCount: 3, groupTotalBoxes: 21, locked: false, blockReason: null }], total: 1 })
const candidateParams = vi.mocked(http.get).mock.calls.at(-1)?.[1]?.params as URLSearchParams
expect(candidateParams.getAll('orderStatuses')).toEqual(['PENDING_REVIEW', 'WAIT_MEASURE'])
expect(candidateParams.has('orderStatus')).toBe(false)
const params = vi.mocked(http.get).mock.calls.at(-1)?.[1]?.params as URLSearchParams
expect(params.getAll('channelGroupCodes')).toEqual(['EXX', 'HEDE'])
expect(params.get('warehouseId')).toBe('5')
@@ -112,7 +115,7 @@ describe('platform container loading API', () => {
page: '1', size: '200', total: '1', pages: '1',
} })
const result = await listSmartLoadingWorkbenchContainers({ channelGroupCodes: ['MATSON_CLX', 'EXX'], customerId: '12', inboundCode: 'IN-1', createdStart: '2026-09-01', createdEnd: '2026-09-03', orderStatus: 'WAIT_SPLIT', page: 1, size: 200 })
const result = await listSmartLoadingWorkbenchContainers({ channelGroupCodes: ['MATSON_CLX', 'EXX'], customerId: '12', inboundCode: 'IN-1', createdStart: '2026-09-01', createdEnd: '2026-09-03', orderStatuses: ['WAIT_SPLIT'], page: 1, size: 200 })
expect(result).toMatchObject({ records: [{ bookingId: '9007199254740993', warehouseId: '8', planId: '9007199254740994', version: 2 }], total: 1 })
const params = vi.mocked(http.get).mock.calls.at(-1)?.[1]?.params as URLSearchParams
@@ -121,7 +124,8 @@ describe('platform container loading API', () => {
expect(params.get('inboundCode')).toBe('IN-1')
expect(params.get('createdStart')).toBe('2026-09-01')
expect(params.get('createdEnd')).toBe('2026-09-03')
expect(params.get('orderStatus')).toBe('WAIT_SPLIT')
expect(params.has('orderStatus')).toBe(false)
expect(params.has('orderStatuses')).toBe(false)
expect(params.has('bookingId')).toBe(false)
})
@@ -228,20 +228,23 @@ export async function completeContainerClearance(containerId: string, expectedVe
return document(unwrapApiResponse(response))
}
function smartLoadingQueryParams(query: SmartLoadingCandidateQuery) {
function smartLoadingQueryParams(query: SmartLoadingCandidateQuery, includeOrderStatuses = false) {
const params = new URLSearchParams()
for (const channelGroupCode of query.channelGroupCodes) params.append('channelGroupCodes', channelGroupCode)
if (includeOrderStatuses) {
for (const orderStatus of query.orderStatuses || []) params.append('orderStatuses', orderStatus)
}
const optionalParams: Array<[string, string | number | undefined]> = [
['warehouseId', query.warehouseId], ['customerId', query.customerId], ['inboundCode', query.inboundCode],
['createdStart', query.createdStart], ['createdEnd', query.createdEnd],
['orderStatus', query.orderStatus], ['page', query.page], ['size', query.size],
['page', query.page], ['size', query.size],
]
for (const [key, value] of optionalParams) if (value !== undefined && value !== '') params.append(key, String(value))
return params
}
export async function listSmartLoadingCandidates(query: SmartLoadingCandidateQuery) {
const params = smartLoadingQueryParams(query)
const params = smartLoadingQueryParams(query, true)
const response = await http.get<ApiEnvelope<SmartLoadingCandidatePage>, ApiEnvelope<SmartLoadingCandidatePage>>(`${smartLoadingPath}/inbound-candidates`, { params })
const result = unwrapApiResponse(response)
return {
@@ -280,7 +280,7 @@ describe('GlobalSmartLoadingView', () => {
expect(wrapper.text()).toContain('MSCU-DG')
const containerRequestCount = vi.mocked(listSmartLoadingWorkbenchContainers).mock.calls.length
wrapper.findComponent(QueryToolbar).vm.$emit('search', { warehouseId: '18', customerId: '', inboundCode: '', createdRange: [], orderStatus: '' })
wrapper.findComponent(QueryToolbar).vm.$emit('search', { warehouseId: '18', customerId: '', inboundCode: '', createdRange: [], orderStatuses: [] })
await flushPromises()
expect(wrapper.text()).toContain('MSCU-DG')
@@ -307,7 +307,7 @@ describe('GlobalSmartLoadingView', () => {
await flushPromises()
const containerRequestCount = vi.mocked(listSmartLoadingWorkbenchContainers).mock.calls.length
wrapper.findComponent(QueryToolbar).vm.$emit('search', { warehouseId: '18', customerId: '', inboundCode: '', createdRange: [], orderStatus: '' })
wrapper.findComponent(QueryToolbar).vm.$emit('search', { warehouseId: '18', customerId: '', inboundCode: '', createdRange: [], orderStatuses: [] })
await flushPromises()
expect(wrapper.find('.channel-tags .el-checkbox-button.is-checked').text()).toBe('美森正班')
@@ -338,7 +338,7 @@ describe('GlobalSmartLoadingView', () => {
const containerRequestCount = vi.mocked(listSmartLoadingWorkbenchContainers).mock.calls.length
wrapper.findComponent(QueryToolbar).vm.$emit('search', {
warehouseId: '', customerId: '', inboundCode: 'NYEXX2609030002', createdRange: [], orderStatus: '',
warehouseId: '', customerId: '', inboundCode: 'NYEXX2609030002', createdRange: [], orderStatuses: [],
})
await flushPromises()
@@ -363,7 +363,7 @@ describe('GlobalSmartLoadingView', () => {
expect(source).toContain(': candidate.availablePackageCount')
})
it('filters candidates by expected arrival range and order status', async () => {
it('filters candidates by creation range and multiple order statuses', async () => {
vi.mocked(listSmartLoadingCandidates)
.mockResolvedValueOnce(candidatePage([
{ ...candidate, inboundId: '51', inboundCode: 'IN-MATCH', orderStatusSnapshot: 'WAIT_SPLIT', expectedArrivalAt: '2026-09-11T08:00:00' },
@@ -376,7 +376,7 @@ describe('GlobalSmartLoadingView', () => {
const containerRequestCount = vi.mocked(listSmartLoadingWorkbenchContainers).mock.calls.length
wrapper.getComponent(QueryToolbar).vm.$emit('search', {
customerId: '', inboundCode: '', orderStatus: 'WAIT_SPLIT', createdRange: ['2026-09-10', '2026-09-12'],
customerId: '', inboundCode: '', orderStatuses: ['WAIT_SPLIT', 'LOADING'], createdRange: ['2026-09-10', '2026-09-12'],
})
await flushPromises()
@@ -384,7 +384,7 @@ describe('GlobalSmartLoadingView', () => {
expect(candidates).toContain('IN-MATCH')
expect(candidates).not.toContain('IN-WRONG-STATUS')
expect(candidates).not.toContain('IN-WRONG-DATE')
expect(listSmartLoadingCandidates).toHaveBeenLastCalledWith({ channelGroupCodes: ['MATSON_CLX'], createdStart: '2026-09-10', createdEnd: '2026-09-12', orderStatus: 'WAIT_SPLIT', page: 1, size: 20 })
expect(listSmartLoadingCandidates).toHaveBeenLastCalledWith({ channelGroupCodes: ['MATSON_CLX'], createdStart: '2026-09-10', createdEnd: '2026-09-12', orderStatuses: ['WAIT_SPLIT', 'LOADING'], page: 1, size: 20 })
expect(listSmartLoadingWorkbenchContainers).toHaveBeenCalledTimes(containerRequestCount)
expect(listSmartLoadingWorkbenchContainers).toHaveBeenLastCalledWith({ channelGroupCodes: ['MATSON_CLX'], page: 1, size: 200 })
})
@@ -520,16 +520,16 @@ describe('GlobalSmartLoadingView', () => {
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
await flushPromises()
const fields = wrapper.findComponent(QueryToolbar).props('fields') as Array<{ key: string; options?: Array<{ label: string; value: string }> }>
expect(fields.map((field) => field.key)).toEqual(['warehouseId', 'customerId', 'inboundCode', 'createdRange', 'orderStatus'])
const fields = wrapper.findComponent(QueryToolbar).props('fields') as Array<{ key: string; options?: Array<{ label: string; value: string }>; props?: Record<string, unknown> }>
expect(fields.map((field) => field.key)).toEqual(['warehouseId', 'customerId', 'inboundCode', 'createdRange', 'orderStatuses'])
expect(fields.find((field) => field.key === 'warehouseId')?.options).toEqual([{ label: '义乌仓', value: '8' }])
expect(fields.find((field) => field.key === 'orderStatus')?.options).toEqual([
{ value: '', label: '全部' },
expect(fields.find((field) => field.key === 'orderStatuses')?.options).toEqual([
{ value: 'PENDING_REVIEW', label: '待审核' },
{ value: 'WAIT_MEASURE', label: '待测量数据' },
{ value: 'WAIT_SPLIT', label: '待分柜' },
{ value: 'LOADING', label: '装柜中' },
])
expect(fields.find((field) => field.key === 'orderStatuses')?.props).toMatchObject({ multiple: true, collapseTags: true, maxCollapseTags: 2 })
expect(source).not.toContain('目的仓')
expect(wrapper.text()).not.toContain('税率')
@@ -63,8 +63,8 @@ const selectedCandidate = ref<SmartLoadingCandidateRow | null>(null)
const assignForm = reactive({ packageCount: 1 })
const quantityDrafts = reactive<Record<string, number>>({})
const planGroupsByInboundId = ref(new Map<string, SmartLoadingPlanCoLoadGroup>())
const query = ref<QueryModel>({ warehouseId: '', customerId: '', inboundCode: '', createdRange: [], orderStatus: '' })
const appliedQuery = ref<QueryModel>({ ...query.value })
const query = ref<QueryModel>({ warehouseId: '', customerId: '', inboundCode: '', createdRange: [], orderStatuses: [] })
const appliedQuery = ref<QueryModel>({ ...query.value, orderStatuses: [] })
const candidatePage = ref(1)
const candidatePageSize = ref(20)
const candidateTotal = ref(0)
@@ -185,7 +185,6 @@ const weightPercent = computed(() => weightLimit.value ? totalWeight.value / wei
const displayedVolumePercent = computed(() => percentage(volumePercent.value))
const displayedWeightPercent = computed(() => percentage(weightPercent.value))
const candidateOrderStatusOptions = [
{ value: '', label: '全部' },
{ value: 'PENDING_REVIEW', label: '待审核' },
{ value: 'WAIT_MEASURE', label: '待测量数据' },
{ value: 'WAIT_SPLIT', label: '待分柜' },
@@ -199,7 +198,7 @@ const queryFields = computed<QueryField[]>(() => [
{ key: 'customerId', type: 'select', label: '客户', placeholder: '全部客户', width: 180, options: customers.value.filter((customer) => customer.status === 'ACTIVE').map((customer) => ({ label: customer.tenantName, value: String(customer.id) })), props: { filterable: true, loading: customersLoading.value } },
{ key: 'inboundCode', type: 'input', label: '入仓码', placeholder: '请输入入仓码', width: 210 },
{ key: 'createdRange', type: 'daterange', label: '创建日期', placeholder: '请选择创建日期范围', width: 260 },
{ key: 'orderStatus', type: 'select', label: '订单状态', placeholder: '全部订单状态', width: 160, options: candidateOrderStatusOptions },
{ key: 'orderStatuses', type: 'select', label: '订单状态', placeholder: '全部订单状态', width: 210, options: candidateOrderStatusOptions, props: { multiple: true, collapseTags: true, collapseTagsTooltip: true, maxCollapseTags: 2 } },
])
const filteredContainers = computed(() => containers.value)
@@ -300,7 +299,9 @@ function candidateQueryParams(): Parameters<typeof listSmartLoadingCandidates>[0
const warehouseId = String(appliedQuery.value.warehouseId || '')
const customerId = String(appliedQuery.value.customerId || '')
const inboundCode = String(appliedQuery.value.inboundCode || '').trim()
const orderStatus = String(appliedQuery.value.orderStatus || '') as typeof params.orderStatus
const orderStatuses = Array.isArray(appliedQuery.value.orderStatuses)
? appliedQuery.value.orderStatuses.map((value) => String(value)).filter(Boolean) as NonNullable<typeof params.orderStatuses>
: []
if (warehouseId) params.warehouseId = warehouseId
if (customerId) params.customerId = customerId
if (inboundCode) params.inboundCode = inboundCode
@@ -308,7 +309,7 @@ function candidateQueryParams(): Parameters<typeof listSmartLoadingCandidates>[0
params.createdStart = createdRange[0]
params.createdEnd = createdRange[1]
}
if (orderStatus) params.orderStatus = orderStatus
if (orderStatuses.length) params.orderStatuses = orderStatuses
return params
}
@@ -443,7 +444,10 @@ async function selectChannels(codes: string[]) {
await loadWorkbenchData()
}
async function applyFilters(model: QueryModel) {
appliedQuery.value = { ...model }
appliedQuery.value = {
...model,
orderStatuses: Array.isArray(model.orderStatuses) ? model.orderStatuses.map((value) => String(value)) : [],
}
candidatePage.value = 1
await loadCandidateData()
}
+1 -1
View File
@@ -279,7 +279,7 @@ export interface SmartLoadingCandidateQuery {
inboundCode?: string
createdStart?: string
createdEnd?: string
orderStatus?: 'PENDING_REVIEW' | 'WAIT_MEASURE' | 'WAIT_SPLIT' | 'LOADING'
orderStatuses?: Array<'PENDING_REVIEW' | 'WAIT_MEASURE' | 'WAIT_SPLIT' | 'LOADING'>
page: number
size: number
}