1、什么是工作流

1.1、概述

工作流(Workflow),就是“业务过程的部分或整体在计算机应用环境下的自动化”,它主要解决的是“使在多个参与者之间按照某种预定义的规则传递文档、信息或任务的过程自动进行,从而实现某个预期的业务目标,或者促使此目标的实现”。

1.2、常见工作流

  1. 采用工作流有以下优点:

    • 提高系统的柔性,适应业务流程的变化
    • 实现更好的业务过程控制,提高顾客服务质量
    • 降低系统开发和维护成本
  2. 常见工作流:
    Activiti、JBPM、OSWorkflow、ActiveBPEL、YAWL等。本文主要采用 Activiti7 工作流开发。

1.3、流程实例

流程实例(ProcessInstance) 代表流程定义的执行实例,一个流程实例包括了所有的运行节点,我们可以利用这个对象来了解当前流程实例的进度等信息。
例如:用户或者程序安装流程定义的内容发起了一个流程,这个就是一个流程实例

1.4、业务管理

流程定义部署在 Activiti 后,我们就可以在系统中通过Activiti 去管理流程的执行,但是如果我们要将我们的流程实例和业务数据关联,这时我们需要使用到 Activiti 中预留的 BusinessKey(业务标识) 来关联

2、Activiti7 介绍

2.1、概念

Alfresco软件在2010年5月17日宣布Activiti业务流程管理(BPM)开源项目的正式启动。Activiti是一个工作流引擎, Activiti可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN2.0进行定义,业务流程按照预先定义的流程进行执行,实现了系统的流程由activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。

2.2、基本术语

  1. ProcessEngine对象: 这是Activiti工作的核心.负责生成流程运行时的各种实例及数据,监控和管理流程的运行
  2. BPM (业务流程管理):是一种以规范化的构造端到端的卓越业务流程为中心,以持续的提高组织业务绩效为目的的系统化方法
  3. BPMN(Business Process Model and Notation): 业务流程建模与标注,描述流程的基本符号,包括这些图元如何组合成一个业务流程图(Business Process Diagram)bpmn文件又可以叫做流程定义文件,它需要遵循BPMN语言规。
  4. 流对象(process engine):通过它可以获得我们需要的一切activiti服务,一个业务流程图有三个流对象的核心元素:

    • 事件:一个事件用圆圈来描述,表示一个业务流程期间发生的东西。事件影响流程的流动,一般有一个原因(触发器)或一个影响(结果),基于它们对流程的影响,有三种事件:开始事件、中间事件、终止事件。
    • 活动:用圆角矩形表示,一个流程由一个活动或多个活动组成。
    • 条件:条件用菱形表示,用于控制序列流的分支与合并,可以作为选择,包括路径的分支与合,内部的标记会给出控制流的类型。

2.3、系统服务类

  1. 结构图

 title=

  1. 核心类
    ProcessEngine: 流程引擎的抽象,可以通过此类获取需要的所有服务
  2. 服务类
    通过ProcessEngine获取,Activiti将不同生命周期的服务封装在不同Service中,包括定义、部署、运行。通过服务类可获取相关生命周期中的服务信息:
  • RepositoryService:Repository Service提供了对repository的存取服,Activiti中每一个不同版本的业务流程的定义都需要使用一些定义文件,部署文件和支持数据(例如BPMN2.0XML文件,表单定义文件,流程定义图像文件等),这些文件都存储在Activiti内建的Repository中。
  • RuntimeService:Runtime Service提供了启动流程,查询流程实例,设置获取流程实例变量等功能。此外它还提供了对流程部署,流程定义和流程实例的存取服务。
  • TaskService:Task Service提供了对用户Task和Form相关的操作。它提供了运行时任务查询、领取、完成、删除以及变量设置等功能。
  • HistoryService:History Service用于获取正在运行或已经完成的流程实例的信息,与Runtime Service获取的流程信息不同,历史信息包含已经持久化存储的永久信息,并已经被针对查询优化。
  • FormService:使用Form Service可以存取启动和完成任务所需的表单数据并且根据需要来渲染表单。Activiti中的流程和状态Task均可以关联业务相关的数据。
  • IdentityService:Identity Service提供了对Activiti系统中的用户和组的管理功,Activiti中内置了用户以及组管理的功能,必须使用这些用户和组的信息才能获取到相应的Task。
  • ManagementService:Management Service提供了对Activiti流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用。

2.4、数据库表结构

Activiti7 工作流总共包含25张数据表(Activiti6 包含23张表):

 title=

