diff --git a/src/api/bpm/processInstance/index.ts b/src/api/bpm/processInstance/index.ts index 5d6eefd7..06392bc4 100644 --- a/src/api/bpm/processInstance/index.ts +++ b/src/api/bpm/processInstance/index.ts @@ -93,6 +93,11 @@ export const getApprovalDetail = async (params: any) => { return await request.get({ url: '/bpm/process-instance/get-approval-detail', params }) } +// 获取下一个执行的流程节点 +export const getNextApprovalNodes = async (params: any) => { + return await request.get({ url: '/bpm/process-instance/get-next-approval-nodes', params }) +} + // 获取表单字段权限 export const getFormFieldsPermission = async (params: any) => { return await request.get({ url: '/bpm/process-instance/get-form-fields-permission', params }) diff --git a/src/components/SimpleProcessDesignerV2/src/consts.ts b/src/components/SimpleProcessDesignerV2/src/consts.ts index c5404f18..f564c3f0 100644 --- a/src/components/SimpleProcessDesignerV2/src/consts.ts +++ b/src/components/SimpleProcessDesignerV2/src/consts.ts @@ -162,6 +162,10 @@ export enum CandidateStrategy { * 指定用户 */ USER = 30, + /** + * 审批人自选 + */ + APPROVE_USER_SELECT = 34, /** * 发起人自选 */ @@ -542,6 +546,7 @@ export const CANDIDATE_STRATEGY: DictDataVO[] = [ { label: '连续多级部门负责人', value: CandidateStrategy.MULTI_LEVEL_DEPT_LEADER }, { label: '指定岗位', value: CandidateStrategy.MULTI_LEVEL_DEPT_LEADER }, { label: '发起人自选', value: CandidateStrategy.START_USER_SELECT }, + { label: '审批人自选', value: CandidateStrategy.APPROVE_USER_SELECT }, { label: '发起人本人', value: CandidateStrategy.START_USER }, { label: '发起人部门负责人', value: CandidateStrategy.START_USER_DEPT_LEADER }, { label: '发起人连续部门负责人', value: CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER }, diff --git a/src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue b/src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue index 8856fac7..689c556d 100644 --- a/src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue +++ b/src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue @@ -117,6 +117,7 @@ const activityNodes = ref([]) // 审批 /** 设置表单信息、获取流程图数据 **/ const initProcessInfo = async (row: any, formVariables?: any) => { + // 重置指定审批人 startUserSelectTasks.value = [] startUserSelectAssignees.value = {} @@ -138,9 +139,12 @@ const initProcessInfo = async (row: any, formVariables?: any) => { await nextTick() fApi.value?.btn.show(false) // 隐藏提交按钮 - // 获取流程审批信息 - await getApprovalDetail(row) - + // 获取流程审批信息,当再次发起时,流程审批节点要根据原始表单参数预测出来 + await getApprovalDetail({ + id: row.id, + processVariablesStr: JSON.stringify(formVariables) + }) + // } // 加载流程图 const processDefinitionDetail = await DefinitionApi.getProcessDefinition(row.id) if (processDefinitionDetail) { diff --git a/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue b/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue index 9e6dbc69..74dfbb32 100644 --- a/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue +++ b/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue @@ -44,7 +44,11 @@ :rows="4" /> - +
() const signRef = ref() const approveSignFormRef = ref() -const nextAssigneesVisible = ref(false) // 是否显示下一个节点的审批人 const nextAssigneesActivityNode = ref([]) // 下一个审批节点信息 const approveReasonForm = reactive({ reason: '', @@ -711,36 +715,30 @@ const closePopover = (type: string, formRef: FormInstance | undefined) => { formRef.resetFields() } popOverVisible.value[type] = false - nextAssigneesVisible.value = false + nextAssigneesActivityNode.value = [] } /** 流程通过时,根据表单变量查询新的流程节点,判断下一个节点类型是否为自选审批人 */ const initNextAssigneesFormField = async () => { // 获取修改的流程变量, 暂时只支持流程表单 const variables = getUpdatedProcessInstanceVariables() - const data = await ProcessInstanceApi.getApprovalDetail({ + const data = await ProcessInstanceApi.getNextApprovalNodes({ processInstanceId: props.processInstance.id, + taskId: runningTask.value.id, processVariablesStr: JSON.stringify(variables) }) - - const activityId = data.todoTask?.taskDefinitionKey - if (data.activityNodes && data.activityNodes.length > 0) { - // 找到当前节点的索引 - const currentNodeIndex = data.activityNodes.findIndex((node: any) => node.id === activityId) - const nextNode = data.activityNodes[currentNodeIndex + 1] - // 情况一:发起人选择审批人:此时一般是因为条件发生变化,需要当前审批人补充选择 - if ( - nextNode.candidateStrategy === CandidateStrategy.START_USER_SELECT && - !nextNode.tasks && - nextNode.candidateUsers?.length === 0 - ) { - // 自选审批人,则弹出选择审批人弹窗 - // TODO @小北:需要考虑下,这里的 nextNode 可能是多个节点,需要怎么处理;类似你在后端的处理哈 - // TODO @小北:有点纠结,是不是写个预测下一个节点的接口,更合适? - nextAssigneesActivityNode.value = [nextNode] - nextAssigneesVisible.value = true - } - // TODO @小北:情况二:审批人选择的情况 + if (data && data.length > 0) { + data.forEach((node: any) => { + // 如果是发起人自选,并且没有审批人 或者 是审批人自选 + if ( + (isEmpty(node.tasks) && + isEmpty(node.candidateUsers) && + CandidateStrategy.START_USER_SELECT === node.candidateStrategy) || + CandidateStrategy.APPROVE_USER_SELECT === node.candidateStrategy + ) { + nextAssigneesActivityNode.value.push(node) + } + }) } } @@ -748,6 +746,20 @@ const initNextAssigneesFormField = async () => { const selectNextAssigneesConfirm = (id: string, userList: any[]) => { approveReasonForm.nextAssignees[id] = userList?.map((item: any) => item.id) } +/** 审批通过时,校验每个自选审批人的节点是否都已配置了审批人 */ +const validateNextAssignees = () => { + // 如果需要自选审批人,则校验自选审批人 + if (Object.keys(nextAssigneesActivityNode.value).length > 0) { + // 校验每个节点是否都已配置审批人 + for (const item of nextAssigneesActivityNode.value) { + if (isEmpty(approveReasonForm.nextAssignees[item.id])) { + message.warning('下一个节点的审批人不能为空!') + return false + } + } + } + return true +} /** 处理审批通过和不通过的操作 */ const handleAudit = async (pass: boolean, formRef: FormInstance | undefined) => { @@ -764,11 +776,8 @@ const handleAudit = async (pass: boolean, formRef: FormInstance | undefined) => } if (pass) { - // 如果需要自选审批人,则校验自选审批人 - if (nextAssigneesVisible.value && Object.keys(approveReasonForm.nextAssignees).length === 0) { - message.warning('下一个节点的审批人不能为空!') - return - } + const nextAssigneesValid = validateNextAssignees() + if (!nextAssigneesValid) return const variables = getUpdatedProcessInstanceVariables() // 审批通过数据 const data = { @@ -791,7 +800,7 @@ const handleAudit = async (pass: boolean, formRef: FormInstance | undefined) => } await TaskApi.approveTask(data) popOverVisible.value.approve = false - nextAssigneesVisible.value = false + nextAssigneesActivityNode.value = [] message.success('审批通过成功') } else { // 审批不通过数据 diff --git a/src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue b/src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue index 30afe69c..110b8bd6 100644 --- a/src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue +++ b/src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue @@ -43,7 +43,8 @@ v-if=" isEmpty(activity.tasks) && isEmpty(activity.candidateUsers) && - CandidateStrategy.START_USER_SELECT === activity.candidateStrategy + (CandidateStrategy.START_USER_SELECT === activity.candidateStrategy || + CandidateStrategy.APPROVE_USER_SELECT === activity.candidateStrategy) " > @@ -252,7 +253,7 @@ const nodeTypeSvgMap = { // 并行分支节点 [NodeType.PARALLEL_BRANCH_NODE]: { color: '#14bb83', svg: parallelSvg }, // 子流程节点 - [NodeType.CHILD_PROCESS_NODE]: { color: '#14bb83', svg: childProcessSvg }, + [NodeType.CHILD_PROCESS_NODE]: { color: '#14bb83', svg: childProcessSvg } } // 只有只有状态是 -1、0、1 才展示头像右小角状态小icon