vue后台管理系统,会有很多表格页面,表格上方会有一些搜索选项,表格直接使用el-table即可,而搜索栏区域每次写起来都很繁琐,而且多人开发情况下每个人写的样式都不相同,布局样式无法统一。
所以要考虑对搜索栏做一个封装,统一配置引用,提升开发维护效率和界面统一。
完成后的效果大概就是长这样:
2、分析
项目使用的是elementui框架,搜索栏这种表单提交,首先要使用el-form组件来封装,而复杂点就是表单项可能有很多种,例如input输入框、select选择框、日期时间选择框、日期时间范围选择框、cascader级联选择框等,每一项的字段名prop、名称label、绑定的属性方法都不尽相同。所以不能通过普通的绑定个别属性的方式来处理,而slot插槽的方式也无法简化,最终决定通过传递一个配置项数组的形式来解析生成相应的结构。
3、实现
目前实现的方式由两部分组成,一部分是form表单组件,接受父组件传递的配置项数组,一部分是封装一些常用的表单项组件,通过v-if来控制,form表单组件里引入该表单项组件,循环遍历,根据传递的表单项类型来匹配显示具体的表单项。
form表单组件(searchForm.vue)示例代码:
<el-form :model="formData" ref="formRef" :inline="true" > <el-form-item v-for="(item, index) in formOptions" :key="newKeys[index]" :prop="item.prop" :label="item.label ? (item.label + ':') : ''" :rules="item.rules" > <formItem v-model="formData[item.prop]" :itemOptions="item" /> </el-form-item> </el-form>
formItem表单项组件(formItem.vue)示例代码:
<el-input v-if="isInput" v-model="currentVal" v-bind="bindProps" v-on="bindEvents" size="mini" ></el-input> <el-select v-if="isSelect" v-model="currentVal" v-bind="bindProps" v-on="bindEvents" size="mini" clearable > <el-option v-for="item in itemOptions.options" :key="item.value" :label="item.label" :value="item.value" ></el-option> </el-select>
4、关键点
由于elementui表单组件本身有很多配置属性,不可能把所有的属性和方法都写死封装,要想无缝支持,需要用到vue的v-bind和v-on特性,vue的v-bind和v-on支持赋值为对象类型,vue会自动遍历对象里的属性依次绑定,v2.4.0+支持。
5、参数配置项解释
(1)示例:
[{ label: '用户名', // label文字 prop: 'username', // 字段名 element: 'el-input', // 指定elementui组件 initValue: '阿黄', // 字段初始值 placeholder: '请输入用户名', // elementui组件属性 rules: [{ required: true, message: '必填项', trigger: 'blur' }], // elementui组件属性 events: { // elementui组件方法 input (val) { console.log(val) }, } }]
label 用于绑定给el-form-item上的label,表单项标题
prop 用于绑定给el-form-item上的prop,字段名,必填
element 指定elementui表单项的组件名,必填
initValue 表单项的初始值,可选
events 对象,对象里加方法,js原生方法或者elementui表单项组件支持的方法都可以加进去,通过v-on遍历绑定
… 其他elementui表单项组件支持的属性或者html原生属性都可以添加,常用的例如rules表单校验、placeholder提示,通过v-bind遍历绑定
(2)参数传递解析的流程:
首先,searchForm.vue组件里通过props接收参数:
formOptions: { type: Array, required: true, default () { return [] } },
created组件里处理初始值:
// 添加初始值 addInitValue () { const obj = {} this.formOptions.forEach(v => { if (v.initValue !== undefined) { obj[v.prop] = v.initValue } }) this.formData = obj }
一部分配置项绑定在el-form-item上,一部分传递给formItem表单项组件再绑定:
<el-form-item v-for="(item, index) in formOptions" :key="newKeys[index]" :prop="item.prop" :label="item.label ? (item.label + ':') : ''" :rules="item.rules" > <formItem v-model="formData[item.prop]" :itemOptions="item" /> </el-form-item>
formItem.vue表单项组件里props接受传参:
itemOptions: { type: Object, default () { return {} } }
computed里处理接收的参数itemOptions,生成要绑定的所有属性对象bindProps:
// 绑定属性 bindProps () { let obj = { ...this.itemOptions } // 移除已使用的或不相关的冗余属性 delete obj.label delete obj.prop delete obj.element delete obj.initValue delete obj.rules delete obj.events if (obj.element === 'el-select') { delete obj.options } return obj },
computed里生成要绑定的所有方法对象bindEvents:
// 绑定方法 bindEvents () { return this.itemOptions.events || {} },
最后dom里使用这些数据绑定:
<el-input v-if="isInput" v-model="currentVal" v-bind="bindProps" v-on="bindEvents" ></el-input>
(3)特殊情况的处理