首頁 > Java > java教程 > 怎麼使用SpringBoot+Vue+Flowable模擬實作請假審批流程

怎麼使用SpringBoot+Vue+Flowable模擬實作請假審批流程

王林
發布: 2023-05-16 19:05:01
轉載
1965 人瀏覽過

1. 效果展示

在正式開搞之前,我先來給小夥伴們看下我們今天要完成的效果。

簡單起見,我這裡並沒有引入用戶、角色等概念,涉及到用戶的地方都是手動輸入,在後續的文章中我會繼續結合Spring Security 來和大家展示引入用戶之後的情況。

我們先來看看請假頁面:

怎麼使用SpringBoot+Vue+Flowable模擬實作請假審批流程

員工可以在這個頁面輸入姓名,請假天數以及請假理由等,然後點擊按鈕提交一個請假申請。

當員工提交請假申請之後,這個請假申請預設是由經理來處理的,此時經理登入之後,就可以看到員工提交上來的請求:

怎麼使用SpringBoot+Vue+Flowable模擬實作請假審批流程

經理此時可以選擇批准或拒絕。無論是批准或拒絕,都可以透過簡訊或郵件等告知員工。

對員工來說,也可以在一個頁面查詢自己請假流程的最終情況:

怎麼使用SpringBoot+Vue+Flowable模擬實作請假審批流程

#2. 工程創建

我就直接來和小夥伴們展示Spring Boot 中flowable 的用法了。

首先我們建立一個Spring Boot 項目,創建的時候引入Web 和MySQL 驅動依賴即可,項目創建成功之後,再引入flowable 依賴,最終的依賴文件如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.flowable</groupId>
    <artifactId>flowable-spring-boot-starter</artifactId>
    <version>6.7.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
登入後複製

項目創建成功之後,首先需要我們在application.properties 中配置一下資料庫連接信息,如下:

spring.datasource.username=root
spring.datasource.password=123
spring.datasource.url=jdbc:mysql:///flowable02?serverTimezone=Asia/Shanghai&useSSL=false&nullCatalogMeansCurrent=true
登入後複製

一旦完成配置,Spring Boot 專案初次運行時,將自動建立相關表格和所需資料。

同時,Spring Boot 專案也會自動建立並揭露 Flowable 中的 ProcessEngine、CmmnEngine、DmnEngine、FormEngine、ContentEngine 及 IdmEngine 等 Bean。

所有的 Flowable 服務都可作為 Spring Bean 呼叫。例如 RuntimeService、TaskService、HistoryService 等等服務,我們都可以在需要使用的時候,直接注入就可以使用了。

同時:

  • resources/processes 目錄下的任何BPMN 2.0 流程定義都會被自動部署,所以在Spring Boot 專案中,我們只需要將自己的流程文件放對位置即可,剩下的事情就會自動完成。

  • cases 目錄下的任何 CMMN 1.1 例子都會自動部署。

  • forms 目錄下的任何 Form 定義都會自動部署。

3. 流程圖分析

今天這個例子比較簡單,就是一個請假流程,我暫時先不跟小夥伴們去扯畫流程圖的事,咱們直接用一個官網現成的請假流程圖:

怎麼使用SpringBoot+Vue+Flowable模擬實作請假審批流程

#我們先來簡單分析一下這張圖:

  • 最左側的圓圈叫做啟動事件(start event),這表示一個流程實例的起點。

  • 一個流程啟動之後,先到達第一個有使用者圖示的矩形中,這個矩形稱為一個User Task,在這個User Task 中,經理可以選擇批准也可以拒絕。

  • UserTask 的下一步是一個菱形,這個稱為排他網關(Exclusive Gateway),這個會將請求路由到不同的地方。

  • 先說批准,如果在第一個矩形中,經理選擇了批准,那麼就會進入到一個帶有齒輪圖示的矩形中,在這個矩形中我們可以額外做一些事情,然後又會呼叫到一個UserTask,最終完成整個流程。

  • 如果經理選擇了拒絕,則會進入到下面的發郵件的矩形中,在這個中我們可以給員工發送一個通知,告知他請假沒有通過。

  • 當系統走到最右邊的圓圈之後,就表示這個流程執行結束了。

