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.mzph.cn/news/207838.shtml

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

相关文章

PHP基础 - 输入输出

在 PHP 中,有多种方法可以用来输出内容。下面是其中的几种: 1、echo: 这是最常见的输出语句之一,可以输出一个或多个字符串。它是一个语言结构,可以省略括号。使用示例如下: <?php // 使用 echo 语句输出一个字符串 echo "Hello, world!\n";// 可以使用…

虚拟仪器的外部接口设计

虚拟仪器的外部接口设计需要考虑多个因素。以下是一些可能涉及的方面&#xff1a; 接口类型&#xff1a;根据实际需要&#xff0c;选择不同类型的接口。例如&#xff0c;计算机内部插卡式接口有isa接口和pci接口&#xff0c;适用于中小型测试系统。计算机外部通用总线接口有增…

P1035 [NOIP2002 普及组] 级数求和题解

题目 已知&#xff1a;.显然对于任意一个整数 k&#xff0c;当 n 足够大的时候,Sn​>k。 现给出一个整数k&#xff0c;要求计算出一个最小的n&#xff0c;使得Sn​>k。 输入输出样例 输入 1 输出 2 代码 #include<iostream> using namespace std; int mai…

周周爱学习之Redis重点总结

redis重点总结 在正常的业务流程中&#xff0c;用户发送请求&#xff0c;然后到缓存中查询数据。如果缓存中不存在数据的话&#xff0c;就会去数据库查询数据。数据库中有的话&#xff0c;就会更新缓存然后返回数据&#xff0c;数据库中也没有的话就会给用户返回一个空。 1.缓…

AIGC创作系统ChatGPT网站源码,Midjourney绘画,GPT联网提问/即将支持TSS语音对话功能

一、AI创作系统 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI…

leetcode 面试题 02.02. 返回倒数第k个节点

提建议就是&#xff0c;有些题还是有联系的&#xff0c;建议就收看完 876.链表的中间节点&#xff08;http://t.csdnimg.cn/7axLa&#xff09;&#xff0c;再将这一题联系起来 面试题 02.02. 返回倒数第k个节点 题目&#xff1a; 实现一种算法&#xff0c;找出单向链表中倒数第…

这些接口自动化测试工具如果不知道,就真out了!

一、Postman Postman是一款广受欢迎的API测试工具&#xff0c;除了手动发送HTTP请求的基本功能&#xff0c;它还提供了自动化测试和脚本测试的功能&#xff0c;非常适合进行HTTP接口的自动化测试。 二、Rest-Assured Rest-Assured是一个Java库&#xff0c;专为REST服务的测试…

java中守护线程的特点是什么?

Java 中守护线程&#xff08;Daemon Thread&#xff09;的特点如下&#xff1a; 随主线程结束而结束&#xff1a; 守护线程是在后台运行的线程&#xff0c;当所有的用户线程都执行完毕后&#xff0c;即主线程结束&#xff0c;守护线程会随之被终止。它不会阻止 JVM 退出。 不执…

C++异常剖析

什么是异常&#xff1f; 在程序运行的过程中&#xff0c;我们不可能保证我们的程序百分百不出现异常和错误&#xff0c;那么出现异常时该怎么报错&#xff0c;让我们知道是哪个地方错误了呢? C中就提供了异常处理的机制。 一、异常处理的关键字 &#xff08;1&#…

联想电脑重装系统Win10步骤和详细教程

联想电脑拥有强大的性能&#xff0c;很多用户办公都喜欢用联想电脑。有使用联想电脑的用户反映系统出现问题了&#xff0c;想重新安装一个正常的系统&#xff0c;但是不知道重新系统的具体步骤。接下来小编详细介绍给联想电脑重新安装Win10系统系统的方法步骤。 推荐下载 系统之…

Codeforces Round 913 (Div. 3) (A-G)