所有的表名默认以“ACT_”开头。表名的第二部分用两个字母表明表的用途:

  • ACT_GE (GE) :表示 general 全局通用数据及设置,各种情况都使用的数据。
  • ACT_HI (HI) :表示 history 历史数据表,包含着程执行的历史相关数据,如结束的流程实例,变量,任务,等等。
  • ACT_RE (RE) :表示 repository 存储,包含的是静态信息,如,流程定义,流程的资源(图片,规则等)。
  • ACT_RU (RU) :表示 runtime 运行时,运行时的流程变量,用户任务,变量,职责(job)等运行时的数据。
  • Activiti 只存储实例执行期间的运行时数据,当流程实例结束时,将删除这些记录。这就保证了这些运行时的表小且快。

activiti 生成的表名解释

表名解释
act_evt_log流程引擎通用日志表
act_ge_bytearray二进制表,存储通用的流程资源
act_ge_property系统存储表,存储整个流程引擎数据,默认存储三条数据
act_hi_actinst历史节点表
act_hi_attachment历史附件表
act_hi_comment历史意见表
act_hi_detail历史详情表
act_hi_identitylink历史用户信息表
act_hi_procinst历史流程实例表
act_hi_taskinst历史任务实例表
act_hi_varinst历史变量表
act_procdef_info流程定义的动态变更信息
act_re_deployment部署信息表
act_re_model流程设计实体表
act_re_procdef流程定义数据表
act_ru_deadletter_job作业失败表,失败次数>重试次数
act_ru_event_subscr运行时事件表
act_ru_execution运行时流程执行实例表
act_ru_identitylink运行时用户信息表
act_ru_integration运行时综合表
act_ru_job作业表
act_ru_suspended_job作业暂停表
act_ru_task运行时任务信息表
act_ru_timer_job运行时定时器表
act_ru_variable运行时变量表

3、集成 IDEA

3.1、部分效果

  • 流程图
     title=
  • 列表页
     title=
  • 请假申请
     title=
  • 同意申请
     title=
  • 驳回申请
     title=
  • 流程结束
     title=

3.2、下载插件

进到 IDEA 的插件页面,搜索插件 Activiti BPMN visualizing,点击安装,应用并完成:

 title=

3.3、配置文件

依赖管理 pom.xml

<dependencies>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--spring boot test-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <!-- web依赖springmvc -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--mybatis plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.2</version>
        </dependency>

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!--activiti 工作流的依赖-->
<!--        <dependency>-->
<!--            <groupId>org.activiti</groupId>-->
<!--            <artifactId>activiti-spring-boot-starter-basic</artifactId>-->
<!--            <version>5.1.17</version>-->
<!--        </dependency>-->

        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter</artifactId>
            <version>7.0.0.Beta2</version>
            <exclusions>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.el</groupId>
                    <artifactId>el-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-image-generator</artifactId>
            <version>7.0.0.Beta2</version>
        </dependency>
        <!--svg转png-->
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-transcoder</artifactId>
            <version>1.13</version>
        </dependency>
        <dependency>
            <groupId>batik</groupId>
            <artifactId>batik-util</artifactId>
            <version>1.6-1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-codec</artifactId>
            <version>1.7</version>
        </dependency>
    </dependencies>

配置文件 application.yml

server:
    port: 9204

spring:
    application:
        name: llh-activiti    # 注册到eureka上面的应用名称

    # 数据源
    datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/activiti?useSSL=false&useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&nullCatalogMeansCurrent=true
        username: root
        password: root

    # 工作流配置
    activiti:
        database-schema-update: true
        check-process-definitions: true
        process-definition-location-prefix: classpath:/process/
        history-level: full
        db-history-used: true

# mybatis plus配置
mybatis-plus:
    mapper-locations: classpath*:mapper/*.xml
    configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
        map-underscore-to-camel-case: true

配置类 SecurityConfig.java

package com.llh.config;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * Security框架配置
 */
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
              // 关闭csrf防护
              .csrf().disable()
              .headers().frameOptions().disable()
              .and()
              //定制url访问权限
              .authorizeRequests()
              //无限登录即可访问
              .antMatchers("/**").permitAll()
              //需要特定权限
//                .antMatchers("/sysUser/**","/sysAuthority/**").hasAnyAuthority("ROLE_ADMIN","ROLE_SA")
              //其他接口登录才能访问
//                .anyRequest().authenticated()
              .and();
    }
}

mybatis-plus配置 MybatisPlusConfig.java

