Browse Source

车辆外采流程办理

master
yxt_djz 3 years ago
parent
commit
9541991f4b
  1. 25
      anrui-base/anrui-base-api/src/main/java/com/yxt/anrui/base/api/baseoutsourcingapplication/BaseOutsourcingApplicationFeign.java
  2. 25
      anrui-base/anrui-base-api/src/main/java/com/yxt/anrui/base/api/baseoutsourcingapplication/BaseOutsourcingApplicationFeignFallback.java
  3. 7
      anrui-base/anrui-base-api/src/main/java/com/yxt/anrui/base/api/basevehicleactualsales/BaseVehicleActualSalesFeign.java
  4. 4
      anrui-base/anrui-base-api/src/main/java/com/yxt/anrui/base/api/basevehicleactualsales/BaseVehicleActualSalesFeignFallback.java
  5. 27
      anrui-base/anrui-base-biz/src/main/java/com/yxt/anrui/base/biz/baseoutsourcingapplication/BaseOutsourcingApplicationRest.java
  6. 30
      anrui-base/anrui-base-biz/src/main/java/com/yxt/anrui/base/biz/baseoutsourcingapplication/BaseOutsourcingApplicationService.java
  7. 12
      anrui-flowable/anrui-flowable-api/src/main/java/com/yxt/anrui/flowable/api/flowtask/FlowTaskFeign.java
  8. 15
      anrui-flowable/anrui-flowable-api/src/main/java/com/yxt/anrui/flowable/api/flowtask/FlowTaskFeignFallBack.java
  9. 4
      anrui-flowable/anrui-flowable-api/src/main/java/com/yxt/anrui/flowable/api/utils/ProcDefEnum.java
  10. 11
      anrui-flowable/anrui-flowable-biz/src/main/java/com/yxt/anrui/flowable/biz/flowdefinition/FlowDefinitionService.java
  11. 59
      anrui-flowable/anrui-flowable-biz/src/main/java/com/yxt/anrui/flowable/biz/flowtask/FlowTaskController.java
  12. 6
      anrui-flowable/anrui-flowable-biz/src/main/java/com/yxt/anrui/flowable/biz/flowtask/FlowTaskMapper.java
  13. 24
      anrui-flowable/anrui-flowable-biz/src/main/java/com/yxt/anrui/flowable/biz/flowtask/FlowTaskMapper.xml
  14. 56
      anrui-flowable/anrui-flowable-biz/src/main/java/com/yxt/anrui/flowable/biz/flowtask/FlowTaskService.java
  15. 4
      anrui-flowable/anrui-flowable-biz/src/main/java/com/yxt/anrui/flowable/biz/process/ProcessService.java
  16. 3
      anrui-portal/anrui-portal-api/src/main/java/com/yxt/anrui/portal/api/sysuser/SysUserFeign.java
  17. 5
      anrui-portal/anrui-portal-api/src/main/java/com/yxt/anrui/portal/api/sysuser/SysUserFeignFallback.java
  18. 3
      anrui-portal/anrui-portal-biz/src/main/java/com/yxt/anrui/portal/biz/sysuser/SysUserMapper.java
  19. 10
      anrui-portal/anrui-portal-biz/src/main/java/com/yxt/anrui/portal/biz/sysuser/SysUserMapper.xml
  20. 5
      anrui-portal/anrui-portal-biz/src/main/java/com/yxt/anrui/portal/biz/sysuser/SysUserRest.java
  21. 6
      anrui-portal/anrui-portal-biz/src/main/java/com/yxt/anrui/portal/biz/sysuser/SysUserService.java
  22. 4
      anrui-scm/anrui-scm-ui/package.json
  23. 47
      anrui-scm/anrui-scm-ui/src/api/baseoutsourcingapplication/baseoutsourcingapplication.js
  24. 68
      anrui-scm/anrui-scm-ui/src/views/Process/BpmData.js
  25. 169
      anrui-scm/anrui-scm-ui/src/views/Process/PropertyPanel.vue
  26. 20
      anrui-scm/anrui-scm-ui/src/views/Process/common/customTranslate.js
  27. 24
      anrui-scm/anrui-scm-ui/src/views/Process/common/mixinExecutionListener.js
  28. 70
      anrui-scm/anrui-scm-ui/src/views/Process/common/mixinPanel.js
  29. 22
      anrui-scm/anrui-scm-ui/src/views/Process/common/mixinXcrud.js
  30. 53
      anrui-scm/anrui-scm-ui/src/views/Process/common/parseElement.js
  31. 24
      anrui-scm/anrui-scm-ui/src/views/Process/components/custom/customContextPad.vue
  32. 81
      anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/gateway.vue
  33. 113
      anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/process.vue
  34. 194
      anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/property/executionListener.vue
  35. 96
      anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/property/listenerParam.vue
  36. 117
      anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/property/multiInstance.vue
  37. 124
      anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/property/signal.vue
  38. 196
      anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/property/taskListener.vue
  39. 92
      anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/sequenceFlow.vue
  40. 94
      anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/startEnd.vue
  41. 426
      anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/task.vue
  42. 1194
      anrui-scm/anrui-scm-ui/src/views/Process/flowable/flowable.json
  43. 24
      anrui-scm/anrui-scm-ui/src/views/Process/flowable/init.js
  44. 55
      anrui-scm/anrui-scm-ui/src/views/Process/flowable/showConfig.js
  45. 5
      anrui-scm/anrui-scm-ui/src/views/Process/index.js
  46. 467
      anrui-scm/anrui-scm-ui/src/views/Process/index.vue
  47. 227
      anrui-scm/anrui-scm-ui/src/views/Process/lang/zh.js
  48. 97
      anrui-scm/anrui-scm-ui/src/views/baseoutsourcingapplication/baseoutsourcingapplicationAdd.vue
  49. 218
      anrui-scm/anrui-scm-ui/src/views/baseoutsourcingapplication/workflow/baseoutsourcingapplication.vue
  50. 32
      anrui-scm/anrui-scm-ui/src/views/baseoutsourcingapplication/workflow/flow.vue
  51. 44
      anrui-system-ui/src/views/flow/todoList.vue

25
anrui-base/anrui-base-api/src/main/java/com/yxt/anrui/base/api/baseoutsourcingapplication/BaseOutsourcingApplicationFeign.java

@ -95,10 +95,30 @@ public interface BaseOutsourcingApplicationFeign {
@ApiParam(value = "业务sid") @PathVariable(value = "sid")String sid,
@ApiParam(value = "用户sid") @PathVariable(value = "userSid")String userSid);
@ApiOperation(value = "读取xml文件")
@GetMapping("/readXml/{deployId}")
public ResultBean readXml(@ApiParam(value = "流程定义id") @PathVariable(value = "deployId") String deployId);
@ApiOperation(value = "获取当前环节的下一分支节点")
@PostMapping(value = "/getNextTasks/{taskId}")
public ResultBean getNextTasks(@PathVariable(value = "taskId")String taskId);
/**
* 生成流程图
*
* @param procInsId 任务ID
*/
@ApiOperation(value = "生成流程图")
@RequestMapping("/flowViewer/{procInsId}")
public ResultBean getFlowViewer(@PathVariable("procInsId") String procInsId) ;
@ApiOperation(value = "办理(同意)")
@PostMapping("/complete")
public ResultBean complete(@ApiParam(value = "变量集合,json对象") @RequestBody Map<String, Object> variables);
@ApiOperation(value = "根据流程实例的id获取最新待办环节")
@PostMapping(value = "/getProcessCirculationNodesByMap")
ResultBean getProcessCirculationNodesByMap(@ApiParam(value = "变量集合,json对象") @RequestBody Map<String, Object> variables);
@ApiOperation(value = "撤回流程")
@PostMapping(value = "/revokeProcess/{userSid}/{businessSid}")
public ResultBean revokeProcess(
@ -121,4 +141,9 @@ public interface BaseOutsourcingApplicationFeign {
public ResultBean flowRecord( @ApiParam(value = "流程实例id")@PathVariable(value = "procInsId")String procInsId,
@ApiParam(value = "目前没用")@PathVariable(value = "deployId")String deployId);
@ApiOperation(value = "根据角色获取用户列表" )
@GetMapping(value = "/getUsers/{roleSid}")
public ResultBean getUsersByRoleSid( @ApiParam(value = "角色sid")@PathVariable(value = "roleSid")String roleSid);
}

25
anrui-base/anrui-base-api/src/main/java/com/yxt/anrui/base/api/baseoutsourcingapplication/BaseOutsourcingApplicationFeignFallback.java

@ -87,6 +87,21 @@ public class BaseOutsourcingApplicationFeignFallback implements BaseOutsourcingA
return null;
}
@Override
public ResultBean readXml(String deployId) {
return null;
}
@Override
public ResultBean getNextTasks(String taskId) {
return null;
}
@Override
public ResultBean getFlowViewer(String procInsId) {
return null;
}
@Override
public ResultBean complete(Map<String, Object> variables) {
return null;
@ -112,4 +127,14 @@ public class BaseOutsourcingApplicationFeignFallback implements BaseOutsourcingA
return null;
}
@Override
public ResultBean getUsersByRoleSid(String roleSid) {
return null;
}
@Override
public ResultBean getProcessCirculationNodesByMap(Map<String, Object> variables) {
return null;
}
}

7
anrui-base/anrui-base-api/src/main/java/com/yxt/anrui/base/api/basevehicleactualsales/BaseVehicleActualSalesFeign.java

