fix(bpm):修正流程实例审批弹窗网关分支重算的并发与提交问题

- 提交时不再用节点表单值覆盖 data.variables;与预览阶段使用同一份合并变量
- onChange 加 useDebounceFn(300ms) + 请求序号去重,handleAudit 提交前 await 最新一轮重算
- 切换任务时重置请求序号与 pending 重算
- 改用 form-create 官方 formData() 取节点表单当前值
- 双 nextTick 改为 until 等 fApi 就绪,1s 兜底超时
feat/mes
YunaiV 2026-05-03 16:34:55 +08:00
parent 8571a27a15
commit 6d5705b655
1 changed files with 50 additions and 53 deletions

View File

@ -526,6 +526,7 @@ import {
} from '@/components/SimpleProcessDesignerV2/src/consts' } from '@/components/SimpleProcessDesignerV2/src/consts'
import { BpmModelFormType, BpmProcessInstanceStatus } from '@/utils/constants' import { BpmModelFormType, BpmProcessInstanceStatus } from '@/utils/constants'
import type { FormInstance, FormRules } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus'
import { until, useDebounceFn } from '@vueuse/core'
import SignDialog from './SignDialog.vue' import SignDialog from './SignDialog.vue'
import ProcessInstanceTimeline from '../detail/ProcessInstanceTimeline.vue' import ProcessInstanceTimeline from '../detail/ProcessInstanceTimeline.vue'
import { isEmpty } from '@/utils/is' import { isEmpty } from '@/utils/is'
@ -574,6 +575,8 @@ const signRef = ref()
const approveSignFormRef = ref() const approveSignFormRef = ref()
const nextAssigneesActivityNode = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([]) // const nextAssigneesActivityNode = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([]) //
const nextAssigneesTimelineRef = ref() // 线 const nextAssigneesTimelineRef = ref() // 线
let nextApprovalRequestId = 0 // onChange
let pendingNextNodesTask: Promise<unknown> | null = null // onChange await
const approveReasonForm = reactive({ const approveReasonForm = reactive({
reason: '', reason: '',
signPicUrl: '', signPicUrl: '',
@ -582,7 +585,11 @@ const approveReasonForm = reactive({
const approveReasonRule = computed(() => { const approveReasonRule = computed(() => {
return { return {
reason: [ reason: [
{ required: reasonRequire.value, message: nodeTypeName.value + '意见不能为空', trigger: 'blur' } {
required: reasonRequire.value,
message: nodeTypeName.value + '意见不能为空',
trigger: 'blur'
}
], ],
signPicUrl: [{ required: true, message: '签名不能为空', trigger: 'change' }], signPicUrl: [{ required: true, message: '签名不能为空', trigger: 'change' }],
nextAssignees: [{ required: true, message: '审批人不能为空', trigger: 'blur' }] nextAssignees: [{ required: true, message: '审批人不能为空', trigger: 'blur' }]
@ -709,11 +716,16 @@ const openPopover = async (type: string) => {
popOverVisible.value[item] = item === type popOverVisible.value[item] = item === type
}) })
if (type === 'approve') { if (type === 'approve') {
// // form-create fApi
await nextTick() // approveFormFApi
// tick form-create API if (runningTask.value?.formId > 0) {
await nextTick() // 1s until
initNextAssigneesFormField() await until(() => typeof approveFormFApi.value?.validate === 'function')
.toBeTruthy({ timeout: 1000 })
.catch(() => {})
}
//
await initNextAssigneesFormField()
} }
// await nextTick() // await nextTick()
// formRef.value.resetFields() // formRef.value.resetFields()
@ -734,6 +746,8 @@ const closePopover = (type: string, formRef: FormInstance | undefined) => {
/** 流程通过时,根据表单变量查询新的流程节点,判断下一个节点类型是否为自选审批人 */ /** 流程通过时,根据表单变量查询新的流程节点,判断下一个节点类型是否为自选审批人 */
const initNextAssigneesFormField = async () => { const initNextAssigneesFormField = async () => {
//
const requestId = ++nextApprovalRequestId
// , // ,
const variables = getUpdatedProcessInstanceVariables() const variables = getUpdatedProcessInstanceVariables()
const data = await ProcessInstanceApi.getNextApprovalNodes({ const data = await ProcessInstanceApi.getNextApprovalNodes({
@ -741,6 +755,12 @@ const initNextAssigneesFormField = async () => {
taskId: runningTask.value.id, taskId: runningTask.value.id,
processVariablesStr: JSON.stringify(variables) processVariablesStr: JSON.stringify(variables)
}) })
//
if (requestId !== nextApprovalRequestId) {
return
}
//
nextAssigneesActivityNode.value = []
if (data && data.length > 0) { if (data && data.length > 0) {
const customApproveUsersData: Record<string, any[]> = {} // Timeline const customApproveUsersData: Record<string, any[]> = {} // Timeline
data.forEach((node: any) => { data.forEach((node: any) => {
@ -769,6 +789,9 @@ const initNextAssigneesFormField = async () => {
} }
} }
/** onChange 高频触发时合并 300ms 内的连续按键,减少网关查询请求 */
const debouncedInitNextAssigneesFormField = useDebounceFn(initNextAssigneesFormField, 300)
/** 选择下一个节点的审批人 */ /** 选择下一个节点的审批人 */
const selectNextAssigneesConfirm = (id: string, userList: any[]) => { const selectNextAssigneesConfirm = (id: string, userList: any[]) => {
approveReasonForm.nextAssignees[id] = userList?.map((item: any) => item.id) approveReasonForm.nextAssignees[id] = userList?.map((item: any) => item.id)
@ -803,6 +826,10 @@ const handleAudit = async (pass: boolean, formRef: FormInstance | undefined) =>
} }
if (pass) { if (pass) {
// onChange + +
if (pendingNextNodesTask) {
await pendingNextNodesTask
}
const nextAssigneesValid = validateNextAssignees() const nextAssigneesValid = validateNextAssignees()
if (!nextAssigneesValid) return if (!nextAssigneesValid) return
const variables = getUpdatedProcessInstanceVariables() const variables = getUpdatedProcessInstanceVariables()
@ -817,13 +844,10 @@ const handleAudit = async (pass: boolean, formRef: FormInstance | undefined) =>
if (runningTask.value.signEnable) { if (runningTask.value.signEnable) {
data.signPicUrl = approveReasonForm.signPicUrl data.signPicUrl = approveReasonForm.signPicUrl
} }
// approveForm + data // getUpdatedProcessInstanceVariables data.variables
// TODO
const formCreateApi = approveFormFApi.value const formCreateApi = approveFormFApi.value
if (Object.keys(formCreateApi)?.length > 0) { if (Object.keys(formCreateApi)?.length > 0) {
await formCreateApi.validate() await formCreateApi.validate()
// @ts-ignore
data.variables = approveForm.value.value
} }
await TaskApi.approveTask(data) await TaskApi.approveTask(data)
popOverVisible.value.approve = false popOverVisible.value.approve = false
@ -1081,27 +1105,23 @@ const loadTodoTask = (task: any) => {
approveForm.value = {} approveForm.value = {}
runningTask.value = task runningTask.value = task
approveFormFApi.value = {} approveFormFApi.value = {}
// pending /Promise
nextApprovalRequestId += 1
pendingNextNodesTask = null
reasonRequire.value = task?.reasonRequire ?? false reasonRequire.value = task?.reasonRequire ?? false
nodeTypeName.value = task?.nodeType === NodeType.TRANSACTOR_NODE ? '办理' : '审批' nodeTypeName.value = task?.nodeType === NodeType.TRANSACTOR_NODE ? '办理' : '审批'
// approve . // approve
if (task && task.formId && task.formConf) { if (task && task.formId && task.formConf) {
const tempApproveForm = {} const tempApproveForm: { option?: any; rule?: any; value?: any } = {}
setConfAndFields2(tempApproveForm, task.formConf, task.formFields, task.formVariables) setConfAndFields2(tempApproveForm, task.formConf, task.formFields, task.formVariables)
// onChange // onChange
// @ts-ignore
if (!tempApproveForm.option) {
// @ts-ignore
tempApproveForm.option = {}
}
// @ts-ignore
tempApproveForm.option.onChange = () => { tempApproveForm.option.onChange = () => {
// //
if (popOverVisible.value.approve) { if (!popOverVisible.value.approve) {
// return
nextAssigneesActivityNode.value = []
//
initNextAssigneesFormField()
} }
// useDebounceFn Promise reject catch 'cancelled'
pendingNextNodesTask = debouncedInitNextAssigneesFormField().catch(() => {})
} }
approveForm.value = tempApproveForm approveForm.value = tempApproveForm
} else { } else {
@ -1128,38 +1148,15 @@ const validateNormalForm = async () => {
const getUpdatedProcessInstanceVariables = () => { const getUpdatedProcessInstanceVariables = () => {
const variables = {} const variables = {}
// //
if (props.writableFields && props.writableFields.length > 0 && props.normalFormApi) { if (props.writableFields?.length && props.normalFormApi) {
props.writableFields.forEach((field) => { props.writableFields.forEach((field) => {
variables[field] = props.normalFormApi.getValue(field) variables[field] = props.normalFormApi.getValue(field)
}) })
} }
// // form-create formData()
// approveForm.value.value form-create const nodeFormData = approveFormFApi.value?.formData?.()
if (approveForm.value?.value) { if (nodeFormData) {
Object.assign(variables, approveForm.value.value) Object.assign(variables, nodeFormData)
}
// formVariables
if (approveForm.value?.formVariables) {
Object.assign(variables, approveForm.value.formVariables)
}
// formFields
if (approveForm.value?.formFields) {
Object.assign(variables, approveForm.value.formFields)
}
// approveFormFApi
if (approveFormFApi.value && approveForm.value?.rule) {
approveForm.value.rule.forEach((field: any) => {
if (field.field) {
try {
const value = approveFormFApi.value.getValue(field.field)
if (value !== undefined && value !== null) {
variables[field.field] = value
}
} catch (e) {
//
}
}
})
} }
return variables return variables
} }