package com.llh.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
 * User: lilinhan
 * DateTime: 2023/10/13 11:53
 */
@Configuration
@MapperScan(value = {"com.llh.mapper"})
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        // 分页拦截器
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return mybatisPlusInterceptor;
    }

    @Bean
    public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(5);// 核心线程数量
        taskExecutor.setMaxPoolSize(10);// 最大线程数量
        taskExecutor.setKeepAliveSeconds(2);// 设置时长(秒)
        taskExecutor.setQueueCapacity(10);// 设置队列容量
        taskExecutor.setThreadNamePrefix("llh-thread:");// 线程名前缀
        return taskExecutor;
    }

}

map 转换工具类 CommonUtils.java

package com.llh.utils;

import org.springframework.beans.BeanUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

public class CommonUtils {

    /**
     * 把指定的复杂对象属性,按照指定的内容,封装到新的map中
     *
     * @param source 目标对象
     * @param ps     需要封装到map中的属性
     * @return
     */
    public static Map<String, Object> objmap(Object source, String[] ps) {
        Map<String, Object> map = new HashMap<>();
        if (source == null)
            return null;
        if (ps == null || ps.length < 1) {
            return null;
        }
        for (String p : ps) {
            PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(
                  source.getClass(), p);
            if (sourcePd != null && sourcePd.getReadMethod() != null) {
                try {
                    Method readMethod = sourcePd.getReadMethod();
                    if (!Modifier.isPublic(readMethod.getDeclaringClass()
                          .getModifiers())) {
                        readMethod.setAccessible(true);
                    }
                    Object value = readMethod.invoke(source, new Object[0]);
                    map.put(p, value);
                } catch (Exception ex) {
                    throw new RuntimeException(
                          "Could not copy properties from source to target",
                          ex);
                }
            }
        }
        return map;
    }
}

3.4、创建流程图

  1. 由于在 yml 配置的路径在 resource 下面的 process 里面:
     title=
  2. 右键 process 文件夹新建:
     title=
  3. 右键生成的 xml 文件:
     title=
  4. 流程图画完之后保存到 process 下,如图所示:
     title=

3.5、部分代码

3.5.1、后端功能

测试类 AppTest.java

package com.llh;

import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Unit test for simple App.
 */
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class AppTest {
    @Autowired
    RepositoryService repositoryService;

    @Autowired
    RuntimeService runtimeService;

    @Autowired
    TaskService taskService;

    // 部署流程
    @Test
    public void deploy() {
        repositoryService.createDeployment()
              .name("请假审批")
              .addClasspathResource("process/test.bpmn20.xml")
              .addClasspathResource("process/diagram.png")
              .deploy();

    }

    // 发起请假:创建一个流程实例
    @Test
    public void starter(){
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("test");
        System.err.println("流程定义id-----"+processInstance.getProcessDefinitionId());
        System.err.println("流程实例id-----"+processInstance.getId());
        System.err.println("当前活动id-----"+processInstance.getActivityId());
    }

    // 根据负责人查询任务列表:代办任务
    @Test
    public void taskList(){
        List<Task> list = taskService.createTaskQuery()
              .processDefinitionKey("test")
//              .taskAssignee("zhangsan")
              .list();
        System.err.println(list);

        list.forEach(task -> {
            System.err.println(task.getProcessInstanceId());
            System.err.println(task.getId());
            System.err.println(task.getAssignee());
            System.err.println(task.getName());
        });
    }

    // 审批任务,处理任务
    @Test
    public void doTask(){
        // 处理任务前,先根据负责人查询出ta当前的代办任务
        Task task = taskService.createTaskQuery()
              .processDefinitionKey("test")
              .taskAssignee("xiaomi")
              .singleResult();
        // 处理代办任务,到下个节点
        // 传入负责人,appro==true|false
        Map<String,Object> map = new HashMap<>();
//        map.put("appro",true);
        taskService.complete(task.getId(),map);
        System.err.println("任务已完成");
    }

    // 查询流程定义信息
    @Test
    public void taskInfo(){
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        List<ProcessDefinition> test = processDefinitionQuery.processDefinitionKey("test")
              .orderByProcessDefinitionVersion()
              .desc()
              .list();

        test.forEach(t->{
            System.err.println("流程定义id-----"+t.getId());
            System.err.println("流程定义name-----"+t.getName());
            System.err.println("流程定义key-----"+t.getKey());
            System.err.println("流程定义version-----"+t.getVersion());
            System.err.println("流程部署ID-----"+t.getDeploymentId());
        });
    }

    // 删除流程
    @Test
    public void delTask(){
        repositoryService.deleteDeployment("",true);
    }
}