@ -1,12 +1,17 @@
package com.yxt.anrui.base.api.basevehicleactualsales;
import com.yxt.anrui.base.api.flow.FlowTaskVo;
import com.yxt.common.core.query.PagerQuery;
import com.yxt.common.core.result.ResultBean;
import com.yxt.common.core.vo.PagerVo;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.Map;
/**
* @Author dimengzhe
* @Date 2022/4/8 11:57
@ -58,4 +63,6 @@ public interface BaseVehicleActualSalesFeign {
@PostMapping("/revokeProcess")
@ResponseBody
ResultBean revokeProcess(@RequestBody BaseVehicleActualSalesFlowDto dto);
}

4
anrui-base/anrui-base-api/src/main/java/com/yxt/anrui/base/api/basevehicleactualsales/BaseVehicleActualSalesFeignFallback.java

@ -1,10 +1,14 @@
package com.yxt.anrui.base.api.basevehicleactualsales;
import com.yxt.anrui.base.api.flow.FlowTaskVo;
import com.yxt.common.core.query.PagerQuery;
import com.yxt.common.core.result.ResultBean;
import com.yxt.common.core.vo.PagerVo;
import org.springframework.stereotype.Component;
import javax.validation.Valid;
import java.util.Map;
/**
* @Author dimengzhe
* @Date 2022/4/8 11:59

27
anrui-base/anrui-base-biz/src/main/java/com/yxt/anrui/base/biz/baseoutsourcingapplication/BaseOutsourcingApplicationRest.java

@ -34,6 +34,7 @@ import com.yxt.common.core.result.ResultBean;
import com.yxt.common.core.vo.PagerVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.flowable.bpmn.model.FlowElement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@ -108,6 +109,22 @@ public class BaseOutsourcingApplicationRest implements BaseOutsourcingApplicatio
return baseOutsourcingApplicationService.submitBaseOutSourcingApplication(sid,userSid);
}
@Override
public ResultBean readXml(String deployId) {
return baseOutsourcingApplicationService.readXml(deployId);
}
@Override
public ResultBean getNextTasks(String taskId) {
return baseOutsourcingApplicationService.getNextTasks(taskId);
}
@Override
public ResultBean getFlowViewer(String procInsId) {
return baseOutsourcingApplicationService.getFlowViewer(procInsId);
}
@Override
public ResultBean complete(Map<String, Object> variables) {
return baseOutsourcingApplicationService.complete(variables);
@ -132,4 +149,14 @@ public class BaseOutsourcingApplicationRest implements BaseOutsourcingApplicatio
public ResultBean flowRecord(String procInsId, String deployId) {
return baseOutsourcingApplicationService.flowRecord(procInsId, deployId);
}
@Override
public ResultBean getUsersByRoleSid(String roleSid) {
return baseOutsourcingApplicationService.getUsersByRoleSid(roleSid);
}
@Override
public ResultBean getProcessCirculationNodesByMap(Map<String, Object> variables) {
return baseOutsourcingApplicationService.getProcessCirculationNodesByMap(variables);
}
}

30
anrui-base/anrui-base-biz/src/main/java/com/yxt/anrui/base/biz/baseoutsourcingapplication/BaseOutsourcingApplicationService.java

@ -54,6 +54,8 @@ import com.yxt.anrui.portal.api.sysuser.SysUserFeign;
import com.yxt.anrui.portal.api.sysuser.SysUserVo;
import com.yxt.anrui.portal.config.DictCommonType;
import com.yxt.common.base.utils.DateUtils;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.lang3.StringUtils;
import com.yxt.common.base.service.MybatisBaseService;
import com.yxt.common.base.utils.PagerUtil;
@ -61,8 +63,11 @@ import com.yxt.common.core.query.PagerQuery;
import com.yxt.common.core.result.ResultBean;
import com.yxt.common.core.vo.PagerVo;
import org.flowable.bpmn.model.FlowElement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.text.SimpleDateFormat;
import java.util.Date;
@ -285,7 +290,8 @@ public class BaseOutsourcingApplicationService extends MybatisBaseService<BaseOu
String orgSid = checkUserOrg(dto.getStaffSid());
ResultBean resultBean = saveOrUpdateDto(dto);
String businessSid = resultBean.getData().toString();
Map<String, Object> variables = new HashMap<>();
Map<String, Object> variables = BeanUtil.beanToMap(dto);
//Map<String, Object> variables = new HashMap<>();
variables.put("businessSid", businessSid);
variables.put("orgSid", orgSid); //部门sid
if (StringUtils.isBlank(dto.getTaskId())) { // 新提交
@ -519,4 +525,26 @@ public class BaseOutsourcingApplicationService extends MybatisBaseService<BaseOu
return flowTaskFeign.businessFlowRecord(procInsId,deployId);
}
public ResultBean readXml(String deployId) {
return flowTaskFeign.readXml(deployId);
}
public ResultBean getFlowViewer(String procInsId) {
return flowTaskFeign.getFlowViewer(procInsId);
}
public ResultBean getNextTasks(String taskId) {
return flowTaskFeign.getNextTasks(taskId);
}
public ResultBean getUsersByRoleSid(String roleSid) {
return sysUserFeign.getUsersByRoleSid(roleSid);
}
public ResultBean getProcessCirculationNodesByMap(Map<String, Object> variables) {
ResultBean processCirculationNodesByMap =flowTaskFeign.getProcessCirculationNodesByMap(variables);
processCirculationNodesByMap.setCode("200");
processCirculationNodesByMap.setSuccess(true);
return processCirculationNodesByMap;
}
}

12
anrui-flowable/anrui-flowable-api/src/main/java/com/yxt/anrui/flowable/api/flowtask/FlowTaskFeign.java

@ -15,6 +15,7 @@ import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* @author dimengzhe
@ -96,10 +97,6 @@ public interface FlowTaskFeign {
@ResponseBody
ResultBean processVariables(@ApiParam(value = "流程任务Id") @PathVariable(value = "taskId") String taskId);
@ApiOperation(value = "获取下一节点")
@PostMapping(value = "/nextFlowNode")
@ResponseBody
ResultBean getNextFlowNode(@RequestBody FlowTaskQueryOne flowTaskQueryOne);
/**
* 生成流程图
*
@ -161,4 +158,11 @@ public interface FlowTaskFeign {
@RequestParam(value = "v")String v,
@RequestParam(value = "outcome")String outcome);
@ApiOperation(value = "根据流程实例的id获取最新待办环节")
@PostMapping(value = "/getProcessCirculationNodesByMap")
ResultBean getProcessCirculationNodesByMap(@RequestBody Map<String, Object> variables);
@ApiOperation("获取流程图")
@GetMapping("/readXml/{sid}")
ResultBean readXml(@PathVariable(value ="sid") String sid);
}

15
anrui-flowable/anrui-flowable-api/src/main/java/com/yxt/anrui/flowable/api/flowtask/FlowTaskFeignFallBack.java

@ -12,6 +12,7 @@ import org.flowable.bpmn.model.FlowElement;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
/**
* @author dimengzhe
@ -86,11 +87,6 @@ public class FlowTaskFeignFallBack implements FlowTaskFeign {
return null;
}
@Override
public ResultBean getNextFlowNode(FlowTaskQueryOne flowTaskQueryOne) {
return null;
}
@Override
public ResultBean getFlowViewer(String procInsId) {
return null;
@ -150,5 +146,14 @@ public class FlowTaskFeignFallBack implements FlowTaskFeign {
String k, String v, String outcome) {
return null;
}
@Override
public ResultBean getProcessCirculationNodesByMap(Map<String, Object> variables) {
return null;
}
@Override
public ResultBean readXml(String deployId) {
return null;
}
}

4
anrui-flowable/anrui-flowable-api/src/main/java/com/yxt/anrui/flowable/api/utils/ProcDefEnum.java

@ -17,9 +17,9 @@ public enum ProcDefEnum {
RECORDAPPLICATION("入账申请", "record_application_7y80d4r8:1:265008"),
SCMVEHICLERETURN("采购退库", "process_md8aeoap:4:245028"),
SEALAPPLY("盖章申请", "process_7h0z66tp:1:240064" ),
BASEVEHICLEACTUALSALES("未售买断", ""),//ToDo
BASEVEHICLEACTUALSALES("未售买断", "process_2l9i88sk:3:272604"),//ToDo
SALESORDER("销售订单审批流程", "process_p5ils2rn:2:245112"),
BASEOUTSOURCINGAPPLICATION("外采申请", "process_5tqysnjc:1:232504"),
BASEOUTSOURCINGAPPLICATION("外采申请", "process_5tqysnjc:2:325008"),
;
ProcDefEnum(String proDefName, String proDefId) {

11
anrui-flowable/anrui-flowable-biz/src/main/java/com/yxt/anrui/flowable/biz/flowdefinition/FlowDefinitionService.java

@ -9,7 +9,9 @@ package com.yxt.anrui.flowable.biz.flowdefinition;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yxt.anrui.flowable.api.flowdefinition.FlowDefinitionVo;
import com.yxt.anrui.flowable.api.flowtask.FlowTask;
import com.yxt.anrui.flowable.api.flowtask.LatestTaskVo;
import com.yxt.anrui.flowable.api.sysform.SysForm;
import com.yxt.anrui.flowable.biz.flow.FlowableService;
import com.yxt.anrui.flowable.biz.flowtask.FlowTaskService;
import com.yxt.anrui.flowable.biz.sysdeployform.SysDeployFormService;
import com.yxt.anrui.flowable.common.FlowComment;
@ -45,7 +47,8 @@ public class FlowDefinitionService extends FlowServiceFactory {
private SysDeployFormService sysDeployFormService;
@Autowired
private SysUserFeign sysUserFeign;
@Autowired
private FlowableService flowableService;
/**
* 导入流程文件
*
@ -191,7 +194,11 @@ public class FlowDefinitionService extends FlowServiceFactory {
taskService.setAssignee(task.getId(), userSid);
taskService.complete(task.getId(), variables);
}
ResultBean<List<LatestTaskVo>> latestTasksNew = flowTaskService.getLatestTasksNew(processInstance.getId());
List<LatestTaskVo> data = latestTasksNew.getData();
LatestTaskVo latestTaskVo = data.get(0);
String id_ = latestTaskVo.getId_();
taskService.setAssignee(id_, userSid);
ResultBean<FlowTask> rb = new ResultBean<>();
FlowTask flowTask = new FlowTask();
flowTask.setTaskId(task.getId());

59
anrui-flowable/anrui-flowable-biz/src/main/java/com/yxt/anrui/flowable/biz/flowtask/FlowTaskController.java

@ -18,6 +18,7 @@ import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.ExtensionAttribute;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
@ -185,11 +186,6 @@ public class FlowTaskController implements FlowTaskFeign {
return flowTaskService.processVariables(taskId);
}
@Override
public ResultBean getNextFlowNode(FlowTaskQueryOne flowTaskQueryOne) {
return null;
}
@Override
public ResultBean getFlowViewer(String procInsId) {
return flowTaskService.getFlowViewer(procInsId);
@ -255,9 +251,43 @@ public class FlowTaskController implements FlowTaskFeign {
map.put(k, v);
map.put("outcome", outcome);
List<FlowElement> flowElements = processService.calApprovePath(procId, modelId, map);
List<Map<String, String>> list = new ArrayList<>();
List<FlowElement> collect = flowElements.stream().filter(item -> item.getId().length() > 0).collect(Collectors.toList());
for (int i = 0; i < collect.size(); i++) {
if (collect.get(i).getId().equals(modelId)) {
FlowElement item = collect.get(i + 1);
log.info("flowElement:{}", item);
log.info("nodeName:{}", item.getName());
log.info("nodeId:{}", item.getId());
String s = JSON.toJSONString(item);
JSONObject jsonObject = JSONObject.parseObject(s);
log.info("item:{}", jsonObject);
Object candidateGroups = jsonObject.get("candidateGroups");
JSONArray candidateGroups_arr = new JSONArray();
if (candidateGroups != null) {
candidateGroups_arr = JSONArray.parseArray(candidateGroups.toString());
}
if (candidateGroups_arr.size() > 0) {
log.info("candidateGroups_sid:{}", candidateGroups_arr.get(0));
}
log.info("candidateGroups:{}", candidateGroups_arr);
}
}
ResultBean<List<FlowElement>> rb = new ResultBean<List<FlowElement>>();
return rb.setData(flowElements);
}
@Override
public ResultBean getProcessCirculationNodesByMap(Map<String, Object> variables) {
String modelId =null;
if(variables.get("modelId")!=null){
modelId= variables.get("modelId").toString();
}
String procId =null;
if(variables.get("procId")!=null){
procId= variables.get("procId").toString();
}
List<FlowElement> flowElements = processService.calApprovePath(procId, modelId, variables);
List<FlowElement> collect = flowElements.stream().filter(item -> item.getId().length() > 0).collect(Collectors.toList());
for (int i = 0; i < collect.size(); i++) {
if (collect.get(i).getId().equals(modelId)) {
FlowElement item = collect.get(i + 1);
@ -279,6 +309,23 @@ public class FlowTaskController implements FlowTaskFeign {
}
}
ResultBean<List<FlowElement>> rb = new ResultBean<List<FlowElement>>();
List<Map<String,Object>> list=new ArrayList<>();
for(FlowElement f:flowElements){
Map<String,Object> map=new HashMap<>();
map.put("name",f.getName());
map.put("id",f.getId());
String s = JSON.toJSONString(f);
JSONObject jsonObject = JSONObject.parseObject(s);
log.info("item:{}", jsonObject);
Object candidateGroups = jsonObject.get("candidateGroups");
map.put("candidateGroups",candidateGroups);
list.add(map);
}
return rb.setData(flowElements);
}
@Override
public ResultBean readXml(String deployId) {
return flowTaskService.readXml(deployId);
}
}

6
anrui-flowable/anrui-flowable-biz/src/main/java/com/yxt/anrui/flowable/biz/flowtask/FlowTaskMapper.java

@ -1,6 +1,5 @@
package com.yxt.anrui.flowable.biz.flowtask;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yxt.anrui.flowable.api.flowtask.FlowTask;
@ -9,6 +8,7 @@ import com.yxt.anrui.flowable.api.flowtask.TaskQueryParamsVo;
import com.yxt.anrui.flowable.api.flowtask.TaskVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.flowable.task.api.history.HistoricTaskInstance;
import java.util.List;
import java.util.Map;
@ -38,4 +38,8 @@ public interface FlowTaskMapper extends BaseMapper<FlowTask> {
* @return
*/
List<LatestTaskVo> getLatestTasks(@Param("procId")String procId);
Map<String,Object> getHistTaskByID(@Param("procId")String procId,@Param("taskId")String id);
Map<String, Object> getTaskByDefKey(@Param("procId")String processInstanceId, @Param("taskId")String id);
}

24
anrui-flowable/anrui-flowable-biz/src/main/java/com/yxt/anrui/flowable/biz/flowtask/FlowTaskMapper.xml

@ -196,4 +196,28 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
WHERE
task.proc_inst_id_= #{procId}
</select>
<select id="getHistTaskByID" resultType="java.util.Map">
SELECT
task.name_ ,
task.task_def_key_ ,
task.id_,
task.ASSIGNEE_
FROM
act_hi_taskinst task
WHERE
task.task_def_key_= #{taskId} and task.PROC_INST_ID_= #{procId} order by task.id_ desc limit 1,1
</select>
<select id="getTaskByDefKey" resultType="java.util.Map">
SELECT
task.name_ ,
task.task_def_key_ ,
task.id_,
task.ASSIGNEE_
FROM
act_ru_task task
WHERE
task.task_def_key_= #{taskId} and task.PROC_INST_ID_= #{procId} order by task.id_ desc limit 0,1
</select>
</mapper>

56
anrui-flowable/anrui-flowable-biz/src/main/java/com/yxt/anrui/flowable/biz/flowtask/FlowTaskService.java

