使用activiti实现工作流

安装activiti插件

在这里插入图片描述

设计工作流

在这里插入图片描述

部署工作流(导入Bpmn文件)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Bpmn文件(demo.bpmn)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 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" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1578711271300" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
  <process id="allowance" isClosed="false" isExecutable="true" name="补贴申请" processType="None">
    <startEvent id="begin" name="开始"></startEvent>
    <userTask activiti:assignee="#{username}" activiti:exclusive="true" id="selfVerify" name="申请人办理">
    </userTask>
    <endEvent id="end" name="结束"/>
    <sequenceFlow id="_5" name="发起申请" sourceRef="begin" targetRef="selfVerify"/>
    <userTask activiti:exclusive="true" id="leaderVerify" name="主管审核">
      <extensionElements>
        <activiti:taskListener event="create" class="com.yangzc.studentboot.workflow.allowance.listener.MyTaskListener">
		</activiti:taskListener>
      </extensionElements>
    </userTask>
    <sequenceFlow id="_8" name="提交" sourceRef="selfVerify" targetRef="leaderVerify">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='提交'}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="_9" name="同意" sourceRef="leaderVerify" targetRef="end">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='通过'}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="_6" name="放弃" sourceRef="selfVerify" targetRef="end">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='撤回'}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="_10" name="不同意" sourceRef="leaderVerify" targetRef="selfVerify">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='驳回'}]]></conditionExpression>
    </sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
    <bpmndi:BPMNPlane bpmnElement="allowance">
      <bpmndi:BPMNShape bpmnElement="begin" id="Shape-begin">
        <omgdc:Bounds height="32.0" width="32.0" x="215.0" y="30.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="selfVerify" id="Shape-selfVerify">
        <omgdc:Bounds height="55.0" width="85.0" x="190.0" y="155.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="end" id="Shape-end">
        <omgdc:Bounds height="32.0" width="32.0" x="215.0" y="415.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="leaderVerify" id="Shape-leaderVerify">
        <omgdc:Bounds height="55.0" width="85.0" x="190.0" y="290.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_5" id="BPMNEdge__5" sourceElement="_2" targetElement="_3">
        <omgdi:waypoint x="231.0" y="62.0"/>
        <omgdi:waypoint x="231.0" y="155.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_6" id="BPMNEdge__6" sourceElement="_3" targetElement="_4">
        <omgdi:waypoint x="190.0" y="182.5"/>
        <omgdi:waypoint x="125.0" y="305.0"/>
        <omgdi:waypoint x="215.0" y="431.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_8" id="BPMNEdge__8" sourceElement="_3" targetElement="_7">
        <omgdi:waypoint x="232.5" y="210.0"/>
        <omgdi:waypoint x="232.5" y="290.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_9" id="BPMNEdge__9" sourceElement="_7" targetElement="_4">
        <omgdi:waypoint x="231.0" y="345.0"/>
        <omgdi:waypoint x="231.0" y="415.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_10" id="BPMNEdge__10" sourceElement="_7" targetElement="_3">
        <omgdi:waypoint x="275.0" y="317.5"/>
        <omgdi:waypoint x="345.0" y="250.0"/>
        <omgdi:waypoint x="275.0" y="182.5"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

依赖配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.yangzc</groupId>
    <artifactId>studentboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>studentboot</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <mysql.version>5.1.41</mysql.version>
        <!--Activiti 启动器版本-->
        <activiti.starter.version>6.0.0</activiti.starter.version>
        <pagehelper.boot.version>1.2.5</pagehelper.boot.version>
    </properties>

    <dependencies>