控制层 ExamController.java

package com.llh.controller;

import cn.hutool.core.date.DateUtil;
import com.llh.domain.Exam;
import com.llh.domain.UserTask;
import com.llh.service.ExamService;
import com.llh.utils.CommonUtils;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * User: lilinhan
 * DateTime: 2023/11/21 11:41
 */
@CrossOrigin
@RestController
@RequestMapping("/exam")
@Slf4j
public class ExamController {

    @Autowired
    RuntimeService runtimeService;

    @Autowired
    HistoryService historyService;

    @Autowired
    ExamService examService;

    @Autowired
    TaskService taskService;

    /**
     * 处理代办任务,同意和驳回共用这个方法
     * @param taskId  任务id
     * @param appro   跟流程图网关的条件表达式名称一致,具体操作 true|false
     * @return  map集合
     */
    @RequestMapping("/doTask")
    public Map<String,Object> doTask(String taskId,String appro){
        Map<String, Object> map = new HashMap<>();

        Map<String, Object> taskMap = new HashMap<>();
        taskMap.put("appro",appro);
        try {
            // 处理任务
            taskService.complete(taskId,taskMap);
            map.put("code",1001);
            // 查询代办任务
            List<Task> list = taskService.createTaskQuery()
                  .processDefinitionKey("test")
                  .list();
            // 遍历集合
            list.forEach(task -> {
                // 将当前负责人、流程名称返回给前端
                map.put("taskName",task.getName());
                map.put("assignee",task.getAssignee());
            });
        }catch (Exception e){
            map.put("code",1005);
            map.put("msg",e.getMessage());
            return map;
        }
        return map;
    }

    // 代办任务列表
    @RequestMapping("/taskList")
    public List<UserTask> taskList(Exam exam) {
        List<UserTask> list = new ArrayList<>();
        // 获取代办任务
        List<Task> tasks = taskService.createTaskQuery()
              .processDefinitionKey("test")
              .taskAssignee(exam.getName())
              .list();

        // 数组中写入要获取的数据
        String[] ps = {"id", "name", "assignee", "processDefinitionId", "processInstanceId"};
        for (Task task : tasks) {
            // 获取绑定业务表的主键
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            String businessKey = historicProcessInstance.getBusinessKey();
            // 根据关联id查询业务表
            Exam examDB = examService.getById(businessKey);
            // 计算请假天数
            long days = DateUtil.betweenDay(examDB.getStartDate(), examDB.getEndDate(), true);

            UserTask userTask = new UserTask();
            // 参数:source--要转换的对象,ps--需要封装到map中的属性
            Map<String, Object> objmap = CommonUtils.objmap(task, ps);
            userTask.setTask(objmap);
            userTask.setServiceKey(businessKey);
            userTask.setRemark(examDB.getRemark());
            userTask.setDays(days);
            userTask.setHistoryId(historicProcessInstance.getId());
            userTask.setExamName(examDB.getName());

            list.add(userTask);
        }
        return list;
    }

    // 请假申请
    @RequestMapping("/start")
    public Map<String, Object> start(Exam exam) {
        Map<String, Object> map = new HashMap<>();

        boolean save = examService.save(exam);
        String businessKey = exam.getId() + "";
        if (save) {
            // 创建一个流程,将业务表的id与历史状态表的businessKey绑定
            ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("test", businessKey);
            log.info("实例id:" + processInstance.getProcessDefinitionId());
            if (processInstance != null) {
                map.put("code", 1001);
                map.put("msg", "申请成功");
                return map;
            }
        }
        map.put("code", 1005);
        map.put("msg", "申请失败");
        return map;
    }

}

3.5.2、前端页面

ExamineView.vue

<script>
import qs from "qs";

