Release/test 合并上生产 #2

Merged
admin merged 736 commits from release/test into main 2026-09-17 11:50:56 +08:00
3 changed files with 62 additions and 46 deletions
Showing only changes of commit dc1cbfcf4a - Show all commits
@@ -211,7 +211,7 @@ export interface ConvertContainerInboundPlanPayload {
snapshotHash: string
}
export type ContainerInboundPlanPreviewReadiness = 'READY' | 'PLANNED' | 'BLOCKED'
export type ContainerInboundPlanPreviewReadiness = 'READY' | 'RESOURCE_PENDING' | 'PLANNED' | 'BLOCKED'
export interface ContainerInboundPlanPreviewOrderDto {
customerId: string
@@ -224,6 +224,7 @@ export interface ContainerInboundPlanPreviewOrderDto {
destinationWarehouseId: string | null
destinationWarehouseCode: string | null
destinationWarehouseName: string | null
fbaWarehouseCode: string | null
packageCount: number
volumeCbm: number
suggestedPalletCount: number | null
@@ -120,6 +120,16 @@ async function openPending() {
return wrapper
}
async function openHiddenContainerWorkbench(wrapper: Awaited<ReturnType<typeof mountView>>) {
const view = wrapper.vm as unknown as {
statusTab: '全部' | '待分配' | '柜子'
changeStatusTab(): void
}
view.statusTab = '柜子'
view.changeStatusTab()
await flushPromises()
}
async function selectRows(wrapper: Awaited<ReturnType<typeof mountView>>, rows: object[]) {
wrapper.findComponent(DataTable).vm.$emit('selection-change', rows)
await wrapper.vm.$nextTick()
@@ -178,6 +188,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
expect(wrapper.text()).toContain('按柜批量生成')
expect(wrapper.text()).not.toContain('按订单')
expect(wrapper.text()).not.toContain('待分配整柜')
expect(wrapper.text()).not.toContain('柜子工作台')
expect(listLastMileOrders).toHaveBeenLastCalledWith(expect.objectContaining({ page: 1, size: 10, currentStatus: 'PENDING_ASSIGNMENT' }))
expect(listContainerPlanProgress).not.toHaveBeenCalled()
})
@@ -208,8 +219,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
await flushPromises()
expect(listLastMileOrders).toHaveBeenLastCalledWith(expect.objectContaining({ currentStatus: 'PENDING_ASSIGNMENT', lxCode: 'LX-SEARCH' }))
await wrapper.findAll('.el-radio-button').find((item) => item.text().startsWith('柜子'))!.trigger('click')
await flushPromises()
await openHiddenContainerWorkbench(wrapper)
expect(listContainerPlanProgress).toHaveBeenLastCalledWith({
page: 1, size: 10, containerNo: 'CONT-SEARCH',
createdFrom: '2026-09-01T00:00:00', createdTo: '2026-09-07T23:59:59',
@@ -265,7 +275,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
expect(createManualInboundPlan).toHaveBeenCalledWith(expect.objectContaining({
customerId, orderId: '2092873463568596994', expectedOrderVersion: 3,
plannedArrivalDate: '2026-09-08', palletCount: 2,
plannedArrivalDate: '2026-09-08', palletCount: 5, palletType: 'WOOD',
}), expect.stringContaining('manual-inbound-plan-'))
const payload = vi.mocked(createManualInboundPlan).mock.calls[0][0] as unknown as Record<string, unknown>
expect(payload).not.toHaveProperty('inboundCode')
@@ -373,11 +383,9 @@ describe('LastMileOrdersView pending order and container conversion', () => {
const nullArrival = { ...summary, sourceContainerBookingId: '2092873463568597002', containerNo: 'CONT-NULL', plannedArrivalAt: null, planCount: 0, pendingPlanCount: 15 }
vi.mocked(listContainerPlanProgress).mockResolvedValueOnce({ records: [summary, nullArrival], page: 1, size: 10, total: 2, pages: 1 })
const wrapper = await mountView()
const containerTab = wrapper.findAll('.el-radio-button').find((item) => item.text().startsWith('柜子'))
await containerTab!.trigger('click')
await flushPromises()
await openHiddenContainerWorkbench(wrapper)
expect(listContainerPlanProgress).toHaveBeenLastCalledWith({ page: 1, size: 10, containerNo: undefined, createdFrom: undefined, createdTo: undefined })
expect(wrapper.text()).toContain('柜子工作台(2)')
expect(wrapper.text()).not.toContain('柜子工作台(2)')
expect(wrapper.text()).toContain('货柜工作台')
expect(wrapper.text()).toContain('按柜查看和修改海外接柜仓,并为剩余入仓码生成独立入库计划')
expect(wrapper.text()).toContain('5个入仓码待生成计划')
@@ -401,8 +409,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
}
vi.mocked(listContainerPlanProgress).mockResolvedValueOnce({ records: [ready], page: 1, size: 10, total: 1, pages: 1 })
const wrapper = await mountView()
await wrapper.findAll('.el-radio-button').find((item) => item.text().startsWith('柜子'))!.trigger('click')
await flushPromises()
await openHiddenContainerWorkbench(wrapper)
expect(wrapper.text()).toContain('CONT-15')
expect(wrapper.text()).toContain('0个入仓码待生成计划')
expect((button(wrapper, '生成入库计划')!.element as HTMLButtonElement).disabled).toBe(true)
@@ -412,8 +419,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
it('renders the all-cabinet empty state and unit', async () => {
vi.mocked(listContainerPlanProgress).mockResolvedValueOnce({ records: [], page: 1, size: 10, total: 0, pages: 0 })
const wrapper = await mountView()
await wrapper.findAll('.el-radio-button').find((item) => item.text().startsWith('柜子'))!.trigger('click')
await flushPromises()
await openHiddenContainerWorkbench(wrapper)
expect(wrapper.text()).toContain('暂无货柜数据')
expect(wrapper.text()).not.toContain('已全部生成计划的柜不再展示')
})
@@ -421,9 +427,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
it('uses independent cabinet pagination and opens the complete long-id detail in a dialog', async () => {
vi.mocked(listContainerPlanProgress).mockResolvedValue({ records: [summary], page: 1, size: 10, total: 21, pages: 3 })
const wrapper = await mountView()
const containerTab = wrapper.findAll('.el-radio-button').find((item) => item.text().startsWith('柜子'))
await containerTab!.trigger('click')
await flushPromises()
await openHiddenContainerWorkbench(wrapper)
const pagination = wrapper.findComponent({ name: 'ElPagination' })
pagination.vm.$emit('current-change', 2)
await flushPromises()
@@ -445,12 +449,10 @@ describe('LastMileOrdersView pending order and container conversion', () => {
it('starts cabinet plan generation directly from the workbench card', async () => {
const wrapper = await mountView()
const containerTab = wrapper.findAll('.el-radio-button').find((item) => item.text().startsWith('柜子'))
await containerTab!.trigger('click')
await flushPromises()
await openHiddenContainerWorkbench(wrapper)
await button(wrapper, '生成入库计划')!.trigger('click')
await flushPromises()
expect(document.body.textContent).toContain('预览已就绪,本次将新增 5 个计划、5 个托盘码')
expect(document.body.textContent).toContain('预览已就绪,本次将处理 5 票订单、生成或补齐 5 个托盘码')
const confirm = [...document.body.querySelectorAll('button')].find((item) => item.textContent?.includes('确认生成')) as HTMLButtonElement
expect(confirm.disabled).toBe(false)
confirm.click()
@@ -474,8 +476,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
vi.mocked(listContainerPlanProgress).mockResolvedValueOnce({ records: [invalid], page: 1, size: 10, total: 1, pages: 1 } as never)
vi.mocked(getContainerPlanProgress).mockResolvedValueOnce({ summary: invalid, orders: [] } as never)
const wrapper = await mountView()
await wrapper.findAll('.el-radio-button').find((item) => item.text().startsWith('柜子'))!.trigger('click')
await flushPromises()
await openHiddenContainerWorkbench(wrapper)
expect(wrapper.text()).toContain('柜内接柜仓业务快照不一致')
expect((button(wrapper, '生成入库计划')!.element as HTMLButtonElement).disabled).toBe(true)
@@ -489,8 +490,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
it('shows the receiving warehouse and changes it from ACTIVE OVERSEAS master data with versioning', async () => {
vi.mocked(listAllPlatformWarehouses).mockResolvedValueOnce([currentReceivingWarehouse, receivingWarehouse] as never)
const wrapper = await mountView()
await wrapper.findAll('.el-radio-button').find((item) => item.text().startsWith('柜子'))!.trigger('click')
await flushPromises()
await openHiddenContainerWorkbench(wrapper)
expect(wrapper.text()).toContain('海外接柜仓')
expect(wrapper.text()).toContain('LAX01 · 洛杉矶接柜仓')
expect(wrapper.get('.container-workbench-card__warehouse strong').attributes('title')).toBe('LAX01 · 洛杉矶接柜仓')
@@ -526,8 +526,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
let resolveCandidates!: (value: typeof receivingWarehouse[]) => void
vi.mocked(listAllPlatformWarehouses).mockReturnValueOnce(new Promise((resolve) => { resolveCandidates = resolve }) as never)
const wrapper = await mountView()
await wrapper.findAll('.el-radio-button').find((item) => item.text().startsWith('柜子'))!.trigger('click')
await flushPromises()
await openHiddenContainerWorkbench(wrapper)
await button(wrapper, '修改接柜仓')!.trigger('click')
await wrapper.vm.$nextTick()
const confirm = [...document.body.querySelectorAll('button')].find((item) => item.textContent?.includes('确认修改')) as HTMLButtonElement
@@ -544,8 +543,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
page: 1, size: 10, total: 1, pages: 1,
})
const wrapper = await mountView()
await wrapper.findAll('.el-radio-button').find((item) => item.text().startsWith('柜子'))!.trigger('click')
await flushPromises()
await openHiddenContainerWorkbench(wrapper)
expect(wrapper.text()).toContain('海外仓已开始拆柜收货')
const changeButton = button(wrapper, '修改接柜仓')!
expect(changeButton.exists()).toBe(true)
@@ -557,8 +555,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
it('keeps a disabled receiving-warehouse action discoverable without edit permission', async () => {
canAccess.mockImplementation((permissionCode: string) => permissionCode !== 'platform:overseas-warehouse:last-mile-order:edit')
const wrapper = await mountView()
await wrapper.findAll('.el-radio-button').find((item) => item.text().startsWith('柜子'))!.trigger('click')
await flushPromises()
await openHiddenContainerWorkbench(wrapper)
const changeButton = button(wrapper, '修改接柜仓')!
expect(changeButton.exists()).toBe(true)
expect((changeButton.element as HTMLButtonElement).disabled).toBe(true)
@@ -574,8 +571,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
'version conflict', 'A0409', 200, undefined, 'request-receiving', 'trace-receiving',
))
const wrapper = await mountView()
await wrapper.findAll('.el-radio-button').find((item) => item.text().startsWith('柜子'))!.trigger('click')
await flushPromises()
await openHiddenContainerWorkbench(wrapper)
await button(wrapper, '修改接柜仓')!.trigger('click')
await flushPromises()
const warehouseSelect = wrapper.findAllComponents({ name: 'ElSelect' }).find((item) => item.props('placeholder') === '请选择 ACTIVE 海外接柜仓')!
@@ -593,8 +589,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
it('renders an explicit empty state when there are no ACTIVE OVERSEAS receiving warehouses', async () => {
vi.mocked(listAllPlatformWarehouses).mockResolvedValueOnce([])
const wrapper = await mountView()
await wrapper.findAll('.el-radio-button').find((item) => item.text().startsWith('柜子'))!.trigger('click')
await flushPromises()
await openHiddenContainerWorkbench(wrapper)
await button(wrapper, '修改接柜仓')!.trigger('click')
await flushPromises()
expect(document.body.textContent).toContain('暂无可用的 ACTIVE 海外接柜仓')
@@ -606,8 +601,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
'receiving warehouse candidate failed', 'A0503', 200, undefined, 'request-receiving-options', 'trace-receiving-options',
))
const wrapper = await mountView()
await wrapper.findAll('.el-radio-button').find((item) => item.text().startsWith('柜子'))!.trigger('click')
await flushPromises()
await openHiddenContainerWorkbench(wrapper)
await button(wrapper, '修改接柜仓')!.trigger('click')
await flushPromises()
expect(document.body.textContent).toContain('request-receiving-options')
@@ -45,7 +45,8 @@ const inboundPlanOpen = ref(false)
const inboundPlanWarehouseLabel = computed(() => [actionTarget.value?.overseasWarehouseCode, actionTarget.value?.warehouse]
.filter((value) => value && value !== '—')
.join(' · '))
const inboundPlanForm = reactive({ plannedArrivalDate: '', inboundCode: '', palletCount: 1, palletType: 'STANDARD' as 'STANDARD' | 'WOOD' | 'PAPER', assigned: false, mergeRequired: false, urgent: false, remark: '' })
const inboundPlanMinimumPalletCount = ref(1)
const inboundPlanForm = reactive({ plannedArrivalDate: '', inboundCode: '', palletCount: 1, palletType: 'WOOD' as 'STANDARD' | 'WOOD' | 'PAPER', assigned: false, mergeRequired: false, urgent: false, remark: '' })
const holdOpen = ref(false)
const holdForm = reactive({ held: true, chargeable: true, reason: '' })
const manualNodesOpen = ref(false)
@@ -472,14 +473,31 @@ function openInboundPlan(source?: object) {
if (!target) { ElMessage.warning('生成入库计划时请选择 1 条尾程订单'); return }
if (target.detained) { ElMessage.warning('订单处于暂扣状态,解除暂扣后才能生成入库计划'); return }
actionTarget.value = target
Object.assign(inboundPlanForm, { plannedArrivalDate: '', inboundCode: target.inboundCode === '—' ? '' : target.inboundCode, palletCount: typeof target.pallets === 'number' && target.pallets > 0 ? target.pallets : 1, palletType: 'STANDARD', assigned: false, mergeRequired: false, urgent: false, remark: '' })
const volumeMinimum = typeof target.volume === 'number' && target.volume > 0 ? Math.ceil(target.volume / 2) : 1
inboundPlanMinimumPalletCount.value = Math.max(1, volumeMinimum,
typeof target.pallets === 'number' ? target.pallets : 0)
Object.assign(inboundPlanForm, { plannedArrivalDate: '', inboundCode: target.inboundCode === '—' ? '' : target.inboundCode, palletCount: inboundPlanMinimumPalletCount.value, palletType: 'WOOD', assigned: false, mergeRequired: false, urgent: false, remark: '' })
actionError.value = null
inboundPlanOpen.value = true
void refreshManualPalletMinimum(target)
}
async function refreshManualPalletMinimum(target: LastMileOrderListItem) {
if (!target.sourceContainerBookingId) return
try {
const preview = await getContainerInboundPlanPreview(target.sourceContainerBookingId)
if (actionTarget.value?.id !== target.id || !inboundPlanOpen.value) return
const order = preview.groups.flatMap((group) => group.orders).find((item) => item.orderId === target.id)
const suggested = Number(order?.suggestedPalletCount || 0)
inboundPlanMinimumPalletCount.value = Math.max(inboundPlanMinimumPalletCount.value, suggested)
inboundPlanForm.palletCount = Math.max(inboundPlanForm.palletCount, inboundPlanMinimumPalletCount.value)
} catch {
// The backend repeats the same calculation and remains authoritative on submit.
}
}
async function submitInboundPlan() {
const row = actionTarget.value
const frozenInboundCode = String(row?.inboundCode || '').trim()
if (!row || !row.overseasWarehouseId || !inboundPlanForm.plannedArrivalDate || !frozenInboundCode || frozenInboundCode === '—' || inboundPlanForm.palletCount < 1) {
if (!row || !row.overseasWarehouseId || !inboundPlanForm.plannedArrivalDate || !frozenInboundCode || frozenInboundCode === '—' || inboundPlanForm.palletCount < inboundPlanMinimumPalletCount.value) {
ElMessage.warning('当前柜须已分配接柜仓,并完整填写预计到仓日期和托数')
return
}
@@ -621,7 +639,7 @@ async function confirmContainerInboundPlans() {
if (!containerConversionIdempotencyKey) containerConversionIdempotencyKey = idempotencyKey('container-inbound-plan')
const bookingId = preview.sourceContainerBookingId
const existingPlanNos = new Set(preview.groups.flatMap((group) => group.orders)
.filter((order) => order.readiness === 'PLANNED' && order.existingPlanNo)
.filter((order) => order.readiness !== 'READY' && order.existingPlanNo)
.map((order) => String(order.existingPlanNo)))
actionLoading.value = true
actionError.value = null
@@ -671,11 +689,11 @@ function orderTypeText(orderType: string) {
}
function readinessText(readiness: ContainerInboundPlanPreviewOrderDto['readiness']) {
return readiness === 'READY' ? '可生成' : readiness === 'PLANNED' ? '已有计划' : '已阻断'
return readiness === 'READY' ? '可生成' : readiness === 'RESOURCE_PENDING' ? '待补托盘' : readiness === 'PLANNED' ? '已有计划' : '已阻断'
}
function readinessType(readiness: ContainerInboundPlanPreviewOrderDto['readiness']) {
return readiness === 'READY' ? 'success' : readiness === 'PLANNED' ? 'info' : 'danger'
return readiness === 'READY' ? 'success' : readiness === 'RESOURCE_PENDING' ? 'warning' : readiness === 'PLANNED' ? 'info' : 'danger'
}
function blockerText(blockers: string[]) {
@@ -687,6 +705,8 @@ function blockerText(blockers: string[]) {
RECEIVING_WAREHOUSE_INCONSISTENT: '收货仓不一致',
DESTINATION_NOT_READY: '目的地未就绪',
CARGO_FACT_INVALID: '货物数据无效',
PLAN_ALREADY_STARTED: '已有计划已开始作业,不能再补生成托盘',
PLAN_PALLET_BINDING_INCOMPLETE: '已有计划只绑定了部分真实托盘,请联系管理员处理',
DROPSHIP_PALLET_SNAPSHOT_MISSING: '缺少一件代发托盘快照',
PALLET_COUNT_NOT_READY: '计划托数暂未就绪',
}
@@ -719,7 +739,8 @@ onMounted(() => { void load() })
<el-radio-group v-model="statusTab" aria-label="尾程订单状态" @change="changeStatusTab">
<el-radio-button value="全部">全部</el-radio-button>
<el-radio-button value="待分配">待分配</el-radio-button>
<el-radio-button value="柜子">柜子工作台({{ containerTotal }})</el-radio-button>
<!-- 柜子工作台入口暂时隐藏,保留现有实现便于后续恢复。 -->
<!-- <el-radio-button value="柜子">柜子工作台({{ containerTotal }})</el-radio-button> -->
</el-radio-group>
<QueryToolbar v-model="filters" :fields="fields" :available-fields="allFields" :loading="loading" ariaLabel="尾程订单筛选" preference-key="overseas-last-mile-orders-query" :search-on-reset="false" @search="search" @reset="reset" />
</section>
@@ -808,12 +829,12 @@ onMounted(() => { void load() })
</div>
<div class="container-preview__metrics">
<div class="container-preview__metric container-preview__metric--primary"><span>本次新增托盘码</span><strong>{{ containerPreview.pendingPalletCount }}</strong></div>
<div class="container-preview__metric"><span>本次新增计划</span><strong>{{ containerPreview.pendingOrderCount }}</strong></div>
<div class="container-preview__metric"><span>本次处理订单</span><strong>{{ containerPreview.pendingOrderCount }}</strong></div>
<div class="container-preview__metric"><span>已有计划托盘</span><strong>{{ containerPreview.existingPlannedPalletCount }}</strong></div>
<div class="container-preview__metric" :class="{ 'container-preview__metric--danger': containerPreview.blockedOrderCount > 0 }"><span>阻断订单</span><strong>{{ containerPreview.blockedOrderCount }}</strong></div>
</div>
<el-alert v-if="containerPreview.blockedOrderCount > 0" class="container-convert-alert" type="error" :closable="false" show-icon title="存在阻断订单,暂不可确认生成;请按下方原因处理后重新预览。" />
<el-alert v-else-if="!containerConversionResult" class="container-convert-alert" type="success" :closable="false" show-icon :title="`预览已就绪,本次将新增 ${containerPreview.pendingOrderCount} 个计划、${containerPreview.pendingPalletCount} 个托盘码。`" />
<el-alert v-else-if="!containerConversionResult" class="container-convert-alert" type="success" :closable="false" show-icon :title="`预览已就绪,本次将处理 ${containerPreview.pendingOrderCount} 票订单、生成或补齐 ${containerPreview.pendingPalletCount} 个托盘码。`" />
<section v-if="containerConversionResult" class="container-conversion-result">
<header><strong>生成完成</strong><span>新建 {{ containerConversionResult.newPlanCount }} 个 / 已有 {{ containerConversionResult.existingPlanCount }} 个</span></header>
<div class="container-conversion-result__plans">
@@ -828,7 +849,7 @@ onMounted(() => { void load() })
<section v-for="group in containerPreview.groups" :key="`${group.destinationType}-${group.destinationWarehouseId || group.destinationWarehouseCode || 'default'}`" class="container-preview__group">
<header class="container-preview__group-header">
<strong class="text-ellipsis" :title="previewGroupTitle(group)">{{ previewGroupTitle(group) }}:{{ group.pendingPalletCount }}托</strong>
<span>本次 {{ group.pendingOrderCount }} 票 / 已有 {{ group.existingPlannedPalletCount }} 托 / 共 {{ group.orderCount }} 票</span>
<span>本次处理 {{ group.pendingOrderCount }} 票 / 已有 {{ group.existingPlannedPalletCount }} 托 / 共 {{ group.orderCount }} 票</span>
</header>
<el-table :data="group.orders" size="small" border>
<el-table-column label="入仓码" min-width="148" show-overflow-tooltip><template #default="{ row }">{{ row.inboundCode || '—' }}</template></el-table-column>
@@ -888,7 +909,7 @@ onMounted(() => { void load() })
<AppDialog v-model="inboundPlanOpen" title="入库计划" width="min(720px, calc(100vw - 28px))" confirm-text="保存并生成入库单" :confirm-loading="actionLoading" @confirm="submitInboundPlan">
<el-alert title="保存后生成入库单;清关完成后入库计划不可更改。" type="info" :closable="false" show-icon />
<el-form class="command-form" label-position="top">
<div class="command-form__grid"><el-form-item label="计划入库仓"><el-input :model-value="inboundPlanWarehouseLabel" disabled placeholder="当前柜接柜仓" /><small>接柜仓按柜统一;如需变更,请在柜子工作台执行“变更接柜仓”。</small></el-form-item><el-form-item label="预计到仓日期" required><el-date-picker v-model="inboundPlanForm.plannedArrivalDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择日期" /></el-form-item><el-form-item label="入仓码"><el-input :model-value="inboundPlanForm.inboundCode" disabled placeholder="订单冻结入仓码" /></el-form-item><el-form-item label="托数" required><el-input-number v-model="inboundPlanForm.palletCount" :min="1" :max="999" controls-position="right" placeholder="请输入托数" /></el-form-item><el-form-item label="托盘类型"><el-select v-model="inboundPlanForm.palletType" placeholder="请选择托盘类型"><el-option label="标准托盘" value="STANDARD" /><el-option label="木托盘" value="WOOD" /><el-option label="纸托盘" value="PAPER" /></el-select></el-form-item><el-form-item label="是否分配"><el-switch v-model="inboundPlanForm.assigned" inline-prompt active-text="是" inactive-text="否" /></el-form-item><el-form-item label="允许合托"><el-switch v-model="inboundPlanForm.mergeRequired" inline-prompt active-text="是" inactive-text="否" /><small>仅作为现场可选提示,不强制工人合托。</small></el-form-item><el-form-item label="是否加急"><el-switch v-model="inboundPlanForm.urgent" inline-prompt active-text="是" inactive-text="否" /></el-form-item></div>
<div class="command-form__grid"><el-form-item label="计划入库仓"><el-input :model-value="inboundPlanWarehouseLabel" disabled placeholder="当前柜接柜仓" /><small>接柜仓按柜统一;如需变更,请在柜子工作台执行“变更接柜仓”。</small></el-form-item><el-form-item label="预计到仓日期" required><el-date-picker v-model="inboundPlanForm.plannedArrivalDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择日期" /></el-form-item><el-form-item label="入仓码"><el-input :model-value="inboundPlanForm.inboundCode" disabled placeholder="订单冻结入仓码" /></el-form-item><el-form-item label="托数" required><el-input-number v-model="inboundPlanForm.palletCount" :min="inboundPlanMinimumPalletCount" :max="999" controls-position="right" placeholder="请输入托数" /><small>按柜级预览至少 {{ inboundPlanMinimumPalletCount }} 托,可增加,不能减少。</small></el-form-item><el-form-item label="托盘类型"><el-select v-model="inboundPlanForm.palletType" placeholder="请选择托盘类型"><el-option label="标准托盘" value="STANDARD" /><el-option label="木托盘" value="WOOD" /><el-option label="纸托盘" value="PAPER" /></el-select></el-form-item><el-form-item label="是否分配"><el-switch v-model="inboundPlanForm.assigned" inline-prompt active-text="是" inactive-text="否" /></el-form-item><el-form-item label="允许合托"><el-switch v-model="inboundPlanForm.mergeRequired" inline-prompt active-text="是" inactive-text="否" /><small>仅作为现场可选提示,不强制工人合托。</small></el-form-item><el-form-item label="是否加急"><el-switch v-model="inboundPlanForm.urgent" inline-prompt active-text="是" inactive-text="否" /></el-form-item></div>
<el-form-item label="备注"><el-input v-model="inboundPlanForm.remark" type="textarea" :rows="3" maxlength="1000" show-word-limit placeholder="请输入备注(选填)" /></el-form-item>
</el-form>
</AppDialog>