<!--        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.3</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
        <!--druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>
        <!--commons -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.6</version>
        </dependency>
        <dependency>
            <groupId>commons-configuration</groupId>
            <artifactId>commons-configuration</artifactId>
            <version>1.10</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
        </dependency>
        <!-- 文件上传工具类 -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
        </dependency>
        <!--shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.2</version>
        </dependency>
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
        <!-- shiro ehcache -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>net.sf.ehcache</groupId>
                    <artifactId>ehcache-core</artifactId>
                </exclusion>
            </exclusions>
            <version>1.4.0</version>
        </dependency>
        <!-- ehchache -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>
        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
        <!-- 添加redis支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
        <!-- shiro-redis -->
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>3.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter-basic</artifactId>
            <version>${activiti.starter.version}</version>
        </dependency>
        <!-- yml解析器 -->
        <dependency>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.17</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.17</version>
        </dependency>
        <!-- pagehelper 分页插件 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>${pagehelper.boot.version}</version>
        </dependency>
        <!--activiti modeler 5.22 start-->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-json-converter</artifactId>
            <version>6.0.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.activiti</groupId>
                    <artifactId>activiti-bpmn-model</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- xml解析依赖-->
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-codec</artifactId>
            <version>1.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-css</artifactId>
            <version> 1.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-svg-dom</artifactId>
            <version>1.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-svggen</artifactId>
            <version>1.7</version>
        </dependency>
        <!-- xml解析依赖-->
        <!--activiti modeler 5.22 end-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <executable>true</executable>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.7</version>
                <configuration>
                    <verbose>true</verbose>
                    <overwrite>true</overwrite>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.7</version>
                    </dependency>
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>${mysql.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

</project>

配置SpringBoot(application.yml)

server:
  session-timeout: 1800
#    tomcat:
#        max-threads: 1000
#        min-spare-threads: 30
  port: 8080
#    uri-encoding: utf-8
#security:
#  basic:
#    enabled: false
spring:
  thymeleaf:
    mode: LEGACYHTML5
    cache: false
  jackson:
    time-zone: GMT+8
    date-format: yyyy-MM-dd HH:mm:ss
  profiles: 
    active: dev
  servlet:
    multipart:
      max-file-size: 30MB
      max-request-size: 30MB
  devtools:
    restart:
      enabled: true
  cache:
    type: redis
   # ehcache:
     # config: classpath:config/ehcache.xml
  # RuoYi-Process 模块
  # 解决启动报错:class path resource [processes/] cannot be resolved to URL because it does not exist
  activiti:
    check-process-definitions: false
    # 检测身份信息表是否存在
    db-identity-used: true
mybatis:
  configuration:
    map-underscore-to-camel-case: true
  mapper-locations: mybatis/**/*Mapper.xml
  typeAliasesPackage: com.yangzc.studentboot.**.domain
#[弃用]配置缓存和session存储方式,默认ehcache,可选redis,[弃用]调整至 spring cache type【shiro.用户,权限,session,spring.cache通用】
#[弃用]cacheType: ehcache

工作流部署接口

package com.yangzc.studentboot.workflow.definition.controller;

import com.yangzc.studentboot.common.annotation.Log;
import com.yangzc.studentboot.common.config.Global;
import com.yangzc.studentboot.common.config.Constant;
import com.yangzc.studentboot.common.controller.BaseController;
import com.yangzc.studentboot.common.domain.AjaxResult;
import com.yangzc.studentboot.common.domain.TableDataInfo;
import com.yangzc.studentboot.common.utils.StringUtils;
import com.yangzc.studentboot.common.utils.FileUploadUtils;
import com.yangzc.studentboot.common.utils.ExcelUtil;
import com.yangzc.studentboot.workflow.definition.domain.ProcessDefinition;
import com.yangzc.studentboot.workflow.definition.service.ProcessDefinitionService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@Controller
@RequestMapping("/workflow/definition")
public class ProcessDefinitionController extends BaseController {

    private static final Logger log = LoggerFactory.getLogger(ProcessDefinitionController.class);

    private String prefix = "workflow/definition";

    @Autowired
    private ProcessDefinitionService processDefinitionService;

    @RequiresPermissions("workflow:definition:list")
    @GetMapping("/list")
    public String processDefinition() {
        return prefix + "/definition";
    }

    @PostMapping("/data")
    @RequiresPermissions("workflow:definition:list")
    @ResponseBody
    public TableDataInfo list(ProcessDefinition processDefinition) {
        List<ProcessDefinition> list = processDefinitionService.listProcessDefinition(processDefinition);
        return getDataTable(list);
    }

    /**
     * 部署流程定义
     */
    @RequiresPermissions("workflow:definition:list")
    @Log("流程定义导入")
    @PostMapping("/upload")
    @ResponseBody
    public AjaxResult upload(@RequestParam("processDefinition") MultipartFile file) {
        try {
            if (!file.isEmpty()) {
                String extensionName = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf('.') + 1);
                if (!"bpmn".equalsIgnoreCase(extensionName)
                        && !"zip".equalsIgnoreCase(extensionName)
                        && !"bar".equalsIgnoreCase(extensionName)) {
                    return error("流程定义文件仅支持 bpmn, zip 和 bar 格式!");
                }
                // p.s. 此时 FileUploadUtils.upload() 返回字符串 fileName 前缀为 Constants.RESOURCE_PREFIX,需剔除
                // 详见: FileUploadUtils.getPathFileName(...)
                String fileName = FileUploadUtils.upload(Global.getProfile() + "/processDefiniton", file);
                if (StringUtils.isNotBlank(fileName)) {
                    String realFilePath = Global.getProfile() + fileName.substring(Constant.RESOURCE_PREFIX.length());
                    processDefinitionService.deployProcessDefinition(realFilePath);
                    return success();
                }
            }
            return error("不允许上传空文件!");
        }
        catch (Exception e) {
            log.error("上传流程定义文件失败!", e);
            return error(e.getMessage());
        }
    }

    @RequiresPermissions("workflow:definition:list")
    @Log("流程定义删除")
    @PostMapping("/remove")
    @ResponseBody
    public AjaxResult remove(String ids) {
        try {
            return toAjax(processDefinitionService.deleteProcessDeploymentByIds(ids));
        }
        catch (Exception e) {
            return error(e.getMessage());
        }
    }

    @Log("流程定义导出")
    @RequiresPermissions("workflow:definition:list")
    @PostMapping("/export")
    @ResponseBody
    public AjaxResult export() {
        List<ProcessDefinition> list = processDefinitionService.listProcessDefinition(new ProcessDefinition());
        ExcelUtil<ProcessDefinition> util = new ExcelUtil<>(ProcessDefinition.class);
        return util.exportExcel(list, "流程定义数据");
    }

}

工作流部署页面

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
	<th:block th:include="include :: header" />
	<th:block th:include="include :: jasny-bootstrap-css" />
	<th:block th:include="include :: bootstrap-fileinput-css" />
