ソースを参照

Merge branch 'feature/activiti' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/activiti

YunaiV 3 年 前
コミット
31c4a52858
27 ファイル変更1328 行追加183 行削除
  1. 2 1
      sql/ruoyi-vue-pro.sql
  2. 8 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/controller/oa/OaLeaveController.java
  3. 1 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/controller/oa/vo/OaLeaveCreateReqVO.java
  4. 7 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/controller/oa/vo/OaLeaveUpdateReqVO.java
  5. 34 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/controller/workflow/ProcessDefinitionController.java
  6. 5 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/controller/workflow/TaskController.java
  7. 2 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/controller/workflow/vo/TaskStepVO.java
  8. 3 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/controller/workflow/vo/TodoTaskRespVO.java
  9. 16 3
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/service/oa/ReportBackEndProcessor.java
  10. 30 4
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/service/oa/impl/OaLeaveServiceImpl.java
  11. 2 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/service/workflow/TaskService.java
  12. 34 25
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/service/workflow/impl/TaskServiceImpl.java
  13. 152 0
      yudao-admin-server/src/main/resources/processes/leave-formkey.bpmn
  14. 0 130
      yudao-admin-server/src/main/resources/processes/leave.bpmn
  15. 9 0
      yudao-admin-ui/src/api/oa/flow.js
  16. 8 0
      yudao-admin-ui/src/api/oa/leave.js
  17. 8 0
      yudao-admin-ui/src/api/oa/todo.js
  18. 70 0
      yudao-admin-ui/src/router/index.js
  19. 36 0
      yudao-admin-ui/src/views/oa/flow/index.vue
  20. 93 0
      yudao-admin-ui/src/views/oa/leave/apply/index.vue
  21. 190 0
      yudao-admin-ui/src/views/oa/leave/approve-hr/index.vue
  22. 190 0
      yudao-admin-ui/src/views/oa/leave/approve-leader/index.vue
  23. 137 0
      yudao-admin-ui/src/views/oa/leave/confirm/index.vue
  24. 47 17
      yudao-admin-ui/src/views/oa/leave/index.vue
  25. 211 0
      yudao-admin-ui/src/views/oa/leave/modify/index.vue
  26. 22 3
      yudao-admin-ui/src/views/oa/todo/index.vue
  27. 11 0
      更新日志.md

+ 2 - 1
sql/ruoyi-vue-pro.sql

