feat(流程审批): 新增 审批时可动态指定下一级审批人
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有用户任务节点
|
* 获取所有用户任务节点
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -57,4 +57,8 @@ public class WfTaskBo {
|
|||||||
* 抄送用户Id
|
* 抄送用户Id
|
||||||
*/
|
*/
|
||||||
private String copyUserIds;
|
private String copyUserIds;
|
||||||
|
/**
|
||||||
|
* 下一节点审批人
|
||||||
|
*/
|
||||||
|
private String nextUserIds;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,16 +385,27 @@ 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') {
|
||||||
// 设置抄送人ID
|
this.copyUser = this.userMultipleSelection;
|
||||||
if (this.userData.copyUser && this.userData.copyUser.length > 0) {
|
// 设置抄送人ID
|
||||||
const val = this.userData.copyUser.map(item => item.id);
|
if (this.copyUser && this.copyUser.length > 0) {
|
||||||
this.taskForm.copyUserIds = val instanceof Array ? val.join(',') : val;
|
const val = this.copyUser.map(item => item.id);
|
||||||
} else {
|
this.taskForm.copyUserIds = val instanceof Array ? val.join(',') : val;
|
||||||
this.taskForm.copyUserIds = '';
|
} else {
|
||||||
|
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 = '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/** 流程变量赋值 */
|
/** 流程变量赋值 */
|
||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user