应用开发平台集成工作流系列之14——流程表单实现示例

news2024/12/24 3:43:02

背景

流程审批,通常对应一个业务表单。这个表单一般有两种实现模式,一是不同的环节对应不同表单,二是做一张大表单,分为不同的区域,从业务角度考虑,前者通常对应复杂业务处理;后面一种则更常见和更常用,更友好和实用一些。

Camunda只是工作流引擎,负责流程的流转。Camunda自身实际也附带了一个表单功能,但是,实在简陋到没法用的地步,感兴趣的可以去了解下,我保证你不会有在项目或系统中使用的想法。

基于上述原因,流程表单需要自行设计与实现。从技术角度而言,流程表单本质上依旧是一个业务实体,仍旧可以使用平台的低代码平台进行配置生成,只不过相比普通的表单多了一些工作流相关的属性和特色。

以常见的请假流程为例,我们通过平台的低代码配置功能,先实现请假申请单的基本功能。

业务流程模块

新增模块businessflow,用于存放集中存放具体的业务流程,如请假申请、设备申请、资产报废等

流程单据模型

实体模型之前预设了一个业务模型,将实体公共属性进行存放,包括标识、创建人、创建时间、修改人,修改时间、版本号、逻辑删除标识,所有实体都继承该模型。

借本次流程实现时机,进行了重构优化,提取出来一个只有标识id的标识模式,由业务模型继承。

对于流程相关的表单实体,如请假申请,同样有大量公共属性,因此设计了一个流程单据的公共实体模型来承载。
image.png
该模型继承自业务模型,从而拥有了业务模型的标识,创建人、创建时间、修改人,修改时间、版本号、逻辑删除标识属性,自身配置如下与流程表单相关的公共属性,如下:
image.png

请假申请实例

配置实体

使用平台的低代码配置功能,在业务流程模块businessflow下新建请假申请实体Leave,继承流程表单模型。
image.png
该实体继承关系如下:

生成库表

通过平台“生成库表”功能,平台自动处理实体模型继承关系,从当前模型递归找到最顶级,获取所有属性,转换成库表字段后生成库表,清单如下:
image.png

运行效果

使用平台的低代码配置功能,配置列表、新增、修改、查看视图,生成代码,编译,实现请假申请表单的基本的增删改查操作。
image.png
image.png

结合流程调整

前面说过,流程表单虽然本质上是实体,但会多一些跟工作流结合衍生出来的元素。

调整表单

调整UI实现,对表单进行分区域显示,兼顾用户体验和表单权限细粒度控制。
image.png
源码如下:

<template>
  <div>
    <basic-info :entity-data="entityData" />
    <el-card>
      <template #header>
        <span>申请信息</span>
      </template>

      <el-form
        ref="form"
        :model="entityData"
        :rules="rules"
        label-width="80px"
        label-position="right"
        :disabled="permissionConfigData.applyArea == 'READONLY'"
      >
        <el-row>
          <el-col :span="12">
            <el-form-item label="开始时间" prop="startTime">
              <el-date-picker
                v-model="entityData.startTime"
                :value-format="$dateFormatter.getDatetimeFormat('SECOND')"
                :type="$dateFormatter.getDatetimeType('SECOND')"
                align="right"
                unlink-panels
                class="form-item"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="结束时间" prop="endTime">
              <el-date-picker
                v-model="entityData.endTime"
                :value-format="$dateFormatter.getDatetimeFormat('SECOND')"
                :type="$dateFormatter.getDatetimeType('SECOND')"
                align="right"
                unlink-panels
                class="form-item"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="12">
            <el-form-item label="总计天数" prop="total">
              <el-input v-model="entityData.total" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="请假类型" prop="leaveType">
              <dictionary-select v-model="entityData.leaveType" code="LeaveType" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="原因" prop="reason">
              <el-input v-model="entityData.reason" type="textarea" :rows="3" />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
    </el-card>

    <el-card v-show="permissionConfigData.organizationApproval != 'INVISIBLE'">
      <template #header>
        <span>部门审批</span>
      </template>

      <el-form
        ref="form"
        :model="entityData"
        :rules="rules"
        label-width="80px"
        label-position="right"
        :disabled="permissionConfigData.organizationApproval == 'READONLY'"
      >
        <el-row>
          <el-col :span="12">
            <el-form-item label="审批人" prop="organizationApprovalName">
              <el-input v-model="entityData.organizationApprovalName" :readonly="readonly" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="审批时间" prop="organizationApprovalTime">
              <el-input v-model="entityData.organizationApprovalTime" :readonly="readonly" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="审批意见" prop="organizationApprovalAdvice">
              <el-input
                v-model="entityData.organizationApprovalAdvice"
                :readonly="readonly"
                type="textarea"
                :rows="3"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
    </el-card>

    <el-card v-show="permissionConfigData.hrApproval != 'INVISIBLE'">
      <template #header>
        <span>人事审批</span>
      </template>
      <el-form
        ref="form"
        :model="entityData"
        :rules="rules"
        label-width="80px"
        label-position="right"
        :disabled="permissionConfigData.hrApproval == 'READONLY'"
      >
        <el-row>
          <el-col :span="12">
            <el-form-item label="审批人" prop="hrApprovalName">
              <el-input v-model="entityData.hrApprovalName" :readonly="readonly" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="审批时间" prop="hrApprovalTime">
              <el-input v-model="entityData.hrApprovalTime" :readonly="readonly" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="审批意见" prop="hrApprovalAdvice">
              <el-input
                v-model="entityData.hrApprovalAdvice"
                :readonly="readonly"
                type="textarea"
                :rows="3"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
    </el-card>
  </div>