@@ -1847,13 +1847,14 @@ INSERT INTO `sys_menu` VALUES (1113, '错误码更新', 'system:error-code:updat
 INSERT INTO `sys_menu` VALUES (1114, '错误码删除', 'system:error-code:delete', 3, 4, 1110, '', '', '', 0, '', '2021-04-13 21:46:42', '', '2021-04-13 22:09:51', b'0');
 INSERT INTO `sys_menu` VALUES (1115, '错误码导出', 'system:error-code:export', 3, 5, 1110, '', '', '', 0, '', '2021-04-13 21:46:42', '', '2021-04-13 22:09:55', b'0');
 INSERT INTO `sys_menu` VALUES (1116, '日志中心', '', 2, 8, 2, 'log-center', 'log', 'infra/skywalking/log', 0, '1', '2021-04-26 22:35:45', '1', '2021-04-26 22:37:25', b'0');
-INSERT INTO `sys_menu` VALUES (1118,'请假申请','',2,0,5,'oa/leave','user','oa/leave/index',0,'','2021-09-20 08:51:03','1','2021-10-12 22:19:02',0x00);
+INSERT INTO `sys_menu` VALUES (1118,'请假查询','',2,0,5,'oa/leave','user','oa/leave/index',0,'','2021-09-20 08:51:03','1','2021-10-12 22:19:02',0x00);
 INSERT INTO `sys_menu` VALUES (1119,'请假申请查询','oa:leave:query',3,1,1118,'','','',0,'','2021-09-20 08:51:03','','2021-09-20 08:51:03',0x00);
 INSERT INTO `sys_menu` VALUES (1120,'请假申请创建','oa:leave:create',3,2,1118,'','','',0,'','2021-09-20 08:51:03','','2021-09-20 08:51:03',0x00);
 INSERT INTO `sys_menu` VALUES (1121,'请假申请更新','oa:leave:update',3,3,1118,'','','',0,'','2021-09-20 08:51:03','','2021-09-20 08:51:03',0x00);
 INSERT INTO `sys_menu` VALUES (1122,'请假申请删除','oa:leave:delete',3,4,1118,'','','',0,'','2021-09-20 08:51:03','','2021-09-20 08:51:03',0x00);
 INSERT INTO `sys_menu` VALUES (1123,'请假申请导出','oa:leave:export',3,5,1118,'','','',0,'','2021-09-20 08:51:03','','2021-09-20 08:51:03',0x00);
 INSERT INTO `sys_menu` VALUES (1124,'待办任务','',2,2,5,'todo','edit','oa/todo/index',0,'1','2021-09-20 22:10:09','1','2021-09-21 23:17:12',0x00);
+INSERT INTO `sys_menu` VALUES (1125,'流程申请','',2,3,5,'flow','form','oa/flow/index',0,'1','2021-10-23 22:10:09','1','2021-10-23 23:17:12',0x00);
 
 COMMIT;
 

+ 8 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/controller/oa/OaLeaveController.java

@@ -41,6 +41,14 @@ public class OaLeaveController {
     @ApiOperation("创建请假申请")
     @PreAuthorize("@ss.hasPermission('oa:leave:create')")
     public CommonResult<Long> createLeave(@Valid @RequestBody OaLeaveCreateReqVO createReqVO) {
+        createReqVO.setProcessKey("leave");
+        return success(leaveService.createLeave(createReqVO));
+    }
+
+    @PostMapping("/form-key/create")
+    @ApiOperation("创建外置请假申请")
+    public CommonResult<Long> createFormKeyLeave(@Valid @RequestBody OaLeaveCreateReqVO createReqVO) {
+        createReqVO.setProcessKey("leave-formkey");
         return success(leaveService.createLeave(createReqVO));
     }
 

+ 1 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/controller/oa/vo/OaLeaveCreateReqVO.java

@@ -11,4 +11,5 @@ import javax.validation.constraints.*;
 @ToString(callSuper = true)
 public class OaLeaveCreateReqVO extends OaLeaveBaseVO {
 
+    private String processKey;
 }

+ 7 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/controller/oa/vo/OaLeaveUpdateReqVO.java

@@ -15,4 +15,11 @@ public class OaLeaveUpdateReqVO extends OaLeaveBaseVO {
     @NotNull(message = "请假表单主键不能为空")
     private Long id;
 
+
+    private String taskId;
+
+    private String comment;
+
+    private Map<String,Object> variables;
+
 }

+ 34 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/controller/workflow/ProcessDefinitionController.java

@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.adminserver.modules.activiti.controller.workflow;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import org.activiti.api.process.runtime.ProcessRuntime;
+import org.activiti.engine.RepositoryService;
+import org.activiti.engine.repository.ProcessDefinition;
+import org.activiti.engine.repository.ProcessDefinitionQuery;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@RestController
+@RequestMapping("/workflow/process/definition")
+public class ProcessDefinitionController {
+
+    @Resource
+    private RepositoryService repositoryService;
+
+    @Resource
+    private ProcessRuntime processRuntime;
+
+
+    @GetMapping(value = "/getStartForm")
+    public CommonResult<String> getStartForm(@RequestParam("processKey") String processKey){
+        //这样查似乎有问题??, 暂时写死
+//        final ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().
+//                processDefinitionKey(processKey).latestVersion().singleResult();
+//        processRuntime.processDefinition(processDefinition.getId()).getFormKey();
+        return CommonResult.success("/flow/leave/apply");
+    }
+}

+ 5 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/controller/workflow/TaskController.java

@@ -43,6 +43,11 @@ public class TaskController {
         return success( taskService.getTaskSteps(taskQuery));
     }
 
+    @PostMapping("/formKey")
+    public CommonResult<TodoTaskRespVO> getTaskFormKey(@RequestBody TaskQueryReqVO taskQuery) {
+        return success( taskService.getTaskFormKey(taskQuery));
+    }
+
     @PostMapping("/complete")
     public CommonResult<Boolean> complete(@RequestBody TaskReqVO taskReq) {
         taskService.completeTask(taskReq);

+ 2 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/controller/workflow/vo/TaskStepVO.java

@@ -19,4 +19,6 @@ public class TaskStepVO {
 
     private String comment;
 
+    private Integer status;
+
 }

+ 3 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/controller/workflow/vo/TodoTaskRespVO.java

@@ -27,4 +27,7 @@ public class TodoTaskRespVO {
 
     private String businessKey;
 
+
+    private String formKey;
+
 }

+ 16 - 3
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/service/oa/ReportBackEndProcessor.java

@@ -3,7 +3,9 @@ package cn.iocoder.yudao.adminserver.modules.activiti.service.oa;
 import cn.iocoder.yudao.adminserver.modules.activiti.dal.dataobject.oa.OaLeaveDO;
 import cn.iocoder.yudao.adminserver.modules.activiti.dal.mysql.oa.OaLeaveMapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import org.activiti.engine.delegate.DelegateExecution;
 import org.activiti.engine.delegate.DelegateTask;
+import org.activiti.engine.delegate.ExecutionListener;
 import org.activiti.engine.delegate.TaskListener;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
@@ -11,16 +13,27 @@ import org.springframework.transaction.annotation.Transactional;
 import javax.annotation.Resource;
 
 @Component
-public class ReportBackEndProcessor implements TaskListener {
+public class ReportBackEndProcessor implements ExecutionListener {
 
     @Resource
     private OaLeaveMapper leaveMapper;
 
 
+//    @Override
+//    @Transactional(rollbackFor = Exception.class)
+//    public void notify(DelegateTask delegateTask) {
+//        final String businessKey = delegateTask.getExecution().getProcessInstanceBusinessKey();
+//        UpdateWrapper<OaLeaveDO> updateWrapper = new UpdateWrapper<>();
+//        updateWrapper.eq("id", Long.valueOf(businessKey));
+//        OaLeaveDO updateDo = new OaLeaveDO();
+//        updateDo.setStatus(2);
+//        leaveMapper.update(updateDo, updateWrapper);
+//    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public void notify(DelegateTask delegateTask) {
-        final String businessKey = delegateTask.getExecution().getProcessInstanceBusinessKey();
+    public void notify(DelegateExecution delegateExecution) {
+        final String businessKey = delegateExecution.getProcessInstanceBusinessKey();
         UpdateWrapper<OaLeaveDO> updateWrapper = new UpdateWrapper<>();
         updateWrapper.eq("id", Long.valueOf(businessKey));
         OaLeaveDO updateDo = new OaLeaveDO();

+ 30 - 4
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/service/oa/impl/OaLeaveServiceImpl.java

@@ -2,6 +2,9 @@ package cn.iocoder.yudao.adminserver.modules.activiti.service.oa.impl;
 
 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import org.activiti.api.task.model.Task;
+import org.activiti.api.task.model.builders.TaskPayloadBuilder;
+import org.activiti.api.task.runtime.TaskRuntime;
 import org.activiti.engine.RuntimeService;
 import org.activiti.engine.runtime.ProcessInstance;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -38,6 +41,12 @@ public class OaLeaveServiceImpl implements OaLeaveService {
     @Resource
     private RuntimeService runtimeService;
 
+    @Resource
+    private org.activiti.engine.TaskService activitiTaskService;
+
+    @Resource
+    private TaskRuntime taskRuntime;
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Long createLeave(OaLeaveCreateReqVO createReqVO) {
@@ -52,7 +61,7 @@ public class OaLeaveServiceImpl implements OaLeaveService {
         variables.put("deptLeader", "admin");
         final Long id = leave.getId();
         String businessKey = String.valueOf(id);
-        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leave", businessKey, variables);
+        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(createReqVO.getProcessKey(), businessKey, variables);
 
         final String processInstanceId = processInstance.getProcessInstanceId();
 
@@ -67,12 +76,29 @@ public class OaLeaveServiceImpl implements OaLeaveService {
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void updateLeave(OaLeaveUpdateReqVO updateReqVO) {
+
         // 校验存在
         this.validateLeaveExists(updateReqVO.getId());
-        // 更新
-        OaLeaveDO updateObj = OaLeaveConvert.INSTANCE.convert(updateReqVO);
-        leaveMapper.updateById(updateObj);
+
+        final Task task = taskRuntime.task(updateReqVO.getTaskId());
+        activitiTaskService.addComment(task.getId(), task.getProcessInstanceId(), updateReqVO.getComment());
+        Map<String, Object> variables = updateReqVO.getVariables();
+
+        //如何得到部门领导人, 暂时写死
+        variables.put("deptLeader", "admin");
+        taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId())
+                .withVariables(variables)
+                .build());
+        final Object reApply = variables.get("reApply");
+        if((reApply instanceof Boolean) && (Boolean)reApply){
+            // 更新 表单
+            OaLeaveDO updateObj = OaLeaveConvert.INSTANCE.convert(updateReqVO);
+            leaveMapper.updateById(updateObj);
+        }
+
+
     }
 
     @Override

+ 2 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/service/workflow/TaskService.java

@@ -20,4 +20,6 @@ public interface TaskService {
     TaskHandleVO getTaskSteps(TaskQueryReqVO taskQuery);
 
     List<TaskStepVO> getHistorySteps(String processInstanceId);
+
+    TodoTaskRespVO getTaskFormKey(TaskQueryReqVO taskQuery);
 }

+ 34 - 25
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/activiti/service/workflow/impl/TaskServiceImpl.java

@@ -117,14 +117,14 @@ public class TaskServiceImpl implements TaskService {
                 .withVariables(taskReq.getVariables())
                 .build());
 
-        if(variables.containsValue(Boolean.FALSE)){
-            final String businessKey = task.getBusinessKey();
-            UpdateWrapper<OaLeaveDO> updateWrapper = new UpdateWrapper<>();
-            updateWrapper.eq("id", Long.valueOf(businessKey));
-            OaLeaveDO updateDo = new OaLeaveDO();
-            updateDo.setStatus(2);
-            leaveMapper.update(updateDo, updateWrapper);
-        }
+//        if(variables.containsValue(Boolean.FALSE)){
+//            final String businessKey = task.getBusinessKey();
+//            UpdateWrapper<OaLeaveDO> updateWrapper = new UpdateWrapper<>();
+//            updateWrapper.eq("id", Long.valueOf(businessKey));
+//            OaLeaveDO updateDo = new OaLeaveDO();
+//            updateDo.setStatus(2);
+//            leaveMapper.update(updateDo, updateWrapper);
+//        }
 
     }
 
@@ -152,19 +152,19 @@ public class TaskServiceImpl implements TaskService {
     public TaskHandleVO getTaskSteps(TaskQueryReqVO taskQuery) {
         TaskHandleVO handleVO = new TaskHandleVO();
 
-        String processKey = taskQuery.getProcessKey();
-        if ("leave".equals(processKey)) {
-            String businessKey = taskQuery.getBusinessKey();
-            final OaLeaveDO leave = leaveMapper.selectById(Long.valueOf(businessKey));
-            handleVO.setFormObject( OaLeaveConvert.INSTANCE.convert(leave));
-        }
+//        String processKey = taskQuery.getProcessKey();
+//        if ("leave".equals(processKey)) {
+//            String businessKey = taskQuery.getBusinessKey();
+//            final OaLeaveDO leave = leaveMapper.selectById(Long.valueOf(businessKey));
+//            handleVO.setFormObject( OaLeaveConvert.INSTANCE.convert(leave));
+//        }
 
+//
+//        final String taskDefKey = task.getTaskDefinitionKey();
+//        final String variableName = Optional.ofNullable(taskVariable.get(taskDefKey)).orElse("");
+//        handleVO.setTaskVariable(variableName);
         final Task task = taskRuntime.task(taskQuery.getTaskId());
-        final String taskDefKey = task.getTaskDefinitionKey();
-        final String variableName = Optional.ofNullable(taskVariable.get(taskDefKey)).orElse("");
-
 
-        handleVO.setTaskVariable(variableName);
         List<TaskStepVO> steps = getTaskSteps(task.getProcessInstanceId());
 
         handleVO.setHistoryTask(steps);
@@ -189,6 +189,7 @@ public class TaskServiceImpl implements TaskService {
             step.setStartTime(instance.getStartTime());
             step.setEndTime(instance.getEndTime());
             step.setAssignee(instance.getAssignee());
+            step.setStatus(1);
             final List<Comment> comments = activitiTaskService.getTaskComments(instance.getTaskId());
             if(comments.size()>0){
                 step.setComment(comments.get(0).getFullMessage());
@@ -204,15 +205,14 @@ public class TaskServiceImpl implements TaskService {
                 .activityType("userTask")
                 .unfinished().list();
 
-        if(unfinished.size()>0) {
-
-            final HistoricActivityInstance unFinishedActiviti = unfinished.get(0);
+        for (HistoricActivityInstance instance : unfinished) {
             TaskStepVO step = new TaskStepVO();
-            step.setStepName(unFinishedActiviti.getActivityName());
-            step.setStartTime(unFinishedActiviti.getStartTime());
-            step.setEndTime(unFinishedActiviti.getEndTime());
-            step.setAssignee(Optional.ofNullable(unFinishedActiviti.getAssignee()).orElse(""));
+            step.setStepName(instance.getActivityName());
+            step.setStartTime(instance.getStartTime());
+            step.setEndTime(instance.getEndTime());
+            step.setAssignee(Optional.ofNullable(instance.getAssignee()).orElse(""));
             step.setComment("");
+            step.setStatus(0);
             steps.add(step);
         }
         return steps;
@@ -225,6 +225,15 @@ public class TaskServiceImpl implements TaskService {
         return getTaskSteps(processInstanceId);
     }
 
+    @Override
+    public TodoTaskRespVO getTaskFormKey(TaskQueryReqVO taskQuery) {
+        final Task task = taskRuntime.task(taskQuery.getTaskId());
+        TodoTaskRespVO respVO = new TodoTaskRespVO();
+        respVO.setFormKey(task.getFormKey());
+        respVO.setBusinessKey(task.getBusinessKey());
+        respVO.setId(task.getId());
+        return respVO;
+    }
 
 
 //    private List<String> getHighLightedFlows(ProcessDefinitionEntity processDefinition, String processInstanceId) {

+ 152 - 0
yudao-admin-server/src/main/resources/processes/leave-formkey.bpmn

@@ -0,0 +1,152 @@
+<?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:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://bpmn.io/schema/bpmn">
+  <process id="leave-formkey" name="请假流程-外置表单" isExecutable="true">
+    <documentation>外置表单</documentation>
+    <startEvent id="startevent1" name="Start" activiti:initiator="applyUserId" activiti:formKey="/flow/leave/apply"></startEvent>
+    <userTask id="deptLeaderVerify" name="部门经理审批" activiti:assignee="${deptLeader}" activiti:formKey="/flow/leave/approve-leader"></userTask>
+    <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway>
+    <userTask id="hrVerify" name="人事经理审批" activiti:candidateGroups="hr" activiti:formKey="/flow/leave/approve-hr"></userTask>
+    <exclusiveGateway id="exclusivegateway2" name="Exclusive Gateway"></exclusiveGateway>
+    <userTask id="reportBack" name="申请人确认" activiti:assignee="${applyUserId}" activiti:formKey="/flow/leave/confirm">
+    </userTask>
+    <endEvent id="endevent1" name="End"></endEvent>
+    <userTask id="modifyApply" name="调整申请内容" activiti:assignee="${applyUserId}" activiti:formKey="/flow/leave/modify"></userTask>
+    <exclusiveGateway id="exclusivegateway3" name="Exclusive Gateway"></exclusiveGateway>
+    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="deptLeaderVerify"></sequenceFlow>
+    <sequenceFlow id="flow2" sourceRef="deptLeaderVerify" targetRef="exclusivegateway1"></sequenceFlow>
+    <sequenceFlow id="flow3" name="同意" sourceRef="exclusivegateway1" targetRef="hrVerify">
+      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${deptLeaderApproved == 'true'}]]></conditionExpression>
+    </sequenceFlow>
+    <sequenceFlow id="flow4" sourceRef="hrVerify" targetRef="exclusivegateway2"></sequenceFlow>
+    <sequenceFlow id="flow5" name="同意" sourceRef="exclusivegateway2" targetRef="reportBack">
+      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${hrApproved == 'true'}]]></conditionExpression>
+    </sequenceFlow>
+    <sequenceFlow id="flow6" sourceRef="reportBack" targetRef="endevent1">
+      <extensionElements>
+        <activiti:executionListener event="take"  delegateExpression="${reportBackEndProcessor}"></activiti:executionListener>
+      </extensionElements>
+    </sequenceFlow>
+    <sequenceFlow id="flow7" name="不同意" sourceRef="exclusivegateway2" targetRef="modifyApply">
+      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${hrApproved == 'false'}]]></conditionExpression>
+    </sequenceFlow>
+    <sequenceFlow id="flow8" name="不同意" sourceRef="exclusivegateway1" targetRef="modifyApply">
+      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${deptLeaderApproved == 'false'}]]></conditionExpression>
+    </sequenceFlow>
+    <sequenceFlow id="flow9" sourceRef="modifyApply" targetRef="exclusivegateway3"></sequenceFlow>
+    <sequenceFlow id="flow10" name="调整后继续申请" sourceRef="exclusivegateway3" targetRef="deptLeaderVerify">
+      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${reApply == 'true'}]]></conditionExpression>
+    </sequenceFlow>
+    <sequenceFlow id="flow11" name="取消申请,并设置取消标志" sourceRef="exclusivegateway3" targetRef="endevent1">
+      <extensionElements>
+        <activiti:executionListener event="take"  delegateExpression="${reportBackEndProcessor}"></activiti:executionListener>
+      </extensionElements>
+      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${reApply == 'false'}]]></conditionExpression>
+    </sequenceFlow>
+    <textAnnotation id="textannotation1" textFormat="text/plain">
+      <text>请求被驳回后员工可以选择继续申请,或者取消本次申请</text>
+    </textAnnotation>
+    <association id="association1" sourceRef="modifyApply" targetRef="textannotation1"></association>
+  </process>
+  <bpmndi:BPMNDiagram id="BPMNDiagram_leave-formkey">
+    <bpmndi:BPMNPlane bpmnElement="leave-formkey" id="BPMNPlane_leave-formkey">
+      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
+        <omgdc:Bounds height="35.0" width="35.0" x="10.0" y="50.0"></omgdc:Bounds>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape bpmnElement="deptLeaderVerify" id="BPMNShape_deptLeaderVerify">
+        <omgdc:Bounds height="55.0" width="105.0" x="90.0" y="40.0"></omgdc:Bounds>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1">
+        <omgdc:Bounds height="40.0" width="40.0" x="230.0" y="47.0"></omgdc:Bounds>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape bpmnElement="hrVerify" id="BPMNShape_hrVerify">
+        <omgdc:Bounds height="55.0" width="105.0" x="310.0" y="40.0"></omgdc:Bounds>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape bpmnElement="exclusivegateway2" id="BPMNShape_exclusivegateway2">
+        <omgdc:Bounds height="40.0" width="40.0" x="470.0" y="47.0"></omgdc:Bounds>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape bpmnElement="reportBack" id="BPMNShape_reportBack">
+        <omgdc:Bounds height="55.0" width="105.0" x="580.0" y="40.0"></omgdc:Bounds>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
+        <omgdc:Bounds height="35.0" width="35.0" x="615.0" y="213.0"></omgdc:Bounds>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape bpmnElement="modifyApply" id="BPMNShape_modifyApply">
+        <omgdc:Bounds height="55.0" width="105.0" x="198.0" y="120.0"></omgdc:Bounds>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape bpmnElement="exclusivegateway3" id="BPMNShape_exclusivegateway3">
+        <omgdc:Bounds height="40.0" width="40.0" x="230.0" y="210.0"></omgdc:Bounds>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape bpmnElement="textannotation1" id="BPMNShape_textannotation1">
+        <omgdc:Bounds height="57.0" width="112.0" x="340.0" y="168.0"></omgdc:Bounds>
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
+        <omgdi:waypoint x="45.0" y="67.0"></omgdi:waypoint>
+        <omgdi:waypoint x="90.0" y="67.0"></omgdi:waypoint>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
+        <omgdi:waypoint x="195.0" y="67.0"></omgdi:waypoint>
+        <omgdi:waypoint x="230.0" y="67.0"></omgdi:waypoint>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
+        <omgdi:waypoint x="270.0" y="67.0"></omgdi:waypoint>
+        <omgdi:waypoint x="310.0" y="67.0"></omgdi:waypoint>
+        <bpmndi:BPMNLabel>
+          <omgdc:Bounds height="11.0" width="22.0" x="269.0" y="50.0"></omgdc:Bounds>
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
+        <omgdi:waypoint x="415.0" y="67.0"></omgdi:waypoint>
+        <omgdi:waypoint x="470.0" y="67.0"></omgdi:waypoint>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
+        <omgdi:waypoint x="510.0" y="67.0"></omgdi:waypoint>
+        <omgdi:waypoint x="580.0" y="67.0"></omgdi:waypoint>
+        <bpmndi:BPMNLabel>
+          <omgdc:Bounds height="11.0" width="22.0" x="529.0" y="50.0"></omgdc:Bounds>
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">
+        <omgdi:waypoint x="632.0" y="95.0"></omgdi:waypoint>
+        <omgdi:waypoint x="632.0" y="213.0"></omgdi:waypoint>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7">
+        <omgdi:waypoint x="490.0" y="87.0"></omgdi:waypoint>
+        <omgdi:waypoint x="490.0" y="147.0"></omgdi:waypoint>
+        <omgdi:waypoint x="303.0" y="147.0"></omgdi:waypoint>
+        <bpmndi:BPMNLabel>
+          <omgdc:Bounds height="11.0" width="33.0" x="438.0" y="119.0"></omgdc:Bounds>
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8">
+        <omgdi:waypoint x="250.0" y="87.0"></omgdi:waypoint>
+        <omgdi:waypoint x="250.0" y="120.0"></omgdi:waypoint>
+        <bpmndi:BPMNLabel>
+          <omgdc:Bounds height="11.0" width="33.0" x="260.0" y="87.0"></omgdc:Bounds>
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9">
+        <omgdi:waypoint x="250.0" y="175.0"></omgdi:waypoint>
+        <omgdi:waypoint x="250.0" y="210.0"></omgdi:waypoint>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge bpmnElement="flow10" id="BPMNEdge_flow10">
+        <omgdi:waypoint x="230.0" y="230.0"></omgdi:waypoint>
+        <omgdi:waypoint x="142.0" y="230.0"></omgdi:waypoint>
+        <omgdi:waypoint x="142.0" y="95.0"></omgdi:waypoint>
+        <bpmndi:BPMNLabel>
+          <omgdc:Bounds height="11.0" width="77.0" x="159.0" y="210.0"></omgdc:Bounds>
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge bpmnElement="flow11" id="BPMNEdge_flow11">
+        <omgdi:waypoint x="270.0" y="230.0"></omgdi:waypoint>
+        <omgdi:waypoint x="615.0" y="230.0"></omgdi:waypoint>
+        <bpmndi:BPMNLabel>
+          <omgdc:Bounds height="33.0" width="100.0" x="58.0" y="-37.0"></omgdc:Bounds>
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge bpmnElement="association1" id="BPMNEdge_association1">
+        <omgdi:waypoint x="303.0" y="147.0"></omgdi:waypoint>
+        <omgdi:waypoint x="396.0" y="168.0"></omgdi:waypoint>
+      </bpmndi:BPMNEdge>
+    </bpmndi:BPMNPlane>
+  </bpmndi:BPMNDiagram>
+</definitions>

+ 0 - 130
yudao-admin-server/src/main/resources/processes/leave.bpmn

@@ -1,130 +0,0 @@
-<?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:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://bpmn.io/schema/bpmn">
-  <process id="leave" name="请假流程-普通表单" isExecutable="true">
-    <documentation>请假流程演示</documentation>
-    <startEvent id="startevent1" name="Start" activiti:initiator="applyUserId"></startEvent>
-    <userTask id="deptLeaderVerify" name="部门领导审批"  activiti:assignee="${deptLeader}"></userTask>
-    <exclusiveGateway id="exclusivegateway5" name="Exclusive Gateway"></exclusiveGateway>
-    <userTask id="hrVerify" name="人事审批" activiti:candidateGroups="hr"></userTask>
-    <exclusiveGateway id="exclusivegateway6" name="Exclusive Gateway"></exclusiveGateway>
-    <userTask id="reportBack" name="申请人确认" activiti:assignee="${applyUserId}">
-      <extensionElements>
-        <activiti:taskListener event="complete" delegateExpression="${reportBackEndProcessor}"></activiti:taskListener>
-      </extensionElements>
-    </userTask>
-    <endEvent id="endevent1" name="End"></endEvent>
-    <sequenceFlow id="flow2" sourceRef="startevent1" targetRef="deptLeaderVerify"></sequenceFlow>
-    <sequenceFlow id="flow3" sourceRef="deptLeaderVerify" targetRef="exclusivegateway5"></sequenceFlow>
-    <sequenceFlow id="flow5" name="部门领导审批-同意" sourceRef="exclusivegateway5" targetRef="hrVerify">
-      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${deptLeaderApproved}]]></conditionExpression>
-    </sequenceFlow>
-    <sequenceFlow id="flow6" sourceRef="hrVerify" targetRef="exclusivegateway6"></sequenceFlow>
-    <sequenceFlow id="flow7" name="人事审批-同意" sourceRef="exclusivegateway6" targetRef="reportBack">
-      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${hrApproved}]]></conditionExpression>
-    </sequenceFlow>
-    <sequenceFlow id="flow8" sourceRef="reportBack" targetRef="endevent1"></sequenceFlow>
-    <sequenceFlow id="flow4" name="部门领导审批-不同意" sourceRef="exclusivegateway5" targetRef="endevent1" >
-      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${!deptLeaderApproved}]]></conditionExpression>
-    </sequenceFlow>
-    <sequenceFlow sourceRef="exclusivegateway6" name="人事审批-不同意" targetRef="endevent1" id="flow9">
-      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${!hrApproved}]]></conditionExpression>
-    </sequenceFlow>
-  </process>
-  <bpmndi:BPMNDiagram id="BPMNDiagram_leave">
-    <bpmndi:BPMNPlane bpmnElement="leave" id="BPMNPlane_leave">
-      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
-        <omgdc:Bounds height="35.0" width="35.0" x="0.0" y="46.0"></omgdc:Bounds>
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape bpmnElement="deptLeaderVerify" id="BPMNShape_deptLeaderVerify">
-        <omgdc:Bounds height="55.0" width="105.0" x="80.0" y="36.0"></omgdc:Bounds>
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape bpmnElement="exclusivegateway5" id="BPMNShape_exclusivegateway5">
-        <omgdc:Bounds height="40.0" width="40.0" x="240.0" y="43.0"></omgdc:Bounds>
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape bpmnElement="hrVerify" id="BPMNShape_hrVerify">
-        <omgdc:Bounds height="55.0" width="105.0" x="348.0" y="36.0"></omgdc:Bounds>
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape bpmnElement="exclusivegateway6" id="BPMNShape_exclusivegateway6">
-        <omgdc:Bounds height="40.0" width="40.0" x="485.0" y="43.0"></omgdc:Bounds>
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape bpmnElement="reportBack" id="BPMNShape_reportBack">
-        <omgdc:Bounds height="55.0" width="105.0" x="580.0" y="36.0"></omgdc:Bounds>
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
-        <omgdc:Bounds height="35.0" width="35.0" x="615.0" y="195.0"></omgdc:Bounds>
-      </bpmndi:BPMNShape>
-      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
-        <omgdi:waypoint x="35.0" y="63.0"></omgdi:waypoint>
-        <omgdi:waypoint x="80.0" y="63.0"></omgdi:waypoint>
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
-        <omgdi:waypoint x="185.0" y="63.0"></omgdi:waypoint>
-        <omgdi:waypoint x="240.0" y="63.0"></omgdi:waypoint>
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
-        <omgdi:waypoint x="260.0" y="83.0"></omgdi:waypoint>
-        <omgdi:waypoint x="260.0" y="114.0"></omgdi:waypoint>
-        <bpmndi:BPMNLabel>
-          <omgdc:Bounds height="11.0" width="33.0" x="270.0" y="83.0"></omgdc:Bounds>
-        </bpmndi:BPMNLabel>
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
-        <omgdi:waypoint x="280.0" y="63.0"></omgdi:waypoint>
-        <omgdi:waypoint x="348.0" y="63.0"></omgdi:waypoint>
-        <bpmndi:BPMNLabel>
-          <omgdc:Bounds height="11.0" width="22.0" x="300.0" y="46.0"></omgdc:Bounds>
-        </bpmndi:BPMNLabel>
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">
-        <omgdi:waypoint x="453.0" y="63.0"></omgdi:waypoint>
-        <omgdi:waypoint x="485.0" y="63.0"></omgdi:waypoint>
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7">
-        <omgdi:waypoint x="525.0" y="63.0"></omgdi:waypoint>
-        <omgdi:waypoint x="580.0" y="63.0"></omgdi:waypoint>
-        <bpmndi:BPMNLabel>
-          <omgdc:Bounds height="11.0" width="22.0" x="539.0" y="46.0"></omgdc:Bounds>
-        </bpmndi:BPMNLabel>
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8">
-        <omgdi:waypoint x="632.0" y="91.0"></omgdi:waypoint>
-        <omgdi:waypoint x="632.0" y="195.0"></omgdi:waypoint>
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9">
-        <omgdi:waypoint x="505.0" y="83.0"></omgdi:waypoint>
-        <omgdi:waypoint x="504.0" y="141.0"></omgdi:waypoint>
-        <omgdi:waypoint x="313.0" y="141.0"></omgdi:waypoint>
-        <bpmndi:BPMNLabel>
-          <omgdc:Bounds height="11.0" width="33.0" x="515.0" y="83.0"></omgdc:Bounds>
-        </bpmndi:BPMNLabel>
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge bpmnElement="flow10" id="BPMNEdge_flow10">
-        <omgdi:waypoint x="240.0" y="212.0"></omgdi:waypoint>
-        <omgdi:waypoint x="132.0" y="212.0"></omgdi:waypoint>
-        <omgdi:waypoint x="132.0" y="91.0"></omgdi:waypoint>
-        <bpmndi:BPMNLabel>
-          <omgdc:Bounds height="11.0" width="44.0" x="142.0" y="192.0"></omgdc:Bounds>
-        </bpmndi:BPMNLabel>
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge bpmnElement="flow11" id="BPMNEdge_flow11">
-        <omgdi:waypoint x="260.0" y="169.0"></omgdi:waypoint>
-        <omgdi:waypoint x="260.0" y="192.0"></omgdi:waypoint>
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge bpmnElement="flow12" id="BPMNEdge_flow12">
-        <omgdi:waypoint x="280.0" y="212.0"></omgdi:waypoint>
-        <omgdi:waypoint x="615.0" y="212.0"></omgdi:waypoint>
-        <bpmndi:BPMNLabel>
-          <omgdc:Bounds height="11.0" width="44.0" x="429.0" y="219.0"></omgdc:Bounds>
-        </bpmndi:BPMNLabel>
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge bpmnElement="flow4">
-        <omgdi:waypoint x="260.0" y="63.0"/>
-        <omgdi:waypoint x="632.5" y="212.5"/>
-      </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge bpmnElement="flow9">
-        <omgdi:waypoint x="505.0" y="63.0"/>
-        <omgdi:waypoint x="632.5" y="212.5"/>
-      </bpmndi:BPMNEdge>
-    </bpmndi:BPMNPlane>
-  </bpmndi:BPMNDiagram>
-</definitions>

