Vue3+ts----根据配置项,动态生成表单
这里使用的UI框架是ElementPlus,更换其他组件直接更换constant.ts中的type配置和对应的Form组件即可.
思路:
1.这里需要使用h函数方便控制要渲染的表单
2.传递type作为组件或html元素进行渲染,
3.默认包一层El-form,每个数组项会包一个El formItem
4.传入formDataKey,对绑定的表单项进行映射
export const DyForm = defineComponent((props, { emit }) => {const form = computed({get() {return props.modelValue},set() {return false}})const rederChild = () => props.dyFormConfig.map((item: any) => {const traverChildren = (child: any[]): any[] => {return child.map(item => {if (typeof item.children === 'string') {return h(item.type, item.children)}return h(item.type, item.formDataKey ?{...item.formConfig,modelValue: form.value[item.formDataKey],'onUpdate:modelValue': (value: any) => emit('update:modelValue', { ...props.modelValue, [item.formDataKey]: value })} : item.formConfig,{ default: () => item.children && traverChildren(item.children) })})}const renderSlots = () => {return Object.keys(item.slots).map((slot: any) => {return item.slots[slot]()})}return h(ElFormItem, { ...item.formItemConfig }, [h(item.type,item.formDataKey ?{...item.formConfig,modelValue: form.value[item.formDataKey],'onUpdate:modelValue': (value: any) => emit('update:modelValue', { ...props.modelValue, [item.formDataKey]: value })} : item.formConfig,{default: () => item.slots ? renderSlots() : traverChildren(item.children || [])})])})return () => {return h(ElForm, {model: form,...props.formConfig}, {default: rederChild()})}},// 其他选项,例如声明 props 和 emits。{props: ['modelValue', 'onSubmit', 'dyFormConfig', 'formConfig'],emits: ['update:modelValue']}
)
创建一个静态文件存放配置
export class UserForm {name?: stringorganization?: stringdate1?: stringdate2?: stringdelivery?: booleantype?: []resource?: stringdesc?: string
}
interface FormItemConfig<T>{type:anyformConfig?: Record<string, any>formDataKey?: T,formItemConfig?: Record<string, any>children?: FormItemConfig<T>[] | stringslots?:Record<string,()=>any>
}export const dyFormConfig:FormItemConfig<keyof UserForm>[] = [{type: ElInput,formItemConfig: {label: '请输入姓名'},formConfig: {},formDataKey:'name'},{type: ElSelect,formItemConfig: {label: '请选择组织'},formConfig: {placeholder: '请选择您的组织'},formDataKey: 'organization',children: [{type: ElOption,formConfig: { label: '个人', value: 'person' }},{type: ElOption,formConfig: { label: '公司', value: 'company' }}]},{type: 'div',formItemConfig: {label: "请选择时间"},formConfig: {style: 'width:100%;display:flex;',},children: [{type: ElCol,formConfig: {span: 11},formItemConfig: {label: '请选择日期'},children: [{type: ElDatePicker,formDataKey: 'date1',formConfig: {type: 'date',placeholder: '选择日期',style: 'width: 100%'}}]},{type: ElCol,formConfig: {span: 2,class: 'text-center'},children: [{type: 'span',formConfig: {class: 'text-gray-500'},children:'-'}]},{type: ElCol,formConfig: {span: 11,class: 'text-center'},children: [{type: ElDatePicker,formDataKey: 'date2',formConfig: {type: 'date',placeholder: '选择日期',style: 'width: 100%'}}]}]},{type: ElButton,formConfig: {type: 'danger'},slots: {default: () => '确认'}}
];
使用示例
<script setup lang="ts">
import { DyForm } from './components/DyForm/DyForm';
import { dyFormConfig, UserForm } from './utils/constant'const form = ref<UserForm>({name: '',organization: '',date1: '',date2: '',delivery: false,type: [],resource: '',desc: ''
});
const onSubmit = () => {console.log(form.value);
};
const formConfig = {labelWidth: '120px',style: {width:'800px'}
};</script><template><DyForm v-model="form" :onSubmit="onSubmit" :dyFormConfig="dyFormConfig" :formConfig="formConfig" />
</template>