目录

  • 一.审批设置需求
    • 1.需求描述
    • 2.页面效果
      • 2.1 管理端
      • 2.2 员工端
    • 3.数据库表设计
  • 二.Activiti入门
    • 1.了解工作流
      • 1 什么是工作流
      • 2 工作流引擎
      • 3 常见工作流引擎
      • 4 Activiti7概述
        • 4.1、Activiti介绍
        • 4.2、建模语言BPMN
        • 4.3、Activiti使用流程
    • 2.Activiti7
      • 1、Activiti使用
        • 1.1、数据库支持
        • 1.2、Activiti环境
          • 1.2.1、引入依赖
          • 1.2.2、添加配置
          • 1.2.3、启动项目
          • 1.2.4、数据库表介绍
        • 1.3、Activiti常用Service服务接口
        • 1.4、流程设计工具
          • 1.4.2、解压获取部署包
          • 1.4.3、部署activiti-explorer.war
          • 1.4.4、访问activiti-explorer
      • 2、Activiti流程操作
        • 2.1 流程定义
          • 2.1.1 新建模型
          • 2.1.2 开始节点
          • 2.1.3 任务节点
          • 2.1.4 结束节点
          • 2.1.5 设置节点属性
          • 2.1.6 设置流程定义key
          • 2.1.7 保存流程定义模型
          • 2.1.8 下载流程定义文件
          • 2.1.9 下载流程定义图片
          • 2.1.10 将资源文件放入项目
        • 2.2、流程定义部署
          • 2.2.1、单个文件部署方式
          • 2.2.2、压缩包部署方式
          • 2.2.3、操作数据库表
        • 2.3、启动流程实例
        • 2.4、查询任务
        • 2.5、处理当前任务
        • 2.6、查询已处理任务
        • 2.7、其他接口(了解)
  • 三.审批功能
    • 1、审批类型CRUD
      • 1.1、mapper
      • 1.2、service接口
      • 1.3、service接口实现
      • 1.4、controller接口
    • 2、前端页面
      • 2.1、动态添加路由
      • 2.2、定义api
      • 2.3、创建vue页面
    • 3.审批模板
      • 1、审批模板CRUD
        • 1.1、mapper
        • 1.2、service接口
        • 1.3、service接口实现
        • 1.4、controller接口
      • 2、列表页面
        • 2.1、动态添加路由
        • 2.2、定义api
        • 2.3、列表页面
      • 3、添加审批模板
        • 3.1、form-create
        • 3.2、集成form-create
        • 3.3、获取全部审批分类接口
        • 3.4、上传流程定义接口
        • 3.5、模板设置完整代码
      • 4、查看审批模板
        • 4.1、添加按钮
        • 4.2、定义data
        • 4.3、定义方法
        • 4.4、定义弹出层
      • 5、发布
        • 5.1、添加controller接口
        • 5.2、添加service接口及实现
        • 5.3、前端实现
    • 4.审批管理需求
      • 1、需求描述
      • 2、页面效果
      • 3、数据库表设计
    • 5、审批管理
      • 1、审批管理CRUD
        • 1.1、mapper
        • 1.2、xml
        • 1.3、service接口
        • 1.4、service接口实现
        • 1.5、controller接口
      • 2、前端审批列表
        • 2.1、定义api
        • 2.2、创建vue页面
      • 3、部署流程定义
        • 3.1、根据上传部署
          • 3.1.1、定义service接口
          • 3.1.2、service接口实现
        • 3.2、完善审批模板发布
        • 3.4、页面按钮控制

一.审批设置需求

1.需求描述

公司日常办公,审批是必不可少的一个功能,例如常用的钉钉OA审批,今天我们就要做类似的审批功能。

审批设置模块包含:审批类型与审批模板

审批类型:审批类型即为审批的分类,如:出勤、人事、财务等

审批模板:设置具体审批的基本信息、表单信息与审批流程定义,审批流涉及工作流引擎Activiti,常见的审批模板如:加班、出差、请假、费用报销等,我们可以根据公司具体业务配置具体的审批模板

2.页面效果

2.1 管理端

1、审批类型

2、审批模板

3、在线流程设计

2.2 员工端

1、审批中心

2、发起审批,显示动态表单

3.数据库表设计

1、审批类型表:oa_process_type

2、审批模板表:oa_process_template

重要字段说明:

​ form_props:动态表单的表单属性,后续入门form-create组件会详细讲解

​ form_options:动态表单的表单选项,后续入门form-create组件会详细讲解,这两项就是配置动态表单的

​ process_definition_key:流程定义key,前面Activiti入门已讲解,我们启动流程实例会使用它

​ process_definition_path:保存流程定义文件的路径

​ process_model_id:流程定义模型id,流程定义实现两种方式,一种:上传,第二种:在线制作,分别使用这两个字段保存记录

二.Activiti入门

1.了解工作流

1 什么是工作流

工作流(Workflow),就是通过计算机对业务流程自动化执行管理。它主要解决的是“使在多个参与者之间按照某种预定义的规则自动进行传递文档、信息或任务的过程,从而实现某个预期的业务目标,或者促使此目标的实现”。**通俗来讲,就是业务上一个完整的审批流程。**例如员工的请假,出差,外出采购,合同审核等等,这些过程,都是一个工作流。

2 工作流引擎

对于工作流的处理,如果采用原始的方式,我们需要拿着各种文件到各个负责人那里去签字,需要在多个部门之间不断审批,这种方式费时费力。而我们可以借助软件系统来协助我们处理这些审批流程,这样就出现了工作流系统,使用工作流系统后可以极大的提高工作效率。

在学习工作流的过程中,我们肯定看到过这个模型:填写请假单->部门经理审批->总经理审批->人事备案。

(1)要实现上述的流程,我们自己可以通过字段标识来实现这个审批效果,在业务表中加个字段,比如填写请假单用1标识,部门经理用2标识,总经理用3标识,人事备案用4标识,好像看起来没啥问题,也实现了审批效果。可是一旦我们的流程出现了变化,这个时候我们就需要改动我们的代码了,这显然是不可取的,那么有没有专业的方式来实现工作流的管理呢?并且可以做到业务流程变化之后,我们的程序可以不用改变,如果可以实现这样的效果,那么我们的业务系统的适应能力就得到了极大提升。在这样的背景下,就出现了工作流引擎

为什么使用工作流引擎,能实现业务流程改变,不用修改代码,流程还能自动推进?

(1)我们先来说说为什么流程改变,不用修改代码:我们的工作流引擎都实现了一个规范,这个规范要求我们的流程管理与状态字段无关,始终都是读取业务流程图的下一个节点。当业务更新的时候我们只需要更新业务流程图就行了。这就实现了业务流程改变,不用修改代码。

(2)再来说说流程自动推进,这个原理就更简单了,就拿上面的请假模型来说,工作流引擎会用一张表来记录当前处在的节点。当填写完请假单后肯定是要轮到部门经理来审批了,所以我们一旦完成了请假单填写那么这条记录将会被从这张表删除掉,并且会把下一个节点部门经理的信息插入到这张表中,当我们用部门经理的信息去这张表中查询的时候就能查出部门经理相关的审批的信息了,以此类推,这样层层递进,就实现了流程的自动递交了。

3 常见工作流引擎

主流的框架有:Activiti、jBPM、Camunda 、Flowable 、还有我们国产的盘古BPM、云程

4 Activiti7概述

4.1、Activiti介绍

activiti是一个工作流引擎,可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN进行定义,业务流程按照预先定义的流程进行执行。实现了系统的流程由activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作流量,从而提高系统的健壮性,同时也减少了系统开发维护成本。

官方网站: https://www.activiti.org

4.2、建模语言BPMN

BPM(Business Process Management)即业务流程管理,是一种规范化的构造端到端的业务流程,以持续提高组织业务效率

BPM 软件就是根据企业中业务环境的变化,推进人与人之间、人与系统之间以及系统与系统之间的整理及调整的经营方法与解决方案的 IT 工具。使用 BPM 软件对企业内部及外部的业务流程的整个生命周期进行建模、自动化、管理监控和优化,可以降低企业成本,提高利润

BPMN(Business Process Model AndNotation)即业务流程模型和符号,是一套标准的业务流程建模符号,使用 BPMN 提供的符号可以创建业务流程。Activit 就是使用 BPMN 进行流程建模、流程执行管理的

BPMN2.0 是业务流程建模符号 2.0 的缩写,它由 Business Process Management Initiative 这个非营利协会创建并不断发展。BPMN2.0 是使用一些符号来明确业务流程设计流程图的一套符号规范,能增进业务建模时的沟通效率。目前 BPMN2.0 是最新的版本,它用于在 BPM 上下文中进行布局和可视化的沟通

BPMN2.0 的基本符号主要包含:

  • 事件 Event

    开始:表示一个流程的开始

    中间:发生的开始和结束事件之间,影响处理的流程

    结束:表示该过程结束

  • 活动 Activities

