fix(admin): open measurement detail after review

This commit is contained in:
2026-09-01 20:49:04 +08:00
parent 73b229bb95
commit 2f26760135
4 changed files with 149 additions and 9 deletions
@@ -195,11 +195,73 @@ describe('MeasurementReviewPanel', () => {
await wrapper.findAll('button').find((button) => button.text().includes('通过并进入待分柜'))?.trigger('click')
await flushPromises()
expect(getMeasurementReviewDetail).toHaveBeenCalledTimes(3)
expect(getMeasurementReviewDetail).toHaveBeenCalledTimes(2)
expect(approveMeasurement).toHaveBeenCalledWith('10001', '90001', { measurementId: '70001', expectedVersion: 8 })
expect(wrapper.emitted('changed')).toHaveLength(1)
})
it('emits only after approval succeeds and blocks duplicate submissions', async () => {
let finishApproval: (() => void) | undefined
vi.mocked(approveMeasurement).mockImplementation(() => new Promise((resolve) => {
finishApproval = () => resolve({ inboundId: '90001', measurementId: '70001', processStatus: 'WAIT_SPLIT', feeStatus: 'CALCULATED', initialPricingSucceeded: true, initialPricingMessage: null, initialFee: null })
}))
vi.spyOn(ElMessageBox, 'confirm').mockResolvedValue(undefined as never)
const wrapper = mount(MeasurementReviewPanel, { props: { customerId: '10001', inboundId: '90001' }, global: { plugins: [ElementPlus] } })
await flushPromises()
const approveButton = wrapper.findAll('button').find((button) => button.text().includes('通过并进入待分柜'))!
await approveButton.trigger('click')
await approveButton.trigger('click')
expect(approveMeasurement).toHaveBeenCalledTimes(1)
expect(wrapper.emitted('changed')).toBeUndefined()
finishApproval?.()
await flushPromises()
expect(wrapper.emitted('changed')).toHaveLength(1)
expect(getMeasurementReviewDetail).toHaveBeenCalledTimes(1)
})
it('keeps the review panel open and does not emit when approval fails', async () => {
vi.mocked(approveMeasurement).mockRejectedValue(new Error('审核写入失败'))
vi.spyOn(ElMessageBox, 'confirm').mockResolvedValue(undefined as never)
const wrapper = mount(MeasurementReviewPanel, { props: { customerId: '10001', inboundId: '90001' }, global: { plugins: [ElementPlus] } })
await flushPromises()
await wrapper.findAll('button').find((button) => button.text().includes('通过并进入待分柜'))!.trigger('click')
await flushPromises()
expect(wrapper.emitted('changed')).toBeUndefined()
expect(wrapper.find('.review-panel').exists()).toBe(true)
expect(wrapper.findAll('button').some((button) => button.text().includes('通过并进入待分柜'))).toBe(true)
})
it('blocks approval while the order detail navigation is loading', async () => {
vi.spyOn(ElMessageBox, 'confirm').mockResolvedValue(undefined as never)
const wrapper = mount(MeasurementReviewPanel, {
props: { customerId: '10001', inboundId: '90001', postApproveLoading: true },
global: { plugins: [ElementPlus] },
})
await flushPromises()
await wrapper.findAll('button').find((button) => button.text().includes('通过并进入待分柜'))!.trigger('click')
expect(approveMeasurement).not.toHaveBeenCalled()
})
it('does not submit approval again after approval succeeded but navigation needs retrying', async () => {
vi.spyOn(ElMessageBox, 'confirm').mockResolvedValue(undefined as never)
const wrapper = mount(MeasurementReviewPanel, {
props: { customerId: '10001', inboundId: '90001', approvalCompleted: true },
global: { plugins: [ElementPlus] },
})
await flushPromises()
const approveButton = wrapper.findAll('button').find((button) => button.text().includes('审核已通过'))!
expect(approveButton.attributes('disabled')).toBeDefined()
await approveButton.trigger('click')
expect(approveMeasurement).not.toHaveBeenCalled()
})
it('shows historical rejected data read-only without new reject or release operations', async () => {
vi.mocked(getMeasurementReviewDetail).mockResolvedValue({ ...detail, processStatus: 'MEASURE_REJECTED', measureReviewStatus: 'REJECTED', reviewReason: '历史审核说明' })
const wrapper = mount(MeasurementReviewPanel, { props: { customerId: '10001', inboundId: '90001' }, global: { plugins: [ElementPlus] } })
@@ -21,6 +21,8 @@ const props = defineProps<{
customerName?: string | null
channelName?: string | null
channelCode?: string | null
postApproveLoading?: boolean
approvalCompleted?: boolean
}>()
const emit = defineEmits<{ changed: [] }>()
@@ -129,7 +131,7 @@ async function handleWeightChanged() {
}
async function confirmApprove() {
if (!detail.value || !isPending.value || !canReview.value) return
if (!detail.value || !isPending.value || !canReview.value || actionLoading.value || props.postApproveLoading || props.approvalCompleted) return
try {
await ElMessageBox.confirm(
'审核通过后,入库单将进入待分柜。系统会尝试生成初算费用,但初算失败不影响审核通过,后续可由平台人员补充调整。是否继续?',
@@ -147,7 +149,6 @@ async function confirmApprove() {
})
if (result.initialPricingSucceeded) ElMessage.success('审核已通过,入库单已进入待分柜,费用已完成初算')
else ElMessage.warning(`审核已通过,入库单已进入待分柜;费用初算未完成${result.initialPricingMessage ? `:${result.initialPricingMessage}` : ''}`)
await reload()
emit('changed')
} catch (error) {
ElMessage.error(isConflict(error) ? '数据已被更新,请刷新后重新审核' : getApiErrorDetails(error, '审核通过失败').message)
@@ -183,7 +184,7 @@ defineExpose({ confirmApprove, isPending, canReview, actionLoading })
</div>
<div class="review-toolbar__actions">
<el-button v-if="isPending && canReview" type="primary" plain @click="correctionMode = true"><Pencil :size="16" />修改重量</el-button>
<el-button v-if="isPending && canReview" type="primary" :loading="actionLoading === 'approve'" @click="confirmApprove"><Check :size="16" />通过并进入待分柜</el-button>
<el-button v-if="isPending && canReview" type="primary" :loading="actionLoading === 'approve' || postApproveLoading" :disabled="approvalCompleted" @click="confirmApprove"><Check :size="16" />{{ approvalCompleted ? '审核已通过' : '通过并进入待分柜' }}</el-button>
<el-button :loading="loading" aria-label="刷新审核数据" @click="reload"><RotateCcw :size="16" />刷新</el-button>
</div>
</div>