+ 9 - 0
yudao-admin-ui/src/api/oa/flow.js

@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+
+
+export function getStartForm(processKey) {
+  return request({
+    url: '/workflow/process/definition/getStartForm?processKey='+processKey,
+    method: 'get'
+  })
+}

+ 8 - 0
yudao-admin-ui/src/api/oa/leave.js

@@ -43,6 +43,14 @@ export function getLeavePage(query) {
   })
 }
 
+export function createFormKeyLeave(data) {
+  return request({
+    url: '/oa/leave/form-key/create',
+    method: 'post',
+    data: data
+  })
+}
+
 // 导出请假申请 Excel
 export function exportLeaveExcel(query) {
   return request({

+ 8 - 0
yudao-admin-ui/src/api/oa/todo.js

@@ -67,6 +67,14 @@ export function taskSteps(data) {
   })
 }
 
+export function getTaskFormKey(data) {
+  return request({
+    url: '/workflow/task/formKey',
+    method: 'post',
+    data: data
+  })
+}
+
 export function processHistorySteps(id) {
   return request({
     url: '/workflow/task/process/history-steps?id='+id,

+ 70 - 0
yudao-admin-ui/src/router/index.js

@@ -123,6 +123,76 @@ export const constantRoutes = [
         meta: { title: '修改生成配置' }
       }
     ]
+  },
+  {
+    path: '/flow',
+    component: Layout,
+    hidden: true,
+    redirect: 'noredirect',
+    children: [
+      {
+        path: 'leave/apply',
+        component: (resolve) => require(['@/views/oa/leave/apply/index'], resolve),
+        name: '请假表单',
+        meta: { title: '请假表单', icon: 'form' }
+      }
+    ]
+  },
+  {
+    path: '/flow',
+    component: Layout,
+    hidden: true,
+    redirect: 'noredirect',
+    children: [
+      {
+        path: 'leave/approve-leader',
+        component: (resolve) => require(['@/views/oa/leave/approve-leader/index'], resolve),
+        name: '请假表单-部门领导审批',
+        meta: { title: '请假表单-部门领导审批', icon: 'form' }
+      }
+    ]
+  },
+  {
+    path: '/flow',
+    component: Layout,
+    hidden: true,
+    redirect: 'noredirect',
+    children: [
+      {
+        path: 'leave/approve-hr',
+        component: (resolve) => require(['@/views/oa/leave/approve-hr/index'], resolve),
+        name: '请假表单-人事审批',
+        meta: { title: '请假表单-人事审批', icon: 'form' }
+      }
+    ]
+  },
+  {
+    path: '/flow',
+    component: Layout,
+    hidden: true,
+    redirect: 'noredirect',
+    children: [
+      {
+        path: 'leave/confirm',
+        component: (resolve) => require(['@/views/oa/leave/confirm/index'], resolve),
+        name: '请假表单-确认',
+        meta: { title: '请假表单-确认', icon: 'form' }
+      }
+    ]
+  },
+  {
+    path: '/flow',
+    component: Layout,
+    hidden: true,
+    redirect: 'noredirect',
+    children: [
+      {
+        path: 'leave/modify',
+        component: (resolve) => require(['@/views/oa/leave/modify/index'], resolve),
+        name: '请假表单-修改',
+        meta: { title: '请假表单-修改', icon: 'form' }
+      }
+    ]
   }
 ]
 

+ 36 - 0
yudao-admin-ui/src/views/oa/flow/index.vue

@@ -0,0 +1,36 @@
+<template>
+  <div class="app-container">
+    <el-row>
+      <el-button type="primary" v-on:click="leave">请假申请</el-button>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { getStartForm } from "@/api/oa/flow";
+export default {
+  name: "Flow",
+  components: {
+  },
+  data() {
+    return {
+      // 表单参数
+      form: {},
+
+      rules: {
+      }
+    };
+  },
+  created() {
+
+  },
+  methods: {
+    leave() {
+      getStartForm('leave-formkey').then(response => {
+        const route = response.data;
+        this.$router.replace(route);
+      });
+    }
+  }
+};
+</script>

+ 93 - 0
yudao-admin-ui/src/views/oa/leave/apply/index.vue

@@ -0,0 +1,93 @@
+<template>
+  <div class="app-container">
+    <!-- 对话框(添加 / 修改) -->
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="开始时间" prop="startTime">
+          <el-date-picker clearable size="small" v-model="form.startTime" type="date" value-format="timestamp" placeholder="选择开始时间" />
+        </el-form-item>
+        <el-form-item label="结束时间" prop="endTime">
+          <el-date-picker clearable size="small" v-model="form.endTime" type="date" value-format="timestamp" placeholder="选择结束时间" />
+        </el-form-item>
+        <el-form-item label="请假类型" prop="leaveType">
+          <el-select v-model="form.leaveType" placeholder="请选择">
+            <el-option
+              v-for="dict in leaveTypeDictData"
+              :key="parseInt(dict.value)"
+              :label="dict.label"
+              :value="parseInt(dict.value)"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="原因" prop="reason">
+          <el-col :span="10">
+            <el-input
+              type="textarea"
+              :rows="3"
+              v-model="form.reason"
+              placeholder="请输入原因" />
+          </el-col>
+        </el-form-item>
+        <el-form-item label="申请时间" prop="applyTime">
+          <el-date-picker clearable size="small" v-model="form.applyTime" type="date" value-format="timestamp" placeholder="选择申请时间" />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="submitForm">确 定</el-button>
+        </el-form-item>
+      </el-form>
+  </div>
+</template>
+
+<script>
+import { createFormKeyLeave } from "@/api/oa/leave"
+import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict'
+export default {
+  name: "ApplyLeave",
+  components: {
+  },
+  data() {
+    return {
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        startTime: [{ required: true, message: "开始时间不能为空", trigger: "blur" }],
+        endTime: [{ required: true, message: "结束时间不能为空", trigger: "blur" }],
+        applyTime: [{ required: true, message: "申请时间不能为空", trigger: "blur" }],
+      },
+      statusFormat(row, column) {
+        return getDictDataLabel(DICT_TYPE.OA_LEAVE_STATUS, row.status)
+      },
+      leaveTypeDictData: getDictDatas(DICT_TYPE.OA_LEAVE_TYPE),
+      leaveStatusData: getDictDatas(DICT_TYPE.OA_LEAVE_STATUS)
+    };
+  },
+  created() {
+  },
+  methods: {
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (!valid) {
+          return;
+        }
+        // 修改的提交
+        // if (this.form.id != null) {
+        //   updateLeave(this.form).then(response => {
+        //     this.msgSuccess("修改成功");
+        //   });
+        //   return;
+        // }
+        // 添加的提交
+        createFormKeyLeave(this.form).then(response => {
+          this.msgSuccess("新增成功");
+          this.$store.dispatch('tagsView/delView', this.$route).then(({ visitedViews }) => {
+            //if (this.isActive(this.$route)) {
+            this.$router.push({path: '/oa/todo'})
+            //}
+          })
+        });
+      });
+    }
+  }
+};
+</script>

