Vue3+ts----根据配置项,动态生成表单

这里使用的UI框架是ElementPlus,更换其他组件直接更换constant.ts中的type配置和对应的Form组件即可.
思路:
1.这里需要使用h函数方便控制要渲染的表单
2.传递type作为组件或html元素进行渲染,
3.默认包一层El-form,每个数组项会包一个El formItem
4.传入formDataKey,对绑定的表单项进行映射
5.通过next函数决定渲染的表单,next函数接收参数即表单数据

创建一个静态文件存放配置

import {
    FormItemConfig } from "../components/DyForm/DyForm"

export class UserForm {
   
    name?: string
    organization?: string
    date1?: string
    date2?: string
    delivery?: boolean
    type?: []
    resource?: string
    desc?: string
    personInfo?: string
    developerName?: string
    sponsorName?:string
}

export function useDyFormConfig(onsubmit=()=>null){
   
    const dyFormConfig: FormItemConfig[] = [
        {
   
            type: ElInput,
            formItemConfig: {
   
                label: '请输入姓名'
            },
            formConfig: {
   
                placeholder: '请输入姓名'
            },
            formDataKey: 'name'
        },
        {
   
            type: ElSelect,
            formItemConfig: {
   
                label: '请选择组织'
            },
            formConfig: {
   
                placeholder: '请选择您的组织'
            },
            formDataKey: 'organization',
            children: [
                {
   
                    type: ElOption,
                    formConfig: {
    label: '个人', value: 'person' },
    
                },
                {
   
                    type: ElOption,
                    formConfig: {
    label: '公司', value: 'company' }
                },
                {
   
                    type: ElOption,
                    formConfig: {
    label: '无', value: '' }
                }
            ],
            next(formData) {
   
                if (!formData.organization) {
   
                    return []
                } else if (formData.organization === 'company') {
   
                    return [
                        {
   
                            type: 'div',
                            formConfig: {
   
                                style: {
   
                                    width: '100%',
                                    display: 'flex'
                                }
                            },
                            formItemConfig: {
   
                                label: '请选择日期'
                            },
                            children: [
                                {
   
                                    type: ElCol,
                                    formConfig: {
   
                                        span: 11
                                    },
                                    children: [
                                        {
   
                                            type: ElDatePicker,
                                            formDataKey: 'date1',
                                            formConfig: {
   
                                                type: "date",
                                                placeholder: "请选择进入公司日期",
                                                style: "width: 100%"
                                            },
                                        }
                                    ]
                                },
                                {
   
                                    type: ElCol,
                                    formConfig: {
   
                                        span: 2
                                    },
                                    children: [
                                        {
   
                                            type: 'span',
                                            children: '-'
                                        }
                                    ]
                                },
                                {
   
                                    type: ElCol,
                                    formConfig: {
   
                                        span: 11
                                    },
                                    children: [
                                        {
   
                                            type: ElDatePicker,
                                            formDataKey: 'date2',
                                            formConfig: {
   
                                                type: "date",
                                                placeholder: "请选择毕业日期",
                                                style: "width: 100%"
                                            },
                                        }
                                    ]
                                },
    
                            ],
                            next(formData) {
   
                                console.log(formData)
                                return [{
   
                                    type: ElInput,
                                    formItemConfig: {
   
                                        label: '请输入个人信息'
                                    },
                                    formConfig: {
   
                                        placeholder: '请输入个人信息'
                                    }
                                }]
                            }
                        },
                    ]
                } else {
   
                    return [{
   
                        type: ElInput,
                        formDataKey: 'personInfo',
                        formItemConfig: {
   
                            label: '请输入个人信息'
                        },
                        formConfig: {
   
                            placeholder: '请输入个人信息'
                        }
                    }]
                }
    
            }
        },
        {
   
            type: ElSwitch,
            formDataKey: 'delivery',
            formItemConfig: {
   
                label: 'Instant delivery'
            }
        },
        {
   
            type: ElCheckboxGroup,
            formDataKey: 'type',
            formItemConfig: {
   
                label: 'Activity type',
            },
            children: [
                {
    type: ElCheckbox,slots:{
   default:()=>'活动1'} ,formConfig:{
    name: 'type',label:'活动1'} },
                {
    type: ElCheckbox, slots:{
   default:()=>'活动2'} ,formConfig:{
    name: 'type',label:'活动2'}},
                {
    type: ElCheckbox, slots:{
   default:()=>'活动3'} ,formConfig:{
    name: 'type',label:'活动3'}},
                {
    type: ElCheckbox,slots:{
   default:()=>'活动4'} ,formConfig:{
    name: 'type',label:'活动4'}}
            ],
    
        },
        {
   
            type: ElRadioGroup,
            formDataKey: 'resource',
            formItemConfig: {
   
                label: 'Resources'
            },
            children: [
                {
   
                    type: ElRadio,
                    formConfig: {
   
                        label: 'Sponsor'
                    }
                },
                {
   
                    type: ElRadio,
                    formConfig: {
   
                        label: 'Developer'
                    }
                },
            ],
            next(formData) {
   
                const resource = formData.resource 
                const obj = {
   
                    'Sponsor': [
                        {
   
                            type: ElInput,
                            formDataKey:'sponsorName',
                            formItemConfig: {
   
                                label:'请输入赞助商名称'
                            },
                        }
                    ],
                    'Developer': [
                        {
   
                            type: ElInput,
                            formDataKey:'developerName',
                            formItemConfig: {
   
                                label:'请输入开发商名称'
                            },
                        }
                    ],
                }as Record<string,FormItemConfig[]>
                if (!resource) {
   
                    return []
                } else {
   
                    return obj[resource]
                }
            },
        },
        {
   
            type: ElInput,
            formConfig: {
   
                type: 'textarea'
            },
            formDataKey: 'desc',
            formItemConfig: {
   
                label: 'Activity form'
            }
        },
        {
   
            type: 'div',
            formConfig: {
   
                style: {
   
                    width: "100%",
                    display: "flex"
                }
            },
            children: [
                {
   
                    type: ElCol,
                    formConfig: {
   
                        span: 6
                    },
                    children: [
                        {
   
                            type: ElButton,
                            formConfig: {
   
                                type: 'warning',
                                onClick: onsubmit
                            },
                            slots: {
   
                                default: () => '确认'
                            }
                        }
                    ]
                },
                {
   
                    type: ElCol,
                    formConfig: {
   
                        span: 18
                    },
                    children: [
                        {
   
                            type: ElButton,
                            formConfig: {
   
                                type: 'danger',
                                onClick: () => {
   
                                    console.log('取消')
                                }
                            },
                            slots: {
   
                                default: () => '取消'
                            }
                        }
                    ]
                }
            ]
        }
    ];
    return {
   
        dyFormConfig
    }
}

