1、序言
项目地址:git clone form-demo: 封装通用el-form
一个后台管理系统最常见的是表单,表单最常见的是输入框、下拉选择、日期选择、单选、复选框等等, 系统添加若干模块,就复制粘贴若干个el-form、el-form-item,有一说一,完成需求快是快,但是代码冗余的部分太多了,能不能通过配置方式,自动生成el-form、el-form-item
不封装代码前:
封装代码后:
两种封装方式的变量、方法名基本一致!
2、自定义组件方式封装el-form
2.1、封装
(1)新建commentForm文件夹,并创建index.vue文件
(2)index.vue中
<template>
<div>
<el-form ref="form" :model="form" :rules="rules" :inline="inline" :label-width="labelWidth">
<template v-for="(item, index) in formItemList">
<!-- 输入框类型 -->
<template v-if="item.type === 'input'">
<el-form-item :label="item.label" :prop="item.model">
<el-input v-model.trim="form[item.model]" :type="`${item.category || 'text'}`"
:style="`width: ${item.width || '250px'}`" :clearable="item.clearable === undefined || item.clearable"
filterable :placeholder="item.placeholder" />
</el-form-item>
</template>
<!-- 下拉选择框 -->
<template v-if="item.type === 'select'">
<el-form-item :label="item.label" :prop="item.model">
<el-select v-model.trim="form[item.model]" :style="`width:${item.width || '250px'}`" clearable
:placeholder="item.placeholder || ''">
<el-option v-for="(i, key) in item.options" :key="i[item.value] || key" :label="i[item.label] || i.label"
:value="i[item.value] || i.value">
</el-option>
</el-select>
</el-form-item>
</template>
<!-- 日期选择器 -->
<template v-if="item.type === 'date-picker'">
<el-form-item :prop="item.model" :label="item.label">
<el-date-picker v-model.trim="form[item.model]" type="daterange" range-separator="至" start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
</template>
<!-- 开关 -->
<template v-if="item.type === 'switch'">
<el-form-item :prop="item.model" :label="item.label">
<el-switch v-model="form[item.model]"></el-switch>
</el-form-item>
</template>
<!-- 复选框 -->
<template v-if="item.type === 'checkbox'">
<el-form-item :prop="item.model" :label="item.label">
<el-checkbox-group v-model="checkboxList">
<el-checkbox :label="item.label" :key="index" v-for="(item, index) in item.options"></el-checkbox>
</el-checkbox-group>
</el-form-item>
</template>
<!-- 单选框 -->
<template v-if="item.type === 'radio'">
<el-form-item :prop="item.model" :label="item.label">
<el-radio-group v-model="form[item.model]">
<el-radio :label="item.label" :key="index" v-for="(item, index) in item.options"></el-radio>
</el-radio-group>
</el-form-item>
</template>
<!-- 自己拓展... -->
</template>
<!-- 按钮列表 -->
<el-form-item>
<template v-for="item in buttonList">
<el-button :type="item.type" @click="item.event">{{ item.text }}</el-button>
</template>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
props: {
// 生成el-form-item的数组
formItemList: {
type: Array,
default: () => [],
},
// 是否行内表单模式
inline: {
type: Boolean,
default: false,
},
// el-form-item的label宽度
labelWidth: {
type: String,
default: '80px',
},
// 表单校验规则
rules: {
type: Object,
default: () => { },
},
// 按钮列表
buttonList: {
type: Array,
default: () => [],
}
},
data() {
return {
form: {},
checkboxList: [],
}
},
methods: {
// 返回经过校验的表单
returnForm() {
// 将复选框内容添加到form表单中,字段从formItemList type=checkbox中找model
this.formItemList.forEach(item => {
if (item.type == 'checkbox') {
this.form[item.model] = this.checkboxList
}
})
// 表单校验
this.$refs['form'].validate((valid) => {
if (valid) {
this.$emit('submitForm', this.form)
}
})
},
// 重置表单
resetForm() {
// 重置复选框
this.checkboxList = []
this.$refs.form.resetFields();
this.$emit('resetForm');
}
}
}
</script>
<style scoped></style>
(3)注意事项:
- el-checkbox中v-model绑定的值要事先存在,不然就会报出无法找到length属性的错误
所以提前在data中声明一个数组,v-model绑定这个数组,返回表单给父组件时,将其添加到表单对象中,重置表单时,使这个数组为空即可实现重置表单功能!
- 重置不成功的原因可能是:(1)未添加prop (2)prop绑定的值与model绑定的对象所对应的属性不一致
2.2、使用
重点关注部位:
(1)formItemList中的type决定生成什么类型的表单项,如输入框、下拉选项等等
(2)formItemList中的model表示表单项双向绑定的名称,也是子组件返回对象给父组件中对象的属性名
(3)formItemList中的options表示下拉选项、复选框、单选按钮的选项值
(4)@submitForm绑定的事件可以获取子组件返回给父组件经过校验的表单,通常不在子组件进行网络请求,让父组件进行网络请求
(5)父组件通过$refs获取组件实例从而调用子组件重置表单、提交表单的方法
<template>
<div id="app">
<CommonForm ref="form" :rules="rules" :buttonList="buttonList" :formItemList="formItemList" @submitForm="getForm">
</CommonForm>
</div>
</template>
<script>
import CommonForm from '@/components/commonForm'
import jsxForm from '@/components/jsxForm';
import JsxForm from '@/components/jsxForm'
export default {
name: 'App',
components: {
CommonForm,
JsxForm,
jsxForm
},
data() {
return {
formItemList: [
{ label: '活动名称', type: 'input', model: 'name', placeholder: '请输入活动名称' },
{ label: '活动区域', type: 'select', model: 'region', placeholder: '请选择状态', options: [{ label: '上海', value: 'shanghai' }, { label: '北京', value: 'beijing' }] },
{ label: '活动时间', type: 'date-picker', model: 'startTime', },
{ label: '即时配送', type: 'switch', model: 'delivery' },
{ label: '活动性质', type: 'checkbox', model: 'type', options: [{ label: '美食/餐厅线上活动' }, { label: '地推活动' }, { label: '线下主题活动' }, { label: '单纯品牌曝光' },] },
{ label: '特殊资源', type: 'radio', model: 'resource', options: [{ label: '线上品牌商赞助' }, { label: '线下场地免费' }] },
],
rules: {
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
},
buttonList: [
{ text: '提交', type: 'primary', event: this.submitForm },
{ text: '重置', event: this.resetForm },
]
}
},
methods: {
// 接受子组件传回来的form表单内容
getForm(val) {
console.log('val:', val);
},
// 点击提交,触发表单校验
submitForm() {
this.$refs.form.returnForm();
},
// 重置表单
resetForm() {
this.$refs.form.resetForm();
}
}
}
</script>
<style>
#app {
display: flex;
flex-direction: column;
align-items: center;
color: #2c3e50;
}
</style>
3、jsx方式封装el-form
jsx封装方式借鉴了这篇文章:element-ui 通用表单封装及VUE JSX应用 - 掘金
3.1、封装
(1)上篇博客有简单介绍jsx:两种方式对el-table二次封装_码上编程的博客-CSDN博客
(2) 新建jsxForm文件夹,并创建index.js文件
(3)封装代码:
代码逻辑图如下:
export default {
name: 'jsxForm',
props: {
// 生成el-form-item的数组
formItemList: {
type: Array,
default: () => [],
},
// 是否行内表单模式
inline: {
type: Boolean,
default: false,
},
// el-form-item的label宽度
labelWidth: {
type: String,
default: '100px',
},
// 表单校验规则
rules: {
type: Object,
default: () => { },
},
// 按钮列表
buttonList: {
type: Array,
default: () => [],
}
},
data() {
return {
form: {},
checkboxList: []
}
},
methods: {
// 生成选项
generateOption(itemObj) {
let options = []
for (let index = 0; index < itemObj.options.length; index++) {
const item = itemObj.options[index]
switch (itemObj.type) {
// 下拉菜单
case 'select':
options.push(<el-option label={item.label} value={item.value}></el-option>)
break
// 多选框
case 'checkbox':
options.push(<el-checkbox label={item.label}></el-checkbox>)
break
// 单选框
case 'radio':
options.push(<el-radio label={item.label}>{item.label}</el-radio>)
break
}
}
return options
},
// 生成下拉菜单
generateSelect(item) {
return <el-select v-model={this.form[item.model]}>{this.generateOption(item)}</el-select>
},
// 生成多选框
generateCheckbox(item) {
this.form[item.model] = this.checkboxList
return <el-checkbox-group v-model={this.checkboxList}>{this.generateOption(item)}</el-checkbox-group>
},
// 生成单选
generateRadio(item) {
return <div>
<el-radio-group v-model={this.form[item.model]}>{this.generateOption(item)}</el-radio-group>
</div>
},
// 生成输入框
generateInput(item) {
return (<div>
<el-input v-model={this.form[item.model]} style={{ width: `${this.formItemContentWidth}` }}></el-input>
</div>)
},
// 生成开关
generateSwitch(item) {
return <div>
<el-switch v-model={this.form[item.model]}></el-switch>
</div>
},
// 生成日期组件
generateDate(item) {
return <div>
<el-date-picker v-model={this.form[item.model]} type={"daterange"} range-separator={"至"}
start-placeholder={"开始日期"} end-placeholder={"结束日期"}>
</el-date-picker>
</div >
},
// 生成表单项
generateFormItems(list = []) {
let formItems = []
list.forEach(item => {
let formItemContent = ''
switch (item.type) {
// 下拉菜单
case 'select':
formItemContent = this.generateSelect(item)
break
// 单选框
case 'radio':
formItemContent = this.generateRadio(item)
break
// 输入框
case 'input':
formItemContent = this.generateInput(item)
break;
// 开关
case 'switch':
formItemContent = this.generateSwitch(item)
break;
// 日期
case 'date-picker':
formItemContent = this.generateDate(item)
break;
// 复选框
case 'checkbox':
formItemContent = this.generateCheckbox(item)
break;
default:
break
}
formItems.push(<el-form-item label={item.label} prop={item.model}>{formItemContent}</el-form-item>)
})
return formItems
},
// 按钮列表
generateBtnList() {
let buttons = []
this.buttonList?.forEach(item => {
buttons.push(<el-button type={item.type} onClick={() => item.event()}>{item.text}</el-button>)
})
return buttons
},
// 重置表单
resetForm() {
// 重置复选框
this.checkboxList = []
this.$refs.form.resetFields();
},
// 返回经过校验的表单
returnForm() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.$emit('submitForm', this.form)
}
})
}
},
render() {
return (
<div>
<el-form ref="form" props={{ model: this.form }} rules={this.rules} inline={this.inline} label-width={this.labelWidth || '150px'}>
{this.generateFormItems(this.formItemList)}
<el-form-item>
{this.generateBtnList()}
</el-form-item>
</el-form>
</div>
)
}
}
3.2、使用
<template>
<div id="app">
<jsxForm ref="form" :buttonList="buttonList" :rules="rules" :formItemList="formItemList" @submitForm="getForm">
</jsxForm>
</div>
</template>
<script>
import jsxForm from '@/components/jsxForm';
export default {
name: 'App',
components: {
JsxForm,
},
data() {
return {
formItemList: [
{ label: '活动名称', type: 'input', model: 'name', placeholder: '请输入活动名称' },
{ label: '活动区域', type: 'select', model: 'region', placeholder: '请选择状态', options: [{ label: '上海', value: 'shanghai' }, { label: '北京', value: 'beijing' }] },
{ label: '活动时间', type: 'date-picker', model: 'startTime', },
{ label: '即时配送', type: 'switch', model: 'delivery' },
{ label: '活动性质', type: 'checkbox', model: 'type', options: [{ label: '美食/餐厅线上活动' }, { label: '地推活动' }, { label: '线下主题活动' }, { label: '单纯品牌曝光' },] },
{ label: '特殊资源', type: 'radio', model: 'resource', options: [{ label: '线上品牌商赞助' }, { label: '线下场地免费' }] },
],
rules: {
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
},
buttonList: [
{ text: '提交', type: 'primary', event: this.submitForm },
{ text: '重置', event: this.resetForm },
]
}
},
methods: {
// 接受子组件传回来的form表单内容
getForm(val) {
console.log('val:', val);
},
// 点击提交,触发表单校验
submitForm() {
this.$refs.form.returnForm();
},
// 重置表单
resetForm() {
this.$refs.form.resetForm();
}
}
}
</script>