当前位置: 首页 > news >正文

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.xdnf.cn/news/351325.html

相关文章:

  • 对PyTorch模块进行性能分析
  • 一种海杂波背景下前视海面目标角超分辨成像方法——论文阅读
  • 【Python 元组】
  • Docker容器启动失败?无法启动?
  • 一些模型测试中的BUG和可能解决方法
  • linux系统管理
  • Java+Selenium+快代理实现高效爬虫
  • 通用外设驱动模型(四步法)
  • 探索大型语言模型的 LLM 安全风险和 OWASP 十大漏洞
  • (x ^ 2 + 2y − 1) ^ 3 − x ^ 2 * y ^ 3 = 1
  • React Native 前瞻式重大更新 Skia WebGPU ThreeJS,未来可期
  • AI客服问答自动生成文章(基于deepseek实现)
  • JAVA简单例题+抽象+继承
  • openssl中BIO的使用
  • PostgreSQL创建只读账号
  • 数据中台建设系列(五):SQL2API驱动的数据共享与服务化实践
  • 游戏引擎学习第266天:添加顶部时钟概览视图。
  • TensorFlow深度学习实战(15)——编码器-解码器架构
  • 可视化图解算法36: 序列化二叉树-I(二叉树序列化与反序列化)
  • 用 Java 实现 哲学家就餐问题
  • 数字信号处理|| 离散序列的基本运算
  • IPv6协议
  • 基于Transformer与SHAP可解释性分析的神经网络回归预测模型【MATLAB】
  • 英文单词 do、play、go 的区别
  • 大模型的RAG技术系列(二)
  • ADV7842KBCZ - 5 富利威长期稳定供应
  • MLX-Audio:高效音频合成的新时代利器
  • 【图片识别内容改名】图片指定区域OCR识别并自动重命名,批量提取图片指定内容并重命名,基于WPF和阿里云OCR识别的解决
  • wpf UserControl 更换 自定义基类
  • 三款实用电脑工具