动态表单生成

199 阅读8分钟

我现在在做一项前端技改,是根据特定的数据结构,利用vue2+element-ui来生成动态表单的一项功能。

我提供如下formConfig的数据结构。代码中有对应的说明注释。其中 fields 为数组结构,其中的元素描述的是表单中的每一项元素。fieldId为提交的元素字段,fieldName为属性中文名,fieldType是element-ui对应的组件类型,fieldValueType是格式类型,required为是否必填,其他的字段对应可映射为对应表单元素的属性,其他附加信息可以参考如下代码中的注释,注释中会对当前属性进行详细说明。

想要通过提供的formConfig数据,来生成对应的element-ui动态表单,请给出实现代码。

const formConfig = {
    pageId: 'user_profile',
    fields: [
        {
            fieldId: 'username',
            fieldName: '用户名',
            fieldType: 'input',
            fieldValueType: 'string', // 支持多种文件类型:string/integer/boolean/decimal/date/pdf/word/excel
            required: true, // 判断是否必填
            maxLength: 20, // 最大长度限制
            placeholder: '请输入用户名',
        },
        {
            fieldId: 'assets',
            fieldName: '资产',
            fieldType: 'input',
            fieldValueType: 'number',
            required: true,
            maxLength: 20,
            placeholder: '请输入用户名',
            suffix: '元', // 放在输入框后的单位
        },
        {
            fieldId: 'intro',
            fieldName: '个人简介',
            fieldType: 'textarea',
            fieldValueType: 'string',
            required: false,
            maxLength: 200,
            placeholder: '请简要介绍自己',
            rows: 4
        },
        {
            fieldId: 'userage',
            fieldName: '用户年龄段',
            fieldType: 'select',// 下拉选择框类型
            fieldValueType: 'string',
            required: true,
            // 下拉选项配置
            dropdownOptions: [
                { value: 18, label: '小于18' },
                { value: 30, label: '18-30' },
                { value: 60, label: '30-60' },
                { value: 100, label: '大于60' }
            ]
        },
        {
            fieldId: 'userType',
            fieldName: '用户类型',
            fieldType: 'select',
            fieldValueType: 'string',
            required: true, // 是否必填
            // 依赖条件配置
            condition: {
                trigRule: 'userage=18', // 触发规则:fieldId=特定value的形式
                displayType: 'hide' // 展示控制类型:展示但是控制是否可用-enable|disable,或隐藏-hide
            },
            // 默认选项
            dropdownOptions: [
                { value: 'normal', label: '普通用户' },
                { value: 'vip', label: 'VIP用户' }
            ],
            // 其他依赖条件下的选项变化
            otherOptions: {
                // 当userage=30时的选项
                'userage=30': [
                    { value: 'normal', label: '普通用户22' },
                    { value: 'vip', label: 'VIP用户22' }
                ]
            }
        },
        {
            fieldId: 'vipLevel',
            fieldName: 'VIP等级',
            fieldType: 'select',
            fieldValueType: 'string',
            required: false,
            condition: {
                trigRule: 'userType=vip',
                displayType: 'hide'
            },
            dropdownOptions: [
                { value: 1, label: 'vip1' },
                { value: 2, label: 'vip2' },
                { value: 3, label: 'vip3' },
                { value: 4, label: 'vip4' }
            ]
        },
        {
            fieldId: 'gender',
            fieldName: '性别',
            fieldType: 'radio',
            fieldValueType: 'string',
            required: true,
            dropdownOptions: [
                { value: 'male', label: '男' },
                { value: 'female', label: '女' }
            ]
        },
        {
            fieldId: 'hobbies',
            fieldName: '兴趣爱好',
            fieldType: 'checkbox',
            fieldValueType: 'array',
            required: false,
            dropdownOptions: [
                { value: 'reading', label: '阅读' },
                { value: 'sports', label: '运动' },
                { value: 'music', label: '音乐' },
                { value: 'travel', label: '旅行' }
            ]
        },
        {
            fieldId: 'birthday',
            fieldName: '生日',
            fieldType: 'date',
            fieldValueType: 'date',
            required: false,
            placeholder: '选择日期'
        },
        {
            fieldId: 'workTime',
            fieldName: '工作时间',
            fieldType: 'time',
            fieldValueType: 'string',
            required: false,
            placeholder: '选择时间'
        },
        {
            fieldId: 'validDateTime',
            fieldName: '有效日期时间',
            fieldType: 'dateTime', // 日期时间选择器
            fieldValueType: 'string',
            required: true, // 必填项
            // 如果发现表单元素中有maxCount字段,则说明该表单中支持存在最多maxCount个该类元素。
            // 最少支持1个,最多支持maxCount个,可在该元素后通过+/-号控制数量,fieldId/fieldName末尾加序号区分,序号从1开始,及1-maxCount。
            maxCount: 5
        },
        {
            fieldId: 'avatar',
            fieldName: '头像',
            fieldType: 'file',
            fieldValueType: 'array',
            required: false,
        }
    ]
};
<template>
  <div class="form-container">
    <el-form ref="dynamicForm" :model="formData" label-width="120px" label-position="top" class="dynamic-form">
      <!-- 遍历所有字段配置 -->
      <div v-for="field in config.fields" :key="field.fieldId">
        <!-- 普通字段 -->
        <div v-if="!field.maxCount" class="form-card" v-show="shouldShowField(field)">
          <div class="form-title">
            <i :class="getFieldIcon(field.fieldType)"></i>
            {{ field.fieldName }}
            <span v-if="field.required" style="color: #F56C6C; margin-left: 5px">*</span>
          </div>

          <!-- 输入框 -->
          <el-form-item v-if="field.fieldType === 'input' || field.fieldType === 'textarea'" :prop="field.fieldId"
            :rules="getRules(field)">
            <el-input v-if="field.fieldType === 'input'" v-model="formData[field.fieldId]"
              :placeholder="field.placeholder || '请输入'" :maxlength="field.maxLength"
              :disabled="isFieldDisabled(field)"></el-input>

            <el-input v-else-if="field.fieldType === 'textarea'" type="textarea" v-model="formData[field.fieldId]"
              :placeholder="field.placeholder || '请输入'" :maxlength="field.maxLength" :rows="4"
              :disabled="isFieldDisabled(field)"></el-input>
          </el-form-item>

          <!-- 下拉选择 -->
          <el-form-item v-else-if="field.fieldType === 'select'" :prop="field.fieldId" :rules="getRules(field)">
            <el-select v-model="formData[field.fieldId]" :placeholder="field.placeholder || '请选择'" style="width: 100%"
              :disabled="isFieldDisabled(field)">
              <el-option v-for="option in getDropdownOptions(field)" :key="option.value" :label="option.label"
                :value="option.value"></el-option>
            </el-select>
          </el-form-item>

          <!-- 日期时间选择器 -->
          <el-form-item v-else-if="field.fieldType === 'dateTime'" :prop="field.fieldId" :rules="getRules(field)">
            <el-date-picker v-model="formData[field.fieldId]" type="datetime" placeholder="选择日期时间" style="width: 100%"
              :disabled="isFieldDisabled(field)"></el-date-picker>
          </el-form-item>
        </div>

        <!-- 可动态增减的字段组 -->
        <div v-else class="dynamic-group">
          <div class="group-header">
            <div class="group-title">
              <i class="el-icon-tickets"></i>
              {{ field.fieldName }} ({{ formData[field.fieldId].length }}/{{ field.maxCount }})
            </div>
            <div class="group-actions">
              <el-tooltip content="添加一项" placement="top">
                <el-button type="success" icon="el-icon-plus" circle size="mini" @click="addFieldItem(field)"
                  :disabled="formData[field.fieldId].length >= field.maxCount"></el-button>
              </el-tooltip>
            </div>
          </div>

          <div v-for="(item, index) in formData[field.fieldId]" :key="index">
            <div class="form-card">
              <div class="form-title">
                <i :class="getFieldIcon(field.fieldType)"></i>
                {{ field.fieldName }} {{ index + 1 }}
                <span v-if="field.required" style="color: #F56C6C; margin-left: 5px">*</span>
              </div>

              <el-form-item :prop="`${field.fieldId}.${index}`" :rules="getRules(field)">
                <!-- 日期时间选择器 -->
                <el-date-picker v-if="field.fieldType === 'dateTime'" v-model="formData[field.fieldId][index]"
                  type="datetime" placeholder="选择日期时间" style="width: 100%"></el-date-picker>

                <!-- 其他类型字段可以在这里扩展 -->
              </el-form-item>

              <div style="text-align: right; margin-top: -10px">
                <el-button type="danger" icon="el-icon-delete" size="mini" @click="removeFieldItem(field, index)"
                  :disabled="formData[field.fieldId].length <= 1">删除</el-button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </el-form>

    <div class="form-actions">
      <el-button type="primary" @click="submitForm" size="medium">提交表单</el-button>
      <el-button @click="resetForm" size="medium">重置表单</el-button>
      <el-button @click="showFormData = !showFormData" size="medium">
        {{ showFormData ? '隐藏数据' : '查看数据' }}
      </el-button>
    </div>

    <div v-if="showFormData" class="json-view">
      <div class="json-view-header">
        <span>表单数据 (JSON格式)</span>
        <el-button type="text" @click="copyJson" style="color: #67c23a;">
          <i class="el-icon-document-copy"></i> 复制JSON
        </el-button>
      </div>
      <pre>{{ formatJson(formData) }}</pre>
    </div>
  </div>