活动是工作或任务的一个通用术语。一个活动可以是一个任务,还可以是一个当前流程的子处理流程;其次,你还可以为活动指定不同的类型。常见活动如下:

  • 网关 GateWay

用于表示流程的分支与合并,有几种常用网关需要了解:

  • 排他网关:只有一条路径会被选择

  • xxxxxxxxxx <el-button type=“success” icon=“el-icon-plus” size=“mini” @click=“add” :disabled=“$hasBP(‘bnt.sysRole.add’) === false”>添 加vue

  • 包容网关:可以同时执行多条线路,也可以在网关上设置条件

  • 事件网关:专门为中间捕获事件设置的,允许设置多个输出流指向多个不同的中间捕获事件。当流程执行到事件网关后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态

- 流向 Flow

流是连接两个流程节点的连线,常见的流向包含以下几种:

顺序流:用一个带实心箭头的实心线表示,用于指定活动执行的顺序

信息流:用一条带箭头的虚线表示,用于描述两个独立的业务参与者(业务实体/业务角色)之间发送和接受的消息流动

关联:用一根带有线箭头的点线表示,用于将相关的数据、文本和其他人工信息与流对象联系起来。用于展示活动的输入和输出

流程示例:

4.3、Activiti使用流程

第一步: 引入依赖并初始化数据库

既然activiti是一个框架,那么我们肯定是需要引入对应的jar包坐标的,具体参考代码中的。

第二步: 通过工具绘画流程图

使用 activiti 流程建模工具(activity-designer)定义业务流程(.bpmn 文件)

.bpmn 文件就是业务流程定义文件,通过 xml 定义业务流程。

第三步:流程定义部署;

向 activiti 部署业务流程定义(.bpmn 文件),使用 activiti 提供的 api 向 activiti 中部署.bpmn 文件,

通俗来讲,就是让activiti认识要使用的流程

第四步: 启动一个流程实例(ProcessInstance)

启动一个流程实例表示开始一次业务流程的运行,比如员工请假流程部署完成,如果张三要请假就可以启动一个流程实例,如果李四要请假也启动一个流程实例,两个流程的执行互相不影响,就好比定义一个java类,实例化两个对象一样,部署的流程就好比java类,启动一个流程实例就好比new一个java对象

第五步: 用户查询待办任务(Task)

因为现在系统的业务流程已经交给 activiti 管理,通过 activiti 就可以查询当前流程执行到哪了,当前用户需要办理什么任务了,这些 activiti帮我们管理了。实际上我们学习activiti也只是学习它的API怎么使用,因为很多功能activiti都已经封装好了,我们会调用就行了

第六步: 用户办理任务

用户查询待办任务后,就可以办理某个任务,如果这个任务办理完成还需要其它用户办理,比如请假单创建后由部门经理审核,这个过程也是由 activiti 帮我们完成了,不需要我们在代码中硬编码指定下一个任务办理人了

第七步: 流程结束

当任务办理完成没有下一个任务节点了,这个流程实例就完成了。

2.Activiti7

1、Activiti使用

1.1、数据库支持

Activiti 运行必须要有数据库的支持,支持的数据库有:mysql、oracle、postgres、mssql、db2、h2

1.2、Activiti环境

我们直接在当前项目:oa-parent做Activiti入门讲解

1.2.1、引入依赖
<!--引入activiti的springboot启动器 -->
<dependency><groupId>org.activiti</groupId><artifactId>activiti-spring-boot-starter</artifactId><version>7.1.0.M6</version><exclusions><exclusion><artifactId>mybatis</artifactId><groupId>org.mybatis</groupId></exclusion></exclusions>
</dependency>

说明:Activiti7与SpringBoot整合后,默认集成了SpringSecurity安全框架,当前我们项目已经集成过了SpringSecurity,后续案例设置审批人时都必须是系统用户,Activiti框架会检查用户是否存在,否则会出现异常,后续大家可以在案例中测试。

1.2.2、添加配置

数据源项目已经添加,只需要如下配置即可

spring:    activiti:#    false:默认,数据库表不变,但是如果版本不对或者缺失表会抛出异常(生产使用)#    true:表不存在,自动创建(开发使用)#    create_drop: 启动时创建,关闭时删除表(测试使用)#    drop_create: 启动时删除表,在创建表 (不需要手动关闭引擎)database-schema-update: true#监测历史表是否存在,activities7默认不开启历史表db-history-used: true#none:不保存任何历史数据,流程中这是最高效的#activity:只保存流程实例和流程行为#audit:除了activity,还保存全部的流程任务以及其属性,audit为history默认值#full:除了audit、还保存其他全部流程相关的细节数据,包括一些流程参数history-level: full#校验流程文件,默认校验resources下的process 文件夹的流程文件check-process-definitions: true
1.2.3、启动项目

启动项目,即可生成项目数据库表

1.2.4、数据库表介绍

Activiti 的运行支持必须要有这 25 张表的支持,主要是在业务流程运行过程中,记录参与流程的用户主体,用户组信息,以及流程的定义,流程执行时的信息,和流程的历史信息等等

1、 表的命名规则和作用

观察创建的表,我们发现 Activiti 的表都以 act_ 开头,紧接着是表示表的用途的两个字母标识,也和 Activiti 所提供的服务的 API 对应:

  • ACT_RE:RE 表示 repository,这个前缀的表包含了流程定义和流程静态资源 (图片、规则、等等)
  • ACT_RU:RU 表示 runtime,这些表运行时,会包含流程实例、任务、变量、异步任务等流程业务进行中的数据。Activiti 只在流程实例执行过程中保存这些数据,在流程结束时就会删除这些记录。这样表就可以一直保持很小的体积,并且速度很快
  • ACT_HI:HI 表示 history,这些表包含一些历史数据,比如历史流程实例、变量、任务等等
  • ACT_GE:GE 表示 general,通用数据

2、Activiti 数据表介绍

表分类 表名 解释
一般数据
[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_RE_DEPLOYMENT] 部署单元信息
[ACT_RE_MODEL] 模型信息
[ACT_RE_PROCDEF] 已部署的流程定义
运行实例表
[ACT_RU_EVENT_SUBSCR] 运行时事件
[ACT_RU_EXECUTION] 运行时流程执行实例
[ACT_RU_IDENTITYLINK] 运行时用户关系信息,存储任务节点与参与者的相关信息
[ACT_RU_JOB] 运行时作业
[ACT_RU_TASK] 运行时任务
[ACT_RU_VARIABLE] 运行时变量表

1.3、Activiti常用Service服务接口

简单介绍一下各个 Service 的实现类:

  • RepositoryService

    Activiti 的资源管理类,该服务负责部署流程定义,管理流程资源。在使用 Activiti 时,一开始需要先完成流程部署,即将使用建模工具设计的业务流程图通过 RepositoryService 进行部署

  • RuntimeService

    Activiti 的流程运行管理类,用于开始一个新的流程实例,获取关于流程执行的相关信息。流程定义用于确定一个流程中的结构和各个节点间行为,而流程实例则是对应的流程定义的一个执行,可以理解为 Java 中类和对象的关系

  • TaskService

    Activiti 的任务管理类,用于处理业务运行中的各种任务,例如查询分给用户或组的任务、创建新的任务、分配任务、确定和完成一个任务

  • HistoryService

    Activiti 的历史管理类,可以查询历史信息。执行流程时,引擎会保存很多数据,比如流程实例启动时间、任务的参与者、完成任务的时间、每个流程实例的执行路径等等。这个服务主要通过查询功能来获得这些数据

  • ManagementService

    Activiti 的引擎管理类,提供了对 Activiti 流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于 Activiti 系统的日常维护

1.4、流程设计工具

IDEA版本小于等于2019,可使用Activiti插件actiBPM,大于该版本的IDEA可使用Activiti BPMN visualizer插件绘制流程设计。

今天我们主角是:Activiti Modeler

Activiti Modeler 是 Activiti 官方提供的一款在线流程设计的前端插件,开发人员可以方便在线进行流程设计,保存流程模型,部署至流程定义等等,后续我们的项目也是集成Activiti Modeler绘制流程定义。
官网下载: https://www.activiti.org/get-started

1.4.2、解压获取部署包

解压activiti-5.22.0.zip,在activiti-5.22.0\wars目录下获取activiti-explorer.war

1.4.3、部署activiti-explorer.war

将activiti-explorer.war放到tomcat部署目录,启动tomcat

1.4.4、访问activiti-explorer

http://localhost:8080/activiti-explorer

默认登录账号:kermit kermit

2、Activiti流程操作

2.1 流程定义

我们定义一个请假流程

2.1.1 新建模型

2.1.2 开始节点

2.1.3 任务节点

2.1.4 结束节点

2.1.5 设置节点属性

指定标签名称:张三审批,节点任务负责人:zhangsan

指定标签名称:李四审批,节点任务负责人:lisi

2.1.6 设置流程定义key

2.1.7 保存流程定义模型

2.1.8 下载流程定义文件