@ -1,5 +1,6 @@
package com.yxt.anrui.flowable.biz.flowtask;
import org.apache.commons.io.IOUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -32,6 +33,7 @@ import com.yxt.common.core.query.PagerQuery;
import com.yxt.common.core.result.ResultBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.api.FlowableException;
@ -57,10 +59,14 @@ import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.ui.modeler.service.FlowableFormService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;
@ -72,6 +78,8 @@ import java.util.stream.Collectors;
@Service
@Slf4j
public class FlowTaskService extends MybatisBaseService<FlowTaskMapper, FlowTask> {
@Autowired
FlowTaskMapper flowTaskMapper;
@Resource
protected HistoryService historyService;
@Resource
@ -357,14 +365,14 @@ public class FlowTaskService extends MybatisBaseService<FlowTaskMapper, FlowTask
}
}
// 用户角色sid
List<String> roleSidList = sysUserRoleFeign.getUserRoleSidByUserSid(userSid).getData();
if (CollectionUtils.isEmpty(roleSidList)) {
// List<String> roleSidList = sysUserRoleFeign.getUserRoleSidByUserSid(userSid).getData();
/* if (CollectionUtils.isEmpty(roleSidList)) {
return new ResultBean().fail().setMsg("请先设置用户角色");
}
}*/
taskQuery.active()
.includeProcessVariables()
.or()
.taskCandidateGroupIn(roleSidList)
//.taskCandidateGroupIn(roleSidList)
.taskAssignee(userSid)
.endOr()
.orderByTaskCreateTime().desc();
@ -738,11 +746,14 @@ public class FlowTaskService extends MybatisBaseService<FlowTaskMapper, FlowTask
*/
@Transactional(rollbackFor = Exception.class)
public ResultBean<FlowTask> businessComplete(BusinessVariables dto) {
ResultBean<FlowTask> rb = new ResultBean<>();
Map<String, Object> formVariables = dto.getFormVariables();
// formVariables.put("handleUrl", dto.getHandleUrl());
// formVariables.put("detailUrl", dto.getDetailUrl());
Object taskId_obj = formVariables.get("taskId");
String procInsId= formVariables.get("instanceId").toString();
String nextUserSid= formVariables.get("nextUserSid").toString();
String taskId = null;
if (taskId_obj == null) {
return rb.fail().setMsg("taskId 不能为空!").setData(new FlowTask());
@ -806,6 +817,12 @@ public class FlowTaskService extends MybatisBaseService<FlowTaskMapper, FlowTask
FlowComment.NORMAL.getType(), comment);
taskService.setAssignee(taskId, userSid);
taskService.complete(taskId, formVariables);
ResultBean<List<LatestTaskVo>> l= getLatestTasksNew(procInsId);
if(l.getData().size()>0){
LatestTaskVo latestTaskVo = l.getData().get(0);
String id_ = latestTaskVo.getId_();
taskService.setAssignee(id_, nextUserSid);
}
}
FlowTask flowTask = new FlowTask();
flowTask.setTaskId(task.getId());
@ -1016,7 +1033,6 @@ public class FlowTaskService extends MybatisBaseService<FlowTaskMapper, FlowTask
// 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务
List<UserTask> currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null);
currentUserTaskList.forEach(item -> currentIds.add(item.getId()));
// 规定:并行网关之前节点必须需存在唯一用户任务节点,如果出现多个任务节点,则并行网关节点默认为结束节点,原因为不考虑多对多情况
if (targetIds.size() > 1 && currentIds.size() > 1) {
return new ResultBean<List<LatestTaskVo>>().fail().setMsg("任务出现多对多情况,无法撤回").setData(new ArrayList<>());
@ -1057,6 +1073,11 @@ public class FlowTaskService extends MybatisBaseService<FlowTaskMapper, FlowTask
LatestTaskVo latestTaskVo = new LatestTaskVo();
latestTaskVo.setASSIGNEE_(item.getAssignee());
latestTaskVo.setId_(item.getId());
Map<String,Object> map= flowTaskMapper.getHistTaskByID(task.getProcessInstanceId(),targetIds.get(0));
Map<String,Object> map2= flowTaskMapper.getTaskByDefKey(task.getProcessInstanceId(),item.getId());
String id_ = map2.get("id_").toString();
String assignee_ = map.get("ASSIGNEE_").toString();
taskService.setAssignee(id_,assignee_);
latestTaskVo.setName_(item.getName());
latestTaskVo.setTask_def_key_(item.getId());
latestTaskVo.setIncomingSourceRef(item.getIncomingFlows().get(0).getSourceRef());
@ -1404,8 +1425,7 @@ public class FlowTaskService extends MybatisBaseService<FlowTaskMapper, FlowTask
FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(crruentActivityId);
// 输出连线
List<SequenceFlow> outFlows = flowNode.getOutgoingFlows();
Map<String, String> nodeMap = new HashMap<>();
List<Map<String, String>> lists = new ArrayList<>();
List<Map<String, Object>> lists = new ArrayList<>();
getNodes(outFlows, node, lists);
System.out.println(JSON.toJSONString(lists));
return ResultBean.fireSuccess().setData(lists);
@ -1421,7 +1441,7 @@ public class FlowTaskService extends MybatisBaseService<FlowTaskMapper, FlowTask
return baseMapper.getLatestTasks(procId);
}
public void getNodes(List<SequenceFlow> outFlows, String node, List<Map<String, String>> lists) {
public void getNodes(List<SequenceFlow> outFlows, String node, List<Map<String, Object>> lists) {
for (SequenceFlow sequenceFlow : outFlows) {
//当前审批节点
if ("now".equals(node)) {
@ -1432,18 +1452,19 @@ public class FlowTaskService extends MybatisBaseService<FlowTaskMapper, FlowTask
FlowElement targetFlow = sequenceFlow.getTargetFlowElement();
if (targetFlow instanceof UserTask) {
System.out.println("下一节点: id=" + targetFlow.getId() + ",name=" + targetFlow.getName());
Map<String, String> map = new HashMap<>();
Map<String, Object> map = new HashMap<>();
map.put("id", targetFlow.getId());
map.put("candidateGroups", ((UserTask) targetFlow).getCandidateGroups());
map.put("name", targetFlow.getName());
map.put("assignee", ((UserTask) targetFlow).getAssignee());
lists.add(map);
}
if (targetFlow instanceof EndEvent) {// 如果下个审批节点为结束节点
System.out.println("下一节点为结束节点:id=" + targetFlow.getId() + ",name=" + targetFlow.getName());
Map<String, String> map = new HashMap<>();
Map<String, Object> map = new HashMap<>();
map.put("id", targetFlow.getId());
map.put("name", targetFlow.getName());
map.put("assignee", ((UserTask) targetFlow).getAssignee());
// map.put("assignee", ((UserTask) targetFlow).getAssignee());
lists.add(map);
}
if (targetFlow instanceof Gateway) {// 如果下个审批节点为结束节点
@ -1484,4 +1505,17 @@ public class FlowTaskService extends MybatisBaseService<FlowTaskMapper, FlowTask
deleteProcess(procInsId, "删除");
return ResultBean.fireSuccess();
}
public ResultBean readXml(String deployId) {
ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult();
InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName());
String result = null;
try {
result = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name());
} catch (IOException e) {
e.printStackTrace();
}
ResultBean r=ResultBean.fireSuccess().setData(result);
return r;
}
}

4
anrui-flowable/anrui-flowable-biz/src/main/java/com/yxt/anrui/flowable/biz/process/ProcessService.java

@ -38,8 +38,8 @@ public class ProcessService {
* @param variableMap 流程变量用于计算条件分支
*/
public List<FlowElement> calApprovePath(String processInstanceId, String modelId, Map<String, Object> variableMap){
// BpmnModel bpmnModel=repositoryService.getBpmnModel(modelId);
BpmnModel bpmnModel =repositoryService.getBpmnModel(processInstanceId);
BpmnModel bpmnModel=repositoryService.getBpmnModel(modelId);
// BpmnModel bpmnModel2 =repositoryService.getBpmnModel(processInstanceId);
/*if(StringUtils.isNotBlank(processInstanceId)){
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());

3
anrui-portal/anrui-portal-api/src/main/java/com/yxt/anrui/portal/api/sysuser/SysUserFeign.java

@ -178,4 +178,7 @@ public interface SysUserFeign {
@ApiOperation(value = "设置是否可用:isEnable:1可用,0不可用")
public ResultBean setIsEnable(@ApiParam(value = "sid", required = true) @PathVariable("sid") String sid, @ApiParam(value = "isEnable", required = true) @PathVariable("isEnable") String isEnable);
@ApiOperation("根据角色sid查询用户")
@GetMapping("/getUsersByRoleSid/{roleSid}")
ResultBean getUsersByRoleSid(@PathVariable("roleSid") String roleSid);
}

5
anrui-portal/anrui-portal-api/src/main/java/com/yxt/anrui/portal/api/sysuser/SysUserFeignFallback.java

@ -163,4 +163,9 @@ public class SysUserFeignFallback implements SysUserFeign {
public ResultBean setIsEnable(String sid, String isEnable) {
return null;
}
@Override
public ResultBean getUsersByRoleSid(String roleSid) {
return null;
}
}

3
anrui-portal/anrui-portal-biz/src/main/java/com/yxt/anrui/portal/biz/sysuser/SysUserMapper.java

@ -12,6 +12,7 @@ import com.yxt.anrui.portal.api.sysuser.app.AppUserOrgInfoVo;
import com.yxt.anrui.portal.api.sysuser.wx.WxHomePageVo;
import com.yxt.anrui.portal.api.sysuser.wx.WxMySysUserInfoVo;
import com.yxt.anrui.portal.api.sysuser.wx.WxSysUserVo;
import com.yxt.common.core.result.ResultBean;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
@ -155,4 +156,6 @@ public interface SysUserMapper extends BaseMapper<SysUser> {
SysUser selectByMobile(String mobile);
int updateIsEnable(@Param("sid") String sid, @Param("isEnable") String isEnable);
List<SysUserVo> getUsersByRoleSid(@Param("roleSid")String roleSid);
}

10
anrui-portal/anrui-portal-biz/src/main/java/com/yxt/anrui/portal/biz/sysuser/SysUserMapper.xml

@ -194,4 +194,14 @@
SET isEnable = #{isEnable}
WHERE sid = #{sid}
</update>
<select id="getUsersByRoleSid" resultType="com.yxt.anrui.portal.api.sysuser.SysUserVo">
SELECT su.sid,ss.name,sso.orgNamePath departmentName
FROM `sys_user` su
LEFT JOIN `sys_staffinfo` ss ON su.staffSid=ss.sid
LEFT JOIN `sys_staff_org` sso ON sso.staffSid=ss.sid
LEFT JOIN `sys_user_role` sur ON sur.userSid=su.sid
LEFT JOIN `sys_role` sr ON sr.sid = sur.roleSid
WHERE sr.sid=#{roleSid}
</select>
</mapper>

5
anrui-portal/anrui-portal-biz/src/main/java/com/yxt/anrui/portal/biz/sysuser/SysUserRest.java

@ -570,4 +570,9 @@ public class SysUserRest implements SysUserFeign {
}
return ResultBean.fireSuccess().setMsg("设置成功");
}
@Override
public ResultBean getUsersByRoleSid(String roleSid) {
return sysUserService.getUsersByRoleSid(roleSid);
}
}

6
anrui-portal/anrui-portal-biz/src/main/java/com/yxt/anrui/portal/biz/sysuser/SysUserService.java

@ -853,4 +853,10 @@ public class SysUserService extends MybatisBaseService<SysUserMapper, SysUser> {
public int updateIsEnable(String sid, String isEnable) {
return baseMapper.updateIsEnable(sid, isEnable);
}
public ResultBean getUsersByRoleSid(String roleSid) {
List<SysUserVo> users = baseMapper.getUsersByRoleSid(roleSid);
ResultBean<Object> objectResultBean = ResultBean.fireSuccess().setData(users);
return objectResultBean;
}
}

4
anrui-scm/anrui-scm-ui/package.json

@ -15,6 +15,7 @@
},
"dependencies": {
"axios": "^0.24.0",
"bpmn-js": "^9.2.2",
"core-js": "^2.6.12",
"ejs": "^2.7.4",
"element-ui": "2.13.2",
@ -31,7 +32,8 @@
"vue-amap": "^0.5.10",
"vue-router": "3.0.6",
"vuex": "3.1.0",
"vuex-persistedstate": "^4.0.0"
"vuex-persistedstate": "^4.0.0",
"xcrud": "^0.4.19"
},
"devDependencies": {
"@vue/cli-plugin-babel": "4.4.4",

47
anrui-scm/anrui-scm-ui/src/api/baseoutsourcingapplication/baseoutsourcingapplication.js

@ -63,6 +63,21 @@ export default {
data: params
})
},
// 读取xml文件
readXml: function (deployId) {
return request({
url: '/base/baseoutsourcingapplication/readXml/' + deployId,
method: 'get'
})
},
// 读取image文件
getFlowViewer: function (procInsId) {
return request({
url: '/base/baseoutsourcingapplication/flowViewer/' + procInsId,
method: 'get'
})
},
// 同意任务
agreeTask: function(params) {
return request({
@ -77,7 +92,7 @@ export default {
// 驳回任务
rejectTask: function(params) {
return request({
url: '/base/baseoutsourcingapplication/' + params.businessSid,
url: '/base/baseoutsourcingapplication/reject/' + params.businessSid,
method: 'post',
data: params,
headers: {
@ -97,7 +112,7 @@ export default {
})
},
// 撤回任务
revokeTask: function(params) {
revokeTask: function (params) {
return request({
url: '/base/baseoutsourcingapplication/' + params.userSid + '/' + params.businessSid,
method: 'post',
@ -106,5 +121,33 @@ export default {
'Content-Type': 'application/json'
}
})
},
getNextNodes: function(taskid) {
return request({
url: '/base/baseoutsourcingapplication/getNextTasks/' + taskid,
method: 'post',
headers: {
'Content-Type': 'application/json'
}
})
},
getNextNodesForSubmit: function(data) {
return request({
url: '/base/baseoutsourcingapplication/getProcessCirculationNodesByMap',
method: 'post',
data: data,
headers: {
'Content-Type': 'application/json'
}
})
},
getUsers: function(roleSId) {
return request({
url: '/base/baseoutsourcingapplication/getUsers/' + roleSId,
method: 'get',
headers: {
'Content-Type': 'application/json'
}
})
}
}

68
anrui-scm/anrui-scm-ui/src/views/Process/BpmData.js

@ -0,0 +1,68 @@
/**
* 存储流程设计相关参数
*/
export default class BpmData {
constructor() {
this.controls = [] // 设计器控件
this.init()
}
init() {
this.controls = [
{
action: 'create.start-event',
title: '开始'
},
{
action: 'create.intermediate-event',
title: '中间'
},
{
action: 'create.end-event',
title: '结束'
},
{
action: 'create.exclusive-gateway',
title: '网关'
},
{
action: 'create.task',
title: '任务'
},
{
action: 'create.user-task',
title: '用户任务'
},
{
action: 'create.user-sign-task',
title: '会签任务'
},
{
action: 'create.subprocess-expanded',
title: '子流程'
},
{
action: 'create.data-object',
title: '数据对象'
},
{
action: 'create.data-store',
title: '数据存储'
},
{
action: 'create.participant-expanded',
title: '扩展流程'
},
{
action: 'create.group',
title: '分组'
}
]
}
// 获取控件配置信息
getControl(action) {
const result = this.controls.filter(item => item.action === action)
return result[0] || {}
}
}

169
anrui-scm/anrui-scm-ui/src/views/Process/PropertyPanel.vue

@ -0,0 +1,169 @@
<template>
<div ref="propertyPanel" class="property-panel">
<div v-if="nodeName" class="node-name">{{ nodeName }}</div>
<component
:is="getComponent"
v-if="element"
:element="element"
:modeler="modeler"
:users="users"
:groups="groups"
:categorys="categorys"
@dataType="dataType"
/>
</div>
</template>
<script>
import taskPanel from './components/nodePanel/task'
import startEndPanel from './components/nodePanel/startEnd'
import processPanel from './components/nodePanel/process'
import sequenceFlowPanel from './components/nodePanel/sequenceFlow'
import gatewayPanel from './components/nodePanel/gateway'
import { NodeName } from './lang/zh'
export default {
name: 'PropertyPanel',
components: { processPanel, taskPanel, startEndPanel, sequenceFlowPanel, gatewayPanel },
props: {
users: {
type: Array,
required: true
},
groups: {
type: Array,
required: true
},
categorys: {
type: Array,
required: true
},
modeler: {
type: Object,
required: true
}
},
data() {
return {
element: null,
form: {
id: '',
name: '',
color: null
},
roles: [
{ value: 'manager', label: '经理' },
{ value: 'personnel', label: '人事' },
{ value: 'charge', label: '主管' }
]
}
},
computed: {
getComponent() {
const type = this.element?.type
if (['bpmn:IntermediateThrowEvent', 'bpmn:StartEvent', 'bpmn:EndEvent'].includes(type)) {
return 'startEndPanel'
}
if ([
'bpmn:UserTask',
'bpmn:Task',
'bpmn:SendTask',
'bpmn:ReceiveTask',
'bpmn:ManualTask',
'bpmn:BusinessRuleTask',
'bpmn:ServiceTask',
'bpmn:ScriptTask'
// 'bpmn:CallActivity',
// 'bpmn:SubProcess'
].includes(type)) {
return 'taskPanel'
}
if (type === 'bpmn:SequenceFlow') {
return 'sequenceFlowPanel'
}
if ([
'bpmn:InclusiveGateway',
'bpmn:ExclusiveGateway',
'bpmn:ParallelGateway',
'bpmn:EventBasedGateway'
].includes(type)) {
return 'gatewayPanel'
}
if (type === 'bpmn:Process') {
return 'processPanel'
}
return null
},
nodeName() {
if (this.element) {
const bizObj = this.element.businessObject
const type = bizObj?.eventDefinitions
? bizObj.eventDefinitions[0].$type
: bizObj.$type
return NodeName[type] || type
}
return ''
}
},
mounted() {
this.handleModeler()
},
methods: {
handleModeler() {
this.modeler.on('root.added', e => {
if (e.element.type === 'bpmn:Process') {
this.element = null
this.$nextTick().then(() => {
this.element = e.element
})
}
})
this.modeler.on('element.click', e => {
const { element } = e
console.log(element)
if (element.type === 'bpmn:Process') {
this.element = element
}
})
this.modeler.on('selection.changed', e => {
// hack
this.element = null
const element = e.newSelection[0]
if (element) {
this.$nextTick().then(() => {
this.element = element
})
}
})
},
/** 获取数据类型 */
dataType(data){
this.$emit('dataType', data)
}
}
}
</script>
<style lang="scss">
.property-panel {
padding: 20px 20px;
// reset element css
.el-form--label-top .el-form-item__label {
padding: 0;
}
.el-form-item {
margin-bottom: 6px;
}
.tab-table .el-form-item {
margin-bottom: 16px;
}
.node-name{
border-bottom: 1px solid #ccc;
padding: 0 0 10px 20px;
margin-bottom: 10px;
font-size: 16px;
font-weight: bold;
color: #444;
}
}
</style>

20
anrui-scm/anrui-scm-ui/src/views/Process/common/customTranslate.js

@ -0,0 +1,20 @@
import translations from '../lang/zh'
export default function customTranslate(template, replacements) {
replacements = replacements || {}
// Translate
template = translations[template] || template
// Replace
return template.replace(/{([^}]+)}/g, function(_, key) {
var str = replacements[key]
if (
translations[replacements[key]] !== null &&
translations[replacements[key]] !== 'undefined'
) {
str = translations[replacements[key]]
}
return str || '{' + key + '}'
})
}

24
anrui-scm/anrui-scm-ui/src/views/Process/common/mixinExecutionListener.js

@ -0,0 +1,24 @@
import executionListenerDialog from '../components/nodePanel/property/executionListener'
export default {
components: {
executionListenerDialog
},
data() {
return {
executionListenerLength: 0,
dialogName: null
}
},
methods: {
computedExecutionListenerLength() {
this.executionListenerLength = this.element.businessObject.extensionElements?.values?.length ?? 0
},
finishExecutionListener() {
if (this.dialogName === 'executionListenerDialog') {
this.computedExecutionListenerLength()
}
this.dialogName = ''
}
}
}

70
anrui-scm/anrui-scm-ui/src/views/Process/common/mixinPanel.js

@ -0,0 +1,70 @@
import xcrud from 'xcrud'
import golbalConfig from 'xcrud/package/common/config'
import showConfig from '../flowable/showConfig'
golbalConfig.set({
input: {
// size: 'mini'
},
select: {
// size: 'mini'
},
colorPicker: {
showAlpha: true
},
xform: {
form: {
labelWidth: 'auto'
// size: 'mini'
}
}
})
export default {
components: { xForm: xcrud.xForm },
props: {
modeler: {
type: Object,
required: true
},
element: {
type: Object,
required: true
},
categorys: {
type: Array,
default: () => []
}
},
watch: {
'formData.id': function(val) {
this.updateProperties({ id: val })
},
'formData.name': function(val) {
this.updateProperties({ name: val })
},
'formData.documentation': function(val) {
if (!val) {
this.updateProperties({ documentation: [] })
return
}
const documentationElement = this.modeler.get('moddle').create('bpmn:Documentation', { text: val })
this.updateProperties({ documentation: [documentationElement] })
}
},
methods: {
updateProperties(properties) {
const modeling = this.modeler.get('modeling')
modeling.updateProperties(this.element, properties)
}
},
computed: {
elementType() {
const bizObj = this.element.businessObject
return bizObj.eventDefinitions
? bizObj.eventDefinitions[0].$type
: bizObj.$type
},
showConfig() {
return showConfig[this.elementType] || {}
}
}
}

22
anrui-scm/anrui-scm-ui/src/views/Process/common/mixinXcrud.js

@ -0,0 +1,22 @@
import xcrud from 'xcrud'
import golbalConfig from 'xcrud/package/common/config'
golbalConfig.set({
input: {
// size: 'mini'
},
select: {
// size: 'mini'
},
colorPicker: {
showAlpha: true
},
xform: {
form: {
labelWidth: 'auto'
// size: 'mini'
}
}
})
export default {
components: { xForm: xcrud.xForm }
}

53
anrui-scm/anrui-scm-ui/src/views/Process/common/parseElement.js

@ -0,0 +1,53 @@
export function commonParse(element) {
const result = {
...element.businessObject,
...element.businessObject.$attrs
}
return formatJsonKeyValue(result)
}
export function formatJsonKeyValue(result) {
// 移除flowable前缀,格式化数组
for (const key in result) {
if (key.indexOf('flowable:') === 0) {
const newKey = key.replace('flowable:', '')
result[newKey] = result[key]
delete result[key]
}
}
result = documentationParse(result)
return result
}
export function documentationParse(obj) {
if ('documentation' in obj) {
let str = ''
obj.documentation.forEach(item => {
str += item.text
})
obj.documentation = str
}
return obj
}
export function conditionExpressionParse(obj) {
if ('conditionExpression' in obj) {
obj.conditionExpression = obj.conditionExpression.body
}
return obj
}
export function userTaskParse(obj) {
for (const key in obj) {
if (key === 'candidateUsers') {
obj.userType = 'candidateUsers'
obj[key] = obj[key]?.split(',') || []
} else if (key === 'candidateGroups') {
obj.userType = 'candidateGroups'
obj[key] = obj[key]?.split(',') || []
} else if (key === 'assignee') {
obj.userType = 'assignee'
}
}
return obj
}

24
anrui-scm/anrui-scm-ui/src/views/Process/components/custom/customContextPad.vue

@ -0,0 +1,24 @@
export default class CustomContextPad {
constructor(config, contextPad, create, elementFactory, injector, translate) {
this.create = create;
this.elementFactory = elementFactory;
this.translate = translate;
if (config.autoPlace !== false) {
this.autoPlace = injector.get('autoPlace', false);
}
contextPad.registerProvider(this); // contextPad
}
getContextPadEntries(element) {}
}
CustomContextPad.$inject = [
'config',
'contextPad',
'create',
'elementFactory',
'injector',
'translate'
];

81
anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/gateway.vue

@ -0,0 +1,81 @@
<template>
<div>
<x-form ref="xForm" v-model="formData" :config="formConfig">
<template #executionListener>
<el-badge :value="executionListenerLength">
<el-button size="small" @click="dialogName = 'executionListenerDialog'">编辑</el-button>
</el-badge>
</template>
</x-form>
<executionListenerDialog
v-if="dialogName === 'executionListenerDialog'"
:element="element"
:modeler="modeler"
@close="finishExecutionListener"
/>
</div>
</template>
<script>
import mixinPanel from '../../common/mixinPanel'
import mixinExecutionListener from '../../common/mixinExecutionListener'
import { commonParse } from '../../common/parseElement'
export default {
mixins: [mixinPanel, mixinExecutionListener],
data() {
return {
formData: {}
}
},
computed: {
formConfig() {
return {
inline: false,
item: [
{
xType: 'input',
name: 'id',
label: '节点 id',
rules: [{ required: true, message: 'Id 不能为空' }]
},
{
xType: 'input',
name: 'name',
label: '节点名称'
},
{
xType: 'input',
name: 'documentation',
label: '节点描述'
},
{
xType: 'slot',
name: 'executionListener',
label: '执行监听器'
},
{
xType: 'switch',
name: 'async',
label: '异步',
activeText: '是',
inactiveText: '否'
}
]
}
}
},
watch: {
'formData.async': function(val) {
if (val === '') val = null
this.updateProperties({ 'flowable:async': val })
}
},
created() {
this.formData = commonParse(this.element)
}
}
</script>
<style>
</style>

113
anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/process.vue

@ -0,0 +1,113 @@
<template>
<div>
<x-form ref="xForm" v-model="formData" :config="formConfig">
<template #executionListener>
<el-badge :value="executionListenerLength">
<el-button size="small" @click="dialogName = 'executionListenerDialog'">编辑</el-button>
</el-badge>
</template>
<template #signal>
<el-badge :value="signalLength">
<el-button size="small" @click="dialogName = 'signalDialog'">编辑</el-button>
</el-badge>
</template>
</x-form>
<executionListenerDialog
v-if="dialogName === 'executionListenerDialog'"
:element="element"
:modeler="modeler"
@close="finishExecutionListener"
/>
<signalDialog
v-if="dialogName === 'signalDialog'"
:element="element"
:modeler="modeler"
@close="finishExecutionListener"
/>
</div>
</template>
<script>
import mixinPanel from '../../common/mixinPanel'
import mixinExecutionListener from '../../common/mixinExecutionListener'
import signalDialog from './property/signal'
import { commonParse } from '../../common/parseElement'
export default {
components: {
signalDialog
},
mixins: [mixinPanel, mixinExecutionListener],
data() {
return {
signalLength: 0,
formData: {}
}
},
computed: {
formConfig() {
const _this = this
return {
inline: false,
item: [
{
xType: 'select',
name: 'processCategory',
label: '流程分类',
dic: { data: _this.categorys, label: 'dictLabel', value: 'dictValue' }
},
{
xType: 'input',
name: 'id',
label: '流程标识key',
rules: [{ required: true, message: 'Id 不能为空' }]
},
{
xType: 'input',
name: 'name',
label: '流程名称'
},
{
xType: 'input',
name: 'documentation',
label: '节点描述'
},
{
xType: 'slot',
name: 'executionListener',
label: '执行监听器'
},
{
xType: 'slot',
name: 'signal',
label: '信号定义'
}
]
}
}
},
watch: {
'formData.processCategory': function(val) {
if (val === '') val = null
this.updateProperties({ 'flowable:processCategory': val })
}
},
created() {
this.formData = commonParse(this.element)
},
methods: {
computedSignalLength() {
this.signalLength = this.element.businessObject.extensionElements?.values?.length ?? 0
},
finishSignal() {
if (this.dialogName === 'signalDialog') {
this.computedSignalLength()
}
this.dialogName = ''
}
}
}
</script>
<style>
</style>

194
anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/property/executionListener.vue

@ -0,0 +1,194 @@
<template>
<div>
<el-dialog
title="执行监听器"
:visible.sync="dialogVisible"
width="900px"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
@closed="$emit('close')"
>
<x-form ref="xForm" v-model="formData" :config="formConfig">
<template #params="scope">
<el-badge :value="scope.row.params ? scope.row.params.length : 0" type="primary">
<el-button size="small" @click="configParam(scope.$index)">配置</el-button>
</el-badge>
</template>
</x-form>
<span slot="footer" class="dialog-footer">
<el-button type="primary" size="medium" @click="closeDialog"> </el-button>
</span>
</el-dialog>
<listenerParam v-if="showParamDialog" :value="formData.executionListener[nowIndex].params" @close="finishConfigParam" />
</div>
</template>
<script>
import mixinPanel from '../../../common/mixinPanel'
import listenerParam from './listenerParam'
export default {
components: { listenerParam },
mixins: [mixinPanel],
data() {
return {
dialogVisible: true,
showParamDialog: false,
nowIndex: null,
formData: {
executionListener: []
}
}
},
computed: {
formConfig() {
// const _this = this
return {
inline: false,
item: [
{
xType: 'tabs',
tabs: [
{
label: '执行监听器',
name: 'executionListener',
column: [
{
label: '事件',
name: 'event',
width: 180,
rules: [{ required: true, message: '请选择', trigger: ['blur', 'change'] }],
xType: 'select',
dic: [
{ label: 'start', value: 'start' },
{ label: 'end', value: 'end' },
{ label: 'take', value: 'take' }
]
},
{
label: '类型',
name: 'type',
width: 180,
rules: [{ required: true, message: '请选择', trigger: ['blur', 'change'] }],
xType: 'select',
dic: [
{ label: '类', value: 'class' },
{ label: '表达式', value: 'expression' },
{ label: '委托表达式', value: 'delegateExpression' }
],
tooltip: `类:示例 com.company.MyCustomListener,自定义类必须实现 org.flowable.engine.delegate.TaskListener 接口 <br />
表达式示例 \${myObject.callMethod(task, task.eventName)} <br />
委托表达式示例 \${myListenerSpringBean} springBean 需要实现 org.flowable.engine.delegate.TaskListener 接口
`
},
{
label: 'java 类名',
name: 'className',
xType: 'input',
rules: [{ required: true, message: '请输入', trigger: ['blur', 'change'] }]
},
{
xType: 'slot',
label: '参数',
width: 120,
slot: true,
name: 'params'
}
]
}
]
}
]
}
}
},
mounted() {
this.formData.executionListener = this.element.businessObject.extensionElements?.values
.filter(item => item.$type === 'flowable:ExecutionListener')
.map(item => {
let type
if ('class' in item) type = 'class'
if ('expression' in item) type = 'expression'
if ('delegateExpression' in item) type = 'delegateExpression'
return {
event: item.event,
type: type,
className: item[type],
params: item.fields?.map(field => {
let fieldType
if ('stringValue' in field) fieldType = 'stringValue'
if ('expression' in field) fieldType = 'expression'
return {
name: field.name,
type: fieldType,
value: field[fieldType]
}
}) ?? []
}
}) ?? []
},
methods: {
configParam(index) {
this.nowIndex = index
const nowObj = this.formData.executionListener[index]
if (!nowObj.params) {
nowObj.params = []
}
this.showParamDialog = true
},
finishConfigParam(param) {
this.showParamDialog = false
// hack
const cache = this.formData.executionListener[this.nowIndex]
cache.params = param
this.$set(this.formData.executionListener[this.nowIndex], this.nowIndex, cache)
this.nowIndex = null
},
updateElement() {
if (this.formData.executionListener?.length) {
let extensionElements = this.element.businessObject.get('extensionElements')
if (!extensionElements) {
extensionElements = this.modeler.get('moddle').create('bpmn:ExtensionElements')
}
//
extensionElements.values = extensionElements.values?.filter(item => item.$type !== 'flowable:ExecutionListener') ?? []
this.formData.executionListener.forEach(item => {
const executionListener = this.modeler.get('moddle').create('flowable:ExecutionListener')
executionListener['event'] = item.event
executionListener[item.type] = item.className
if (item.params && item.params.length) {
item.params.forEach(field => {
const fieldElement = this.modeler.get('moddle').create('flowable:Field')
fieldElement['name'] = field.name
fieldElement[field.type] = field.value
// flowable.json stringexpressionStringhack
// const valueElement = this.modeler.get('moddle').create(`flowable:${field.type}`, { body: field.value })
// fieldElement[field.type] = valueElement
executionListener.get('fields').push(fieldElement)
})
}
extensionElements.get('values').push(executionListener)
})
this.updateProperties({ extensionElements: extensionElements })
} else {
const extensionElements = this.element.businessObject[`extensionElements`]
if (extensionElements) {
extensionElements.values = extensionElements.values?.filter(item => item.$type !== 'flowable:ExecutionListener') ?? []
}
}
},
closeDialog() {
this.$refs.xForm.validate().then(() => {
this.updateElement()
this.dialogVisible = false
}).catch(e => console.error(e))
}
}
}
</script>
<style>
.flow-containers .el-badge__content.is-fixed {
top: 18px;
}
</style>

96
anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/property/listenerParam.vue

@ -0,0 +1,96 @@
<template>
<div>
<el-dialog
title="监听器参数"
:visible.sync="dialogVisible"
width="700px"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
@closed="$emit('close', formData.paramList)"
>
<x-form ref="xForm" v-model="formData" :config="formConfig" />
<span slot="footer" class="dialog-footer">
<el-button type="primary" size="medium" @click="closeDialog"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import mixinXcrud from '../../../common/mixinXcrud'
export default {
mixins: [mixinXcrud],
props: {
value: {
type: Array,
default: () => []
}
},
data() {
return {
dialogVisible: true,
formData: {
paramList: this.value
}
}
},
computed: {
formConfig() {
return {
inline: false,
item: [
{
xType: 'tabs',
tabs: [
{
label: '监听器参数',
name: 'paramList',
column: [
{
label: '类型',
name: 'type',
width: 180,
rules: [{ required: true, message: '请选择', trigger: ['blur', 'change'] }],
xType: 'select',
dic: [
{ label: '字符串', value: 'stringValue' },
{ label: '表达式', value: 'expression' }
]
},
{
label: '名称',
name: 'name',
width: 180,
rules: [{ required: true, message: '请选择', trigger: ['blur', 'change'] }],
xType: 'input'
},
{
label: '值',
name: 'value',
xType: 'input',
rules: [{ required: true, message: '请输入', trigger: ['blur', 'change'] }]
}
]
}
]
}
]
}
}
},
methods: {
closeDialog() {
this.$refs.xForm.validate().then(() => {
this.dialogVisible = false
}).catch(e => console.error(e))
}
}
}
</script>
<style>
.flow-containers .el-badge__content.is-fixed {
top: 18px;
}
</style>

117
anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/property/multiInstance.vue

@ -0,0 +1,117 @@
<template>
<div>
<el-dialog
title="多实例配置"
:visible.sync="dialogVisible"
width="500px"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
class="muti-instance"
@closed="$emit('close')"
>
<el-alert
type="info"
:closable="false"
show-icon
style="margin-bottom: 20px"
>
<template #title>
按照BPMN2.0规范的要求用于为每个实例创建执行的父执行会提供下列变量:<br>
nrOfInstances实例总数<br>
nrOfActiveInstances当前活动的即未完成的实例数量对于顺序多实例这个值总为1<br>
nrOfCompletedInstances已完成的实例数量<br>
loopCounter给定实例在for-each循环中的index<br>
</template>
</el-alert>
<x-form ref="xForm" v-model="formData" :config="formConfig" />
</el-dialog>
</div>
</template>
<script>
import mixinPanel from '../../../common/mixinPanel'
import { formatJsonKeyValue } from '../../../common/parseElement'
export default {
mixins: [mixinPanel],
data() {
return {
dialogVisible: true,
formData: {}
}
},
computed: {
formConfig() {
const _this = this
return {
inline: false,
item: [
{
xType: 'input',
name: 'collection',
label: '集合',
tooltip: '属性会作为表达式进行解析。如果表达式解析为字符串而不是一个集合,<br />不论是因为本身配置的就是静态字符串值,还是表达式计算结果为字符串,<br />这个字符串都会被当做变量名,并从流程变量中用于获取实际的集合。'
},
{
xType: 'input',
name: 'elementVariable',
label: '元素变量',
tooltip: '每创建一个用户任务前,先以该元素变量为label,集合中的一项为value,<br />创建(局部)流程变量,该局部流程变量被用于指派用户任务。<br />一般来说,该字符串应与指定人员变量相同。'
},
{
xType: 'radio',
name: 'isSequential',
label: '执行方式',
dic: [{ label: '串行', value: true }, { label: '并行', value: false }]
},
{
xType: 'input',
name: 'completionCondition',
label: '完成条件',
tooltip: '多实例活动在所有实例都完成时结束,然而也可以指定一个表达式,在每个实例<br />结束时进行计算。当表达式计算为true时,将销毁所有剩余的实例,并结束多实例<br />活动,继续执行流程。例如 ${nrOfCompletedInstances/nrOfInstances >= 0.6 },<br />表示当任务完成60%时,该节点就算完成'
}
],
operate: [
{ text: '确定', show: true, click: _this.save },
{ text: '清空', show: true, click: () => { _this.formData = {} } }
]
}
}
},
mounted() {
const cache = JSON.parse(JSON.stringify(this.element.businessObject.loopCharacteristics ?? {}))
cache.completionCondition = cache.completionCondition?.body
this.formData = formatJsonKeyValue(cache)
},
methods: {
updateElement() {
if (this.formData.isSequential !== null && this.formData.isSequential !== undefined) {
let loopCharacteristics = this.element.businessObject.get('loopCharacteristics')
if (!loopCharacteristics) {
loopCharacteristics = this.modeler.get('moddle').create('bpmn:MultiInstanceLoopCharacteristics')
}
loopCharacteristics['isSequential'] = this.formData.isSequential
loopCharacteristics['collection'] = this.formData.collection
loopCharacteristics['elementVariable'] = this.formData.elementVariable
if (this.formData.completionCondition) {
const completionCondition = this.modeler.get('moddle').create('bpmn:Expression', { body: this.formData.completionCondition })
loopCharacteristics['completionCondition'] = completionCondition
}
this.updateProperties({ loopCharacteristics: loopCharacteristics })
} else {
delete this.element.businessObject.loopCharacteristics
}
},
save() {
this.updateElement()
this.dialogVisible = false
}
}
}
</script>
<style>
.muti-instance .el-form-item {
margin-bottom: 22px;
}
</style>

124
anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/property/signal.vue

@ -0,0 +1,124 @@
<template>
<div>
<el-dialog
title="信号定义"
:visible.sync="dialogVisible"
width="700px"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
@closed="$emit('close')"
>
<x-form ref="xForm" v-model="formData" :config="formConfig" />
<span slot="footer" class="dialog-footer">
<el-button type="primary" size="medium" @click="closeDialog"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import mixinPanel from '../../../common/mixinPanel'
export default {
mixins: [mixinPanel],
data() {
return {
dialogVisible: true,
formData: {
signal: []
}
}
},
computed: {
formConfig() {
// const _this = this
return {
inline: false,
item: [
{
xType: 'tabs',
tabs: [
{
label: '信号定义',
name: 'signal',
column: [
{
label: 'scope',
name: 'scope',
width: 180,
rules: [{ required: true, message: '请选择', trigger: ['blur', 'change'] }],
xType: 'select',
dic: [
{ label: '全局', value: 'start' },
{ label: '流程实例', value: 'end' }
]
},
{
label: 'id',
name: 'id',
width: 200,
rules: [{ required: true, message: '请输入', trigger: ['blur', 'change'] }],
xType: 'input'
},
{
label: '名称',
name: 'name',
xType: 'input',
rules: [{ required: true, message: '请输入', trigger: ['blur', 'change'] }]
}
]
}
]
}
]
}
}
},
mounted() {
// this.formData.signal = this.element.businessObject.extensionElements?.values.map(item => {
// let type
// if ('class' in item.$attrs) type = 'class'
// if ('expression' in item.$attrs) type = 'expression'
// if ('delegateExpression' in item.$attrs) type = 'delegateExpression'
// return {
// event: item.$attrs.event,
// type: type,
// className: item.$attrs[type]
// }
// }) ?? []
},
methods: {
updateElement() {
if (this.formData.signal?.length) {
let extensionElements = this.element.businessObject.get('extensionElements')
if (!extensionElements) {
extensionElements = this.modeler.get('moddle').create('bpmn:signal')
}
const length = extensionElements.get('values').length
for (let i = 0; i < length; i++) {
//
extensionElements.get('values').pop()
}
this.updateProperties({ extensionElements: extensionElements })
} else {
const extensionElements = this.element.businessObject[`extensionElements`]
if (extensionElements) {
extensionElements.values = extensionElements.values?.filter(item => item.$type !== 'flowable:ExecutionListener')
}
}
},
closeDialog() {
this.$refs.xForm.validate().then(() => {
this.updateElement()
this.dialogVisible = false
}).catch(e => console.error(e))
}
}
}
</script>
<style>
.flow-containers .el-badge__content.is-fixed {
top: 18px;
}
</style>

196
anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/property/taskListener.vue

@ -0,0 +1,196 @@
<template>
<div>
<el-dialog
title="任务监听器"
:visible.sync="dialogVisible"
width="900px"
:close-on-click-modal="false"
:close-on-press-escape="false"
:show-close="false"
@closed="$emit('close')"
>
<x-form ref="xForm" v-model="formData" :config="formConfig">
<template #params="scope">
<el-badge :value="scope.row.params ? scope.row.params.length : 0" type="primary">
<el-button size="small" @click="configParam(scope.$index)">配置</el-button>
</el-badge>
</template>
</x-form>
<span slot="footer" class="dialog-footer">
<el-button type="primary" size="medium" @click="closeDialog"> </el-button>
</span>
</el-dialog>
<listenerParam v-if="showParamDialog" :value="formData.taskListener[nowIndex].params" @close="finishConfigParam" />
</div>
</template>
<script>
import mixinPanel from '../../../common/mixinPanel'
import listenerParam from './listenerParam'
export default {
components: { listenerParam },
mixins: [mixinPanel],
data() {
return {
dialogVisible: true,
showParamDialog: false,
nowIndex: null,
formData: {
taskListener: []
}
}
},
computed: {
formConfig() {
// const _this = this
return {
inline: false,
item: [
{
xType: 'tabs',
tabs: [
{
label: '任务监听器',
name: 'taskListener',
column: [
{
label: '事件',
name: 'event',
width: 180,
rules: [{ required: true, message: '请选择', trigger: ['blur', 'change'] }],
xType: 'select',
dic: [
{ label: 'create', value: 'create' },
{ label: 'assignment', value: 'assignment' },
{ label: 'complete', value: 'complete' },
{ label: 'delete', value: 'delete' }
],
tooltip: `create(创建):当任务已经创建,并且所有任务参数都已经设置时触发。<br />
assignment指派当任务已经指派给某人时触发请注意当流程执行到达用户任务时在触发create事件之前会首先触发assignment事件<br />
complete完成当任务已经完成从运行时数据中删除前触发<br />
delete删除在任务即将被删除前触发请注意任务由completeTask正常完成时也会触发
`
},
{
label: '类型',
name: 'type',
width: 180,
rules: [{ required: true, message: '请选择', trigger: ['blur', 'change'] }],
xType: 'select',
dic: [
{ label: '类', value: 'class' },
{ label: '表达式', value: 'expression' },
{ label: '委托表达式', value: 'delegateExpression' }
]
},
{
label: 'java 类名',
name: 'className',
xType: 'input',
rules: [{ required: true, message: '请输入', trigger: ['blur', 'change'] }]
},
{
xType: 'slot',
label: '参数',
width: 120,
slot: true,
name: 'params'
}
]
}
]
}
]
}
}
},
mounted() {
this.formData.taskListener = this.element.businessObject.extensionElements?.values
.filter(item => item.$type === 'flowable:TaskListener')
.map(item => {
let type
if ('class' in item) type = 'class'
if ('expression' in item) type = 'expression'
if ('delegateExpression' in item) type = 'delegateExpression'
return {
event: item.event,
type: type,
className: item[type],
params: item.fields?.map(field => {
let fieldType
if ('stringValue' in field) fieldType = 'stringValue'
if ('expression' in field) fieldType = 'expression'
return {
name: field.name,
type: fieldType,
value: field[fieldType]
}
}) ?? []
}
}) ?? []
},
methods: {
configParam(index) {
this.nowIndex = index
const nowObj = this.formData.taskListener[index]
if (!nowObj.params) {
nowObj.params = []
}
this.showParamDialog = true
},
finishConfigParam(param) {
this.showParamDialog = false
// hack
const cache = this.formData.taskListener[this.nowIndex]
cache.params = param
this.$set(this.formData.taskListener[this.nowIndex], this.nowIndex, cache)
this.nowIndex = null
},
updateElement() {
if (this.formData.taskListener?.length) {
let extensionElements = this.element.businessObject.get('extensionElements')
if (!extensionElements) {
extensionElements = this.modeler.get('moddle').create('bpmn:ExtensionElements')
}
//
extensionElements.values = extensionElements.values?.filter(item => item.$type !== 'flowable:TaskListener') ?? []
this.formData.taskListener.forEach(item => {
const taskListener = this.modeler.get('moddle').create('flowable:TaskListener')
taskListener['event'] = item.event
taskListener[item.type] = item.className
if (item.params && item.params.length) {
item.params.forEach(field => {
const fieldElement = this.modeler.get('moddle').create('flowable:Field')
fieldElement['name'] = field.name
fieldElement[field.type] = field.value
// flowable.json stringexpressionStringhack
// const valueElement = this.modeler.get('moddle').create(`flowable:${field.type}`, { body: field.value })
// fieldElement[field.type] = valueElement
taskListener.get('fields').push(fieldElement)
})
}
extensionElements.get('values').push(taskListener)
})
this.updateProperties({ extensionElements: extensionElements })
} else {
const extensionElements = this.element.businessObject[`extensionElements`]
if (extensionElements) {
extensionElements.values = extensionElements.values?.filter(item => item.$type !== 'flowable:TaskListener') ?? []
}
}
},
closeDialog() {
this.$refs.xForm.validate().then(() => {
this.updateElement()
this.dialogVisible = false
}).catch(e => console.error(e))
}
}
}
</script>
<style>
.flow-containers .el-badge__content.is-fixed {
top: 18px;
}
</style>

92
anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/sequenceFlow.vue

@ -0,0 +1,92 @@
<template>
<div>
<x-form ref="xForm" v-model="formData" :config="formConfig">
<template #executionListener>
<el-badge :value="executionListenerLength">
<el-button size="small" @click="dialogName = 'executionListenerDialog'">编辑</el-button>
</el-badge>
</template>
</x-form>
<executionListenerDialog
v-if="dialogName === 'executionListenerDialog'"
:element="element"
:modeler="modeler"
@close="finishExecutionListener"
/>
</div>
</template>
<script>
import mixinPanel from '../../common/mixinPanel'
import mixinExecutionListener from '../../common/mixinExecutionListener'
import { commonParse, conditionExpressionParse } from '../../common/parseElement'
export default {
mixins: [mixinPanel, mixinExecutionListener],
data() {
return {
formData: {}
}
},
computed: {
formConfig() {
return {
inline: false,
item: [
{
xType: 'input',
name: 'id',
label: '节点 id',
rules: [{ required: true, message: 'Id 不能为空' }]
},
{
xType: 'input',
name: 'name',
label: '节点名称'
},
{
xType: 'input',
name: 'documentation',
label: '节点描述'
},
{
xType: 'slot',
name: 'executionListener',
label: '执行监听器'
},
{
xType: 'input',
name: 'conditionExpression',
label: '跳转条件'
},
{
xType: 'input',
name: 'skipExpression',
label: '跳过表达式'
}
]
}
}
},
watch: {
'formData.conditionExpression': function(val) {
if (val) {
const newCondition = this.modeler.get('moddle').create('bpmn:FormalExpression', { body: val })
this.updateProperties({ conditionExpression: newCondition })
} else {
this.updateProperties({ conditionExpression: null })
}
},
'formData.skipExpression': function(val) {
if (val === '') val = null
this.updateProperties({ 'flowable:skipExpression': val })
}
},
created() {
let cache = commonParse(this.element)
cache = conditionExpressionParse(cache)
this.formData = cache
}
}
</script>
<style></style>

94
anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/startEnd.vue

@ -0,0 +1,94 @@
<template>
<div>
<x-form ref="xForm" v-model="formData" :config="formConfig">
<template #executionListener>
<el-badge :value="executionListenerLength">
<el-button size="small" @click="dialogName = 'executionListenerDialog'">编辑</el-button>
</el-badge>
</template>
</x-form>
<executionListenerDialog
v-if="dialogName === 'executionListenerDialog'"
:element="element"
:modeler="modeler"
@close="finishExecutionListener"
/>
</div>
</template>
<script>
import mixinPanel from '../../common/mixinPanel'
import mixinExecutionListener from '../../common/mixinExecutionListener'
import { commonParse } from '../../common/parseElement'
export default {
mixins: [mixinPanel, mixinExecutionListener],
data() {
return {
formData: {}
}
},
computed: {
formConfig() {
const _this = this
return {
inline: false,
item: [
{
xType: 'input',
name: 'id',
label: '节点 id',
rules: [{ required: true, message: 'Id 不能为空' }]
},
{
xType: 'input',
name: 'name',
label: '节点名称'
},
{
xType: 'input',
name: 'documentation',
label: '节点描述'
},
{
xType: 'slot',
name: 'executionListener',
label: '执行监听器'
},
{
xType: 'input',
name: 'initiator',
label: '发起人',
show: !!_this.showConfig.initiator
},
{
xType: 'input',
name: 'formKey',
label: '表单标识key',
show: !!_this.showConfig.formKey
}
]
}
}
},
watch: {
'formData.initiator': function(val) {
if (val === '') val = null
//
// if (val === '') val = 'INITIATOR'
this.updateProperties({ 'flowable:initiator': val })
},
'formData.formKey': function(val) {
if (val === '') val = null
this.updateProperties({ 'flowable:formKey': val })
}
},
created() {
// this.updateProperties({ 'flowable:initiator': 'INITIATOR' })
this.formData = commonParse(this.element)
}
}
</script>
<style>
</style>

426
anrui-scm/anrui-scm-ui/src/views/Process/components/nodePanel/task.vue

@ -0,0 +1,426 @@
<template>
<div>
<x-form ref="xForm" v-model="formData" :config="formConfig">
<template #executionListener>
<el-badge :value="executionListenerLength">
<el-button size="small" @click="dialogName = 'executionListenerDialog'">编辑</el-button>
</el-badge>
</template>
<template #taskListener>
<el-badge :value="taskListenerLength">
<el-button size="small" @click="dialogName = 'taskListenerDialog'">编辑</el-button>
</el-badge>
</template>
<template #multiInstance>
<el-badge :is-dot="hasMultiInstance">
<el-button size="small" @click="dialogName = 'multiInstanceDialog'">编辑</el-button>
</el-badge>
</template>
</x-form>
<executionListenerDialog
v-if="dialogName === 'executionListenerDialog'"
:element="element"
:modeler="modeler"
@close="finishExecutionListener"
/>
<taskListenerDialog
v-if="dialogName === 'taskListenerDialog'"
:element="element"
:modeler="modeler"
@close="finishTaskListener"
/>
<multiInstanceDialog
v-if="dialogName === 'multiInstanceDialog'"
:element="element"
:modeler="modeler"
@close="finishMultiInstance"
/>
</div>
</template>
<script>
import mixinPanel from '../../common/mixinPanel'
import executionListenerDialog from './property/executionListener'
import taskListenerDialog from './property/taskListener'
import multiInstanceDialog from './property/multiInstance'
import { commonParse, userTaskParse } from '../../common/parseElement'
export default {
components: {
executionListenerDialog,
taskListenerDialog,
multiInstanceDialog
},
mixins: [mixinPanel],
props: {
users: {
type: Array,
required: true
},
groups: {
type: Array,
required: true
}
},
data() {
return {
userTypeOption: [
{ label: '指定人员', value: 'assignee' },
{ label: '候选人员', value: 'candidateUsers' },
{ label: '候选组', value: 'candidateGroups' }
],
dataTypeOption: [
{ label: '固定', value: 'fixed' },
{ label: '动态', value: 'dynamic' }
],
dialogName: '',
executionListenerLength: 0,
taskListenerLength: 0,
hasMultiInstance: false,
formData: {}
}
},
computed: {
formConfig() {
const _this = this
return {
inline: false,
item: [
{
xType: 'input',
name: 'id',
label: '节点 id',
rules: [{ required: true, message: 'Id 不能为空' }]
},
{
xType: 'input',
name: 'name',
label: '节点名称',
rules: [{ required: true, message: '节点名称不能为空' }]
},
{
xType: 'input',
name: 'documentation',
label: '节点描述'
},
{
xType: 'slot',
name: 'executionListener',
label: '执行监听器'
},
{
xType: 'slot',
name: 'taskListener',
label: '任务监听器',
show: !!_this.showConfig.taskListener
},
{
xType: 'select',
name: 'userType',
label: '人员类型',
dic: _this.userTypeOption,
show: !!_this.showConfig.userType
},
{
xType: 'radio',
name: 'dataType',
label: '指定方式',
dic: _this.dataTypeOption,
show: !!_this.showConfig.dataType,
rules: [{ required: true, message: '请指定方式' }]
},
// {
// 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',
label: '指定人员',
allowCreate: true,
filterable: true,
dic: { data: _this.users, label: 'nickName', value: 'userId' },
show: !!_this.showConfig.assignee && _this.formData.userType === 'assignee'
},
{
xType: 'select',
name: 'candidateUsers',
label: '候选人员',
multiple: true,
allowCreate: true,
filterable: true,
dic: { data: _this.users, label: 'nickName', value: 'userId' },
show: !!_this.showConfig.candidateUsers && _this.formData.userType === 'candidateUsers'
},
{
xType: 'select',
name: 'candidateGroups',
label: '候选组',
multiple: true,
allowCreate: true,
filterable: true,
dic: { data: _this.groups, label: 'roleName', value: 'roleId' },
show: !!_this.showConfig.candidateGroups && _this.formData.userType === 'candidateGroups'
},
{
xType: 'slot',
name: 'multiInstance',
label: '多实例'
},
{
xType: 'switch',
name: 'async',
label: '异步',
activeText: '是',
inactiveText: '否',
show: !!_this.showConfig.async
},
{
xType: 'input',
name: 'priority',
label: '优先级',
show: !!_this.showConfig.priority
},
{
xType: 'input',
name: 'formKey',
label: '表单标识key',
show: !!_this.showConfig.formKey
},
{
xType: 'input',
name: 'skipExpression',
label: '跳过表达式',
show: !!_this.showConfig.skipExpression
},
{
xType: 'switch',
name: 'isForCompensation',
label: '是否为补偿',
activeText: '是',
inactiveText: '否',
show: !!_this.showConfig.isForCompensation
},
{
xType: 'switch',
name: 'triggerable',
label: '服务任务可触发',
activeText: '是',
inactiveText: '否',
show: !!_this.showConfig.triggerable
},
{
xType: 'switch',
name: 'autoStoreVariables',
label: '自动存储变量',
activeText: '是',
inactiveText: '否',
show: !!_this.showConfig.autoStoreVariables
},
{
xType: 'input',
name: 'ruleVariablesInput',
label: '输入变量',
show: !!_this.showConfig.ruleVariablesInput
},
{
xType: 'input',
name: 'rules',
label: '规则',
show: !!_this.showConfig.rules
},
{
xType: 'input',
name: 'resultVariable',
label: '结果变量',
show: !!_this.showConfig.resultVariable
},
{
xType: 'switch',
name: 'exclude',
label: '排除',
activeText: '是',
inactiveText: '否',
show: !!_this.showConfig.exclude
},
{
xType: 'input',
name: 'class',
label: '类',
show: !!_this.showConfig.class
},
{
xType: 'datePicker',
type: 'datetime',
name: 'dueDate',
label: '到期时间',
show: !!_this.showConfig.dueDate
}
]
}
}
},
watch: {
'formData.userType': function(val, oldVal) {
if (oldVal) {
const types = ['assignee', 'candidateUsers', 'candidateGroups']
types.forEach(type => {
delete this.element.businessObject.$attrs[`flowable:${type}`]
delete this.formData[type]
})
}
},
//
'formData.dataType': function(val) {
const that = this
this.updateProperties({'flowable:dataType': val})
if (val === 'dynamic') {
this.updateProperties({'flowable:userType': that.formData.userType})
}
//
const types = ['assignee', 'candidateUsers', 'candidateGroups']
types.forEach(type => {
delete this.element.businessObject.$attrs[`flowable:${type}`]
delete this.formData[type]
})
//
const params = {
dataType: val,
userType: this.formData.userType
}
this.$emit('dataType', params)
},
'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(',')})
},
'formData.candidateGroups': function(val) {
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
this.updateProperties({ 'flowable:async': true })
},
'formData.dueDate': function(val) {
if (val === '') val = null
this.updateProperties({ 'flowable:dueDate': val })
},
'formData.formKey': function(val) {
if (val === '') val = null
this.updateProperties({ 'flowable:formKey': val })
},
'formData.priority': function(val) {
if (val === '') val = null
this.updateProperties({ 'flowable:priority': val })
},
'formData.skipExpression': function(val) {
if (val === '') val = null
this.updateProperties({ 'flowable:skipExpression': val })
},
'formData.isForCompensation': function(val) {
if (val === '') val = null
this.updateProperties({ 'isForCompensation': val })
},
'formData.triggerable': function(val) {
if (val === '') val = null
this.updateProperties({ 'flowable:triggerable': val })
},
'formData.class': function(val) {
if (val === '') val = null
this.updateProperties({ 'flowable:class': val })
},
'formData.autoStoreVariables': function(val) {
if (val === '') val = null
this.updateProperties({ 'flowable:autoStoreVariables': val })
},
'formData.exclude': function(val) {
if (val === '') val = null
this.updateProperties({ 'flowable:exclude': val })
},
'formData.ruleVariablesInput': function(val) {
if (val === '') val = null
this.updateProperties({ 'flowable:ruleVariablesInput': val })
},
'formData.rules': function(val) {
if (val === '') val = null
this.updateProperties({ 'flowable:rules': val })
},
'formData.resultVariable': function(val) {
if (val === '') val = null
this.updateProperties({ 'flowable:resultVariable': val })
}
},
created() {
let cache = commonParse(this.element)
cache = userTaskParse(cache)
this.formData = cache
this.computedExecutionListenerLength()
this.computedTaskListenerLength()
this.computedHasMultiInstance()
},
methods: {
computedExecutionListenerLength() {
this.executionListenerLength = this.element.businessObject.extensionElements?.values
?.filter(item => item.$type === 'flowable:ExecutionListener').length ?? 0
},
computedTaskListenerLength() {
this.taskListenerLength = this.element.businessObject.extensionElements?.values
?.filter(item => item.$type === 'flowable:TaskListener').length ?? 0
},
computedHasMultiInstance() {
if (this.element.businessObject.loopCharacteristics) {
this.hasMultiInstance = true
} else {
this.hasMultiInstance = false
}
},
finishExecutionListener() {
if (this.dialogName === 'executionListenerDialog') {
this.computedExecutionListenerLength()
}
this.dialogName = ''
},
finishTaskListener() {
if (this.dialogName === 'taskListenerDialog') {
this.computedTaskListenerLength()
}
this.dialogName = ''
},
finishMultiInstance() {
if (this.dialogName === 'multiInstanceDialog') {
this.computedHasMultiInstance()
}
this.dialogName = ''
}
}
}
</script>
<style></style>

