优化
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import SmartLoadingCandidateInbounds from './SmartLoadingCandidateInbounds.vue'
|
||||
import type { SmartLoadingCandidate } from '../types'
|
||||
|
||||
describe('sealed pallet composition', () => {
|
||||
it('shows the two inbound portions, not the whole pallet count twice', async () => {
|
||||
const candidate = { unitType: 'SEALED_PALLET', inboundCode: 'I180', availablePackageCount: 13,
|
||||
groupMembers: [{ accountId: '1', inboundId: '180', inboundCode: 'I180', availablePackageCount: 3 },
|
||||
{ accountId: '1', inboundId: '181', inboundCode: 'I181', availablePackageCount: 10 }] } as SmartLoadingCandidate
|
||||
const wrapper = mount(SmartLoadingCandidateInbounds, { props: { candidate } })
|
||||
expect(wrapper.get('summary').text()).toBe('2 个入仓码 · 展开')
|
||||
expect(wrapper.text()).toContain('I180:3 箱')
|
||||
expect(wrapper.text()).toContain('I181:10 箱')
|
||||
expect(wrapper.text()).not.toContain('13 箱')
|
||||
expect(wrapper.findAll('button')).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import type { SmartLoadingCandidate } from '../types'
|
||||
|
||||
defineProps<{ candidate: SmartLoadingCandidate }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<details v-if="candidate.unitType === 'SEALED_PALLET' && candidate.groupMembers.length">
|
||||
<summary>{{ candidate.groupMembers.length }} 个入仓码 · 展开</summary>
|
||||
<div v-for="member in candidate.groupMembers" :key="`${member.accountId}:${member.inboundId}`">
|
||||
{{ member.inboundCode || '—' }}:{{ member.availablePackageCount }} 箱
|
||||
</div>
|
||||
</details>
|
||||
<span v-else>{{ candidate.inboundCode || '—' }}</span>
|
||||
</template>
|
||||
@@ -492,7 +492,7 @@ describe('GlobalSmartLoadingView', () => {
|
||||
expect(wrapper.text()).toContain('CLG-2093539779795795969 · 2 票')
|
||||
expect(wrapper.text()).toContain('3 个入仓片段')
|
||||
expect(wrapper.text()).toContain('ORD-201:NYEXX2608290051、NYEXX2608290051-B')
|
||||
const buttons = wrapper.findAll('button').filter((button) => button.text().includes('整组分配'))
|
||||
const buttons = wrapper.findAll('.el-table__body-wrapper button').filter((button) => button.text() === '加入')
|
||||
expect(buttons).toHaveLength(1)
|
||||
await buttons[0].trigger('click')
|
||||
await flushPromises()
|
||||
@@ -560,7 +560,7 @@ describe('GlobalSmartLoadingView', () => {
|
||||
|
||||
expect(wrapper.text()).toContain('CLG-9900 · 3 票')
|
||||
expect(wrapper.text()).toContain('同柜不可拆组成员候选不齐,缺少 1 票')
|
||||
const button = wrapper.findAll('button').find((item) => item.text().includes('整组分配'))!
|
||||
const button = wrapper.findAll('.el-table__body-wrapper button').find((item) => item.text() === '加入')!
|
||||
expect(button.attributes('disabled')).toBeDefined()
|
||||
expect(assignSmartLoadingGroup).not.toHaveBeenCalled()
|
||||
})
|
||||
@@ -599,6 +599,57 @@ describe('GlobalSmartLoadingView', () => {
|
||||
expect(adjustSmartLoadingItem).toHaveBeenCalledWith(plan.id, plan.items[0].id, 4, plan.version, plan.items[0].version)
|
||||
})
|
||||
|
||||
it('shows a shared pallet once with exact members and a text-only join action', async () => {
|
||||
vi.mocked(listSmartLoadingCandidates).mockResolvedValue(candidatePage([{
|
||||
...candidate, unitType: 'SEALED_PALLET', palletId: '801', palletNo: 'DP-0025', indivisible: true,
|
||||
availablePackageCount: 13, unitGrossWeightKg: 10,
|
||||
groupMembers: [
|
||||
{ accountId: '1', inboundId: '180', orderId: '180', orderNo: 'O180', inboundCode: 'I180', customerName: '客户', availablePackageCount: 3, orderStatusSnapshot: 'WAIT_SPLIT', expectedArrivalAt: null },
|
||||
{ accountId: '1', inboundId: '181', orderId: '181', orderNo: 'O181', inboundCode: 'I181', customerName: '客户', availablePackageCount: 10, orderStatusSnapshot: 'WAIT_SPLIT', expectedArrivalAt: null },
|
||||
],
|
||||
}]))
|
||||
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
|
||||
await flushPromises()
|
||||
const buttons = wrapper.findAll('.el-table__body-wrapper button').filter(button => button.text() === '加入')
|
||||
expect(buttons).toHaveLength(1)
|
||||
expect(buttons[0].find('svg').exists()).toBe(false)
|
||||
expect(wrapper.text()).toContain('I180:3 箱')
|
||||
expect(wrapper.text()).toContain('I181:10 箱')
|
||||
expect(wrapper.text()).toContain('130 kg')
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('searches any shared-pallet inbound, retains its complete composition across pages, and resets the search', async () => {
|
||||
const sharedPallet = {
|
||||
...candidate, inboundId: '180', inboundCode: 'I180', unitType: 'SEALED_PALLET' as const,
|
||||
palletId: '801', palletNo: 'DP-0025', indivisible: true, availablePackageCount: 13,
|
||||
groupMembers: [
|
||||
{ accountId: '1', inboundId: '180', orderId: '180', orderNo: 'O180', inboundCode: 'I180', customerName: '客户', availablePackageCount: 3, orderStatusSnapshot: 'WAIT_SPLIT', expectedArrivalAt: null },
|
||||
{ accountId: '1', inboundId: '181', orderId: '181', orderNo: 'O181', inboundCode: 'I181', customerName: '客户', availablePackageCount: 10, orderStatusSnapshot: 'WAIT_SPLIT', expectedArrivalAt: null },
|
||||
],
|
||||
}
|
||||
vi.mocked(listSmartLoadingCandidates).mockResolvedValue(candidatePage([sharedPallet], 1, 20, 21))
|
||||
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
|
||||
await flushPromises()
|
||||
const filters = { warehouseId: '', customerId: '', inboundCode: ' I181 ', createdRange: [], expectedArrivalRange: [], orderStatuses: [] }
|
||||
wrapper.getComponent(QueryToolbar).vm.$emit('search', filters)
|
||||
await flushPromises()
|
||||
expect(listSmartLoadingCandidates).toHaveBeenLastCalledWith({ channelGroupCodes: ['MATSON_CLX'], inboundCode: 'I181', page: 1, size: 20 })
|
||||
const pane = wrapper.get('.candidate-pane')
|
||||
expect(pane.text()).toContain('I180:3 箱')
|
||||
expect(pane.text()).toContain('I181:10 箱')
|
||||
expect(pane.findAll('.el-table__body-wrapper button').filter(button => button.text() === '加入')).toHaveLength(1)
|
||||
|
||||
await pane.get('button[aria-label="下一页"]').trigger('click')
|
||||
await flushPromises()
|
||||
expect(listSmartLoadingCandidates).toHaveBeenLastCalledWith({ channelGroupCodes: ['MATSON_CLX'], inboundCode: 'I181', page: 2, size: 20 })
|
||||
|
||||
wrapper.getComponent(QueryToolbar).vm.$emit('reset', { ...filters, inboundCode: '' })
|
||||
await flushPromises()
|
||||
expect(listSmartLoadingCandidates).toHaveBeenLastCalledWith({ channelGroupCodes: ['MATSON_CLX'], page: 1, size: 20 })
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('rounds capacity percentages and hides column settings in embedded tables', async () => {
|
||||
vi.mocked(getSmartLoadingPlanByBooking).mockResolvedValue({
|
||||
...plan,
|
||||
@@ -607,8 +658,8 @@ describe('GlobalSmartLoadingView', () => {
|
||||
const wrapper = mount(GlobalSmartLoadingView, { global: { plugins: [ElementPlus] } })
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.text()).toContain('0.16%')
|
||||
expect(wrapper.text()).toContain('0.03%')
|
||||
expect(wrapper.text()).toContain('0.18%')
|
||||
expect(wrapper.text()).toContain('0.05%')
|
||||
expect(wrapper.text()).not.toContain('0.169999999')
|
||||
expect(wrapper.findAll('button[aria-label="列设置"]')).toHaveLength(0)
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { Pencil, Plus, RefreshCw, Sparkles, Trash2 } from '@lucide/vue'
|
||||
import { Pencil, RefreshCw, Sparkles, Trash2 } from '@lucide/vue'
|
||||
import SmartLoadingCandidateInbounds from '../components/SmartLoadingCandidateInbounds.vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
import { listPlatformTenants, type PlatformTenantResource } from '@/modules/iam'
|
||||
@@ -121,7 +122,6 @@ function groupMemberLabel(group: SmartLoadingPlanCoLoadGroup) {
|
||||
: `${group.memberOrderCount} 票 · ${group.inboundFragmentCount} 个入仓片段`
|
||||
}
|
||||
function candidateTotalGrossWeight(candidate: SmartLoadingCandidate) {
|
||||
if (candidate.unitType === 'SEALED_PALLET') return candidate.unitGrossWeightKg
|
||||
return candidate.unitGrossWeightKg == null ? null : Number(candidate.unitGrossWeightKg) * Number(candidate.availablePackageCount || 0)
|
||||
}
|
||||
function candidateTotalVolume(candidate: SmartLoadingCandidate) {
|
||||
@@ -228,7 +228,7 @@ function preferredContainerId() {
|
||||
|
||||
const candidateColumns: DataTableColumn[] = [
|
||||
{ key: 'actions', label: '操作', width: 90, fixed: 'left', slotName: 'actions' },
|
||||
{ key: 'inboundCode', prop: 'inboundCode', label: '入仓码', minWidth: 140, fixed: 'left', showOverflowTooltip: true },
|
||||
{ key: 'inboundCode', label: '入仓码', minWidth: 220, fixed: 'left', slotName: 'candidateInbounds' },
|
||||
{ key: 'palletNo', prop: 'palletNo', label: '托盘号', minWidth: 145, showOverflowTooltip: true },
|
||||
{ key: 'unitType', label: '分配单位', width: 105, slotName: 'unitType' },
|
||||
{ key: 'coLoadGroup', label: '同柜', minWidth: 80, slotName: 'coLoadGroup' },
|
||||
@@ -745,8 +745,9 @@ onMounted(() => { void load(); void loadCustomers() })
|
||||
plain
|
||||
:disabled="mutating || candidateBlocked(row as SmartLoadingCandidateRow)"
|
||||
@click="openAssign(row)"
|
||||
><Plus :size="15" />{{ row.unitType === 'SEALED_PALLET' ? '整托分配' : row.coLoadGroupId ? '整组分配' : '加入' }}</el-button>
|
||||
>加入</el-button>
|
||||
</template>
|
||||
<template #candidateInbounds="{ row }"><SmartLoadingCandidateInbounds :candidate="row as SmartLoadingCandidate" /></template>
|
||||
<template #customerName="{ row }"><LineClampText :content="row.customerName" /></template>
|
||||
<template #coLoadGroup="{ row }"><div v-if="row.coLoadGroupId" class="candidate-group"><el-tag size="small" type="warning"><LineClampText :content="`${row.groupNo} · ${row.memberOrderCount} 票 · ${row.inboundFragmentCount} 个入仓片段`" /></el-tag><LineClampText :content="row.memberOrderSummaries.join(';')" /><LineClampText :content="`整组 ${row.groupTotalBoxes} 箱,必须全量进入同一柜`" /><LineClampText v-if="row.blockReason" class="candidate-block" :content="row.blockReason" /><LineClampText v-else-if="row.locked" content="该组已锁定,不能重复分配" /></div><LineClampText v-else :content="row.unitType === 'SEALED_PALLET' ? '封托不可拆' : '普通单票'" /></template>
|
||||
<template #orderStatusSnapshot="{ row }"><el-tag size="small" effect="plain">{{ candidateStatusLabel(row.orderStatusSnapshot) }}</el-tag></template>
|
||||
@@ -845,7 +846,8 @@ onMounted(() => { void load(); void loadCustomers() })
|
||||
<el-alert :title="selectedCandidate.blockReason || '同柜且不可拆:全部订单及其入仓片段按完整箱数一次加入,不能部分输入、单独分配或单独移除。'" :type="selectedCandidate.blockReason ? 'error' : 'warning'" :closable="false" />
|
||||
</template>
|
||||
<template v-else-if="selectedCandidate.unitType === 'SEALED_PALLET'">
|
||||
<p>托盘号 {{ selectedCandidate.palletNo || '—' }} · 入仓码 {{ selectedCandidate.inboundCode || '—' }} · {{ selectedCandidate.availablePackageCount }} 箱</p>
|
||||
<p>托盘号 {{ selectedCandidate.palletNo || '—' }} · 整托 {{ selectedCandidate.availablePackageCount }} 箱</p>
|
||||
<SmartLoadingCandidateInbounds :candidate="selectedCandidate" />
|
||||
<el-alert title="该封托托盘将作为一个不可拆分单元整托加入;装柜阶段复用此托盘,不会重新生成工作托盘。" type="warning" :closable="false" />
|
||||
</template>
|
||||
<template v-else>
|
||||
|
||||
@@ -159,8 +159,8 @@ describe('SmartLoadingWorkbenchView', () => {
|
||||
expect(wrapper.text()).toContain('3 个入仓片段')
|
||||
expect(wrapper.text()).toContain('ORD-1:IN-201、IN-203')
|
||||
expect(wrapper.text()).toContain('整组 12 箱')
|
||||
expect(wrapper.findAll('button').filter((button) => button.text().includes('整组分配'))).toHaveLength(1)
|
||||
await wrapper.findAll('button').find((button) => button.text().includes('整组分配'))!.trigger('click')
|
||||
expect(wrapper.findAll('.el-table__body-wrapper button').filter((button) => button.text() === '加入')).toHaveLength(1)
|
||||
await wrapper.findAll('.el-table__body-wrapper button').find((button) => button.text() === '加入')!.trigger('click')
|
||||
await flushPromises()
|
||||
;([...document.body.querySelectorAll('button')].find((button) => button.textContent?.includes('确认整组分配')) as HTMLButtonElement).click()
|
||||
await flushPromises()
|
||||
@@ -181,7 +181,7 @@ describe('SmartLoadingWorkbenchView', () => {
|
||||
await flushPromises()
|
||||
expect(wrapper.text()).toContain('CLG-9900 · 3 票')
|
||||
expect(wrapper.text()).toContain('同柜不可拆组成员候选不齐,缺少 1 票')
|
||||
const button = wrapper.findAll('button').find((item) => item.text().includes('整组分配'))!
|
||||
const button = wrapper.findAll('.el-table__body-wrapper button').find((item) => item.text() === '加入')!
|
||||
expect(button.attributes('disabled')).toBeDefined()
|
||||
expect(wrapper.text()).not.toContain('本次计划装柜箱数')
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import SmartLoadingCandidateInbounds from '../components/SmartLoadingCandidateInbounds.vue'
|
||||
import '@/styles/adminDensity.css'
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
@@ -142,7 +143,7 @@ const itemColumns: DataTableColumn[] = [
|
||||
]
|
||||
const candidateColumns: DataTableColumn[] = [
|
||||
{ key: 'actions', label: '操作', width: 110, fixed: 'left', slotName: 'actions' },
|
||||
{ key: 'inboundCode', prop: 'inboundCode', label: '入仓码', minWidth: 190, fixed: 'left', showOverflowTooltip: true },
|
||||
{ key: 'inboundCode', label: '入仓码', minWidth: 220, fixed: 'left', slotName: 'candidateInbounds' },
|
||||
{ key: 'palletNo', prop: 'palletNo', label: '托盘号', minWidth: 150, showOverflowTooltip: true },
|
||||
{ key: 'unitType', label: '分配单位', width: 110, slotName: 'unitType' },
|
||||
{ key: 'coLoadGroup', label: '同柜不可拆', minWidth: 210, slotName: 'coLoadGroup' },
|
||||
@@ -393,7 +394,8 @@ onMounted(load)
|
||||
</DataTable>
|
||||
<DataTable :data="displayedCandidates" :columns="candidateColumns" :loading="loading || mutating" stripe preference-key="smart-loading-candidates" column-settings-mode="hide" empty-text="当前仓库暂无可装柜货物">
|
||||
<template #toolbar-start><div><h2>候选货物</h2><p>系统基于装柜仓库、物流渠道、可用箱数及 150% 容量上限校验。</p></div></template>
|
||||
<template #actions="{ row }"><el-button data-tone="primary" class="density-action" v-if="canAssign && assignable" size="small" type="primary" plain :disabled="candidateBlocked(row as SmartLoadingCandidateRow)" @click="openAssign(row)">{{ row.unitType === 'SEALED_PALLET' ? '整托分配' : row.coLoadGroupId ? '整组分配' : '分配' }}</el-button></template>
|
||||
<template #actions="{ row }"><el-button data-tone="primary" class="density-action" v-if="canAssign && assignable" size="small" type="primary" plain :disabled="candidateBlocked(row as SmartLoadingCandidateRow)" @click="openAssign(row)">加入</el-button></template>
|
||||
<template #candidateInbounds="{ row }"><SmartLoadingCandidateInbounds :candidate="row as SmartLoadingCandidate" /></template>
|
||||
<template #customerName="{ row }"><LineClampText :content="row.customerName" /></template>
|
||||
<template #goodsSummary="{ row }"><LineClampText :content="row.goodsSummary || '—'" /></template>
|
||||
<template #coLoadGroup="{ row }"><div v-if="row.coLoadGroupId" class="candidate-group"><el-tag size="small" type="warning"><LineClampText :content="`${row.groupNo} · ${row.memberOrderCount} 票 · ${row.inboundFragmentCount} 个入仓片段`" /></el-tag><LineClampText :content="row.memberOrderSummaries.join(';')" /><LineClampText :content="`整组 ${row.groupTotalBoxes} 箱,必须全量进入同一柜`" /><LineClampText v-if="row.blockReason" class="candidate-block" :content="row.blockReason" /><LineClampText v-else-if="row.locked" content="该组已锁定,不能重复分配" /></div><LineClampText v-else :content="row.unitType === 'SEALED_PALLET' ? '封托不可拆' : '普通单票'" /></template>
|
||||
@@ -404,7 +406,7 @@ onMounted(load)
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
<AppDialog class="admin-density-dialog" v-model="assignOpen" :title="selectedCandidate?.unitType === 'SEALED_PALLET' ? '整托加入当前柜次' : selectedCandidate?.coLoadGroupId ? '整组加入当前柜次' : '按入仓码分配箱数'" :confirm-loading="mutating" :confirm-disabled="Boolean(selectedCandidate?.blockReason || selectedCandidate?.locked)" :confirm-text="selectedCandidate?.unitType === 'SEALED_PALLET' ? '确认整托分配' : selectedCandidate?.coLoadGroupId ? '确认整组分配' : '确认分配'" @confirm="assign"><div v-if="selectedCandidate" class="assign-dialog"><p><strong>{{ selectedCandidate.palletNo || selectedCandidate.groupNo || selectedCandidate.inboundCode || '入仓码暂缺' }}</strong></p><template v-if="selectedCandidate.coLoadGroupId"><p>组内 {{ selectedCandidate.memberOrderCount }} 票、{{ selectedCandidate.inboundFragmentCount }} 个入仓片段,共 {{ selectedCandidate.groupTotalBoxes }} 箱</p><p v-for="summary in selectedCandidate.memberOrderSummaries" :key="summary">{{ summary }}</p><el-alert :title="selectedCandidate.blockReason || '同柜且不可拆:全部订单及其入仓片段按完整箱数一次加入,不能部分输入或单独移除。'" :type="selectedCandidate.blockReason ? 'error' : 'warning'" :closable="false" /></template><template v-else-if="selectedCandidate.unitType === 'SEALED_PALLET'"><p>托盘号 {{ selectedCandidate.palletNo || '—' }} · 入仓码 {{ selectedCandidate.inboundCode || '—' }} · {{ selectedCandidate.availablePackageCount }} 箱</p><el-alert title="该封托托盘将作为一个不可拆分单元整托加入;装柜阶段复用此托盘,不会重新生成工作托盘。" type="warning" :closable="false" /></template><template v-else><p>{{ selectedCandidate.goodsSummary || '货物摘要暂缺' }} · 可用 {{ selectedCandidate.availablePackageCount }} 箱</p><el-form class="density-form" label-position="top"><el-form-item label="本次计划装柜箱数" required><el-input-number v-model="assignForm.packageCount" :min="1" :max="selectedCandidate.availablePackageCount" :step="1" controls-position="right" placeholder="请输入本次分配箱数" /></el-form-item></el-form><el-alert :title="explainCandidate(selectedCandidate)" type="info" :closable="false" /><small>只记录数量,具体上柜箱号未核验。</small></template></div></AppDialog>
|
||||
<AppDialog class="admin-density-dialog" v-model="assignOpen" :title="selectedCandidate?.unitType === 'SEALED_PALLET' ? '整托加入当前柜次' : selectedCandidate?.coLoadGroupId ? '整组加入当前柜次' : '按入仓码分配箱数'" :confirm-loading="mutating" :confirm-disabled="Boolean(selectedCandidate?.blockReason || selectedCandidate?.locked)" :confirm-text="selectedCandidate?.unitType === 'SEALED_PALLET' ? '确认整托分配' : selectedCandidate?.coLoadGroupId ? '确认整组分配' : '确认分配'" @confirm="assign"><div v-if="selectedCandidate" class="assign-dialog"><p><strong>{{ selectedCandidate.palletNo || selectedCandidate.groupNo || selectedCandidate.inboundCode || '入仓码暂缺' }}</strong></p><template v-if="selectedCandidate.coLoadGroupId"><p>组内 {{ selectedCandidate.memberOrderCount }} 票、{{ selectedCandidate.inboundFragmentCount }} 个入仓片段,共 {{ selectedCandidate.groupTotalBoxes }} 箱</p><p v-for="summary in selectedCandidate.memberOrderSummaries" :key="summary">{{ summary }}</p><el-alert :title="selectedCandidate.blockReason || '同柜且不可拆:全部订单及其入仓片段按完整箱数一次加入,不能部分输入或单独移除。'" :type="selectedCandidate.blockReason ? 'error' : 'warning'" :closable="false" /></template><template v-else-if="selectedCandidate.unitType === 'SEALED_PALLET'"><p>托盘号 {{ selectedCandidate.palletNo || '—' }} · 整托 {{ selectedCandidate.availablePackageCount }} 箱</p><SmartLoadingCandidateInbounds :candidate="selectedCandidate" /><el-alert title="该封托托盘将作为一个不可拆分单元整托加入;装柜阶段复用此托盘,不会重新生成工作托盘。" type="warning" :closable="false" /></template><template v-else><p>{{ selectedCandidate.goodsSummary || '货物摘要暂缺' }} · 可用 {{ selectedCandidate.availablePackageCount }} 箱</p><el-form class="density-form" label-position="top"><el-form-item label="本次计划装柜箱数" required><el-input-number v-model="assignForm.packageCount" :min="1" :max="selectedCandidate.availablePackageCount" :step="1" controls-position="right" placeholder="请输入本次分配箱数" /></el-form-item></el-form><el-alert :title="explainCandidate(selectedCandidate)" type="info" :closable="false" /><small>只记录数量,具体上柜箱号未核验。</small></template></div></AppDialog>
|
||||
</section>
|
||||
</AdminPage>
|
||||
</template>
|
||||
|
||||
@@ -332,6 +332,7 @@ export interface SmartLoadingCandidate {
|
||||
groupTotalBoxes: number | null
|
||||
locked: boolean | null
|
||||
blockReason: string | null
|
||||
/** Complete co-load group or sealed-pallet membership; pallet member counts are inbound-specific. */
|
||||
groupMembers: SmartLoadingCandidateGroupMember[]
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user