feat(流程审批): 新增 审批时可动态指定下一级审批人

This commit is contained in:
konbai
2022-11-23 02:30:05 +08:00
parent 704252fa5b
commit 6e6e386a31
4 changed files with 136 additions and 33 deletions

View File

@@ -1,5 +1,6 @@
package com.ruoyi.flowable.utils; package com.ruoyi.flowable.utils;
import cn.hutool.core.util.ObjectUtil;
import org.flowable.bpmn.converter.BpmnXMLConverter; import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*; import org.flowable.bpmn.model.*;
@@ -92,6 +93,23 @@ public class ModelUtils {
return null; return null;
} }
public static UserTask getUserTaskByKey(BpmnModel model, String taskKey) {
Process process = model.getMainProcess();
FlowElement flowElement = process.getFlowElement(taskKey);
if (flowElement instanceof UserTask) {
return (UserTask) flowElement;
}
return null;
}
public static boolean isMultiInstance(BpmnModel model, String taskKey) {
UserTask userTask = getUserTaskByKey(model, taskKey);
if (ObjectUtil.isNotNull(userTask)) {
return userTask.hasMultiInstanceLoopCharacteristics();
}
return false;
}
/** /**
* 获取所有用户任务节点 * 获取所有用户任务节点
* *

View File

@@ -57,4 +57,8 @@ public class WfTaskBo {
* 抄送用户Id * 抄送用户Id
*/ */
private String copyUserIds; private String copyUserIds;
/**
* 下一节点审批人
*/
private String nextUserIds;
} }

View File

@@ -97,6 +97,10 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
} }
// 设置任务节点名称 // 设置任务节点名称
taskBo.setTaskName(task.getName()); taskBo.setTaskName(task.getName());
// 处理下一级审批人
if (StringUtils.isNotBlank(taskBo.getNextUserIds())) {
this.assignNextUsers(task.getProcessDefinitionId(), taskBo.getProcInsId(), taskBo.getNextUserIds());
}
// 处理抄送用户 // 处理抄送用户
if (!copyService.makeCopy(taskBo)) { if (!copyService.makeCopy(taskBo)) {
throw new RuntimeException("抄送任务失败"); throw new RuntimeException("抄送任务失败");
@@ -684,4 +688,54 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
} }
} }
} }
/**
* 指派下一任务审批人
* @param processDefId 流程定义id
* @param processInsId 流程实例id
* @param userIds 用户ids
*/
private void assignNextUsers(String processDefId, String processInsId, String userIds) {
// 获取所有节点信息
List<Task> list = taskService.createTaskQuery()
.processInstanceId(processInsId)
.list();
if (list.size() == 0) {
return;
}
Queue<String> assignIds = CollUtil.newLinkedList(userIds.split(","));
if (list.size() == assignIds.size()) {
for (Task task : list) {
taskService.setAssignee(task.getId(), assignIds.poll());
}
return;
}
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefId);
// 优先处理非多实例任务
Iterator<Task> iterator = list.iterator();
while (iterator.hasNext()) {
Task task = iterator.next();
if (!ModelUtils.isMultiInstance(bpmnModel, task.getTaskDefinitionKey())) {
if (!assignIds.isEmpty()) {
taskService.setAssignee(task.getId(), assignIds.poll());
}
iterator.remove();
}
}
// 若存在多实例任务,则进行动态加减签
if (CollUtil.isNotEmpty(list)) {
if (assignIds.isEmpty()) {
// 动态减签
for (Task task : list) {
runtimeService.deleteMultiInstanceExecution(task.getExecutionId(), true);
}
} else {
// 动态加签
for (String assignId : assignIds) {
Map<String, Object> assignVariables = Collections.singletonMap(BpmnXMLConstants.ATTRIBUTE_TASK_USER_ASSIGNEE, assignId);
runtimeService.addMultiInstanceExecution(list.get(0).getTaskDefinitionKey(), list.get(0).getProcessInstanceId(), assignVariables);
}
}
}
}
} }

View File