1194
anrui-scm/anrui-scm-ui/src/views/Process/flowable/flowable.json

File diff suppressed because it is too large

24
anrui-scm/anrui-scm-ui/src/views/Process/flowable/init.js

@ -0,0 +1,24 @@
function randomStr() {
return Math.random().toString(36).slice(-8)
}
export default function() {
return `<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:bioc="http://bpmn.io/schema/bpmn/biocolor/1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" targetNamespace="http://www.flowable.org/processdef">
<process id="process_${randomStr()}" name="name_${randomStr()}">
<startEvent id="startNode1" name="开始" />
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_flow">
<bpmndi:BPMNPlane id="BPMNPlane_flow" bpmnElement="T-2d89e7a3-ba79-4abd-9f64-ea59621c258c">
<bpmndi:BPMNShape id="BPMNShape_startNode1" bpmnElement="startNode1" bioc:stroke="">
<omgdc:Bounds x="240" y="200" width="30" height="30" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="242" y="237" width="23" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
`
}

55
anrui-scm/anrui-scm-ui/src/views/Process/flowable/showConfig.js

@ -0,0 +1,55 @@
export default {
'bpmn:EndEvent': {},
'bpmn:StartEvent': {
initiator: true,
formKey: true
},
'bpmn:UserTask': {
userType: true,
dataType: true,
assignee: true,
candidateUsers: true,
candidateGroups: true,
// assigneeFixed: true,
// candidateUsersFixed: true,
// candidateGroupsFixed: true,
async: true,
priority: true,
formKey: true,
skipExpression: true,
dueDate: true,
taskListener: true
},
'bpmn:ServiceTask': {
async: true,
skipExpression: true,
isForCompensation: true,
triggerable: true,
class: true
},
'bpmn:ScriptTask': {
async: true,
isForCompensation: true,
autoStoreVariables: true
},
'bpmn:ManualTask': {
async: true,
isForCompensation: true
},
'bpmn:ReceiveTask': {
async: true,
isForCompensation: true
},
'bpmn:SendTask': {
async: true,
isForCompensation: true
},
'bpmn:BusinessRuleTask': {
async: true,
isForCompensation: true,
ruleVariablesInput: true,
rules: true,
resultVariable: true,
exclude: true
}
}