</head>
<body class="gray-bg">
	 <div class="container-div">
		 <div class="row">
			 <div class="col-sm-12 search-collapse">
				 <form id="formId">
					 <div class="select-list">
						 <ul>
							 <li>
								 <p>流程KEY:</p>
								 <input type="text" name="key"/>
							 </li>
							 <li>
								 <p>名称:</p>
								 <input type="text" name="name"/>
							 </li>
							 <li>
								 <p>所属分类:</p>
								 <input type="text" name="category"/>
							 </li>
							 <!--<li class="select-time">
								 <label>部署时间: </label>
								 <input type="text" class="time-input" id="startTime" placeholder="开始时间" name="params[beginTime]"/>
								 <span>-</span>
								 <input type="text" class="time-input" id="endTime" placeholder="结束时间" name="params[endTime]"/>
							 </li>-->
							 <li>
								 <a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i>&nbsp;搜索</a>
								 <a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i>&nbsp;重置</a>
							 </li>
						 </ul>
					 </div>
				 </form>
			 </div>

			 <div class="btn-group-sm" id="toolbar" role="group">
				 <div id="processDefinitionDiv" class="fileinput fileinput-new" data-provides="fileinput" style="margin-bottom: 0; margin-right: -3px;">
										<span class="btn btn-success btn-file" style="font-size: 12px;">
											<span><i class="fa fa-upload"></i> 部署流程定义</span>
											<input type="file" name="processDefinition" multiple>
										</span>
					 <span class="fileinput-filename"></span>
					 <a href="#" class="close fileinput-exists" data-dismiss="fileinput" style="float: none">&times;</a>
				 </div>
				 <a class="btn btn-danger multiple disabled" onclick="$.operate.removeAll()" shiro:hasPermission="workflow:definition:list">
					 <i class="fa fa-remove"></i> 删除
				 </a>
				 <a class="btn btn-warning" onclick="$.table.exportExcel()" shiro:hasPermission="workflow:definition:list">
					 <i class="fa fa-download"></i> 导出
				 </a>
			 </div>
			 <div class="col-sm-12 select-table table-striped">
				 <table id="bootstrap-table"></table>
			 </div>
		 </div>
	 </div>

    <div th:include="include :: footer"></div>
	<th:block th:include="include :: jasny-bootstrap-js" />
	<th:block th:include="include :: bootstrap-fileinput-js" />
    <script th:inline="javascript">
				var editFlag = [[${@permission.hasPermi('workflow:definition:list')}]];
				var removeFlag = [[${@permission.hasPermi('workflow:definition:list')}]];
        var prefix = ctx + "workflow/definition";
        $(function() {
            var options = {
                url: prefix + "/data",
								removeUrl: prefix + "/remove",
								exportUrl: prefix + "/export",
								modalName: "流程定义",
								uniqueId: "deploymentId",
                columns: [{
		            checkbox: true
		        },
				{
					field : 'id',
					title : '流程ID'
				},
				{
					field : 'key',
					title : '流程KEY'
				},
				{
					field : 'name',
					title : '流程名称'
				},
				{
					field : 'version',
					title : '版本'
				},
				{
					field : 'description',
					title : '流程描述'
				},
				{
					field : 'category',
					title : '所属分类'
				},
				{
					field : 'deploymentTime',
					title : '部署时间'
				},
				{
						title: '操作',
						align: 'center',
						formatter: function(value, row, index) {
							var actions = [];
							actions.push('<a class="btn btn-danger btn-xs ' + removeFlag + '" href="javascript:void(0)" οnclick="$.operate.remove(\'' + row.deploymentId + '\')"><i class="fa fa-remove"></i> 删除</a>');
							return actions.join('');
						}
				}]
            };
            $.table.init(options);

            initProcessDefinitionFileInput();
        });

        function initProcessDefinitionFileInput() {
					$('#processDefinitionDiv').on('change.bs.fileinput', function (e) {
						// 处理自己的业务
						var formdata = new FormData();
						formdata.append("processDefinition", $('input[type=file]')[0].files[0]);
						$.ajax({
							url: prefix + '/upload',
							data: formdata,
							type: "post",
							processData: false,
							contentType: false,
							success: function(result) {
								$('#processDefinitionDiv').fileinput('reset');     // 重置
								$.operate.ajaxSuccess(result);
							}
						})
					});
				}

    </script>
</body>
</html>

工作流管理接口

package com.yangzc.studentboot.workflow.allowance.controller;

import com.yangzc.studentboot.common.annotation.Log;
import com.yangzc.studentboot.common.controller.BaseController;
import com.yangzc.studentboot.common.domain.AjaxResult;
import com.yangzc.studentboot.common.domain.TableDataInfo;
import com.yangzc.studentboot.common.utils.StringUtils;
import com.yangzc.studentboot.common.utils.ExcelUtil;
import com.yangzc.studentboot.common.utils.ShiroUtils;
import com.yangzc.studentboot.workflow.allowance.domain.BizAllowanceVo;
import com.yangzc.studentboot.workflow.allowance.service.BizAllowanceService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.text.SimpleDateFormat;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: yangzc
 * @Description:
 * @Date: Created on 16:45 2020/1/11
 * @Modified By:
 */
@Controller
@RequestMapping("/workflow/allowance")
public class BizAllowanceController extends BaseController {
    private String prefix = "workflow/allowance";

    @Autowired
    private BizAllowanceService bizAllowanceService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private IdentityService identityService;

    @RequiresPermissions("workflow:allowance:apply:list")
    @GetMapping("/apply/list")
    public String allowance(ModelMap mmap) {
        mmap.put("currentUser", ShiroUtils.getUser());
        return prefix + "/allowance";
    }

    /**
     * 查询补贴申请列表
     */
    @RequiresPermissions("workflow:allowance:apply:list")
    @PostMapping("/data")
    @ResponseBody
    public TableDataInfo list(BizAllowanceVo bizAllowance) {
        if (!ShiroUtils.getUser().getUsername().equals("admin")) {
            bizAllowance.setCreateBy(ShiroUtils.getUser().getUsername());
        }
        startPage();
        List<BizAllowanceVo> list = bizAllowanceService.selectBizAllowanceList(bizAllowance);
        return getDataTable(list);
    }

    /**
     * 导出补贴申请列表
     */
    @RequiresPermissions("process:allowance:export")
    @PostMapping("/export")
    @ResponseBody
    public AjaxResult export(BizAllowanceVo bizAllowance) {
        List<BizAllowanceVo> list = bizAllowanceService.selectBizAllowanceList(bizAllowance);
        ExcelUtil<BizAllowanceVo> util = new ExcelUtil<BizAllowanceVo>(BizAllowanceVo.class);
        return util.exportExcel(list, "allowance");
    }

    /**
     * 新增补贴申请
     */
    @GetMapping("/add")
    public String add() {
        return prefix + "/add";
    }

    /**
     * 新增保存补贴申请
     */
    @RequiresPermissions("workflow:allowance:apply:add")
    @Log("补贴申请")
    @PostMapping("/add")
    @ResponseBody
    public AjaxResult addSave(BizAllowanceVo bizAllowance) {
        Long userId = ShiroUtils.getUserId();
        if (ShiroUtils.getUser().getUsername().equals("admin")) {
            return error("提交申请失败:不允许管理员提交申请!");
        }
        return toAjax(bizAllowanceService.insertBizAllowance(bizAllowance));
    }

    /**
     * 修改补贴申请
     */
    @GetMapping("/edit/{id}")
    public String edit(@PathVariable("id") Long id, ModelMap mmap) {
        BizAllowanceVo bizAllowance = bizAllowanceService.selectBizAllowanceById(id);
        mmap.put("bizAllowance", bizAllowance);
        return prefix + "/edit";
    }