下载文件为:qingjia.bpmn20.xml

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/processdef"><process id="qingjia" isExecutable="true"><startEvent id="sid-14A3E2A6-84E4-49E0-BF92-3DABD741430B"></startEvent><userTask id="sid-38632C81-C407-4F0D-944D-FC30F90637A3" name="张三审批" activiti:assignee="zhangsan"></userTask><sequenceFlow id="sid-081A176E-6756-4C4C-B36C-2649B12CFC5D" sourceRef="sid-14A3E2A6-84E4-49E0-BF92-3DABD741430B" targetRef="sid-38632C81-C407-4F0D-944D-FC30F90637A3"></sequenceFlow><userTask id="sid-655780D5-8492-494F-9E30-2CFD6691E98D" name="李四审批" activiti:assignee="lisi"></userTask><sequenceFlow id="sid-7DCE821D-4AE0-4F27-9811-80B575E7A758" sourceRef="sid-38632C81-C407-4F0D-944D-FC30F90637A3" targetRef="sid-655780D5-8492-494F-9E30-2CFD6691E98D"></sequenceFlow><endEvent id="sid-7EE28419-BC61-49AC-8990-C63C4D2F7C0D"></endEvent><sequenceFlow id="sid-2E583A5C-265A-4C05-B5E1-7F5DB98291F1" sourceRef="sid-655780D5-8492-494F-9E30-2CFD6691E98D" targetRef="sid-7EE28419-BC61-49AC-8990-C63C4D2F7C0D"></sequenceFlow></process><bpmndi:BPMNDiagram id="BPMNDiagram_qingjia"><bpmndi:BPMNPlane bpmnElement="qingjia" id="BPMNPlane_qingjia"><bpmndi:BPMNShape bpmnElement="sid-14A3E2A6-84E4-49E0-BF92-3DABD741430B" id="BPMNShape_sid-14A3E2A6-84E4-49E0-BF92-3DABD741430B"><omgdc:Bounds height="30.0" width="30.0" x="93.5" y="75.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="sid-38632C81-C407-4F0D-944D-FC30F90637A3" id="BPMNShape_sid-38632C81-C407-4F0D-944D-FC30F90637A3"><omgdc:Bounds height="80.0" width="100.0" x="168.5" y="50.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="sid-655780D5-8492-494F-9E30-2CFD6691E98D" id="BPMNShape_sid-655780D5-8492-494F-9E30-2CFD6691E98D"><omgdc:Bounds height="80.0" width="100.0" x="313.5" y="50.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="sid-7EE28419-BC61-49AC-8990-C63C4D2F7C0D" id="BPMNShape_sid-7EE28419-BC61-49AC-8990-C63C4D2F7C0D"><omgdc:Bounds height="28.0" width="28.0" x="458.5" y="76.0"></omgdc:Bounds></bpmndi:BPMNShape><bpmndi:BPMNEdge bpmnElement="sid-7DCE821D-4AE0-4F27-9811-80B575E7A758" id="BPMNEdge_sid-7DCE821D-4AE0-4F27-9811-80B575E7A758"><omgdi:waypoint x="268.5" y="90.0"></omgdi:waypoint><omgdi:waypoint x="313.5" y="90.0"></omgdi:waypoint></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="sid-081A176E-6756-4C4C-B36C-2649B12CFC5D" id="BPMNEdge_sid-081A176E-6756-4C4C-B36C-2649B12CFC5D"><omgdi:waypoint x="123.5" y="90.0"></omgdi:waypoint><omgdi:waypoint x="168.5" y="90.0"></omgdi:waypoint></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="sid-2E583A5C-265A-4C05-B5E1-7F5DB98291F1" id="BPMNEdge_sid-2E583A5C-265A-4C05-B5E1-7F5DB98291F1"><omgdi:waypoint x="413.5" y="90.0"></omgdi:waypoint><omgdi:waypoint x="458.5" y="90.0"></omgdi:waypoint></bpmndi:BPMNEdge></bpmndi:BPMNPlane></bpmndi:BPMNDiagram>
</definitions>
2.1.9 下载流程定义图片

单击右键上图图片,图片另存为:qingjia.png

2.1.10 将资源文件放入项目

在service-oa模块resources下新建process资源文件夹

将qingjia.bpmn20.xml与qingjia.png放入process目录

2.2、流程定义部署

将上面在设计器中定义的流程部署到activiti数据库中,就是流程定义部署。通过调用activiti的api将流程定义的bpmn和png两个文件一个一个添加部署到activiti中,也可以将两个文件打成zip包进行部署。