export default {
    data(){
        return{
            dialogFormVisible:false,
            form:{},
            formLabelWidth:'150px',
            value1:'',
            tableData:[],
            formInline:{
                name:'zhangsan'
            },
        }
    },
    methods:{
        success(row){
            this.axios.get("http://localhost:9204/exam/doTask?taskId="+row.task.id+"&appro=true").then(res=>{
                if(res.data.code==1001){
                    var assignee  = res.data.assignee;
                    var taskName  = res.data.taskName;
                    if(assignee!=undefined && taskName!=undefined){
                        this.$message.success("已同意!当前需要审批的是:{负责人:"+assignee+",流程名称:"+taskName+"}");
                        // 通过当前负责人查询代办任务
                        this.formInline.name = assignee;
                        this.onSubmit();
                    }else {
                        this.$message.warning("当前没有任务咯!")
                        this.onSubmit();
                    }
                }else {
                    this.$message.error(res.data.msg);
                }
            })
        },
        redound(row){
            this.axios.get("http://localhost:9204/exam/doTask?taskId="+row.task.id+"&appro=false").then(res=>{
                if(res.data.code==1001){
                    var assignee  = res.data.assignee;
                    var taskName  = res.data.taskName;
                    if(assignee!=undefined && taskName!=undefined){
                        this.$message.warning("已驳回!当前需要审批的是:{负责人:"+assignee+",流程名称:"+taskName+"}");
                        // 通过当前负责人查询代办任务
                        this.formInline.name = assignee;
                        this.onSubmit();
                    }else {
                        this.$message.warning("当前没有任务咯!")
                        this.onSubmit();
                    }
                }else {
                    this.$message.error(res.data.msg);
                }
            })
        },
        onSubmit(){
            this.axios.post("http://localhost:9204/exam/taskList",qs.stringify(this.formInline)).then(res=>{
                this.tableData = res.data
            })
        },
        toExam(){
            this.form.startDate = this.value1[0]
            this.form.endDate = this.value1[1]
            this.axios.post("http://localhost:9204/exam/start",qs.stringify(this.form)).then(res=>{
                if(res.data.code==1001){
                    this.$message.success(res.data.msg);
                    this.dialogFormVisible = false
                    this.onSubmit();
                }else {
                    this.$message.error(res.data.msg);
                }
            })
        }
    },
    created() {

    }
}
</script>

<template>
<div>
    <el-form :inline="true" :model="formInline" class="demo-form-inline">
        <el-form-item label="负责人">
            <el-select v-model="formInline.name" placeholder="负责人" clearable="clearable">
                <el-option label="张三" value="zhangsan"></el-option>
                <el-option label="李四" value="lisi"></el-option>
                <el-option label="小米" value="xiaomi"></el-option>
            </el-select>
        </el-form-item>
        <el-form-item>
            <el-button type="primary" @click="onSubmit">查询</el-button>
            <el-button type="primary" @click="dialogFormVisible = true">请假申请</el-button>
        </el-form-item>
    </el-form>

    <el-dialog title="请假申请" :visible.sync="dialogFormVisible">
        <el-form :model="form">
            <el-form-item label="申请人姓名" :label-width="formLabelWidth">
                <el-input v-model="form.name" autocomplete="off"></el-input>
            </el-form-item>

            <el-form-item label="请假日期" :label-width="formLabelWidth">
                <div class="block">
                    <el-date-picker
                        v-model="value1"
                        type="daterange"
                        value-format="yyyy-MM-dd"
                        range-separator="至"
                        start-placeholder="开始日期"
                        end-placeholder="结束日期">
                    </el-date-picker>
                </div>
            </el-form-item>

            <el-form-item label="请假说明" :label-width="formLabelWidth">
                <el-input type="textarea" v-model="form.remark"></el-input>
            </el-form-item>
        </el-form>
        <div slot="footer" class="dialog-footer">
            <el-button @click="dialogFormVisible = false">取 消</el-button>
            <el-button type="primary" @click="toExam">确 定</el-button>
        </div>
    </el-dialog>

    <el-table
        :data="tableData"
        border
        style="width: 100%">
        <el-table-column prop="task.id" label="任务实例id" width="180"></el-table-column>
        <el-table-column prop="task.name" label="任务名称" width="180"></el-table-column>
        <el-table-column prop="task.assignee" label="负责人" width="180"></el-table-column>
        <el-table-column prop="remark" label="请假原因" width="180"></el-table-column>
        <el-table-column prop="days" label="请假天数" width="180"></el-table-column>
        <el-table-column prop="examName" label="申请人" width="180"></el-table-column>
        <el-table-column prop="historyId" label="历史流程实例id" width="180"></el-table-column>
        <el-table-column prop="serviceKey" label="绑定的业务id" width="180"></el-table-column>
        <el-table-column label="操作" width="180">
            <template v-slot="scope">
                <el-button type="primary" size="mini" @click="success(scope.row)">同意</el-button>
                <el-button type="warning" size="mini" @click="redound(scope.row)">驳回</el-button>
            </template>
        </el-table-column>
    </el-table>
</div>
</template>

<style scoped>

</style>

标签:no tag
本文到此就结束啦
Last modification:December 16, 2023
如果觉得我的文章对你有用,请随意赞赏