使用示例

<script setup lang="ts">
import {
    DyForm } from './components/DyForm/DyForm';
import {
    useDyFormConfig, UserForm } from './utils/constant'
const {
    dyFormConfig }  = useDyFormConfig()
const form = ref<UserForm>({
   
    name: '',
    organization: '',
    date1: '',
    date2: '',
    delivery: false,
    type: [],
    resource: '',
    desc: '',
    personInfo:''
});

watch(form, (n:UserForm) => {
   
  console.log(n)
})
const formConfig = {
   
  labelWidth: '120px',
};



</script>

<template>
    <DyForm ref="dyForm" v-model="form" :dyFormConfig="dyFormConfig" :formConfig="formConfig" />
</template>

<style scoped>
#app,
html,
body {
   
    width: 100vw;
    height: 100vh;
}

</style>

动态组件源码

export interface FormItemConfig<T = Props['modelValue']> {
   
  type: any
  formConfig?: Record<string, any>
  formDataKey?: keyof T
  formItemConfig?: Record<string, any>
  children?: FormItemConfig<T>[] | string
  slots?: Record<string, () => any>
  next?: (formData: T) => FormItemConfig<T>[]
}
interface Props {
   
  modelValue: Record<string, any>
  dyFormConfig:FormItemConfig[]
  formConfig:Record<string, any>
}

