优化
This commit is contained in:
@@ -203,3 +203,16 @@ describe('whole-dispatch four-tab detail', () => {
|
||||
wrapper.unmount()
|
||||
})
|
||||
})
|
||||
|
||||
it('shows saved dispatch exceptions and their own images in the existing dispatch tab', async () => {
|
||||
const value = detail({ stage: 'DELIVERING' })
|
||||
value.dispatchInfo.exceptions = [{ exceptionType: 'REFUSED', returnDirection: 'OVERSEAS_WAREHOUSE', description: '现场拒收待处理', imageFileIds: ['901'], occurredAt: '2026-10-21T10:00:00Z' }]
|
||||
vi.mocked(getDispatch).mockResolvedValue(value)
|
||||
vi.mocked(getDispatchEvidence).mockResolvedValue({ fileId: '901', fileName: '异常现场.png', contentType: 'image/png', sizeBytes: 3 })
|
||||
const wrapper = mount(DispatchDetailView, options); await flushPromises()
|
||||
await wrapper.findAll('[role="tab"]')[1]!.trigger('click'); await flushPromises()
|
||||
expect(wrapper.text()).toContain('拒收'); expect(wrapper.text()).toContain('退回海外仓'); expect(wrapper.text()).toContain('现场拒收待处理')
|
||||
expect(getDispatchEvidence).toHaveBeenCalledWith('PC/1', 'EXCEPTION', '901')
|
||||
expect(wrapper.findAll('[role="tab"]')).toHaveLength(4); expect(wrapper.findAll('input[type="file"]')).toHaveLength(0)
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
@@ -13,7 +13,7 @@ import LoadErrorState from '@/shared/components/LoadErrorState.vue'
|
||||
import { getDispatch } from '../api/dispatches'
|
||||
import type { DispatchAction, DispatchDetail } from '../dispatchContracts'
|
||||
import { stageLabel } from '../dispatchContracts'
|
||||
import { canDispatchAction, dispatchActionLabels, dispatchRejectionReason, dispatchReviewAttachmentFields, dispatchReviewEvidence, displayDispatchValue as text } from '../dispatchPresentation'
|
||||
import { dispatchExceptionLabels, dispatchReturnDirectionLabels, canDispatchAction, dispatchActionLabels, dispatchRejectionReason, dispatchReviewAttachmentFields, dispatchReviewEvidence, displayDispatchValue as text } from '../dispatchPresentation'
|
||||
import TransportExecutionPanel from '../components/TransportExecutionPanel.vue'
|
||||
import DispatchWorkflowDialog from '../components/DispatchWorkflowDialog.vue'
|
||||
import DispatchEvidenceFiles from '../components/DispatchEvidenceFiles.vue'
|
||||
@@ -89,7 +89,13 @@ onBeforeUnmount(() => { generation++ })
|
||||
<el-alert v-if="dispatchRejectionReason(detail?.reviews)" title="已退回客服处理(车辆信息),请核对原因后重新提交调度审核。" :description="dispatchRejectionReason(detail?.reviews)" type="warning" :closable="false" show-icon />
|
||||
<el-tabs v-if="!detail?.taskType || detail.taskType === 'DELIVERY'" v-model="activeTab" class="density-element-tabs density-contained-tabs">
|
||||
<el-tab-pane name="vehicle"><template #label><span class="density-tab-icon" aria-hidden="true">🚚</span><span>车辆信息</span></template><dl class="detail-fields density-fields"><div v-for="field in vehicleFields" :key="field[0]"><dt>{{ field[0] }}</dt><dd>{{ field[1] }}</dd></div></dl><DispatchEvidenceFiles v-for="field in dispatchReviewAttachmentFields" :key="field.key" :model-value="reviewEvidence[field.key]" :dispatch-no="dispatchNo" :kind="field.kind" :label="field.label" readonly /></el-tab-pane>
|
||||
<el-tab-pane name="dispatch"><template #label><span class="density-tab-icon" aria-hidden="true">📋</span><span>排车信息</span></template><dl class="detail-fields density-fields"><div v-for="field in dispatchFields" :key="field[0]"><dt>{{ field[0] }}</dt><dd>{{ field[1] }}</dd></div></dl></el-tab-pane>
|
||||
<el-tab-pane name="dispatch"><template #label><span class="density-tab-icon" aria-hidden="true">📋</span><span>排车信息</span></template><dl class="detail-fields density-fields"><div v-for="field in dispatchFields" :key="field[0]"><dt>{{ field[0] }}</dt><dd>{{ field[1] }}</dd></div></dl><section v-if="detail && detail.dispatchInfo.exceptions.length" class="detail-section" aria-label="派送异常记录">
|
||||
<h3>派送异常记录</h3>
|
||||
<article v-for="(record, index) in detail.dispatchInfo.exceptions" :key="index">
|
||||
<dl class="density-fields"><div class="density-field"><dt>异常类型</dt><dd>{{ dispatchExceptionLabels[record.exceptionType] }}</dd></div><div class="density-field"><dt>登记时间</dt><dd>{{ time(record.occurredAt) }}</dd></div><div class="density-field"><dt>退货去向</dt><dd>{{ record.returnDirection ? dispatchReturnDirectionLabels[record.returnDirection] : '—' }}</dd></div><div class="density-field"><dt>异常说明</dt><dd>{{ record.description || '—' }}</dd></div></dl>
|
||||
<DispatchEvidenceFiles v-if="record.imageFileIds.length" :model-value="record.imageFileIds" :dispatch-no="dispatchNo" kind="EXCEPTION" label="异常图片" readonly />
|
||||
</article>
|
||||
</section></el-tab-pane>
|
||||
<el-tab-pane name="documents"><template #label><span class="density-tab-icon" aria-hidden="true">📝</span><span>单据信息</span></template>
|
||||
<dl class="detail-fields density-fields"><div v-for="field in documentFields" :key="field[0]"><dt>{{ field[0] }}</dt><dd>{{ field[1] }}</dd></div><div class="linked-documents"><dt>关联派送单</dt><dd><template v-for="delivery in detail?.documents.deliveries || []" :key="delivery.deliveryNo"><el-button v-if="access.can(permission.deliveryList)" link type="primary" @click="router.push({ name: 'overseas-delivery-order-detail', params: { deliveryNo: delivery.deliveryNo } })">{{ delivery.deliveryNo }}</el-button><span v-else>{{ text(delivery.deliveryNo) }}</span></template><span v-if="!detail?.documents.deliveries.length">—</span></dd></div></dl>
|
||||
<div class="density-actions"><el-button v-for="outbound in detail?.documents.outbounds || []" v-show="access.can(permission.outboundList)" :key="outbound.outboundNo" link @click="router.push({ name: 'overseas-outbounds', query: { documentNo: outbound.outboundNo } })">查看出库 {{ outbound.outboundNo }}</el-button></div>
|
||||
|
||||
@@ -204,3 +204,18 @@ it('clears an earlier input rejection after the corrected workflow succeeds', as
|
||||
expect(wrapper.text()).not.toContain('请补齐本趟资料')
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('opens the exception form from the dispatching row and hides it without permission', async () => {
|
||||
const gate = { allowed: true, blockCode: null, blockReason: null }
|
||||
vi.mocked(listDispatches).mockResolvedValue(page([listRow({ stage: 'DELIVERING', actions: { MARK_EXCEPTION: gate } })]))
|
||||
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.arrayContaining([expect.objectContaining({ key: 'MARK_EXCEPTION', label: '标记异常', tone: 'danger' })]))
|
||||
row.vm.$emit('action', 'MARK_EXCEPTION'); await flushPromises()
|
||||
expect(wrapper.getComponent({ name: 'DispatchWorkflowDialog' }).props()).toMatchObject({ modelValue: true, action: 'MARK_EXCEPTION', dispatchNo: 'PC/1' })
|
||||
wrapper.unmount(); can.mockReturnValue(false)
|
||||
const denied = mount(DispatchesView, options); await flushPromises()
|
||||
const deniedRow = denied.findAllComponents(RowActions).find(entry => entry.props('moreLabel') === '更多操作 · PC/1')!
|
||||
expect((deniedRow.props('actions') as Array<{ key: string }>).map(item => item.key)).not.toContain('MARK_EXCEPTION')
|
||||
denied.unmount()
|
||||
})
|
||||
|
||||
@@ -78,7 +78,7 @@ function search(value: QueryModel) { model.value = value; page.value = 1; void l
|
||||
function switchStage(value: DispatchStage) { stage.value = value; section.value = 'VEHICLE_INFO'; page.value = 1; void load() }
|
||||
function switchSection(value: BeforeLoadingSection) { section.value = value; page.value = 1; void load() }
|
||||
function openDetail(row: DispatchListRow) { void router.push({ name: 'overseas-dispatch-detail', params: { dispatchNo: row.dispatchNo } }) }
|
||||
const rowActionOrder: DispatchAction[] = ['EDIT', 'SUBMIT_REVIEW', 'DISPATCH_REVIEW', 'LOADING_REVIEW', 'RETURN_TO_REVIEW', 'LOADING_COMPLETE', 'CANCEL']
|
||||
const rowActionOrder: DispatchAction[] = ['EDIT', 'SUBMIT_REVIEW', 'DISPATCH_REVIEW', 'LOADING_REVIEW', 'RETURN_TO_REVIEW', 'LOADING_COMPLETE', 'MARK_EXCEPTION', 'CANCEL']
|
||||
function rowActions(row: DispatchListRow): RowActionItem[] {
|
||||
if (showSections.value && section.value !== 'VEHICLE_INFO') {
|
||||
const review = section.value
|
||||
@@ -92,7 +92,7 @@ function rowActions(row: DispatchListRow): RowActionItem[] {
|
||||
label: dispatchActionLabels[value],
|
||||
ariaLabel: dispatchActionLabels[value],
|
||||
priority: value === primaryAction ? 600 : 400 - index * 10,
|
||||
tone: value === 'CANCEL' ? 'danger' as const : undefined,
|
||||
tone: ['CANCEL', 'MARK_EXCEPTION'].includes(value) ? 'danger' as const : undefined,
|
||||
})), { key: 'detail', label: '详情', priority: 500 }, ...(canDispatchAction(row.actions, 'EDIT', access.can) ? [{ key: 'route-order', label: '调整卸货顺序', priority: 190 }] : []), { key: 'bol', label: '下载BOL', priority: 100, loading: bolLoadingNo.value === row.dispatchNo }]
|
||||
}
|
||||
function handleAction(value: string, row: DispatchListRow) {
|
||||
|
||||
Reference in New Issue
Block a user