這個流程圖對應的XML 檔案位於src/main/resources/processes/holiday-request.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:flowable="http://flowable.org/bpmn"
             typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath"
             targetNamespace="http://www.flowable.org/processdef">
    <process id="holidayRequest" name="Holiday Request" isExecutable="true">

        <startEvent id="startEvent"/>
        <sequenceFlow sourceRef="startEvent" targetRef="approveTask"/>

        <userTask id="approveTask" name="Approve or reject request" flowable:candidateGroups="managers"/>
        <sequenceFlow sourceRef="approveTask" targetRef="decision"/>

        <exclusiveGateway id="decision"/>
        <sequenceFlow sourceRef="decision" targetRef="externalSystemCall">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[
          ${approved}
        ]]>
            </conditionExpression>
        </sequenceFlow>
        <sequenceFlow  sourceRef="decision" targetRef="rejectLeave">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[
          ${!approved}
        ]]>
            </conditionExpression>
        </sequenceFlow>

        <serviceTask id="externalSystemCall" name="Enter holidays in external system"
                     flowable:class="org.javaboy.flowable02.flowable.Approve"/>
        <sequenceFlow sourceRef="externalSystemCall" targetRef="holidayApprovedTask"/>

        <userTask id="holidayApprovedTask" flowable:assignee="${employee}" name="Holiday approved"/>
        <sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/>

        <serviceTask id="rejectLeave" name="Send out rejection email"
                     flowable:class="org.javaboy.flowable02.flowable.Reject"/>
        <sequenceFlow sourceRef="rejectLeave" targetRef="rejectEnd"/>

        <endEvent id="approveEnd"/>

        <endEvent id="rejectEnd"/>

    </process>
</definitions>
登入後複製

很多想學習流程引擎的夥伴都會被這個XML 檔案勸退,但是! ! !

如果你願意靜下心來認真閱讀這個 XML 文件,你會發現流程引擎原來如此簡單!

我們來挨個看下這裡的每一個節點:

  • process:這表示一個流程,例如本文和大家分享的請假就是一個流程。

  • startEvent:這表示流程的開始,這就是一個開始事件。

  • userTask:這就是一個特定的流程節點了,flowable:candidateGroups 屬性表示這個節點該由哪個使用者群組中的使用者來處理。

  • sequenceFlow:这就是连接各个流程节点之间的线条,这个里边一般有两个属性,sourceRef 和 targetRef,前者表示线条的起点,后者表示线条的终点。

  • exclusiveGateway:表示一个排他性网关,也就是那个菱形选择框。

  • 从排他性网关出来的线条有两个,大家注意看上面的代码,这两个线条中都涉及到一个变量 approved,如果这个变量为 true,则 targeRef 就是 externalSystemCall;如果这个变量为 false,则 targetRef 就是 rejectLeave。

  • serviceTask:这就是我们定义的一个具体的外部服务,如果在整个流程执行的过程中,你有一些需要自己完成的事情,那么可以通过 serviceTask 来实现,这个节点会有一个 flowable:class 属性,这个属性的值就是一个自定义类。

  • 另外,上文中部分节点中还涉及到变量 ${},这个变量是在流程执行的过程中传入进来的。

总而言之,只要小伙伴们静下心来认真阅读一下上面的 XML,你会发现 So Easy!

4. 请假申请

好了,接下来我们就来看一个具体的请假申请。由于请假流程只要放对位置,就会自动加载,所以我们并不需要手动加载请假流程,直接开始一个请假申请流程即可。

4.1 服务端接口

首先我们需要一个实体类来接受前端传来的请假参数:用户名、请假天数以及请假理由:

public class AskForLeaveVO {
    private String name;
    private Integer days;
    private String reason;
    // 省略 getter/setter
}
登入後複製

再拿出祖传的 RespBean,以便响应数据方便一些:

public class RespBean {
    private Integer status;
    private String msg;
    private Object data;

    public static RespBean ok(String msg, Object data) {
        return new RespBean(200, msg, data);
    }


    public static RespBean ok(String msg) {
        return new RespBean(200, msg, null);
    }


    public static RespBean error(String msg, Object data) {
        return new RespBean(500, msg, data);
    }


    public static RespBean error(String msg) {
        return new RespBean(500, msg, null);
    }

    private RespBean() {
    }

    private RespBean(Integer status, String msg, Object data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }
    // 省略 getter/setter
}
登入後複製

接下来我们提供一个处理请假申请的接口:

@RestController
public class AskForLeaveController {

