fix: align audit and dispatch workflows and release failed requests
This commit is contained in:
@@ -11,7 +11,7 @@ import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||||
import { useUnsavedWorkRegistration } from '@/core/unsaved-work/useUnsavedWorkRegistration'
|
||||
import { listOverseasPurchaseWarehouseOptions } from '../api/platformOverseasBusiness'
|
||||
import { submitProcurementCommand, uploadProcurementFile, type ProcurementCommand, type ProcurementCommandBody } from '../api/procurementCommands'
|
||||
import { commandKey, definitelyRejected, readPendingProcurement, type PendingProcurement } from '../procurementPending'
|
||||
import { commandKey, type PendingProcurement } from '../procurementPending'
|
||||
import { readCorrectionDraft, type ProcurementCorrectionDraft } from '../procurementCorrectionDraft'
|
||||
import type { PlatformProcurement, ProcurementAction, ProcurementFile } from '../procurementTypes'
|
||||
import type { OverseasPurchaseWarehouseOption } from '../types'
|
||||
@@ -75,8 +75,6 @@ const { captureBaseline } = useUnsavedWorkRegistration({ key: 'procurement-purch
|
||||
watch(key, () => {
|
||||
opened.value = false; pending.value = null; storageError.value = ''; extraFiles.value = []; active.value = null; editingDraft.value = false; correctionDraft.value = null; draftError.value = ''
|
||||
if (!key.value) return
|
||||
try { pending.value = readPendingProcurement(sessionStorage.getItem(key.value)) }
|
||||
catch { storageError.value = '上次操作记录无法读取,请先联系管理员核对该订单的处理结果。' }
|
||||
try { correctionDraft.value = readCorrectionDraft(sessionStorage.getItem(draftKey.value)) }
|
||||
catch { draftError.value = '本浏览器的费用更正草稿无法读取,请核对后重新填写。' }
|
||||
}, { immediate: true })
|
||||
@@ -123,11 +121,6 @@ function useCorrectionDraft() {
|
||||
const fee = props.detail.fees.find(row => row.id === finance.value.feeId)
|
||||
active.value = action; financialCommand.value = fee && !fee.active && ['TERMINATED', 'RELEASED'].includes(fee.walletStatus) ? 'fees/replace-failed' : 'fees/correct-target'; editingDraft.value = false
|
||||
}
|
||||
async function retryPending() {
|
||||
if (editingDraft.value) { saveCorrectionDraft(); if (draftError.value) return }
|
||||
editingDraft.value = false; opened.value = false
|
||||
await submit()
|
||||
}
|
||||
watch(financialCommand, () => { if (editingDraft.value) return; finance.value.feeId = ''; finance.value.targetOriginalAmount = ''; finance.value.refundAmount = ''; form.voucherFileId = ''; error.value = null }, { flush: 'sync' })
|
||||
watch(needsWarehouse, value => { if (value && opened.value) void loadWarehouses() })
|
||||
function build(): ProcurementCommandBody {
|
||||
@@ -157,33 +150,29 @@ function build(): ProcurementCommandBody {
|
||||
async function submit() {
|
||||
if (editingDraft.value) { saveCorrectionDraft(); return }
|
||||
if (saving.value || uploading.value || storageError.value || !key.value) return
|
||||
const resuming = !!pending.value
|
||||
const scope = key.value; const orderNo = props.detail.customerView.order.orderNo; const customer = props.customerId
|
||||
if (!pending.value) {
|
||||
if (!active.value?.allowed || !access.can(active.value.permission) || !command.value) return
|
||||
try {
|
||||
if (managementBlock.value) throw new Error(managementBlock.value)
|
||||
const request = { command: command.value, payload: build() }
|
||||
const serialized = JSON.stringify(request); readPendingProcurement(serialized)
|
||||
sessionStorage.setItem(scope, serialized); pending.value = request
|
||||
pending.value = request
|
||||
} catch (issue) { error.value = { message: issue instanceof Error ? issue.message : '提交信息保存失败,请重试' }; return }
|
||||
}
|
||||
const retryFee = pending.value.command === 'fees/retry' ? props.detail.fees.find(fee => fee.id === pending.value?.payload.feeId) : null
|
||||
const requiredPermission = pending.value.command === 'fees/retry' ? (retryFee ? feeRetryPermission(retryFee) : '') : `platform:overseas-business:purchase:${commandPermissions[pending.value.command]}`
|
||||
if (!requiredPermission || !access.can(requiredPermission)) { error.value = { message: '当前账号无此节点操作权限' }; return }
|
||||
if (!requiredPermission || !access.can(requiredPermission)) { pending.value = null; error.value = { message: '当前账号无此节点操作权限' }; return }
|
||||
saving.value = true; error.value = null
|
||||
const confirmsCorrection = !resuming && ['fees/correct-target', 'fees/replace-failed'].includes(pending.value.command)
|
||||
const confirmsCorrection = ['fees/correct-target', 'fees/replace-failed'].includes(pending.value.command)
|
||||
try {
|
||||
await submitProcurementCommand(orderNo, customer, pending.value.command, pending.value.payload)
|
||||
sessionStorage.removeItem(scope)
|
||||
if (key.value !== scope) return
|
||||
if (confirmsCorrection) { sessionStorage.removeItem(draftKey.value); correctionDraft.value = null }
|
||||
pending.value = null; opened.value = false; captureBaseline(); ElMessage.success('操作已保存,请核对最新业务及资金结果'); emit('changed')
|
||||
} catch (issue) {
|
||||
if (key.value !== scope) return
|
||||
if (!resuming && definitelyRejected(issue)) { sessionStorage.removeItem(scope); pending.value = null }
|
||||
error.value = getApiErrorDetails(issue, '结果尚未确认,请使用原请求重试')
|
||||
} finally { saving.value = false }
|
||||
error.value = getApiErrorDetails(issue, '操作失败,请重新提交')
|
||||
} finally { pending.value = null; saving.value = false }
|
||||
}
|
||||
async function upload(event: Event) {
|
||||
const input = event.target as HTMLInputElement; const file = input.files?.[0]; input.value = ''
|
||||
@@ -212,10 +201,9 @@ async function upload(event: Event) {
|
||||
<dl><template v-for="action in unavailableActions" :key="action.key"><dt>{{ action.label }}</dt><dd>{{ action.reason || '当前不可操作' }}</dd></template></dl>
|
||||
</details>
|
||||
<el-alert v-if="draftError" :title="draftError" type="error" :closable="false" /><el-alert v-if="storageError" :title="storageError" type="error" :closable="false" />
|
||||
<el-alert v-if="pending" title="上次提交结果尚未确认,重试将使用原始内容与原请求编号。" type="warning" :closable="false"><p>原操作:{{ pending.command === 'review' ? (pending.payload.decision === 'APPROVE' ? '审核通过' : '审核拒绝') : actions.find(action => routes[action.key] === pending?.command)?.label || '采购节点操作' }} · {{ pending.payload.remark || pending.payload.reason }}</p><p v-if="pending.payload.amount !== undefined">原提交金额:{{ pending.payload.amount }} {{ pending.payload.currency || (['international-estimate', 'fees/refund'].includes(pending.command) ? 'CNY' : '') }}</p><el-button :loading="saving" @click="retryPending">确认上次提交结果</el-button></el-alert>
|
||||
<ApiErrorMeta v-if="error && !opened" :details="error" />
|
||||
<AppDialog class="admin-density-dialog admin-workbench is-dialog" v-model="opened" :title="active?.label || '采购节点操作'" :width="['packing', 'labels', 'charges/confirm', 'exception-close'].includes(command || '') ? 'min(1120px, calc(100vw - 28px))' : 'min(760px, calc(100vw - 28px))'" :confirm-loading="saving || uploading" :confirm-disabled="!!managementBlock || uploading || warehouseLoading || (!editingDraft && !!storageError) || !key" :confirm-text="editingDraft ? '保存本地草稿' : pending ? '重试原请求' : '确认提交'" @confirm="submit">
|
||||
<template #default><el-alert v-if="editingDraft" title="草稿仅保存在当前浏览器标签页,关闭标签页后可能丢失;不代表已确认费用。原请求未决时只能保存,结果明确后请刷新订单再核对确认。" type="info" :closable="false" /><el-alert v-if="command === 'purchase'" title="记录已实际购买的商品和供应商实付总额。资金未完成时,真实购买事实仍保留。" type="info" :closable="false" />
|
||||
<AppDialog class="admin-density-dialog admin-workbench is-dialog" v-model="opened" :title="active?.label || '采购节点操作'" :width="['packing', 'labels', 'charges/confirm', 'exception-close'].includes(command || '') ? 'min(1120px, calc(100vw - 28px))' : 'min(760px, calc(100vw - 28px))'" :confirm-loading="saving || uploading" :confirm-disabled="!!managementBlock || uploading || warehouseLoading || (!editingDraft && !!storageError) || !key" :confirm-text="editingDraft ? '保存本地草稿' : '确认提交'" @confirm="submit">
|
||||
<template #default><el-alert v-if="editingDraft" title="草稿仅保存在当前浏览器标签页,关闭标签页后可能丢失;不代表已确认费用。" type="info" :closable="false" /><el-alert v-if="command === 'purchase'" title="记录已实际购买的商品和供应商实付总额。资金未完成时,真实购买事实仍保留。" type="info" :closable="false" />
|
||||
<el-alert v-if="managementBlock" :title="managementBlock" type="warning" :closable="false" />
|
||||
<el-form class="density-form" label-position="top" :disabled="saving || uploading || (!!pending && !editingDraft)">
|
||||
<el-form-item v-if="active?.key === 'MANAGE'" label="实物更正操作"><el-select v-model="manageCommand" placeholder="选择处置或更正操作"><el-option v-for="option in manageOptions" :key="option.value" :value="option.value" :disabled="!!option.reason" :label="option.reason ? `${option.label}(${option.reason})` : option.label" /></el-select></el-form-item>
|
||||
@@ -235,7 +223,7 @@ async function upload(event: Event) {
|
||||
<el-form-item v-if="needsAmount || needsEvidence" label="本单凭证" required><el-select v-model="form.voucherFileId" placeholder="选择已上传凭证"><el-option v-for="file in files" :key="file.fileId" :value="file.fileId" :label="file.fileName" /></el-select><input aria-label="上传本单凭证" type="file" accept="application/pdf,image/jpeg,image/png" :disabled="uploading || (!!pending && !editingDraft) || saving" @change="upload" /></el-form-item>
|
||||
<el-form-item label="操作说明" required><el-input v-model="form.reason" type="textarea" :rows="3" maxlength="1000" show-word-limit /></el-form-item>
|
||||
</el-form><el-alert v-if="error" :title="error.message" type="error" :closable="false" /><ApiErrorMeta v-if="error" :details="error" /></template>
|
||||
<template #footer><div class="density-actions density-actions--footer"><el-button :disabled="saving || uploading" @click="opened = false">关闭</el-button><el-button v-if="editingDraft" :disabled="!!pending || !!storageError || saving || uploading" @click="useCorrectionDraft">核对并继续确认</el-button><el-button type="primary" :loading="saving" :disabled="uploading || warehouseLoading || (!editingDraft && !!storageError) || !key" @click="submit">{{ editingDraft ? '保存本地草稿' : pending ? '重试原请求' : '确认提交' }}</el-button></div></template>
|
||||
<template #footer><div class="density-actions density-actions--footer"><el-button :disabled="saving || uploading" @click="opened = false">关闭</el-button><el-button v-if="editingDraft" :disabled="!!pending || !!storageError || saving || uploading" @click="useCorrectionDraft">核对并继续确认</el-button><el-button type="primary" :loading="saving" :disabled="uploading || warehouseLoading || (!editingDraft && !!storageError) || !key" @click="submit">{{ editingDraft ? '保存本地草稿' : '确认提交' }}</el-button></div></template>
|
||||
</AppDialog>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user