2.2.1、单个文件部署方式
package com.example;import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
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.SpringRunner;@RunWith(SpringRunner.class)
@SpringBootTest
public class ProcessTest {@Autowiredprivate RepositoryService repositoryService;@Testpublic void deployProcess() {// 流程部署Deployment deploy = repositoryService.createDeployment().addClasspathResource("process/qingjia.bpmn20.xml").addClasspathResource("process/qingjia.png").name("请假申请流程").deploy();System.out.println(deploy.getId());System.out.println(deploy.getName());}
}
2.2.2、压缩包部署方式
@Test
public void deployProcessByZip() {// 定义zip输入流InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("process/qingjia.zip");ZipInputStream zipInputStream = new ZipInputStream(inputStream);// 流程部署Deployment deployment = repositoryService.createDeployment().addZipInputStream(zipInputStream).name("请假申请流程").deploy();System.out.println("流程部署id:" + deployment.getId());System.out.println("流程部署名称:" + deployment.getName());
}
2.2.3、操作数据库表

流程定义部署后操作activiti的3张表如下:

act_re_deployment 流程定义部署表,每部署一次增加一条记录

act_re_procdef 流程定义表,部署每个新的流程定义都会在这张表中增加一条记录

act_ge_bytearray 流程资源表

2.3、启动流程实例

流程定义:将bpmn文件放到activiti的三张表中,好比是java中的一个类
流程实例:好比是java中的一个实例对象(一个流程定义可以对应多个流程实例),张三可以启动一个请假流程实例,李四也可以启动一个请假流程实例,他们互不影响

@Autowired
private RuntimeService runtimeService;@Test
public void startUpProcess() {//创建流程实例,我们需要知道流程定义的keyProcessInstance processInstance = runtimeService.startProcessInstanceByKey("qingjia");//输出实例的相关信息System.out.println("流程定义id:" + processInstance.getProcessDefinitionId());System.out.println("流程实例id:" + processInstance.getId());System.out.println("当前活动Id:" + processInstance.getActivityId());
}

操作数据表

act_hi_actinst 流程实例执行历史

act_hi_identitylink 流程的参与用户历史信息

act_hi_procinst 流程实例历史信息

act_hi_taskinst 流程任务历史信息

act_ru_execution 流程执行信息

act_ru_identitylink 流程的参与用户信息

act_ru_task 任务信息

2.4、查询任务

每个节点都配置了Assignee,流程启动后,任务的负责人就可以查询自己当前需要处理的任务,查询出来的任务都是该用户的待办任务。

@Autowired
private TaskService taskService;/*** 查询当前个人待执行的任务*/
@Test
public void findPendingTaskList() {//任务负责人String assignee = "zhangsan";List<Task> list = taskService.createTaskQuery().taskAssignee(assignee)//只查询该任务负责人的任务.list();for (Task task : list) {System.out.println("流程实例id:" + task.getProcessInstanceId());System.out.println("任务id:" + task.getId());System.out.println("任务负责人:" + task.getAssignee());System.out.println("任务名称:" + task.getName());}
}

说明:

​ 流程实例id:一个流程只有一个,标识这个流程

​ 任务id:流程每进行到某个节点,就会给这个节点分配一个任务id

输出结果如下:

​ 流程实例id:d969f534-825e-11ed-95b4-7c57581a7819
​ 任务id:d96c3f28-825e-11ed-95b4-7c57581a7819
​ 任务负责人:zhangsan
​ 任务名称:张三审批

2.5、处理当前任务

任务负责人查询待办任务,选择任务进行处理,完成任务。

/*** 完成任务*/
@Test
public void completTask(){Task task = taskService.createTaskQuery().taskAssignee("zhangsan")  //要查询的负责人.singleResult();//返回一条//完成任务,参数:任务idtaskService.complete(task.getId());
}

完成任务后,任务自动到下一个节点

2.6、查询已处理任务

@Autowired
private HistoryService historyService;/*** 查询已处理历史任务*/
@Test
public void findProcessedTaskList() {//张三已处理过的历史任务List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().taskAssignee("zhangsan").finished().list();for (HistoricTaskInstance historicTaskInstance : list) {System.out.println("流程实例id:" + historicTaskInstance.getProcessInstanceId());System.out.println("任务id:" + historicTaskInstance.getId());System.out.println("任务负责人:" + historicTaskInstance.getAssignee());System.out.println("任务名称:" + historicTaskInstance.getName());}
}

2.7、其他接口(了解)

/*** 查询流程定义*/
@Test
public void findProcessDefinitionList(){List<ProcessDefinition> definitionList = repositoryService.createProcessDefinitionQuery().orderByProcessDefinitionVersion().desc().list();//输出流程定义信息for (ProcessDefinition processDefinition : definitionList) {System.out.println("流程定义 id="+processDefinition.getId());System.out.println("流程定义 name="+processDefinition.getName());System.out.println("流程定义 key="+processDefinition.getKey());System.out.println("流程定义 Version="+processDefinition.getVersion());System.out.println("流程部署ID ="+processDefinition.getDeploymentId());}
}/*** 删除流程定义*/
public void deleteDeployment() {//部署idString deploymentId = "82e3bc6b-81da-11ed-8e03-7c57581a7819";//删除流程定义,如果该流程定义已有流程实例启动则删除时出错repositoryService.deleteDeployment(deploymentId);//设置true 级联删除流程定义,即使该流程有流程实例启动也可以删除,设置为false非级别删除方式//repositoryService.deleteDeployment(deploymentId, true);
}

三.审批功能

1、审批类型CRUD

1.1、mapper

package com.example.process.mapper;import com.example.model.process.ProcessType;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface ProcessTypeMapper extends BaseMapper<ProcessType> {}

1.2、service接口

package com.example.process.service;import com.example.model.process.ProcessType;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;public interface ProcessTypeService extends IService<ProcessType> {}

1.3、service接口实现

package com.example.process.service.impl;import com.example.model.process.ProcessType;
import com.example.process.mapper.ProcessTypeMapper;
import com.example.process.service.ProcessTypeService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class ProcessTypeServiceImpl extends ServiceImpl<ProcessTypeMapper, ProcessType> implements ProcessTypeService {}

1.4、controller接口

package com.example.process.controller;import com.example.common.result.Result;
import com.example.model.process.ProcessType;
import com.example.process.service.ProcessTypeService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;@Api(value = "审批类型", tags = "审批类型")
@RestController
@RequestMapping(value = "/admin/process/processType")
@SuppressWarnings({"unchecked", "rawtypes"})
public class ProcessTypeController {@Autowiredprivate ProcessTypeService processTypeService;@PreAuthorize("hasAuthority('bnt.processType.list')")@ApiOperation(value = "获取分页列表")@GetMapping("{page}/{limit}")public Result index(@PathVariable Long page,@PathVariable Long limit) {Page<ProcessType> pageParam = new Page<>(page,limit);IPage<ProcessType> pageModel = processTypeService.page(pageParam);return Result.ok(pageModel);}@PreAuthorize("hasAuthority('bnt.processType.list')")@ApiOperation(value = "获取")@GetMapping("get/{id}")public Result get(@PathVariable Long id) {ProcessType processType = processTypeService.getById(id);return Result.ok(processType);}@PreAuthorize("hasAuthority('bnt.processType.add')")@ApiOperation(value = "新增")@PostMapping("save")public Result save(@RequestBody ProcessType processType) {processTypeService.save(processType);return Result.ok();}@PreAuthorize("hasAuthority('bnt.processType.update')")@ApiOperation(value = "修改")@PutMapping("update")public Result updateById(@RequestBody ProcessType processType) {processTypeService.updateById(processType);return Result.ok();}@PreAuthorize("hasAuthority('bnt.processType.remove')")@ApiOperation(value = "删除")@DeleteMapping("remove/{id}")public Result remove(@PathVariable Long id) {processTypeService.removeById(id);return Result.ok();}
}

2、前端页面

2.1、动态添加路由

在“系统管理”->“菜单管理”添加“审批设置”->“审批类型”

对于菜单信息,我们也可以直接导入菜单表初始化数据,后续不用再单独配置

2.2、定义api

创建src/api/process/processType.js

import request from '@/utils/request'const api_name = '/admin/process/processType'export default {getPageList(page, limit) {return request({url: `${api_name}/${page}/${limit}`,method: 'get'})},getById(id) {return request({url: `${api_name}/get/${id}`,method: 'get'})},save(role) {return request({url: `${api_name}/save`,method: 'post',data: role})},updateById(role) {return request({url: `${api_name}/update`,method: 'put',data: role})},removeById(id) {return request({url: `${api_name}/remove/${id}`,method: 'delete'})}
}

2.3、创建vue页面

创建views/processSet/processType/list.vue

<template><div class="app-container"><!-- 工具条 --><div class="tools-div"><el-button type="success" icon="el-icon-plus" size="mini" @click="add" :disabled="$hasBP('bnt.processType.add')  === false">添 加</el-button></div><!-- banner列表 --><el-tablev-loading="listLoading":data="list"stripeborderstyle="width: 100%;margin-top: 10px;"><el-table-columntype="selection"width="55"/><el-table-columnlabel="序号"width="70"align="center"><template slot-scope="scope">{{ (page - 1) * limit + scope.$index + 1 }}</template></el-table-column><el-table-column prop="name" label="类型名称"/><el-table-column prop="description" label="描述"/><el-table-column prop="createTime" label="创建时间"/><el-table-column prop="updateTime" label="更新时间"/><el-table-column label="操作" width="200" align="center"><template slot-scope="scope"><el-button type="text" size="mini" @click="edit(scope.row.id)" :disabled="$hasBP('bnt.processType.update')  === false">修改</el-button><el-button type="text" size="mini" @click="removeDataById(scope.row.id)" :disabled="$hasBP('bnt.processType.remove')  === false">删除</el-button></template></el-table-column></el-table><!-- 分页组件 --><el-pagination:current-page="page":total="total":page-size="limit":page-sizes="[5, 10, 20, 30, 40, 50, 100]"style="padding: 30px 0; text-align: center;"layout="sizes, prev, pager, next, jumper, ->, total, slot"@current-change="fetchData"@size-change="changeSize"/><el-dialog title="添加/修改" :visible.sync="dialogVisible" width="40%"><el-form ref="flashPromotionForm" label-width="150px" size="small" style="padding-right: 40px;"><el-form-item label="类型名称"><el-input v-model="processType.name"/></el-form-item><el-form-item label="描述"><el-input v-model="processType.description"/></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="dialogVisible = false" size="small">取 消</el-button><el-button type="primary" @click="saveOrUpdate()" size="small">确 定</el-button></span></el-dialog></div>
</template>
<script>
import api from '@/api/process/processType'const defaultForm = {id: '',name: '',description: ''
}
export default {data() {return {listLoading: true, // 数据是否正在加载list: null, // banner列表total: 0, // 数据库中的总记录数page: 1, // 默认页码limit: 10, // 每页记录数searchObj: {}, // 查询表单对象dialogVisible: false,processType: defaultForm,saveBtnDisabled: false}},// 生命周期函数:内存准备完毕,页面尚未渲染created() {this.fetchData()},// 生命周期函数:内存准备完毕,页面渲染成功mounted() {},methods: {// 当页码发生改变的时候changeSize(size) {console.log(size)this.limit = sizethis.fetchData(1)},// 加载列表数据fetchData(page = 1) {this.page = pageapi.getPageList(this.page, this.limit, this.searchObj).then(response => {this.list = response.data.recordsthis.total = response.data.total// 数据加载并绑定成功this.listLoading = false})},// 重置查询表单resetData() {console.log('重置查询表单')this.searchObj = {}this.fetchData()},// 根据id删除数据removeDataById(id) {this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => { // promise// 点击确定,远程调用ajaxreturn api.removeById(id)}).then((response) => {this.fetchData(this.page)this.$message.success(response.message)}).catch(() => {this.$message.info('取消删除')})},add() {this.dialogVisible = truethis.processType = Object.assign({}, defaultForm)},edit(id) {this.dialogVisible = truethis.fetchDataById(id)},fetchDataById(id) {api.getById(id).then(response => {this.processType = response.data})},saveOrUpdate() {this.saveBtnDisabled = true // 防止表单重复提交if (!this.processType.id) {this.saveData()} else {this.updateData()}},// 新增saveData() {api.save(this.processType).then(response => {this.$message.success(response.message || '操作成功')this.dialogVisible = falsethis.fetchData(this.page)})},// 根据id更新记录updateData() {api.updateById(this.processType).then(response => {this.$message.success(response.message || '操作成功')this.dialogVisible = falsethis.fetchData(this.page)})}}
}
</script>

3.审批模板

1、审批模板CRUD

1.1、mapper

package com.atguigu.process.mapper;import com.atguigu.model.process.ProcessTemplate;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface ProcessTemplateMapper extends BaseMapper<ProcessTemplate> {}

1.2、service接口

package com.atguigu.process.service;import com.atguigu.model.process.ProcessTemplate;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;public interface ProcessTemplateService extends IService<ProcessTemplate> {IPage<ProcessTemplate> selectPage(Page<ProcessTemplate> pageParam);}

1.3、service接口实现

package com.atguigu.process.service.impl;import com.atguigu.model.process.ProcessTemplate;
import com.atguigu.model.process.ProcessType;
import com.atguigu.process.mapper.ProcessTemplateMapper;
import com.atguigu.process.service.ProcessTemplateService;
import com.atguigu.process.service.ProcessTypeService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class ProcessTemplateServiceImpl extends ServiceImpl<ProcessTemplateMapper, ProcessTemplate> implements ProcessTemplateService {@Resourceprivate ProcessTemplateMapper processTemplateMapper;@Resourceprivate ProcessTypeService processTypeService;@Overridepublic IPage<ProcessTemplate> selectPage(Page<ProcessTemplate> pageParam) {LambdaQueryWrapper<ProcessTemplate> queryWrapper = new LambdaQueryWrapper<ProcessTemplate>();queryWrapper.orderByDesc(ProcessTemplate::getId);IPage<ProcessTemplate> page = processTemplateMapper.selectPage(pageParam, queryWrapper);List<ProcessTemplate> processTemplateList = page.getRecords();List<Long> processTypeIdList = processTemplateList.stream().map(processTemplate -> processTemplate.getProcessTypeId()).collect(Collectors.toList());if(!CollectionUtils.isEmpty(processTypeIdList)) {Map<Long, ProcessType> processTypeIdToProcessTypeMap = processTypeService.list(new LambdaQueryWrapper<ProcessType>().in(ProcessType::getId, processTypeIdList)).stream().collect(Collectors.toMap(ProcessType::getId, ProcessType -> ProcessType));for(ProcessTemplate processTemplate : processTemplateList) {ProcessType processType = processTypeIdToProcessTypeMap.get(processTemplate.getProcessTypeId());if(null == processType) continue;processTemplate.setProcessTypeName(processType.getName());}}return page;}
}

1.4、controller接口

package com.atguigu.process.controller;import com.atguigu.common.result.Result;
import com.atguigu.model.process.ProcessTemplate;
import com.atguigu.process.service.ProcessTemplateService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;@Api(value = "审批模板管理", tags = "审批模板管理")
@RestController
@RequestMapping(value = "/admin/process/processTemplate")
@SuppressWarnings({"unchecked", "rawtypes"})
public class ProcessTemplateController {@Autowiredprivate ProcessTemplateService processTemplateService;//@PreAuthorize("hasAuthority('bnt.processTemplate.list')")@ApiOperation(value = "获取分页列表")@GetMapping("{page}/{limit}")public Result index(@ApiParam(name = "page", value = "当前页码", required = true)@PathVariable Long page,@ApiParam(name = "limit", value = "每页记录数", required = true)@PathVariable Long limit) {Page<ProcessTemplate> pageParam = new Page<>(page, limit);IPage<ProcessTemplate> pageModel = processTemplateService.selectPage(pageParam);return Result.ok(pageModel);}//@PreAuthorize("hasAuthority('bnt.processTemplate.list')")@ApiOperation(value = "获取")@GetMapping("get/{id}")public Result get(@PathVariable Long id) {ProcessTemplate processTemplate = processTemplateService.getById(id);return Result.ok(processTemplate);}//@PreAuthorize("hasAuthority('bnt.processTemplate.templateSet')")@ApiOperation(value = "新增")@PostMapping("save")public Result save(@RequestBody ProcessTemplate processTemplate) {processTemplateService.save(processTemplate);return Result.ok();}//@PreAuthorize("hasAuthority('bnt.processTemplate.templateSet')")@ApiOperation(value = "修改")@PutMapping("update")public Result updateById(@RequestBody ProcessTemplate processTemplate) {processTemplateService.updateById(processTemplate);return Result.ok();}//@PreAuthorize("hasAuthority('bnt.processTemplate.remove')")@ApiOperation(value = "删除")@DeleteMapping("remove/{id}")public Result remove(@PathVariable Long id) {processTemplateService.removeById(id);return Result.ok();}
}

2、列表页面

2.1、动态添加路由

在“系统管理”->“菜单管理”添加“审批设置”->“审批类型”

对于菜单信息,我们也可以直接导入菜单表初始化数据,后续不用再单独配置

说明:“审批模板设置”页面内容较多,因此单独打开一个独立页面

2.2、定义api

创建src/api/process/processTemplate.js

基本的增删改查接口

import request from '@/utils/request'const api_name = '/admin/process/processTemplate'export default {getPageList(page, limit) {return request({url: `${api_name}/${page}/${limit}`,method: 'get'})},getById(id) {return request({url: `${api_name}/get/${id}`,method: 'get'})},save(role) {return request({url: `${api_name}/save`,method: 'post',data: role})},updateById(role) {return request({url: `${api_name}/update`,method: 'put',data: role})},removeById(id) {return request({url: `${api_name}/remove/${id}`,method: 'delete'})}
}

2.3、列表页面

创建views/processSet/processTemplate/list.vue

<template><div class="app-container"><!-- 工具条 --><div class="tools-div"><el-button type="success" icon="el-icon-plus" size="mini" @click="add()" :disabled="$hasBP('bnt.processTemplate.templateSet')  === false">添加审批设置</el-button></div><!-- 列表 --><el-tablev-loading="listLoading":data="list"stripeborderstyle="width: 100%;margin-top: 10px;"><el-table-columnlabel="序号"width="70"align="center"><template slot-scope="scope">{{ (page - 1) * limit + scope.$index + 1 }}</template></el-table-column>iconPath<el-table-column prop="name" label="审批名称"/><el-table-column label="图标"><template slot-scope="scope"><img :src="scope.row.iconUrl" style="width: 30px;height: 30px;vertical-align: text-bottom;"></template></el-table-column><el-table-column prop="processTypeName" label="审批类型"/><el-table-column prop="description" label="描述"/><el-table-column prop="createTime" label="创建时间"/><el-table-column prop="updateTime" label="更新时间"/><el-table-column label="操作" width="250" align="center"><template slot-scope="scope"><el-button type="text" size="mini" @click="edit(scope.row.id)" :disabled="$hasBP('bnt.processTemplate.templateSet')  === false">修改审批设置</el-button><el-button type="text" size="mini" @click="removeDataById(scope.row.id)" :disabled="$hasBP('bnt.processTemplate.remove')  === false">删除</el-button></template></el-table-column></el-table><!-- 分页组件 --><el-pagination:current-page="page":total="total":page-size="limit":page-sizes="[5, 10, 20, 30, 40, 50, 100]"style="padding: 30px 0; text-align: center;"layout="sizes, prev, pager, next, jumper, ->, total, slot"@current-change="fetchData"@size-change="changeSize"/></div>
</template>
<script>
import api from '@/api/process/processTemplate'export default {data() {return {listLoading: true, // 数据是否正在加载list: null, // banner列表total: 0, // 数据库中的总记录数page: 1, // 默认页码limit: 10, // 每页记录数searchObj: {} // 查询表单对象}},// 生命周期函数:内存准备完毕,页面尚未渲染created() {this.fetchData()},// 生命周期函数:内存准备完毕,页面渲染成功mounted() {},methods: {// 当页码发生改变的时候changeSize(size) {this.limit = sizethis.fetchData(1)},// 加载banner列表数据fetchData(page = 1) {// 异步获取远程数据(ajax)this.page = pageapi.getPageList(this.page, this.limit, this.searchObj).then(response => {this.list = response.data.recordsthis.total = response.data.total// 数据加载并绑定成功this.listLoading = false})},// 重置查询表单resetData() {this.searchObj = {}this.fetchData()},// 根据id删除数据removeDataById(id) {this.$confirm('此操作将永久删除该记录, 是否继续?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => { // promise// 点击确定,远程调用ajaxreturn api.removeById(id)}).then((response) => {this.fetchData(this.page)this.$message.success(response.message)}).catch(() => {this.$message.info('取消删除')})},add() {this.$router.push('/processSet/templateSet')},edit(id) {this.$router.push('/processSet/templateSet?id=' + id)}}
}
</script>

3、添加审批模板


1、基本设置:一些基本信息

2、表单设置:动态表单

3、流程设置:本地设计流程定义,上传流程定义文件及流程定义图片(压缩上传)

涉及未实现接口:

1、获取全部审批分类

2、上传流程定义压缩文件

3.1、form-create

官网: http://www.form-create.com/v2/guide/

轻松搞定 form 表单,让你不再为表单而烦恼。

form-create 是一个可以通过 JSON 生成具有动态渲染、数据收集、验证和提交功能的表单生成组件。

form-create-designer 是基于 form-create实现的表单设计器组件。可以通过拖拽的方式快速创建表单,提高开发者对表单的开发效率,节省开发者的时间

表单设计器:

链接: http://www.form-create.com/designer/?fr=home

可以通过拖拽的方式快速配置动态表单,配置好的动态表单可以通过:生成JSON与生成Options获取数据,这两数据对于表字段:form_props与form_options,后续我们通过这两字段渲染动态表单。

大家可以根据表单设计器,查看数据格式

3.2、集成form-create

1、添加依赖

在package.json文件添加依赖,注意版本号,更高的版本号可能与本项目不兼容

"@form-create/element-ui": "^2.5.17",
"@form-create/designer": "^1.0.8",

2、在 main.js 中写入以下内容:

import formCreate from '@form-create/element-ui'
import FcDesigner from '@form-create/designer'
Vue.use(formCreate)
Vue.use(FcDesigner)

3、集成表单设计器

创建views/processSet/processTemplate/templateSet.vue

<template><div class="app-container"><div id="app1"><fc-designer class="form-build" ref="designer"/><el-button @click="save">获取数据</el-button></div></div>
</template><script>export default {data() {return {}},created() {},methods: {save() {console.log(this.$refs.designer.getRule())console.log(this.$refs.designer.getOption())}}
}
</script>

显示效果:

随便拉几个表单项,点击“获取数据”,就是我们需要的动态表单数据格式了。

3.3、获取全部审批分类接口

1、在ProcessTypeController类添加接口

@ApiOperation(value = "获取全部审批分类")
@GetMapping("findAll")
public Result findAll() {return Result.ok(processTypeService.list());
}

2、在processType.js添加前端接口

findAll() {return request({url: `${api_name}/findAll`,method: 'get'})
}

3.4、上传流程定义接口

在ProcessTemplateController类添加接口

@PreAuthorize("hasAuthority('bnt.processTemplate.templateSet')")
@ApiOperation(value = "上传流程定义")
@PostMapping("/uploadProcessDefinition")
public Result uploadProcessDefinition(MultipartFile file) throws FileNotFoundException {String path = new File(ResourceUtils.getURL("classpath:").getPath()).getAbsolutePath();String fileName = file.getOriginalFilename();// 上传目录File tempFile = new File(path + "/processes/");// 判断目录是否存着if (!tempFile.exists()) {tempFile.mkdirs();//创建目录}// 创建空文件用于写入文件File imageFile = new File(path + "/processes/" + fileName);// 保存文件流到本地try {file.transferTo(imageFile);} catch (IOException e) {e.printStackTrace();return Result.fail("上传失败");}Map<String, Object> map = new HashMap<>();//根据上传地址后续部署流程定义,文件名称为流程定义的默认keymap.put("processDefinitionPath", "processes/" + fileName);map.put("processDefinitionKey", fileName.substring(0, fileName.lastIndexOf(".")));return Result.ok(map);
}

3.5、模板设置完整代码

<template><div class="app-container"><el-steps :active="stepIndex" finish-status="success"><el-step title="基本设置"></el-step><el-step title="表单设置"></el-step><el-step title="流程设置"></el-step></el-steps><div class="tools-div"><el-button v-if="stepIndex > 1" icon="el-icon-check" type="primary" size="small" @click="pre()" round>上一步</el-button><el-button icon="el-icon-check" type="primary" size="small" @click="next()" round>{{stepIndex == 3 ? '提交保存' : '下一步'}}</el-button><el-button type="primary" size="small" @click="back()">返回</el-button></div><!-- 第一步 --><div v-show="stepIndex == 1" style="margin-top: 20px;"><el-form ref="flashPromotionForm" label-width="150px" size="small" style="padding-right: 40px;"><el-form-item label="审批类型"><el-select v-model="processTemplate.processTypeId" placeholder="请选择审批类型"><el-option v-for="item in processTypeList" :label="item.name" :value="item.id"></el-option></el-select></el-form-item><el-form-item label="审批名称"><el-input v-model="processTemplate.name"/></el-form-item><el-form-item label="审批图标"><el-select v-model="processTemplate.iconUrl" placeholder="请选择审批图标"><el-option v-for="item in iconUrlList" :label="item.iconUrl" :value="item.iconUrl"><img :src="item.iconUrl" style="width: 30px;height: 30px;vertical-align: text-bottom;"></el-option></el-select></el-form-item><el-form-item label="描述"><el-input v-model="processTemplate.description"/></el-form-item></el-form></div><!-- 第二步 --><div v-show="stepIndex == 2" style="margin-top: 20px;"><!--表单构建器--><fc-designer class="form-build" ref="designer"/></div><!-- 第三步 --><div v-show="stepIndex == 3" style="margin-top: 20px;"><el-uploadclass="upload-demo"dragaction="/dev-api/admin/process/processTemplate/uploadProcessDefinition":headers="uploadHeaders"multiple="false":before-upload="beforeUpload":on-success="onUploadSuccess":file-list="fileList"><i class="el-icon-upload"></i><div class="el-upload__text">将Activiti流程设计文件拖到此处,或<em>点击上传</em></div><div class="el-upload__tip" slot="tip">只能上传zip压缩文件,且不超过2048kb</div></el-upload></div></div>
</template><script>
import api from '@/api/process/processTemplate'
import processTypeApi from '@/api/process/processType'
import store from '@/store'const defaultForm = {id: '',name: '',iconUrl: '',formProps: '',formOptions: '',processDefinitionKey: '',processDefinitionPath: '',description: ''
}
export default {data() {return {stepIndex: 1,processTypeList: [],processTemplate: defaultForm,iconUrlList: [{ iconUrl: 'https://gw.alicdn.com/tfs/TB1t695CFYqK1RjSZLeXXbXppXa-102-102.png', tag: '请假' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1bHOWCSzqK1RjSZFjXXblCFXa-112-112.png', tag: '出差' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1cbCYCPTpK1RjSZKPXXa3UpXa-112-112.png', tag: '机票出差' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1cbCYCPTpK1RjSZKPXXa3UpXa-112-112.png', tag: '机票改签' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1e76lCOLaK1RjSZFxXXamPFXa-112-112.png', tag: '外出' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1Yfa0CG6qK1RjSZFmXXX0PFXa-112-112.png', tag: '补卡申请' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1Y8PlCNjaK1RjSZKzXXXVwXXa-112-112.png', tag: '加班' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB11X99CNTpK1RjSZFKXXa2wXXa-102-102.png', tag: '居家隔离' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1_YG.COrpK1RjSZFhXXXSdXXa-102-102.png', tag: '请假' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB13ca1CMDqK1RjSZSyXXaxEVXa-102-102.png', tag: '调岗' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1U9iBCSzqK1RjSZPcXXbTepXa-102-102.png', tag: '离职' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB11pS_CFzqK1RjSZSgXXcpAVXa-102-102.png', tag: '费用申请' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1t695CFYqK1RjSZLeXXbXppXa-102-102.png', tag: '用章申请' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB13f_aCQzoK1RjSZFlXXai4VXa-102-102.png', tag: '携章外出' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1_YG.COrpK1RjSZFhXXXSdXXa-102-102.png', tag: '学期内分期' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1_YG.COrpK1RjSZFhXXXSdXXa-102-102.png', tag: '特殊学费' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1Yfa0CG6qK1RjSZFmXXX0PFXa-112-112.png', tag: '充值卡申领' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1e76lCOLaK1RjSZFxXXamPFXa-112-112.png', tag: '礼品申领' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1FNG.CMHqK1RjSZFgXXa7JXXa-102-102.png', tag: '邮寄快递申请' },{ iconUrl: 'https://gw.alicdn.com/imgextra/i3/O1CN01LLn0YV1LhBXs7T2iO_!!6000000001330-2-tps-120-120.png', tag: '合同审批' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1e76lCOLaK1RjSZFxXXamPFXa-112-112.png', tag: '合同借阅' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1e76lCOLaK1RjSZFxXXamPFXa-112-112.png', tag: '魔点临时开门权限' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1bHOWCSzqK1RjSZFjXXblCFXa-112-112.png', tag: '北京科技园车证审批' },{ iconUrl: 'https://gw.alicdn.com/tfs/TB1e76lCOLaK1RjSZFxXXamPFXa-112-112.png', tag: '魔点访客提前预约审批' }],uploadHeaders: {'token': store.getters.token},fileList: []}},created() {let id = this.$route.query.idconsole.log(id)if (id > 0) {this.fetchDataById(id)}this.fetchProcessTypeData()},methods: {pre() {this.stepIndex -= 1},next() {if (this.stepIndex === 2) {this.processTemplate.formProps = JSON.stringify(this.$refs.designer.getRule())this.processTemplate.formOptions = JSON.stringify(this.$refs.designer.getOption())console.log(JSON.stringify(this.processTemplate))}if (this.stepIndex === 3) {this.saveOrUpdate()}this.stepIndex += 1},fetchProcessTypeData() {processTypeApi.findAll().then(response => {this.processTypeList = response.data})},fetchDataById(id) {api.getById(id).then(response => {this.processTemplate = response.data// 给表单设计器赋值this.$refs.designer.setRule(JSON.parse(this.processTemplate.formProps))this.$refs.designer.setOption(JSON.parse(this.processTemplate.formOptions))this.fileList = [{name: this.processTemplate.processDefinitionPath,url: this.processTemplate.processDefinitionPath}]})},saveOrUpdate() {this.saveBtnDisabled = true // 防止表单重复提交if (!this.processTemplate.id) {this.saveData()} else {this.updateData()}},// 新增saveData() {api.save(this.processTemplate).then(response => {this.$router.push('/processSet/processTemplate')})},// 根据id更新记录updateData() {api.updateById(this.processTemplate).then(response => {this.$router.push('/processSet/processTemplate')})},// 文件上传限制条件beforeUpload(file) {const isZip = file.type === 'application/x-zip-compressed'const isLt2M = file.size / 1024 / 1024 < 2if (!isZip) {this.$message.error('文件格式不正确!')return false}if (!isLt2M) {this.$message.error('上传大小不能超过 2MB!')return false}return true},// 上传成功的回调onUploadSuccess(res, file) {// 填充上传文件列表this.processTemplate.processDefinitionPath = res.data.processDefinitionPaththis.processTemplate.processDefinitionKey = res.data.processDefinitionKey},back() {this.$router.push('/processSet/processTemplate')}}
}
</script>

4、查看审批模板

查看审批模板基本信息、动态表单信息

4.1、添加按钮

<el-button type="text" size="mini" @click="show(scope.row)">查看审批设置</el-button>

4.2、定义data

rule: [],
option: {},
processTemplate: {},
formDialogVisible: false

4.3、定义方法

show(row) {this.rule = JSON.parse(row.formProps)this.option = JSON.parse(row.formOptions)this.processTemplate = rowthis.formDialogVisible = true
}

4.4、定义弹出层

<el-dialog title="查看审批设置" :visible.sync="formDialogVisible" width="35%"><h3>基本信息</h3><el-divider/><el-form ref="flashPromotionForm" label-width="150px" size="small" style="padding-right: 40px;"><el-form-item label="审批类型" style="margin-bottom: 0px;">{{ processTemplate.processTypeName }}</el-form-item><el-form-item label="名称" style="margin-bottom: 0px;">{{ processTemplate.name }}</el-form-item><el-form-item label="创建时间" style="margin-bottom: 0px;">{{ processTemplate.createTime }}</el-form-item></el-form><h3>表单信息</h3><el-divider/><div><form-create:rule="rule":option="option"></form-create></div><span slot="footer" class="dialog-footer"><el-button @click="formDialogVisible = false" size="small">取 消</el-button></span>
</el-dialog>

5、发布

发布后审批模板就不可以修改了,然后部署流程定义

5.1、添加controller接口

@PreAuthorize("hasAuthority('bnt.processTemplate.publish')")
@ApiOperation(value = "发布")
@GetMapping("/publish/{id}")
public Result publish(@PathVariable Long id) {processTemplateService.publish(id);return Result.ok();
}

5.2、添加service接口及实现

void publish(Long id);

接口实现:

@Transactional
@Override
public void publish(Long id) {ProcessTemplate processTemplate = this.getById(id);processTemplate.setStatus(1);processTemplateMapper.updateById(processTemplate);//TODO 部署流程定义,后续完善
}

5.3、前端实现

1、添加api接口

publish(id) {return request({url: `${api_name}/publish/${id}`,method: 'get'})
}

2、添加按钮

<el-button v-if="scope.row.status == 0" type="text" size="mini" @click="publish(scope.row.id)" :disabled="$hasBP('bnt.processTemplate.publish')  === false">发布</el-button>

3、添加按钮方法

publish(id) {api.publish(id).then(response => {this.$message.success('发布成功')this.fetchData(this.page)})
}

4.审批管理需求

1、需求描述

前面我们已经准备好了审批需要的数据:审批类型、审批模板(基本信息、动态表单、流程定义模型),接下来我们要部署审批定义模型,启动审批流,管理审批流

2、页面效果

3、数据库表设计

5、审批管理

1、审批管理CRUD

1.1、mapper

package com.example.process.mapper;import com.example.model.process.Process;
import com.example.vo.process.ProcessQueryVo;
import com.example.vo.process.ProcessVo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;@Mapper
public interface ProcessMapper extends BaseMapper<Process> {IPage<ProcessVo> selectPage(Page<ProcessVo> page, @Param("vo") ProcessQueryVo processQueryVo);
}

1.2、xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
"http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd"><mapper namespace="com.example.process.mapper.ProcessMapper"><select id="selectPage" resultType="com.atguigu.vo.process.ProcessVo">selecta.id,a.process_code,a.user_id,a.process_template_id,a.process_type_id,a.title,a.description,a.form_values,a.process_instance_id,a.current_auditor,a.status,a.create_time,a.update_time,b.name as processTemplateName,c.name as processTypeName,d.namefrom oa_process aleft join oa_process_template b on b.id = a.process_template_idleft join oa_process_type c on c.id = a.process_type_idleft join sys_user d on d.id = a.user_id<where><if test="vo.keyword != null and vo.keyword != ''">and (a.process_code like CONCAT('%',#{vo.keyword},'%') or  a.title like CONCAT('%',#{vo.keyword},'%') or d.phone like CONCAT('%',#{vo.keyword},'%') or d.name like CONCAT('%',#{vo.keyword},'%'))</if><if test="vo.userId != null and vo.userId != ''">and a.user_id = #{vo.userId}</if><if test="vo.status != null and vo.status != ''">and a.status = #{vo.status}</if><if test="vo.createTimeBegin != null and vo.createTimeBegin != ''">and a.create_time >= #{vo.createTimeBegin}</if><if test="vo.createTimeEnd != null and vo.createTimeEnd != ''">and a.create_time &lt;= #{vo.createTimeEnd}</if></where>order by id desc</select></mapper>

1.3、service接口

package com.example.process.service;import com.example.model.process.Process;
import com.example.vo.process.ProcessQueryVo;
import com.example.vo.process.ProcessVo;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;public interface ProcessService extends IService<Process> {IPage<ProcessVo> selectPage(Page<ProcessVo> pageParam, ProcessQueryVo processQueryVo);}

1.4、service接口实现

package com.example.process.service.impl;import com.example.model.process.Process;
import com.example.process.mapper.ProcessMapper;
import com.example.process.service.ProcessService;
import com.example.vo.process.ProcessQueryVo;
import com.example.vo.process.ProcessVo;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import javax.annotation.Resource;@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class ProcessServiceImpl extends ServiceImpl<ProcessMapper, Process> implements ProcessService {@Autowiredprivate ProcessMapper processMapper;@Overridepublic IPage<ProcessVo> selectPage(Page<ProcessVo> pageParam, ProcessQueryVo processQueryVo) {IPage<ProcessVo> page = processMapper.selectPage(pageParam, processQueryVo);return page;}
}

1.5、controller接口

package com.example.process.controller;import com.example.common.result.Result;
import com.example.process.service.ProcessService;
import com.example.vo.process.ProcessQueryVo;
import com.example.vo.process.ProcessVo;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@Api(tags = "审批流管理")
@RestController
@RequestMapping(value = "/admin/process")
@SuppressWarnings({"unchecked", "rawtypes"})
public class ProcessController {@Autowiredprivate ProcessService processService;@PreAuthorize("hasAuthority('bnt.process.list')")@ApiOperation(value = "获取分页列表")@GetMapping("{page}/{limit}")public Result index(@ApiParam(name = "page", value = "当前页码", required = true)@PathVariable Long page,@ApiParam(name = "limit", value = "每页记录数", required = true)@PathVariable Long limit,@ApiParam(name = "processQueryVo", value = "查询对象", required = false)ProcessQueryVo processQueryVo) {Page<ProcessVo> pageParam = new Page<>(page, limit);IPage<ProcessVo> pageModel = processService.selectPage(pageParam, processQueryVo);return Result.ok(pageModel);}}

2、前端审批列表

2.1、定义api

创建src/api/process/process.js

import request from '@/utils/request'const api_name = '/admin/process'export default {getPageList(page, limit, searchObj) {return request({url: `${api_name}/${page}/${limit}`,method: 'get',params: searchObj // url查询字符串或表单键值对})}
}

2.2、创建vue页面

创建views/processMgr/process/list.vue

<template><div class="app-container"><div class="search-div"><el-form label-width="70px" size="small"><el-row><el-col :span="8"><el-form-item label="关 键 字"><el-input style="width: 95%" v-model="searchObj.keyword" placeholder="审批编号/标题/手机号码/姓名"></el-input></el-form-item></el-col><el-col :span="8"><el-form-item label="状态"><el-selectv-model="searchObj.status"placeholder="请选状态" style="width: 100%;"><el-optionv-for="item in statusList":key="item.status":label="item.name":value="item.status"/></el-select></el-form-item></el-col><el-col :span="8"><el-form-item label="操作时间"><el-date-pickerv-model="createTimes"type="datetimerange"range-separator="至"start-placeholder="开始时间"end-placeholder="结束时间"value-format="yyyy-MM-dd HH:mm:ss"style="margin-right: 10px;width: 100%;"/></el-form-item></el-col></el-row><el-row style="display:flex"><el-button type="primary" icon="el-icon-search" size="mini" :loading="loading" @click="fetchData()">搜索</el-button><el-button icon="el-icon-refresh" size="mini" @click="resetData">重置</el-button></el-row></el-form></div><!-- 列表 --><el-tablev-loading="listLoading":data="list"stripeborderstyle="width: 100%;margin-top: 10px;"><el-table-columnlabel="序号"width="70"align="center"><template slot-scope="scope">{{ (page - 1) * limit + scope.$index + 1 }}</template></el-table-column><el-table-column prop="processCode" label="审批编号" width="130"/><el-table-column prop="title" label="标题" width="180"/><el-table-column prop="name" label="用户"/><el-table-column prop="processTypeName" label="审批类型"/><el-table-column prop="processTemplateName" label="审批模板"/><el-table-column prop="description" label="描述" width="180"/><el-table-column label="状态"><template slot-scope="scope">{{ scope.row.status === 1 ? '审批中' : scope.row.status === 2 ? '完成' : '驳回' }}</template></el-table-column><el-table-column prop="createTime" label="创建时间" width="160"/><el-table-column label="操作" width="120" align="center"><template slot-scope="scope"><el-button type="text" size="mini" @click="show(scope.row.id)">查看</el-button></template></el-table-column></el-table><!-- 分页组件 --><el-pagination:current-page="page":total="total":page-size="limit":page-sizes="[5, 10, 20, 30, 40, 50, 100]"style="padding: 30px 0; text-align: center;"layout="sizes, prev, pager, next, jumper, ->, total, slot"@current-change="fetchData"@size-change="changeSize"/></div>
</template><script>
import api from '@/api/process/process'export default {data() {return {listLoading: true, // 数据是否正在加载list: null, // banner列表total: 0, // 数据库中的总记录数page: 1, // 默认页码limit: 10, // 每页记录数searchObj: {}, // 查询表单对象statusList: [{ 'status': '1', 'name': '进行中' },{ 'status': '2', 'name': '已完成' },{ 'status': '-1', 'name': '驳回' }],createTimes: []}},// 生命周期函数:内存准备完毕,页面尚未渲染created() {console.log('list created......')this.fetchData()},// 生命周期函数:内存准备完毕,页面渲染成功mounted() {console.log('list mounted......')},methods: {// 当页码发生改变的时候changeSize(size) {console.log(size)this.limit = sizethis.fetchData(1)},// 加载banner列表数据fetchData(page = 1) {console.log('翻页。。。' + page)// 异步获取远程数据(ajax)this.page = pageif (this.createTimes && this.createTimes.length === 2) {this.searchObj.createTimeBegin = this.createTimes[0]this.searchObj.createTimeEnd = this.createTimes[1]}api.getPageList(this.page, this.limit, this.searchObj).then(response => {this.list = response.data.recordsthis.total = response.data.total// 数据加载并绑定成功this.listLoading = false})},// 重置查询表单resetData() {console.log('重置查询表单')this.searchObj = {}this.fetchData()},show(id) {console.log(id)}}
}
</script>

3、部署流程定义

3.1、根据上传部署

3.1.1、定义service接口

操作类:ProcessService

void deployByZip(String deployPath);
3.1.2、service接口实现

操作类:ProcessServiceImpl

@Override
public void deployByZip(String deployPath) {// 定义zip输入流InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(deployPath);ZipInputStream zipInputStream = new ZipInputStream(inputStream);// 流程部署Deployment deployment = repositoryService.createDeployment().addZipInputStream(zipInputStream).deploy();
}

3.2、完善审批模板发布

操作类:ProcessTemplateServiceImpl

@Autowired
private ProcessService processService;@Transactional
@Override
public void publish(Long id) {ProcessTemplate processTemplate = this.getById(id);processTemplate.setStatus(1);processTemplateMapper.updateById(processTemplate);//优先发布在线流程设计if(!StringUtils.isEmpty(processTemplate.getProcessDefinitionPath())) {processService.deployByZip(processTemplate.getProcessDefinitionPath());}
}

说明:审批模板发布后不可以再编辑

3.4、页面按钮控制

按钮添加判断,发布后不可以编辑:v-if=“scope.row.status == 0”

<el-button type="text" v-if="scope.row.status == 0" size="mini" @click="edit(scope.row.id)" :disabled="$hasBP('bnt.processTemplate.templateSet')  === false">修改审批设置</el-button>
<el-button type="text" v-if="scope.row.status == 0" size="mini" @click="removeDataById(scope.row.id)" :disabled="$hasBP('bnt.processTemplate.remove')  === false">删除</el-button>

云尚办公OA系统学习笔记(6.审批设置-管理端)相关推荐

  1. 云尚办公OA系统学习笔记(5.权限管理)

    目录 一.权限管理 1.权限管理介绍 1.1 菜单权限 1.2 按钮权限 1.3 权限管理设计思路 2.JWT 2.1 JWT介绍 2.2 JWT令牌的组成 2.3 项目集成JWT 2.3.1 引入依 ...

  2. 云尚办公OA系统(8.微信公众号)

    目录 一.功能说明 二.公众号菜单管理 1.菜单管理CRUD 1.1.mapper 1.2.service接口 1.3.service接口实现 1.4.controller接口 2.前端实现 2.1. ...

  3. oa系统云服务器租赁,新睿云小编手把手教您用云服务器安装办公OA系统

    AnyOffice.Net在平台准备充分的情况下可以通过一次点击安装.配置IIS站点就能完成.所以在安装AnyOffice.Net系统之前,确保IIS+.NET 2.0+SQL Server数据库安装 ...

  4. 尚医通项目学习笔记Part1

    尚医通项目学习笔记 前言 一.目前学习进度 二.学习记录 1.项目简介 1.1 项目所会用到的技术栈 1.2 业务流程 2.项目学习笔记 2.1MyBatis-Plus相关 2.2搭建项目框架 2.3 ...

  5. 办公OA系统毕业设计论文

    摘 要 随着计算机技术的飞速发展,企业管理的传统方式逐渐将要被电子信息化管理所取代.近几年来,电子信息化是当代非常流行的经营模式,是学校.企业等团体组织最常用的系统,这种电子信息管理系统可以提高管理效 ...

  6. 尚硅谷-云尚办公-项目复盘

    尚硅谷-云尚办公-项目复盘 资料地址 本文介绍 问题汇总 问题1.knife4j无法下载 视频4 问题2.dev等含义 视频5 问题3.wrapper继承/实现图 视频8 问题4.修改统一返回结果 视 ...

  7. 尚学堂JAVA高级学习笔记_1/2

    尚学堂JAVA高级学习笔记 文章目录 尚学堂JAVA高级学习笔记 写在前面 第1章 手写webserver 1. 灵魂反射 2. 高效解析xml 3. 解析webxml 4. 反射webxml 5. ...

  8. 五、云尚办公-菜单管理

    云尚办公系统:菜单管理 B站直达[为尚硅谷点赞]: https://www.bilibili.com/video/BV1Ya411S7aT 本博文以课程相关为主发布,并且融入了自己的一些看法以及对学习 ...

  9. 阿里云HaaS100物联网开发板学习笔记(二)硬件控制初步--让小灯闪烁起来

    摘要:无论是哪种开发板,要想开发特定的功能,必先从GPIO开始,HaaS100开发也是一样.如果仅仅利用HaaS100的联网功能,那简直是太浪费了.HaaS100拥有其他开发板所具备的所有的功能,比如 ...

最新文章

  1. 如何将半页纸论文写到十页?
  2. 架构之道(4) - 最後的最後,让大家都知道自己在做什麽
  3. 【Groovy】Groovy 方法调用 ( Groovy 构造函数中为成员赋值 | Groovy 函数的参数传递与键值对参数 | 完整代码示例 )
  4. Android根据baidu Android定位SDK实现定位
  5. visual studio 2017 显示行号
  6. 1t硬盘怎么分区最好_win7系统硬盘怎么分区 win7系统硬盘分区步骤【介绍】
  7. gini系数 决策树_案例7:机器学习--使用决策树实现泰坦尼克号乘客生存率预测...
  8. 计算机主机异常经常蓝屏,计算机频繁发生蓝屏怎么解决
  9. django外键和多数据库应用
  10. total commander按文件夹大小显示
  11. MAC地址前三位是厂家标识符(Organizationally Unique Identifier),可以从IEEE官网查询
  12. 鲍威尔法c语言程序详解,鲍威尔法编程-powell法编程 c语言编程 c++6.0
  13. 深入浅出WPF教程笔记_XAML介绍
  14. Servlet常用依赖
  15. 耗资52亿美元,历时15年,人类有史以来建造的最复杂机器
  16. leetcode 三数之和
  17. php加速模块cpan模块,查看perl模块和cpan模块介绍
  18. nginx+域名配置
  19. 超导量子计算机最新报道,量子效应的量子计算机,在高温超导体加持下,或将迎来重大突破!...
  20. 人脸识别接口_人脸识别双模摄像头解析,免费搭配活体检测

热门文章

  1. php随机给文字拼音,汉字转化为拼音(php版)
  2. 如何在微信里一键唤醒app
  3. MSCI推出新的开放分析平台MSCI Beon
  4. [转载]uni-app 换肤实现-原生导航栏、tabbar和页面全部替换
  5. 成都传智播客就业班简介
  6. GESP一级、二级、三级、四级 样题 真题 解析
  7. git解决冲突 visual studio
  8. 常量和变量的区别(正确认识)
  9. 访问学者在美国生活中有哪些风俗禁忌?
  10. 小公司比较吃亏的两道微服务面试题