Vue实现动态表单生成功能详解

动态表单是现代Web应用中常见的需求,它允许根据不同的业务场景或用户权限动态生成表单字段。本文将详细介绍如何使用Vue.js实现一个灵活的动态表单生成功能。

功能需求

我们需要实现一个表单系统,能够:

  1. 根据JSON配置动态渲染表单
  2. 支持多种表单字段类型(文本、数字、选择器等)
  3. 实现表单验证
  4. 支持动态添加/删除表单字段

实现步骤

1. 项目初始化

首先创建一个Vue项目(本文以Vue 3为例):

npm init vue@latest dynamic-form
cd dynamic-form
npm install

2. 定义表单配置数据结构

src目录下创建formConfig.js,定义表单配置:

export const formConfig = {
  title: '用户信息表单',
  fields: [
    {
      type: 'text',
      label: '姓名',
      model: 'name',
      required: true,
      placeholder: '请输入姓名',
      validation: {
        pattern: '^[\\u4e00-\\u9fa5]{2,4}$',
        message: '请输入2-4个中文字符'
      }
    },
    {
      type: 'number',
      label: '年龄',
      model: 'age',
      min: 18,
      max: 100
    },
    {
      type: 'select',
      label: '性别',
      model: 'gender',
      options: [
        { value: 'male', label: '男' },
        { value: 'female', label: '女' }
      ]
    },
    // 更多字段...
  ]
}

3. 创建动态表单组件

创建src/components/DynamicForm.vue

<template>
  <div class="dynamic-form">
    <h2>{{ config.title }}</h2>
    <form @submit.prevent="handleSubmit">
      <div v-for="field in config.fields" :key="field.model" class="form-field">
        <!-- 文本输入框 -->
        <div v-if="field.type === 'text'">
          <label :for="field.model">{{ field.label }}</label>
          <input
            type="text"
            :id="field.model"
            v-model="formData[field.model]"
            :placeholder="field.placeholder"
            :required="field.required"
          />
          <span v-if="errors[field.model]" class="error">{{ errors[field.model] }}</span>
        </div>
        
        <!-- 数字输入框 -->
        <div v-if="field.type === 'number'">
          <label :for="field.model">{{ field.label }}</label>
          <input
            type="number"
            :id="field.model"
            v-model.number="formData[field.model]"
            :min="field.min"
            :max="field.max"
          />
        </div>
        
        <!-- 下拉选择框 -->
        <div v-if="field.type === 'select'">
          <label :for="field.model">{{ field.label }}</label>
          <select :id="field.model" v-model="formData[field.model]">
            <option 
              v-for="option in field.options" 
              :key="option.value" 
              :value="option.value"
            >
              {{ option.label }}
            </option>
          </select>
        </div>
        
        <!-- 可以继续添加其他类型的表单字段 -->
      </div>
      
      <button type="submit">提交</button>
    </form>
  </div>
</template>

<script>
import { reactive, watch } from 'vue'

export default {
  props: {
    config: {
      type: Object,
      required: true
    }
  },
  
  setup(props) {
    // 初始化表单数据
    const formData = reactive({})
    const errors = reactive({})
    
    // 根据配置初始化表单数据
    props.config.fields.forEach(field => {
      formData[field.model] = field.defaultValue || ''
    })
    
    // 表单验证
    const validateField = (field) => {
      if (field.required && !formData[field.model]) {
        errors[field.model] = '此字段为必填项'
        return false
      }
      
      if (field.validation && field.validation.pattern) {
        const regex = new RegExp(field.validation.pattern)
        if (!regex.test(formData[field.model])) {
          errors[field.model] = field.validation.message || '格式不正确'
          return false
        }
      }
      
      delete errors[field.model]
      return true
    }
    
    // 监听表单数据变化进行验证
    props.config.fields.forEach(field => {
      watch(() => formData[field.model], () => {
        validateField(field)
      })
    })
    
    // 提交表单
    const handleSubmit = () => {
      let isValid = true
      props.config.fields.forEach(field => {
        if (!validateField(field)) {
          isValid = false
        }
      })
      
      if (isValid) {
        console.log('表单提交:', formData)
        // 这里可以触发提交事件或调用API
      } else {
        console.log('表单验证未通过')
      }
    }
    
    return {
      formData,
      errors,
      handleSubmit
    }
  }
}
</script>

<style scoped>
.dynamic-form {
  max-width: 500px;
  margin: 0 auto;
  padding: 20px;
}

.form-field {
  margin-bottom: 15px;
}

label {
  display: block;
  margin-bottom: 5px;
}

input, select {
  width: 100%;
  padding: 8px;
  box-sizing: border-box;
}

.error {
  color: red;
  font-size: 12px;
  display: block;
  margin-top: 5px;
}

button {
  background: #42b983;
  color: white;
  border: none;
  padding: 10px 15px;
  cursor: pointer;
  margin-top: 10px;
}

button:hover {
  background: #369f6b;
}
</style>

4. 使用动态表单组件

App.vue中使用我们的动态表单组件:

<template>
  <DynamicForm :config="formConfig" />
</template>

<script>
import DynamicForm from './components/DynamicForm.vue'
import { formConfig } from './formConfig'

export default {
  components: {
    DynamicForm
  },
  data() {
    return {
      formConfig
    }
  }
}
</script>

功能扩展

1. 动态添加/删除字段

DynamicForm.vue中添加方法:

const addField = () => {
  const newField = {
    type: 'text',
    label: '新字段',
    model: `field_${Date.now()}`,
    placeholder: '请输入内容'
  }
  props.config.fields.push(newField)
  formData[newField.model] = ''
}

const removeField = (index) => {
  const field = props.config.fields[index]
  delete formData[field.model]
  props.config.fields.splice(index, 1)
}

2. 支持更多字段类型

可以扩展支持更多字段类型,如:

// 复选框
<div v-if="field.type === 'checkbox'">
  <label>
    <input 
      type="checkbox" 
      v-model="formData[field.model]"
    />
    {{ field.label }}
  </label>
</div>

// 单选按钮组
<div v-if="field.type === 'radio'">
  <label>{{ field.label }}</label>
  <div v-for="option in field.options" :key="option.value">
    <label>
      <input 
        type="radio" 
        :value="option.value"
        v-model="formData[field.model]"
      />
      {{ option.label }}
    </label>
  </div>
</div>
posted @ 2025-06-13 21:38  99客服  阅读(309)  评论(0)    收藏  举报