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

本文详细描述了如何设计用于车辆申请的工作流表单,包括创建业务表结构,使用process_instance_id关联流程实例,以及在Vue应用中实现create和detail页面的开发。同时涉及代码生成、监听流程结果和权限管理等内容。
摘要由CSDN通过智能技术生成

需求描述:

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

    这里表中的每一条记录,都将通过流程实例编号(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:如果单从角色来控制显示业务表单还不够,因为有可能这个角色在多个节点都需要审批,但每一个节点所看到的内容又不一致,所以,我们根据角色以及当前节点两个条件来锁定,是否显示表单动态的那一部分。

### 回答1: 在 ant-design-vue3.0 中,我们可以通过自定义表单校验错误显示来实现我们想要的效果。首先,我们需要在表单项中定义校验规则,并给每个表单项添加一个唯一的 `name` 属性。例如: ```html <a-form-item label="用户名" name="username" rules={[{ required: true, message: '请输入用户名' }]}> <a-input v-model:value="formData.username"></a-input> </a-form-item> ``` 接下来,我们可以在表单的 `validateTrigger` 属性中指定触发校验的时机。默认情况下,它的值为 `change` 和 `blur`,即在表单项失去焦点或值改变时触发校验。我们可以将它修改为 `submit`,表示只有在提交表单的时候才触发校验。 ```html <a-form ref="formRef" :model="formData" :validateTrigger="'submit'"> <!-- 表单项 --> </a-form> ``` 当表单校验失败时,默认情况下会在表单项下方显示错误信息。我们可以通过 `validateStatus` 和 `help` 属性来自定义错误显示。`validateStatus` 可以接受三种值:`'success'` 表示校验通过,`'error'` 表示校验失败,`'validating'` 表示校验中。我们可以根据需要设置不同的显示效果。 ```html <a-form-item label="用户名" name="username" :validateStatus="validateStatus('username')" :help="errorMessages['username']"> <a-input v-model:value="formData.username"></a-input> </a-form-item> ``` 其中,`validateStatus` 是一个方法,用于根据校验结果返回对应的状态值。`errorMessages` 是一个对象,保存所有的错误信息。 ```javascript data() { return { formData: { username: '' }, errorMessages: {} // 错误信息 } }, methods: { validateStatus(name) { if (this.errorMessages[name]) { return 'error'; } return ''; } } ``` 最后,我们需要在提交表单时手动触发校验,并捕获校验结果,并将错误信息保存到 `errorMessages` 中。 ```javascript methods: { submitForm() { this.$refs.formRef.validate((valid) => { if (valid) { // 校验通过,提交表单 } else { // 校验失败,显示错误信息 this.errorMessages = this.$refs.formRef.getFieldsError().reduce((acc, {name, errors}) => { if (errors.length > 0) { acc[name] = errors[0].message; } return acc; }, {}); } }); } } ``` 通过以上步骤,我们就可以实现自定义表单校验错误显示。在校验失败时,表单项下方会显示我们设置的错误信息,而不是默认的错误提示。 ### 回答2: 在 ant-design-vue 3.0 中,可以通过使用自定义校验规则和错误信息来自定义表单校验错误显示。 首先,创建一个自定义的校验规则函数。校验规则函数需要接受两个参数:rule 和 value。rule 代表当前校验规则的配置,而 value 代表当前表单字段的值。在校验函数中,可以根据具体的校验逻辑进行判断并返回一个校验结果。校验结果可以返回一个布尔值或一个 Promise 对象。 接着,在需要进行校验的表单控件中,可以使用 rules 属性来指定校验规则函数或校验规则数组。如果是多个校验规则,可以将它们放入数组中。可以通过 message 属性来设置错误提示信息。 例如,我们需要自定义一个校验规则,判断输入的字符串是否包含特定字符。可以创建一个函数 validateSpecialChar,接受 rule 和 value 作为参数,根据具体需求进行判断,并返回一个校验结果。 ```javascript const validateSpecialChar = (rule, value) => { if (/[@#$%^&*]/.test(value)) { return Promise.reject(new Error('不能包含特殊字符')); } return Promise.resolve(); }; ``` 然后,在表单控件中,可以指定 rules 属性来使用这个校验规则,并设置错误提示信息。 ```html <a-form-item label="用户名" required> <a-input v-decorator="['username', { rules: [{ validator: validateSpecialChar, message: '不能包含特殊字符' }] }]"></a-input> </a-form-item> ``` 当用户输入的字符串中包含特殊字符时,会显示错误提示信息"不能包含特殊字符"。如果输入合法,则不显示错误提示信息。 通过这种方式,我们可以方便地自定义表单校验错误的显示方式。根据具体需求,可以自定义不同的校验规则函数和错误提示信息来满足不同的校验需求。 ### 回答3: 在Ant Design Vue 3.0中,默认的表单校验错误显示是使用Tooltip组件,在表单控件的右上方显示错误提示信息。如果需要自定义表单校验错误显示,可以通过以下步骤实现: 1. 创建自定义的错误提示组件:可以使用Message组件或者其他任意组件作为错误提示的样式,根据具体需求进行定制。 ```vue <template> <div class="custom-error"> <span class="error-message">{{ errorMessage }}</span> </div> </template> <script> export default { name: 'CustomError', props: { errorMessage: { type: String, required: true } } } </script> <style> .custom-error { /* 自定义样式 */ } .error-message { /* 自定义样式 */ } </style> ``` 2. 在表单组件中使用自定义的错误提示组件。在需要进行校验的表单控件外部包裹一层`FormItem`组件,并通过`rules`属性设置校验规则,在`validateStatus`属性中根据校验结果设置表单控件的状态,如果校验失败,将错误信息通过`error-message`属性传给自定义错误提示组件。 ```vue <template> <a-form :form="form"> <a-form-item label="用户名" :validateStatus="validateStatus('username')" :help="helpMessage('username')"> <a-input v-model="username" /> </a-form-item> </a-form> </template> <script> import CustomError from '@/components/CustomError.vue'; export default { name: 'MyForm', components: { CustomError }, data() { return { form: this.$form.createForm(this), username: '' }; }, methods: { validateStatus(fieldName) { const { isFieldTouched, getFieldError } = this.form; return isFieldTouched(fieldName) && getFieldError(fieldName) ? 'error' : ''; }, helpMessage(fieldName) { const { isFieldTouched, getFieldError } = this.form; return isFieldTouched(fieldName) && getFieldError(fieldName) ? getFieldError(fieldName)[0] : ''; } } } </script> ``` 3. 构建校验规则:根据具体需求,使用`rules`属性定义校验规则,可以使用Ant Design Vue提供的校验规则或者自定义校验规则。 ```vue <script> export default { // ... methods: { validateStatus(fieldName) { // ... }, helpMessage(fieldName) { // ... } }, created() { this.form.setFields({ username: { rules: [ { required: true, message: '请输入用户名' }, { pattern: /^[a-zA-Z0-9_]{5,16}$/, message: '用户名由字母、数字或下划线组成,长度为5-16位' } ] } }); } } </script> ``` 通过以上步骤,我们可以实现自定义表单校验错误显示。可以根据自己的需求,使用不同的组件和样式来进行自定义
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿土不土

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值