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

背景

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

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
开源不易,欢迎收藏、点赞、评论。

最近更新

  1. TCP协议是安全的吗?

    2023-12-23 10:40:02       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-23 10:40:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-23 10:40:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-23 10:40:02       18 阅读

热门阅读

  1. 飞行路径预测:基于MATLAB的支持向量机

    2023-12-23 10:40:02       42 阅读
  2. Ubuntu 22.04 配置LLM大语言模型环境

    2023-12-23 10:40:02       31 阅读
  3. C#中的.NET与.NET Framework区别

    2023-12-23 10:40:02       39 阅读
  4. 2023最新Python全栈开发学习路线

    2023-12-23 10:40:02       40 阅读
  5. NPM的介绍和使用

    2023-12-23 10:40:02       40 阅读
  6. WPF StackPanel

    2023-12-23 10:40:02       37 阅读
  7. 零基础学C语言——函数

    2023-12-23 10:40:02       40 阅读