+ 190 - 0
yudao-admin-ui/src/views/oa/leave/approve-hr/index.vue

@@ -0,0 +1,190 @@
+<template>
+  <div class="app-container">
+    <el-tabs type="border-card">
+      <el-tab-pane label="任务处理">
+        <el-form ref="form" :model="form"  label-width="80px">
+          <el-row :gutter="20">
+            <el-col :span="6"><el-form-item label="申请人" >{{form.userId}}</el-form-item></el-col>
+            <el-col :span="6">
+              <el-form-item label="请假类型" prop="leaveType">
+                {{ getDictDataLabel(DICT_TYPE.OA_LEAVE_TYPE, form.leaveType) }}
+              </el-form-item>
+            </el-col>
+            <el-col :span="6"><el-form-item label="原因" prop="reason">{{form.reason}}</el-form-item></el-col>
+          </el-row>
+          <el-row :gutter="20">
+            <el-col :span="6"><el-form-item label="开始时间" >{{ parseTime(form.startTime) }}</el-form-item></el-col>
+            <el-col :span="6"><el-form-item label="结束时间" prop="endTime">{{ parseTime(form.endTime) }}</el-form-item></el-col>
+            <el-form-item label="申请时间" prop="applyTime">{{ parseTime(form.applyTime) }}</el-form-item>
+          </el-row>
+        </el-form>
+        <el-divider></el-divider>
+        <el-form ref="taskForm" :model="leaveApprove" :rules="rules"  label-width="80px">
+          <el-form-item label="是否同意" prop="approved">
+            <el-select v-model="leaveApprove.approved" placeholder="是否同意" v-on:change="approveChange">
+              <el-option
+                v-for="dict in approvedData"
+                :key="parseInt(dict.value)"
+                :label="dict.label"
+                :value="parseInt(dict.value)"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="处理意见" prop="comment">
+            <el-col :span="11">
+              <el-input
+                type="textarea"
+                :rows="3"
+                v-model="leaveApprove.comment">
+              </el-input>
+            </el-col>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="submitForm">确 定</el-button>
+          </el-form-item>
+        </el-form>
+      </el-tab-pane>
+      <el-tab-pane label="历史跟踪">
+        <el-steps :active="stepActive" simple finish-status="success">
+          <el-step :title="stepTitle(item)" icon="el-icon-edit" v-for="(item) in handleTask.historyTask" ></el-step>
+        </el-steps>
+        <br/>
+        <el-steps direction="vertical" :active="stepActive"  space="65px">
+          <el-step :title="stepTitle(item)" :description="stepDes(item)" v-for="(item) in handleTask.historyTask" ></el-step>
+        </el-steps>
+      </el-tab-pane>
+      <el-tab-pane label="流程图">流程图-TODO</el-tab-pane>
+    </el-tabs>
+
+  </div>
+</template>
+
+<script>
+import { getLeave } from "@/api/oa/leave"
+import { completeTask,taskSteps } from "@/api/oa/todo";
+import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict'
+export default {
+  name: "HrApproveLeave",
+  components: {
+  },
+  data() {
+    return {
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        comment: [{ required: true, message: "意见不能为空", trigger: "blur" }]
+      },
+      handleTask: {
+        historyTask:[]
+      },
+      leaveApprove: {
+        approved : 1,
+        variables: {},
+        taskId: "",
+        comment: "同意"
+      },
+      approvedData: [
+        {
+          value: 1,
+          label: '同意'
+        },
+        {
+          value: 0,
+          label: '不同意'
+        }
+      ],
+      statusFormat(row, column) {
+        return getDictDataLabel(DICT_TYPE.OA_LEAVE_STATUS, row.status)
+      },
+      leaveTypeDictData: getDictDatas(DICT_TYPE.OA_LEAVE_TYPE),
+      leaveStatusData: getDictDatas(DICT_TYPE.OA_LEAVE_STATUS)
+    };
+  },
+  computed:{
+     stepActive: function () {
+         let idx = 0;
+         for(let i=0; i<this.handleTask.historyTask.length; i++){
+           if(this.handleTask.historyTask[i].status === 1){
+             idx= idx+1;
+           }else{
+             break;
+           }
+         }
+         return idx;
+     },
+    stepTitle() {
+      return function (item) {
+        let name = item.stepName;
+        if (item.status === 1) {
+          name += '(已完成)'
+        }
+        if (item.status === 0) {
+          name += '(进行中)'
+        }
+        return name;
+      }
+    },
+    stepDes(){
+      return function (item) {
+        let desc = "";
+        if (item.status === 1) {
+          desc+="审批人:["+ item.assignee +"]    审批意见: [" + item.comment + "]   审批时间: " + this.parseTime(item.endTime);
+        }
+        return desc;
+      }
+    }
+  },
+  created() {
+    const businessKey = this.$route.query.businessKey;
+    const taskId = this.$route.query.taskId;
+    this.leaveApprove.taskId = taskId;
+    this.getForm(businessKey);
+  },
+  methods: {
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["taskForm"].validate(valid => {
+        if (!valid) {
+          return;
+        }
+        if (this.leaveApprove.approved == 1) {
+          this.leaveApprove.variables["hrApproved"] = true;
+        }
+        if (this.leaveApprove.approved == 0) {
+          this.leaveApprove.variables["hrApproved"] = false;
+        }
+        completeTask(this.leaveApprove).then(response => {
+          this.msgSuccess("执行任务成功");
+          this.$store.dispatch('tagsView/delView', this.$route).then(({ visitedViews }) => {
+            //if (this.isActive(this.$route)) {
+            this.$router.push({path: '/oa/todo'})
+            //}
+          })
+        })
+      });
+    },
+    getForm(id){
+      getLeave(id).then(response => {
+        this.form = response.data;
+      });
+      const data = {
+        taskId : this.leaveApprove.taskId,
+        businessKey: id,
+      }
+      taskSteps(data).then(response => {
+        this.handleTask = response.data;
+
+      });
+    },
+    approveChange(){
+      if (this.leaveApprove.approved === 1) {
+        this.leaveApprove.comment = "同意"
+      }
+      if(this.leaveApprove.approved === 0){
+        this.leaveApprove.comment = "不同意"
+      }
+    }
+  }
+};
+</script>