    /**
     * 修改保存补贴申请
     */
    @RequiresPermissions("process:allowance:edit")
    @Log("补贴申请修改")
    @PostMapping("/edit")
    @ResponseBody
    public AjaxResult editSave(BizAllowanceVo bizAllowance) {
        return toAjax(bizAllowanceService.updateBizAllowance(bizAllowance));
    }

    /**
     * 删除补贴申请
     */
    @RequiresPermissions("process:allowance:remove")
    @Log("补贴申请删除")
    @PostMapping( "/remove")
    @ResponseBody
    public AjaxResult remove(String ids) {
        return toAjax(bizAllowanceService.deleteBizAllowanceByIds(ids));
    }

    /**
     * 提交申请
     */
    @Log("补贴申请提交")
    @PostMapping( "/submitApply")
    @ResponseBody
    public AjaxResult submitApply(Long id) {
        BizAllowanceVo allowance = bizAllowanceService.selectBizAllowanceById(id);
        String applyUserId = ShiroUtils.getUser().getUsername();
        bizAllowanceService.submitApply(allowance, applyUserId);
        return success();
    }

    @RequiresPermissions("workflow:allowance:task:list")
    @GetMapping("/task/list")
    public String todoView() {
        return prefix + "/allowanceToDo";
    }

    /**
     * 我的待办列表
     * @param bizAllowance
     * @return
     */
    @RequiresPermissions("workflow:allowance:task:list")
    @PostMapping("/task/data")
    @ResponseBody
    public TableDataInfo taskList(BizAllowanceVo bizAllowance) {
        startPage();
        List<BizAllowanceVo> list = bizAllowanceService.findTodoTasks(bizAllowance, ShiroUtils.getUser().getUsername());
        return getDataTable(list);
    }

    /**
     * 加载审批弹窗
     * @param taskId
     * @param mmap
     * @return
     */
    @GetMapping("/showVerifyDialog/{taskId}")
    public String showVerifyDialog(@PathVariable("taskId") String taskId, ModelMap mmap) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        String processInstanceId = task.getProcessInstanceId();
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        BizAllowanceVo bizAllowance = bizAllowanceService.selectBizAllowanceById(new Long(processInstance.getBusinessKey()));
        mmap.put("bizAllowance", bizAllowance);
        mmap.put("taskId", taskId);
        String verifyName = task.getTaskDefinitionKey().substring(0, 1).toUpperCase() + task.getTaskDefinitionKey().substring(1);
        return prefix + "/task" + verifyName;
    }

    /**
     * 完成任务
     *
     * @return
     */
    @RequestMapping(value = "/complete/{taskId}", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public AjaxResult complete(@PathVariable("taskId") String taskId, @RequestParam(value = "saveEntity", required = false) String saveEntity,
                               @ModelAttribute("preloadAllowance") BizAllowanceVo allowance, HttpServletRequest request) {
        boolean saveEntityBoolean = BooleanUtils.toBoolean(saveEntity);
        Map<String, Object> variables = new HashMap<String, Object>();
        Enumeration<String> parameterNames = request.getParameterNames();
        String comment = null;          // 批注
        try {
            while (parameterNames.hasMoreElements()) {
                String parameterName = (String) parameterNames.nextElement();
                if (parameterName.startsWith("p_")) {
                    // 参数结构:p_B_name,p为参数的前缀,B为类型,name为属性名称
                    String[] parameter = parameterName.split("_");
                    if (parameter.length == 3) {
                        String paramValue = request.getParameter(parameterName);
                        Object value = paramValue;
                        if (parameter[1].equals("B")) {
                            value = BooleanUtils.toBoolean(paramValue);
                        } else if (parameter[1].equals("DT")) {
                            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
                            value = sdf.parse(paramValue);
                        } else if (parameter[1].equals("COM")) {
                            comment = paramValue;
                        } else if (parameter[1].equals("STR")) {
                            value = paramValue;
                        }
                        variables.put(parameter[2], value);
                    } else {
                        throw new RuntimeException("invalid parameter for activiti variable: " + parameterName);
                    }
                }
            }
            if (StringUtils.isNotEmpty(comment)) {
                identityService.setAuthenticatedUserId(ShiroUtils.getUser().getUsername());
                taskService.addComment(taskId, allowance.getInstanceId(), comment);
            }
            bizAllowanceService.complete(allowance, saveEntityBoolean, taskId, variables);

            return success("任务已完成");
        } catch (Exception e) {
            //logger.error("error on complete task {}, variables={}", new Object[]{taskId, variables, e});
            return error("完成任务失败");
        }
    }

    /**
     * 自动绑定页面字段
     */
    @ModelAttribute("preloadAllowance")
    public BizAllowanceVo getAllowance(@RequestParam(value = "id", required = false) Long id, HttpSession session) {
        if (id != null) {
            return bizAllowanceService.selectBizAllowanceById(id);
        }
        return new BizAllowanceVo();
    }

    @RequiresPermissions("workflow:allowance:done:list")
    @GetMapping("/done/list")
    public String doneView() {
        return prefix + "/allowanceDone";
    }

    /**
     * 我的已办列表
     * @param bizAllowance
     * @return
     */
    @RequiresPermissions("workflow:allowance:done:list")
    @PostMapping("/done/data")
    @ResponseBody
    public TableDataInfo taskDoneList(BizAllowanceVo bizAllowance) {
        startPage();
        List<BizAllowanceVo> list = bizAllowanceService.findDoneTasks(bizAllowance, ShiroUtils.getUser().getUsername());
        return getDataTable(list);
    }
    
}

申请单列表页面

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <th:block th:include="include :: header" />
    <th:block th:include="include :: select2-css" />