@@ -17,20 +17,31 @@
</div> </div>
<el-row> <el-row>
<el-col :span="20" :offset="2"> <el-col :span="20" :offset="2">
<el-form ref="taskForm" :model="taskForm" :rules="rules" label-width="80px"> <el-form ref="taskForm" :model="taskForm" :rules="rules" label-width="120px">
<el-form-item label="审批意见" prop="comment"> <el-form-item label="审批意见" prop="comment">
<el-input type="textarea" :rows="5" v-model="taskForm.comment" placeholder="请输入 审批意见" /> <el-input type="textarea" :rows="5" v-model="taskForm.comment" placeholder="请输入 审批意见" />
</el-form-item> </el-form-item>
<el-form-item label="抄送人" prop="copyUserIds"> <el-form-item label="抄送人" prop="copyUserIds">
<el-tag <el-tag
:key="index" :key="index"
v-for="(item, index) in userData.copyUser" v-for="(item, index) in copyUser"
closable closable
:disable-transitions="false" :disable-transitions="false"
@close="handleClose(item)"> @close="handleClose('copy', item)">
{{ item.label }} {{ item.nickName }}
</el-tag> </el-tag>
<el-button class="button-new-tag" type="primary" icon="el-icon-plus" size="mini" circle @click="onSelectUsers" /> <el-button class="button-new-tag" type="primary" icon="el-icon-plus" size="mini" circle @click="onSelectCopyUsers" />
</el-form-item>
<el-form-item label="指定审批人" prop="copyUserIds">
<el-tag
:key="index"
v-for="(item, index) in nextUser"
closable
:disable-transitions="false"
@close="handleClose('next', item)">
{{ item.nickName }}
</el-tag>
<el-button class="button-new-tag" type="primary" icon="el-icon-plus" size="mini" circle @click="onSelectNextUsers" />
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-col> </el-col>
@@ -167,7 +178,7 @@
highlight-current-row highlight-current-row
@current-change="changeCurrentUser" @current-change="changeCurrentUser"
@selection-change="handleSelectionChange"> @selection-change="handleSelectionChange">
<el-table-column v-if="userData.type === 'copy'" width="55" type="selection" /> <el-table-column v-if="userData.type === 'copy' || userData.type === 'next'" width="55" type="selection" />
<el-table-column v-else width="30"> <el-table-column v-else width="30">
<template slot-scope="scope"> <template slot-scope="scope">
<el-radio :label="scope.row.userId" v-model="currentUserId">{{''}}</el-radio> <el-radio :label="scope.row.userId" v-model="currentUserId">{{''}}</el-radio>
@@ -292,11 +303,11 @@ export default {
rejectTitle: null, rejectTitle: null,
userData: { userData: {
title: '', title: '',
type: 'copy', type: '',
open: false, open: false,
currentUserId: null,
copyUser: [],
}, },
copyUser: [],
nextUser: [],
userMultipleSelection: [], userMultipleSelection: [],
userDialogTitle: '', userDialogTitle: '',
userOpen: false userOpen: false
@@ -374,17 +385,28 @@ export default {
} }
}, },
// 关闭标签 // 关闭标签
handleClose(tag) { handleClose(type, tag) {
let userObj = this.userMultipleSelection.find(item => item.userId === tag.id); let userObj = this.userMultipleSelection.find(item => item.userId === tag.id);
this.userMultipleSelection.splice(this.userMultipleSelection.indexOf(userObj), 1); this.userMultipleSelection.splice(this.userMultipleSelection.indexOf(userObj), 1);
this.userData.copyUser.splice(this.userData.copyUser.indexOf(tag), 1); if (type === 'copy') {
this.copyUser = this.userMultipleSelection;
// 设置抄送人ID // 设置抄送人ID
if (this.userData.copyUser && this.userData.copyUser.length > 0) { if (this.copyUser && this.copyUser.length > 0) {
const val = this.userData.copyUser.map(item => item.id); const val = this.copyUser.map(item => item.id);
this.taskForm.copyUserIds = val instanceof Array ? val.join(',') : val; this.taskForm.copyUserIds = val instanceof Array ? val.join(',') : val;
} else { } else {
this.taskForm.copyUserIds = ''; this.taskForm.copyUserIds = '';
} }
} else if (type === 'next') {
this.nextUser = this.userMultipleSelection;
// 设置抄送人ID
if (this.nextUser && this.nextUser.length > 0) {
const val = this.nextUser.map(item => item.id);
this.taskForm.nextUserIds = val instanceof Array ? val.join(',') : val;
} else {
this.taskForm.nextUserIds = '';
}
}
}, },
/** 流程变量赋值 */ /** 流程变量赋值 */
handleCheckChange(val) { handleCheckChange(val) {
@@ -413,15 +435,20 @@ export default {
this.formOpen = true this.formOpen = true
}) })
}, },
onSelectUsers() { onSelectCopyUsers() {
this.userData.title = '添加抄送人'; this.userMultipleSelection = this.copyUser;
this.userData.type = 'copy'; this.onSelectUsers('添加抄送人', 'copy')
},
onSelectNextUsers() {
this.userMultipleSelection = this.nextUser;
this.onSelectUsers('指定审批人', 'next')
},
onSelectUsers(title, type) {
this.userData.title = title;
this.userData.type = type;
this.getTreeSelect(); this.getTreeSelect();
this.getList() this.getList()
this.userData.open = true; this.userData.open = true;
this.$nextTick(() => {
this.$refs.userTable.clearSelection();
});
}, },
/** 通过任务 */ /** 通过任务 */
handleComplete() { handleComplete() {
@@ -518,20 +545,20 @@ export default {
}, },
submitUserData() { submitUserData() {
let type = this.userData.type; let type = this.userData.type;
if (type === 'copy') { if (type === 'copy' || type === 'next') {
if (!this.userMultipleSelection || this.userMultipleSelection.length <= 0) { if (!this.userMultipleSelection || this.userMultipleSelection.length <= 0) {
this.$modal.msgError("请选择用户"); this.$modal.msgError("请选择用户");
return false; return false;
} }
this.userData.copyUser = this.userMultipleSelection.map(k => { let userIds = this.userMultipleSelection.map(k => k.userId);
return { id: k.userId, label: k.nickName } if (type === 'copy') {
}) // 设置抄送人ID信息
// 设置抄送人ID this.copyUser = this.userMultipleSelection;
if (this.userData.copyUser && this.userData.copyUser.length > 0) { this.taskForm.copyUserIds = userIds instanceof Array ? userIds.join(',') : userIds;
const val = this.userData.copyUser.map(item => item.id); } else if (type === 'next') {
this.taskForm.copyUserIds = val instanceof Array ? val.join(',') : val; // 设置下一级审批人ID信息
} else { this.nextUser = this.userMultipleSelection;
this.taskForm.copyUserIds = ''; this.taskForm.nextUserIds = userIds instanceof Array ? userIds.join(',') : userIds;
} }
this.userData.open = false; this.userData.open = false;
} else { } else {