+ 190 - 0
yudao-admin-ui/src/views/oa/leave/approve-leader/index.vue

@@ -0,0 +1,190 @@
+<template>
+  <div class="app-container">
+    <el-tabs type="border-card">
+      <el-tab-pane label="任务处理">
+        <el-form ref="form" :model="form"  label-width="80px">
+          <el-row :gutter="20">
+            <el-col :span="6"><el-form-item label="申请人" >{{form.userId}}</el-form-item></el-col>
+            <el-col :span="6">
+              <el-form-item label="请假类型" prop="leaveType">
+                {{ getDictDataLabel(DICT_TYPE.OA_LEAVE_TYPE, form.leaveType) }}
+              </el-form-item>
+            </el-col>
+            <el-col :span="6"><el-form-item label="原因" prop="reason">{{form.reason}}</el-form-item></el-col>
+          </el-row>
+          <el-row :gutter="20">
+            <el-col :span="6"><el-form-item label="开始时间" >{{ parseTime(form.startTime) }}</el-form-item></el-col>
+            <el-col :span="6"><el-form-item label="结束时间" prop="endTime">{{ parseTime(form.endTime) }}</el-form-item></el-col>
+            <el-form-item label="申请时间" prop="applyTime">{{ parseTime(form.applyTime) }}</el-form-item>
+          </el-row>
+        </el-form>
+        <el-divider></el-divider>
+        <el-form ref="taskForm" :model="leaveApprove" :rules="rules"  label-width="80px">
+          <el-form-item label="是否同意" prop="approved">
+            <el-select v-model="leaveApprove.approved" placeholder="是否同意" v-on:change="approveChange">
+              <el-option
+                v-for="dict in approvedData"
+                :key="parseInt(dict.value)"
+                :label="dict.label"
+                :value="parseInt(dict.value)"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="处理意见" prop="comment">
+            <el-col :span="11">
+              <el-input
+                type="textarea"
+                :rows="3"
+                v-model="leaveApprove.comment">
+              </el-input>
+            </el-col>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="submitForm">确 定</el-button>
+          </el-form-item>
+        </el-form>
+      </el-tab-pane>
+      <el-tab-pane label="历史跟踪">
+        <el-steps :active="stepActive" simple finish-status="success">
+          <el-step :title="stepTitle(item)" icon="el-icon-edit" v-for="(item) in handleTask.historyTask" ></el-step>
+        </el-steps>
+        <br/>
+        <el-steps direction="vertical" :active="stepActive"  space="65px">
+          <el-step :title="stepTitle(item)" :description="stepDes(item)" v-for="(item) in handleTask.historyTask" ></el-step>
+        </el-steps>
+      </el-tab-pane>
+      <el-tab-pane label="流程图">流程图-TODO</el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import { getLeave } from "@/api/oa/leave"
+import { completeTask,taskSteps } from "@/api/oa/todo";
+import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict'
+export default {
+  name: "ApproveLeaderLeave",
+  components: {
+  },
+  data() {
+    return {
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        comment: [{ required: true, message: "意见不能为空", trigger: "blur" }]
+      },
+      handleTask: {
+        historyTask:[]
+      },
+      leaveApprove: {
+        approved : 1,
+        variables: {},
+        taskId: "",
+        comment: "同意"
+      },
+      approvedData: [
+        {
+          value: 1,
+          label: '同意'
+        },
+        {
+          value: 0,
+          label: '不同意'
+        }
+      ],
+      statusFormat(row, column) {
+        return getDictDataLabel(DICT_TYPE.OA_LEAVE_STATUS, row.status)
+      },
+      leaveTypeDictData: getDictDatas(DICT_TYPE.OA_LEAVE_TYPE),
+      leaveStatusData: getDictDatas(DICT_TYPE.OA_LEAVE_STATUS)
+    };
+  },
+  created() {
+    const businessKey = this.$route.query.businessKey;
+    const taskId = this.$route.query.taskId;
+    this.leaveApprove.taskId = taskId;
+    this.getForm(businessKey);
+  },
+  computed:{
+    stepActive: function () {
+      let idx = 0;
+      for(let i=0; i<this.handleTask.historyTask.length; i++){
+        if(this.handleTask.historyTask[i].status === 1){
+          idx= idx+1;
+        }else{
+          break;
+        }
+      }
+      return idx;
+    },
+    stepTitle() {
+      return function (item) {
+        let name = item.stepName;
+        if (item.status === 1) {
+          name += '(已完成)'
+        }
+        if (item.status === 0) {
+          name += '(进行中)'
+        }
+        return name;
+      }
+    },
+    stepDes(){
+      return function (item) {
+        let desc = "";
+        if (item.status === 1) {
+          desc+="审批人:["+ item.assignee +"]    审批意见: [" + item.comment + "]   审批时间: " + this.parseTime(item.endTime);
+        }
+        return desc;
+      }
+    }
+  },
+  methods: {
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["taskForm"].validate(valid => {
+        if (!valid) {
+          return;
+        }
+        if (this.leaveApprove.approved == 1) {
+          this.leaveApprove.variables["deptLeaderApproved"] = true;
+        }
+        if (this.leaveApprove.approved == 0) {
+          this.leaveApprove.variables["deptLeaderApproved"] = false;
+        }
+        completeTask(this.leaveApprove).then(response => {
+          this.msgSuccess("执行任务成功");
+          this.$store.dispatch('tagsView/delView', this.$route).then(({ visitedViews }) => {
+            //if (this.isActive(this.$route)) {
+            this.$router.push({path: '/oa/todo'})
+            //}
+          })
+        });
+      });
+
+    },
+    getForm(id){
+      getLeave(id).then(response => {
+        this.form = response.data;
+      });
+      const data = {
+        taskId : this.leaveApprove.taskId,
+        businessKey: id,
+      }
+      taskSteps(data).then(response => {
+        this.handleTask = response.data;
+
+      });
+    },
+    approveChange(){
+      if (this.leaveApprove.approved === 1) {
+        this.leaveApprove.comment = "同意"
+      }
+      if(this.leaveApprove.approved === 0){
+        this.leaveApprove.comment = "不同意"
+      }
+    }
+  }
+};
+</script>