</head>
<body class="gray-bg">
     <div class="container-div">
        <div class="row">
            <div class="col-sm-12 search-collapse">
                <form id="formId">
                    <div class="select-list">
                        <ul>
                            <li>
                                <p>标题:</p>
                                <input type="text" name="title"/>
                            </li>
                            <li class="select-time">
                                <p>申请时间:</p>
                                <input type="text" class="time-input" id="startTime" placeholder="开始时间" name="params[beginTime]"/>
                                <span>-</span>
                                <input type="text" class="time-input" id="endTime" placeholder="结束时间" name="params[endTime]"/>
                            </li>
                            <li>
                                <p>流程实例ID:</p>
                                <input type="text" name="instanceId"/>
                            </li>
                            <li>
                                <a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i>&nbsp;搜索</a>
                                <a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i>&nbsp;重置</a>
                            </li>
                        </ul>
                    </div>
                </form>
            </div>

            <div class="btn-group-sm" id="toolbar" role="group">
                <a class="btn btn-success" onclick="$.operate.add()" shiro:hasPermission="workflow:allowance:apply:add">
                    <i class="fa fa-plus"></i> 添加
                </a>
                <!--<a class="btn btn-warning" οnclick="$.table.exportExcel()" shiro:hasPermission="process:allowance:export">
                    <i class="fa fa-download"></i> 导出
                 </a>-->
            </div>
            <div class="col-sm-12 select-table table-striped">
                <table id="bootstrap-table"></table>
            </div>
        </div>
    </div>
    <th:block th:include="include :: footer" />
     <th:block th:include="include :: select2-js" />
    <script th:inline="javascript">
        var editFlag = [[${@permission.hasPermi('workflow:allowance:edit')}]];
        var removeFlag = [[${@permission.hasPermi('workflow:allowance:remove')}]];
        var prefix = ctx + "workflow/allowance";
        var currentUser = [[${currentUser}]];
        console.log("==============" + currentUser.username);
        $(function() {
            var options = {
                url: prefix + "/data",
                createUrl: prefix + "/add",
                updateUrl: prefix + "/edit/{id}",
                removeUrl: prefix + "/remove",
                exportUrl: prefix + "/export",
                detailUrl: prefix + "/edit/{id}",
                sortName: "applyTime",
                sortOrder: "desc",
                modalName: "补贴申请",
                columns: [{
                    checkbox: true
                },
                {
                    field : 'id',
                    title : '主键ID',
                    visible: false
                },
                {
                    field : 'title',
                    title : '标题'
                },
                {
                    field : 'reason',
                    title : '原因'
                },
                {
                    field : 'totalMoney',
                    title : '补贴金额',
                    formatter: function(value, row, index) {
                        if (!value) return '未知';
                        return parseFloat(value).toFixed(2);
                    }
                },
                {
                    field : 'instanceId',
                    title : '流程实例ID'
                },
                {
                    field: 'createBy',
                    title: '创建人ID',
                    visible: false
                },
                {
                    field: 'createUserName',
                    title: '创建人'
                },
                {
                    field: 'applyUser',
                    title: '申请人ID',
                    visible: false
                },
                {
                    field: 'applyUserName',
                    title: '<span style="color: blue;">申请人</span>',
                    formatter: function(value, row, index) {
                        return '<span style="color: blue;">' + (value ? value : "-") + '</span>';
                    }
                },
                {
                  field: 'applyTime',
                  title: '申请时间'
                },
                {
                    field: 'taskId',
                    title: '当前任务ID',
                    visible: false
                },
                {
                    field: 'taskName',
                    title: '当前任务名称',
                    align: 'center',
                    formatter: function(value, row, index) {
                        return '<span class="badge badge-primary">' + value + '</span>';
                    }
                },
                {
                    title: '操作',
                    align: 'center',
                    formatter: function(value, row, index) {
                        var actions = [];
                        if (row.instanceId) {
                            actions.push('<a class="btn btn-primary btn-xs" href="javascript:void(0)" οnclick="$.operate.detail(\'' + row.id + '\')"><i class="fa fa-eye"></i> 表单数据</a> ');
                            actions.push('<a class="btn btn-warning btn-xs" href="javascript:void(0)" οnclick="showHistoryDialog(\'' + row.instanceId + '\')"><i class="fa fa-list"></i> 审批历史</a> ');
                            actions.push('<a class="btn btn-info btn-xs" href="javascript:void(0)" οnclick="showProcessImgDialog(\'' + row.instanceId + '\')"><i class="fa fa-image"></i> 进度查看</a> ');
                        } else {
                            actions.push('<a class="btn btn-success btn-xs" href="javascript:void(0)" οnclick="submitApply(\'' + row.id + '\',\'' + row.createBy + '\')"><i class="fa fa-user"></i> 提交申请</a> ');
                            actions.push('<a class="btn btn-primary btn-xs" href="javascript:void(0)" οnclick="editForm(\'' + row.id + '\',\'' + row.createBy + '\')"><i class="fa fa-edit"></i> 编辑</a> ');
                            actions.push('<a class="btn btn-danger btn-xs" href="javascript:void(0)" οnclick="removeForm(\'' + row.id + '\',\'' + row.createBy + '\')"><i class="fa fa-remove"></i> 删除</a>');
                        }
                        return actions.join('');
                    }
                }]
            };
            $.table.init(options);
        });

        function submitApply(id, createBy) {
            if (createBy !== currentUser.username) {
                $.modal.alertWarning("不允许非创建人提交申请!");
                return;
            }
            $.modal.confirm("确认要提交申请吗?", function() {
                var url = prefix + "/submitApply";
                var data = { "id": id };
                $.operate.submit(url, "post", "json", data);
            });
        }

        function editForm(id, createBy) {
            if (createBy !== currentUser.username) {
                $.modal.alertWarning("不允许非创建人编辑表单!");
                return;
            }
            $.operate.edit(id);
        }

        function removeForm(id, createBy) {
            if (createBy !== currentUser.username) {
                $.modal.alertWarning("不允许非创建人删除表单!");
                return;
            }
            $.operate.remove(id);
        }

        /* 查看审批历史 */
        function showHistoryDialog(instanceId) {
            var url = ctx + 'workflow/general/historyList/' + instanceId;
            $.modal.open("查看审批历史", url);
        }

        function showProcessImgDialog(instanceId) {
            var url = ctx + 'workflow/general/processImg/' + instanceId;
            $.modal.open("查看流程图 (标红表示已结束或活动中的流程)", url);
        }
    </script>
</body>
</html>

申请单新增页面

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
    <th:block th:include="include :: header" />
