Vue3+Element Plus动态表单实现

 更新时间:2025年05月15日 08:41:15   作者:滿  
本文主要介绍了Vue3+Element Plus动态表单实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

 完整代码

<template>
  <div class="dynamic-form-container">
    <el-form
      ref="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-item
          v-if="field.type === 'input'"
          :label="field.label"
          :prop="field.name"
          :rules="generateFieldRules(field)"
        >
          <el-input
            v-model="formData[field.name]"
            :placeholder="field.placeholder || `请输入${field.label}`"
            :type="field.inputType || 'text'"
            :clearable="field.clearable !== false"
          />
        </el-form-item>

        <!-- 下拉选择 -->
        <el-form-item
          v-else-if="field.type === 'select'"
          :label="field.label"
          :prop="field.name"
          :rules="generateFieldRules(field)"
        >
          <el-select
            v-model="formData[field.name]"
            :placeholder="field.placeholder || `请选择${field.label}`"
            :clearable="field.clearable !== false"
            style="width: 100%"
          >
            <el-option
              v-for="option in field.options"
              :key="option.value"
              :label="option.label"
              :value="option.value"
            />
          </el-select>
        </el-form-item>

        <!-- 单选框 -->
        <el-form-item
          v-else-if="field.type === 'radio'"
          :label="field.label"
          :prop="field.name"
          :rules="generateFieldRules(field)"
        >
          <el-radio-group v-model="formData[field.name]">
            <el-radio
              v-for="option in field.options"
              :key="option.value"
              :label="option.value"
            >
              {{ option.label }}
            </el-radio>
          </el-radio-group>
        </el-form-item>

        <!-- 复选框 -->
        <el-form-item
          v-else-if="field.type === 'checkbox'"
          :label="field.label"
          :prop="field.name"
          :rules="generateFieldRules(field)"
        >
          <el-checkbox-group v-model="formData[field.name]">
            <el-checkbox
              v-for="option in field.options"
              :key="option.value"
              :label="option.value"
            >
              {{ option.label }}
            </el-checkbox>
          </el-checkbox-group>
        </el-form-item>

        <!-- 日期选择器 -->
        <el-form-item
          v-else-if="field.type === 'date'"
          :label="field.label"
          :prop="field.name"
          :rules="generateFieldRules(field)"
        >
          <el-date-picker
            v-model="formData[field.name]"
            :type="field.dateType || 'date'"
            :placeholder="field.placeholder || `请选择${field.label}`"
            style="width: 100%"
          />
        </el-form-item>

        <!-- 开关 -->
        <el-form-item
          v-else-if="field.type === 'switch'"
          :label="field.label"
          :prop="field.name"
        >
          <el-switch v-model="formData[field.name]" />
        </el-form-item>

        <!-- 自定义插槽 -->
        <el-form-item
          v-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 || []
        break
      case 'switch':
        data[field.name] = field.defaultValue || false
        break
      default:
        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()
    
    // 这里替换为实际的提交API
    const 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
      }
    ]
  }
}

到此这篇关于Vue3+Element Plus动态表单实现的文章就介绍到这了,更多相关Vue3 Element Plus动态表单内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 解决vue无法加载文件D:\Program Files\nodejs\node_global\vue.ps1,因为在此系统上禁止运行脚本

    解决vue无法加载文件D:\Program Files\nodejs\node_global\vue.ps1,

    这篇文章主要给大家介绍了关于解决vue无法加载文件D:\Program Files\nodejs\node_global\vue.ps1,因为在此系统上禁止运行脚本的相关资料,这个报错是由于在系统上禁止运行脚本导致的,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2024-01-01
  • Vue elementUI表单嵌套表格并对每行进行校验详解

    Vue elementUI表单嵌套表格并对每行进行校验详解

    element-ui中有详细的各种表格及表格方法,下面这篇文章主要给大家介绍了关于Vue elementUI表单嵌套表格并对每行进行校验的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-01-01
  • 如何在 Vue3 中使用 OpenLayers 实现事件 loadstart 和 loadend

    如何在 Vue3 中使用 OpenLayers 实现事件 loadst

    在这篇文章中,我将详细介绍如何在 Vue3 + OpenLayers 中监听 loadstart 和 loadend 事件,并通过 Vue3 Composition API 进行代码优化,使其更加高效、健壮,感兴趣的朋友一起看看吧
    2025-04-04
  • Vue中如何处理token过期问题

    Vue中如何处理token过期问题

    这篇文章主要介绍了Vue中如何处理token过期问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10
  • Vue专属状态管理库Pinia的使用与实践分享

    Vue专属状态管理库Pinia的使用与实践分享

    在 Vue 的开发中,状态管理是一个不可或缺的部分,尤其是在复杂的应用中,组件之间的状态共享和管理变得至关重要,Pinia 作为 Vue 的专属状态管理库,本文将深入介绍 Pinia 的基础知识、核心功能以及实际使用场景,需要的朋友可以参考下
    2024-11-11
  • 使用vue-i18n 入口文件配置控制台报警问题解决

    使用vue-i18n 入口文件配置控制台报警问题解决

    这篇文章主要介绍了使用vue-i18n 入口文件配置控制台报警问题解决,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • Vue中使用element-ui给按钮绑定一个单击事件实现点击按钮就弹出dialog对话框

    Vue中使用element-ui给按钮绑定一个单击事件实现点击按钮就弹出dialog对话框

    最近遇到了个需求是使用element-ui插件编写页面,点击按钮,弹出对话框,这篇文章主要给大家介绍了关于Vue中使用element-ui给按钮绑定一个单击事件实现点击按钮就弹出dialog对话框的相关资料,需要的朋友可以参考下
    2022-11-11
  • Vue2.x响应式简单讲解及示例

    Vue2.x响应式简单讲解及示例

    这篇文章主要介绍了Vue2.x响应式及简单的示例,应用了简单的源代码进行讲解,感兴趣的小伙伴可以参考一下,希望可以帮助到你
    2021-08-08
  • vue-manage-system升级到vue3的开发总结分析

    vue-manage-system升级到vue3的开发总结分析

    这篇文章主要为大家介绍了vue-manage-system升级到vue3的开发总结分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • 浅谈Vue知识系列-axios

    浅谈Vue知识系列-axios

    这篇文章主要介绍了浅谈Vue知识系列-axios,本文章内容详细,具有很好的参考价值,希望对大家有所帮助,需要的朋友可以参考下<BR>
    2023-01-01

最新评论