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

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

建议配置unplugin-auto-impor插件

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

import { type DyFormConfig } from '../components/DyForm/DyForm.'
import {ElButton,ElInput,ElCheckbox,ElSelect,ElOption,ElSwitch,ElCheckboxGroup,ElRadioGroup,ElRadio,ElCol,ElDatePicker} from 'element-plus'
export class UserForm {name?: stringorganization?: stringdate1?: stringdate2?: stringdelivery?: booleantype?: []resource?: stringdesc?: stringpersonInfo?: stringdeveloperName?: stringsponsorName?: string
}export function useDyFormConfig(dyForm: any) {const DyFormConfig: DyFormConfig<UserForm>[] = [{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: 'none' }}],next(formData) {if (formData.organization === 'none') {return {next() {return {type: ElInput,formItemConfig: {label: '请输入您所属组织'},}}}} else if (formData.organization === 'company') {return [{type: 'div',formConfig: {style: {width: '100%',display: 'flex'}},formItemConfig: {label: '请选择日期', },children: [{type: ElCol,formConfig: {span: 11},children: [{needFormItem: true,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: [{needFormItem: true,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.resourceconst obj = {'Sponsor': [{type: ElInput,formDataKey: 'sponsorName',formItemConfig: {label: '请输入赞助商名称'},}],'Developer': [{type: ElInput,formDataKey: 'developerName',formItemConfig: {label: '请输入开发商名称'},}],} as Record<string, DyFormConfig[]>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: async () => {const formRef = dyForm!.value!.getFormRef()formRef.value.validate()}},slots: {default: () => '确认'}}]},{type: ElCol,formConfig: {span: 18},children: [{type: ElButton,formConfig: {type: 'danger',onClick: () => {const formRef = dyForm!.value!.getFormRef()formRef.value.resetFields()}},slots: {default: () => '取消'}}]}]}];return {DyFormConfig}
}

使用示例

<script setup lang="ts">
import {DyForm} from './components/Dyform/DyForm.ts';
import { useDyFormConfig, UserForm } from './utils/constant'
import {ref,reactive} from 'vue'const dyForm = ref(null)
const { DyFormConfig }  = useDyFormConfig(dyForm)
const form = ref<UserForm>({name: '',organization: '',date1: '',date2: '',delivery: false,type: [],resource: '',desc: '',personInfo:''
});const rules = {name: [{ required: true, message: 'Please input Activity name', trigger: 'blur' },{ min: 3, max: 5, message: 'Length should be 3 to 5', trigger: 'blur' },],region: [{required: true,message: 'Please select Activity zone',trigger: 'change',},],delivery: [{required: true,message: 'Please select delivery',trigger: 'change',},],organization: [{required: true,message: 'Please select organization',trigger: 'change',},],date1: [{type: 'date',required: true,message: 'Please pick a date',trigger: 'change',},],date2: [{type: 'date',required: true,message: 'Please pick a time',trigger: 'change',},],type: [{type: 'array',required: true,message: 'Please select at least one activity type',trigger: 'change',},],resource: [{required: true,message: 'Please select activity resource',trigger: 'change',},],desc: [{ required: true, message: 'Please input activity form', trigger: 'blur' },],
}const formConfig = reactive({labelWidth: '130px',rules
})</script><template><DyForm ref="dyForm" v-model="form" :dyFormConfig="DyFormConfig" :formConfig="formConfig" />
</template><style scoped>
#app,
html,
body {width: 100vw;height: 100vh;
}</style>

动态组件源码

import { VNode } from "vue"
import { getTypes } from '../../utils/types'
import {type FormInstance,ElForm,ElFormItem } from "element-plus"
import {defineComponent,ref,h} from 'vue'
interface Slots extends Record<string, () => any> {default: () => string | VNode
}
type Next = (formData: Props['modelValue']) => DyFormConfig[] | DyFormConfigexport interface DyFormConfig<T = Props['modelValue']> {type?: anyformConfig?: Record<string, any>formDataKey?: keyof TformItemConfig?: Record<string, any>children?: DyFormConfig<T>[] | stringslots?: Slotsnext?: NextneedFormItem?: boolean
}
interface Props {modelValue: Record<string, any>dyFormConfig: DyFormConfig[]formConfig: Record<string, any>
}
export const DyForm = defineComponent<Props>((props, { emit, expose }) => {expose({getFormRef: (): any & FormInstance => formRef})const rederChild = () => props.dyFormConfig.map((item: DyFormConfig) => {const traverChildren = (child: DyFormConfig['children']): any => {return child && typeof child !== 'string' && child.map(item => {if (typeof item.children === 'string') {return h(item.type, item.children)}const childeVnode = h(item.type, item.formDataKey ?{...item.formConfig,modelValue: props.modelValue[item.formDataKey],'onUpdate:modelValue': (value: any) => emit('update:modelValue', { ...props.modelValue, [item.formDataKey as string]: value })} :item.formConfig,{default:()=>[item.children && traverChildren(item.children), item.slots && renderSlots(item)]})if (item.needFormItem) {return h(ElFormItem, { ...item?.formItemConfig, prop:item?.formConfig?.prop || item.formDataKey , },{default:()=>childeVnode})}return childeVnode})}const renderSlots = (options: DyFormConfig): any => {return Object.keys(options.slots || {}).map((slot: any) => {return options.slots![slot]()})}const render = (options: DyFormConfig): any => {return h(ElFormItem, { ...options.formItemConfig, prop: options?.formConfig?.prop || options.formDataKey },{default:()=>[h(options.type,options.formDataKey ?{...options.formConfig,modelValue: props.modelValue[options.formDataKey],'onUpdate:modelValue': (value: any) => emit('update:modelValue', { ...props.modelValue, [options.formDataKey as string]: value })} : options.formConfig,{default:()=>options.slots ? renderSlots(options) : traverChildren(options.children || [])})]})}const renderNext = (item: DyFormConfig): DyFormConfig[] | [] => {const nextOptions = item.next?.(props.modelValue) as DyFormConfigif (!nextOptions) {console.error(`请检查next函数返回值是否有误,目前返回值为${nextOptions}`)return []}if (getTypes(nextOptions) === 'Object' && nextOptions?.next) {return renderNext(nextOptions)}return Array.isArray(nextOptions) ? nextOptions.map(option => render(option)) : render(nextOptions)}const renderVnode = render(item)return item.next ? [renderVnode,renderNext(item)] : [renderVnode]})const formRef = ref<FormInstance>()return () => {return h(ElForm, {ref: formRef,model: props.modelValue,...props.formConfig}, {default: ()=>rederChild()})}},{props: {modelValue: {type: Object,required:true,},dyFormConfig: {type: Object,required:true,},formConfig: {type: Object,default:()=>{}}},emits: ['update:modelValue']}
)

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/258400.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NestJs的基础使用

初始化项目 创建项目 // 安装脚手架(只需要安装一次,因为这个是全局的) npm i -g nestjs/cli // 创建项目 nest new project-name // (该过程有个选择包管理工具的,我选的yarn)启动项目 yarn run start:dev // 可以在浏览器访问localhost:3000 输出helloWorld控制器和路由 …

MDK官网如何下载stm32支持包

网站&#xff1a;https://www.keil.com/demo/eval/arm.htm 1 2 3点这个下载

4.Java程序设计-基于springboot得在线考试系统

编程技术交流、源码分享、模板分享、网课分享 企鹅&#x1f427;裙&#xff1a;772162324 摘要&#xff1a; 本文设计并实现了一款基于Spring Boot框架的在线考试系统小程序。随着远程学习和在线教育的普及&#xff0c;对于灵活、便捷的在线考试系统的需求逐渐增加。该小程序…

视频推拉流直播点播EasyDSS平台点播文件加密存储的实现方法

视频推拉流直播点播系统EasyDSS平台&#xff0c;可提供流畅的视频直播、点播、视频推拉流、转码、管理、分发、录像、检索、时移回看等功能&#xff0c;可兼容多操作系统&#xff0c;还能支持CDN转推&#xff0c;具备较强的可拓展性与灵活性&#xff0c;在直播点播领域具有广泛…

低代码与MES:智能制造的新篇章

一、引言 随着工业4.0和智能制造的兴起&#xff0c;企业对于生产过程的数字化、智能化需求日益迫切。制造执行系统&#xff08;MES&#xff09;作为连接计划层与控制层的关键信息系统&#xff0c;在提升生产效率、优化资源配置、保障产品质量等方面发挥着重要作用。然而&#…

微信小程序 - 创建 ZIP 压缩包

微信小程序 - 创建 ZIP 压缩包 场景分享代码片段导入 JSZip创建ZIP文件追加写入文件测试方法参考资料 场景 微信小程序只提供了解压ZIP的API&#xff0c;并没有提供创建ZIP的方法。 当我们想把自己处理好的保存&#xff0c;打包ZIP保存下来时就需要自己实现了。 分享代码片段…

2024 IEEE Fellow名单出炉(附部分华人获奖理由)

11月22日&#xff0c;美国电子电气工程师学会&#xff08;IEEE&#xff09;公布了 2024 年新晋 Fellow 名单。因为这些学术大咖也是访问学者、博士后研究人员及联合培养博士心仪的目标导师&#xff0c;故知识人网小编特编辑此文以飨读者。 IEEE&#xff0c;全称Institute of El…

Mybatis XML 多表查询

这篇需结合 <<Mybatis XML 配置文件>>那一篇博客一起看 工作中尽量避免使用多表查询,尤其是对性能要求非常高的项目 我们之前建了个用户表(代码在Mybatis XML配置文件那篇博客里),这次再建一个文章表,代码如下 : -- 创建⽂章表 DROP TABLE IF EXISTS articleinf…

【EI会议征稿】2024年粤港澳大湾区数字经济与人工智能国际学术会议(DEAI2024)

2024年粤港澳大湾区数字经济与人工智能国际学术会议(DEAI2024) 2024 Guangdong-Hong Kong-Macao Greater Bay Area International Conference on Digital Economy and Artificial Intelligence(DEAI2024) 2024年粤港澳大湾区数字经济与人工智能国际学术会议(DEAI2024)由广东科…

Spring Cloud + Vue前后端分离-第4章 使用Vue cli 4搭建管理控台

Spring Cloud Vue前后端分离-第4章 使用Vue cli 4搭建管理控台 4-1 使用vue cli创建admin项目 Vue 简介 Vue作者尤雨溪在google工作时&#xff0c;最早只想研究angular的数据绑定功能&#xff0c;后面觉得这个小功能很好用&#xff0c;有前景&#xff0c;就再扩展&#xff…

高效率完成工作任务的工具推荐,待办清单类工具用哪个

日常办公中&#xff0c;领导常常会以高效率完成工作任务来评判一个员工是否敬业&#xff0c;是否在工作岗位上兢兢业业。而想要高效率完成工作也是有技巧的&#xff0c;如提前对各项工作做好规划&#xff0c;制定工作条目清单&#xff0c;跟进好工作任务的进展等等。 职场办公…

Python文件操作(txt + xls + json)

文章目录 简介1、使用with_open读取和保存&#xff1a;.txt .bin&#xff08;二进制文本&#xff09;1.1、with open语句详解1.1、项目实战 2、使用pandas读取和保存&#xff1a;.xls .xlsx2.1、pandas简介2.2、环境配置2.3、项目实战 3、 使用json.dump读取和保存&#xff1…