From 682592fcdf26e68316806a506d898b1c9e6c5abe Mon Sep 17 00:00:00 2001 From: tony <846249920@qq.com> Date: Sun, 18 Dec 2022 21:55:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BB=BB=E5=8A=A1=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E5=8D=95=E7=8B=AC=E8=AE=BE=E7=BD=AE=E8=A1=A8=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/FlowTaskController.java | 10 + .../flowable/service/IFlowTaskService.java | 7 + .../service/impl/FlowTaskServiceImpl.java | 85 ++++- ruoyi-ui/src/api/flowable/todo.js | 8 + .../nodePanel/property/multiInstance.vue | 8 +- .../views/flowable/task/todo/detail/index.vue | 294 +++++++++++------- 6 files changed, 286 insertions(+), 126 deletions(-) diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/FlowTaskController.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/FlowTaskController.java index 57bc42ed..6f181498 100644 --- a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/FlowTaskController.java +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/controller/FlowTaskController.java @@ -214,4 +214,14 @@ public class FlowTaskController { return flowTaskService.flowXmlAndNode(procInsId,deployId); } + /** + * 流程节点表单 + * @param taskId 流程任务编号 + * @return + */ + @GetMapping("/flowTaskForm") + public AjaxResult flowTaskForm(@RequestParam(value = "taskId",required = false) String taskId) throws Exception { + return flowTaskService.flowTaskForm(taskId); + } + } diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/IFlowTaskService.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/IFlowTaskService.java index e545056c..806d38c8 100644 --- a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/IFlowTaskService.java +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/IFlowTaskService.java @@ -180,4 +180,11 @@ public interface IFlowTaskService { * @return */ AjaxResult flowXmlAndNode(String procInsId,String deployId); + + /** + * 流程节点表单 + * @param taskId 流程任务编号 + * @return + */ + AjaxResult flowTaskForm(String taskId) throws Exception; } diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/impl/FlowTaskServiceImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/impl/FlowTaskServiceImpl.java index d5eedda1..7b338d8b 100644 --- a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/impl/FlowTaskServiceImpl.java +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/impl/FlowTaskServiceImpl.java @@ -1,7 +1,10 @@ package com.ruoyi.flowable.service.impl; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.core.domain.model.LoginUser; import com.ruoyi.flowable.common.constant.ProcessConstants; @@ -22,6 +25,7 @@ import com.ruoyi.flowable.flow.FindNextNodeUtil; import com.ruoyi.flowable.flow.FlowableUtils; import com.ruoyi.flowable.service.IFlowTaskService; import com.ruoyi.flowable.service.ISysDeployFormService; +import com.ruoyi.flowable.service.ISysFormService; import com.ruoyi.system.domain.SysForm; import com.ruoyi.system.service.ISysRoleService; import com.ruoyi.system.service.ISysUserService; @@ -54,6 +58,7 @@ import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.io.InputStream; +import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -70,14 +75,12 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask @Resource private ISysUserService sysUserService; - - @Resource private ISysRoleService sysRoleService; - - @Resource private ISysDeployFormService sysInstanceFormService; + @Resource + private ISysFormService sysFormService; /** * 完成任务 @@ -1075,14 +1078,84 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName()); String xmlData = IOUtils.toString(inputStream, StandardCharsets.UTF_8); - result.put("nodeData",flowViewerList); - result.put("xmlData",xmlData); + result.put("nodeData", flowViewerList); + result.put("xmlData", xmlData); return AjaxResult.success(result); } catch (Exception e) { return AjaxResult.error("高亮历史任务失败"); } } + /** + * 流程节点表单 + * + * @param taskId 流程任务编号 + * @return + */ + @Override + public AjaxResult flowTaskForm(String taskId) throws Exception { + JSONObject result = new JSONObject(); + result.put("formKeyExist",false); + Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId()); + FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); + // 流程变量 + Map parameters = new HashMap<>(); + HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().includeProcessVariables().finished().taskId(taskId).singleResult(); + if (Objects.nonNull(historicTaskInstance)) { + parameters = historicTaskInstance.getProcessVariables(); + } else { + parameters = taskService.getVariables(taskId); + } + // TODO 暂时只处理用户任务上的表单 + if (flowElement instanceof UserTask) { + String formKey = ((UserTask) flowElement).getFormKey(); + if (StringUtils.isNotBlank(formKey)) { + SysForm sysForm = sysFormService.selectSysFormById(Long.parseLong(formKey)); + + JSONObject oldVariables = JSONObject.parseObject(JSON.toJSONString(parameters.get("variables"))); + List oldFields = JSON.parseObject(JSON.toJSONString(oldVariables.get("fields")), new TypeReference>() { + }); + oldFields.forEach(obj -> obj.put("disabled", true)); + + JSONObject data = JSONObject.parseObject(sysForm.getFormContent()); + List newFields = JSON.parseObject(JSON.toJSONString(data.get("fields")), new TypeReference>() { + }); + + oldFields.addAll(newFields); + oldVariables.put("fields", oldFields); + oldVariables.put("disabled", false); + oldVariables.put("formBtns", true); + result.put("formData",oldVariables); + result.put("formKeyExist",true); + return AjaxResult.success("", result); + } else { + result.put("formData",parameters.get("variables")); + return AjaxResult.success("", result); + } + } else { + result.put("formData",parameters.get("variables")); + return AjaxResult.success("", result); + } + } + + /** + * 将Object类型的数据转化成Map + * + * @param obj + * @return + * @throws Exception + */ + public Map obj2Map(Object obj) throws Exception { + Map map = new HashMap(); + Field[] fields = obj.getClass().getDeclaredFields(); + for (Field field : fields) { + field.setAccessible(true); + map.put(field.getName(), field.get(obj)); + } + return map; + } + /** * 流程完成时间处理 * diff --git a/ruoyi-ui/src/api/flowable/todo.js b/ruoyi-ui/src/api/flowable/todo.js index 6ea6c2cd..276977b9 100644 --- a/ruoyi-ui/src/api/flowable/todo.js +++ b/ruoyi-ui/src/api/flowable/todo.js @@ -123,3 +123,11 @@ export function exportDeployment(query) { params: query }) } +// 流程节点表单 +export function flowTaskForm(query) { + return request({ + url: '/flowable/task/flowTaskForm', + method: 'get', + params: query + }) +} diff --git a/ruoyi-ui/src/components/Process/components/nodePanel/property/multiInstance.vue b/ruoyi-ui/src/components/Process/components/nodePanel/property/multiInstance.vue index 0da6edb1..5912ea7f 100644 --- a/ruoyi-ui/src/components/Process/components/nodePanel/property/multiInstance.vue +++ b/ruoyi-ui/src/components/Process/components/nodePanel/property/multiInstance.vue @@ -46,14 +46,14 @@ export default { name: 'collection', label: '集合', tooltip: 'collection: 属性会作为表达式进行解析。如果表达式解析为字符串而不是一个集合,
不论是因为本身配置的就是静态字符串值,还是表达式计算结果为字符串,
这个字符串都会被当做变量名,并从流程变量中用于获取实际的集合。', - rules: [{ required: true, message: '请输入集合名称', trigger: ['blur', 'change'] }] + // rules: [{ required: true, message: '请输入集合名称', trigger: ['blur', 'change'] }] }, { xType: 'input', name: 'elementVariable', label: '元素变量', tooltip: 'elementVariable: 每创建一个用户任务前,先以该元素变量为label,集合中的一项为value,
创建(局部)流程变量,该局部流程变量被用于指派用户任务。
一般来说,该字符串应与指定人员变量相同。', - rules: [{ required: true, message: '请输入元素变量', trigger: ['blur', 'change'] }] + // rules: [{ required: true, message: '请输入元素变量', trigger: ['blur', 'change'] }] }, { xType: 'select', @@ -61,14 +61,14 @@ export default { label: '执行方式', tooltip: '并行会签(parallel)、串行会签(sequential),其中并行会签的意思是 多个人同时执行任务。串行会签是按顺序执行任务。', dic: [{label: '串行', value: true}, {label: '并行', value: false}], - rules: [{ required: true, message: '请选择执行方式', trigger: ['blur', 'change'] }] + // rules: [{ required: true, message: '请选择执行方式', trigger: ['blur', 'change'] }] }, { xType: 'input', name: 'completionCondition', label: '完成条件', tooltip: 'completionCondition: 多实例活动在所有实例都完成时结束,然而也可以指定一个表达式,在每个实例
结束时进行计算。当表达式计算为true时,将销毁所有剩余的实例,并结束多实例
活动,继续执行流程。例如 ${nrOfCompletedInstances/nrOfInstances >= 0.6 },
表示当任务完成60%时,该节点就算完成', - rules: [{ required: true, message: '请输入完成条件', trigger: ['blur', 'change'] }] + // rules: [{ required: true, message: '请输入完成条件', trigger: ['blur', 'change'] }] } ], operate: [ diff --git a/ruoyi-ui/src/views/flowable/task/todo/detail/index.vue b/ruoyi-ui/src/views/flowable/task/todo/detail/index.vue index b79d7130..ba31c373 100644 --- a/ruoyi-ui/src/views/flowable/task/todo/detail/index.vue +++ b/ruoyi-ui/src/views/flowable/task/todo/detail/index.vue @@ -1,33 +1,37 @@ @@ -134,8 +142,16 @@ import {flowRecord} from "@/api/flowable/finished"; import FlowUser from '@/components/flow/User' import FlowRole from '@/components/flow/Role' import Parser from '@/components/parser/Parser' -import {getProcessVariables, flowXmlAndNode} from "@/api/flowable/definition"; -import {complete, rejectTask, returnList, returnTask, getNextFlowNode, delegate} from "@/api/flowable/todo"; +import {getProcessVariables, flowXmlAndNode, definitionStart} from "@/api/flowable/definition"; +import { + complete, + rejectTask, + returnList, + returnTask, + getNextFlowNode, + delegate, + flowTaskForm, +} from "@/api/flowable/todo"; import flow from '@/views/flowable/task/todo/detail/flow' import "@riophae/vue-treeselect/dist/vue-treeselect.css"; import {listUser} from "@/api/system/user"; @@ -175,18 +191,20 @@ export default { src: null, rules: {}, // 表单校验 variablesForm: {}, // 流程变量数据 - taskForm:{ + taskForm: { returnTaskShow: false, // 是否展示回退表单 delegateTaskShow: false, // 是否展示回退表单 defaultTaskShow: true, // 默认处理 - comment:"", // 意见内容 + comment: "", // 意见内容 procInsId: "", // 流程实例编号 instanceId: "", // 流程实例编号 deployId: "", // 流程定义编号 - taskId: "" ,// 流程任务编号 + taskId: "",// 流程任务编号 procDefId: "", // 流程编号 - targetKey:"", - variables:{}, + targetKey: "", + variables: { + variables: {} + }, }, assignee: null, formConf: {}, // 默认表单数据 @@ -199,13 +217,14 @@ export default { returnOpen: false, rejectOpen: false, rejectTitle: null, - userData:[], + userData: [], checkSendUser: false, // 是否展示人员选择模块 checkSendRole: false,// 是否展示角色选择模块 checkType: 'single', // 选择类型 taskName: null, // 任务节点 startUser: null, // 发起人信息, - multiInstanceVars: '' // 会签节点 + multiInstanceVars: '', // 会签节点 + formKeyExist: false, // 当前节点是否存在表单 }; }, created() { @@ -217,17 +236,18 @@ export default { this.taskForm.procInsId = this.$route.query.procInsId; this.taskForm.executionId = this.$route.query.executionId; this.taskForm.instanceId = this.$route.query.procInsId; - // 流程任务重获取变量表单 + // 流程任务获取变量信息 if (this.taskForm.taskId) { this.processVariables(this.taskForm.taskId) - this.getNextFlowNode(this.taskForm.taskId) + this.getFlowTaskForm(this.taskForm.taskId) + // this.getNextFlowNode(this.taskForm.taskId) } this.getFlowRecordList(this.taskForm.procInsId, this.taskForm.deployId); } }, methods: { handleClick(tab, event) { - if (tab.name === '3'){ + if (tab.name === '3') { flowXmlAndNode({procInsId: this.taskForm.procInsId, deployId: this.taskForm.deployId}).then(res => { this.flowData = res.data; }) @@ -258,7 +278,7 @@ export default { this.$set(this.taskForm.variables, "approval", selectVal.join(',')); } } else { - this.$set(this.taskForm.variables, "approval", selection); + this.$set(this.taskForm.variables, "approval", selection); } } }, @@ -296,7 +316,18 @@ export default { if (taskId) { // 提交流程申请时填写的表单存入了流程变量中后续任务处理时需要展示 getProcessVariables(taskId).then(res => { - this.variablesData = res.data.variables; + // this.variablesData = res.data.variables; + }); + } + }, + /** 流程节点表单 */ + getFlowTaskForm(taskId) { + if (taskId) { + // 提交流程申请时填写的表单存入了流程变量中后续任务处理时需要展示 + flowTaskForm({taskId: taskId}).then(res => { + this.variablesData = res.data.formData; + this.taskForm.variables = res.data.formData; + this.formKeyExist = res.data.formKeyExist; }); } }, @@ -326,35 +357,51 @@ export default { }, /** 加载审批任务弹框 */ handleComplete() { - this.completeOpen = true; - this.completeTitle = "流程审批"; + // this.completeOpen = true; + // this.completeTitle = "流程审批"; + this.submitForm(null); }, /** 用户审批任务 */ taskComplete() { - if (!this.taskForm.variables && this.checkSendUser){ + if (!this.taskForm.variables && this.checkSendUser) { this.$modal.msgError("请选择流程接收人员!"); return; } - if (!this.taskForm.variables && this.checkSendRole){ + if (!this.taskForm.variables && this.checkSendRole) { this.$modal.msgError("请选择流程接收角色组!"); return; } - if (!this.taskForm.comment){ + if (!this.taskForm.comment) { this.$modal.msgError("请输入审批意见!"); return; } - console.log(this.taskForm,"流程审批提交表单数据") - complete(this.taskForm).then(response => { - this.$modal.msgSuccess(response.msg); - this.goBack(); - }); + if (this.taskForm && this.formKeyExist) { + // 表单是否禁用 + this.taskForm.formData.formData.disabled = true; + // 是否显示按钮 + this.taskForm.formData.formData.formBtns = false; + this.taskForm.variables = Object.assign({}, this.taskForm.variables, this.taskForm.formData.valData); + this.taskForm.variables.variables = this.taskForm.formData.formData; + + console.log(this.taskForm, "流程审批提交表单数据") + complete(this.taskForm).then(response => { + this.$modal.msgSuccess(response.msg); + this.goBack(); + }); + } else { + console.log(this.taskForm, "流程审批提交表单数据") + complete(this.taskForm).then(response => { + this.$modal.msgSuccess(response.msg); + this.goBack(); + }); + } }, /** 委派任务 */ handleDelegate() { this.taskForm.delegateTaskShow = true; this.taskForm.defaultTaskShow = false; }, - handleAssign(){ + handleAssign() { }, /** 返回页面 */ @@ -363,28 +410,6 @@ export default { this.$store.dispatch("tagsView/delView", this.$route); this.$router.go(-1) }, - /** 接收子组件传的值 */ - getData(data) { - if (data) { - const variables = []; - data.fields.forEach(item => { - let variableData = {}; - variableData.label = item.__config__.label - // 表单值为多个选项时 - if (item.__config__.defaultValue instanceof Array) { - const array = []; - item.__config__.defaultValue.forEach(val => { - array.push(val) - }) - variableData.val = array; - } else { - variableData.val = item.__config__.defaultValue - } - variables.push(variableData) - }) - this.variables = variables; - } - }, /** 驳回任务 */ handleReject() { this.rejectOpen = true; @@ -411,7 +436,7 @@ export default { }) }, /** 提交退回任务 */ - taskReturn() { + taskReturn() { this.$refs["taskForm"].validate(valid => { if (valid) { returnTask(this.taskForm).then(res => { @@ -444,7 +469,43 @@ export default { this.taskForm.defaultTaskShow = true; this.returnTaskList = []; }, - } + /** 申请流程表单数据提交 */ + submitForm(formData) { + // 根据当前任务或者流程设计配置的下一步节点 todo 暂时未涉及到考虑网关、表达式和多节点情况 + const params = {taskId: this.taskForm.taskId} + getNextFlowNode(params).then(res => { + const data = res.data; + if (data) { + if (data.type === 'assignee') { // 指定人员 + this.checkSendUser = true; + this.checkType = "single"; + } else if (data.type === 'candidateUsers') { // 候选人员(多个) + this.checkSendUser = true; + this.checkType = "multiple"; + } else if (data.type === 'candidateGroups') { // 指定组(所属角色接收任务) + this.checkSendRole = true; + } else if (data.type === 'multiInstance') { // 会签? + // 流程设计指定的 elementVariable 作为会签人员列表 + this.multiInstanceVars = data.vars; + this.checkSendUser = true; + this.checkType = "multiple"; + } + if (this.checkSendUser || this.checkSendRole) { + this.completeOpen = true; + this.completeTitle = "流程审批"; + this.taskForm.formData = formData; + } + } else { + // 最后一个任务节点 + console.log(this.taskForm, "流程审批提交表单数据") + complete(this.taskForm).then(response => { + this.$modal.msgSuccess(response.msg); + this.goBack(); + }); + } + }) + }, + }, };