</template>

<script>
import { flowMixin } from '@/mixin/flowMixin'
const MODULE_CODE = 'businessflow'
const ENTITY_TYPE = 'leave'
export default {
  name: ENTITY_TYPE,
  mixins: [flowMixin],
  data() {
    return {
      entityType: ENTITY_TYPE,
      moduleCode: MODULE_CODE,
      // eslint-disable-next-line no-eval
      api: eval('this.$api.' + MODULE_CODE + '.' + ENTITY_TYPE),
      pageCode: MODULE_CODE + ':' + ENTITY_TYPE + ':',
      entityData: {},
      rules: {
        startTime: [{ required: true, message: '【开始时间】不能为空', trigger: 'blur' }],
        endTime: [{ required: true, message: '【结束时间】不能为空', trigger: 'blur' }],
        total: [{ required: true, message: '【总计天数】不能为空', trigger: 'blur' }],
        leaveType: [{ required: true, message: '【请假类型】不能为空', trigger: 'blur' }],
        reason: [{ required: true, message: '【原因】不能为空', trigger: 'blur' }]
      }
    }
  },
  methods: {}
}
</script>

<style scoped></style>

封装基本信息组件

对于工作流单据而言,存在共性,如单据编号、发起人、发起时间、发起部门等信息,在这里封装了一个基本信息的组件,界面如下:
image.png
源码如下:

<template>
  <el-card class="box-card">
    <template #header>
      <span>基本信息</span>
    </template>
    <el-form ref="form" :model="entityData" label-width="80px" label-position="right">
      <el-row>
        <el-col :span="12">
          <el-form-item label="单据编号">{{ entityData.billNo }}</el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="发起时间">{{
            $dateFormatter.formatUTCTime(entityData.initiateTime)
          }}</el-form-item>
        </el-col>
      </el-row>

      <el-row>
        <el-col :span="12">
          <el-form-item label="发起人">{{ entityData.initiateUserName }}</el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="联系方式">{{ entityData.initiateUserPhone }}</el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="24">
          <el-form-item label="发起部门">{{
            entityData.initiateOrganizationFullName
          }}</el-form-item>
        </el-col>
      </el-row>
    </el-form>
  </el-card>
</template>
<script>
export default {
  props: {
    entityData: {
      type: Object,
      required: false,
      default() {
        return {}
      }
    }
  },
  data() {
    return {}
  }
}
</script>

<style scoped>

</style>

处理工作流相关工作

流程表单实体新增的时候,除了需要将自身数据持久化外,还需要进行工作流相关操作。
结合平台的运行机制,只需要覆写afterAdd方法即可,包括验证流程启动权限、设置流程启动处理人、设置流程变量、启动流程、更新流程表单的与工作流相关的属性(流程类型、流程实例标识、流程状态等)

