芋道--如何自定义业务表单,配置对应的工作流程(详细步骤)

news2024/11/24 22:57:20

需求描述:

    芋道的动态表单就不再介绍了,相对来讲比较简单,跟着官网文档就可以实现,本文将详细的介绍如何新建独立的业务表记录申请的信息,并设计对应的工作流。

    这里表中的每一条记录,都将通过流程实例编号(process_instance_id )和对应的流程实例进行关联。

    而每一个流程实例也都会通过业务键(BUSINESS_KEY_)指向对应的业务记录

这里假设创建一个车辆申请工作流,来举例:

1.设计业务表:

CREATE TABLE `bpm_oa_apply` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '车辆申请表单主键',
  `user_id` bigint NOT NULL COMMENT '申请人的用户编号',
  `type` tinyint NOT NULL COMMENT '车辆类型',
  `reason` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '申请原因',
  `start_time` datetime NOT NULL COMMENT '开始时间',
  `end_time` datetime NOT NULL COMMENT '结束时间',
  `result` tinyint NOT NULL COMMENT '申请结果',
  `process_instance_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '流程实例的编号',
  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
  `tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='OA 车辆申请表';

  • process_instance_id 字段,关联流程引擎的流程实例对应的 ACT_HI_PROCINST 表的 PROC_INST_ID_ 字段
  • result 字段,申请结果,需要通过 Listener 监听回调结果

2.代码生成

利用系统中的代码生成,可以快速的生成对应的结构,将生成的代码粘贴到对应的模块,具体可以看芋道官网的开发文档中 代码生成(单表)说明文档。代码生成【单表】(新增功能) | ruoyi-vue-pro 开发指南 (iocoder.cn)icon-default.png?t=N7T8https://doc.iocoder.cn/new-feature/#_2-1-%E5%AF%BC%E5%85%A5%E8%A1%A8

生成的代码都导入完成后,我们创建审批流要用到的create和detail页面,这里我们仿照给的请假申请示例:

3.创建create和detail界面,具体如下:

  create.vue:该页面用于填写业务表的信息,提交流程

<template>
  <div class="app-container">
      <el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" label-width="100px">

                    <el-form-item label="车辆类型" prop="type">
                      <el-select v-model="formData.type" placeholder="请选择车辆类型">
                            <el-option v-for="dict in typeDictData" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/>
                      </el-select>
                    </el-form-item>
                    <el-form-item label="申请原因" prop="reason">
                      <el-input v-model="formData.reason" placeholder="请输入申请原因" />
                    </el-form-item>
                    <el-form-item label="开始时间" prop="startTime">
                      <el-date-picker clearable v-model="formData.startTime" type="date" value-format="timestamp" placeholder="选择开始时间" />
                    </el-form-item>
                    <el-form-item label="结束时间" prop="endTime">
                      <el-date-picker clearable v-model="formData.endTime" type="date" value-format="timestamp" placeholder="选择结束时间" />
                    </el-form-item>
                    <el-form-item>
                      <el-button type="primary" @click="submitForm">提 交</el-button>
                    </el-form-item>
      </el-form>
  </div>
</template>

<script>
  import { getDictDatas, DICT_TYPE } from '@/utils/dict'
  import * as OaApplyApi from '@/api/bpm/oaapply';
      export default {
    name: "OaApplyForm",
    components: {
    },
    data() {
      return {
        formData: {
            type: undefined,
            reason: undefined,
            startTime: undefined,
            endTime: undefined,
        },
        typeDictData: getDictDatas(DICT_TYPE.BPM_OA_LEAVE_TYPE),
                        };
    },
    methods: {
      /** 提交按钮 */
       submitForm() {
        this.$refs["formRef"].validate(valid => {
        if (!valid) {
          return;
        }
        const data = this.formData;
         OaApplyApi.createOaApply(data);
          this.$modal.msgSuccess("新增成功");
          this.dialogVisible = false;
          this.$emit('success');

      });
      },
    }
  };
</script>

  detai.vue: 该页面用于查看业务表的信息

