该组件目前只封装了input 和select,其他类型依照element ui中的form表单格式封装
组件封装
<!--动态form组件 DynamicForm.vue-->
<template>
</template>
<script lang="ts">
import {defineComponent, PropType, ref, h} from 'vue'
import type {FormConfigFace, FormButtonFace, SelectOptionFace} from "@/types/form.d.ts";
import {ElButton, ElForm, ElFormItem, ElInput, ElOption, ElSelect, FormInstance} from "element-plus";
export default defineComponent({
name: 'DynamicForm',
props: {
// el-form的属性配置,如label-width等
attrsConfig: {
type: Object,
default: () => ({})
},
// el-form-item的属性配置,具体的参数为FormConfigFace
config: {
type: Array as PropType<FormConfigFace[]>, // 使用 PropType 来指定 config 为 FormConfigFace 类型的数组
require: true
},
// 表单的验证按钮,参数为FormButtonFace
btnConfig: {
type: Array as PropType<FormButtonFace[]>,
default: () => ([])
},
// el-form的model数,需要双向绑定
modelValue: {
type: Object,
default: () => ({})
}
},
emits:['update:modelValue', 'submit', 'close'],
setup(props, { emit }) {
const formData = ref({ ...props.modelValue })
// 动态生成表单
const renderFormItems = () => {
return [
h('div', {class: 'dynamic-form'}, props.config.map((item: FormConfigFace) => {
// 通用属性的配置
const commonProps = {
...item.attrs,
modelValue: formData.value[item.prop],
'onUpdate:modelValue': (val: any) => {
formData.value[item.prop] = val
emit('update:modelValue', formData.value)
},
}
// 根据不同类型渲染不同组件
let formComponent: any;
switch (item.type) {
case "input":
formComponent = h(ElInput, commonProps);
break;
case "select":
formComponent = h(ElSelect, commonProps, () => {
item.options?.map((opt:SelectOptionFace) => h(ElOption, {
label: opt.label,
value: opt.value
}))
});
break;
// 这里设置其他类型
}
return h(ElFormItem, {
prop: item.prop,
label: item.label,
rules: item.rules
}, () => formComponent)
})),
props.btnConfig.map((item: FormButtonFace) => {
const btnComponent = h(ElButton, {...item.attrs, onClick: () => {
if(item.type === 1) {
emit('submit', {formData: formData.value})
}
if(item.type === 2) {
emit('close')
}
}}, item.name)
return h(ElFormItem, {class: 'dynamic-form-btn'}, () => btnComponent)
})
]
}
return () => h(ElForm, { ...props.attrsConfig, model: formData.value }, renderFormItems)
}
})
</script>
<style scoped lang="scss">
</style>
类型声明
// src/types/form.d.ts
// 下拉选择的选项类型
export declare interface SelectOptionFace {
label: string,
value: any
}
// 表单组件类型
export declare interface FormConfigFace {
// 类型--暂定input|select|button
type: 'input' | 'select',
// 属性值
prop: string,
// 标签文本
label: string,
// 下拉选择的属性值
options?: SelectOptionFace[];
// 规则验证
rules?: any[],
// 原生属性(如placeholder)
attrs?: Record<string, any>;
}
export declare interface FormButtonFace {
// 类型1--提交| 取消
type: 1 | 2,
// 名字
name: string,
// 原生属性(如placeholder)
attrs?: Record<string, any>;
}
组件的使用
<template>
<DynamicForm
:attrsConfig="attrsConfig"
:config="formConfig"
:btn-config="btnConfig"
:model-value="formData"
@submit="submitHandle"
></DynamicForm>
</template>
<script lang="ts" setup>
import DynamicForm from '@/components/common/DynamicForm.vue';
import { ref } from 'vue';
import type { FormConfigFace, FormButtonFace } from '@/types/form.d.ts';
const formData = ref({
username: '',
password: '',
});
const formConfig = ref<FormConfigFace[]>([
{
type: 'input',
prop: 'username',
label: '用户名',
rules: [{ required: true, message: '请输入用户名' }],
},
{
type: 'input',
prop: 'password',
label: '密码',
rules: [{ required: true, message: '请输入密码' }],
attrs: { type: 'password', showPassword: true },
},
]);
const btnConfig = ref<FormButtonFace[]>([
{
type: 1,
name: '登录',
attrs: { type: 'primary', style: { width: '60%' } },
},
]);
const attrsConfig = ref({
labelWidth: 'auto',
labelPosition: 'left',
});
const submitHandle = (data: any) => {
console.log('data', data);
};
</script>
<style scoped lang="scss"></style>