基于 el-form 封装一个依赖 json 动态渲染的表单控件

基于 el-form 封装了一个表单控件,包括表单的子控件 。
既然要封装,那么就要完善一些,把能想到的功能都要实现出来,不想留遗憾 。
毕竟UI库提供的功能都很强大了,不能浪费了对吧 。

  • 依赖 json 动态创建表单
  • 可以多行多列
  • 可以调整布局
  • 可以自定义子控件(插槽和动态组件)
  • 可以扩展表单子控件
  • 数据验证
  • 数据联动
  • 组件联动
  • 依据 json 自动创建 model
功能演示介绍代码之前先看看效果 。
  • 单列表单
    这个比较基础,直接贴图 。

基于 el-form 封装一个依赖 json 动态渲染的表单控件

文章插图
 
  • 多列表单
    有时候需要双列或者三列的表单,这个也是要支持的 。

基于 el-form 封装一个依赖 json 动态渲染的表单控件

文章插图
 

基于 el-form 封装一个依赖 json 动态渲染的表单控件

文章插图
 
因为采用的是 el-col 实现的多列,所以理论上最多支持 24 列,当然要看屏幕的宽度了 。
  • 调整布局
    看上面的图片,可以发现个问题,改变列数之后,表单页面变得不好看了,这时候需要我们做一些调整,比如让某个组件占用两份空间,调整一下组件的先后顺序 。
【单列中的合并】
基于 el-form 封装一个依赖 json 动态渲染的表单控件

文章插图
 
调整之后,页面可以更紧凑 。可以两个组件占一行,也可以三个组件占一行,具体看屏幕的宽度和一个组件的大小 。
【多列里的占一行】
基于 el-form 封装一个依赖 json 动态渲染的表单控件

文章插图
 
  • 自定义子控件
    如果表单提供的子控件不能满足需求,那么怎么办?我们可以自己来定义一个子控件 。
  1. 使用插槽
    使用插槽比较简单和灵活,可以在表单控件外部完全控制,适合临时的情况,插槽里可以有多个组件 。

基于 el-form 封装一个依赖 json 动态渲染的表单控件

文章插图
 
  1. 使用动态组件
    插槽的方式虽然灵活,但是不便于复用,如果需要在多个地方使用的话,可以先做成一个组件,然后用动态组件的方式加入表单 。

基于 el-form 封装一个依赖 json 动态渲染的表单控件

文章插图
 
这里使用动态组件的方式加入了 element 的穿梭控件,也可以加入其它各种组件 。
  • 数据验证
    可以直接使用 el-form 提供的验证功能,在json里面设置好验证规则即可 。

基于 el-form 封装一个依赖 json 动态渲染的表单控件

文章插图
 
  • 数据联动
  1. 一个组件内的联动
    这个可以使用 el-cascader 来实现 。
  2. 多个组件的联动
    可以用简单来实现 。
  • 组件联动
    可以根据某个组件的值,设置其他组件是否显示 。

基于 el-form 封装一个依赖 json 动态渲染的表单控件

文章插图
 

基于 el-form 封装一个依赖 json 动态渲染的表单控件

文章插图
 
  • 视频演示
    看一下动态演示:【表单演示的视频】
封装表单子控件表单控件需要很多子控件,所以要先封装一下子控件,然后才方便封装表单控件 。
定义接口,统一规范表单子控件有一个相同的需求,都需要实现属性和 v-model 数据交换,因为 element 把 value 封装成了v-model,所以无法直接绑定组件的属性,必须建立一个内部变量来绑定 。
所以需要一个转换的方式,这里采用自定义ref来实现,顺便实现了一下防抖功能 。
虽然在表单控件里面并不需要防抖功能,但是查询的时候需要,而表单子控件是可以通用到查询控件里面的 。
定义一个 v-model 和 my-change
// 自定义 ref /** * 自定义的ref,实现属性和内部变量的数据转换 * @param { reactive } props 组件的属性 * @param { object } context 组件的上下文 * @param { number } delay 延迟刷新的时间,单位:毫秒,默认:0 * @param { string } name 要对应的属性名称,默认:modelValue * @returns 自定义的ref */export const debounceRef = (props, context, delay = 0, name = 'modelValue') => {let _value = https://www.isolves.com/it/cxkf/bk/2021-05-19/props[name]// 计时器let timeout// 是否输入状态 。输入时取 value;输入完毕取 modelValue 属性let isInput = falsereturn customRef((track, trigger) => {return {get () {track()if (isInput) {// console.log(isInput)return _value} else {// console.log(isInput)return props[name]}},set (newValue) {isInput = true_value = newValue // 绑定值trigger() // 组件内部刷新模板clearTimeout(timeout) // 清掉上一次的计时timeout = setTimeout(() => {// 修改 modelValue 属性context.emit(`update:${name}`, newValue) // 提交给父组件// 用于区分是哪个组件触发的事件 。context.emit('my-change', newValue, props.controlId, props.colName)isInput = false}, delay)}}})}


推荐阅读