5
anrui-scm/anrui-scm-ui/src/views/Process/index.js

@ -0,0 +1,5 @@
import workflowBpmnModeler from './index.vue'
workflowBpmnModeler.install = Vue => Vue.component(workflowBpmnModeler.name, workflowBpmnModeler) // 给组件配置install方法
export default workflowBpmnModeler

467
anrui-scm/anrui-scm-ui/src/views/Process/index.vue

@ -0,0 +1,467 @@
<template>
<div v-loading="isView" class="flow-containers" :class="{ 'view-mode': isView }">
<el-container style="height: 100%">
<el-header style="border-bottom: 1px solid rgb(218 218 218);height: auto;">
<div style="display: flex; padding: 10px 0px; justify-content: space-between;">
{{xml}} <div>
<el-upload action="" :before-upload="openBpmn" style="margin-right: 10px; display:inline-block;">
<el-tooltip effect="dark" content="加载xml" placement="bottom">
<el-button size="mini" icon="el-icon-folder-opened" />
</el-tooltip>
</el-upload>
<el-tooltip effect="dark" content="新建" placement="bottom">
<el-button size="mini" icon="el-icon-circle-plus" @click="newDiagram" />
</el-tooltip>
<el-tooltip effect="dark" content="自适应屏幕" placement="bottom">
<el-button size="mini" icon="el-icon-rank" @click="fitViewport" />
</el-tooltip>
<el-tooltip effect="dark" content="放大" placement="bottom">
<el-button size="mini" icon="el-icon-zoom-in" @click="zoomViewport(true)" />
</el-tooltip>
<el-tooltip effect="dark" content="缩小" placement="bottom">
<el-button size="mini" icon="el-icon-zoom-out" @click="zoomViewport(false)" />
</el-tooltip>
<el-tooltip effect="dark" content="后退" placement="bottom">
<el-button size="mini" icon="el-icon-back" @click="modeler.get('commandStack').undo()" />
</el-tooltip>
<el-tooltip effect="dark" content="前进" placement="bottom">
<el-button size="mini" icon="el-icon-right" @click="modeler.get('commandStack').redo()" />
</el-tooltip>
</div>
<div>
<el-button size="mini" icon="el-icon-view" @click="showXML">查看xml</el-button>
<el-button size="mini" icon="el-icon-download" @click="saveXML(true)">下载xml</el-button>
<el-button size="mini" icon="el-icon-picture" @click="saveImg('svg', true)">下载svg</el-button>
<el-button size="mini" type="primary" @click="save">保存模型</el-button>
</div>
</div>
</el-header>
<el-container style="align-items: stretch">
<el-main style="padding: 0;">
<div ref="canvas" class="canvas" />
</el-main>
<el-aside style="width: 400px; min-height: 650px; background-color: #f0f2f5">
<panel v-if="modeler" :modeler="modeler" :users="users" :groups="groups" :categorys="categorys" @dataType="dataType" />
</el-aside>
</el-container>
</el-container>
</div>
</template>
<script>
//
import customTranslate from './common/customTranslate'
import Modeler from 'bpmn-js/lib/Modeler'
import panel from './PropertyPanel'
import BpmData from './BpmData'
import getInitStr from './flowable/init'
// flowable
import flowableModdle from './flowable/flowable.json'
export default {
name: 'WorkflowBpmnModeler',
components: {
panel
},
props: {
xml: {
type: String,
default: ''
},
users: {
type: Array,
default: () => []
},
groups: {
type: Array,
default: () => []
},
categorys: {
type: Array,
default: () => []
},
isView: {
type: Boolean,
default: false
},
taskList: {
type: Array,
default: () => []
}
},
data() {
return {
modeler: null,
zoom: 1
}
},
watch: {
xml: function(val) {
if (val) {
this.createNewDiagram(val)
}
}
},
mounted() {
//
this.modeler = new Modeler({
container: this.$refs.canvas,
additionalModules: [
{
translate: ['value', customTranslate]
}
],
moddleExtensions: {
flowable: flowableModdle
}
})
console.log('1111111111111111111111111111111');
console.log(this.xml);
console.log(this.taskList);
console.log('1111111111111111111111111111122');
//
if (!this.xml) {
this.newDiagram()
} else {
this.createNewDiagram(this.xml)
}
},
methods: {
newDiagram() {
this.createNewDiagram(getInitStr())
},
//
fitViewport() {
this.zoom = this.modeler.get('canvas').zoom('fit-viewport')
const bbox = document.querySelector('.flow-containers .viewport').getBBox()
const currentViewbox = this.modeler.get('canvas').viewbox()
const elementMid = {
x: bbox.x + bbox.width / 2 - 65,
y: bbox.y + bbox.height / 2
}
this.modeler.get('canvas').viewbox({
x: elementMid.x - currentViewbox.width / 2,
y: elementMid.y - currentViewbox.height / 2,
width: currentViewbox.width,
height: currentViewbox.height
})
this.zoom = bbox.width / currentViewbox.width * 1.8
},
//
zoomViewport(zoomIn = true) {
this.zoom = this.modeler.get('canvas').zoom()
this.zoom += (zoomIn ? 0.1 : -0.1)
this.modeler.get('canvas').zoom(this.zoom)
},
async createNewDiagram(data) {
//
// data = data.replace(/<!\[CDATA\[(.+?)]]>/g, '&lt;![CDATA[$1]]&gt;')
data = data.replace(/<!\[CDATA\[(.+?)]]>/g, function(match, str) {
return str.replace(/</g, '&lt;')
})
try {
await this.modeler.importXML(data)
this.adjustPalette()
this.fitViewport()
if (this.taskList !==undefined && this.taskList.length > 0 ) {
this.fillColor()
}
} catch (err) {
console.error(err.message, err.warnings)
}
},
//
adjustPalette() {
try {
// bpmn
const canvas = this.$refs.canvas
const djsPalette = canvas.children[0].children[1].children[4]
const djsPalStyle = {
width: '130px',
padding: '5px',
background: 'white',
left: '20px',
borderRadius: 0
}
for (var key in djsPalStyle) {
djsPalette.style[key] = djsPalStyle[key]
}
const palette = djsPalette.children[0]
const allGroups = palette.children
allGroups[0].style['display'] = 'none'
//
for (var gKey in allGroups) {
const group = allGroups[gKey]
for (var cKey in group.children) {
const control = group.children[cKey]
const controlStyle = {
display: 'flex',
justifyContent: 'flex-start',
alignItems: 'center',
width: '100%',
padding: '5px'
}
if (
control.className &&
control.dataset &&
control.className.indexOf('entry') !== -1
) {
const controlProps = new BpmData().getControl(
control.dataset.action
)
control.innerHTML = `<div style='font-size: 14px;font-weight:500;margin-left:15px;'>${
controlProps['title']
}</div>`
for (var csKey in controlStyle) {
control.style[csKey] = controlStyle[csKey]
}
}
}
}
} catch (e) {
console.log(e)
}
},
fillColor() {
const canvas = this.modeler.get('canvas')
this.modeler._definitions.rootElements[0].flowElements.forEach(n => {
const completeTask = this.taskList.find(m => m.key === n.id)
const todoTask = this.taskList.find(m => !m.completed)
const endTask = this.taskList[this.taskList.length - 1]
if (n.$type === 'bpmn:UserTask') {
if (completeTask) {
canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo')
n.outgoing?.forEach(nn => {
const targetTask = this.taskList.find(m => m.key === nn.targetRef.id)
if (targetTask) {
if (todoTask && completeTask.key === todoTask.key && !todoTask.completed){
canvas.addMarker(nn.id, todoTask.completed ? 'highlight' : 'highlight-todo')
canvas.addMarker(nn.targetRef.id, todoTask.completed ? 'highlight' : 'highlight-todo')
}else {
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo')
canvas.addMarker(nn.targetRef.id, targetTask.completed ? 'highlight' : 'highlight-todo')
}
}
})
}
}
//
else if (n.$type === 'bpmn:ExclusiveGateway') {
if (completeTask) {
canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo')
n.outgoing?.forEach(nn => {
const targetTask = this.taskList.find(m => m.key === nn.targetRef.id)
if (targetTask) {
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo')
canvas.addMarker(nn.targetRef.id, targetTask.completed ? 'highlight' : 'highlight-todo')
}
})
}
}
//
else if (n.$type === 'bpmn:ParallelGateway') {
if (completeTask) {
canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo')
n.outgoing?.forEach(nn => {
debugger
const targetTask = this.taskList.find(m => m.key === nn.targetRef.id)
if (targetTask) {
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo')
canvas.addMarker(nn.targetRef.id, targetTask.completed ? 'highlight' : 'highlight-todo')
}
})
}
}
else if (n.$type === 'bpmn:StartEvent') {
n.outgoing.forEach(nn => {
const completeTask = this.taskList.find(m => m.key === nn.targetRef.id)
if (completeTask) {
canvas.addMarker(nn.id, 'highlight')
canvas.addMarker(n.id, 'highlight')
return
}
})
}
else if (n.$type === 'bpmn:EndEvent') {
if (endTask.key === n.id && endTask.completed) {
canvas.addMarker(n.id, 'highlight')
return
}
}
})
},
// api
getProcess() {
const element = this.getProcessElement()
return {
id: element.id,
name: element.name,
category: element.$attrs['flowable:processCategory']
}
},
getProcessElement() {
const rootElements = this.modeler.getDefinitions().rootElements
for (let i = 0; i < rootElements.length; i++) {
if (rootElements[i].$type === 'bpmn:Process') return rootElements[i]
}
},
async saveXML(download = false) {
try {
const { xml } = await this.modeler.saveXML({ format: true })
if (download) {
this.downloadFile(`${this.getProcessElement().name}.bpmn20.xml`, xml, 'application/xml')
}
return xml
} catch (err) {
console.log(err)
}
},
async showXML() {
try {
const { xml } = await this.modeler.saveXML({ format: true })
debugger
this.$emit('showXML',xml)
} catch (err) {
console.log(err)
}
},
async saveImg(type = 'svg', download = false) {
try {
const { svg } = await this.modeler.saveSVG({ format: true })
if (download) {
this.downloadFile(this.getProcessElement().name, svg, 'image/svg+xml')
}
return svg
} catch (err) {
console.log(err)
}
},
async save() {
const process = this.getProcess()
const xml = await this.saveXML()
const svg = await this.saveImg()
const result = { process, xml, svg }
this.$emit('save', result)
window.parent.postMessage(result, '*')
},
openBpmn(file) {
const reader = new FileReader()
reader.readAsText(file, 'utf-8')
reader.onload = () => {
this.createNewDiagram(reader.result)
}
return false
},
downloadFile(filename, data, type) {
var a = document.createElement('a')
var url = window.URL.createObjectURL(new Blob([data], { type: type }))
a.href = url
a.download = filename
a.click()
window.URL.revokeObjectURL(url)
},
/** 获取数据类型 */
dataType(data){
this.$emit('dataType', data)
}
}
}
</script>
<style lang="scss">
/*左边工具栏以及编辑节点的样式*/
@import "~bpmn-js/dist/assets/diagram-js.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css";
@import "~bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css";
.view-mode {
.el-header, .el-aside, .djs-palette, .bjs-powered-by {
display: none;
}
.el-loading-mask {
background-color: initial;
}
.el-loading-spinner {
display: none;
}
}
.flow-containers {
// background-color: #ffffff;
width: 100%;
height: 100%;
.canvas {
width: 100%;
height: 100%;
}
.panel {
position: absolute;
right: 0;
top: 50px;
width: 300px;
}
.load {
margin-right: 10px;
}
.el-form-item__label{
font-size: 13px;
}
.djs-palette{
left: 0px!important;
top: 0px;
border-top: none;
}
.djs-container svg {
min-height: 650px;
}
.highlight.djs-shape .djs-visual > :nth-child(1) {
fill: green !important;
stroke: green !important;
fill-opacity: 0.2 !important;
}
.highlight.djs-shape .djs-visual > :nth-child(2) {
fill: green !important;
}
.highlight.djs-shape .djs-visual > path {
fill: green !important;
fill-opacity: 0.2 !important;
stroke: green !important;
}
.highlight.djs-connection > .djs-visual > path {
stroke: green !important;
}
// .djs-connection > .djs-visual > path {
// stroke: orange !important;
// stroke-dasharray: 4px !important;
// fill-opacity: 0.2 !important;
// }
// .djs-shape .djs-visual > :nth-child(1) {
// fill: orange !important;
// stroke: orange !important;
// stroke-dasharray: 4px !important;
// fill-opacity: 0.2 !important;
// }
.highlight-todo.djs-connection > .djs-visual > path {
stroke: orange !important;
stroke-dasharray: 4px !important;
fill-opacity: 0.2 !important;
}
.highlight-todo.djs-shape .djs-visual > :nth-child(1) {
fill: orange !important;
stroke: orange !important;
stroke-dasharray: 4px !important;
fill-opacity: 0.2 !important;
}
.overlays-div {
font-size: 10px;
color: red;
width: 100px;
top: -20px !important;
}
}
</style>

