背景:为了避免编写重复的html, css, js代码(虽然一般情况下都是复制粘贴的),提高开发效率(其实是不想写)。配置化生成表单只需要我们写js对象就可以了。
知识点:Vue自带的component动态组件,is属性可以根据我们传入的参数解析成不同的组件。传入我们自定义的组件就会渲染成相应组件,传入el-input
就会渲染成<el-input></el-input>
组件。
封装schema-form组件
<template>
<div>
<el-form :model="form" ref="ruleForm" :rules="rules" :label-width="labelWidth" :class="direction==='vertical'?'normal_form':'flex_form'">
<el-form-item v-for="item in options" :key="item.key" :prop="item.key" :label="item.label" :label-width="item.labelWidth || labelWidth" class="form_item">
<!--提供一个插槽,提高扩展性-->
<slot v-if="item.component === 'slot'" :name="item.slotName"></slot>
<template v-else>
<component :is="item.component" v-model="form[item.key]" v-bind="item.props" v-on="item.listeners" :style="{width: item.width || '200px'}">
<template v-if="item.component === 'el-select'">
<el-option v-for="(subitem,index) in item.data" :key="index" :label="subitem.label" :value="subitem.value"></el-option>
</template>
<template v-else-if="item.component === 'el-checkbox-group'">
<el-checkbox
v-for="(subitem,index) in item.data"
:key="index"
:label="subitem.value"
>
{{ subitem.label }}
</el-checkbox>
</template>
<template v-else-if="item.component === 'el-radio-group'">
<el-radio
v-for="(subitem,index) in item.data"
:key="index"
:label="subitem.value"
>
{{ subitem.label }}
</el-radio>
</template>
</component>
</template>
</el-form-item>
</el-form>
</div>
</template>
el-select,el-checkbox-group,el-radio-group组件需要做特殊处理。
<script>
export default{
props: {
form: { // 表单绑定数据
type: Object,
default: {}
},
options: { // 表单项配置数据
type: Array,
default: []
},
rules: { // 表单验证规则
type: Object,
default: {}
},
labelWidth: { // label宽度
type: String,
default: '100px'
},
direction: { // 表单方向
type: String,
default: 'vertical'
},
},
methods: {
validate(callback) {
this.$refs.ruleForm.validate(valid => {
callback(valid)
})
},
resetFields() {
this.$refs.ruleForm.resetFields()
}
},
}
</script>
<style lang='scss' scoped>
.flex_form {
display: flex;
flex-flow: wrap;
}
</style>
使用组件
<template>
<div>
<schema-form ref="schemaForm" :form="form" :options="options" :rules="rules" labelWidth="120px" direction="vertical">
<div slot="search">
<el-button type="primary" @click="handleSubmit">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
</div>
</schema-form>
</div>
</template>
<script>
import schemaForm from './components/schemaForm';
export default{
components: { schemaForm },
data() {
return {
form: {
activity: [],
age: '18'
},
options: [],
rules: {
name: [
{ required: true, message: '请输入活动名称', trigger: 'blur' },
],
age: [
{ required: true, message: '请输入年龄', trigger: 'blur' },
],
}
}
},
created() {
this.renderForm()
},
methods: {
renderForm() {
this.options = [
{
component: 'el-input',
key: 'name',
label: '名字',
labelWidth: '120px',
props: {
placeholder: '请输入名字',
clearable: true
}
},
{
component: 'el-select',
key: 'age',
label: '年龄',
labelWidth: '120px',
props: {
placeholder: '请输入年龄',
clearable: true
},
data: [ // el-select, el-checkbox-group,el-radio-group 选项数据
{
label: '18',
value: '18'
},
{
label: '38',
value: '38'
}
]
},
{
component: 'el-date-picker',
key: 'time',
label: '时间',
labelWidth: '120px',
width: '300px',
props: { // $attrs传给子组件的属性
placeholder: '请选择时间',
clearable: true,
type: 'daterange',
'range-separator': "至",
'start-placeholder': "开始日期",
'end-placeholder': "结束日期",
'value-format': 'yyyy-MM-dd HH:mm:ss'
},
listeners: { // $listeners传给子组件的事件
change: (e) => {
console.log("eeeeee", e)
}
}
},
{
component: 'slot',
slotName: 'search',
}
]
},
handleSubmit() {
this.$refs.schemaForm.validate(valid => {
if(valid) {
}
})
},
handleReset() {
this.$refs.schemaForm.resetFields()
},
},
}
</script>
options
参数 | 说明 | 默认值 |
---|---|---|
component | 组件名称 | |
key | 属性名称 | |
label | 标签文本 | |
labelWidth | 标签文本宽度 | '100px' |
width | 表单元素宽度 | '200px' |
props | 表单元素属性,根据element-ui提供属性 | |
listeners | 表单元素事件,根据element-ui提供事件 | |
data | 对el-select, el-checkbox-group,el-radio-group有效,选项数据 |
direction
表单方向,可选值:vertical和horizontal。
到目前写的例子还是很简单的配置,不能兼容各种场景,但是对于一般通用的场景还是值得一试的。后面可以根据业务场景进行不断的完善。(最后发现还是要写不少代码,感觉偷懒失败。)