当前位置: 首页 > 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}]}
}

相关文章:

  • vscode离线安装python插件
  • git高效杀器——cz-customizable 搭配 commitlint
  • 抖音到店摸着京东外卖过河
  • 树初步 #1(插排串联 - 辽宁省2024CCPC)
  • 【八股消消乐】你在项目中如何优化垃圾回收机制?
  • 动态规划之背包问题:组合优化中的经典NP挑战
  • 基于vue框架的电子商城m8qu8(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • WPDRRC 模型:构建动态闭环的信息安全防御体系
  • 【RAG】重点部分 RAG-Fusion, Decomposition, HyDE 和 Routing
  • apipost快捷使用实例
  • 耳机插进电脑只有一边有声音怎么办 解决方法分享
  • Java——包装类
  • 【大模型面试每日一题】Day 13:数据并行与模型并行的区别是什么?ZeRO优化器如何结合二者?
  • MLX-Audio:高效音频合成的新时代利器
  • 依赖关系-根据依赖关系求候选码
  • 基于Llama3的开发应用(一):Llama模型的简单部署
  • 力扣刷题 每日四道
  • vue项目的创建
  • LDO与DCDC总结
  • 华为5.7机考-最小代价相遇的路径规划Java题解
  • 宇树科技王兴兴:第一桶金来自上海,欢迎上海的年轻人加入
  • 中国海外发展:今年前4个月销售665.8亿元,花费305亿元拿地
  • 化学家、台湾地区“中研院”原学术副院长陈长谦逝世
  • 上市不足一年,吉利汽车拟私有化极氪并合并:整合资源,杜绝重复投入
  • 指挥家高健:东方市民音乐会“高贵不贵”,我愿意常来
  • 五一假期上海虹桥边检站出入境近4.7万人次,韩国入境旅客同比增118%