直接跳到内容

表单自定义控件

简介

表单控件用于表单设计,配置相应的前端组件使其对实体字段在表单中展示形式的定义。

项目已经准备了大部分常见的控件,如果想要新增自定义组件,或对内置组件进行改动时,就需要在前端项目中配置对应的内容。与自定义列表和自定义对话框不同的是,控件的配置为源码式,需要一定的代码基础。

img.png

作者将会从头到尾配置完整的自定义测试案例,以 element 中的输入框为原型进行扩展。(也支持 element 以外的其他框架或组件,安装相应的依赖引入即可,配置方法趋同)。

一、增加配置组件 (后端)

表单中的字段的类型有: 字符串、数字型、大文本、日期型。四种类型的数据对应了不同可选择的组件

img_1.png

具体的配置在后端枚举中。

src/main/java/com/dstz/biz/api/constant/BizColumnCtrlType.java

img_3.png

如图所示,输入框理应支持字符串和数字,配置完毕并重启服务后,即可在页面中看到此组件。

img_4.png

二、配置组件选项(前端)

本案例将输入框的最大长度和最小长度作为配置选项,前端配置目录 config 中,新建你要扩展的配置页面 customTestConfig.vue

agilebpm\package\avue-form-design\ab\config\customTestConfig.vue
js
<template>
  <!-- max和min为前端定义的新参数,将来后端会获取新增的两个参数,并生成到真正组件属性中 -->
  <el-form-item label="最大长度">
    <el-input v-model="data.max" placeholder="请输入最大长度限制" />
  </el-form-item>
  <el-form-item label="最小长度">
    <el-input v-model="data.min" placeholder="请输入最小长度限制" />
  </el-form-item>
</template>

<script lang="ts">
  export default {
    name: 'custom-test-config',
  }
</script>

<script lang="ts" setup>
  //data传参给后台生成真正组件的属性依据
  const props = defineProps({ data: { type: Object, required: true } })
  //将data内的属性转为响应式数据
  const { data } = toRefs(props)
</script>

然后在父级目录中的 index.js 中,注册刚才写的 config 即可

agilebpm\package\avue-form-design\ab\index.js
js
//中间还有注册的很多其他组件,此处省略
import CustomTestConfig from './config/customTestConfig.vue'
const components = [
    ...,
    CustomTestConfig,
]

同时需要告诉表单,碰到哪些组件使用此配置页,配置文件地址如下

js
agilebpm\package\avue-form-design\components\config\widgetColumn.vue

并找到computed位置中增加如下逻辑即可

if ('customTest'==this.data.type) {
      return 'custom-test-config'
}

img_2.png

配置完成后,即可在页面中看到效果。

img_6.png

说明: 上面只读,校验,占位符之类的为通用配置,无需自己添加,需要关闭可在下方路径中 v-if 里配置

agilebpm\package\avue-form-design\components\config\widgetColumn.vue

img_9.png

三、优化预览页展示(前端)

选项虽然出来了,但是预览页中同样需要展示出对应的样式。(由于默认展示为输入框,因此新增的自定义测试组件展示并未出问题,如果组件不是输入框类型的就需要配置预览效果)

img_7.png

在 item 目录内新建组件展示页面 customTest.vue

agilebpm\package\avue-form-design\ab\item\customTest.vue
js
<template>
  <!-- disabled为item中的只读开关。 placeholder为item中的占位符,这么配置为了配置联动实时预览展示效果-->
  <el-input v-model="testStr" :disabled="item.readOnly"  :placeholder="item.placeholder" />
</template>

<!-- 导出该组件的名称 -->
<script lang="ts">
  export default {
    name: 'custom-test',
  }
</script>

<script lang="ts" setup>
  //item为右侧配置项目的对象,如果想要预览跟配置联动效果
  (打开只读按钮联动预览输入框置灰之类的,需要在此引入,并在上方组件中配合使用)
  const props = defineProps({ item: { type: Object, required: true } })
  //将item内的属性转为响应式数据
  const { item } = toRefs(props)
  //仅作测试使用
  const testStr = ref("我是自定义组件的默认值")
</script>

跟配置项同理,需要在父级目录中的 index.js 中,注册刚才写的 customTest 即可

agilebpm\package\avue-form-design\ab\index.js
js
//中间还有注册的很多其他组件,此处省略
import CustomTest from './item/customTest.vue'
const components = [
    ...,
    CustomTest,
]

同样需要告诉表单,碰到哪些组件会预览此组件。

js
agilebpm\package\avue-form-design\components\widget\item.vue

在item文件中的methods方法中添加如下方法

  case 'customTest':
        return 'custom-test'

img_5.png

此时联动预览完成,刷新页面即可看到效果

img_8.png

但是保存或预览的时候会提示没有后端处理器,此时我们需要增加后端处理器

img_11.png

四、增加后端处理器(后端)

后端处理器是利用配置项(例:输入框最大和最小限制)的各种条件生成表单组件源码的一个 API,相当于一个连接配置项和表单组件的桥梁。 (上面两个组件一个是针对配置的,另一个是针对预览的,并非真正的表单组件,第五步才是制作表单组件。)