</head>
<body class="white-bg">
    <div class="wrapper wrapper-content animated fadeInRight ibox-content">
        <form class="form-horizontal m" id="form-allowance-add">
            <div class="form-group">
                <label class="col-sm-3 control-label"><span style="color: red; ">*</span>标题:</label>
                <div class="col-sm-8">
                    <input name="title" class="form-control" type="text" required>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label"><span style="color: red; ">*</span>原因:</label>
                <div class="col-sm-8">
                    <textarea name="reason" class="form-control" required></textarea>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label">补贴金额:</label>
                <div class="col-sm-8">
                    <input name="totalMoney" class="form-control" type="text">
                </div>
            </div>
        </form>
    </div>
    <th:block th:include="include :: footer" />
    <script th:inline="javascript">
        var prefix = ctx + "workflow/allowance"
        $("#form-allowance-add").validate({
            focusCleanup: true
        });

        function submitHandler() {
            if ($.validate.form()) {
                $.operate.save(prefix + "/add", $('#form-allowance-add').serialize());
            }
        }

    </script>
</body>
</html>

申请单详情页面

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
    <th:block th:include="include :: header" />
    <th:block th:include="include :: datetimepicker-css" />
    <th:block th:include="include :: select2-css" />
</head>
<body class="white-bg">
    <div class="wrapper wrapper-content animated fadeInRight ibox-content">
        <form class="form-horizontal m" id="form-allowance-edit" th:object="${bizAllowance}">
            <input name="id" th:field="*{id}" type="hidden">
            <div class="form-group">
                <label class="col-sm-3 control-label">申请人:</label>
                <div class="col-sm-8">
                    <input name="applyUserName" th:field="*{applyUserName}" class="form-control" type="text" readonly>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label">申请时间:</label>
                <div class="col-sm-8">
                    <div class="input-group date">
                        <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
                        <input name="applyTime" th:value="${#dates.format(bizAllowance.applyTime, 'yyyy-MM-dd HH:mm')}" class="form-control" type="text" readonly>
                    </div>
                </div>
            </div>

            <div class="form-group">
                <label class="col-sm-3 control-label"><span style="color: red; ">*</span>标题:</label>
                <div class="col-sm-8">
                    <input name="title" th:field="*{title}" class="form-control" type="text" required>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label"><span style="color: red; ">*</span>原因:</label>
                <div class="col-sm-8">
                    <textarea name="reason" class="form-control" required>[[*{reason}]]</textarea>
                </div>
            </div>

            <div class="form-group">
                <label class="col-sm-3 control-label">补贴金额:</label>
                <div class="col-sm-8">
                    <input name="totalMoney" th:field="*{totalMoney}" class="form-control" type="text">
                </div>
            </div>
        </form>
    </div>
    <th:block th:include="include :: footer" />
    <th:block th:include="include :: datetimepicker-js" />
    <th:block th:include="include :: select2-js" />
    <script th:inline="javascript">
        var prefix = ctx + "process/allowance";
        $("#form-allowance-edit").validate({
            focusCleanup: true
        });

        function submitHandler() {
            if ($.validate.form() && validateStartTimeAndEndTime()) {
                $.operate.save(prefix + "/edit", $('#form-allowance-edit').serialize());
            }
        }

    </script>
</body>
</html>

申请单审批页面(申请人办理)

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
    <th:block th:include="include :: header" />
    <th:block th:include="include :: select2-css" />
</head>
<body class="white-bg">
    <div class="wrapper wrapper-content animated fadeInRight ibox-content">
        <form class="form-horizontal m" id="form-allowance-edit" th:object="${bizAllowance}">
            <input name="id" th:field="*{id}" type="hidden">
            <input name="taskId" th:field="*{taskId}" type="hidden">
            <input type="hidden" name="p_COM_comment" />
            <div class="form-group">
                <label class="col-sm-3 control-label">申请人:</label>
                <div class="col-sm-8">
                    <input name="applyUserName" th:field="*{applyUserName}" class="form-control" type="text" readonly>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label">申请时间:</label>
                <div class="col-sm-8">
                    <div class="input-group date">
                        <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
                        <input name="applyTime" th:value="${#dates.format(bizAllowance.applyTime, 'yyyy-MM-dd HH:mm')}" class="form-control" type="text" disabled>
                    </div>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label">标题:</label>
                <div class="col-sm-8">
                    <input name="title" th:field="*{title}" class="form-control" type="text" readonly>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label">原因:</label>
                <div class="col-sm-8">
                    <textarea name="reason" class="form-control" readonly>[[*{reason}]]</textarea>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label">补贴金额:</label>
                <div class="col-sm-8">
                    <input name="totalMoney" th:field="*{totalMoney}" class="form-control" type="text" readonly>
                </div>
            </div>
            <hr />
            <div class="form-group">
                <label class="col-sm-3 control-label" for="selfApproved">处理任务:</label>
                <div class="col-sm-8">
                    <select name="p_STR_outcome" id="selfApproved" class="form-control m-b">
                        <option value="提交">提交</option>
                        <option value="撤回">放弃</option>
                    </select>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label">批注:</label>
                <div class="col-sm-8">
                    <textarea name="comment" class="form-control"></textarea>
                </div>
            </div>
        </form>
    </div>
    <th:block th:include="include :: footer" />
    <th:block th:include="include :: select2-js" />
    <script th:inline="javascript">
        var prefix = ctx + "workflow/allowance";
        $("#form-allowance-edit").validate({
            focusCleanup: true
        });

        function submitHandler() {
            if ($.validate.form()) {
                if ($('textarea[name="comment"]').val()) {
                    $('input[name="p_COM_comment"]').val($('textarea[name="comment"]').val());
                }
                var taskId = [[${taskId}]];
                $.operate.save(prefix + "/complete/" + taskId, $('#form-allowance-edit').serialize());
            }
        }
    </script>
</body>
</html>

申请单审批页面(领导审核)

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
    <th:block th:include="include :: header" />
    <th:block th:include="include :: select2-css" />
