feat: add tail order controls and terminal drayage UI

This commit is contained in:
2026-09-07 23:58:18 +08:00
parent 36d34b7d1d
commit 74d883a443
11 changed files with 664 additions and 51 deletions
@@ -11,12 +11,15 @@ import QueryToolbar from '@/shared/components/QueryToolbar.vue'
import { listAllPlatformWarehouses } from '@/modules/warehouses'
import {
changeContainerReceivingWarehouse,
createManualInboundPlan,
convertLastMileContainerToInboundPlans,
convertLastMileOrderToInboundPlan,
getContainerPlanProgress,
listContainerPlanProgress,
listLastMileOrders,
transferLastMileOrders,
getLastMileOrderManualNodes,
updateLastMileOrderHold,
updateLastMileOrderManualNodes,
} from '../api/lastMileOrders'
import LastMileOrdersView from './LastMileOrdersView.vue'
@@ -26,7 +29,8 @@ vi.mock('@/modules/warehouses', () => ({ listAllPlatformWarehouses: vi.fn() }))
vi.mock('../api/lastMileOrders', () => ({
listLastMileOrders: vi.fn(), listContainerPlanProgress: vi.fn(), getContainerPlanProgress: vi.fn(),
changeContainerReceivingWarehouse: vi.fn(),
convertLastMileOrderToInboundPlan: vi.fn(), convertLastMileContainerToInboundPlans: vi.fn(), transferLastMileOrders: vi.fn(),
createManualInboundPlan: vi.fn(), convertLastMileContainerToInboundPlans: vi.fn(), transferLastMileOrders: vi.fn(),
getLastMileOrderManualNodes: vi.fn(), updateLastMileOrderHold: vi.fn(), updateLastMileOrderManualNodes: vi.fn(),
downloadLastMileOrders: vi.fn(),
}))
@@ -121,7 +125,10 @@ describe('LastMileOrdersView pending order and container conversion', () => {
summary,
orders: [{ orderId: '2092873463568596994', customerId, orderNo: 'LMO-1', inboundCode: 'IN-1', orderStatus: 'INBOUND_PLANNED', planId: '2092873463568597010', planNo: 'LIP-1', planStatus: 'PENDING_RECEIVING', planValid: true, plannedArrivalAt: '2026-09-01T08:00:00' }],
})
vi.mocked(convertLastMileOrderToInboundPlan).mockReset().mockResolvedValue({})
vi.mocked(createManualInboundPlan).mockReset().mockResolvedValue({} as never)
vi.mocked(getLastMileOrderManualNodes).mockReset().mockResolvedValue({ orderId: '2092873463568596994', manualOverrideEnabled: false, nodes: [], version: 0 })
vi.mocked(updateLastMileOrderHold).mockReset().mockResolvedValue({ orderId: '2092873463568596994', held: true, chargeable: true, version: 1 })
vi.mocked(updateLastMileOrderManualNodes).mockReset().mockResolvedValue({ orderId: '2092873463568596994', manualOverrideEnabled: true, nodes: [], version: 1 })
vi.mocked(convertLastMileContainerToInboundPlans).mockReset().mockResolvedValue([{ planNo: 'LIP-1' }, { planNo: 'LIP-2' }] as never)
vi.mocked(changeContainerReceivingWarehouse).mockReset().mockResolvedValue({ ...summary, overseasWarehouseId: '9007199254740996', overseasWarehouseCode: 'LAX02', overseasWarehouseName: '洛杉矶二号接柜仓', assignmentVersion: 8 })
vi.mocked(listAllPlatformWarehouses).mockReset().mockResolvedValue([fbaWarehouse] as never)
@@ -180,13 +187,15 @@ describe('LastMileOrdersView pending order and container conversion', () => {
expect(wrapper.text()).not.toContain('尾程订单加载失败')
})
it('keeps single-order conversion isolated with long order and customer ids', async () => {
it('opens the required single-order inbound-plan form without invoking the cabinet conversion', async () => {
const wrapper = await openPending()
await selectRows(wrapper, [selectedRow()])
await button(wrapper, '生成入库计划')!.trigger('click')
await flushPromises()
expect(convertLastMileOrderToInboundPlan).toHaveBeenCalledTimes(1)
expect(convertLastMileOrderToInboundPlan).toHaveBeenCalledWith({ orderId: '2092873463568596994', customerId })
expect(document.body.textContent).toContain('计划入库仓')
expect(document.body.textContent).toContain('预计到仓日期')
expect(document.body.textContent).toContain('托盘类型')
expect(createManualInboundPlan).not.toHaveBeenCalled()
expect(convertLastMileContainerToInboundPlans).not.toHaveBeenCalled()
expect(listContainerPlanProgress).not.toHaveBeenCalled()
})
@@ -211,7 +220,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
await flushPromises()
expect(convertLastMileContainerToInboundPlans).toHaveBeenCalledTimes(1)
expect(convertLastMileContainerToInboundPlans).toHaveBeenCalledWith({ sourceContainerBookingId: bookingId })
expect(convertLastMileOrderToInboundPlan).not.toHaveBeenCalled()
expect(createManualInboundPlan).not.toHaveBeenCalled()
expect(document.body.textContent).toContain('已按柜生成 2 个独立入库计划')
expect(listLastMileOrders).toHaveBeenCalledTimes(3)
})
@@ -435,7 +444,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
expect(source).toContain('.container-detail-summary > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }')
expect(source).toContain('.container-workbench-card__warehouse strong { min-width: 0; overflow: hidden; color: var(--cc-text); font-size: 14px; text-overflow: ellipsis; white-space: nowrap; }')
expect(source).toContain('.container-workbench-card__actions { display: flex; justify-content: flex-end; gap: 8px; padding-top: 2px; white-space: nowrap; }')
expect(source).toContain("{ key: 'actions', label: '操作', width: 110, fixed: 'left'")
expect(source).toContain("{ key: 'actions', label: '操作', width: 178, fixed: 'left'")
})
it('loads only active FBA master data and submits exact string fields for a cross-customer transfer', async () => {