Vue3 + Element Plus 动态表单实现

 完整代码

<template><div class="dynamic-form-container"><el-formref="dynamicFormRef":model="formData":rules="formRules"label-width="auto"label-position="top"v-loading="loading"><!-- 动态渲染表单字段 --><template v-for="field in formConfig" :key="field.name"><!-- 输入框 --><el-form-itemv-if="field.type === 'input'":label="field.label":prop="field.name":rules="generateFieldRules(field)"><el-inputv-model="formData[field.name]":placeholder="field.placeholder || `请输入${field.label}`":type="field.inputType || 'text'":clearable="field.clearable !== false"/></el-form-item><!-- 下拉选择 --><el-form-itemv-else-if="field.type === 'select'":label="field.label":prop="field.name":rules="generateFieldRules(field)"><el-selectv-model="formData[field.name]":placeholder="field.placeholder || `请选择${field.label}`":clearable="field.clearable !== false"style="width: 100%"><el-optionv-for="option in field.options":key="option.value":label="option.label":value="option.value"/></el-select></el-form-item><!-- 单选框 --><el-form-itemv-else-if="field.type === 'radio'":label="field.label":prop="field.name":rules="generateFieldRules(field)"><el-radio-group v-model="formData[field.name]"><el-radiov-for="option in field.options":key="option.value":label="option.value">{{ option.label }}</el-radio></el-radio-group></el-form-item><!-- 复选框 --><el-form-itemv-else-if="field.type === 'checkbox'":label="field.label":prop="field.name":rules="generateFieldRules(field)"><el-checkbox-group v-model="formData[field.name]"><el-checkboxv-for="option in field.options":key="option.value":label="option.value">{{ option.label }}</el-checkbox></el-checkbox-group></el-form-item><!-- 日期选择器 --><el-form-itemv-else-if="field.type === 'date'":label="field.label":prop="field.name":rules="generateFieldRules(field)"><el-date-pickerv-model="formData[field.name]":type="field.dateType || 'date'":placeholder="field.placeholder || `请选择${field.label}`"style="width: 100%"/></el-form-item><!-- 开关 --><el-form-itemv-else-if="field.type === 'switch'":label="field.label":prop="field.name"><el-switch v-model="formData[field.name]" /></el-form-item><!-- 自定义插槽 --><el-form-itemv-else-if="field.type === 'slot'":label="field.label":prop="field.name":rules="generateFieldRules(field)"><slot :name="field.slotName" :field="field" :model="formData" /></el-form-item></template><el-form-item><el-button type="primary" @click="submitForm">提交</el-button><el-button @click="resetForm">重置</el-button></el-form-item></el-form></div>
</template><script setup>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'// 表单引用
const dynamicFormRef = ref()// 加载状态
const loading = ref(false)// 表单数据
const formData = ref({})// 表单验证规则
const formRules = ref({})// 表单配置(从后端获取)
const formConfig = ref([// 默认配置,实际会被后端数据覆盖{name: 'username',label: '用户名',type: 'input',required: true,placeholder: '请输入用户名'}
])// 模拟从后端获取表单配置
const fetchFormConfig = async () => {try {loading.value = true// 这里替换为实际的API调用const response = await mockApiGetFormConfig()formConfig.value = response.data.fields// 初始化表单数据initFormData()// 生成验证规则generateFormRules()} catch (error) {ElMessage.error('获取表单配置失败: ' + error.message)} finally {loading.value = false}
}// 初始化表单数据
const initFormData = () => {const data = {}formConfig.value.forEach(field => {// 根据字段类型设置默认值switch (field.type) {case 'checkbox':data[field.name] = field.defaultValue || []breakcase 'switch':data[field.name] = field.defaultValue || falsebreakdefault:data[field.name] = field.defaultValue || ''}})formData.value = data
}// 生成表单验证规则
const generateFormRules = () => {const rules = {}formConfig.value.forEach(field => {if (field.required || field.rules) {rules[field.name] = generateFieldRules(field)}})formRules.value = rules
}// 生成单个字段的验证规则
const generateFieldRules = (field) => {const rules = []// 必填规则if (field.required) {rules.push({required: true,message: field.message || `${field.label}不能为空`,trigger: field.trigger || 'blur'})}// 自定义规则if (field.rules && Array.isArray(field.rules)) {rules.push(...field.rules)}// 类型校验if (field.type === 'input' && field.inputType === 'email') {rules.push({type: 'email',message: '请输入正确的邮箱格式',trigger: ['blur', 'change']})}return rules
}// 提交表单
const submitForm = async () => {try {// 表单验证await dynamicFormRef.value.validate()// 这里替换为实际的提交APIconst response = await mockApiSubmitForm(formData.value)ElMessage.success('提交成功')console.log('表单数据:', formData.value)console.log('服务器响应:', response)// 可以在这里处理提交成功后的逻辑} catch (error) {if (error instanceof Error) {ElMessage.error('表单验证失败: ' + error.message)}}
}// 重置表单
const resetForm = () => {dynamicFormRef.value.resetFields()
}// 模拟API获取表单配置
const mockApiGetFormConfig = () => {return new Promise((resolve) => {setTimeout(() => {resolve({data: {fields: [{name: 'username',label: '用户名',type: 'input',required: true,placeholder: '请输入用户名',maxlength: 20},{name: 'password',label: '密码',type: 'input',inputType: 'password',required: true,placeholder: '请输入密码',rules: [{ min: 6, max: 18, message: '密码长度在6到18个字符', trigger: 'blur' }]},{name: 'gender',label: '性别',type: 'select',required: true,options: [{ label: '男', value: 'male' },{ label: '女', value: 'female' },{ label: '其他', value: 'other' }]},{name: 'hobbies',label: '兴趣爱好',type: 'checkbox',options: [{ label: '游泳', value: 'swimming' },{ label: '跑步', value: 'running' },{ label: '阅读', value: 'reading' }]},{name: 'subscribe',label: '订阅通知',type: 'switch',defaultValue: true},{name: 'birthday',label: '出生日期',type: 'date',dateType: 'date',required: true}]}})}, 800)})
}// 模拟API提交表单
const mockApiSubmitForm = (data) => {return new Promise((resolve) => {setTimeout(() => {resolve({ code: 200, message: 'success', data })}, 500)})
}// 组件挂载时获取表单配置
onMounted(() => {fetchFormConfig()
})
</script><style scoped>
.dynamic-form-container {max-width: 800px;margin: 0 auto;padding: 20px;
}
</style>

后端API数据结构建议

后端API返回的表单配置建议采用如下JSON格式:

{"code": 200,"message": "success","data": {"fields": [{"name": "username","label": "用户名","type": "input","required": true,"placeholder": "请输入用户名","inputType": "text","maxlength": 20,"rules": [{"pattern": "^[a-zA-Z0-9_]+$","message": "只能包含字母、数字和下划线"}]},{"name": "gender","label": "性别","type": "select","required": true,"options": [{"label": "男","value": "male"},{"label": "女","value": "female"}]},{"name": "subscribe","label": "订阅通知","type": "switch","defaultValue": true}]}
}

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

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

相关文章

Mac修改hosts文件方法

Mac修改hosts文件方法 在 macOS 上修改 hosts 文件需要管理员权限 步骤 1&#xff1a;打开终端 通过 Spotlight 搜索&#xff08;Command 空格&#xff09;输入 Terminal&#xff0c;回车打开。或进入 应用程序 > 实用工具 > 终端。 步骤 2&#xff1a;备份 hosts 文件…

深度学习—BP神经网络

文章目录 [TOC](文章目录) 一、基本概念二、 网络结构三、BP神经网络的原理总结特点&#xff1a;应用场景优缺点 一、基本概念 BP 神经网络&#xff08;Backpropagation Neural Network&#xff09;是一种基于误差反向传播算法的多层前馈神经网络&#xff0c;由输入层、隐藏层…

Spring AI(6)——向量存储

向量数据库是一种特殊类型的数据库&#xff0c;在 AI 应用中发挥着至关重要的作用。 在向量数据库中&#xff0c;查询与传统关系型数据库不同。它们执行的是相似性搜索&#xff0c;而非精确匹配。当给定一个向量作为查询时&#xff0c;向量数据库会返回与该查询向量“相似”的…

Qt功能区:简介与安装

Qt功能区 1. 功能区简介2. SARibbon2.1 简介2.2 编译与安装采用CMake-gui进行编译采用VS进行编译安装与使用 Qt 官方不支持 Ribbon 风格&#xff08;Ribbon UI 风格是微软开创的&#xff0c;具有专利许可协议&#xff0c;许可协议对从构建 UI 的指令到每个按钮间的空格数都做了…

iOS safari和android chrome开启网页调试与检查器的方法

手机开启远程调试教程&#xff08;适用于 Chrome / Safari&#xff09; 前端移动端调试指南&#xff5c;适用 iPhone 和 Android&#xff5c;WebDebugX 出品 本教程将详细介绍如何在 iPhone 和 Android 手机上开启网页检查器&#xff0c;配合 WebDebugX 实现远程调试。教程包含…

Golang企业级商城高并发微服务实战

Golang企业级商城高并发微服务实战包含内容介绍&#xff1a; 从零开始讲了百万级单体高并发架构、千万级微服务架构&#xff0c;其中包含Rpc实现微服务、微服务的跨语言调用jsonrpc和protobuf、protobuf的安装、protobuf高级语法、protobuf结合Grpc实现微服务实战、微服务服务…

实现可靠的 WebSocket 连接:心跳与自动重连的最佳实践

概览 本文将手把手教你如何从零编写一个可用于直播或在线聊天的 WSocket 类&#xff0c;依次实现连接建立、心跳检测、断线重连、消息收发以及资源清理等功能。我们将结合 WebSocket API 的标准用法、心跳保持 和 重连策略&#xff0c;并充分运用现代 JavaScript 语法&#xf…

UEFI Spec 学习笔记---33 - Human Interface Infrastructure Overview(1)

33 - Human Interface Infrastructure Overview 本章节主要用于介绍Human Interface Infrastructure&#xff08;HII&#xff09;架构介绍&#xff0c;描述如何通过 HII 来管理用户的输入&#xff0c;以及描述在 UEFI spec 中涉及 HII 相关的 Protocol、function 和类型定义。…

ip命令详解

控制网卡的硬件状态 ip link set ens36 down ip link set ens36 up 修改网卡名称&#xff08;临时&#xff09; ip link set ens36 down ip link set ens36 name xxx 修改网卡的mac地址 ip link set ens36 down ip link set xxx name ens36 查看ip的addr ip addr show ip ad…

hadoop中了解yarm

Hadoop中的YARN&#xff08;Yet Another Resource Negotiator&#xff09;是一种新的Hadoop资源管理器&#xff0c;是一个通用资源管理系统&#xff0c;可为上层应用提供统一的资源管理和调度。以下是其相关介绍&#xff1a; 核心思想 将JobTracker的资源管理和作业调度/监控功…

做好的QT软件,换一个笔记本打开后发现字体很小,部分字体还被控件遮挡

出现这种情况的原因主要是屏幕的DPI&#xff08;每英寸点数&#xff09;不同。Qt中控件的大小单位为像素&#xff0c;在高DPI下&#xff0c;控件会变小&#xff0c;低DPI下控件会变大。而Qt中字体的单位默认为磅&#xff0c;无论在什么显示器上显示同一磅值的字体&#xff0c;其…

linux - 权限的概念

目录 用户权限 超级用户与普通用户的区别 超级用户&#xff08;root&#xff09;&#xff1a; 普通用户&#xff1a; 切换用户身份 使用sudo执行高权限命令 用户管理 用户组管理 文件权限 文件访问者类别 基本权限 权限表示方法 权限修改 chmod chown chgrp u…

Python函数返回值的艺术:为何True/False是更优实践及例外情况分析

在Python编程实践中&#xff0c;子程序的返回值设计往往是一个容易被忽视但却至关重要的设计决策。本文将深入探讨为什么返回True/False往往是更好的选择&#xff0c;何时应该避免这种做法&#xff0c;以及如何处理与None值相关的问题。 为什么返回True/False是更好的实践&…

STM32单片机内存分配详细讲解

单片机的内存无非就两种&#xff0c;内部FLASH和SRAM&#xff0c;最多再加上一个外部的FLASH拓展。在这里我以STM32F103C8T6为例子讲解FLASH和SRAM。 STM32F103C8T6具有64KB的闪存和20KB的SRAM。 一. Flash 1.1 定义 非易失性存储器&#xff0c;即使在断电后&#xff0c;其所…

【Tools】Visual Studio使用经验介绍(包括基本功能、远程调试、引入第三方库等等)

这里写目录标题 1. VS基本使用1.1. 快捷键1.2. 查看变量地址1.3. 查看代码汇编1.4. visual studio 热重载功能的使用1.5. vs远程服务器调试1.6. 引入第三方库VLD1.7. release debug模式 1. VS基本使用 1.1. 快捷键 ctrl c :复制光标所在行 注意&#xff1a;只需要光标在这…

网络爬虫学习之httpx的使用

开篇 本文整理自《Python3 网络爬虫实战》&#xff0c;主要是httpx的使用。 笔记整理 使用urllib库requests库的使用&#xff0c;已经可以爬取绝大多数网站的数据&#xff0c;但对于某些网站依然无能为力。 这是因为这些网站强制使用HTTP/2.0协议访问&#xff0c;这时urllib和r…

Python内存管理:赋值、浅拷贝与深拷贝解析

赋值与共享资源 在Python中&#xff0c;直接赋值操作&#xff08;如 list2 list1&#xff09;会导致两个变量共享同一个内存地址。这意味着对 list1 的修改会直接影响到 list2&#xff0c;因为它们指向同一个对象。 注意: 赋值等于完全共享资源 如果我们不希望这样完全共享&…

CentOS7原有磁盘扩容实战记录(LVM非LVM)【针对GPT分区】

一、环境 二、命令及含义 fdisk ‌ ‌ fdisk‌是一个较老的分区表创建和管理工具&#xff0c;主要支持MBR&#xff08;Master Boot Record&#xff09;格式的分区表。MBR分区表支持的硬盘单个分区最大容量为2TB&#xff0c;最多可以有4个主分区。fdisk通过命令行界面进行操…

获取相机图像(ROS2)

文章目录 前言一、获取笔记本自带相机图像1.打开终端2.安装usb-cam功能包3.启动相机节点4.再打开一个终端5.启动rqt查看图像(1)方法一&#xff1a;点击窗口选项&#xff0c;打开图像话题(2)方法二&#xff1a;使用命令行&#xff0c;直接打开图像话题 二、获取USB相机图像总结 …

Go 语言中接口类型转换为具体类型

类型转换方法 在 Go 语言中&#xff0c;将接口类型转换为具体类型主要有以下几种方法&#xff1a; 1. 类型断言&#xff08;Type Assertion&#xff09; var i interface{} "hello"// 基本形式 s : i.(string) // 将接口i转换为string类型 fmt.Println(s) // 输…