227
anrui-scm/anrui-scm-ui/src/views/Process/lang/zh.js

@ -0,0 +1,227 @@
export default {
// Labels
'Activate the global connect tool': '激活全局连接工具',
'Append {type}': '添加 {type}',
'Add Lane above': '在上面添加道',
'Divide into two Lanes': '分割成两个道',
'Divide into three Lanes': '分割成三个道',
'Add Lane below': '在下面添加道',
'Append compensation activity': '追加补偿活动',
'Change type': '修改类型',
'Connect using Association': '使用关联连接',
'Connect using Sequence/MessageFlow or Association': '使用顺序/消息流或者关联连接',
'Connect using DataInputAssociation': '使用数据输入关联连接',
'Remove': '移除',
'Activate the hand tool': '激活抓手工具',
'Activate the lasso tool': '激活套索工具',
'Activate the create/remove space tool': '激活创建/删除空间工具',
'Create expanded SubProcess': '创建扩展子过程',
'Create IntermediateThrowEvent/BoundaryEvent': '创建中间抛出事件/边界事件',
'Create Pool/Participant': '创建池/参与者',
'Parallel Multi Instance': '并行多重事件',
'Sequential Multi Instance': '时序多重事件',
'DataObjectReference': '数据对象参考',
'DataStoreReference': '数据存储参考',
'Loop': '循环',
'Ad-hoc': '即席',
'Create {type}': '创建 {type}',
'Task': '任务',
'Send Task': '发送任务',
'Receive Task': '接收任务',
'User Task': '用户任务',
'Manual Task': '手工任务',
'Business Rule Task': '业务规则任务',
'Service Task': '服务任务',
'Script Task': '脚本任务',
'Call Activity': '调用活动',
'Sub Process (collapsed)': '子流程(折叠的)',
'Sub Process (expanded)': '子流程(展开的)',
'Start Event': '开始事件',
'StartEvent': '开始事件',
'Intermediate Throw Event': '中间事件',
'End Event': '结束事件',
'EndEvent': '结束事件',
'Create Gateway': '创建网关',
'Create Intermediate/Boundary Event': '创建中间/边界事件',
'Message Start Event': '消息开始事件',
'Timer Start Event': '定时开始事件',
'Conditional Start Event': '条件开始事件',
'Signal Start Event': '信号开始事件',
'Error Start Event': '错误开始事件',
'Escalation Start Event': '升级开始事件',
'Compensation Start Event': '补偿开始事件',
'Message Start Event (non-interrupting)': '消息开始事件(非中断)',
'Timer Start Event (non-interrupting)': '定时开始事件(非中断)',
'Conditional Start Event (non-interrupting)': '条件开始事件(非中断)',
'Signal Start Event (non-interrupting)': '信号开始事件(非中断)',
'Escalation Start Event (non-interrupting)': '升级开始事件(非中断)',
'Message Intermediate Catch Event': '消息中间捕获事件',
'Message Intermediate Throw Event': '消息中间抛出事件',
'Timer Intermediate Catch Event': '定时中间捕获事件',
'Escalation Intermediate Throw Event': '升级中间抛出事件',
'Conditional Intermediate Catch Event': '条件中间捕获事件',
'Link Intermediate Catch Event': '链接中间捕获事件',
'Link Intermediate Throw Event': '链接中间抛出事件',
'Compensation Intermediate Throw Event': '补偿中间抛出事件',
'Signal Intermediate Catch Event': '信号中间捕获事件',
'Signal Intermediate Throw Event': '信号中间抛出事件',
'Message End Event': '消息结束事件',
'Escalation End Event': '定时结束事件',
'Error End Event': '错误结束事件',
'Cancel End Event': '取消结束事件',
'Compensation End Event': '补偿结束事件',
'Signal End Event': '信号结束事件',
'Terminate End Event': '终止结束事件',
'Message Boundary Event': '消息边界事件',
'Message Boundary Event (non-interrupting)': '消息边界事件(非中断)',
'Timer Boundary Event': '定时边界事件',
'Timer Boundary Event (non-interrupting)': '定时边界事件(非中断)',
'Escalation Boundary Event': '升级边界事件',
'Escalation Boundary Event (non-interrupting)': '升级边界事件(非中断)',
'Conditional Boundary Event': '条件边界事件',
'Conditional Boundary Event (non-interrupting)': '条件边界事件(非中断)',
'Error Boundary Event': '错误边界事件',
'Cancel Boundary Event': '取消边界事件',
'Signal Boundary Event': '信号边界事件',
'Signal Boundary Event (non-interrupting)': '信号边界事件(非中断)',
'Compensation Boundary Event': '补偿边界事件',
'Exclusive Gateway': '互斥网关',
'Parallel Gateway': '并行网关',
'Inclusive Gateway': '相容网关',
'Complex Gateway': '复杂网关',
'Event based Gateway': '事件网关',
'Transaction': '转运',
'Sub Process': '子流程',
'Event Sub Process': '事件子流程',
'Collapsed Pool': '折叠池',
'Expanded Pool': '展开池',
// Errors
'no parent for {element} in {parent}': '在{parent}里,{element}没有父类',
'no shape type specified': '没有指定的形状类型',
'flow elements must be children of pools/participants': '流元素必须是池/参与者的子类',
'out of bounds release': 'out of bounds release',
'more than {count} child lanes': '子道大于{count} ',
'element required': '元素不能为空',
'diagram not part of bpmn:Definitions': '流程图不符合bpmn规范',
'no diagram to display': '没有可展示的流程图',
'no process or collaboration to display': '没有可展示的流程/协作',
'element {element} referenced by {referenced}#{property} not yet drawn': '由{referenced}#{property}引用的{element}元素仍未绘制',
'already rendered {element}': '{element} 已被渲染',
'failed to import {element}': '导入{element}失败',
// 属性面板的参数
'Id': '标识',
'Name': '名称',
'General': '常规',
'Details': '详情',
'Message Name': '消息名称',
'Message': '消息',
'Initiator': '创建者',
'Asynchronous Continuations': '持续异步',
'Asynchronous Before': '异步前',
'Asynchronous After': '异步后',
'Job Configuration': '工作配置',
'Exclusive': '排除',
'Job Priority': '工作优先级',
'Retry Time Cycle': '重试时间周期',
'Documentation': '文档',
'Element Documentation': '元素文档',
'History Configuration': '历史配置',
'History Time To Live': '历史的生存时间',
'Forms': '表单',
'Form Key': '表单key',
'Form Fields': '表单字段',
'Business Key': '业务key',
'Form Field': '表单字段',
'ID': '编号',
'Type': '类型',
'Label': '名称',
'Default Value': '默认值',
'Validation': '校验',
'Add Constraint': '添加约束',
'Config': '配置',
'Properties': '属性',
'Add Property': '添加属性',
'Value': '值',
'Listeners': '监听器',
'Execution Listener': '执行监听',
'Event Type': '事件类型',
'Listener Type': '监听器类型',
'Java Class': 'Java类',
'Expression': '表达式',
'Must provide a value': '必须提供一个值',
'Delegate Expression': '代理表达式',
'Script': '脚本',
'Script Format': '脚本格式',
'Script Type': '脚本类型',
'Inline Script': '内联脚本',
'External Script': '外部脚本',
'Resource': '资源',
'Field Injection': '字段注入',
'Extensions': '扩展',
'Input/Output': '输入/输出',
'Input Parameters': '输入参数',
'Output Parameters': '输出参数',
'Parameters': '参数',
'Output Parameter': '输出参数',
'Timer Definition Type': '定时器定义类型',
'Timer Definition': '定时器定义',
'Date': '日期',
'Duration': '持续',
'Cycle': '循环',
'Signal': '信号',
'Signal Name': '信号名称',
'Escalation': '升级',
'Error': '错误',
'Link Name': '链接名称',
'Condition': '条件名称',
'Variable Name': '变量名称',
'Variable Event': '变量事件',
'Specify more than one variable change event as a comma separated list.': '多个变量事件以逗号隔开',
'Wait for Completion': '等待完成',
'Activity Ref': '活动参考',
'Version Tag': '版本标签',
'Executable': '可执行文件',
'External Task Configuration': '扩展任务配置',
'Task Priority': '任务优先级',
'External': '外部',
'Connector': '连接器',
'Must configure Connector': '必须配置连接器',
'Connector Id': '连接器编号',
'Implementation': '实现方式',
'Field Injections': '字段注入',
'Fields': '字段',
'Result Variable': '结果变量',
'Topic': '主题',
'Configure Connector': '配置连接器',
'Input Parameter': '输入参数',
'Assignee': '代理人',
'Candidate Users': '候选用户',
'Candidate Groups': '候选组',
'Due Date': '到期时间',
'Follow Up Date': '跟踪日期',
'Priority': '优先级',
'The follow up date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)': '跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00',
'The due date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)': '跟踪日期必须符合EL表达式,如: ${someDate} ,或者一个ISO标准日期,如:2015-06-26T09:54:00',
'Variables': '变量'
}
export const NodeName = {
'bpmn:Process': '流程',
'bpmn:StartEvent': '开始事件',
'bpmn:IntermediateThrowEvent': '中间事件',
'bpmn:Task': '任务',
'bpmn:SendTask': '发送任务',
'bpmn:ReceiveTask': '接收任务',
'bpmn:UserTask': '用户任务',
'bpmn:ManualTask': '手工任务',
'bpmn:BusinessRuleTask': '业务规则任务',
'bpmn:ServiceTask': '服务任务',
'bpmn:ScriptTask': '脚本任务',
'bpmn:EndEvent': '结束事件',
'bpmn:SequenceFlow': '流程线',
'bpmn:ExclusiveGateway': '互斥网关',
'bpmn:ParallelGateway': '并行网关',
'bpmn:InclusiveGateway': '相容网关',
'bpmn:ComplexGateway': '复杂网关',
'bpmn:EventBasedGateway': '事件网关'
}