@Override
protected void afterAdd(Leave entity) {

    String userId=UserUtil.getId();
    String code= entity.getClass().getSimpleName();
    //验证流程启动权限
    flowTemplateService.checkProcessStartPermission(code);

    //设置流程启动处理人,缺失此句会导致act_hi_procinst表的START_USER_ID_字段,即流程启动人数据为空
    identityService.setAuthenticatedUserId(userId);

    // 首环节处理人默认为启动人
    Map<String,Object> instanceParams=new HashMap<>(5);
    instanceParams.put(WorkFlowConstant.INSTANCE_FIRST_STEP_HANDLER,userId);

    //设置请假天数
    instanceParams.put("total",entity.getTotal());


    WorkflowTemplate workflowTemplate=flowTemplateService.getByCode(code);
    //启动流程
    ProcessInstance processInstance =runtimeService.startProcessInstanceById(workflowTemplate.getProcessDefinitionId(),entity.getBillNo(),instanceParams);

    //更新流程相关字段
    //流程类型
    entity.setFlowTypeName(workflowTemplate.getName());
    //流程实例标识
    entity.setFlowInstanceId(processInstance.getProcessInstanceId());
    //流程状态
    entity.setFlowStatus(WorkflowInstanceStatusEnum.ACTIVE.name());
    //发起时间
    entity.setInitiateTime(LocalDateTime.now());

    //保存
    modify(entity);

}

开发平台资料

平台名称:一二三开发平台
简介: 企业级通用开发平台
设计资料:csdn专栏
开源地址:Gitee
开源协议:MIT
开源不易,欢迎收藏、点赞、评论。

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

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

相关文章

苹果手机如何设置软件提醒功能?苹果手机哪个软件有提醒功能?

作为苹果手机用户&#xff0c;我们都希望能够准时接收待办事项或日程提醒&#xff0c;高效完成各项任务和待办事项&#xff0c;例如无论是工作中的重要会议&#xff0c;还是生活中的约会安排&#xff0c;我们都不能够忘记。那么苹果手机如何设置软件的提醒功能呢&#xff1f;苹…

解决 Git:This is not a valid source path/URL

由于sourcetree 可以获取不同仓库的代码&#xff0c;而我的用户名密码比较杂乱&#xff0c;导致经常会修改密码&#xff0c;在新建拉去仓库代码的时候sourcetree 不会提示你密码错误&#xff0c;直接提示 This is not a valid source path/URL。 在已存在的代码仓库&#xff0…

QT UI控件汇总介绍

按钮 ToolButton 和pushbutton没什么区别&#xff0c;可以用来设置图标 设置展示策略 RadioButton 一般用Container可以将其框起来设置互斥域&#xff0c;推荐选用GroupBox 使用方法 qDebug()<<ui->radioButton_3->isChecked(); CheckBox 可以勾选三态 stat…

动态代理IP常见超时原因及解决方法

在使用动态代理IP时&#xff0c;常常会遇到代理超时的问题。网络环境的不稳定性以及代理IP的质量问题&#xff0c;都可能会引起代理超时。这种情况下&#xff0c;代理服务器无法在规定时间内响应我们的请求&#xff0c;导致请求失败。 使用动态代理IP时&#xff0c;哪些原因会引…

SpringBoot和Hibernate——如何提高数据库性能

摘要&#xff1a;本文由葡萄城技术团队发布。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 前言 在软件开发领域&#xff0c;性能是重中之重。无论您是构建小型 Web 应用程序还是大型企业系统…

MySQL详解 七:数据库高级语句(视图表、存储过程)

文章目录 1. create view ---- (视图表)1.1 视图的简单介绍1.2 基本语法1.2.1 创建视图表1.2.2 查看视图表1.2.3 删除视图表1.2.4 修改视图表 1.3 通过视图表得出无交集 2. case语句3. 空值&#xff08;null&#xff09; 和 无值&#xff08; &#xff09; 的区别4. 正则表达式…

大型语言模型:DistilBERT — 更小、更快、更便宜、更轻

一、介绍 近年来&#xff0c;大型语言模型的演进速度飞速发展。BERT成为最流行和最有效的模型之一&#xff0c;可以高精度地解决各种NLP任务。在BERT之后&#xff0c;一组其他模型随后出现在现场&#xff0c;也展示了出色的结果。 很容易观察到的明显趋势是&#xff0c;随着时间…

Vulnhub系列靶机-The Planets Earth

文章目录 Vulnhub系列靶机-The Planets: Earth1. 信息收集1.1 主机扫描1.2 端口扫描1.3 目录爆破 2. 漏洞探测2.1 XOR解密2.2 解码 3. 漏洞利用3.1 反弹Shell 4. 权限提升4.1 NC文件传输 Netcat&#xff08;nc&#xff09;文件传输 Vulnhub系列靶机-The Planets: Earth 1. 信息…

工矿企业电力运维云平台:提升效率与降低成本的关键