<template>
  <div class="app-container">
    <el-form ref="formRef" :model="formData" label-width="100px">

      <el-form-item label="车辆类型" prop="type">
        <dict-tag :type="DICT_TYPE.BPM_OA_CAR_TYPE" :value="formData.type"/>
      </el-form-item>
      <el-form-item label="申请原因" prop="reason">
        {{ formData.reason}}
      </el-form-item>
      <el-form-item label="开始时间" prop="startTime">
        {{parseTime(formData.startTime, '{y}-{m}-{d}')}}
      </el-form-item>
      <el-form-item label="结束时间" prop="endTime">
        {{parseTime(formData.endTime, '{y}-{m}-{d}')}}
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
import {getOaApply} from '@/api/bpm/apply';
import {getDictDatas, DICT_TYPE} from '@/utils/dict'
export default {
  name: "BpmOALeaveDetail",
  components: {
  },
  props: {
    id: {
      type: [String, Number],
      default: undefined
    },
  },
  data() {
    return {
      carId: undefined, // 申请编号
      // 表单参数
      form: {
        startTime: undefined,
        endTime: undefined,
        type: undefined,
        reason: undefined,
      },

      typeDictData: getDictDatas(DICT_TYPE.BPM_OA_CAR_TYPE),
    };
  },
  created() {
    this.carId = this.id || this.$route.query.id;
    if (!this.carId) {
      this.$message.error('未传递 id 参数,无法查看 OA 信息');
      return;
    }
    this.getDetail();
  },
  methods: {
    /** 获得请假信息 */
    getDetail() {
      getOaApply(this.carId).then(response => {
        this.formData = response.data;
      });
    },
  }
};
</script>

3.配置路由

   在 router/index.js 中定义 create.vue 和 detail.vue 的路由,配置如下:

 {
    path: '/bpm',
    component: Layout,
    hidden: true,
    redirect: 'noredirect',
    children: [{
        path: 'oa/leave/create',
        component: (resolve) => require(['@/views/bpm/oa/leave/create'], resolve),
        name: 'BpmOALeaveCreate',
        meta: {title: '发起 OA 请假', icon: 'form', activeMenu: '/bpm/oa/leave'}
      }, {
        path: 'oa/leave/detail',
        component: (resolve) => require(['@/views/bpm/oa/leave/detail'], resolve),
        name: 'BpmOALeaveDetail',
        meta: {title: '查看 OA 请假', icon: 'view', activeMenu: '/bpm/oa/leave'}
      },
      {
        path: 'oa/oaapply/create',
        component: (resolve) => require(['@/views/bpm/oa/oaapply/create'], resolve),
        name: 'BpmOAApplyCreate',
        meta: {title: '发起 OA 申请', icon: 'form', activeMenu: '/bpm/oa/oaapply'}
      }, {
        path: 'oa/oaapply/detail',
        component: (resolve) => require(['@/views/bpm/oa/oaapply/detail'], resolve),
        name: 'BpmOAApplyDetail',
        meta: {title: '查看 OA 申请', icon: 'view', activeMenu: '/bpm/oa/oaapply'}
      }
    ]
  },

4.修改权限

   由于新增、查看OA申请,都在侧边的菜单栏当中,因此,审批人的角色中,应当配置对应的权限,如图所示

   这样在审批的过程中就可以看到对应的表单信息了。

5.常见问题

1.如何区分这个流程是什么状态(处理中、同意、拒绝)?

    首先,我们需要监听每个流程进展到哪一步了,因此这里实现流程引擎定义的BpmProcessInstanceResultEventListener监听器,在流程结束时,会回调通知最终的结果是否通过。

getProcessDefinitionKey()方法实际上就是返回业务key(在service对应的实现类中设置的)

而监听器捕捉到时间以后就会触发event方法,这个时候会更新业务表中的result字段,将对应的结果更新到业务表中。

2.枚举是如何获取,并赋值的呢?

    流程状态的枚举类都是定义在BpmProcessInstanceResultEnum中,通过监听工作流中的处理结果,然后将其赋值到业务对象的result中。

3.审批流程每经过一个节点,处理完成后是如何触发短信通知的呢?

    具体可以看对应的专栏芋道框架----(业务表单工作流)短信通知流程分析-CSDN博客

4.BpmOACarResultListener监听器是什么时候触发的呢?需要注意的点是什么?

    首先,我们在创建监听器的时候记得要加注解@Component,否则不起作用,该监听器继承了BpmProcessInstanceResultEventListener,所以当流程示例的结果发生变更了就会触发该监听器。