</head>
<body class="white-bg">
    <div class="wrapper wrapper-content animated fadeInRight ibox-content">
        <form class="form-horizontal m" id="form-allowance-edit" th:object="${bizAllowance}">
            <input name="id" th:field="*{id}" type="hidden">
            <input name="taskId" th:field="*{taskId}" type="hidden">
            <input type="hidden" name="p_COM_comment" />
            <div class="form-group">
                <label class="col-sm-3 control-label">申请人:</label>
                <div class="col-sm-8">
                    <input name="applyUserName" th:field="*{applyUserName}" class="form-control" type="text" readonly>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label">申请时间:</label>
                <div class="col-sm-8">
                    <div class="input-group date">
                        <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
                        <input name="applyTime" th:value="${#dates.format(bizAllowance.applyTime, 'yyyy-MM-dd HH:mm')}" class="form-control" type="text" disabled>
                    </div>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label">标题:</label>
                <div class="col-sm-8">
                    <input name="title" th:field="*{title}" class="form-control" type="text" readonly>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label">原因:</label>
                <div class="col-sm-8">
                    <textarea name="reason" class="form-control" readonly>[[*{reason}]]</textarea>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label">补贴金额:</label>
                <div class="col-sm-8">
                    <input name="totalMoney" th:field="*{totalMoney}" class="form-control" type="text" readonly>
                </div>
            </div>
            <hr />
            <div class="form-group">
                <label class="col-sm-3 control-label" for="deptLeaderApproved">审批意见:</label>
                <div class="col-sm-8">
                    <select name="p_STR_outcome" id="deptLeaderApproved" class="form-control m-b">
                        <option value="通过">同意</option>
                        <option value="驳回">拒绝</option>
                    </select>
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-3 control-label">批注:</label>
                <div class="col-sm-8">
                    <textarea name="comment" class="form-control"></textarea>
                </div>
            </div>
        </form>
    </div>
    <th:block th:include="include :: footer" />
    <th:block th:include="include :: select2-js" />
    <script th:inline="javascript">
        var prefix = ctx + "workflow/allowance";
        $("#form-allowance-edit").validate({
            focusCleanup: true
        });

        function submitHandler() {
            if ($.validate.form()) {
                if ($('textarea[name="comment"]').val()) {
                    $('input[name="p_COM_comment"]').val($('textarea[name="comment"]').val());
                }
                var taskId = [[${taskId}]];
                $.operate.save(prefix + "/complete/" + taskId, $('#form-allowance-edit').serialize());
            }
        }
    </script>
</body>
</html>

任务待办列表页面

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <th:block th:include="include :: header" />
</head>
<body class="gray-bg">
     <div class="container-div">
        <div class="row">
            <div class="col-sm-12 search-collapse">
                <form id="formId">
                    <div class="select-list">
                        <ul>
                            <li>
                                <p>流程实例ID:</p>
                                <input type="text" name="instanceId"/>
                            </li>
                            <li>
                                <a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i>&nbsp;搜索</a>
                                <a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i>&nbsp;重置</a>
                            </li>
                        </ul>
                    </div>
                </form>
            </div>

            <div class="btn-group-sm" id="toolbar" role="group">
                <!--<a class="btn btn-warning" οnclick="$.table.exportExcel()" shiro:hasPermission="process:allowance:export">
                    <i class="fa fa-download"></i> 导出
                 </a>-->
            </div>
            <div class="col-sm-12 select-table table-striped">
                <table id="bootstrap-table"></table>
            </div>
        </div>
    </div>
    <th:block th:include="include :: footer" />
    <script th:inline="javascript">
        var editFlag = [[${@permission.hasPermi('workflow:allowance:edit')}]];
        var removeFlag = [[${@permission.hasPermi('workflow:allowance:remove')}]];
        var prefix = ctx + "workflow/allowance";

        $(function() {
            var options = {
                url: prefix + "/task/data",
                createUrl: prefix + "/add",
                updateUrl: prefix + "/edit/{id}",
                removeUrl: prefix + "/remove",
                exportUrl: prefix + "/export",
                detailUrl: prefix + "/edit/{id}",
                modalName: "补贴申请",
                columns: [{
                    checkbox: true
                },
                {
                    field : 'id',
                    title : '主键ID',
                    visible: false
                },
                {
                    field : 'title',
                    title : '标题'
                },
                {
                    field : 'reason',
                    title : '原因'
                },
                {
                    field : 'totalMoney',
                    title : '补贴金额',
                    formatter: function(value, row, index) {
                        if (!value) return '未知';
                        return parseFloat(value).toFixed(2);
                    }
                },
                {
                    field : 'instanceId',
                    title : '流程实例ID'
                },
                {
                    field: 'applyUserName',
                    title: '<span style="color: red;">申请人</span>',
                    formatter: function(value, row, index) {
                        return '<span style="color: red;">' + (value ? value : "-") + '</span>';
                    }
                },
                {
                    field: 'applyTime',
                    title: '申请时间'
                },
                {
                    field: 'taskId',
                    title: '任务ID',
                    visible: false
                },
                {
                    field: 'taskName',
                    title: '当前任务名称',
                    align: 'center',
                    formatter: function(value, row, index) {
                        return '<span class="badge badge-primary">' + value + '</span>';
                    }
                },
                {
                    title: '操作',
                    align: 'center',
                    formatter: function(value, row, index) {
                        var actions = [];
                        if (row.taskName.indexOf('审批') !== -1) {
                            actions.push('<a class="btn btn-success btn-xs" href="javascript:void(0)" οnclick="showVerifyDialog(\'' + row.taskId + '\', \'' + row.taskName + '\')"><i class="fa fa-edit"></i> 审批</a> ');
                        } else {
                            actions.push('<a class="btn btn-success btn-xs" href="javascript:void(0)" οnclick="showVerifyDialog(\'' + row.taskId + '\', \'' + row.taskName + '\')"><i class="fa fa-edit"></i> ' + row.taskName + '</a> ');
                        }
                        actions.push('<a class="btn btn-primary btn-xs" href="javascript:void(0)" οnclick="$.operate.detail(\'' + row.id + '\')"><i class="fa fa-eye"></i> 申请详情</a> ');
                        actions.push('<a class="btn btn-warning btn-xs" href="javascript:void(0)" οnclick="showHistoryDialog(\'' + row.instanceId + '\')"><i class="fa fa-list"></i> 审批历史</a> ');
                        return actions.join('');
                    }
                }]
            };
            $.table.init(options);
        });

        function submitApply(id) {
            $.modal.confirm("确认要提交申请吗?", function() {
                var url = prefix + "/submitApply";
                var data = { "id": id };
                $.operate.submit(url, "post", "json", data);
            });
        }

        function showVerifyDialog(taskId, taskName) {
            var url = prefix + "/showVerifyDialog/" + taskId;
            $.modal.open(taskName, url);
        }

        /* 查看审批历史 */
        function showHistoryDialog(instanceId) {
            var url = ctx + 'workflow/general/historyList/' + instanceId;
            $.modal.open("查看审批历史", url);
        }
    </script>