97
anrui-scm/anrui-scm-ui/src/views/baseoutsourcingapplication/baseoutsourcingapplicationAdd.vue

@ -165,6 +165,45 @@
<!-- 车型 -->
<modellibrary v-show="viewState == 1.2" ref="chexingxuanze" @handleChexing="selectChexing"></modellibrary>
<el-dialog title="选择待办人" :visible.sync="nodeDialogVisible" width="80%">
<el-form label-position="right" class="formadd" >
<el-row>
<el-col :span="4" class="tleftb">当前环节</el-col>
<el-col :span="20" class="trightb">
<el-form-item prop="purchasingUnitName"><span>{{firstNode.name}}->{{ nextNode.name }}</span></el-form-item>
</el-col>
</el-row>
<!-- <el-row>
<el-col :span="4" class="tleftb">意见</el-col>
<el-col :span="20" class="trightb">
<el-input size="small" v-model="formobj.comment" placeholder="审批意见" class="addinputw" clearable ></el-input>
</el-col>
</el-row>-->
<el-row>
<el-col :span="4" class="tleftb">下一步处理人</el-col>
<el-col :span="20" class="trightb">
{{this.userName}} <el-button type="primary" size="mini" @click="selectUser">选择</el-button>
</el-col>
</el-row>
<div style="text-align:center;margin-top: 20px;">
<el-button type="primary" size="mini" @click="agree"> </el-button>
<el-button type="info " size="mini" @click="closeNodeDialog"> </el-button>
</div>
</el-form>
</el-dialog>
<el-dialog title="选择用户" :visible.sync="nodeUserDialogVisible" width="80%">
<el-form label-position="right" class="formadd" >
<el-row>
<el-col :span="4" class="tleftb">选择动作</el-col>
<el-col :span="20" class="trightb" >
<el-radio-group v-model="userSid">
<el-radio v-for="item in users" :key="item.sid" :label="item.sid"
@change="checkedUser">{{ item.name }}</el-radio>
</el-radio-group>
</el-col>
</el-row>
</el-form>
</el-dialog>
</div>
</template>
<script>
@ -180,13 +219,32 @@ export default {
},
data() {
return {
firstNode:{},
nextNode:{},
roleSid:'',
userSid:'',
users:[],
nodeSid:'',
nodes:'',
nodeDialogVisible:false,
nodeUserDialogVisible:false,
selectedSids: '',
vehicleDialogVisible: false,
dialogVisible: false,
viewState: '1',
submitdisabled: false,
viewTitle: '',
agreeList: { //
businessSid: '',
comment: '',
instanceId: '',
taskId: '',
taskName: '',
nextNodeSid:'',
userSid: window.sessionStorage.getItem('userSid')
},
formobj: {
nextUserSid:'',
sid: '',
staffSid: window.sessionStorage.getItem('staffSid'),
applicationCode: '', //
@ -230,7 +288,9 @@ export default {
created() {
},
methods: {
closeNodeDialog(){
this.nodeDialogVisible = false
},
quedingVehicle() {
var obj = this.$refs['cheliang'].getSids()
var length = obj.length
@ -243,7 +303,6 @@ export default {
this.formobj.baseOutsourcingApplicationVehicleDtos = obj
this.vehicleDialogVisible = false
},
resetState() {
this.viewState = 1
this.handReset()
@ -353,20 +412,52 @@ export default {
var pageindex=index+1+pagestart;
return pageindex;*/
},
selectUser() {
this.nodeUserDialogVisible=true
req.getUsers( this.roleSid).then(res => {
this.users = res.data
})
},
checkedUser(val) {
const choosetItem = this.users.filter((item) => item.sid == val)
this.userName = choosetItem[0].name
this.formobj.nextUserSid = choosetItem[0].sid
this.nodeUserDialogVisible = false
},
doSubmit(row) {
//
this.submitdisabled = true
this.formobj.userSid = window.sessionStorage.getItem('userSid')
this.formobj.orgSid = this.$store.getters.orgSid
this.formobj.modelId = 'process_5tqysnjc:2:325008'
req.getNextNodesForSubmit(this.formobj).then(resp=>{
if (resp.success) {
this.nextNode=resp.data[1];
this.firstNode=resp.data[0];
var candidateGroups=this.nextNode.candidateGroups;
this.roleSid=candidateGroups[0];
console.log(resp.data);
this.nodeDialogVisible = true
}
this.submitdisabled = false
});
},
agree() {
req
.doSubmit(this.formobj)
.then(resp => {
this.submitdisabled = false
console.log(resp);
if (resp.success) {
//
this.$message({ showClose: true, type: 'success', message: resp.msg })
this.handleReturn('true')
/* window.parent.postMessage({
cmd: 'returnHeight',
params: {
code: 1,
data: document.body.scrollHeight + 'px'
}
}, '*')*/
} else {
// resp.code
}

218
anrui-scm/anrui-scm-ui/src/views/baseoutsourcingapplication/workflow/baseoutsourcingapplication.vue

@ -74,19 +74,77 @@
<el-table-column prop="vinNo" label="车架号" align="center" />
</el-table>
</div>
<span class="el-icon-picture-outline">流程图</span>
<flow :xmlData="xmlData" :taskData="taskList"></flow>
</el-form>
</div>
<el-dialog title="选择待办人" :visible.sync="nodeDialogVisible" width="80%">
<el-form label-position="right" class="formadd" >
<el-row>
<el-col :span="4" class="tleftb">当前环节</el-col>
<el-col :span="20" class="trightb">
<el-form-item prop="purchasingUnitName"><span>{{ agreeList.taskName }}->{{ nextNode.name }}</span></el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="4" class="tleftb">意见</el-col>
<el-col :span="20" class="trightb">
<el-input size="small" v-model="formobj.comment" placeholder="审批意见" class="addinputw" clearable ></el-input>
</el-col>
</el-row>
<el-row v-show="!endTask">
<el-col :span="4" class="tleftb">下一步处理人</el-col>
<el-col :span="20" class="trightb">
{{this.userName}} <el-button type="primary" size="mini" @click="selectUser">选择</el-button>
</el-col>
</el-row>
<div style="text-align:center;margin-top: 20px;">
<el-button type="primary" size="mini" @click="agree"> </el-button>
<el-button type="info " size="mini" @click="closeNodeDialog"> </el-button>
</div>
</el-form>
</el-dialog>
<el-dialog title="选择用户" :visible.sync="nodeUserDialogVisible" width="80%">
<el-form label-position="right" class="formadd" >
<el-row>
<el-col :span="4" class="tleftb">选择动作</el-col>
<el-col :span="20" class="trightb" >
<el-radio-group v-model="userSid">
<el-radio v-for="item in users" :key="item.sid" :label="item.sid"
@change="checkedUser">{{ item.name }}</el-radio>
</el-radio-group>
</el-col>
</el-row>
</el-form>
</el-dialog>
</div>
</template>
<script>
import flow from '@/views/baseoutsourcingapplication/workflow/flow'
import req from '@/api/baseoutsourcingapplication/baseoutsourcingapplication'
export default {
name: 'BaseOutsourcingApplicationInfo',
components: {
flow
},
data() {
return {
nodeDialogVisible:false,
nodeUserDialogVisible:false,
// xml
xmlData: "",
nodeSid: "",
// nextNodeSid:'',
nodes: [],
users:[],
userName:'',
userSid:'',
taskList: [],
endTask:true,
formobj: {
sid: '',
comment:'',
applicationCode: '123456', //
createByName: window.sessionStorage.getItem('name'), //
applicationDate: '', //
@ -107,11 +165,16 @@ export default {
orgSid: '', // sid
baseOutsourcingApplicationVehicleDtos:[]
},
nextNode:{},
agreeList: { //
businessSid: '',
comment: '',
instanceId: '',
taskId: '',
taskDefKey: '',
taskName: '',
nextNodeSid:'',
nextUserSid:'',
userSid: window.sessionStorage.getItem('userSid')
},
regectList: { //
@ -147,6 +210,8 @@ export default {
this.agreeList.businessSid = sid
this.agreeList.instanceId = obj.instanceId
this.agreeList.taskId = obj.taskId
this.agreeList.taskDefKey = obj.taskDefKey
this.agreeList.taskName = obj.taskName
//
this.regectList.businessSid = sid
this.regectList.instanceId = obj.instanceId
@ -155,8 +220,51 @@ export default {
this.stopList.businessSid = sid
this.stopList.instanceId = obj.instanceId
this.stopList.taskId = obj.taskId
this.getModelDetail(obj.deployId)
this.getFlowViewer(obj.instanceId)
this.getNextNodes(obj.taskId)
window.parent.postMessage({
cmd: 'returnHeight',
params: {
code: 2,
data: 1500 + 'px'
}
}, '*')
},
methods: {
closeNodeDialog(){
this.nodeDialogVisible = false
},
selectUser() {
this.nodeUserDialogVisible=true
req.getUsers(this.nextNode.candidateGroups[0]).then(res => {
this.users = res.data
})
},
checkedUser(val) {
const choosetItem = this.users.filter((item) => item.sid == val)
this.userName = choosetItem[0].name
this.nodeUserDialogVisible = false
},
/** xml 文件 */
getNextNodes(taskid) {
// xml
req.getNextNodes(taskid).then(res => {
this.nodes = res.data
})
},
/** xml 文件 */
getModelDetail(deployId) {
// xml
req.readXml(deployId).then(res =>{
this.xmlData = res.data
})
},
getFlowViewer(procInsId){
req.getFlowViewer(procInsId).then(res =>{
this.taskList = res.data
})
},
//
openStop() {
this.$confirm('是否确认执行终止操作', '提示', {
@ -193,21 +301,32 @@ export default {
},
//
openAgree() {
this.$confirm('是否确认执行同意操作', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
this.handleAgree()
})
.catch((e) => {
console.log(e);
this.$message({
type: 'info',
message: '已取消同意'
})
})
this.formobj.modelId = 'process_5tqysnjc:2:325008'
req.getNextNodesForSubmit(this.formobj).then(resp=>{
if (resp.success) {
var arr= resp.data;
for(var i=0;i<arr.length;i++){
if(i+1<arr.length&&this.agreeList.taskDefKey==arr[i].id){
this.nextNode=arr[i+1];
this.endTask=fasle;
}
}
if(this.endTask){
this.nextNode={
name:'结束'
}
}else{
/*this.nextNode=resp.data[1];
this.firstNode=resp.data[0];*/
var candidateGroups=this.nextNode.candidateGroups;
this.roleSid=candidateGroups[0];
}
this.nodeDialogVisible = true
}
this.submitdisabled = false
// this.handleAgree()
});
},
handleReturn() {
this.$emit('doback')
@ -249,31 +368,35 @@ export default {
},
/** 同意任务 */
handleAgree() {
console.log(this)
console.log(parent.parent)
/* this.agreeList.comment = ""; this.comment
req.agreeTask(this.agreeList).then((response) => {
if (response.code === '200') {
this.$notify({
title: '提示',
message: '执行成功',
type: 'success',
duration: 2000
})
// this.refreshIt()
} else {
this.$notify({
title: '提示',
message: '执行失败',
type: 'error',
duration: 2000
})
}
})*/
this.nodeDialogVisible = true
},
agree() {
this.agreeList.comment = this.formobj.comment
// this.agreeList.nextNodeSid = this.nextNodeSid
this.agreeList.nextUserSid = this.userSid
req.agreeTask(this.agreeList).then((response) => {
if (response.code === '200') {
this.nodeDialogVisible=false;
window.parent.postMessage({
cmd: 'returnHeight',
params: {
code: 1,
data: document.body.scrollHeight + 'px'
}
}, '*')
} else {
this.$notify({
title: '提示',
message: '执行失败',
type: 'error',
duration: 2000
})
}
})
},
/** 驳回任务 */
handleReject() {
this.regectList.comment = this.comment
this.regectList.comment = this.formobj.comment
req.rejectTask(this.regectList).then((response) => {
if (response.code === '200') {
this.$notify({
@ -282,6 +405,14 @@ export default {
type: 'success',
duration: 2000
})
this.nodeDialogVisible=false;
window.parent.postMessage({
cmd: 'returnHeight',
params: {
code: 1,
data: document.body.scrollHeight + 'px'
}
}, '*')
// this.refreshIt()
} else {
this.$notify({
@ -304,6 +435,14 @@ export default {
type: 'success',
duration: 2000
})
this.nodeDialogVisible=false;
window.parent.postMessage({
cmd: 'returnHeight',
params: {
code: 1,
data: document.body.scrollHeight + 'px'
}
}, '*')
// this.refreshIt()
} else {
this.$notify({
@ -326,4 +465,9 @@ export default {
.trightb{
font-size: 12px;
}
.box-card {
width: 100%;
margin-bottom: 20px;
}
</style>

32
anrui-scm/anrui-scm-ui/src/views/baseoutsourcingapplication/workflow/flow.vue

@ -0,0 +1,32 @@
<template>
<div>
<bpmn-modeler
ref="refNode"
:xml="xmlData"
:is-view="true"
:taskList="taskData"
/>
</div>
</template>
<script>
import bpmnModeler from '@/views/Process/index'
export default {
name: "Flow",
components: {
bpmnModeler
},
props: {
xmlData: {
type: String,
default: ''
},
taskData: {
type: Array,
default: () => []
}
},
data() {
return {};
}
};
</script>

44
anrui-system-ui/src/views/flow/todoList.vue

@ -34,10 +34,10 @@
<el-table :key="tableKey" v-loading="listLoading" :data="list" border style="width:100%"
@selection-change="handleSelectionChange">
<el-table-column width="60px" label="序号" type="index" :index="tableKey+1" align="center"/>
<el-table-column width="150px" label="操作" align="center">
<el-table-column width="100px" label="操作" align="center">
<template slot-scope="scope">
<el-button type="primary" size="small" @click="handleCheck(scope.row)">办理</el-button>
<el-button type="primary" size="small" disabled @click="handleReject(scope.row)">退回</el-button>
<!-- <el-button type="primary" size="small" disabled @click="handleReject(scope.row)">退回</el-button>-->
</template>
</el-table-column>
<!-- <el-table-column label="流程类别" align="center">-->
@ -50,12 +50,12 @@
<span class="bluezi">{{scope.row.procDefName}}</span>
</template>
</el-table-column>
<el-table-column label="发起人" align="center">
<el-table-column width="100px" label="发起人" align="center">
<template slot-scope="scope">
<span class="bluezi">{{scope.row.startUserName}}</span>
</template>
</el-table-column>
<el-table-column label="发起日期" align="center">
<el-table-column width="100px" label="发起日期" align="center">
<template slot-scope="scope">
<span class="bluezi">{{scope.row.createTime|formatTimer}}</span>
</template>
@ -65,7 +65,7 @@
<span class="bluezi">{{scope.row.startDeptName}}</span>
</template>
</el-table-column>
<el-table-column label="提交日期" align="center">
<el-table-column width="100px" label="提交日期" align="center">
<template slot-scope="scope">
<span class="bluezi">{{scope.row.processCreateTime|formatTimer}}</span>
</template>
@ -94,14 +94,13 @@
title=""
:visible.sync="centerDialogVisible"
width="78%"
height="80%"
height="1%"
:before-close="closeIt"
center>
<!-- <div class="scroll_div">-->
<iframe frameborder="0"
style="height:600px;width:100%;"
<iframe frameborder="0" id="iframe"
style="width:100%;"
:src="this.centerDialogVisible === true ? url :''"
scrolling="no"
></iframe>
<!--意见要移进具体的表单页面里-->
<!-- <div class="diaTitle"><i class="el-icon-chat-line-square"></i><span>审批意见</span></div>
@ -161,7 +160,7 @@
<!-- End办理页面-->
<!-- 编辑信息页面 -->
<el-dialog title="审批记录" :visible.sync="editDialog" width="60%">
<el-dialog title="审批记录" :visible.sync="editDialog" width="60%" >
<!--流程流转记录-->
<el-card class="box-card" v-if="flowRecordList">
<el-col :span="16" :offset="4">
@ -209,7 +208,6 @@
import {getStorage} from "@/utils/auth";
import {flowRecord} from "@/api/workflow/finished";
import {loginDetails} from "@/api/user"
export default {
name: "",
components: {
@ -217,6 +215,7 @@
},
data() {
return {
dialogHeight:'80%',
centerDialogVisible: false, //
editDialog: false,
flowRecordList: [],
@ -238,6 +237,7 @@
instanceId: '',
taskId: '',
userSid: '',
taskDefKey: ''
},
regectList: { //
businessSid: '',
@ -270,6 +270,11 @@
}
}
},
mounted() {
// vuewindowpostMessagehandleMessage
window.addEventListener('message', this.handleMessage)
},
created() {
//
// this.getList()
@ -293,6 +298,19 @@
}
},
methods: {
setIframeHeight(iframe) {
iframe.height = this.dialogHeight;//iframeWin.document.documentElement.scrollHeight || iframeWin.document.body.scrollHeight;
},
async handleMessage (event) {
var code= event.data.params.code;
if(code==1){
this.init()
this.centerDialogVisible=false
}else if(code==2){
this.dialogHeight = event.data.params.data
this.setIframeHeight(document.getElementById('iframe'));
}
},
init() {
var token = getStorage();
loginDetails(token).then((response) => {
@ -302,7 +320,6 @@
this.regectList.userSid = this.loginInfo.sid
this.stopList.userSid = this.loginInfo.sid
this.listQuery.params.userSid = this.loginInfo.sid
console.log('8888', this.loginInfo)
this.getList()
}
});
@ -315,6 +332,7 @@
this.agreeList.businessSid = row.processVariables.businessSid
this.agreeList.instanceId = row.procInsId
this.agreeList.taskId = row.taskId
this.agreeList.taskDefKey = row.taskDefKey
//
this.regectList.businessSid = row.processVariables.businessSid
this.regectList.instanceId = row.procInsId
@ -331,6 +349,8 @@
instanceId: row.procInsId,
taskId: row.taskId,
taskName: row.taskName,
deployId: row.deployId,
taskDefKey: row.taskDefKey,
transactState: '00' //
}
selectUrl(this.selectUrl_list).then((response) => {

Loading…
Cancel
Save