> 文章列表 > 封装通用el-form表单(2种方式)

封装通用el-form表单(2种方式)

封装通用el-form表单(2种方式)

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中找modelthis.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.checkboxListreturn <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>