</body>
</html>

已办理任务列表页面

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <th:block th:include="include :: header" />
</head>
<body class="gray-bg">
     <div class="container-div">
        <div class="row">
            <div class="col-sm-12 search-collapse">
                <form id="formId">
                    <div class="select-list">
                        <ul>
                            <li>
                                <p>流程实例ID:</p>
                                <input type="text" name="instanceId"/>
                            </li>
                            <li>
                                <a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search"></i>&nbsp;搜索</a>
                                <a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh"></i>&nbsp;重置</a>
                            </li>
                        </ul>
                    </div>
                </form>
            </div>

            <div class="btn-group-sm" id="toolbar" role="group">
                <!--<a class="btn btn-warning" οnclick="$.table.exportExcel()" shiro:hasPermission="process:allowance:export">
                    <i class="fa fa-download"></i> 导出
                 </a>-->
            </div>
            <div class="col-sm-12 select-table table-striped">
                <table id="bootstrap-table"></table>
            </div>
        </div>
    </div>
    <th:block th:include="include :: footer" />
    <script th:inline="javascript">
        var editFlag = [[${@permission.hasPermi('workflow:allowance:edit')}]];
        var removeFlag = [[${@permission.hasPermi('workflow:allowance:remove')}]];
        var prefix = ctx + "workflow/allowance";

        $(function() {
            var options = {
                url: prefix + "/done/data",
                createUrl: prefix + "/add",
                updateUrl: prefix + "/edit/{id}",
                removeUrl: prefix + "/remove",
                exportUrl: prefix + "/export",
                detailUrl: prefix + "/edit/{id}",
                modalName: "补贴申请",
                columns: [{
                    checkbox: true
                },
                {
                    field : 'id',
                    title : '主键ID',
                    visible: false
                },
                {
                    field : 'title',
                    title : '标题'
                },
                {
                    field : 'reason',
                    title : '原因'
                },
                {
                    field : 'totalMoney',
                    title : '补贴金额',
                    formatter: function(value, row, index) {
                        if (!value) return '未知';
                        return parseFloat(value).toFixed(2);
                    }
                },
                {
                    field : 'instanceId',
                    title : '流程实例ID'
                },
                {
                    field: 'applyUserName',
                    title: '<span style="color: red;">申请人</span>',
                    formatter: function(value, row, index) {
                        return '<span style="color: red;">' + (value ? value : "-") + '</span>';
                    }
                },
                {
                    field: 'applyTime',
                    title: '申请时间'
                },
                {
                    field: 'taskId',
                    title: '任务ID',
                    visible: false
                },
                {
                    field: 'taskName',
                    title: '已办任务名称',
                    align: 'center',
                    formatter: function(value, row, index) {
                        return '<span class="badge badge-primary">' + value + '</span>';
                    }
                },
                {
                    field: 'doneTime',
                    title: '办理时间'
                },
                {
                    title: '操作',
                    align: 'center',
                    formatter: function(value, row, index) {
                        var actions = [];
                        actions.push('<a class="btn btn-primary btn-xs" href="javascript:void(0)" οnclick="$.operate.detail(\'' + row.id + '\')"><i class="fa fa-eye"></i> 申请详情</a> ');
                        actions.push('<a class="btn btn-warning btn-xs" href="javascript:void(0)" οnclick="showHistoryDialog(\'' + row.instanceId + '\')"><i class="fa fa-list"></i> 审批历史</a> ');
                        return actions.join('');
                    }
                }]
            };
            $.table.init(options);
        });

        function submitApply(id) {
            $.modal.confirm("确认要提交申请吗?", function() {
                var url = prefix + "/submitApply";
                var data = { "id": id };
                $.operate.submit(url, "post", "json", data);
            });
        }

        function showVerifyDialog(taskId, taskName) {
            var url = prefix + "/showVerifyDialog/" + taskId;
            $.modal.open(taskName, url);
        }

        /* 查看审批历史 */
        function showHistoryDialog(instanceId) {
            var url = ctx + 'workflow/general/historyList/' + instanceId;
            $.modal.open("查看审批历史", url);
        }
    </script>
</body>
</html>

github项目地址

https://github.com/yangzc23/studentboot

参考资料

[01] idea 2019 集成activiti, idea activiti 新建bpmn文件
[02] Activiti,自定义表单,外置表单,工作流,微服务,子系统
[03] activiti监听器设置审批人
[04] springboot-activiti TaskListener无法注入service
[05] Activiti的环节监听taskListener中注入spring bean
[06] Activiti6生成实例历史活动追踪图片
[07] Spring Boot整合Activiti,查看流程图出现中文乱码问题
[08] 一篇文章让你轻松搞定springboot+activiti
[09] Springboot 集成 Activiti时启动报错
[10] SpringBoot 集成 Activiti 一路踩得坑
[11] springboot2.x整合工作流activiti6.0问题及解决方案
[12] activiti会签多人审批(通过以及驳回)
[13] activiti工作流,驳回问题详细解析(尤其会签的驳回问题)
[14] 在工作流开发中,一般会签有哪几种模式
[15] activity6 会签以及会签驳回操作
[16] activiti 动态自定义流程(包含会签流程)
[17] activiti自定义各种流程
[18] activiti动态创建流程

微信扫一扫关注公众号
image.png
点击链接加入群聊

https://jq.qq.com/?_wv=1027&k=5eVEhfN
软件测试学习交流QQ群号:511619105

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值