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.pswp.cn/news/207838.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/207838.shtml
英文地址,請注明出處:http://en.pswp.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組件存放位置…