-
+
{{ metadata[id]?.fileName || '附件资料待核实' }}
替换移除引用
diff --git a/src/modules/overseas-warehouse/components/DispatchResourceFields.test.ts b/src/modules/overseas-warehouse/components/DispatchResourceFields.test.ts
index e24b8575..7fee63bb 100644
--- a/src/modules/overseas-warehouse/components/DispatchResourceFields.test.ts
+++ b/src/modules/overseas-warehouse/components/DispatchResourceFields.test.ts
@@ -22,7 +22,7 @@ it.each([['本趟承运公司', '555-0123'], [null, null]])('shows continuous ma
const value = resources(); value.vehicle.mode = 'MASTER'; value.vehicle.vehicleId = 'vehicle-1'; value.vehicle.carrierName = carrierName; value.driver.mode = 'MASTER'; value.driver.driverId = 'driver-1'; value.driver.phone = phone
const wrapper = mount(DispatchResourceFields, { props: { vehicle: value.vehicle, driver: value.driver, disabled: false, continuous: true }, global: { plugins: [ElementPlus] } }); await flushPromises()
const field = (label: string) => wrapper.findAllComponents({ name: 'ElFormItem' }).find(item => item.props('label') === label)!.getComponent({ name: 'ElInput' })
- expect(field('拖车公司名称').props('modelValue')).toBe(carrierName || '—'); expect(field('拖车公司名称').props('readonly')).toBe(true)
+ expect(wrapper.findAllComponents({ name: 'ElFormItem' }).map(item => item.props('label'))).not.toContain('拖车公司名称')
expect(field('联系电话').props('modelValue')).toBe(phone || '—'); expect(field('联系电话').props('readonly')).toBe(true)
expect(listVehicles).toHaveBeenCalled(); expect(listDrivers).not.toHaveBeenCalled()
expect(wrapper.emitted('update:vehicle')).toBeUndefined(); expect(wrapper.emitted('update:driver')).toBeUndefined(); wrapper.unmount()
@@ -43,8 +43,8 @@ it('shows the newly selected authorized vehicle company without copying it into
const selected = wrapper.emitted('update:vehicle')!.at(-1)![0] as typeof value.vehicle
expect(selected).toMatchObject({ vehicleId: 'vehicle-2', carrierName: null })
await wrapper.setProps({ vehicle: selected })
- const company = wrapper.findAllComponents({ name: 'ElFormItem' }).find(item => item.props('label') === '拖车公司名称')!.getComponent({ name: 'ElInput' })
- expect(company.props('modelValue')).toBe('新承运公司'); expect(company.props('readonly')).toBe(true); wrapper.unmount()
+ expect(wrapper.findAllComponents({ name: 'ElFormItem' }).map(item => item.props('label'))).not.toContain('拖车公司名称')
+ wrapper.unmount()
})
it.each([false, true])('renders known business labels without internal IDs (continuous=%s)', async continuous => {
const value = resources(); value.vehicle.mode = 'MASTER'; value.vehicle.vehicleId = '900719925474099301'; value.driver.mode = 'MASTER'; value.driver.driverId = '900719925474099302'
diff --git a/src/modules/overseas-warehouse/components/DispatchResourceFields.vue b/src/modules/overseas-warehouse/components/DispatchResourceFields.vue
index 86204128..2027f27b 100644
--- a/src/modules/overseas-warehouse/components/DispatchResourceFields.vue
+++ b/src/modules/overseas-warehouse/components/DispatchResourceFields.vue
@@ -10,7 +10,6 @@ const driver = defineModel
('driver', { required: true })
const props = defineProps<{ disabled: boolean; continuous?: boolean; dispatchNo?: string }>()
const access = useAccessPreviewStore(), vehicles = ref([]), drivers = ref([]), error = ref('')
/** 维护弹窗只读展示本趟快照或已读取的当前主档资料,不回写主档字段。 */
-const masterCarrierName = computed(() => vehicle.value.carrierName?.trim() || vehicles.value.find(row => row.id === vehicle.value.vehicleId)?.driverOrDispatchCompany?.trim() || '—')
const masterDriverPhone = computed(() => driver.value.phone?.trim() || drivers.value.find(row => row.id === driver.value.driverId)?.details.phone?.trim() || '—')
const currentVehicle = ref(null), vehicleUnavailable = ref(false)
const vehicleOptions = computed(() => [...vehicles.value, ...(currentVehicle.value && !vehicles.value.some(row => row.id === currentVehicle.value?.id) ? [currentVehicle.value] : [])])
@@ -63,7 +62,6 @@ function selectDriver(id: string) {
| |
| |
- |
| |
|
| |
diff --git a/src/modules/overseas-warehouse/components/DispatchVehicleMaintenanceForm.vue b/src/modules/overseas-warehouse/components/DispatchVehicleMaintenanceForm.vue
index 57794491..0a76d002 100644
--- a/src/modules/overseas-warehouse/components/DispatchVehicleMaintenanceForm.vue
+++ b/src/modules/overseas-warehouse/components/DispatchVehicleMaintenanceForm.vue
@@ -31,7 +31,7 @@ function confirmedAt(appointment: DispatchDestinationAppointment) {
diff --git a/src/modules/overseas-warehouse/components/DispatchWorkflowDialog.layout.test.ts b/src/modules/overseas-warehouse/components/DispatchWorkflowDialog.layout.test.ts
index ab69481c..8996d019 100644
--- a/src/modules/overseas-warehouse/components/DispatchWorkflowDialog.layout.test.ts
+++ b/src/modules/overseas-warehouse/components/DispatchWorkflowDialog.layout.test.ts
@@ -151,7 +151,7 @@ describe('dispatch workflow native layout', () => {
const { dialog } = await render('EDIT', undefined, false, { dispatchInfo: { ...source.dispatchInfo,
departedAt: '2026-07-01T15:00:00Z', plannedDepartureAt: '2026-07-01T16:00:00Z', estimatedArrivalAt: '2026-07-01T17:00:00Z',
} })
- expect(field(dialog, '发车时间').get('[aria-label="发车时间时区换算"]').text()).toBe('美东2026-07-01 11:00:00美西2026-07-01 08:00:00')
+ expect(field(dialog, '发车时间')).toBeUndefined()
expect(field(dialog, '计划发车时间').get('[aria-label="计划发车时间时区换算"]').text()).toBe('美东2026-07-01 12:00:00美西2026-07-01 09:00:00')
expect(field(dialog, '预计到达时间').get('[aria-label="预计到达时间时区换算"]').text()).toBe('美东2026-07-01 13:00:00美西2026-07-01 10:00:00')
expect(field(dialog, '预计任务结束').get('[aria-label="预计任务结束时区换算"]').text()).toBe('美东2026-11-01 05:00:00美西2026-11-01 02:00:00')
@@ -178,7 +178,7 @@ describe('dispatch workflow native layout', () => {
for (const [label, value] of [['派送方式', '外配'], ['提货方式', '一提多卸'], ['装车类型', '混装'], ['车辆类型', '厢式货车']]) field(dialog, label!).getComponent({ name: 'ElSelect' }).vm.$emit('update:modelValue', value)
await field(dialog, '车队邮箱').get('input').setValue(' fleet@example.test ')
await field(dialog, '预约账号').get('input').setValue(' fleet-account ')
- expect(field(dialog, '发车时间').get('input').attributes('readonly')).toBeDefined()
+ expect(field(dialog, '发车时间')).toBeUndefined()
expect(dialog.find('textarea[aria-label="本次操作备注"]').exists()).toBe(false)
await dialog.get('button[aria-label="展开全屏"]').trigger('click'); await flushPromises()
dialog.vm.$emit('confirm'); await flushPromises()
@@ -202,7 +202,7 @@ describe('dispatch workflow native layout', () => {
await field(dialog,'车牌号').get('input').setValue('CA-PRESERVED')
dialog.vm.$emit('confirm');await flushPromises()
const original=structuredClone(vi.mocked(api.updateDispatch).mock.calls[0]![1])
- expect(dialog.text()).toContain('workflow-request')
+ expect((wrapper.vm as unknown as { error: { requestId: string } }).error.requestId).toBe('workflow-request')
expect(field(dialog,'车牌号').get('input').attributes('disabled')).toBeUndefined()
expect((field(dialog,'车牌号').get('input').element as HTMLInputElement).value).toBe('CA-PRESERVED')
await field(dialog,'车牌号').get('input').setValue('CA-CHANGED')
diff --git a/src/modules/overseas-warehouse/components/DispatchWorkflowDialog.vue b/src/modules/overseas-warehouse/components/DispatchWorkflowDialog.vue
index 1c2ab435..073f3a3e 100644
--- a/src/modules/overseas-warehouse/components/DispatchWorkflowDialog.vue
+++ b/src/modules/overseas-warehouse/components/DispatchWorkflowDialog.vue
@@ -275,7 +275,7 @@ watch([open, () => props.dispatchNo, () => props.action, () => props.operation],
-
+
diff --git a/src/modules/overseas-warehouse/pages/InboundDialogs.behavior.test.ts b/src/modules/overseas-warehouse/pages/InboundDialogs.behavior.test.ts
index 7f8d3b5d..20c24827 100644
--- a/src/modules/overseas-warehouse/pages/InboundDialogs.behavior.test.ts
+++ b/src/modules/overseas-warehouse/pages/InboundDialogs.behavior.test.ts
@@ -57,7 +57,6 @@ beforeEach(() => {
})
async function listPage() { const wrapper = mount(InboundOrdersView, { attachTo: document.body, global: { plugins: [ElementPlus] } }); await flushPromises(); return wrapper }
function popup(wrapper: VueWrapper, title: string) { return wrapper.findAllComponents(AppDialog).find(dialog => dialog.props('title').startsWith(title))! }
-async function openEditPlan() { const wrapper = await listPage(); await wrapper.findAll('.el-table__body-wrapper button').find(button => button.text() === '编辑')!.trigger('click'); await flushPromises(); return { wrapper, dialog: popup(wrapper, '编辑入库单') } }
async function openEditAppointment() { const wrapper = await listPage(); await wrapper.findAll('.el-table__body-wrapper button').find(button => button.text() === '预约')!.trigger('click'); await flushPromises(); return { wrapper, dialog: popup(wrapper, '编辑预约') } }
async function openMove() { const wrapper = mount(InboundOrderDetailView, { attachTo: document.body, global: { plugins: [ElementPlus] } }); await flushPromises(); await wrapper.findAll('.el-table__body-wrapper button').find(button => button.text() === '移库')!.trigger('click'); await flushPromises(); return { wrapper, dialog: popup(wrapper, '托盘移库') } }
@@ -140,47 +139,17 @@ describe('inbound appointment native dialogs', () => {
})
describe('inbound plan and pallet move native dialogs', () => {
- it('hides plan editing for first-leg generated inbound orders', async () => {
- vi.mocked(listInboundOrders).mockResolvedValue({ records: [{ ...order, orderType: 'FIRST_LEG' }], total: 1, page: 1, size: 10, pages: 1 })
+ it.each(['ONE_CLICK_DROPSHIP', 'FIRST_LEG', 'RELABEL_APPLICATION', 'WAREHOUSE_TRANSFER', 'RETURN_RELABEL', 'DELIVERY_RETURN', 'LOCAL_LOOSEBOARD', 'CARGO_DISCARD', 'REJECTED', 'EXPRESS', 'FUTURE_TYPE', null])('hides plan editing for inbound order type %s even with edit permission', async (orderType) => {
+ vi.mocked(listInboundOrders).mockResolvedValue({ records: [{ ...order, orderType }], total: 1, page: 1, size: 10, pages: 1 })
const wrapper = await listPage()
- expect(wrapper.findAll('.el-table__body-wrapper button').map(button => button.text())).not.toContain('编辑')
- expect(wrapper.findAll('.el-table__body-wrapper button').map(button => button.text())).toContain('查看')
+ const actions = wrapper.findAll('.el-table__body-wrapper button').map(button => button.text())
+ expect(actions).not.toContain('编辑')
+ expect(actions).toContain('查看')
+ expect(actions).toContain('查看预约')
+ expect(actions).toContain('预约')
+ expect(wrapper.findAllComponents(AppDialog).some(dialog => dialog.props('title').startsWith('编辑入库单'))).toBe(false)
expect(listReceivingOverseasWarehouses).not.toHaveBeenCalled()
- })
- it('shows plan identity and saves the selected master warehouse with the internal version through fullscreen', async () => {
- const { dialog } = await openEditPlan()
- expect(dialog.get('[aria-label="入库计划来源"]').findAll('input')).toHaveLength(0)
- expect(dialog.get('[aria-label="入库计划来源"]').text()).toContain('入库单号')
- expect(dialog.get('[aria-label="入库计划来源"]').text()).not.toContain('版本')
- const select = dialog.findComponent({ name: 'ElSelect' })
- select.vm.$emit('update:modelValue', warehouse.id); select.vm.$emit('change', warehouse.id); await flushPromises()
- await dialog.get('button[aria-label="展开全屏"]').trigger('click'); await flushPromises()
- dialog.vm.$emit('confirm'); await flushPromises()
- expect(updateInboundPlan).toHaveBeenCalledExactlyOnceWith('IP-1', { customerId: 'C-1', overseasWarehouseId: warehouse.id, warehouseCode: 'LA2', warehouseName: '洛杉矶二仓', version: 0 })
- })
- it('locks a pending plan edit and keeps the warehouse and diagnostic context after failure', async () => {
- let reject!: (reason: unknown) => void
- vi.mocked(updateInboundPlan).mockReturnValueOnce(new Promise((_, no) => { reject = no }))
- const { dialog } = await openEditPlan()
- const select = dialog.findComponent({ name: 'ElSelect' })
- select.vm.$emit('update:modelValue', warehouse.id); select.vm.$emit('change', warehouse.id); await flushPromises()
- dialog.vm.$emit('confirm'); dialog.vm.$emit('confirm'); await flushPromises()
- expect(updateInboundPlan).toHaveBeenCalledTimes(1)
- expect(dialog.findComponent({ name: 'ElDialog' }).props()).toMatchObject({ showClose: false, closeOnPressEscape: false })
- expect(select.find('input').attributes('disabled')).toBeDefined()
- reject(new ApiResponseError('版本已变化', 'A0409', 409, null, 'plan-popup-request')); await flushPromises()
- expect(dialog.props('modelValue')).toBe(true)
- expect(select.props('modelValue')).toBe(warehouse.id)
- expect(dialog.text()).toContain('plan-popup-request')
- })
- it('does not save while the plan warehouse candidates are loading', async () => {
- let resolve!: (value: Awaited>) => void
- vi.mocked(listReceivingOverseasWarehouses).mockReturnValue(new Promise(done => { resolve = done }))
- const { dialog } = await openEditPlan()
- expect(dialog.props('loading')).toBe(true)
- dialog.vm.$emit('confirm'); await flushPromises(); expect(updateInboundPlan).not.toHaveBeenCalled()
- resolve({ records: [warehouse], page: 1, size: 200, total: 1, pages: 1 }); await flushPromises()
- expect(dialog.props('loading')).toBe(false)
+ expect(updateInboundPlan).not.toHaveBeenCalled()
})
it('loads only physical usable locations and preserves a valid zero pallet version in the command', async () => {
const { dialog } = await openMove()
diff --git a/src/modules/overseas-warehouse/pages/InboundOrdersView.vue b/src/modules/overseas-warehouse/pages/InboundOrdersView.vue
index 7a8d6d72..f2e3ebe0 100644
--- a/src/modules/overseas-warehouse/pages/InboundOrdersView.vue
+++ b/src/modules/overseas-warehouse/pages/InboundOrdersView.vue
@@ -19,8 +19,7 @@ import SmartDispatchDialog from '../components/SmartDispatchDialog.vue'
import FbaAppointmentFields from '../components/FbaAppointmentFields.vue'
import type { PlanningSourceLookup } from '@/modules/appointment-calendar'
import { createAppointmentForm, appointmentPayload, appointmentFormError } from '../appointmentForm'
-import { createFbaAppointment, getFbaAppointment, listInboundOrders, updateFbaAppointment, updateInboundPlan, type InboundOrderSummaryDto } from '../api/inboundOrders'
-import { listReceivingOverseasWarehouses, type ReceivingOverseasWarehouseDto } from '../api/pallets'
+import { createFbaAppointment, getFbaAppointment, listInboundOrders, updateFbaAppointment, type InboundOrderSummaryDto } from '../api/inboundOrders'
import { overseasWarehousePermissions as permission } from '../permissions'
import { formatOverseasDateTime, inboundPlanStatusLabel, lastMileOrderTypeLabel } from '../lastMilePresentation'
import type { InboundOrderListItem } from '../types'
@@ -68,7 +67,6 @@ const appointmentViewOnly = ref(false)
const appointmentReplacementNos = ref([])
const appointmentReadOnly = computed(() => appointmentViewOnly.value || !access.can(permission.inboundOrderAppointment))
const appointmentTitle = computed(() => `${appointmentReadOnly.value ? '查看预约' : appointmentMode.value === 'edit' ? '编辑预约' : '预约'} · ${editingAppointmentNo.value || `${appointmentSelectedCount.value} 条入库单`}`)
-const planEditOpen = ref(false); const planEditLoading = ref(false); const planEditSaving = ref(false); const planEditTarget = ref(null); const planEditForm = reactive({ warehouseId: '', warehouseCode: '', warehouseName: '', version: 0 }); const warehouses = ref([])
function selectableRow(source: object) {
const row = source as InboundOrderListItem
@@ -234,9 +232,6 @@ async function load(model: QueryModel = filters.value) {
function search(model: QueryModel) { page.value = 1; void load(model) }
function reset(model: QueryModel) { page.value = 1; void load(model) }
function openDetail(source: object) { const row = source as InboundOrderListItem; void router.push({ name: 'overseas-inbound-order-detail', params: { orderId: row.orderNo }, query: { customerId: row.customerId } }) }
-function canEditPlan(row: InboundOrderListItem) { return access.can(permission.inboundOrderEdit) && row.status === '待入库' && row.type !== '头程订单' }
-async function openPlanEdit(source: object) { const row = source as InboundOrderListItem; if (!canEditPlan(row)) return; planEditTarget.value = row; planEditOpen.value = true; planEditLoading.value = true; mutationError.value = null; try { Object.assign(planEditForm, { warehouseId: row.warehouseId, warehouseCode: row.destinationWarehouseCode === '—' ? '' : row.destinationWarehouseCode, warehouseName: row.destinationWarehouseName === '—' ? '' : row.destinationWarehouseName, version: row.version }); if (!warehouses.value.length) warehouses.value = (await listReceivingOverseasWarehouses()).records } catch (error) { planEditOpen.value = false; mutationError.value = getApiErrorDetails(error, '海外仓列表加载失败,请重试') } finally { planEditLoading.value = false } }
-async function submitPlanEdit() { if (planEditSaving.value || planEditLoading.value || !access.can(permission.inboundOrderEdit)) return; const target = planEditTarget.value; if (!target || !canEditPlan(target)) return; if (!planEditForm.warehouseId) { ElMessage.warning('请选择目的仓'); return } planEditSaving.value = true; mutationError.value = null; try { await updateInboundPlan(target.orderNo, { customerId: target.customerId, overseasWarehouseId: planEditForm.warehouseId, warehouseCode: planEditForm.warehouseCode, warehouseName: planEditForm.warehouseName, version: planEditForm.version }); planEditOpen.value = false; ElMessage.success('入库单已更新'); await load() } catch (error) { mutationError.value = getApiErrorDetails(error, '入库单更新失败,请重试') } finally { planEditSaving.value = false } }
onMounted(() => { void load() })
@@ -251,7 +246,7 @@ onMounted(() => { void load() })
预约智能排车
- 查看编辑查看预约预约管理多批预约
+ 查看查看预约预约管理多批预约
共 {{ total }} 条入库单 · 已选 {{ selectedRows.length }} 条
@@ -264,11 +259,6 @@ onMounted(() => { void load() })