一、Form组件二次封装考虑组件构成:
form组件:
input text passworld
select
checkbox
radio
文本域
日期
二、实现Form表单的二次封装:
1. 分析出对应的位置 开始抽离组件
2. 如果需要产生多个form表单,则需要产生多个el-form-item,则需要一个数据来循环渲染form-item
3. 结合form表单属性继续分析:
- el-form-item:label 显示文本
- 内部标签:el-input v-model绑定数据 placeholder 文本提示等等
4. 首先确认一个集合,配置内部属性:比如name,placeholder,type,size,width等等
🔰其中name使用配置使用哪一种表单(input,elbutton)
5. 在结合form表单v-model定义一个表单数据对象formData。
6. 修改二次封装组件代码中循环formConfig配置产生form
- 首先先要导入需要使用的element-ui组件
// 按需引入组件
import { ElInput, ElRadioGroup, ElRadioButton, ElDatePicker } from 'element-plus'
- 配置formConfig表单配置集合,form-item循环该配置自动生成多个form-item,该formConfig是从父组件通过props传过来的值,所以需要使用defineProps来进行接收
interface propTypes {
formData: any;
formConfig: Array<{
name: string;
placeholder?: string;
label?: string;
key: string;
type?: string;
size?: string;
width?: string
children?: Array<{
label: string
value: string
}>
}>
}
const props = defineProps<propTypes>()
注意:因为form-item需要循环formConfig,所以formConfig为数组类型。
扩展:这里需要注意的是如果需要给defineProps设置默认值(el-table组件的二次封装需要设置默认值)时,使用withDefaults()
interface PropType {
tableData?: Array<any>
columnKey?: Array<any>,
title: Array<any>,
border?: Boolean,
// 分页参数:
pageSizes?: Array<any>,
layout?: string,
total?: number
}
const props = withDefaults(defineProps<PropType>(),{layout:'total, prev, pager, next,sizes',total:18,pageSizes:()=>[5,10,15,20]})
- 在template组件中循环生成form-item
<template>
<el-form :inline="true" class="demo-form-inline">
<!-- 循环遍历产生Form组件 -->
<template v-for="(item, index) in formConfig">
<el-form-item :label="item.label">
<el-input :placeholder="item.placeholder" />
</el-form-item>
</template>
</el-form>
</template>
7. 分析简易封装之后的代码,得知el-input不是固定的,需要根据配置中的key动态切换,可以使用vue中component组件的动态挂载来完成
相当于因为循环的是formConfig,而formConfig中的key值是需要绑定的key值,也就是后端需要请求的字段,然后formData对象中存储的刚好是后端需要的字段所以根据对象的中括号来获取key值
<!-- 使用组件的动态挂载 -->
<component :is="UICcom[item.name]" :placeholder="item.placeholder" v- model="formData[item.key]"@click="RadioGroup">
<template v-if="item.children && item.children.length">
<el-radio-button v-for="its in item.children" :label="its.value">{{ its.label}}
</el-radio-button>
</template>
</component>
- 定义对象存储UI组件
interface UIComType{
[propName:string]:any
}
let UICom:UIComType = {
ElInput:ElInput
}
🔰这里需要使用TS对UICom做类型限制否则在动态挂载组件时会报错。这里对属性值不明确的情况下使用[propName:string]:any
8. 父组件使用
- 在父组件中配置form表单配置:
const formConfig = [
{
name: 'ElRadioGroup',
label: '时间选择:',
key: 'data',
type: 'DatePick',
width: '6',
children: [
{
value: '',
label: '全部'
}
],
}
]
- 在组件中配置form表单数据源:formData: 绑定form表单中的数据,form表单根据key值来配置v-model的值,这里form表单中的元素都是后端需要请求的字段
const FormData = ref({
paid: '',
extract_type: '',
nickname: '',
data: '',
page: 1,
limit: 5
})
- 导入form表单组件并动态绑定formConfig、formData
<FormVue :form-config="formConfig"
:form-data="FormData"
@FormEvent="FormEvent">
</FormVue>
效果图:
9. 若像是el-select、el-radio-group需要子项,则需要在formConfig中添加children数组,封装组件这里需要对这种包含子项的组件使用component组件动态加载并使用判断。
{
name: 'ElRadioGroup',
label: '时间选择:',
key: 'data',
type: 'DatePick',
width: '6',
children: [
{
value: '',
label: '全部'
},
{
value: 'today',
label: '今天'
},
],
},
<el-form-item :label="item.label" v-for="(item, index) in formConfig" :key="index">
<!-- 使用组件的动态挂载 -->
<component :is="UICcom[item.name]" :placeholder="item.placeholder" v-model="formData[item.key]">
<template v-if="item.children && item.children.length > 0">
<el-radio-button v-for="its in item.children" :label="its.value">{{ its.label
}}</el-radio-button>
</template>
<template v-else>
<!-- 使用组件的动态挂载 -->
<component :is="UICcom[item.name]" :placeholder="item.placeholder" v-model="formData[item.key]"
@click="RadioGroup">
<template v-if="item.children && item.children.length">
<el-radio-button v-for="its in item.children" :label="its.value">{{ its.label
}}</el-radio-button>
</template>
</component>
</template>
</component>
</el-form-item>
效果图:
但是我们发现所有的表单都在一行,所以就需要添加el-row和el-col
代码:
在这里需要注意的是如果需要在一行中添加两个表单组件,则需要将formConfig结构改为数组类型。
然后在父组件中的formConfig中进行改变,将每一项全部改为数组类型,并且如果哪一行需要几个表单则在该数组中添加n个对象。
form二次封装表单使用$emit来向父组件传递值:
const emit = defineEmits<{ (e: 'FormEvent', val: any): void }>()
const RadioGroup = () => {
console.log(props.formData);
emit('FormEvent', props.formData)
}
🔰将绑定的formData传递给父组件
form表单二次封装的整体代码:
<template>
<el-form label-position="left" :model="formData" :inline="true" >
<!-- <template > -->
<el-row v-for="(items, index) in formConfig" :key="index">
<el-col v-for="(item, index) in items" :span="10" :key="index">
<el-form-item :label="item.label">
<!-- 使用组件的动态挂载 -->
<component :is="UICcom[item.name]" :placeholder="item.placeholder" v-model="formData[item.key]">
<template v-if="item.children && item.children.length > 0">
<el-radio-button v-for="its in item.children" :label="its.value">{{ its.label
}}</el-radio-button>
</template>
<template v-else>
<!-- 使用组件的动态挂载 -->
<component :is="UICcom[item.name]" :placeholder="item.placeholder" v-model="formData[item.key]"
@click="RadioGroup">
<template v-if="item.children && item.children.length">
<el-radio-button v-for="its in item.children" :label="its.value">{{ its.label
}}</el-radio-button>
</template>
</component>
</template>
</component>
</el-form-item>
</el-col>
</el-row>
</el-form>
</template>
<script lang="ts" setup>
// import {reactive} from 'vue'
// 按需引入组件
import { ElInput, ElRadioGroup, ElRadioButton, ElDatePicker } from 'element-plus'
interface propTypes {
formData: any;
formConfig: Array<Array<{
name: string;
placeholder?: string;
label?: string;
key: string;
type?: string;
size?: string;
width?: string
children?: Array<{
label: string
value: string
}>
}>>
}
const props = defineProps<propTypes>()
// 定义对象存储key:UI组件
interface UIComType {
[propName: string]: any
}
let UICcom: UIComType = {
"ElInput": ElInput,
"ElRadioGroup": ElRadioGroup,
"ElDatePicker": ElDatePicker
}
const RadioGroup = () => {
console.log(props.formData);
emit('FormEvent', props.formData)
}
const emit = defineEmits<{ (e: 'FormEvent', val: any): void }>()
</script>
<style lang="scss" scoped></style>
父组件使用代码:
<FormVue :form-config="formConfig" :form-data="FormData" @FormEvent="FormEvent"></FormVue>
const formConfig = [
[{
name: 'ElRadioGroup',
label: '时间选择:',
key: 'data',
type: 'DatePick',
width: '6',
children: [
{
value: '',
label: '全部'
},
{
value: 'today',
label: '今天'
},
{
value: 'yesterday',
label: '昨天'
},
{
value: 'week',
label: '本周'
},
{
value: 'month',
label: '本月'
},
{
value: 'quarter',
label: '本季度'
},
{
value: 'year',
label: '本年'
},
],
},
{
name: 'ElInput',
label: '搜索:',
placeholder: '请输入用户昵称,订单号',
key: 'nickname'
}
],
[{
name: 'ElRadioGroup',
label: '支付类型:',
key: 'paid',
type: 'DatePick',
width: '6',
children: [
{
value: '',
label: '全部'
},
{
value: '1',
label: '已支付'
},
{
value: '0',
label: '未支付'
},
],
}],
[{
name: 'ElInput',
label: '搜索:',
placeholder: '请输入用户昵称,订单号',
key: 'nickname'
}]
]
const FormData = ref({
paid: '',
extract_type: '',
nickname: '',
data: '',
page: 1,
limit: 5
})