diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/ProcessConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ProcessConstants.java new file mode 100644 index 0000000..1ba6548 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ProcessConstants.java @@ -0,0 +1,27 @@ +package com.ruoyi.common.constant; + +/** + * 流程常量信息 + * @author Xuan xuan + * @date 2021/4/17 22:46 + */ +public class ProcessConstants { + + /** 动态数据 */ + public static final String DATA_TYPE = "dynamic"; + + + /** 单个审批人 */ + public static final String USER_TYPE_ASSIGNEE = "assignee"; + + + /** 候选人 */ + public static final String USER_TYPE_USERS = "candidateUsers"; + + + /** 审批组 */ + public static final String USER_TYPE_ROUPS = "candidateGroups"; + + /** 单个审批人 */ + public static final String PROCESS_APPROVAL = "approval"; +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/FlowNextDto.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/FlowNextDto.java new file mode 100644 index 0000000..fc041b3 --- /dev/null +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/FlowNextDto.java @@ -0,0 +1,22 @@ +package com.ruoyi.flowable.domain.dto; + +import com.ruoyi.common.core.domain.entity.SysUser; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 动态人员、组 + * @author Xuan xuan + * @date 2021/4/17 22:59 + */ +@Data +public class FlowNextDto implements Serializable { + + private String type; + + private String vars; + + private List userList; +} diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/FlowTaskDto.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/FlowTaskDto.java index cceae9d..86ec8ce 100644 --- a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/FlowTaskDto.java +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/domain/dto/FlowTaskDto.java @@ -1,6 +1,7 @@ package com.ruoyi.flowable.domain.dto; import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.core.domain.entity.SysUser; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; diff --git a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/impl/FlowDefinitionServiceImpl.java b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/impl/FlowDefinitionServiceImpl.java index 2bce8b9..e3c89e7 100644 --- a/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/impl/FlowDefinitionServiceImpl.java +++ b/ruoyi-flowable/src/main/java/com/ruoyi/flowable/service/impl/FlowDefinitionServiceImpl.java @@ -151,8 +151,11 @@ public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFl @Override public AjaxResult startProcessInstanceById(String procDefId, Map variables) { try { + // 设置流程发起人Id到流程中 Long userId = SecurityUtils.getLoginUser().getUser().getUserId(); identityService.setAuthenticatedUserId(userId.toString()); + variables.put("initiator",userId); + variables.put("_FLOWABLE_SKIP_EXPRESSION_ENABLED", true); runtimeService.startProcessInstanceById(procDefId, variables); return AjaxResult.success("流程启动成功"); } catch (Exception e) { 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 234e867..c38318a 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 @@ -5,12 +5,14 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.google.common.collect.Lists; +import com.ruoyi.common.constant.ProcessConstants; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.domain.entity.SysRole; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.exception.CustomException; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.flowable.domain.dto.FlowCommentDto; +import com.ruoyi.flowable.domain.dto.FlowNextDto; import com.ruoyi.flowable.domain.dto.FlowTaskDto; import com.ruoyi.flowable.domain.vo.FlowTaskVo; import com.ruoyi.flowable.factory.FlowServiceFactory; @@ -23,6 +25,7 @@ import com.ruoyi.system.service.ISysRoleService; import com.ruoyi.system.service.ISysUserService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.*; @@ -86,34 +89,23 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask taskService.addComment(task.getTaskId(), task.getInstanceId(), "1", task.getComment()); } Long userId = SecurityUtils.getLoginUser().getUser().getUserId(); - // 变量信息 - if (task.getValues() != null && task.getValues().size() > 0) { - Map map = (Map) task.getValues().get("mainTable"); - map.put("FormOrigData" + task.getTaskId(), JSON.toJSONString(task.getValues())); -// List list = getTaskFormList(task.getTaskId()); -// // 节点上挂载的表单ID -// if (!CollectionUtils.isEmpty(list)) { -// List formList = new ArrayList<>(); -// for (TaskDto taskDto : list) { -// formList.addAll(taskDto.getFormList()); -// } + // 读取变量 动态设置下一环节审批人 +// Map taskValues = task.getValues(); +// if (taskValues != null && taskValues.size() > 0) { +// if(StringUtils.isNotBlank((taskValues.get("assignee").toString()))){ +// taskService.ad +// } +// if(CollectionUtils.isNotEmpty((List) taskValues.get("candidateUsers"))){ // -// List strs = new ArrayList<>(); -// for (int k = 0; k < formList.size(); k++) { -// Map map1 = (Map) formList.get(k); -// strs.add(map1.get("id").toString()); -// } // } - taskService.setVariables(task.getTaskId(), map); - } +// if(CollectionUtils.isNotEmpty((List) taskValues.get("candidateGroups"))){ +// +// } +// } // 设置任务审批人员 taskService.setAssignee(task.getTaskId(), userId.toString()); - // todo : 这种方式无法动态设置任务接收人 -// if (StringUtils.isNotBlank(task.getAssignee())) { -// taskService.addCandidateUser(task.getTaskId(), task.getAssignee()); -// } // 提交任务 - taskService.complete(task.getTaskId()); + taskService.complete(task.getTaskId(),task.getValues()); } @@ -617,6 +609,7 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask .processInstanceId(task.getProcessInstanceId()) .singleResult(); SysUser startUser = sysUserService.selectUserById(Long.parseLong(historicProcessInstance.getStartUserId())); +// SysUser startUser = sysUserService.selectUserById(Long.parseLong(task.getAssignee())); flowTask.setStartUserId(startUser.getNickName()); flowTask.setStartUserName(startUser.getNickName()); flowTask.setStartDeptName(startUser.getDept().getDeptName()); @@ -757,6 +750,7 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask map.put("finished", false); } } + // 第一次申请获取初始化表单 if (StringUtils.isNotBlank(deployId)) { SysForm sysForm = sysInstanceFormService.selectSysDeployFormByDeployId(deployId); map.put("formData", JSONObject.parseObject(sysForm.getFormContent())); @@ -852,7 +846,7 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask @Override public AjaxResult getNextFlowNode(FlowTaskVo flowTaskVo) { Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); - List nextNodeList = new ArrayList<>(); + FlowNextDto flowNextDto = new FlowNextDto(); if (Objects.nonNull(task)) { ExecutionEntity ee = (ExecutionEntity) runtimeService.createExecutionQuery() .executionId(task.getExecutionId()).singleResult(); @@ -862,26 +856,34 @@ public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTask FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(crruentActivityId); // 输出连线 List outFlows = flowNode.getOutgoingFlows(); - for (SequenceFlow sequenceFlow : outFlows) { // 下一个审userTask FlowElement targetFlow = sequenceFlow.getTargetFlowElement(); if (targetFlow instanceof UserTask) { - FlowTaskDto flowTaskDto = new FlowTaskDto(); + // 当流程设计时未指定任务接受人员/组时 判定为用户动态选择下一任务审批人 // todo 1. 读取自定义节点属性来验证是否动态选择审批人 // 2. 验证表达式 - if (StringUtils.isBlank(((UserTask) targetFlow).getAssignee()) && - CollectionUtils.isEmpty(((UserTask) targetFlow).getCandidateGroups()) && - CollectionUtils.isEmpty(((UserTask) targetFlow).getCandidateUsers())) { - flowTaskDto.setTaskDefKey(targetFlow.getId()); - flowTaskDto.setTaskName(targetFlow.getName()); - nextNodeList.add(flowTaskDto); + String dataType = targetFlow.getAttributeValue("http://flowable.org/bpmn", "dataType"); + String userType = targetFlow.getAttributeValue("http://flowable.org/bpmn", "userType"); +// Map> attributes = targetFlow.getAttributes(); +// List extensionAttributes = attributes.get("dataType"); +// for (ExtensionAttribute attribute : extensionAttributes) { +// String value = attribute.getValue(); +// } + if (ProcessConstants.DATA_TYPE.equals(dataType)){ + if (ProcessConstants.USER_TYPE_ASSIGNEE.equals(userType)){ + List list = sysUserService.selectUserList(new SysUser()); + + flowNextDto.setVars(ProcessConstants.PROCESS_APPROVAL); + flowNextDto.setType(ProcessConstants.USER_TYPE_ASSIGNEE); + flowNextDto.setUserList(list); + } } } } } - return AjaxResult.success(nextNodeList); + return AjaxResult.success(flowNextDto); } /** diff --git a/ruoyi-ui/src/components/Process/components/nodePanel/task.vue b/ruoyi-ui/src/components/Process/components/nodePanel/task.vue index dcf6dc5..3c37b40 100644 --- a/ruoyi-ui/src/components/Process/components/nodePanel/task.vue +++ b/ruoyi-ui/src/components/Process/components/nodePanel/task.vue @@ -68,6 +68,10 @@ export default { { label: '候选人员', value: 'candidateUsers' }, { label: '候选组', value: 'candidateGroups' } ], + dataTypeOption: [ + { label: '固定', value: 'fixed' }, + { label: '动态', value: 'dynamic' } + ], dialogName: '', executionListenerLength: 0, taskListenerLength: 0, @@ -116,6 +120,31 @@ export default { dic: _this.userTypeOption, show: !!_this.showConfig.userType }, + { + xType: 'radio', + name: 'dataType', + label: '指定方式', + dic: _this.dataTypeOption, + show: !!_this.showConfig.dataType + }, + // { + // xType: 'input', + // name: 'assigneeFixed', + // label: '指定人(表达式)', + // show: !!_this.showConfig.assigneeFixed && _this.formData.userType === 'assignee' && _this.formData.dataType === 'fixed' + // }, + // { + // xType: 'input', + // name: 'candidateUsersFixed', + // label: '候选人(表达式)', + // show: !!_this.showConfig.candidateUsersFixed && _this.formData.userType === 'candidateUsers' && _this.formData.dataType === 'fixed' + // }, + // { + // xType: 'input', + // name: 'candidateGroupsFixed', + // label: '候选组(表达式)', + // show: !!_this.showConfig.candidateGroupsFixed && _this.formData.userType === 'candidateGroups' && _this.formData.dataType === 'fixed' + // }, { xType: 'select', name: 'assignee', @@ -253,26 +282,35 @@ export default { }) } }, - 'formData.assignee': function(val) { - if (this.formData.userType !== 'assignee') { - delete this.element.businessObject.$attrs[`flowable:assignee`] - return + // 动态选择流程执行人 + 'formData.dataType': function(val) { + const that = this + this.updateProperties({'flowable:dataType': val}) + if (val === 'dynamic') { + debugger + this.updateProperties({'flowable:userType': that.formData.userType}) } - this.updateProperties({ 'flowable:assignee': val }) + }, + 'formData.assignee': function(val) { + if (this.formData.userType !== 'assignee') { + delete this.element.businessObject.$attrs[`flowable:assignee`] + return + } + this.updateProperties({'flowable:assignee': val}) }, 'formData.candidateUsers': function(val) { - if (this.formData.userType !== 'candidateUsers') { - delete this.element.businessObject.$attrs[`flowable:candidateUsers`] - return - } - this.updateProperties({ 'flowable:candidateUsers': val?.join(',') }) + if (this.formData.userType !== 'candidateUsers') { + delete this.element.businessObject.$attrs[`flowable:candidateUsers`] + return + } + this.updateProperties({'flowable:candidateUsers': val?.join(',')}) }, 'formData.candidateGroups': function(val) { - if (this.formData.userType !== 'candidateGroups') { - delete this.element.businessObject.$attrs[`flowable:candidateGroups`] - return - } - this.updateProperties({ 'flowable:candidateGroups': val?.join(',') }) + if (this.formData.userType !== 'candidateGroups') { + delete this.element.businessObject.$attrs[`flowable:candidateGroups`] + return + } + this.updateProperties({'flowable:candidateGroups': val?.join(',')}) }, 'formData.async': function(val) { if (val === '') val = null diff --git a/ruoyi-ui/src/components/Process/flowable/showConfig.js b/ruoyi-ui/src/components/Process/flowable/showConfig.js index 45a752b..69ba83e 100644 --- a/ruoyi-ui/src/components/Process/flowable/showConfig.js +++ b/ruoyi-ui/src/components/Process/flowable/showConfig.js @@ -6,9 +6,13 @@ export default { }, 'bpmn:UserTask': { userType: true, + dataType: true, assignee: true, candidateUsers: true, candidateGroups: true, + // assigneeFixed: true, + // candidateUsersFixed: true, + // candidateGroupsFixed: true, async: true, priority: true, formKey: true, diff --git a/ruoyi-ui/src/components/Process/index.vue b/ruoyi-ui/src/components/Process/index.vue index 13e8f91..8008a35 100644 --- a/ruoyi-ui/src/components/Process/index.vue +++ b/ruoyi-ui/src/components/Process/index.vue @@ -29,6 +29,7 @@
+ 查看xml 下载xml 下载svg 保存模型 @@ -44,7 +45,9 @@ - + +
+
@@ -67,6 +70,9 @@ export default { type: String, default: '' }, + xmlTitle: "", + xmlOpen: false, + xmlContent: "", users: { type: Array, default: () => [] @@ -287,6 +293,17 @@ export default { console.log(err) } }, + async showXML() { + try { + const { xml } = await this.modeler.saveXML({ format: true }) + this.xmlOpen = true; + this.xmlTitle = 'xml查看'; + debugger + // this.xmlContent = this.parseXml(xml); + } catch (err) { + console.log(err) + } + }, async saveImg(type = 'svg', download = false) { try { const { svg } = await this.modeler.saveSVG({ format: true }) @@ -321,6 +338,50 @@ export default { a.download = filename a.click() window.URL.revokeObjectURL(url) + }, + parseXml(content) { + let xml_doc = null + try { + xml_doc = (new DOMParser()).parseFromString(content.replace(/[\n\r\s]/g, ''), 'text/xml') + } catch (e) { + return false + } + let flag = 0 + + function build_xml(index, list, element) { + let t = [] + for (let i = 0; i < flag; i++) { + t.push('    ') + } + t = t.join('') + list.push(t + '<' + element.nodeName + '>
') + for (let i = 0; i < element.childNodes.length; i++) { + const nodeName = element.childNodes[i].nodeName + if (element.childNodes[i].childNodes.length === 0) { + const value_txt = '' + const item = t + '    <' + nodeName + + '>' + value_txt + '</' + nodeName + '>
' + list.push(item) + } else if ((element.childNodes[i].childNodes.length === 1 && element.childNodes[i].childNodes[0].nodeValue != null)) { + const value = element.childNodes[i].childNodes[0].nodeValue + const value_color = !isNaN(Number(value)) ? 'code-number' : 'code-string' + const value_txt = '' + value + '' + const item = t + '    <' + nodeName + + '>' + value_txt + '</' + nodeName + '>
' + list.push(item) + } else { + flag++ + build_xml(++index, list, element.childNodes[i]) + flag-- + } + } + list.push(t + '</' + element.nodeName + '>
') + } + + const list = [] + build_xml(0, list, xml_doc.documentElement) + + return list.join('') } } } @@ -374,6 +435,12 @@ export default { min-height: 650px; } + .code-string{color:#993300;} + .code-number{color:#cc00cc;} + .code-boolean{color:#000033;} + .code-null{color:magenta;} + .code-key{color:#003377;font-weight:bold;} + // .highlight.djs-shape .djs-visual > :nth-child(1) { // fill: green !important; // stroke: green !important; diff --git a/ruoyi-ui/src/views/flowable/definition/index.vue b/ruoyi-ui/src/views/flowable/definition/index.vue index 1822966..a19c7bd 100644 --- a/ruoyi-ui/src/views/flowable/definition/index.vue +++ b/ruoyi-ui/src/views/flowable/definition/index.vue @@ -404,8 +404,6 @@ export default { }, /** 挂载表单 */ submitFormDeploy(){ - debugger - console.log(this.formDeployParam) addDeployForm(this.formDeployParam).then(res =>{ this.msgSuccess(res.msg); this.formDeployOpen = false; diff --git a/ruoyi-ui/src/views/flowable/definition/model.vue b/ruoyi-ui/src/views/flowable/definition/model.vue index 9f33e8f..4bb3939 100644 --- a/ruoyi-ui/src/views/flowable/definition/model.vue +++ b/ruoyi-ui/src/views/flowable/definition/model.vue @@ -14,7 +14,6 @@