    @Autowired
    AskForLeaveService askForLeaveService;

    @PostMapping("/ask_for_leave")
    public RespBean askForLeave(@RequestBody AskForLeaveVO askForLeaveVO) {
        return askForLeaveService.askForLeave(askForLeaveVO);
    }
}
登入後複製

核心逻辑在 AskForLeaveService 中,来继续看:

@Service
public class AskForLeaveService {

    @Autowired
    RuntimeService runtimeService;

    @Transactional
    public RespBean askForLeave(AskForLeaveVO askForLeaveVO) {
        Map<String, Object> variables = new HashMap<>();
        variables.put("name", askForLeaveVO.getName());
        variables.put("days", askForLeaveVO.getDays());
        variables.put("reason", askForLeaveVO.getReason());
        try {
            runtimeService.startProcessInstanceByKey("holidayRequest", askForLeaveVO.getName(), variables);
            return RespBean.ok("已提交请假申请");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return RespBean.error("提交申请失败");
    }
}
登入後複製

小伙伴们看一下,在提交请假申请的时候,分别传入了 name、days 以及 reason 三个参数,我们将这三个参数放入到一个 Map 中,然后通过 RuntimeService#startProcessInstanceByKey 方法来开启一个流程,开启流程的时候一共传入了三个参数:

  • 第一个参数表示流程引擎的名字,这就是我们刚才在流程的 XML 文件中定义的名字。

  • 第二个参数表示当前这个流程的 key,我用了申请人的名字,将来我们可以通过申请人的名字查询这个人曾经提交的所有申请流程。