6.如果不同的审批节点,需要看到业务表单中的不同模块,甚至想要补充业务表单,又该如何设计?

 如果不同的审批节点,想看到不同的模块,我们可以根据角色来区分,例如,

   这样,就只有有bpm:oa-car:name权限的角色才能够在detail页面中看到车辆名称,然后我们在将对应的权限赋给需要的角色即可。 

   想要补充表单,实际上就是在detail页面中将对应的内容写成可编辑即可,然后根据id去更新对应的业务表。 

这里,根据我的业务,判断了一下是否是当前审批的人是否是申请人,如果是的话,在判断是哪一个节点,因为 如果是起始提交表单的时候,我们依旧是不露出这个input的。

    tips:如果单从角色来控制显示业务表单还不够,因为有可能这个角色在多个节点都需要审批,但每一个节点所看到的内容又不一致,所以,我们根据角色以及当前节点两个条件来锁定,是否显示表单动态的那一部分。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1405738.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mysql-进阶篇

文章目录 存储引擎MySQL体系结构相关操作 存储引擎特点InnoDBInnoDB 逻辑存储结构 MyISAMMemory三个存储引擎之间的区别存储引擎的选择 索引1. 索引结构B-TreeB-Tree (多路平衡查找树)B-Tree演变过程 BTree与 B-Tree 的区别BTree演变过程 Hash 2.索引分类3.索引语法演示 4.SQL性…

946. 验证栈序列(力扣)

946. 验证栈序列 Problem: 946. 验证栈序列 文章目录 思路解题方法复杂度Code 思路 对栈的使用 解题方法 1.我们可以通过把pushed重新一个一个入我们自己创建的栈如果某次入栈碰到与poped第一个元素相同的那我们就对poped出栈处理(即跳过第一个元素);如此循环,直到我们的栈到最…

【C++记忆站】类和对象(二)

类和对象(二) 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在什么都不写时&#xff0c;编译器会自动生成以下6个默认成员函数。 默认成员函数&#xff1a;用户没有显式实现&#xff0c;编译器会生成的成员…

使用DTS实现TiDB到GaiaDB数据迁移

1 概览 本文主要介绍通过 DTS 数据迁移功能&#xff0c;结合消息服务 for Kafka 与 TiDB 数据库的 Pump、Drainer 组件&#xff0c;完成从TiDB迁移至百度智能云云原生数据库 GaiaDB。 消息服务 for Kafka&#xff1a;详细介绍参见&#xff1a;消息服务 for Kafka 产品介绍百度智…

Ubuntu Desktop 隐藏 / 显示文件和文件夹

Ubuntu Desktop 隐藏 / 显示文件和文件夹 1. GUI hot key2. Show hidden and backup filesReferences 1. GUI hot key Ctrl H: 隐藏 / 显示文件和文件夹 2. Show hidden and backup files Edit -> Preferences -> Views References [1] Yongqiang Cheng, https://yo…

AI大概不会很快抢走你的饭碗哦!

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

交互式AI百舸争流,声通科技要再次破题实现IPO?

仅隔半年&#xff0c;声通科技两次递表可以窥见其上市势在必行的决心。 事实也确实如此&#xff0c;由于对赌期限在即&#xff0c;声通科技上市迫在眉睫。 招股书显示&#xff0c;若声通科技未能于2024年12月31日之前完成合资格首次公开发售。那么声通科技及其创始人将赎回已…

Redis服务端优化(持久化配置、慢查询、命令及安全配置、内存配置)

文章目录 持久化配置慢查询命令及安全配置内存配置 持久化配置 慢查询 命令及安全配置 漏洞&#xff1a;Redis未授权访问配合SSH key文件利用分析-腾讯云开发者社区-腾讯云 (tencent.com) 漏洞出现的核心的原因有以下几点 Redis未设置密码利用了Redis的config set命令动态修…

Go 基本数据

第 2 章 基本数据类型 Go 的数值类型包括了不同大小的整数 、浮点数 、复数&#xff1b; 各种数值类型分别有自己的大小&#xff0c;对正负号支持也各不相同&#xff1b; 1. 整数&#xff08;OK&#xff09; 整数类型&#xff08;整型&#xff09;整数类型Go 语言同时支持 有…

VUE---插槽

