Vue3 +ElementPlus 实现表单组件的封装,通过JSON控制表单数据
在日常开发后台管理系统时,经常会遇见各种各样的表单,如果没有一个公共的表单组件,在开发过程中就会出现表单样式的不统一及复杂的代码,不利于代码的维护,故此封装一个公共的表单组件极为重要。
- 主要分为两个文件 PublicFormComponents.vue 和 form.d.ts
PublicFormComponents.vue
<template>
<div>
<el-row :gutter='gutter'>
<el-col
v-for='(item,index) in formArray'
:key='index'
:xs="item.row.xs"
:sm="item.row.sm"
:md="item.row.md"
:lg="item.row.lg"
:xl="item.row.xl">
<!-- 普通的文字显示 -->
<el-form-item :label="item.label" :prop='item.prop' v-if="item.type == 'span'">
<span style='margin-left: 20px;color: #606266;text-align: start'>{{formObject[item.value]}}</span>
</el-form-item>
<el-tooltip
class="box-item"
effect="dark"
:content="item.tooltipText"
placement="bottom"
:disabled='!item.tooltipDisabled'
v-else
>
<el-form-item :label="item.label" :prop='item.prop' >
<!-- 选择器 -->
<el-select
v-if="item.type == 'select'"
v-model="formObject[item.value]"
:placeholder="item.placeholder"
:disabled='item.disabled'
:multiple='item.multiple'
:value-key="'label'"
:clearable='!item.clearable'
:size="item.size ||'default'"
@change="selectChange($event,item.value)"
>
<el-option
v-for="(items,indexs) in item.options"
:key="indexs"
:label="items.label"
:value="items.value"
/>
</el-select>
<!-- 级联选择器 -->
<el-cascader
v-if="item.type == 'cascader'"
ref="elCascader"
v-model="formObject[item.value]"
:options="item.options"
:placeholder="item.placeholder"
:clearable='!item.clearable'
:disabled='item.disabled'
:props='item.props'
:show-all-levels="item.showAllLevels"
:size="item.size ||'default'"
@change='cascaderChange($event,item.value)'
/>
<!-- 输入框 -->
<el-input
v-if="item.type == 'input' || item.type == 'textarea' || item.type == 'number' "
v-model="formObject[item.value]"
:disabled='item.disabled'
:placeholder="item.placeholder"
:type='item.type'
:maxlength="item.maxlengthShow ? item.maxlength : ''"
autosize
:size="item.size ||'default'"
:clearable='!item.clearable'
@blur='inputBlur($event,item.value)'
@focus='inputFocus($event,item.value)'
@change='inputChange($event,item.value)'
/>
<!-- 时间日期选择器 -->
<el-date-picker
v-if="item.type == 'date' || item.type == 'monthrange' || item.type == 'datetimerange' || item.type == 'datetime' || item.type == 'daterange'"
v-model="formObject[item.value]"
style='width: 100%'
:disabled='item.disabled'
:value-format="item.format"
:format="item.format"
:type="item.type"
:placeholder="item.placeholder"
:start-placeholder='item.startPlaceholder'
:end-placeholder='item.endPlaceholder'
:size="item.size ||'default'"
:clearable='!item.clearable'
@change='dataPickerChange($event,item.value)'
:range-separator='item.rangeSeparator'
:editable='false'
:shortcuts="item.shortcuts"
/>
</el-form-item>
</el-tooltip>
</el-col>
<slot name="custom">
<!-- 自定义表单项插槽-->
</slot>
<slot name="button">
<!-- 按钮插槽 -->
</slot>
</el-row>
</div>
</template>
<script lang='ts' setup>
import { reactive, ref, toRefs } from 'vue';
const props = defineProps({
//表单数组
formArray:{
type: Array,
default: () => {
return [];
}
},
//表单对象
formObject:{
type: Object,
default: () => {
return {};
}
},
gutter:{
type: Number,
default: 24
}
})
const { formArray , formObject } = toRefs(props);
const emit = defineEmits(
['selectChange','cascaderChange','inputBlur','inputFocus','inputChange','dataPickerChange']
);
//以下为表单的部分输入框的监听事件
const selectChange = (e:any,value:string) => {
emit('selectChange',{data:e,value:value});
}
const cascaderChange = (e:any,value:string) => {
emit('cascaderChange',{data:e,value:value});
}
const inputBlur = (e:any,value:string) => {
emit('inputBlur',{data:e,value:value});
}
const inputFocus = (e:any,value:string) => {
emit('inputFocus',{data:e,value:value});
}
const inputChange = (e:any,value:string) => {
emit('inputChange',{data:e,value:value});
}
const dataPickerChange = (e:any,value:string) => {
emit('dataPickerChange',{data:e,value:value});
}
</script>
<style scoped lang='scss'>
:deep(.el-form-item){
align-items: center !important;
width: 100%;
}
:deep(.el-select){
width: 100%;
}
:deep(.el-input__wrapper){
width: 100%;
}
:deep(.el-cascader){
width: 100%;
}
:deep(.is-disabled .el-input__inner){
cursor: pointer !important;
}
:deep(.is-disabled .el-textarea__inner){
cursor: pointer !important;
}
</style>
- form.d.ts
type rowType = {
xs:number,
sm:number,
md:number,
lg:number,
xl:number,
}
export type formType = Array<{
label?:string,//表单的label
value?:string,//v-model绑定的字段
prop?:string,//必填校验
options?:any,//type为select/cascader时的下拉数据
type?:string,//输入框类型,类型:input、select、cascader、number、date、textarea、datetimerange、datetime
placeholder?:string,//输入框的提示语
startPlaceholder?:string,//输入框的开始提示语
endPlaceholder?:string,//输入框的结束提示语
rangeSeparator?:string,//时间范围选择器的中间提示语
size?:string,//输入框的大小,可选值为large、small、default或者不设置
props?:any,//当type为cascader时设置的数据格式
row:rowType,//栅格布局
multiple?:boolean,//是否多选
maxlengthShow?:boolean,//是否设置字数上限,默认为false
maxlength?:string,//字数上限
disabled?:boolean,//是否禁用,默认为false
format?:string,//当type为date时的时间格式
showAllLevels?:boolean,
shortcuts?:Array<{ text: string, value: Date | Function }>,//当type为date时的时间格式
clearable?:boolean,//是否显示清除按钮
tooltipDisabled?:boolean,//是否显示tooltip,默认禁用
tooltipText?:string,//tooltip提示的文字,当tooltipDisabled为false时生效
}>
组件的使用方法
<PublicFormComponents :form-array='parmasArray' :form-object='queryParams'></PublicFormComponents >
import { formType } from '@/types/form';
import { reactive } from 'vue';
const from : formType = reactive([
{ label: '213', value:'12' , prop: '123',options:[] , type: 'cascader', placeholder:'123', props:{value:'id', label:'name', children:'children', checkStrictly: true,},row:{xs:24,sm:24,md:24,lg:24,xl:24} },
])