+ 137 - 0
yudao-admin-ui/src/views/oa/leave/confirm/index.vue

@@ -0,0 +1,137 @@
+<template>
+  <div class="app-container">
+    <el-tabs type="border-card">
+      <el-tab-pane label="任务处理">
+        <el-form ref="form" :model="form"  label-width="80px">
+          <el-row :gutter="20">
+            <el-col :span="6"><el-form-item label="申请人" >{{form.userId}}</el-form-item></el-col>
+            <el-col :span="6">
+              <el-form-item label="请假类型" prop="leaveType">
+                {{ getDictDataLabel(DICT_TYPE.OA_LEAVE_TYPE, form.leaveType) }}
+              </el-form-item>
+            </el-col>
+            <el-col :span="6"><el-form-item label="原因" prop="reason">{{form.reason}}</el-form-item></el-col>
+          </el-row>
+          <el-row :gutter="20">
+            <el-col :span="6"><el-form-item label="开始时间" >{{ parseTime(form.startTime) }}</el-form-item></el-col>
+            <el-col :span="6"><el-form-item label="结束时间" prop="endTime">{{ parseTime(form.endTime) }}</el-form-item></el-col>
+            <el-form-item label="申请时间" prop="applyTime">{{ parseTime(form.applyTime) }}</el-form-item>
+          </el-row>
+          <el-row :gutter="20">
+            <el-col :span="6"><el-button type="primary" @click="submitForm">确 定</el-button></el-col>
+          </el-row>
+        </el-form>
+      </el-tab-pane>
+      <el-tab-pane label="历史跟踪">
+        <el-steps :active="stepActive" simple finish-status="success">
+          <el-step :title="stepTitle(item)" icon="el-icon-edit" v-for="(item) in handleTask.historyTask" ></el-step>
+        </el-steps>
+        <br/>
+        <el-steps direction="vertical" :active="stepActive"  space="65px">
+          <el-step :title="stepTitle(item)" :description="stepDes(item)" v-for="(item) in handleTask.historyTask" ></el-step>
+        </el-steps>
+      </el-tab-pane>
+      <el-tab-pane label="流程图">流程图-TODO</el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import { getLeave } from "@/api/oa/leave"
+import { completeTask,taskSteps } from "@/api/oa/todo";
+import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict'
+export default {
+  name: "ConfirmLeave",
+  components: {
+  },
+  data() {
+    return {
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      },
+      handleTask: {
+        historyTask:[]
+      },
+      leaveApprove: {
+        variables: {},
+        taskId: "",
+        comment: ""
+      },
+      statusFormat(row, column) {
+        return getDictDataLabel(DICT_TYPE.OA_LEAVE_STATUS, row.status)
+      },
+      leaveTypeDictData: getDictDatas(DICT_TYPE.OA_LEAVE_TYPE),
+      leaveStatusData: getDictDatas(DICT_TYPE.OA_LEAVE_STATUS)
+    };
+  },
+  created() {
+    const businessKey = this.$route.query.businessKey;
+    const taskId = this.$route.query.taskId;
+    this.leaveApprove.taskId = taskId;
+    this.getForm(businessKey);
+  },
+  computed:{
+    stepActive: function () {
+      let idx = 0;
+      for(let i=0; i<this.handleTask.historyTask.length; i++){
+        if(this.handleTask.historyTask[i].status === 1){
+          idx= idx+1;
+        }else{
+          break;
+        }
+      }
+      return idx;
+    },
+    stepTitle() {
+      return function (item) {
+        let name = item.stepName;
+        if (item.status === 1) {
+          name += '(已完成)'
+        }
+        if (item.status === 0) {
+          name += '(进行中)'
+        }
+        return name;
+      }
+    },
+    stepDes(){
+      return function (item) {
+        let desc = "";
+        if (item.status === 1) {
+          desc+="审批人:["+ item.assignee +"]    审批意见: [" + item.comment + "]   审批时间: " + this.parseTime(item.endTime);
+        }
+        return desc;
+      }
+    }
+  },
+  methods: {
+    /** 提交按钮 */
+    submitForm() {
+      completeTask(this.leaveApprove).then(response => {
+        this.msgSuccess("执行任务成功");
+        this.$store.dispatch('tagsView/delView', this.$route).then(({ visitedViews }) => {
+          //if (this.isActive(this.$route)) {
+          this.$router.push({path: '/oa/todo'})
+          //}
+        })
+      })
+    },
+    getForm(id){
+      getLeave(id).then(response => {
+        this.form = response.data;
+      });
+      const data = {
+        taskId : this.leaveApprove.taskId,
+        businessKey: id,
+      }
+      taskSteps(data).then(response => {
+        this.handleTask = response.data;
+
+      });
+
+    }
+  }
+};
+</script>

