01
nf-form 表单控件的功能
基于 el-form 封装了一个表单控件,包括表单的子控件。既然要封装,那么就要完善一些,把能想到的功能都要实现出来,不想留遗憾。毕竟UI库提供的功能都很强大了,不能浪费了对吧。
- 依赖 json 动态创建表单
- 可以多行多列
- 可以调整布局
- 可以自定义子控件(插槽和动态组件)
- 可以扩展表单子控件
- 数据验证
- 数据联动
- 组件联动
- 依据 json 自动创建 model
02
功能演示
看一下功能动态演示:
接下来,具体看看实现方法。
- 单列表单
这个比较基础,直接贴图。
单列表单
- 多列表单
有时候需要双列或者三列的表单,这个也是要支持
双列表单
三列表单
因为采用的是 el-col 实现的多列,所以理论上最多支持 24 列,当然要看屏幕的宽度了。
- 调整布局 看上面的图片,可以发现个问题,改变列数之后,表单页面变得不好看了,这时候需要我们做一些调整,比如让某个组件占用两份空间,调整一下组件的先后顺序。
【单列中的合并】
单列的合并组件位置
调整之后,页面可以更紧凑。可以两个组件占一行,也可以三个组件占一行,具体看屏幕的宽度和一个组件的大小。
【多列里的占一行】
多列里面一个组件占用两个位置
- 自定义子控件
如果表单提供的子控件不能满足需求,那么怎么办?我们可以自己来定义一个子控件。
- 使用插槽
使用插槽比较简单和灵活,可以在表单控件外部完全控制,适合临时的情况,插槽里可以有多个组件。
用插槽实现自定义组件
- 使用动态组件 插槽的方式虽然灵活,但是不便于复用,如果需要在多个地方使用的话,可以先做成一个组件,然后用动态组件的方式加入表单。
动态组件实现自定义子控件
这里使用动态组件的方式加入了 element 的穿梭控件,也可以加入其它各种组件。
- 数据验证 可以直接使用 el-form 提供的验证功能,在json里面设置好验证规则即可。
表单验证
- 数据联动
- 一个组件内的联动 这个可以使用 el-cascader 来实现。
- 多个组件的联动 可以用简单来实现。
- 组件联动 可以根据某个组件的值,设置其他组件是否显示。
文本类
选择类
封装表单子控件
表单控件需要很多子控件,所以要先封装一下子控件,然后才方便封装表单控件。
定义接口,统一规范
表单子控件有一个相同的需求,都需要实现属性和 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 = props[name] // 计时器 let timeout // 是否输入状态。输入时取 value;输入完毕取 modelValue 属性 let isInput = false return 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) } } }) }