针对工矿企业用户&#xff0c;聚焦供配电领系统“安全监控、能耗管理、智能运维” 的三大需求&#xff0c;研发推出了“电易云”--智慧电力物联网&#xff0c;为工矿企业用户提供智慧电力数字化解决方案及数据服务&#xff0c;实现供配电系统的数字化、云端化、智能化、绿色化&…

笔记36:CNN的多通道卷积到底是什么样的

总结&#xff1a; &#xff08;1&#xff09;输入卷积层的feature_map的通道数&#xff0c;就是该卷积层每个卷积核所含有的通道数 &#xff08;2&#xff09;输出卷积层的feature_map的通道数&#xff0c;就是该卷积层所含有的卷积核的个数 a a a a 解释&#xff1a;【…

【数据结构C/C++】稀疏矩阵的压缩

文章目录 什么是稀疏矩阵&#xff1f;使用C语实现对稀疏矩阵的压缩408考研各数据结构C/C代码&#xff08;Continually updating&#xff09; 什么是稀疏矩阵&#xff1f; 稀疏矩阵&#xff08;Sparse Matrix&#xff09;是一种矩阵&#xff0c;其中大多数元素都是零。与稠密矩…

Floorplanning with Graph Attention

Floorplanning with Graph Attention DAC ’22 目录 Floorplanning with Graph Attention摘要1.简介2.相关工作3.问题公式化4. FLORA的方法4.1 解决方案概述4.2 C-谱聚类算法 4.3 基于GAT的模型4.4 合成训练数据集生成 摘要 布图规划一直是一个关键的物理设计任务&#xff0…

mac(M1)安装anaconda3

首先下载 然后正常安装即可&#xff0c;之所以我现在测试了anaconda,因为我发现miniconda后&#xff0c;jupyter notebook的安装就出现问题&#xff0c;所以就直接卸载miniconda&#xff0c;而直接安装anaconda了 (base) yxkbogon ~ % pip list Package …

算法通过村第十三关-术数|白银笔记|术数高频问题

文章目录 前言数组实现加法专题数组实现整数加法字符串加法二进制加法 幂运算专题求2的次幂求3的次幂求4的次幂 总结 前言 提示&#xff1a;人心本易趋死寂&#xff0c;苦难之后&#xff0c;焕然重建&#xff0c;激荡一阵&#xff0c;又趋麻木。 --苏枕书《有鹿来》 我们继续看…

连续子数组的最大和

这其实用到的是一个dp的动态规划数组来描写的。 用两个变量就能解决了&#xff0c;一个是max(记录前i个数中子数组的最大的和), 一个是sum是记录前i个数组的和最大值和自己去比较&#xff0c;就是前i-1个和是8&#xff0c;自己是-2&#xff0c; 8 - 2 > -2&#xff0c;所以…

Qt内置的图标与图标字体库加载应用实例(内置图标与加载外置的图标字体库)

一、前言 当涉及到创建应用程序的图形用户界面(GUI)时,图标是不可或缺的一部分。Qt作为一个流行的跨平台开发框架,不仅提供了丰富的UI组件和功能,还内置了许多精美的图标供开发者使用。这些内置图标提供了一种简便的方式,使开发者可以在应用程序中轻松地添加各种图标,提…

科技云报道:联络中心效能与体验齐飞,容联云AICC是如何做到的?

科技云报道原创。 AI与大模型为千行万业带来的进化与改造&#xff0c;远比想象来得更加猛烈。作为数字化升级改造的核心场景之一&#xff0c;联络中心在AI与大模型加持下&#xff0c;正在从基础云通讯迈入智能化的3.0时代。 身处行业智能化浪潮之中&#xff0c;容联云AICC作为…

【Linux】冯诺依曼体系结构初识操作系统

文章目录 1. 冯诺依曼体系结构2. 初识操作系统2.1 操作系统是什么&#xff1f;2. 为什么要有操作系统3. 操作系统是怎么管理的4. 系统调用&#xff08;System Call&#xff09; 1. 冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服…

qml入门

window import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.5Window { //root控件&#xff0c;父窗口是主界面width: 640height: 480visible: true//相对于父控件的偏移量x: 100y:100minimumWidth: 400 //最小宽度minimumHeight: 300 //最小高度ma…

浏览器安装vue调试工具

下载扩展程序文件 下载链接&#xff1a;链接: 下载连接网盘地址&#xff0c; 提取码: 0u46&#xff0c;里面有两个crx,一个适用于vue2&#xff0c;一个适用于vue3&#xff0c;可根据vue版本选择不同的调试工具 crx安装扩展程序不成功&#xff0c;将文件改为rar文件然后解压 安装…