fix: align audit and dispatch workflows and release failed requests
This commit is contained in:
@@ -102,7 +102,7 @@ describe('MeasurementReviewPanel', () => {
|
||||
expect(wrapper.text()).not.toContain('通过并进入')
|
||||
await wrapper.findAll('button').find((button) => button.text() === '通过')!.trigger('click')
|
||||
await flushPromises()
|
||||
expect(confirmation).toHaveBeenCalledWith(expect.stringContaining('进入待一件代发组托'), '确认测量审核通过', expect.objectContaining({ confirmButtonText: '通过' }))
|
||||
expect(confirmation).toHaveBeenCalledWith('审核通过后,入库单将进入待分柜', '确认测量', expect.objectContaining({ confirmButtonText: '通过' }))
|
||||
expect(initialPricingSucceeded ? success : warning).toHaveBeenCalledWith(expect.stringContaining('入库单已进入待一件代发组托'))
|
||||
expect(approveMeasurement).toHaveBeenCalledTimes(1)
|
||||
expect(wrapper.emitted('changed')).toHaveLength(1)
|
||||
|
||||
@@ -140,8 +140,8 @@ async function confirmApprove() {
|
||||
if (!detail.value || !isPending.value || !canReview.value || actionLoading.value || props.postApproveLoading || props.approvalCompleted) return
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`审核通过后,入库单将进入${approvalTargetLabel.value}。系统会尝试生成初算费用,但初算失败不影响审核通过,后续可由平台人员补充调整。是否继续?`,
|
||||
'确认测量审核通过',
|
||||
'审核通过后,入库单将进入待分柜',
|
||||
'确认测量',
|
||||
{ customClass: 'admin-density-confirm', confirmButtonText: approvalButtonLabel.value, cancelButtonText: '返回', type: 'warning' },
|
||||
)
|
||||
} catch {
|
||||
@@ -285,6 +285,8 @@ defineExpose({ confirmApprove, enableWeightCorrection, approvalButtonLabel, isPe
|
||||
</section>
|
||||
|
||||
<WeightOcrPanel
|
||||
class="density-dialog-section"
|
||||
data-tone="source"
|
||||
review-mode
|
||||
table-mode
|
||||
:correction-mode="correctionMode"
|
||||
@@ -310,7 +312,7 @@ defineExpose({ confirmApprove, enableWeightCorrection, approvalButtonLabel, isPe
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.review-panel { display: grid; gap: var(--admin-space-5, 12px); color: var(--workbench-text); }
|
||||
.review-panel { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--admin-space-5, 12px); color: var(--workbench-text); }
|
||||
.review-toolbar { display: flex; align-items: center; justify-content: space-between; gap: var(--admin-space-5, 12px); position: sticky; top: 0; z-index: 3; flex-wrap: wrap; padding: var(--admin-space-3, 8px) 0; border-bottom: 1px solid var(--workbench-border); background: var(--admin-surface, #fff); }
|
||||
.review-toolbar__status, .review-toolbar__actions { display: flex; min-width: 0; align-items: center; flex-wrap: wrap; gap: var(--admin-space-3, 8px); }
|
||||
.review-toolbar__status > span { color: var(--workbench-muted); font-size: var(--admin-font-caption, 12px); }
|
||||
|
||||
@@ -100,6 +100,17 @@ const workspace: WeightWorkspaceResource = {
|
||||
},
|
||||
}
|
||||
|
||||
const confirmedReviewWorkspace: WeightWorkspaceResource = {
|
||||
...workspace,
|
||||
processStatus: 'MEASURE_PENDING',
|
||||
actualGrossWeightKg: 120,
|
||||
actualNetWeightKg: 108,
|
||||
weightResults: [{
|
||||
...pendingWeight, recognitionStatus: 'CONFIRMED', adjustedWeightValue: 120,
|
||||
adjustedWeightUnit: 'KG', grossWeightKg: 120, netWeightKg: 108, version: 1,
|
||||
}],
|
||||
}
|
||||
|
||||
function palletPage(records = [{
|
||||
palletId: '50001', palletCode: 'TP-001', palletType: '木托盘', capacityCbm: 1.2, tareWeightKg: 12,
|
||||
}]) {
|
||||
@@ -496,7 +507,7 @@ describe('WeightOcrPanel confirmation behavior', () => {
|
||||
expect(wrapper.text()).toContain('1747.500 KG')
|
||||
})
|
||||
|
||||
it('opens a table image and atomically corrects and confirms an approved weight during measurement review', async () => {
|
||||
it('opens the explicit table correction action and atomically confirms an approved weight during measurement review', async () => {
|
||||
const confirmed = {
|
||||
...pendingWeight,
|
||||
recognitionStatus: 'CONFIRMED',
|
||||
@@ -521,7 +532,10 @@ describe('WeightOcrPanel confirmation behavior', () => {
|
||||
expect(wrapper.text()).not.toContain('删除图片')
|
||||
expect(wrapper.find('.weight-data-table').exists()).toBe(true)
|
||||
expect(wrapper.find('.weight-grid').exists()).toBe(false)
|
||||
await openWeightDialog(wrapper)
|
||||
expect(wrapper.get('.weight-data-table thead th').text()).toBe('操作')
|
||||
expect(wrapper.find('.pallet-picker').exists()).toBe(false)
|
||||
await wrapper.get('button[aria-label="修改重量 1"]').trigger('click')
|
||||
await flushPromises()
|
||||
await dialogWeightInput().setValue('125')
|
||||
const reason = document.body.querySelector<HTMLTextAreaElement>('textarea[placeholder="请填写本次毛重修正原因"]')
|
||||
expect(reason).not.toBeNull()
|
||||
@@ -538,6 +552,57 @@ describe('WeightOcrPanel confirmation behavior', () => {
|
||||
expect(correctWeightResult).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ permissions: allWeightPermissions.filter(permission => !permission.endsWith('weight:edit')), status: 'MEASURE_PENDING', correctionMode: true },
|
||||
{ permissions: allWeightPermissions.filter(permission => !permission.endsWith('weight:confirm')), status: 'MEASURE_PENDING', correctionMode: true },
|
||||
{ permissions: allWeightPermissions, status: 'WAIT_SPLIT', correctionMode: true },
|
||||
{ permissions: allWeightPermissions, status: 'MEASURE_PENDING', correctionMode: false },
|
||||
])('keeps table correction unavailable for missing permissions or inactive review: %j', async ({ permissions, status, correctionMode }) => {
|
||||
vi.mocked(getWeightWorkspace).mockResolvedValue({ ...confirmedReviewWorkspace, processStatus: status })
|
||||
const wrapper = mountPanel(permissions, { reviewMode: true, correctionMode, tableMode: true })
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.find('button[aria-label="修改重量 1"]').exists()).toBe(false)
|
||||
await openWeightDialog(wrapper)
|
||||
expect(dialogWeightInput().attributes('disabled')).toBeDefined()
|
||||
expect(wrapper.getComponent(AppDialog).props('showConfirm')).toBe(false)
|
||||
expect(confirmWeightResult).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('allows cancelling and reopening correction, and preserves the draft for retry after a save fails', async () => {
|
||||
vi.mocked(getWeightWorkspace).mockResolvedValue(confirmedReviewWorkspace)
|
||||
vi.mocked(confirmWeightResult)
|
||||
.mockRejectedValueOnce(new Error('重量写入失败'))
|
||||
.mockResolvedValueOnce({ ...confirmedReviewWorkspace.weightResults[0], grossWeightKg: 125, netWeightKg: 113, version: 2 })
|
||||
const wrapper = mountPanel(allWeightPermissions, { reviewMode: true, correctionMode: true, tableMode: true })
|
||||
await flushPromises()
|
||||
const correctionButton = wrapper.get('button[aria-label="修改重量 1"]')
|
||||
await correctionButton.trigger('click')
|
||||
await flushPromises()
|
||||
await dialogWeightInput().setValue('125')
|
||||
await new DOMWrapper(document.body.querySelector<HTMLTextAreaElement>('#weight-correction-reason')!).setValue('复核磅秤读数')
|
||||
const cancelButton = [...document.body.querySelectorAll<HTMLButtonElement>('.el-dialog__footer button')].find(button => button.textContent?.trim() === '取消')!
|
||||
await new DOMWrapper(cancelButton).trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.getComponent(AppDialog).props('modelValue')).toBe(false)
|
||||
expect(confirmWeightResult).not.toHaveBeenCalled()
|
||||
|
||||
await correctionButton.trigger('click')
|
||||
await flushPromises()
|
||||
expect(Number(dialogWeightInput().element.value)).toBe(125)
|
||||
await dialogConfirmButton('保存重量修正').trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.getComponent(AppDialog).props('modelValue')).toBe(true)
|
||||
expect(Number(dialogWeightInput().element.value)).toBe(125)
|
||||
expect(dialogConfirmButton('保存重量修正').attributes('disabled')).toBeUndefined()
|
||||
expect(wrapper.emitted('changed')).toBeUndefined()
|
||||
await dialogConfirmButton('保存重量修正').trigger('click')
|
||||
await flushPromises()
|
||||
expect(confirmWeightResult).toHaveBeenCalledTimes(2)
|
||||
expect(wrapper.getComponent(AppDialog).props('modelValue')).toBe(false)
|
||||
expect(wrapper.emitted('changed')).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('closes the confirmation dialog immediately after a successful confirmation', async () => {
|
||||
const wrapper = mountPanel()
|
||||
await flushPromises()
|
||||
|
||||
@@ -504,9 +504,8 @@ defineExpose({ reload: loadWorkspace })
|
||||
<div class="weight-summary"><span>实测毛重</span><strong>{{ formatWeight(workspace.actualGrossWeightKg) }}</strong></div>
|
||||
<div class="weight-summary"><span>货物重量</span><strong>{{ formatWeight(workspace.actualNetWeightKg) }}</strong></div>
|
||||
</div>
|
||||
<div class="pallet-picker">
|
||||
<div v-if="canEdit && !reviewMode" class="pallet-picker">
|
||||
<el-select-v2
|
||||
v-if="canEdit && !reviewMode"
|
||||
v-model="selectedPalletId"
|
||||
:options="virtualPalletOptions"
|
||||
aria-label="选择称重托盘"
|
||||
@@ -582,9 +581,14 @@ defineExpose({ reload: loadWorkspace })
|
||||
|
||||
<div v-else-if="tableMode" class="weight-table-wrap">
|
||||
<div class="workbench-evidence"><table class="weight-data-table">
|
||||
<thead><tr><th>#</th><th>称重图片</th><th>托盘编码</th><th>托盘类型</th><th>库位</th><th>实测毛重(KG)</th><th>服务端确认毛重(KG)</th><th>托盘皮重(KG)</th><th>货物重量(KG)</th><th>操作</th></tr></thead>
|
||||
<thead><tr><th>操作</th><th>#</th><th>称重图片</th><th>托盘编码</th><th>托盘类型</th><th>库位</th><th>实测毛重(KG)</th><th>服务端确认毛重(KG)</th><th>托盘皮重(KG)</th><th>货物重量(KG)</th></tr></thead>
|
||||
<tbody>
|
||||
<tr v-for="(row, index) in rows" :key="row.id">
|
||||
<td>
|
||||
<el-button v-if="reviewCorrectionEnabled && row.recognitionStatus === 'CONFIRMED' && canEdit && canConfirm" link type="primary" :aria-label="`修改重量 ${index + 1}`" :disabled="Boolean(activeAction)" @click="openPreview(row)">修改重量</el-button>
|
||||
<el-tooltip v-else-if="canEdit && editable && !reviewMode" content="删除图片" placement="top"><el-button text type="danger" :aria-label="`删除称重图片 ${index + 1}`" :loading="isActionLoading('delete', row.id)" :disabled="Boolean(activeAction)" @click="removeResult(row)"><Trash2 :size="16" /></el-button></el-tooltip>
|
||||
<span v-else>—</span>
|
||||
</td>
|
||||
<td>{{ index + 1 }}</td>
|
||||
<td><WeightImageThumbnail :customer-id="customerId" :inbound-id="inboundId" :file-id="row.fileId" :alt="`称重图片 ${index + 1}`" @open="openPreview(row)" /></td>
|
||||
<td>{{ row.palletCode || '—' }}</td>
|
||||
@@ -594,7 +598,6 @@ defineExpose({ reload: loadWorkspace })
|
||||
<td>{{ formatWeight(row.grossWeightKg) }}</td>
|
||||
<td>{{ formatWeight(row.palletTareWeightKg) }}</td>
|
||||
<td class="weight-net-value">{{ formatWeight(row.netWeightKg) }}</td>
|
||||
<td><el-tooltip v-if="canEdit && editable && !reviewMode" content="删除图片" placement="top"><el-button text type="danger" :aria-label="`删除称重图片 ${index + 1}`" :loading="isActionLoading('delete', row.id)" :disabled="Boolean(activeAction)" @click="removeResult(row)"><Trash2 :size="16" /></el-button></el-tooltip><span v-else>—</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></div>
|
||||
|
||||
Reference in New Issue
Block a user