</template>

<script>
import { formConfig } from './demo2'

export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  data() {
    // 初始化表单数据
    const formData = {};
    formConfig.fields.forEach(field => {
      if (field.maxCount) {
        // 可动态增减的字段组初始化为数组
        formData[field.fieldId] = [null];
      } else {
        // 普通字段初始化为null
        formData[field.fieldId] = null;
      }
    });

    return {
      config: formConfig,
      formData: formData,
      showFormData: false
    };
  },
  methods: {
    // 获取字段图标
    getFieldIcon(fieldType) {
      const icons = {
        input: 'el-icon-edit',
        textarea: 'el-icon-document',
        select: 'el-icon-s-operation',
        dateTime: 'el-icon-date',
        radio: 'el-icon-circle-check',
        checkbox: 'el-icon-check',
        file: 'el-icon-paperclip',
        url: 'el-icon-link'
      };
      return icons[fieldType] || 'el-icon-question';
    },

    // 获取验证规则
    getRules(field) {
      const rules = [];
      if (field.required) {
        rules.push({
          required: true,
          message: `请输入${field.fieldName}`,
          trigger: 'blur'
        });
      }
      return rules;
    },

    // 检查条件是否满足(支持多个条件)
    checkCondition(trigRule) {
      if (!trigRule) return false;

      // 分割多个条件
      const conditions = trigRule.split('&');

      // 检查每个条件是否满足
      return conditions.every(condition => {
        const [fieldId, expectedValue] = condition.split('=');
        const actualValue = this.formData[fieldId];

        // 处理数字类型的比较
        if (typeof actualValue === 'number' && !isNaN(expectedValue)) {
          return actualValue === Number(expectedValue);
        }

        // 默认使用宽松比较
        return actualValue == expectedValue;
      });
    },

    // 判断字段是否应该显示
    shouldShowField(field) {
      if (!field.condition) return true;

      const conditionMet = this.checkCondition(field.condition.trigRule);

      // 如果条件满足且显示类型是隐藏,则不显示
      if (field.condition.displayType === 'hide') {
        return !conditionMet;
      }

      // 其他情况都显示
      return true;
    },

    // 判断字段是否被禁用
    isFieldDisabled(field) {
      if (!field.condition) return false;

      const conditionMet = this.checkCondition(field.condition.trigRule);

      // 如果条件满足且显示类型是禁用,则禁用字段
      if (field.condition.displayType === 'disable') {
        return conditionMet;
      }

      return false;
    },

    // 获取下拉选项
    getDropdownOptions(field) {
      if (!field.otherOptions) return field.dropdownOptions;

      // 检查是否有满足条件的otherOptions
      for (const condition in field.otherOptions) {
        if (this.checkCondition(condition)) {
          return field.otherOptions[condition];
        }
      }

      return field.dropdownOptions;
    },

    // 添加字段项
    addFieldItem(field) {
      if (this.formData[field.fieldId].length < field.maxCount) {
        this.formData[field.fieldId].push(null);
      }
    },

    // 删除字段项
    removeFieldItem(field, index) {
      if (this.formData[field.fieldId].length > 1) {
        this.formData[field.fieldId].splice(index, 1);
      }
    },

    // 提交表单
    submitForm() {
      this.$refs.dynamicForm.validate(valid => {
        if (valid) {
          this.$message.success('表单验证通过!');
          this.showFormData = true;
          console.log('-------->', this.formData)
        } else {
          this.$message.error('请填写完整表单信息');
          return false;
        }
      });
    },

    // 重置表单
    resetForm() {
      this.$refs.dynamicForm.resetFields();
      this.showFormData = false;

      // 重置动态字段组
      this.config.fields.forEach(field => {
        if (field.maxCount) {
          this.formData[field.fieldId] = [null];
        }
      });
    },

    // 格式化JSON显示
    formatJson(obj) {
      return JSON.stringify(obj, null, 2);
    },

    // 复制JSON
    copyJson() {
      const textarea = document.createElement('textarea');
      textarea.value = JSON.stringify(this.formData, null, 2);
      document.body.appendChild(textarea);
      textarea.select();
      document.execCommand('copy');
      document.body.removeChild(textarea);
      this.$message.success('JSON已复制到剪贴板');
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 10px;
}

a {
  color: #42b983;
}
</style>

<template>
  <div class="form-container">
    <h2 class="form-title">动态生成表单</h2>
    <el-form :model="formData" :rules="formRules" ref="dynamicForm" label-width="140px" label-position="right">
      <!-- 遍历字段配置 -->
      <template v-for="field in formConfig.fields">
        <!-- 处理可动态添加的字段 -->
        <template v-if="field.maxCount > 1">
          <div class="field-group" :key="field.fieldId">
            <div class="field-group-header">
              <span class="field-group-title">{{ field.fieldName }}</span>
              <span class="field-count">({{ getFieldCount(field.fieldId) }}/{{ field.maxCount }})</span>
            </div>

            <el-button v-if="getFieldCount(field.fieldId) < field.maxCount" class="add-item-btn" type="primary"
              icon="el-icon-plus" size="small" @click="addDynamicField(field)">
              添加{{ field.fieldName }}
            </el-button>

            <div v-for="index in getFieldCount(field.fieldId)" :key="index">
              <el-form-item :label="field.fieldName + ' ' + index + ':'" :prop="getDynamicFieldId(field.fieldId, index)"
                :rules="getValidationRules(field)">
                <!-- 日期时间范围选择器 -->
                <el-date-picker v-if="field.fieldType === 'dateTime' && field.isRange"
                  v-model="formData[getDynamicFieldId(field.fieldId, index)]" type="datetimerange"
                  :placeholder="field.placeholder || '选择日期时间范围'" style="width: 100%" value-format="yyyy-MM-dd HH:mm:ss"
                  range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期">
                </el-date-picker>

                <!-- 其他字段类型的渲染 -->
                <template v-else>
                  <!-- 这里可以添加其他字段类型的渲染 -->
                  <el-input v-if="field.fieldType === 'input'"
                    v-model="formData[getDynamicFieldId(field.fieldId, index)]" :placeholder="field.placeholder || ''"
                    :maxlength="field.maxLength" clearable>
                    <template v-slot:suffix v-if="field.suffix">
                      {{ field.suffix }}
                    </template>
                  </el-input>
                </template>

                <el-button v-if="getFieldCount(field.fieldId) > 1" class="remove-btn" type="danger"
                  icon="el-icon-delete" size="mini" circle @click="removeDynamicField(field, index)"></el-button>
              </el-form-item>
            </div>
          </div>
        </template>

        <!-- 普通字段 -->
        <template v-else>
          <!-- 条件判断是否显示 -->
          <div v-if="shouldShowField(field)" :key="field.fieldId">
            <el-form-item :label="field.fieldName + ':'" :prop="field.fieldId" :rules="getValidationRules(field)">
              <!-- 输入框 -->
              <el-input v-if="field.fieldType === 'input' || field.fieldType === 'textarea'"
                v-model="formData[field.fieldId]" :type="field.fieldType" :placeholder="field.placeholder || ''"
                :maxlength="field.maxLength" :rows="field.rows || 2" clearable>
                <template v-slot:suffix v-if="field.suffix">
                  {{ field.suffix }}
                </template>
              </el-input>

              <!-- 下拉选择框 -->
              <el-select v-else-if="field.fieldType === 'select'" v-model="formData[field.fieldId]"
                :placeholder="field.placeholder || '请选择'" clearable style="width: 100%">
                <el-option v-for="option in getDropdownOptions(field)" :key="option.value" :label="option.label"
                  :value="option.value">
                </el-option>
              </el-select>

              <!-- 单选框 -->
              <el-radio-group v-else-if="field.fieldType === 'radio'" v-model="formData[field.fieldId]">
                <el-radio v-for="option in field.dropdownOptions" :key="option.value" :label="option.value">
                  {{ option.label }}
                </el-radio>
              </el-radio-group>

              <!-- 复选框 -->
              <el-checkbox-group v-else-if="field.fieldType === 'checkbox'" v-model="formData[field.fieldId]">
                <el-checkbox v-for="option in field.dropdownOptions" :key="option.value" :label="option.value">
                  {{ option.label }}
                </el-checkbox>
              </el-checkbox-group>

              <!-- 日期选择器 -->
              <el-date-picker v-else-if="field.fieldType === 'date'" v-model="formData[field.fieldId]"
                :type="field.isRange ? 'daterange' : 'date'"
                :placeholder="field.placeholder || (field.isRange ? '选择日期范围' : '选择日期')" style="width: 100%"
                value-format="yyyy-MM-dd" :range-separator="field.isRange ? '至' : ''"
                :start-placeholder="field.isRange ? '开始日期' : ''" :end-placeholder="field.isRange ? '结束日期' : ''">
              </el-date-picker>

              <!-- 时间选择器 -->
              <el-time-picker v-else-if="field.fieldType === 'time'" v-model="formData[field.fieldId]"
                :is-range="field.isRange" :placeholder="field.placeholder || (field.isRange ? '选择时间范围' : '选择时间')"
                style="width: 100%" value-format="HH:mm:ss" :range-separator="field.isRange ? '至' : ''"
                :start-placeholder="field.isRange ? '开始时间' : ''" :end-placeholder="field.isRange ? '结束时间' : ''">
              </el-time-picker>

              <!-- 日期时间选择器 -->
              <el-date-picker v-else-if="field.fieldType === 'dateTime'" v-model="formData[field.fieldId]"
                :type="field.isRange ? 'datetimerange' : 'datetime'"
                :placeholder="field.placeholder || (field.isRange ? '选择日期时间范围' : '选择日期时间')" style="width: 100%"
                value-format="yyyy-MM-dd HH:mm:ss" :range-separator="field.isRange ? '至' : ''"
                :start-placeholder="field.isRange ? '开始日期' : ''" :end-placeholder="field.isRange ? '结束日期' : ''">
              </el-date-picker>

              <!-- 文件上传 -->
              <el-upload v-else-if="field.fieldType === 'file'" action="#" :auto-upload="false"
                :on-change="(file) => handleFileChange(file, field)" :file-list="formData[field.fieldId]" multiple>
                <el-button size="small" type="primary">点击上传</el-button>
                <div slot="tip" class="el-upload__tip">请上传文件</div>
              </el-upload>
            </el-form-item>
          </div>
        </template>
      </template>
    </el-form>
  </div>
</template>

<script>
// import { formConfig } from './demo2'

export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  data() {
    return {
      formData: {},
      formRules: {},
      dynamicFields: {}, // 存储动态添加的字段信息
      formConfig: {
        pageId: 'user_profile',
        fields: [
          {
            fieldId: 'username',
            fieldName: '用户名',
            fieldType: 'input',
            fieldValueType: 'string',
            required: true,
            maxLength: 20,
            placeholder: '请输入用户名',
          },
          {
            fieldId: 'assets',
            fieldName: '资产',
            fieldType: 'input',
            fieldValueType: 'number',
            required: true,
            maxLength: 20,
            placeholder: '请输入资产',
            suffix: '元',
          },
          {
            fieldId: 'intro',
            fieldName: '个人简介',
            fieldType: 'textarea',
            fieldValueType: 'string',
            required: false,
            maxLength: 200,
            placeholder: '请简要介绍自己',
            rows: 4
          },
          {
            fieldId: 'userage',
            fieldName: '用户年龄段',
            fieldType: 'select',
            fieldValueType: 'string',
            required: true,
            dropdownOptions: [
              { value: 18, label: '小于18' },
              { value: 30, label: '18-30' },
              { value: 60, label: '30-60' },
              { value: 100, label: '大于60' }
            ]
          },
          {
            fieldId: 'userType',
            fieldName: '用户类型',
            fieldType: 'select',
            fieldValueType: 'string',
            required: true,
            condition: {
              trigRule: 'userage=18',
              displayType: 'hide'
            },
            dropdownOptions: [
              { value: 'normal', label: '普通用户' },
              { value: 'vip', label: 'VIP用户' }
            ],
            otherOptions: {
              'userage=30': [
                { value: 'normal', label: '普通用户22' },
                { value: 'vip', label: 'VIP用户22' }
              ]
            }
          },
          {
            fieldId: 'vipLevel',
            fieldName: 'VIP等级',
            fieldType: 'select',
            fieldValueType: 'string',
            required: false,
            condition: {
              trigRule: 'userType=vip',
              displayType: 'hide'
            },
            dropdownOptions: [
              { value: 1, label: 'vip1' },
              { value: 2, label: 'vip2' },
              { value: 3, label: 'vip3' },
              { value: 4, label: 'vip4' }
            ]
          },
          {
            fieldId: 'gender',
            fieldName: '性别',
            fieldType: 'radio',
            fieldValueType: 'string',
            required: true,
            dropdownOptions: [
              { value: 'male', label: '男' },
              { value: 'female', label: '女' }
            ]
          },
          {
            fieldId: 'hobbies',
            fieldName: '兴趣爱好',
            fieldType: 'checkbox',
            fieldValueType: 'array',
            required: false,
            dropdownOptions: [
              { value: 'reading', label: '阅读' },
              { value: 'sports', label: '运动' },
              { value: 'music', label: '音乐' },
              { value: 'travel', label: '旅行' }
            ]
          },
          {
            fieldId: 'birthday',
            fieldName: '生日',
            fieldType: 'date',
            fieldValueType: 'date',
            required: false,
            placeholder: '选择日期'
          },
          {
            fieldId: 'workTime',
            fieldName: '工作时间',
            fieldType: 'time',
            isRange: true,
            fieldValueType: 'string',
            required: false,
            placeholder: '选择时间'
          },
          {
            fieldId: 'validDateTime',
            fieldName: '有效日期时间',
            fieldType: 'dateTime',
            fieldValueType: 'string',
            isRange: true,
            required: true,
            maxCount: 5
          },
          {
            fieldId: 'avatar',
            fieldName: '头像',
            fieldType: 'file',
            fieldValueType: 'array',
            required: false,
          }
        ]
      }
    };
  },
  created() {
    // 初始化表单数据
    this.initFormData();
    // 初始化动态字段
    this.initDynamicFields();
  },
  methods: {
    // 初始化表单数据
    initFormData() {
      this.formConfig.fields.forEach(field => {
        // 根据字段类型设置初始值
        if (field.fieldType === 'checkbox') {
          this.$set(this.formData, field.fieldId, []);
        } else if (field.fieldType === 'file') {
          this.$set(this.formData, field.fieldId, []);
        } else if (field.isRange && (field.fieldType === 'date' || field.fieldType === 'time' || field.fieldType === 'dateTime')) {
          this.$set(this.formData, field.fieldId, []);
        } else {
          this.$set(this.formData, field.fieldId, '');
        }
      });
    },

    // 初始化动态字段
    initDynamicFields() {
      this.formConfig.fields.forEach(field => {
        if (field.maxCount > 1) {
          // 初始化动态字段计数
          this.$set(this.dynamicFields, field.fieldId, 1);
          // 初始化第一个动态字段的值
          this.$set(this.formData, this.getDynamicFieldId(field.fieldId, 1), '');
        }
      });
    },

    // 获取动态字段ID
    getDynamicFieldId(fieldId, index) {
      return `${fieldId}${index}`;
    },

    // 获取动态字段数量
    getFieldCount(fieldId) {
      return this.dynamicFields[fieldId] || 0;
    },

    // 添加动态字段
    addDynamicField(field) {
      if (this.dynamicFields[field.fieldId] < field.maxCount) {
        this.dynamicFields[field.fieldId]++;
        const newIndex = this.dynamicFields[field.fieldId];
        this.$set(this.formData, this.getDynamicFieldId(field.fieldId, newIndex), '');
      }
    },

    // 移除动态字段
    removeDynamicField(field, index) {
      if (this.dynamicFields[field.fieldId] > 1) {
        // 删除表单数据
        this.$delete(this.formData, this.getDynamicFieldId(field.fieldId, index));

        // 重新组织动态字段
        const currentCount = this.dynamicFields[field.fieldId];
        for (let i = index + 1; i <= currentCount; i++) {
          const value = this.formData[this.getDynamicFieldId(field.fieldId, i)];
          this.$set(this.formData, this.getDynamicFieldId(field.fieldId, i - 1), value);
          this.$delete(this.formData, this.getDynamicFieldId(field.fieldId, i));
        }

        this.dynamicFields[field.fieldId]--;
      }
    },

    // 获取验证规则
    getValidationRules(field) {
      const rules = [];

      if (field.required) {
        rules.push({
          required: true,
          message: `${field.fieldName}是必填项`,
          trigger: field.fieldType === 'input' || field.fieldType === 'textarea' ? 'blur' : 'change'
        });
      }

      if (field.maxLength && (field.fieldType === 'input' || field.fieldType === 'textarea')) {
        rules.push({
          max: field.maxLength,
          message: `最大长度不能超过${field.maxLength}个字符`,
          trigger: 'blur'
        });
      }

      return rules;
    },

    // 获取下拉选项
    getDropdownOptions(field) {
      // 检查是否有条件选项
      if (field.otherOptions) {
        for (const condition in field.otherOptions) {
          const [conditionField, conditionValue] = condition.split('=');
          if (this.formData[conditionField] == conditionValue) {
            return field.otherOptions[condition];
          }
        }
      }

      return field.dropdownOptions || [];
    },

    // 判断字段是否显示
    shouldShowField(field) {
      if (!field.condition) return true;

      const { trigRule, displayType } = field.condition;
      const [fieldId, value] = trigRule.split('=');

      if (displayType === 'hide') {
        return this.formData[fieldId] != value;
      }

      return true;
    },

    // 文件上传处理
    handleFileChange(file, field) {
      if (!this.formData[field.fieldId]) {
        this.$set(this.formData, field.fieldId, []);
      }
      this.formData[field.fieldId].push(file);
    },

    // 提交表单
    submitForm() {
      this.$refs.dynamicForm.validate((valid) => {
        if (valid) {
          this.$message.success('表单验证成功!');
          console.log('表单数据:', this.formData);
        } else {
          this.$message.error('表单验证失败,请检查输入!');
          return false;
        }
      });
    },

    // 重置表单
    resetForm() {
      this.$refs.dynamicForm.resetFields();
      // 重置动态字段
      this.initDynamicFields();
      this.$message.info('表单已重置');
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}

ul {
  list-style-type: none;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 10px;
}

a {
  color: #42b983;
}
</style>