  • 第三个参数就是我们的变量了。

好了,这服务端就写好了。

4.2 前端页面

接下来我们来开发前端页面。

前端我使用 Vue+ElementUI+Axios,咱们这个案例比较简单,就没有必要搭建单页面了,直接用普通的 HTML 就行了。另外,Vue 我是用了 Vue3:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <!-- Import style -->
    <link rel="stylesheet" href="//unpkg.com/element-plus/dist/index.css" rel="external nofollow"  rel="external nofollow"  rel="external nofollow" />
    <script src="https://unpkg.com/vue@3"></script>
    <!-- Import component library -->
    <script src="//unpkg.com/element-plus"></script>
</head>
<body>
<div id="app">
    <h2>开始一个请假流程</h2>
    <table>
        <tr>
            <td>请输入姓名:</td>
            <td>
                <el-input type="text" v-model="afl.name"/>
            </td>
        </tr>
        <tr>
            <td>请输入请假天数:</td>
            <td>
                <el-input type="text" v-model="afl.days"/>
            </td>
        </tr>
        <tr>
            <td>请输入请假理由:</td>
            <td>
                <el-input type="text" v-model="afl.reason"/>
            </td>
        </tr>
    </table>
    <el-button type="primary" @click="submit">提交请假申请</el-button>
</div>
<script>
    Vue.createApp(
        {
            data() {
                return {
                    afl: {
                        name: &#39;javaboy&#39;,
                        days: 3,
                        reason: &#39;休息一下&#39;
                    }
                }
            },
            methods: {
                submit() {
                    let _this = this;
                    axios.post(&#39;/ask_for_leave&#39;, this.afl)
                        .then(function (response) {
                            if (response.data.status == 200) {
                                //提交成功
                                _this.$message.success(response.data.msg);
                            } else {
                                //提交失败
                                _this.$message.error(response.data.msg);
                            }
                        })
                        .catch(function (error) {
                            console.log(error);
                        });
                }
            }
        }
    ).use(ElementPlus).mount(&#39;#app&#39;)
</script>
</body>
</html>
登入後複製

这个页面有几个需要注意的点:

  • 通过 Vue.createApp 来创建一个 Vue 实例,这跟以前 Vue2 中直接 new 一个 Vue 实例不一样。

  • 使用 use 方法来配置 ElementPlus 插件,这一点与 Vue2 不同。在 Vue2 中,使用 ElementUI 只需要在HTML页面中进行简单的引用即可,不需要额外的步骤。

  • 剩下的东西就比较简单了,上面先引入 Vue3、Axios 以及 ElementPlus,然后三个输入框,点击按钮提交请求,参数就是三个输入框中的数据,提交成功或者失败,分别弹个框出来提示一下就行了。

好啦,这就写好了。

然而,提交完成后,没有一个直观的展示,虽然前端提示说提交成功了,但是究竟成功没,还得眼见为实。

5. 任务展示

好了,接下来我们要做的事情就是把用户提交的流程展示出来。

按理说,比如经理登录成功之后,系统页面就自动展示出来经理需要审批的流程,但是我们当前这个例子为了简单,就没有登录这个操作了,需要需要用户将来在网页上选一下自己的身份,接下来就会展示出这个身份所对应的需要操作的流程。

我们来看任务接口:

@GetMapping("/list")
public RespBean leaveList(String identity) {
    return askForLeaveService.leaveList(identity);
}
登入後複製

这个请求参数 identity 就表示当前用户的身份(本来应该是登录后自动获取,但是因为我们目前没有登录,所以这个参数是由前端传递过来)。来继续看 askForLeaveService 中的方法:

@Service
public class AskForLeaveService {

    @Autowired
    TaskService taskService;

    public RespBean leaveList(String identity) {
        List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup(identity).list();
        List<Map<String, Object>> list = new ArrayList<>();
        for (int i = 0; i < tasks.size(); i++) {
            Task task = tasks.get(i);
            Map<String, Object> variables = taskService.getVariables(task.getId());
            variables.put("id", task.getId());
            list.add(variables);
        }
        return RespBean.ok("加载成功", list);
    }
}
登入後複製

Task 就是流程中要做的每一件事情,我们首先通过 TaskService,查询出来这个用户需要处理的任务,例如前端前传来的是 managers,那么这里就是查询所有需要由 managers 用户组处理的任务。

这段代码要结合流程图一起来理解,小伙伴们回顾下我们流程图中有如下一句:

<userTask id="approveTask" name="Approve or reject request" flowable:candidateGroups="managers"/>
登入後複製

这意思就是说这个 userTask 是由 managers 这个组中的用户来处理,所以上面 Java 代码中的查询就是查询 managers 这个组中的用户需要审批的任务。

我们将所有需要审批的任务查询出来后,通过 taskId 可以进一步查询到这个任务中当时传入的各种变量,我们将这些数据封装成一个对象,并最终返回到前端。

最后,我们再来看下前端页面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <!-- Import style -->
    <link rel="stylesheet" href="//unpkg.com/element-plus/dist/index.css" rel="external nofollow"  rel="external nofollow"  rel="external nofollow" />
    <script src="https://unpkg.com/vue@3"></script>
    <!-- Import component library -->
    <script src="//unpkg.com/element-plus"></script>
</head>
<body>
<div id="app">
    <div>
        <div>请选择你的身份:</div>
        <div>
            <el-select name="" id="" v-model="identity" @change="initTasks">
                <el-option :value="iden" v-for="(iden,index) in identities" :key="index" :label="iden"></el-option>
            </el-select>
            <el-button type="primary" @click="initTasks">刷新一下</el-button>
        </div>

    </div>
    <el-table border strip :data="tasks">
        <el-table-column prop="name" label="姓名"></el-table-column>
        <el-table-column prop="days" label="请假天数"></el-table-column>
        <el-table-column prop="reason" label="请假原因"></el-table-column>
        <el-table-column lable="操作">
            <template #default="scope">
                <el-button type="primary" @click="approveOrReject(scope.row.id,true,scope.row.name)">批准</el-button>
                <el-button type="danger" @click="approveOrReject(scope.row.id,false,scope.row.name)">拒绝</el-button>
            </template>
        </el-table-column>
    </el-table>
</div>
<script>
    Vue.createApp(
        {
            data() {
                return {
                    tasks: [],
                    identities: [
                        &#39;managers&#39;
                    ],
                    identity: &#39;&#39;
                }
            },
            methods: {
                initTasks() {
                    let _this = this;
                    axios.get(&#39;/list?identity=&#39; + this.identity)
                        .then(function (response) {
                            _this.tasks = response.data.data;
                        })
                        .catch(function (error) {
                            console.log(error);
                        });
                }
            }
        }
    ).use(ElementPlus).mount(&#39;#app&#39;)
</script>
</body>
</html>
登入後複製

我们先选择一个用户身份,具体说就是在下拉菜单中选择。在完成选择后,调用 initTasks 方法,发起网络请求并渲染其结果。

最终效果如下:

怎麼使用SpringBoot+Vue+Flowable模擬實作請假審批流程

当然用户也可以点击刷新按钮,刷新列表。

这样,当第五小节中,员工提交了一个请假审批之后,我们在这个列表中就可以查看到员工提交的请假审批了(在流程图中,我们直接设置了用户的请假审批固定提交给 managers,在后续的文章中,松哥会教大家如何把这个提交的目标用户变成一个动态的)。

6. 请假审批

接下来经理就可以选择批准或者是拒绝这请假了。

首先我们封装一个实体类用来接受前端传来的请求:

public class ApproveRejectVO {
    private String taskId;
    private Boolean approve;
    private String name;
    // 省略 getter/setter
}
登入後複製

参数都好理解,approve 为 true 表示申请通过,false 表示申请被拒绝。

接下来我们来看接口:

@PostMapping("/handler")
public RespBean askForLeaveHandler(@RequestBody ApproveRejectVO approveRejectVO) {
    return askForLeaveService.askForLeaveHandler(approveRejectVO);
}
登入後複製

看具体的 askForLeaveHandler 方法:

@Service
public class AskForLeaveService {

    @Autowired
    TaskService taskService;

    public RespBean askForLeaveHandler(ApproveRejectVO approveRejectVO) {
        try {
            boolean approved = approveRejectVO.getApprove();
            Map<String, Object> variables = new HashMap<String, Object>();
            variables.put("approved", approved);
            variables.put("employee", approveRejectVO.getName());
            Task task = taskService.createTaskQuery().taskId(approveRejectVO.getTaskId()).singleResult();
            taskService.complete(task.getId(), variables);
            if (approved) {
                //如果是同意,还需要继续走一步
                Task t = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
                taskService.complete(t.getId());
            }
            return RespBean.ok("操作成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return RespBean.error("操作失败");
    }
}
登入後複製

大家注意这个审批流程:

  • 审批时需要两个参数,approved 和 employee,approved 为 true,就会自动进入到审批通过的流程中,approved 为 false 则会自动进入到拒绝流程中。

  • 通过 taskService,结合 taskId,从流程中查询出对应的 task,然后调用 taskService.complete 方法传入 taskId 和 变量,以使流程向下走。

  • 小伙伴们再回顾一下我们前面的流程图,如果请求被批准备了,那么在执行完自定义的 Approve 逻辑后,就会进入到 Holiday approved 这个 userTask 中,注意此时并不会继续向下走了(还差一步到结束事件);如果是请求拒绝,则在执行完自定义的 Reject 逻辑后,就进入到结束事件了,这个流程就结束了。

  • 针对第三条,所以代码中我们还需要额外再加一步,如果是 approved 为 true,那么就再从当前流程中查询出来需要执行的 task,再调用 complete 继续走一步,此时就到了结束事件了,这个流程就结束了。注意这次的查询是根据当前流程的 ID 查询的,一个流程就是一条线,这条线上有很多 Task,我们可以从 Task 中获取到流程的 ID。

好啦,接口就写好了。

当然,这里还涉及到两个自定义的逻辑,就是批准或者拒绝之后的自定义逻辑,这个其实很好写,如下:

public class Approve implements JavaDelegate {
    @Override
    public void execute(DelegateExecution execution) {
        System.out.println("申请通过:"+execution.getVariables());
    }
}
登入後複製

我们自定义类实现 JavaDelegate 接口即可,然后我们在 execute 方法中做自己想要做的事情即可,execution 中有这个流程中的所有变量。我们可以在这里发邮件、发短信等等。Reject 的定义方式也是类似的。一旦完成这些自定义类的编写,它们就可以被配置到流程图中(请参考上文提供的流程图)。

最后再来看看前端提交方法就简单了(页面源码上文已经列出):

approveOrReject(taskId, approve,name) {
    let _this = this;
    axios.post(&#39;/handler&#39;, {taskId: taskId, approve: approve,name:name})
        .then(function (response) {
            _this.initTasks();
        })
        .catch(function (error) {
            console.log(error);
        });
}
登入後複製

这就一个普通的 Ajax 请求,批准的话第二个参数就为 true,拒绝的话第二个参数就为 false。

7. 结果查询

最后,每个用户都可以查看自己曾经的申请记录。本来这个登录之后就可以展示了,但是因为我们没有登录,所以这里也是需要手动输入查询的用户,然后根据用户名查询这个用户的历史记录,我们先来看查询接口:

@GetMapping("/search")
public RespBean searchResult(String name) {
    return askForLeaveService.searchResult(name);
}
登入後複製

参数就是要查询的用户名。具体的查询流程如下:

public RespBean searchResult(String name) {
    List<HistoryInfo> historyInfos = new ArrayList<>();
    List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(name).finished().orderByProcessInstanceEndTime().desc().list();
    for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {
        HistoryInfo historyInfo = new HistoryInfo();
        Date startTime = historicProcessInstance.getStartTime();
        Date endTime = historicProcessInstance.getEndTime();
        List<HistoricVariableInstance> historicVariableInstances = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(historicProcessInstance.getId())
                .list();
        for (HistoricVariableInstance historicVariableInstance : historicVariableInstances) {
            String variableName = historicVariableInstance.getVariableName();
            Object value = historicVariableInstance.getValue();
            if ("reason".equals(variableName)) {
                historyInfo.setReason((String) value);
            } else if ("days".equals(variableName)) {
                historyInfo.setDays(Integer.parseInt(value.toString()));
            } else if ("approved".equals(variableName)) {
                historyInfo.setStatus((Boolean) value);
            } else if ("name".equals(variableName)) {
                historyInfo.setName((String) value);
            }
        }
        historyInfo.setStartTime(startTime);
        historyInfo.setEndTime(endTime);
        historyInfos.add(historyInfo);
    }
    return RespBean.ok("ok", historyInfos);
}
登入後複製
  • 我们当时在开启流程的时候,传入了一个参数 key,这里就是再次通过这个 key,也就是用户名去查询历史流程,查询的时候还加上了 finished 方法,这个表示要查询的流程必须是执行完毕的流程,对于没有执行完毕的流程,这里不查询,查完之后,按照流程最后的处理时间进行排序。

  • 遍历第一步的查询结果,从 HistoricProcessInstance 中提取出每一个流程的详细信息,并存入到集合中,并最终返回。

  • 这里涉及到两个历史数据查询,createHistoricProcessInstanceQuery 用来查询历史流程,而 createHistoricVariableInstanceQuery 则主要是用来查询流程变量的。

最后,前端通过表格展示这个数据即可:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <!-- Import style -->
    <link rel="stylesheet" href="//unpkg.com/element-plus/dist/index.css" rel="external nofollow"  rel="external nofollow"  rel="external nofollow" />
    <script src="https://unpkg.com/vue@3"></script>
    <!-- Import component library -->
    <script src="//unpkg.com/element-plus"></script>
</head>
<body>
<div id="app">
    <div >
        <el-input v-model="name"  placeholder="请输入用户名"></el-input>
        <el-button type="primary" @click="search">查询</el-button>
    </div>

    <div>
        <el-table border strip :data="historyInfos">
            <el-table-column prop="name" label="姓名"></el-table-column>
            <el-table-column prop="startTime" label="提交时间"></el-table-column>
            <el-table-column prop="endTime" label="审批时间"></el-table-column>
            <el-table-column prop="reason" label="事由"></el-table-column>
            <el-table-column prop="days" label="天数"></el-table-column>
            <el-table-column label="状态">
                <template #default="scope">
                    <el-tag type="success" v-if="scope.row.status">已通过</el-tag>
                    <el-tag type="danger" v-else>已拒绝</el-tag>
                </template>
            </el-table-column>
        </el-table>
    </div>
</div>
<script>
    Vue.createApp(
        {
            data() {
                return {
                    historyInfos: [],
                    name: &#39;zhangsan&#39;
                }
            },
            methods: {
                search() {
                    let _this = this;
                    axios.get(&#39;/search?name=&#39; + this.name)
                        .then(function (response) {
                            if (response.data.status == 200) {
                                _this.historyInfos=response.data.data;
                            } else {
                                _this.$message.error(response.data.msg);
                            }
                        })
                        .catch(function (error) {
                            console.log(error);
                        });
                }
        }
    ).use(ElementPlus).mount(&#39;#app&#39;)
</script>
</body>
</html>
登入後複製

这个都是一些常规操作,我就不多说了,最终展示效果如下:

怎麼使用SpringBoot+Vue+Flowable模擬實作請假審批流程

#

以上是怎麼使用SpringBoot+Vue+Flowable模擬實作請假審批流程的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:yisu.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板