由于容器布局以及不同样式的表单模板在前端动态生成复杂度以及耦合性太高,因此所有针对组件的配置项将会传回后端,由后端生成整个表单, 然后在重新传回前端渲染。一方面为了解耦,另一方面利用后端模板静态化实现起来较为简单。

需要现在下方/parser/ctrl 目录里新建自己的处理器 CustomTestParser

src/main/java/com/dstz/biz/view/design/parser/ctrl/CustomTestParser.java
java
package com.dstz.biz.view.design.parser.ctrl;

import com.dstz.biz.api.constant.BizColumnCtrlType;
import com.dstz.biz.view.design.entity.DesignColumn;
import org.jsoup.nodes.Element;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class CustomTestParser extends AbstractCtrlParser {

    @Override
    public BizColumnCtrlType getCtrlType() {
        return BizColumnCtrlType.CUSTOM_TEST;
    }

    @Override
    public String parserCtrlHtml(DesignColumn column) {
        Map<String, Object> dataMap = column.getJson();
        Element customTest = createElement("ab-custom_test");
        handleVModel(customTest, column);
        handlePermission(customTest, column);
        handleReadOnly(customTest, column);
        handleRules(customTest, column);
        handlePlaceholder(customTest, column);
        attrParam("min", dataMap.get("min"), customTest);
        attrParam("max", dataMap.get("max"), customTest);
        return customTest.toString();
    }

}

img_10.png

其他操作已经实现好了,此时后端处理器制作完成。(基于模板生成整个表单的详细操作有需求的自行查阅源码即可)

虽然有了处理器保存表单已经不报错了,但是预览不出来效果,是因为表单组件还未制作。

五、制作表单组件(前端)

制作组件本质就是在原始组件中包一层外壳,并根据自己的配置,动态传入框架内的组件以实现效果。

在 ab-form\components 目录内创建文件夹 ab-custom-test,并在其中创建文件 abCustomTest

之所以组件名称前面加 ab,一方面为了跟之前配置的预览组件名称区分开,另一方面后端处理器 createElement 组件时名称带有 ab,为了跟组件导出名保持一致

agilebpm\package\ab-form\components\ab-custom-test\abCustomTest.vue

maxlength 和 minlength 为 element 原生属性,

js
<template>
  <el-input
    v-bind="$attrs"
    v-model="value"
    :desc="props.desc"
    :disabled="props.disabled"
    :maxlength="props.max"
    :minlength="props.min"
    :permission="props.permission"
    :rules="props.rules"
  />
</template>

<script lang="ts" setup>
  import { RuleItem } from 'async-validator'
  import { PropType } from 'vue'
  //仅作测试
  console.log('这是自定义组件运行了')
  const props = defineProps({
    modelValue: {
      type: [String, Number],
      default: '',
    },
    //是否禁用
    disabled: {
      type: Boolean,
      default: false,
    },
    //操作权限
    permission: {
      type: String,
      default: 'w',
    },
    rules: {
      type: Object as PropType<Array<RuleItem> | RuleItem>,
      default: [] as Array<RuleItem>,
    },
    desc: {
      type: String,
      required: true,
    },
    //自定义的扩展配置项 最小输入长度
    min: {
      type: Number,
      default: null,
    },
    //自定义的扩展配置项 最大输入长度
    max: {
      type: Number,
      default: null,
    },
  })
  //双向绑定
  const emit = defineEmits(['update:modelValue', 'input'])
  const value = useVModel(props, 'modelValue', emit)
</script>

此时一个简单的组件就做好了,但其中的 permission,以及 rules 并未生效,需要更为复杂的配置才行, (例如:实时监听值,看是否满足校验,校验失败让输入框变红。 只读下输入框置灰,和表单权限只读下只展示文本, 以及对权限的实时监控,并做出相应的显示等)不同组件实现的方式不太一样,更详细的请参考其他组件。

同时需要在父级目录下的 custFormBaseImports 中注册组件

agilebpm\package\ab-form\components\custFormBaseImports.ts
js
//其他组件和方法省略
import abCustomTest from './ab-custom-test/abCustomTest.vue'
...
export const CustFormBaseImportsUse = {
  components: {
    ...,
    abCustomTest,
  },
  ...
}

六、添加组件到简单表单设计器

完场上述操作步骤以后,如果需要把制作完成的表单组件添加到简单表单设计器只需要在 abFieldIndexDiy.js 中,添加控件配置就可以实现。

把控件配置json添加到abFieldIndexDiy.js的对应的分类目录下:

agilebpm/package/avue-form-design/ab/abFieldIndexDiy.js
{
    title: '高级控件',
    list: [
      {
        type: 'userSelect',//后端枚定义的控件key
        title: '用户选择',//控件名称
        label: '用户选择',//字段描述
        icon: 'icon-number',//控件图标
        span: 24,
        display: true,//是否展示
        maxlength: 50,//最大长度
        placeholder: `请选择用户`,//占位符
        //下面字段由ab定义
        defaultValue: '', //默认值
        rules: [], //校验
        fieldType: 'varchar', //字段类型
        readOnly: false, //是否只读
      },
    ],
}    

展示效果

注册完即可保存并预览源码

img_14.png

组件配置完成,功能正常

img_15.png

简单表单设计器 img_16.png

表单自定义控件 has loaded