export const DyForm = defineComponent<Props>(
  (props, {
    emit,expose }) => {
   
    expose({
   
      getFormData: () => {
   
        return props.modelValue
      }
    })
    const form = computed({
   
      get() {
   
        return props.modelValue
      },
      set() {
   
        return false
      }
    })
    const rederChild = () => props.dyFormConfig.map((item:FormItemConfig) => {
   
      const traverChildren = (child: FormItemConfig['children']): any => {
   
        return child && typeof child !== 'string' && 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 as string]: value })
              } :
              item.formConfig,
              [item.children && traverChildren(item.children), item.slots && renderSlots(item)])
          }
        )
      }
      const renderSlots = (options: FormItemConfig): any => {
   
        return Object.keys(options.slots || {
   }).map((slot: any) => {
   
          return options.slots![slot]()
        })
      }
      const render = (options: FormItemConfig): any => {
   
        return h(ElFormItem, {
    ...options.formItemConfig }, [
          h(
            options.type,
            options.formDataKey ?
              {
   
                ...options.formConfig,
                modelValue: form.value[options.formDataKey],
                'onUpdate:modelValue': (value: any) => emit('update:modelValue', {
    ...props.modelValue, [options.formDataKey as string]: value })
              } : options.formConfig,
            options.slots ? renderSlots(options) : traverChildren(options.children || [])
          )
        ])
      }

      if (!item.next) {
   
        return render(item)
      } else {
   
        const nextOptions = item.next(props.modelValue)
        if (!nextOptions || !Array .isArray(nextOptions)) {
   
          console.error('请检查next函数返回值是否有误')
          return item.children && render(item)
        }
        return [item.children && render(item), ...nextOptions.map((options: FormItemConfig) => render(options))]
      }

    })
    return () => {
   
      return h(ElForm, {
   
        model: form,
        ...props.formConfig
      }, {
   
        default: rederChild()
      })
    }
  },
  {
   
    props: ['modelValue',  'dyFormConfig', 'formConfig'],
    emits: ['update:modelValue']
  }
)

结果展示:
在这里插入图片描述

相关推荐

  1. vue生成动态

    2023-12-06 00:58:36       49 阅读
  2. vue3+ts实现一个组件

    2023-12-06 00:58:36       27 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2023-12-06 00:58:36       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2023-12-06 00:58:36       101 阅读
  3. 在Django里面运行非项目文件

    2023-12-06 00:58:36       82 阅读
  4. Python语言-面向对象

    2023-12-06 00:58:36       91 阅读

热门阅读

  1. 2023大厂高频面试题之Vue篇(3)

    2023-12-06 00:58:36       65 阅读
  2. SQL Server对象类型(7)——4.7.触发器(Trigger)

    2023-12-06 00:58:36       59 阅读
  3. vue el-cascader 省市区封装及使用

    2023-12-06 00:58:36       57 阅读
  4. Go函数和方法之间有什么区别

    2023-12-06 00:58:36       63 阅读
  5. 大厂面试整理

    2023-12-06 00:58:36       79 阅读
  6. Linux-hid

    2023-12-06 00:58:36       45 阅读
  7. 一文详解Docker数据卷(volume)

    2023-12-06 00:58:36       59 阅读
  8. 安装vscode插件与安装vue项目

    2023-12-06 00:58:36       63 阅读
  9. webpack对项目进行优化

    2023-12-06 00:58:36       60 阅读
  10. CoreDns在K8S中的作用原理概述

    2023-12-06 00:58:36       51 阅读
  11. expect自动化交互

    2023-12-06 00:58:36       59 阅读
  12. Docker 安装 Nacos

    2023-12-06 00:58:36       56 阅读