+ 47 - 17
yudao-admin-ui/src/views/oa/leave/index.vue

@@ -28,9 +28,9 @@
         <el-select v-model="queryParams.leaveType" placeholder="请选择请假类型">
           <el-option
             v-for="dict in leaveTypeDictData"
-            :key="parseInt(dict.value)"
+            :key="dict.value"
             :label="dict.label"
-            :value="parseInt(dict.value)"
+            :value="dict.value"
           />
         </el-select>
       </el-form-item>
@@ -49,10 +49,6 @@
 
     <!-- 操作工具栏 -->
     <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
-        <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
-                   v-hasPermi="['oa:leave:create']">新增</el-button>
-      </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
@@ -71,7 +67,7 @@
           <span>{{ parseTime(scope.row.endTime) }}</span>
         </template>
       </el-table-column>
-      <el-table-column label="请假类型" align="center" prop="leaveType" />
+      <el-table-column label="请假类型" align="center" prop="leaveType" :formatter="leaveTypeFormat" />
       <el-table-column label="原因" align="center" prop="reason" />
       <el-table-column label="申请时间" align="center" prop="applyTime" width="180">
         <template slot-scope="scope">
@@ -102,9 +98,9 @@
           <el-select v-model="form.leaveType" placeholder="请选择">
             <el-option
               v-for="dict in leaveTypeDictData"
-              :key="parseInt(dict.value)"
+              :key="dict.value"
               :label="dict.label"
-              :value="parseInt(dict.value)"
+              :value="dict.value"
             />
           </el-select>
         </el-form-item>
@@ -142,12 +138,12 @@
     </el-dialog>
 
     <el-dialog :title="title" :visible.sync="dialogStepsVisible" width="600px" append-to-body>
-      <el-steps :active="handleTask.historyTask.length-1" finish-status="success" >
-        <el-step :title="item.stepName " :description="' 办理人:' + item.assignee " icon="el-icon-edit"  v-for="(item) in handleTask.historyTask"></el-step>
+      <el-steps :active="stepActive" finish-status="success" >
+        <el-step :title="stepTitle(item)" :description="' 办理人:' + item.assignee " icon="el-icon-edit"  v-for="(item) in handleTask.historyTask"></el-step>
       </el-steps>
       <br/>
-      <el-steps direction="vertical" :active="handleTask.historyTask.length-1">
-        <el-step :title="item.stepName" :description=" ' 意见:'+ item.comment" v-for="(item) in handleTask.historyTask"></el-step>
+      <el-steps direction="vertical" :active="stepActive">
+        <el-step :title="stepTitle(item)" :description="stepDes(item)" v-for="(item) in handleTask.historyTask"></el-step>
       </el-steps>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="dialogStepsVisible = false">确 定</el-button>