后天就是 I C P C ICPC ICPC杭州站了&#xff0c;今天把之前做的 d i v 3 div3 div3题补一下&#xff0c;打完这场杭州站这赛季除了 E C F i n a l EC\,\,Final ECFinal就结束了&#xff0c;以后应该要多打 c f cf cf比赛练习保持手感&#xff0c;争取下赛季冲一下金牌。 感觉这…

客厅颜值担当:木饰面电视背景墙设计。福州中宅装饰,福州装修

你是否也为客厅的装修设计而烦恼&#xff1f;现在&#xff0c;我为你带来一款高颜值的木饰面电视背景墙设计&#xff0c;它将是你客厅的亮点所在。 1️⃣ 确定背景墙的尺寸和位置 首先&#xff0c;你需要确定背景墙的尺寸和位置&#xff0c;这取决于你家电视的大小和放置位置。…

重新认识Word——多级列表和项目符号

重新认识Word——多级列表和项目符号 多级列表没有运用标题样式但标题格式统一 正式公本文书项目符号和自动编号项目符号自动编号软回车重新起头开始编号解决编号与文本距离过大问题 之前我们重新认识了Word里面的样式&#xff0c;现在的情况就是&#xff0c;我的一些文字已经运…

Python Authlib库:构建安全可靠的身份验证系统

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在现代应用程序中&#xff0c;安全性是至关重要的&#xff0c;特别是在处理用户身份验证时。Authlib库为Python开发者提供了一套强大的工具&#xff0c;用于简化和增强身份验证和授权流程。本文将深入探讨Authli…

因小失大,一个普通的postman多接口顺序执行,让项目瘫痪了一天

Runner的使用 postman不仅可以单独运行某个接口&#xff0c;postman的 Runner模块可以运行多个接口&#xff0c;可以实现真正意义上的自动化接口测试 Runner的主要功能如下 按顺序调用接口&#xff0c;执行用例&#xff1b; 批量发送请求&#xff1b; 对接口数据进行参数化…

yarn和npm的区别

2023-12-8 yarn和npm的区别 是常用的包管理工具&#xff0c;用于node.js项目中安装、管理、和更新依赖项 有以下几个区别&#xff1a; 性能和速度&#xff1a;在包的安装和下载方面&#xff0c;yarn比npm更快速&#xff0c;yarn通过并行下载和缓存等优化策略&#xff0c;可以…

DreamPlace 的下载安装与使用

DreamPlace 是一款芯片放置工具&#xff0c;用于宏单元&#xff08;macro&#xff09;和标准单元&#xff08;Standard Cell&#xff09;的放置以及布线&#xff0c;并计算 HPWL、Overlap 等用于衡量芯片性能的参数。 一、环境 1. 系统环境&#xff1a;Ubuntu 20.04 DreamPla…

我获取股票和期货数据的常用函数

记录一下获取数据所使用的函数&#xff0c;以防止遗忘和方便查找。 # 获取掘金的数据 # 需要打开并登陆掘金终端 def get_data_juejin(symbol"bu2112",start"2021-8-1",end"2021-8-30 23:00:00",frequency"1800s",fields"eob,sy…

MySQL视图介绍与实验练习

文章目录 1. MySQL 中的视图&#xff08;View&#xff09;简介1.1 视图的基本概念&#xff1a;1.2 创建视图&#xff1a;1.3 查看视图&#xff1a;1.4 更新视图&#xff1a;1.5 删除视图&#xff1a;1.6 视图的嵌套&#xff1a;1.7 权限管理&#xff1a;1.8 检查视图信息&#…

vuepress-----15、md用法进阶

vuepress markdown说明文档 https://www.vuepress.cn/guide/markdown.html # 示例&#xff1a;封装countUp.js为Vue组件 https://github.com/inorganik/countUp.js https://inorganik.github.io/countUp.js/ # 安装 yarn add countup.js# 创建vue文件 全局Vue组件存放位置…