Vue3组件(九)封装一个长大的表单(上)
一个神奇的表单
表单表单,你已经长大了,你要学会自己:
- 排成一列
- 排成一行
- 验证表单数据
- 排成方队
- 合并调整
- 动态渲染
- 支持 item 扩展组件
- 可以自动创建 model
实现多行多列的表单
首先感谢 el-form,真的很强大,不仅好看,还提供了验证功能,还有很多其他的功能。
只是好像只能横着排,或者竖着排。那么能不能多行多列呢?似乎没有直接提供。
我们知道 el-row、el-col 可以实现多行多列的功能,那么能不能结合一下呢?官网也不直说,害的我各种找,还好找到了。(好吧,其实折腾了一阵着的table)
二者结合一下就可以了,这里有个小技巧,el-row只需要一个就可以,el-col可以有多个,这样一行排满后,会自动排到下一行。
<el-form ref="form" :model="formModule" label-width="130px">
<el-row>
<!--不循环row,直接循环col,放不下会自动往下串行。-->
<el-col :span="8">
<!--假装有好多好多的el-col-->
<el-form-item :label="姓名:">
<!--这里可以放组件-->
</el-form-item>
</el-col>
</el-row>
</el-form>
这样有什么好处呢?当然是便于我们做v-for呀,给 el-col 加上 v-for 就好。
实现动态渲染功能
表单嘛,那么多字段一个一个做多麻烦,v-for 一下不香吗?
前面封装了那么多的组件,就是为了可以 v-for。
首先准备一个json文件,里面放置需要的组件的属性。
003表单的json.png
json比较长,我们还是看截图吧,直观一些。
然后用 require 读取进来,当然也可以用 axios 来读取。
然后表单控件就可以用这些属性做循环了。
另外还有几个附带功能:
-
支持单行下的合并。
在单行的情况下,一些短的控件会比较占空间,我们可以把多个小的合并到一行。 -
支持多行下的扩展。
多行的情况下,一些长的控件需要占更多的空间,我们可以设置它多占几个格子。 -
自动创建表单需要的 model。
不需要手动写 model了。
自动创建 model
我比较懒,手撸 model 是不是有点麻烦?如果能够自动获得该多好,于是我写了这个函数。
// 创建 v-model
const createModel = () => {
// 依据meta,创建module
for (const key in formItemMeta) {
const m = formItemMeta[key]
// 根据控件类型设置属性值
switch (m.controlType) {
case 100: // 文本类
case 101:
case 102:
case 103:
case 104:
case 105:
case 106:
case 107:
case 130:
case 131:
formModel[m.colName] = ''
break
case 110: // 日期
case 111: // 日期时间
case 112: // 年月
case 114: // 年
case 113: // 年周
formModel[m.colName] = null
break
case 115: // 任意时间
formModel[m.colName] = '00:00:00'
break
case 116: // 选择时间
formModel[m.colName] = '00:00'
break
case 120: // 数字
case 121:
formModel[m.colName] = 0
break
case 150: // 勾选
case 151: // 开关
formModel[m.colName] = false
break
case 153: // 单选组
case 160: // 下拉单选
case 162: // 下拉联动
formModel[m.colName] = null
break
case 152: // 多选组
case 161: // 下拉多选
formModel[m.colName] = []
break
}
// 看看有没有设置默认值
if (typeof m.defaultValue !== 'undefined') {
switch (m.defaultValue) {
case '':
break
case '{}':
formModel[m.colName] = {}
break
case '[]':
formModel[m.colName] = []
break
case 'date':
formModel[m.colName] = new Date()
break
default:
formModel[m.colName] = m.defaultValue
break
}
}
}
// 同步父组件的v-model
context.emit('update:modelValue', formModel)
}
可以根据类型和默认值,设置model的属性,这样就方便多了。
多列的表单
这个是最复杂的,分为两种情况:单列的挤一挤、多列的抢位置。
单列
单列的表单有一个特点,一行比较宽松,那么有时候就需要两个组件在一行里显示,其他的还是一行一个组件,那么要如何调整呢?
这里做一个设定:
- 一个组件一行的,记做1
- 两个组件挤一行的,记做-2
- 三个组件挤一行的,记做-3
以此类推,理论上最多支持-24,当然实际上似乎没有这么宽的显示器。
这样记录之后,我们就可以判断,≥1的记做span=24,负数的,用24去除,得到的就是span的数字。当然记得取整数。
为啥用负数做标记呢?就是为了区分开多列的调整。
多列
多列的表单有一个特点,一个格子比较小,有些组件太长放不下,这个时候这个组件就要抢后面的格子来用。
那么我们还是做一个设定:
- 一个组件占一格的,还是记做1
- 一个组件占两格的,记做2
- 一个组件占三格的,记做3
以此类推。
这样记录之后,我们可以判断,≤1的,记做 24 / 列数,大于1的记做 24/ 列数 * n。
这样就可以了,可以兼容单列的设置,不用因为单列变多列而调整设置。
只是有个小麻烦,占得格子太多的话,就会提取挤到下一行,而本行会出现“空缺”。
这个暂时靠人工调整吧。
毕竟哪个字段在前面,还是需要人工设置的。
一顿分析猛如虎,一看代码没几行。
// 调整行列
const span = reactive({})
// 根据配置里面的colCount,设置span
const getSpan = () => {
const formColCount = formMeta.formColCount // 列数
const moreColSpan = 24 / formColCount // 一个格子占多少份
if (formColCount === 1) {
// 一列的情况
for (const key in formItemMeta) {
const m = formItemMeta[key]
if (typeof m.colCount === 'undefined') {
span[m.controlId] = moreColSpan
} else {
if (m.colCount >= 1) {
// 单列,多占的也只有24格
span[m.controlId] = moreColSpan
} else if (m.colCount < 0) {
// 挤一挤的情况, 24 除以 占的份数
span[m.controlId] = moreColSpan / (0 - m.colCount)
}
}
}
} else {
// 多列的情况
for (const key in formItemMeta) {
const m = formItemMeta[key]
if (typeof m.colCount === 'undefined') {
span[m.controlId] = moreColSpan
} else {
if (m.colCount < 0 || m.colCount === 1) {
// 多列,挤一挤的占一份
span[m.controlId] = moreColSpan
} else if (m.colCount > 1) {
// 多列,占的格子数 * 份数
span[m.controlId] = moreColSpan * m.colCount
}
}
}
}
}
最后看看效果,可以动态设置列数:
动态表单001.gif如果空间够的话,最多可以是24列。应该是够用了。