@@ -199,10 +195,7 @@ export default {
       // 表单参数
       form: {},
       handleTask: {
-        historyTask:[{
-          stepName:"步骤一"
-        }
-        ],
+        historyTask:[],
         taskVariable: "",
         formObject: {}
       },
@@ -218,6 +211,9 @@ export default {
       statusFormat(row, column) {
         return getDictDataLabel(DICT_TYPE.OA_LEAVE_STATUS, row.status)
       },
+      leaveTypeFormat(row, column) {
+        return getDictDataLabel(DICT_TYPE.OA_LEAVE_TYPE, row.leaveType)
+      },
       leaveTypeDictData: getDictDatas(DICT_TYPE.OA_LEAVE_TYPE),
       leaveStatusData: getDictDatas(DICT_TYPE.OA_LEAVE_STATUS)
     };
@@ -225,6 +221,40 @@ export default {
   created() {
     this.getList();
   },
+  computed: {
+    stepActive: function () {
+      let idx = 0;
+      for (let i = 0; i < this.handleTask.historyTask.length; i++) {
+        if (this.handleTask.historyTask[i].status === 1) {
+          idx = idx + 1;
+        } else {
+          break;
+        }
+      }
+      return idx;
+    },
+    stepTitle() {
+      return function (item) {
+        let name = item.stepName;
+        if (item.status === 1) {
+          name += '(已完成)'
+        }
+        if (item.status === 0) {
+          name += '(进行中)'
+        }
+        return name;
+      }
+    },
+    stepDes() {
+      return function (item) {
+        let desc = "";
+        if (item.status === 1) {
+          desc += "审批人:[" + item.assignee + "]    审批意见: [" + item.comment + "]   审批时间: " + this.parseTime(item.endTime);
+        }
+        return desc;
+      }
+    }
+  },
   methods: {
     /** 查询列表 */
     getList() {

+ 211 - 0
yudao-admin-ui/src/views/oa/leave/modify/index.vue

@@ -0,0 +1,211 @@
+<template>
+  <div class="app-container">
+    <el-tabs type="border-card">
+      <el-tab-pane label="任务处理">
+        <el-form ref="form" :model="form"  :rules="rules" label-width="100px">
+          <el-row>
+            <el-col :span="15">
+              <el-form-item label="是否调整申请" prop="reApply">
+                <el-select v-model="reApplySelect"  placeholder="是否调整申请" v-on:change="reApplyChange">
+                  <el-option
+                    v-for="dict in reApplyData"
+                    :key="parseInt(dict.value)"
+                    :label="dict.label"
+                    :value="parseInt(dict.value)"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20" v-show="modifyShow">
+            <el-col :span="8"><el-form-item label="申请人" >{{form.userId}}</el-form-item></el-col>
+            <el-col :span="8">
+              <el-form-item label="申请时间" prop="applyTime">{{ parseTime(form.applyTime) }}</el-form-item>
+            </el-col>
+          </el-row>
+          <el-row :gutter="20" v-show="modifyShow">
+            <el-col :span="8">
+              <el-form-item label="开始时间" prop="startTime">
+                <el-date-picker clearable size="small" v-model="form.startTime" type="date" value-format="timestamp" placeholder="选择开始时间" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="结束时间" prop="endTime">
+                <el-date-picker clearable size="small" v-model="form.endTime" type="date" value-format="timestamp" placeholder="选择结束时间" />
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row v-show="modifyShow" >
+            <el-col :span="8">
+              <el-form-item label="请假类型" prop="leaveType">
+                <el-select v-model="form.leaveType" placeholder="请选择">
+                  <el-option
+                    v-for="dict in leaveTypeDictData"
+                    :key="dict.value"
+                    :label="dict.label"
+                    :value="dict.value"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row v-show="modifyShow">
+            <el-col :span="15">
+              <el-form-item label="原因" prop="reason">
+                <el-input
+                  type="textarea"
+                  :rows="3"
+                  v-model="form.reason"
+                  placeholder="请输入原因" />
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-form-item>
+            <el-button type="primary" @click="submitForm">确 定</el-button>
+          </el-form-item>
+        </el-form>
+      </el-tab-pane>
+      <el-tab-pane label="历史跟踪">
+        <el-steps :active="stepActive" simple finish-status="success">
+          <el-step :title="stepTitle(item)" icon="el-icon-edit" v-for="(item) in handleTask.historyTask" ></el-step>
+        </el-steps>
+        <br/>
+        <el-steps direction="vertical" :active="stepActive"  space="65px">
+          <el-step :title="stepTitle(item)" :description="stepDes(item)" v-for="(item) in handleTask.historyTask" ></el-step>
+        </el-steps>
+      </el-tab-pane>
+      <el-tab-pane label="流程图">流程图-TODO</el-tab-pane>
+    </el-tabs>
+
+  </div>
+</template>
+
+<script>
+import { getLeave,updateLeave } from "@/api/oa/leave"
+import { taskSteps } from "@/api/oa/todo"
+import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict'
+export default {
+  name: "HrApproveLeave",
+  components: {
+  },
+  data() {
+    return {
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      },
+      handleTask: {
+        historyTask:[]
+      },
+      modifyShow: true,
+      reApplySelect: 1,
+      reApplyData: [
+        {
+          value: 0,
+          label: '取消申请'
+        },
+        {
+          value: 1,
+          label: '继续申请'
+        }
+      ],
+      statusFormat(row, column) {
+        return getDictDataLabel(DICT_TYPE.OA_LEAVE_STATUS, row.status)
+      },
+      leaveTypeDictData: getDictDatas(DICT_TYPE.OA_LEAVE_TYPE),
+      leaveStatusData: getDictDatas(DICT_TYPE.OA_LEAVE_STATUS)
+    };
+  },
+  mounted() {
+    const businessKey = this.$route.query.businessKey;
+    const taskId = this.$route.query.taskId;
+
+    this.getForm(businessKey,taskId);
+  },
+  computed:{
+    stepActive: function () {
+      let idx = 0;
+      for(let i=0; i<this.handleTask.historyTask.length; i++){
+        if(this.handleTask.historyTask[i].status === 1){
+          idx= idx+1;
+        }else{
+          break;
+        }
+      }
+      return idx;
+    },
+    stepTitle() {
+      return function (item) {
+        let name = item.stepName;
+        if (item.status === 1) {
+          name += '(已完成)'
+        }
+        if (item.status === 0) {
+          name += '(进行中)'
+        }
+        return name;
+      }
+    },
+    stepDes(){
+      return function (item) {
+        let desc = "";
+        if (item.status === 1) {
+          desc+="审批人:["+ item.assignee +"]    审批意见: [" + item.comment + "]   审批时间: " + this.parseTime(item.endTime);
+        }
+        return desc;
+      }
+    }
+  },
+  methods: {
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (!valid) {
+          return;
+        }
+        if (this.reApplySelect === 1) {
+          this.form.variables["reApply"] = true;
+          this.form.comment = '调整请假申请';
+        }
+        if (this.reApplySelect === 0) {
+          this.form.variables["reApply"] = false;
+          this.form.comment = '取消请假申请';
+        }
+        updateLeave(this.form).then(response => {
+          this.msgSuccess("修改成功");
+          this.$store.dispatch('tagsView/delView', this.$route).then(({ visitedViews }) => {
+            //if (this.isActive(this.$route)) {
+            this.$router.push({path: '/oa/todo'})
+            //}
+          })
+        });
+      });
+    },
+    getForm(id, taskId){
+      getLeave(id).then(response => {
+        this.form = response.data;
+        this.form.taskId = taskId;
+        this.form.variables = {};
+      });
+      const data = {
+        taskId : taskId,
+        businessKey: id,
+      }
+      taskSteps(data).then(response => {
+        this.handleTask = response.data;
+
+      });
+    },
+    reApplyChange(){
+      if (this.reApplySelect === 1) {
+        this.modifyShow = true;
+      }
+      if (this.reApplySelect === 0) {
+        this.modifyShow = false;
+      }
+    }
+
+  }
+};
+</script>

+ 22 - 3
yudao-admin-ui/src/views/oa/todo/index.vue

@@ -47,7 +47,7 @@
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
           <el-button size="mini" type="text" icon="el-icon-edit" v-if="scope.row.status == 1"  @click="handleClaim(scope.row)">签收</el-button>
-          <el-button size="mini" type="text" icon="el-icon-edit" v-if="scope.row.status == 2"  @click="handleLeaveApprove(scope.row)">办理</el-button>
+          <el-button size="mini" type="text" icon="el-icon-edit" v-if="scope.row.status == 2"  @click="getTaskFormKey(scope.row)">办理</el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -107,7 +107,7 @@
 </template>
 
 <script>
-import { completeTask, taskSteps, deleteLeave, getLeave, getTodoTaskPage, claimTask } from "@/api/oa/todo";
+import { completeTask, taskSteps, getTaskFormKey,deleteLeave, getLeave, getTodoTaskPage, claimTask } from "@/api/oa/todo";
 import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict'
 export default {
   name: "Todo",
@@ -220,7 +220,26 @@ export default {
       this.resetForm("queryForm");
       this.handleQuery();
     },
-
+    getTaskFormKey(row) {
+      const taskId = row.id;
+      const data = {
+        taskId : taskId
+      }
+      getTaskFormKey(data).then(response => {
+        const resp = response.data;
+        const path = resp.formKey;
+        const taskId = resp.id;
+        const businessKey =  resp.businessKey;
+        const route = {
+          path: path,
+          query: {
+            businessKey: businessKey,
+            taskId:taskId
+          }
+        }
+        this.$router.replace(route);
+      });
+    },
     handleLeaveApprove(row) {
       this.reset();
       const businessKey = row.businessKey;

+ 11 - 0
更新日志.md

@@ -6,6 +6,17 @@
 ## [v1.3.0] 待定
 
 * 工作流
+  * 修改表单为外置表单
+  * 新增菜单流程申请
+  * 请假流程如下
+    1. 请假人 在流程申请, 点击请假申请,填写表单提交
+    2. 部门领导(目前写死admin)  登陆,待办任务中 点击办理, 进行审批
+    3. hr 登陆(用户名:hradmin 密码:123456)  待办任务中 点击办理, 进行审批
+    4. 请假人登陆, 待办任务中, 确认
+    5. 流程结束
+  * 请假查询中,可以查询本人的请假申请, 和进度
+  * 流程跟踪图 待实现
+
 
 ## [v1.2.0] 进行中