一、插槽的作用&场景 1、在封装组件的时候&#xff0c;将可变的结构设计为插槽&#xff08;<slot></slot>&#xff09; 2、使用上述组件的时候&#xff0c;可以按需为插槽提供自定义的结构&#xff0c;以达到复用组件且高度自定的效果 二、基本语法 1、组件内…

关于网络安全 的 ARP欺骗 实验操作

实验设备&#xff1a; Windows server 2008 kali 1. vmware--上面菜单栏--虚拟机--设置--网络--NAT 模式 确定靶机与攻击机的连通性&#xff08;互相能 ping 通&#xff09; 靶机查看 arp 表&#xff08;arp -a&#xff09; 查看攻击机(kali)物理地址&#xff08;ip addr&…

MIT 6s081 lab4.xv6进程调度

xv6进程调度 在xv6中&#xff0c;调度发生的两种情况&#xff1a; 时钟中断导致的进程切换&#xff08;也叫时间片轮转&#xff09;睡眠锁&#xff0c;当进程调用sleep时&#xff0c;发生cpu的调度 xv6进程相关概念 xv6用struct proc来描述进程 // Per-process state stru…

uvicorn日志清空问题以及uvicorn日志配置

uvicorn日志清空问题 1、配置&#xff1a; uvicorn starlette 2、现象描述&#xff1a; 当我使用uvicorn starlette进行Python web开发的时候&#xff0c;本来想把所有的日志都打印到一个文件里面&#xff0c;于是我写了一个启动脚本&#xff0c;所有的日志都输出到log.t…

最大流-Dinic算法,原理详解,四大优化,详细代码

文章目录 零、前言一、概念回顾(可略过)1.1流网络1.2流1.3最大流1.4残留网络1.5增广路径1.6流网络的割1.7最大流最小割定理1.7.1证明 1.8Ford-Fulkerson方法 二、Dinic算法2.1EK算法的可优化之处2.2Dinic算法的优化策略2.3Dinic算法原理2.3.1找增广路2.3.2更新剩余容量 2.4算法…

浏览器无网

目录 1.运行网络诊断&#xff0c;确认原因 原因A.远程计算机或设备将不接受连接(该设备或资源(Web 代理)未设置为接受端口“7890”上的连接 原因B.DNS服务器未响应 场景A.其他的浏览器可以打开网页&#xff0c;自带的Edge却不行 方法A&#xff1a;关闭代理 Google自带翻译…

<C++>STL->vector

vector的介绍 vector的使用文档 vector是一个可改变数组大小的序列容器vector和数组一样采取连续的空间存放数据&#xff0c;可以使用方括号访问vector的元素&#xff0c;和数组一样高效。但是vector的大小可以动态增长&#xff0c;而数组不行实际上vector内部使用一个动态分…

MySQL-SQL-DQL

DQL-介绍 DQL-语法 基本查询 1、查询多个字段 2、设置别名 3、去除重复记录 条件查询 1、语法 2、条件 聚合函数 1、介绍 2、常见的聚合函数 3、语法 分组查询 1、语法 2、where与having区别 排序查询 1、语法 2、排序方式 分页查询 1、语法 DQL-执行顺序

多维时序 | Matlab实现WOA-TCN-Multihead-Attention鲸鱼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测

多维时序 | Matlab实现WOA-TCN-Multihead-Attention鲸鱼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测 目录 多维时序 | Matlab实现WOA-TCN-Multihead-Attention鲸鱼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测效果一览基本介绍程序设计参考资料 效…

C++大学教程(第九版)6.29素数

题目 (素数)素数是只能被1和自已整除的整数。例如,235和7是素数而468和9不是素数 a)编写一个函数&#xff0c;确定一个数是否是素数。 b)在程序中使用这个函数&#xff0c;该程序确定和打印2 ~10000之间的所有素数。在确信已找到所有的素数之前&#xff0c;实际需测试这些数中…

五邑大学餐厅网络点餐系统设计与实现(包含完整源码详细开发过程)

博主介绍&#xff1a;✌专研于前后端领域优质创作者、本质互联网精神开源贡献答疑解惑、坚持优质作品共享、掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战&#xff0c;深受全网粉丝喜爱与支持✌有需要可以联系作者我哦&#xff01; &#x1f345;文末获…