From ee7386f4012634d086dbf7ff1e1e4cbbf37e14b4 Mon Sep 17 00:00:00 2001 From: liuyihe <2513762445@qq.com> Date: Wed, 28 Oct 2026 11:30:00 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/TransportExecutionPanel.test.ts | 1 + .../components/TransportExecutionPanel.vue | 4 +++- .../overseas-warehouse/pages/DispatchesView.test.ts | 6 +++++- src/modules/overseas-warehouse/pages/DispatchesView.vue | 4 +++- .../pages/LastMileOrderDetailView.test.ts | 9 +++++++++ 5 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/modules/overseas-warehouse/components/TransportExecutionPanel.test.ts b/src/modules/overseas-warehouse/components/TransportExecutionPanel.test.ts index fe368ef4..60cd566a 100644 --- a/src/modules/overseas-warehouse/components/TransportExecutionPanel.test.ts +++ b/src/modules/overseas-warehouse/components/TransportExecutionPanel.test.ts @@ -177,6 +177,7 @@ it.each([false, true])('confirms the %s photo stage with its own evidence and cu const wrapper = mount(TransportExecutionPanel, { ...options, props: { dispatchNo: 'PC1', pickupApproved: true } }); await flushPromises() expect(wrapper.text()).toContain(arriving ? '派送中' : '装货中') expect(wrapper.findAll('button').some(button => button.text() === '确认发车')).toBe(false) + expect(wrapper.findAll('button').some(button => ['维护车辆信息', '取消排车'].includes(button.text()))).toBe(false) await wrapper.findAll('button').find(button => button.text() === (arriving ? '上传到仓照片' : '上传装货照片'))!.trigger('click'); await flushPromises() if (!arriving) { const quantity = wrapper.get('input[role="spinbutton"]') diff --git a/src/modules/overseas-warehouse/components/TransportExecutionPanel.vue b/src/modules/overseas-warehouse/components/TransportExecutionPanel.vue index 733d64d8..764e2c96 100644 --- a/src/modules/overseas-warehouse/components/TransportExecutionPanel.vue +++ b/src/modules/overseas-warehouse/components/TransportExecutionPanel.vue @@ -38,7 +38,9 @@ const pickupStage = computed(() => trip.value?.status === 'COMPLETED' ? '取货 const pending = ref<{ suffix: string; body: object; kind?: 'cancel' } | null>(null) const trip = computed(() => detail.value?.execution.trip) const editable = computed(() => !props.readonly && access.can(permission.EDIT)) -const canChangeResources = computed(() => trip.value?.status === 'PLANNED' && props.pickupStage !== 'LOADING' && !detail.value?.pickups.some(row => row.status !== 'ASSIGNED')) +const canChangeResources = computed(() => trip.value?.status === 'PLANNED' && props.pickupStage !== 'LOADING' + && !(detail.value?.taskType === 'PICKUP' && (props.pickupApproved || props.pickupReviewState === 'APPROVED' || (props.pickupStage && props.pickupStage !== 'BEFORE_LOADING'))) + && !detail.value?.pickups.some(row => row.status !== 'ASSIGNED')) const resourceFormOpen = computed(() => canChangeResources.value && editable.value && (editingResources.value || !trip.value?.vehicleId || !trip.value?.driverId)) const locked = computed(() => busy.value || !!pending.value || licenseLoading.value || Object.values(photoBusy).some(Boolean)) const completionAction = computed(() => { diff --git a/src/modules/overseas-warehouse/pages/DispatchesView.test.ts b/src/modules/overseas-warehouse/pages/DispatchesView.test.ts index abe14858..58cb4892 100644 --- a/src/modules/overseas-warehouse/pages/DispatchesView.test.ts +++ b/src/modules/overseas-warehouse/pages/DispatchesView.test.ts @@ -72,10 +72,14 @@ it('confirms cancelling pickup and linked transfers using the transport version' wrapper.unmount(); confirm.mockRestore() }) it.each([['LOADING', '上传装货照片'], ['DELIVERING', '上传到仓照片']] as const)('opens pickup %s work from the list while detail stays separate', async (stage, label) => { - vi.mocked(listDispatches).mockResolvedValue(page([listRow({ taskType: 'PICKUP', stage, reviewState: 'APPROVED', actions: {} })])) + const allowed = { allowed: true, blockCode: null, blockReason: null } + vi.mocked(listDispatches).mockResolvedValue(page([listRow({ taskType: 'PICKUP', stage, reviewState: 'APPROVED', actions: { EDIT: allowed, CANCEL: allowed } })])) const wrapper = mount(DispatchesView, options); await flushPromises() const row = wrapper.findAllComponents(RowActions).find(entry => entry.props('moreLabel') === '更多操作 · PC/1')! expect(row.props('actions')).toEqual([expect.objectContaining({ key: 'transport', label }), expect.objectContaining({ key: 'detail', label: '详情' })]) + row.vm.$emit('action', 'EDIT'); row.vm.$emit('action', 'CANCEL'); await flushPromises() + expect(wrapper.findComponent({ name: 'TransportExecutionPanel' }).exists()).toBe(false) + expect(cancelTransport).not.toHaveBeenCalled() row.vm.$emit('action', 'transport'); await flushPromises() expect(wrapper.getComponent({ name: 'TransportExecutionPanel' }).props()).toMatchObject({ dispatchNo: 'PC/1', pickupApproved: true, editResources: false }) expect(push).not.toHaveBeenCalled() diff --git a/src/modules/overseas-warehouse/pages/DispatchesView.vue b/src/modules/overseas-warehouse/pages/DispatchesView.vue index 90d24575..37bb4ba3 100644 --- a/src/modules/overseas-warehouse/pages/DispatchesView.vue +++ b/src/modules/overseas-warehouse/pages/DispatchesView.vue @@ -140,7 +140,9 @@ function rowActions(row: DispatchListRow): RowActionItem[] { { key: 'detail', label: '详情', priority: 500 }, ] } - const available = rowActionOrder.filter(value => (value !== 'SUBMIT_REVIEW' || row.taskType === 'PICKUP') && canDispatchAction(row.actions, value, access.can)) + const available = rowActionOrder.filter(value => (value !== 'SUBMIT_REVIEW' || row.taskType === 'PICKUP') + && !(row.taskType === 'PICKUP' && ['EDIT', 'CANCEL'].includes(value) && (row.stage !== 'BEFORE_LOADING' || row.reviewState === 'APPROVED')) + && canDispatchAction(row.actions, value, access.can)) const primaryAction = available.find(value => value === 'EDIT') ?? available[0] const execution = pickupExecutionLabel(row) return [...(execution ? [{ key: 'transport', label: execution, priority: 650 }] : []), ...(canManageDocuments(row) ? [{ key: 'documents', label: '管理资料', priority: 700 }] : []), ...available.map((value, index) => ({ diff --git a/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.test.ts b/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.test.ts index 006ec8df..422064e6 100644 --- a/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.test.ts +++ b/src/modules/overseas-warehouse/pages/LastMileOrderDetailView.test.ts @@ -270,6 +270,15 @@ describe('LastMileOrderDetailView domestic detail alignment', () => { settlementMode: payable ? 'ON_ACCOUNT' : undefined, chargeableQuantity: 2, quantityUnit: '托', unitPrice: 40, }) }) + it.each(['BILL', 'WALLET'])('submits receivable settlement mode %s from the fee editor', async settlementMode => { + const { dialog } = await openFeeEditor() + await fillFee(dialog, 'RECEIVABLE') + dialog.findAllComponents({ name: 'ElSelect' }).find(select => select.props('placeholder') === '按业务默认规则')!.vm.$emit('update:modelValue', settlementMode) + await flushPromises() + await dialog.findAll('button').find(button => button.text() === '保存费用')!.trigger('click') + await flushPromises() + expect(createLastMileOrderFee).toHaveBeenCalledWith('internal-order-id', expect.objectContaining({ direction: 'RECEIVABLE', settlementMode })) + }) it('keeps fee required-field validation before a request', async () => { const { dialog } = await openFeeEditor() await dialog.findAll('button').find(button => button.text() === '保存费用')!.trigger('click'); await flushPromises()