fix(inbound): refresh authoritative container arrival when opening plan
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
export * from './api/platformContainerLoading'
|
export * from './api/platformContainerLoading'
|
||||||
|
export { listContainerArrivalTimes, type ContainerArrivalTime } from './api/containerArrivalTimes'
|
||||||
export * from './types'
|
export * from './types'
|
||||||
export { projectWarehouseArrival } from './warehouseArrival'
|
export { projectWarehouseArrival } from './warehouseArrival'
|
||||||
export { getSmartLoadingChannelGroup, isSeaRailChannel, SMART_LOADING_CHANNEL_GROUPS } from './smartLoadingChannelGroups'
|
export { getSmartLoadingChannelGroup, isSeaRailChannel, SMART_LOADING_CHANNEL_GROUPS } from './smartLoadingChannelGroups'
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import DataTable from '@/shared/components/DataTable.vue'
|
|||||||
import AppDialog from '@/shared/components/AppDialog.vue'
|
import AppDialog from '@/shared/components/AppDialog.vue'
|
||||||
import QueryToolbar from '@/shared/components/QueryToolbar.vue'
|
import QueryToolbar from '@/shared/components/QueryToolbar.vue'
|
||||||
import { listAllPlatformWarehouses } from '@/modules/warehouses'
|
import { listAllPlatformWarehouses } from '@/modules/warehouses'
|
||||||
|
import { listContainerArrivalTimes } from '@/modules/container-loading'
|
||||||
import {
|
import {
|
||||||
changeContainerReceivingWarehouse,
|
changeContainerReceivingWarehouse,
|
||||||
createManualInboundPlan,
|
createManualInboundPlan,
|
||||||
@@ -35,6 +36,10 @@ vi.mock('@/shared/downloadCsv', () => ({ saveCsv: vi.fn() }))
|
|||||||
const routerPush = vi.hoisted(() => vi.fn())
|
const routerPush = vi.hoisted(() => vi.fn())
|
||||||
vi.mock('vue-router', () => ({ useRouter: () => ({ push: routerPush }) }))
|
vi.mock('vue-router', () => ({ useRouter: () => ({ push: routerPush }) }))
|
||||||
vi.mock('@/modules/warehouses', () => ({ listAllPlatformWarehouses: vi.fn() }))
|
vi.mock('@/modules/warehouses', () => ({ listAllPlatformWarehouses: vi.fn() }))
|
||||||
|
vi.mock('@/modules/container-loading', async () => ({
|
||||||
|
...await import('../../container-loading/warehouseArrival'),
|
||||||
|
listContainerArrivalTimes: vi.fn(),
|
||||||
|
}))
|
||||||
vi.mock('../api/lastMileOrders', () => ({
|
vi.mock('../api/lastMileOrders', () => ({
|
||||||
getReturnOrderStatistics: vi.fn(),
|
getReturnOrderStatistics: vi.fn(),
|
||||||
listLastMileOrders: vi.fn(), listContainerPlanProgress: vi.fn(), getContainerPlanProgress: vi.fn(),
|
listLastMileOrders: vi.fn(), listContainerPlanProgress: vi.fn(), getContainerPlanProgress: vi.fn(),
|
||||||
@@ -54,6 +59,10 @@ function memoryStorage(): Storage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const bookingId = '2092873463568597001'
|
const bookingId = '2092873463568597001'
|
||||||
|
const arrivalTime = {
|
||||||
|
sourceContainerBookingId: bookingId, estimatedWarehouseArrivalAt: '2026-09-01T08:00:00',
|
||||||
|
actualWarehouseArrivalAt: null, version: 1, planCount: 0, plannedDate: '2026-09-01', isDefault: false,
|
||||||
|
}
|
||||||
const emptyReturnStatistics = { totals: { orderCount: 0, skuTypes: 0, expectedQuantity: 0, receivedQuantity: 0, remainingQuantity: 0, receivedBoxCount: 0, receivedParcelCount: 0, receivedPalletCount: 0 }, pageDimensions: [] }
|
const emptyReturnStatistics = { totals: { orderCount: 0, skuTypes: 0, expectedQuantity: 0, receivedQuantity: 0, remainingQuantity: 0, receivedBoxCount: 0, receivedParcelCount: 0, receivedPalletCount: 0 }, pageDimensions: [] }
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.mocked(getReturnOrderStatistics).mockReset().mockResolvedValue(emptyReturnStatistics)
|
vi.mocked(getReturnOrderStatistics).mockReset().mockResolvedValue(emptyReturnStatistics)
|
||||||
@@ -171,6 +180,12 @@ async function selectRows(wrapper: Awaited<ReturnType<typeof mountView>>, rows:
|
|||||||
await wrapper.vm.$nextTick()
|
await wrapper.vm.$nextTick()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 定向验证入库弹框,独立于列表页签和批量操作入口。 */
|
||||||
|
async function openArrivalPlan(wrapper: Awaited<ReturnType<typeof mountView>>, source = selectedRow()) {
|
||||||
|
;(wrapper.vm as unknown as { openInboundPlan(source: object): void }).openInboundPlan(source)
|
||||||
|
await flushPromises()
|
||||||
|
}
|
||||||
|
|
||||||
function button(wrapper: Awaited<ReturnType<typeof mountView>>, text: string) {
|
function button(wrapper: Awaited<ReturnType<typeof mountView>>, text: string) {
|
||||||
return wrapper.findAll('button').find((item) => item.text().includes(text))
|
return wrapper.findAll('button').find((item) => item.text().includes(text))
|
||||||
}
|
}
|
||||||
@@ -359,6 +374,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
|
|||||||
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' }],
|
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(createManualInboundPlan).mockReset().mockResolvedValue({} as never)
|
vi.mocked(createManualInboundPlan).mockReset().mockResolvedValue({} as never)
|
||||||
|
vi.mocked(listContainerArrivalTimes).mockReset().mockResolvedValue([arrivalTime])
|
||||||
vi.mocked(getContainerInboundPlanPreview).mockReset().mockResolvedValue(preview as never)
|
vi.mocked(getContainerInboundPlanPreview).mockReset().mockResolvedValue(preview as never)
|
||||||
vi.mocked(getLastMileOrderManualNodes).mockReset().mockResolvedValue({ orderId: '2092873463568596994', manualOverrideEnabled: false, nodes: [], version: 0 })
|
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(updateLastMileOrderHold).mockReset().mockResolvedValue({ orderId: '2092873463568596994', held: true, chargeable: true, version: 1 })
|
||||||
@@ -627,11 +643,35 @@ describe('LastMileOrdersView pending order and container conversion', () => {
|
|||||||
expect(confirm).toBeUndefined()
|
expect(confirm).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('blocks manual plan creation when the cabinet has no frozen expected arrival time', async () => {
|
it.each([
|
||||||
|
[{ ...arrivalTime, estimatedWarehouseArrivalAt: '2026-10-25T09:30:00', plannedDate: '2026-10-25' }, '2026-10-25 09:30'],
|
||||||
|
[{ ...arrivalTime, estimatedWarehouseArrivalAt: null, version: 0, plannedDate: '2026-11-01', isDefault: true }, '2026-11-01(默认推算)'],
|
||||||
|
[{ ...arrivalTime, estimatedWarehouseArrivalAt: null, version: 0, planCount: 1, plannedDate: '2026-11-02' }, '2026-11-02'],
|
||||||
|
])('reads the latest cabinet arrival on opening and permits planning with %s', async (arrival, expectedText) => {
|
||||||
|
vi.mocked(listContainerArrivalTimes).mockResolvedValue([arrival])
|
||||||
const wrapper = await openPending()
|
const wrapper = await openPending()
|
||||||
await selectRows(wrapper, [selectedRow({ estimatedWarehouseAt: '', warehouseEta: '—' })])
|
await openArrivalPlan(wrapper, arrival.estimatedWarehouseArrivalAt ? selectedRow() : selectedRow({ estimatedWarehouseAt: '', warehouseEta: '—' }))
|
||||||
await button(wrapper, '入库')!.trigger('click')
|
|
||||||
|
expect(listContainerArrivalTimes).toHaveBeenCalledWith([bookingId])
|
||||||
|
const source = wrapper.get('[aria-label="入库来源"]')
|
||||||
|
expect(source.text()).toContain(expectedText)
|
||||||
|
if (!arrival.estimatedWarehouseArrivalAt) expect(source.text()).not.toContain('00:00')
|
||||||
|
const dialog = wrapper.findAllComponents(AppDialog).find(item => item.props('title').startsWith('入库计划 ·'))!
|
||||||
|
expect(dialog.props('confirmDisabled')).toBe(false)
|
||||||
|
dialog.vm.$emit('confirm')
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
expect(createManualInboundPlan).toHaveBeenCalledTimes(1)
|
||||||
|
expect(vi.mocked(createManualInboundPlan).mock.calls[0][0]).not.toHaveProperty('plannedArrivalDate')
|
||||||
|
})
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{ arrivals: [] },
|
||||||
|
{ arrivals: [{ ...arrivalTime, estimatedWarehouseArrivalAt: null, plannedDate: null }] },
|
||||||
|
{ arrivals: [{ ...arrivalTime, version: -1 }] },
|
||||||
|
])('blocks manual planning when the authoritative cabinet arrival is missing or inconsistent: $arrivals', async ({ arrivals }) => {
|
||||||
|
vi.mocked(listContainerArrivalTimes).mockResolvedValue(arrivals)
|
||||||
|
const wrapper = await openPending()
|
||||||
|
await openArrivalPlan(wrapper)
|
||||||
|
|
||||||
const confirm = [...document.body.querySelectorAll('button')]
|
const confirm = [...document.body.querySelectorAll('button')]
|
||||||
.find((item) => item.textContent?.includes('保存并生成入库单')) as HTMLButtonElement
|
.find((item) => item.textContent?.includes('保存并生成入库单')) as HTMLButtonElement
|
||||||
@@ -639,6 +679,37 @@ describe('LastMileOrdersView pending order and container conversion', () => {
|
|||||||
expect(createManualInboundPlan).not.toHaveBeenCalled()
|
expect(createManualInboundPlan).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('shows arrival read failures and does not reuse the list timestamp', async () => {
|
||||||
|
vi.mocked(listContainerArrivalTimes).mockRejectedValue(new ApiResponseError('到仓来源暂不可读', 'A0503', 503))
|
||||||
|
const wrapper = await openPending()
|
||||||
|
await openArrivalPlan(wrapper)
|
||||||
|
const dialog = wrapper.findAllComponents(AppDialog).find(item => item.props('title').startsWith('入库计划 ·'))!
|
||||||
|
expect(dialog.text()).toContain('到仓来源暂不可读')
|
||||||
|
expect(dialog.get('[aria-label="入库来源"]').text()).not.toContain('2026-09-01')
|
||||||
|
expect(dialog.props('confirmDisabled')).toBe(true)
|
||||||
|
dialog.vm.$emit('confirm'); await flushPromises()
|
||||||
|
expect(createManualInboundPlan).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('keeps confirmation blocked during arrival loading and ignores an older opening of the same order', async () => {
|
||||||
|
let resolveOld!: (value: typeof arrivalTime[]) => void
|
||||||
|
vi.mocked(listContainerArrivalTimes).mockReturnValueOnce(new Promise(resolve => { resolveOld = resolve }))
|
||||||
|
const wrapper = await openPending()
|
||||||
|
await openArrivalPlan(wrapper)
|
||||||
|
const dialog = wrapper.findAllComponents(AppDialog).find(item => item.props('title').startsWith('入库计划 ·'))!
|
||||||
|
expect(dialog.props('confirmDisabled')).toBe(true)
|
||||||
|
dialog.vm.$emit('confirm'); await flushPromises()
|
||||||
|
expect(createManualInboundPlan).not.toHaveBeenCalled()
|
||||||
|
dialog.vm.$emit('update:modelValue', false)
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
vi.mocked(listContainerArrivalTimes).mockResolvedValueOnce([{ ...arrivalTime, estimatedWarehouseArrivalAt: '2026-10-26T10:00:00', plannedDate: '2026-10-26' }])
|
||||||
|
await openArrivalPlan(wrapper)
|
||||||
|
expect(dialog.get('[aria-label="入库来源"]').text()).toContain('2026-10-26 10:00')
|
||||||
|
resolveOld([arrivalTime]); await flushPromises()
|
||||||
|
expect(dialog.get('[aria-label="入库来源"]').text()).toContain('2026-10-26 10:00')
|
||||||
|
expect(dialog.props('confirmDisabled')).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
it('locks one-click dropship manual plans to the frozen source pallet count', async () => {
|
it('locks one-click dropship manual plans to the frozen source pallet count', async () => {
|
||||||
vi.mocked(getContainerInboundPlanPreview).mockResolvedValue({
|
vi.mocked(getContainerInboundPlanPreview).mockResolvedValue({
|
||||||
...preview,
|
...preview,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { ChevronDown, ChevronUp, Download, Pencil, PackagePlus, SlidersHorizonta
|
|||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { listAllPlatformWarehouses, type PlatformWarehouseSummaryResource } from '@/modules/warehouses'
|
import { listAllPlatformWarehouses, type PlatformWarehouseSummaryResource } from '@/modules/warehouses'
|
||||||
|
import { listContainerArrivalTimes, projectWarehouseArrival, type ContainerArrivalTime } from '@/modules/container-loading'
|
||||||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||||||
import AdminPage from '@/shared/components/AdminPage.vue'
|
import AdminPage from '@/shared/components/AdminPage.vue'
|
||||||
import AppDialog from '@/shared/components/AppDialog.vue'
|
import AppDialog from '@/shared/components/AppDialog.vue'
|
||||||
@@ -110,12 +111,23 @@ const inboundPlanSourcePalletCount = ref<number | null>(null)
|
|||||||
const inboundPlanPreviewLoading = ref(false)
|
const inboundPlanPreviewLoading = ref(false)
|
||||||
const inboundPlanPreviewError = ref('')
|
const inboundPlanPreviewError = ref('')
|
||||||
const inboundPlanIsDropship = computed(() => actionTarget.value?.orderTypeCode === 'ONE_CLICK_DROPSHIP')
|
const inboundPlanIsDropship = computed(() => actionTarget.value?.orderTypeCode === 'ONE_CLICK_DROPSHIP')
|
||||||
const inboundPlanSubmitDisabled = computed(() => !actionTarget.value?.estimatedWarehouseAt
|
const inboundPlanArrival = ref<ContainerArrivalTime | null>(null)
|
||||||
|
const inboundPlanArrivalLoading = ref(false)
|
||||||
|
const inboundPlanArrivalError = ref('')
|
||||||
|
let inboundPlanArrivalRequest = 0
|
||||||
|
const inboundPlanArrivalProjection = computed(() => {
|
||||||
|
const arrival = inboundPlanArrival.value
|
||||||
|
return projectWarehouseArrival(arrival ? { ...arrival, plannedAt: arrival.estimatedWarehouseArrivalAt } : undefined)
|
||||||
|
})
|
||||||
|
const inboundPlanSubmitDisabled = computed(() => inboundPlanArrivalLoading.value || !inboundPlanArrivalProjection.value.date
|
||||||
|| (inboundPlanIsDropship.value
|
|| (inboundPlanIsDropship.value
|
||||||
&& (inboundPlanPreviewLoading.value || inboundPlanSourcePalletCount.value == null
|
&& (inboundPlanPreviewLoading.value || inboundPlanSourcePalletCount.value == null
|
||||||
|| inboundPlanSourcePalletCount.value <= 0)))
|
|| inboundPlanSourcePalletCount.value <= 0)))
|
||||||
const inboundPlanForm = reactive({ inboundCode: '', palletCount: 1, palletType: 'WOOD' as 'STANDARD' | 'WOOD' | 'PAPER', assigned: false, mergeRequired: false, urgent: false, remark: '' })
|
const inboundPlanForm = reactive({ inboundCode: '', palletCount: 1, palletType: 'WOOD' as 'STANDARD' | 'WOOD' | 'PAPER', assigned: false, mergeRequired: false, urgent: false, remark: '' })
|
||||||
const inboundPlanExpectedArrival = computed(() => actionTarget.value?.warehouseEta || '—')
|
const inboundPlanExpectedArrival = computed(() => {
|
||||||
|
const { date, time, isDefault } = inboundPlanArrivalProjection.value
|
||||||
|
return date ? `${date}${time ? ` ${time}` : ''}${isDefault ? '(默认推算)' : ''}` : '—'
|
||||||
|
})
|
||||||
const holdOpen = ref(false)
|
const holdOpen = ref(false)
|
||||||
const holdForm = reactive({ held: true, chargeable: true, reason: '' })
|
const holdForm = reactive({ held: true, chargeable: true, reason: '' })
|
||||||
const manualNodesOpen = ref(false)
|
const manualNodesOpen = ref(false)
|
||||||
@@ -633,9 +645,37 @@ function openInboundPlan(source?: object) {
|
|||||||
inboundPlanPreviewError.value = ''
|
inboundPlanPreviewError.value = ''
|
||||||
Object.assign(inboundPlanForm, { inboundCode: target.inboundCode === '—' ? '' : target.inboundCode, palletCount: inboundPlanMinimumPalletCount.value, palletType: 'WOOD', assigned: false, mergeRequired: false, urgent: false, remark: '' })
|
Object.assign(inboundPlanForm, { inboundCode: target.inboundCode === '—' ? '' : target.inboundCode, palletCount: inboundPlanMinimumPalletCount.value, palletType: 'WOOD', assigned: false, mergeRequired: false, urgent: false, remark: '' })
|
||||||
actionError.value = null
|
actionError.value = null
|
||||||
|
inboundPlanArrival.value = null
|
||||||
|
inboundPlanArrivalError.value = ''
|
||||||
inboundPlanOpen.value = true
|
inboundPlanOpen.value = true
|
||||||
|
void refreshInboundPlanArrival(target)
|
||||||
void refreshManualPalletMinimum(target)
|
void refreshManualPalletMinimum(target)
|
||||||
}
|
}
|
||||||
|
/** 每次打开重新读取柜级权威日期,避免使用列表旧值或在页面推算默认日期。 */
|
||||||
|
async function refreshInboundPlanArrival(target: LastMileOrderListItem) {
|
||||||
|
const request = ++inboundPlanArrivalRequest
|
||||||
|
inboundPlanArrivalLoading.value = true
|
||||||
|
try {
|
||||||
|
if (!target.sourceContainerBookingId) {
|
||||||
|
inboundPlanArrivalError.value = '尾程订单未绑定来源柜,无法取得预计到仓时间'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const arrivals = await listContainerArrivalTimes([target.sourceContainerBookingId])
|
||||||
|
if (request !== inboundPlanArrivalRequest || !inboundPlanOpen.value) return
|
||||||
|
const arrival = arrivals.find(item => item.sourceContainerBookingId === target.sourceContainerBookingId)
|
||||||
|
if (!arrival || arrival.version < 0) {
|
||||||
|
inboundPlanArrivalError.value = '来源柜预计到仓日期缺失或不一致,请先核对柜级时间'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
inboundPlanArrival.value = arrival
|
||||||
|
} catch (error) {
|
||||||
|
if (request === inboundPlanArrivalRequest && inboundPlanOpen.value) {
|
||||||
|
inboundPlanArrivalError.value = getApiErrorDetails(error, '预计到仓时间加载失败,请重新打开入库计划重试').message
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (request === inboundPlanArrivalRequest) inboundPlanArrivalLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
async function refreshManualPalletMinimum(target: LastMileOrderListItem) {
|
async function refreshManualPalletMinimum(target: LastMileOrderListItem) {
|
||||||
if (!target.sourceContainerBookingId) return
|
if (!target.sourceContainerBookingId) return
|
||||||
inboundPlanPreviewLoading.value = true
|
inboundPlanPreviewLoading.value = true
|
||||||
@@ -671,7 +711,7 @@ async function submitInboundPlan() {
|
|||||||
ElMessage.warning('一件代发必须按头程已封托托盘数整体生成入库计划')
|
ElMessage.warning('一件代发必须按头程已封托托盘数整体生成入库计划')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!row || !row.overseasWarehouseId || !row.estimatedWarehouseAt || !frozenInboundCode || frozenInboundCode === '—' || inboundPlanForm.palletCount < inboundPlanMinimumPalletCount.value) {
|
if (!row || !row.overseasWarehouseId || inboundPlanArrivalLoading.value || !inboundPlanArrivalProjection.value.date || !frozenInboundCode || frozenInboundCode === '—' || inboundPlanForm.palletCount < inboundPlanMinimumPalletCount.value) {
|
||||||
ElMessage.warning('当前柜须已分配接柜仓、具备预计到仓时间,并完整填写托数')
|
ElMessage.warning('当前柜须已分配接柜仓、具备预计到仓时间,并完整填写托数')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1279,11 +1319,12 @@ onMounted(() => { void load() })
|
|||||||
</LoadErrorState>
|
</LoadErrorState>
|
||||||
</el-form>
|
</el-form>
|
||||||
</AppDialog>
|
</AppDialog>
|
||||||
<AppDialog class="admin-density-dialog admin-workbench is-dialog" v-model="inboundPlanOpen" :title="`入库计划 · ${actionTarget?.orderNo || '—'}`" width="min(720px, calc(100vw - 28px))" fullscreenable confirm-text="保存并生成入库单" :loading="inboundPlanPreviewLoading" :confirm-loading="actionLoading" :confirm-disabled="inboundPlanSubmitDisabled" @confirm="submitInboundPlan">
|
<AppDialog class="admin-density-dialog admin-workbench is-dialog" v-model="inboundPlanOpen" :title="`入库计划 · ${actionTarget?.orderNo || '—'}`" width="min(720px, calc(100vw - 28px))" fullscreenable confirm-text="保存并生成入库单" :loading="inboundPlanPreviewLoading || inboundPlanArrivalLoading" :confirm-loading="actionLoading" :confirm-disabled="inboundPlanSubmitDisabled" @confirm="submitInboundPlan">
|
||||||
<el-alert title="保存后生成入库单;清关完成后入库计划不可更改。" type="info" :closable="false" show-icon />
|
<el-alert title="保存后生成入库单;首个入库计划生成后,整柜预计到仓时间不可更改。" type="info" :closable="false" show-icon />
|
||||||
<el-alert v-if="inboundPlanPreviewError" :title="inboundPlanPreviewError" type="error" :closable="false" show-icon />
|
<el-alert v-if="inboundPlanPreviewError" :title="inboundPlanPreviewError" type="error" :closable="false" show-icon />
|
||||||
|
<el-alert v-if="inboundPlanArrivalError" :title="inboundPlanArrivalError" type="error" :closable="false" show-icon />
|
||||||
<el-form class="command-form density-form density-dialog-sections" label-position="left" label-width="110px" :disabled="actionLoading">
|
<el-form class="command-form density-form density-dialog-sections" label-position="left" label-width="110px" :disabled="actionLoading">
|
||||||
<section class="density-dialog-section" data-tone="source" aria-label="入库来源"><h3 class="density-dialog-section__title">入库来源 <InlineHelp title="入库来源说明">取整柜预计到仓时间,同柜所有入库计划保持一致;已生成计划后锁定。接柜仓按柜统一;如需变更,请在柜子工作台执行“变更接柜仓”。</InlineHelp></h3>
|
<section class="density-dialog-section" data-tone="source" aria-label="入库来源"><h3 class="density-dialog-section__title">入库来源 <InlineHelp title="入库来源说明">优先使用整柜人工维护的预计到仓时间;未维护时,按到港当地日期加两个自然日默认推算。首个入库计划生成后冻结日期,同柜计划保持一致。接柜仓按柜统一;如需变更,请在柜子工作台执行“变更接柜仓”。</InlineHelp></h3>
|
||||||
<table class="density-table last-mile-dialog-facts"><tbody>
|
<table class="density-table last-mile-dialog-facts"><tbody>
|
||||||
<tr><th scope="row">预计到仓时间</th><td>{{ inboundPlanExpectedArrival }} <el-button v-if="access.can('platform:firstleg:terminal-drayage:list') && actionTarget?.containerNo" link type="primary" @click="router.push({ name: 'overseas-container-workbench', query: { keyword: actionTarget.containerNo } })">前往货柜维护预计到仓</el-button></td></tr>
|
<tr><th scope="row">预计到仓时间</th><td>{{ inboundPlanExpectedArrival }} <el-button v-if="access.can('platform:firstleg:terminal-drayage:list') && actionTarget?.containerNo" link type="primary" @click="router.push({ name: 'overseas-container-workbench', query: { keyword: actionTarget.containerNo } })">前往货柜维护预计到仓</el-button></td></tr>
|
||||||
<tr><th scope="row">计划入库仓</th><td>{{ inboundPlanWarehouseLabel || '—' }}</td></tr><tr><th scope="row">入仓码</th><td>{{ inboundPlanForm.inboundCode || '—' }}</td></tr>
|
<tr><th scope="row">计划入库仓</th><td>{{ inboundPlanWarehouseLabel || '—' }}</td></tr><tr><th scope="row">入仓码</th><td>{{ inboundPlanForm.inboundCode || '—' }}</td></tr>
|
||||||
|
|||||||
Reference in New Issue
Block a user