最近遇到一个需要超级写超级多字段的表单,初略计算大概四十来个吧字段吧,/(ㄒoㄒ)/~~,这也太麻烦了,想了想能不能简单封装一下,找到了个巨人,嗯哼,就踩一下吧
参考文章: Vue3.0 根据JSON对象生成指定form表单 上面文章是vue3的,逻辑都一样。以下是vue2 我的写法

1.定义 formTemplate.vue

<template>
  <el-col v-if="isCol" :span="colSpan">
    <div v-if="isForm">
      <el-form-item :prop="prop" :label="label">
        <component
          :is="isCom"
          :config="config"
          v-model="templateValue"
          @change="change"
        />
      </el-form-item>
    </div>
    <div v-else>
      <component
        :is="isCom"
        :config="config"
        v-model="templateValue"
        @change="change"
      />
    </div>
  </el-col>
  <div v-else>
    <div v-if="isForm">
      <el-form-item :prop="prop" :label="label">
        <component
          :is="isCom"
          :config="config"
          v-model="templateValue"
          @change="change"
        />
      </el-form-item>
    </div>
    <div v-else>
      <component
        :is="isCom"
        :config="config"
        v-model="templateValue"
        @change="change"
      />
    </div>
  </div>
</template>

<script>
import formInput from "./formInput";
import formSelect from "./formSelect"

/**
 * @author BlackKey
 * @params value 双向绑定值
 * @params colSpan 布局占位
 * @params isCol 是否启用布局
 * @params isForm 是否用form包裹
 * @params label 标题
 * @params prop model的键名
 * @params isCom 需要的生成的组件
 * @params config 组件对应的配置文件
 * @desc  form表单默认格式
 */
export default {
  components: {
    formInput,
    formSelect
  },
model: {
    prop: "value",
    event: "change",
  },
  props: {
    colSpan: {
      type: Number,
      default: 6,
    },
    isCol: {
      type: Boolean,
      default: true,
    },
    isForm: {
      type: Boolean,
      default: true,
    },
    label: {
      type: String,
      default: "",
    },
    prop: {
      type: String,
      default: "",
    },
    isCom: {
      type: String,
      default: "formInput",
    },
    value: {
      type: [String, Number, Object, Array],
      default: "",
    },
    config: {
      type: Object,
      default: () => ({}),
    },
  },
  methods: {
    change(data) {
        this.$emit("change", data);
    }
  },
  computed: {
    templateValue: {
      get() {
        return this.value;
      },
      set(val) {
        this.$emit("change", val);
      },
    }
  },
};

</script>

<style lang="less" scoped></style>

2. 输入框组件 formInput.vue

<template>
  <el-input
    v-model="inputValue"
    :placeholder="defaultConfig.placeholder"
    :clearable="defaultConfig.clearable"
    :disabled="defaultConfig.disabled"
    :type="defaultConfig.type"
    :maxlength="defaultConfig.maxlength"
    :autosize="defaultConfig.autosize"
  />
</template>

<script>
/**
 * @author BlackKey
 * @params value 双向绑定值
 * @params placeholder 输入框占位符文本
 * @params type 输入框类型
 * @params maxlength 最大长度
 * @params disabled 是否禁用
 * @params size 输入框尺寸
 * @params autosize textarea 高度是否自适应,仅 type 为 'textarea' 时生效。
 * @params clearable 是否展示清空按钮
 * @desc  输入框
 */

export default {
    props: {
        config: {
            type: Object,
            default: () => ({ }),
        },
        value: {
            type: [String, Number, Object, Array],
            default: "",
        },
    },
    data(){
        return {
            inputValue: '',

            defaultConfig: {
                placeholder: "",
                type: "text",
                maxlength: "",
                disabled: false,
                size: "",
                autosize: false,
                clearable: true,
            }
        }
    },
    mounted(){
        this.inputValue = this.value
        this.defaultConfig = {...this.defaultConfig, ...this.config}
    },
    watch: {
        inputValue: {
            immediate: true,
            handler(newVal, oldVal) {
                this.$emit("change", newVal);
            },
        },
    },
}
</script>

3. 下拉选择框 formSelect.vue

<template>
  <el-select
    v-model="selectValue"
    :clearable="defaultConfig.clearable"
    :placeholder="defaultConfig.placeholder"
    :disabled="defaultConfig.disabled"
    :size="defaultConfig.size"
  >
    <el-option
      v-for="item in defaultConfig.options"
      :key="item[defaultConfig.labelKey]"
      :label="item[defaultConfig.labelKey]"
      :value="item[defaultConfig.valueKey]"
    ></el-option>

  </el-select>

  
</template>

<script>
/**
 * @author BlackKey
 * @params modelValue 双向绑定值
 * @params placeholder 输入框占位符文本
 * @params disabled 是否禁用
 * @params size 输入框尺寸
 * @params clearable 是否展示清空按钮
 * @params options 下拉框的值
 * @params dataType 预先设定好的枚举
 * @params labelKey 下拉框labelKey
 * @params valueKey 下拉框valueKey
 * @desc  下拉框
 */

export default {
  props: {
    config: {
      type: Object,
      default: () => ({}),
    },
    value: {
      type: [String, Number, Object, Array],
      default: "",
    },
  },
  data() {
    return {
      selectValue: "",
      source: [
        {
          label: "男",
          value: "man",
        },
        {
          label: "女",
          value: "woman",
        },
      ],

      defaultConfig: {
        placeholder: "",
        disabled: false,
        size: "",
        clearable: true,
        options: [],
        dataType: "",
        labelKey: "label",
        valueKey: "value",
      }
    };
  },
  mounted() {
    this.selectValue = this.value

    this.defaultConfig = {...this.defaultConfig, ...this.config}
    // Object.keys(this.config).forEach(key=>{this.defaultConfig[key]=this.config[key]})
  },
  watch: {
    selectValue: function (newVal, oldVal) {
      this.$emit("change", newVal);
    }
  }
};
</script>

使用方法

<template>
  <div class="content">
    <div class="query">
      
      <div class="formWrap">
        <el-form
          :model="form"
          :rules="rules"
          ref="formRef"
          label-width="120px"
        >
          <el-row :gutter="20">
            <form-template
              v-for="item in formTemplateData"
              :key="item.label"
              :label="item.label"
              :prop="item.prop"
              :config="item.config"
              :isCom="item.isCom"
              v-model="item.value"
            ></form-template>
            <el-col :span="24">
              <div class="btn">
                <el-button type="primary" @click="search">查询</el-button>
              </div>
            </el-col>
          </el-row>
        </el-form>

        
      </div>
    </div>
  </div>
</template>

<script>
import formTemplate from "../components/formTemplate";
import formInput from "../components/formInput";
import formSelect from "../components/formSelect";

export default {
  components: {
    formTemplate,
    formInput,
    formSelect,
  },
  data() {
    return {
      form: {
        dataId: "123123123123",
        orgLicense: "rrrr1234",
        orgType: "99",
        address: "测试地址110",
        postalCode: "1",
        workNum: 1,
        orgLicenseTime: "2019-12-21 10:30:00",
        economicOwnership: "310",
        fixedAssets: 1,
        unitArea: 0,
        totalBuildingArea: 0,
        monitorLevel: "1",
        gridId: "",
        maintenanceUnitId: "",
        checkUnitId: "1",
        constructionUnitId: "1",
        monitorType: "2",
        monitorCenterId: "1278158407189680128",
        fireManageId: "JXXF1",
        orgTel: "13256781234",
        networkStatus: "1",
        accessDate: "2019-12-21 10:30:00",
        fireSafePerson: "测试人1",
        fireSafePersonId: "1",
        fireSafePersonTel: "18100000001",
        fireSafeManager: "1",
        fireSafeManagerId: "1",
        fireSafeManagerTel: "1",
        fptimeFireManager: "1",
        fptimeFireManagerId: "1",
        fptimeFireManagerTel: "1",
        corporator: "1",
        corporatorId: "1",
        corporatorTel: "1",
        fireSupervisor: "1",
        fireSupervisorTel: "1",
        companyProfile: "",
        informationPushSignature: "",
        containSystem: "1",
        scopeOfBusiness: "1",
        rescueDepartment: "1",
        commercialProjects: "1",
        contractualStatus: "1",
        company: {
          companyName: "第三方-拓宝",
          fullName: "第三方-拓宝",
          area: {
            areaCode: "360100",
            areaName: "南昌市",
          },
          industryType: "6",
          industryDept: "1",
          lng: 114.47335,
          lat: 30.452798,
        },
      },
      formTemplateData: [
        {
          label: "dataId",
          value: "",
          prop: "dataId",
          config: {
            placeholder: "请输入dataId",
          },
        },
        {
          label: "orgLicense",
          value: "",
          prop: "orgLicense",
          config: {
            placeholder: "请输入orgLicense",
          },
        },
        {
          label: "orgType",
          value: "",
          prop: "orgType",
          config: {
            placeholder: "请输入orgType",
          },
        },
        {
          label: "address",
          value: "",
          prop: "address",
          config: {
            placeholder: "请输入address",
          },
        },
        {
          label: "postalCode",
          value: "",
          prop: "postalCode",
          config: {
            placeholder: "请输入postalCode",
          },
        },
        {
          label: "workNum",
          value: "",
          prop: "workNum",
          config: {
            placeholder: "请输入workNum",
          },
        },
        {
          label: "orgLicenseTime",
          value: "",
          prop: "orgLicenseTime",
          config: {
            placeholder: "请输入orgLicenseTime",
          },
        },
        {
          label: "economicOwnership",
          value: "",
          prop: "economicOwnership",
          config: {
            placeholder: "请输入economicOwnership",
          },
        },
        {
          label: "fixedAssets",
          value: "",
          prop: "fixedAssets",
          config: {
            placeholder: "请输入fixedAssets",
          },
        },
        {
          label: "unitArea",
          value: "",
          prop: "unitArea",
          config: {
            placeholder: "请输入unitArea",
          },
        },
        {
          label: "totalBuildingArea",
          value: "",
          prop: "totalBuildingArea",
          config: {
            placeholder: "请输入totalBuildingArea",
          },
        },
        {
          label: "monitorLevel",
          value: "",
          prop: "monitorLevel",
          config: {
            placeholder: "请输入monitorLevel",
          },
        },
        {
          label: "gridId",
          value: "",
          prop: "gridId",
          config: {
            placeholder: "请输入gridId",
          },
        },
        {
          label: "maintenanceUnitId",
          value: "",
          prop: "maintenanceUnitId",
          config: {
            placeholder: "请输入maintenanceUnitId",
          },
        },
        {
          label: "checkUnitId",
          value: "",
          prop: "checkUnitId",
          config: {
            placeholder: "请输入checkUnitId",
          },
        },
        {
          label: "constructionUnitId",
          value: "",
          prop: "constructionUnitId",
          config: {
            placeholder: "请输入constructionUnitId",
          },
        },
        {
          label: "monitorType",
          value: "",
          prop: "monitorType",
          config: {
            placeholder: "请输入monitorType",
          },
        },
        {
          label: "monitorCenterId",
          value: "",
          prop: "monitorCenterId",
          config: {
            placeholder: "请输入monitorCenterId",
          },
        },
        {
          label: "fireManageId",
          value: "",
          prop: "fireManageId",
          config: {
            placeholder: "请输入fireManageId",
          },
        },
        {
          label: "orgTel",
          value: "",
          prop: "orgTel",
          config: {
            placeholder: "请输入orgTel",
          },
        },
        {
          label: "networkStatus",
          value: "",
          prop: "networkStatus",
          config: {
            placeholder: "请输入networkStatus",
          },
        },
        {
          label: "accessDate",
          value: "",
          prop: "accessDate",
          config: {
            placeholder: "请输入accessDate",
          },
        },
        {
          label: "fireSafePerson",
          value: "",
          prop: "fireSafePerson",
          config: {
            placeholder: "请输入fireSafePerson",
          },
        },
        {
          label: "fireSafePersonId",
          value: "",
          prop: "fireSafePersonId",
          config: {
            placeholder: "请输入fireSafePersonId",
          },
        },
        {
          label: "fireSafePersonId",
          value: "",
          prop: "fireSafePersonId",
          config: {
            placeholder: "请输入fireSafePersonId",
          },
        },
        {
          label: "fireSafePersonTel",
          value: "",
          prop: "fireSafePersonTel",
          config: {
            placeholder: "请输入fireSafePersonTel",
          },
        },
        {
          label: "fireSafeManagerId",
          value: "",
          prop: "fireSafeManagerId",
          config: {
            placeholder: "请输入fireSafeManagerId",
          },
        },
        {
          label: "fireSafeManager",
          value: "",
          prop: "fireSafeManager",
          config: {
            placeholder: "请输入fireSafeManager",
          },
        },
        {
          label: "fireSafeManagerTel",
          value: "",
          prop: "fireSafeManagerTel",
          config: {
            placeholder: "请输入fireSafeManagerTel",
          },
        },
        {
          label: "fptimeFireManager",
          value: "",
          prop: "fptimeFireManager",
          config: {
            placeholder: "请输入fptimeFireManager",
          },
        },
        {
          label: "fptimeFireManagerId",
          value: "",
          prop: "fptimeFireManagerId",
          config: {
            placeholder: "请输入fptimeFireManagerId",
          },
        },
        {
          label: "corporator",
          value: "",
          prop: "corporator",
          config: {
            placeholder: "请输入corporator",
          },
        },
        {
          label: "fptimeFireManagerTel",
          value: "",
          prop: "fptimeFireManagerTel",
          config: {
            placeholder: "请输入fptimeFireManagerTel",
          },
        },
        {
          label: "corporatorId",
          value: "",
          prop: "corporatorId",
          config: {
            placeholder: "请输入corporatorId",
          },
        },
        {
          label: "corporatorTel",
          value: "",
          prop: "corporatorTel",
          config: {
            placeholder: "请输入corporatorTel",
          },
        },
        {
          label: "fireSupervisor",
          value: "",
          prop: "fireSupervisor",
          config: {
            placeholder: "请输入fireSupervisor",
          },
        },
        {
          label: "fireSupervisorTel",
          value: "",
          prop: "fireSupervisorTel",
          config: {
            placeholder: "请输入fireSupervisorTel",
          },
        },
        {
          label: "companyProfile",
          value: "",
          prop: "companyProfile",
          config: {
            placeholder: "请输入companyProfile",
          },
        },
        {
          label: "informationPushSignature",
          value: "",
          prop: "informationPushSignature",
          config: {
            placeholder: "请输入informationPushSignature",
          },
        },
        {
          label: "containSystem",
          value: "",
          prop: "containSystem",
          config: {
            placeholder: "请输入containSystem",
          },
        },
        {
          label: "scopeOfBusiness",
          value: "",
          prop: "scopeOfBusiness",
          config: {
            placeholder: "请输入scopeOfBusiness",
          },
        },
        {
          label: "rescueDepartment",
          value: "",
          prop: "rescueDepartment",
          config: {
            placeholder: "请输入rescueDepartment",
          },
        },
        {
          label: "commercialProjects",
          value: "",
          prop: "commercialProjects",
          config: {
            placeholder: "请输入commercialProjects",
          },
        },
        {
          label: "contractualStatus",
          value: "",
          prop: "contractualStatus",
          config: {
            placeholder: "请输入contractualStatus",
          },
        },
        {
          label: "companyName",
          value: "",
          prop: "company.companyName",
          config: {
            placeholder: "请输入companyName",
          },
        },
        {
          label: "fullName",
          value: "",
          prop: "company.fullName",
          config: {
            placeholder: "请输入fullName",
          },
        },
        {
          label: "industryType",
          value: "",
          prop: "company.industryType",
          config: {
            placeholder: "请输入industryType",
          },
        },
        {
          label: "industryDept",
          value: "",
          prop: "company.industryDept",
          config: {
            placeholder: "请输入industryDept",
          },
        },
        {
          label: "lng",
          value: "",
          prop: "company.lng",
          config: {
            placeholder: "请输入lng",
          },
        },
        {
          label: "lat",
          value: "",
          prop: "company.lat",
          config: {
            placeholder: "请输入lat",
          },
        },
        {
          label: "areaCode",
          value: "",
          prop: "company.area.areaCode",
          config: {
            placeholder: "请输入areaCode",
          },
        },
        {
          label: "areaName",
          value: "",
          prop: "company.area.areaName",
          config: {
            placeholder: "请输入areaName",
          },
        },
      ],
      rules: {
        dataId: [{ required: true, message: '请填写', trigger: 'change' }]
      }
    };
  },
  
 
  methods: {
    search() {
      this.formTemplateData.forEach((val) => {
        // 组装数据
        let keysArr = val.prop.split(".");
        this.setValueByKeys(this.form, keysArr, val.value);
        // this.form[val.prop] = val.value;
      });

      this.$refs.formRef.validate((valid)=>{
        if(valid){
          console.log('-----------',valid)
          // this.onSubmit()
        }
      })
      
      // console.log(this.form);
    },
    /**
     * obj 当前层的对象
     * keysArr 每层key的数组 例如 company.companyName
     * value 要赋的值
     */
    setValueByKeys(obj, keysArr, value) {
      if (keysArr.length == 1) {
        obj[keysArr[0]] = value;
      } else {
        let p = keysArr[0];
        keysArr.shift();
        return this.setValueByKeys(obj[p], keysArr, value);
      }
    },
  },
};
</script>

<style scoped lang="scss">
.flex {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.content {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;

  h3 {
    font-size: 1.2em;
    line-height: 1.43;
    margin-top: 20px;
  }

  .notice {
    .text {
      padding: 16px;
      overflow: auto;
      line-height: 1.45;
      background-color: #f7f7f7;
      border: 0;
      border-radius: 3px;
    }
  }

  .query{
    .formWrap{
      max-height: 320px;
      overflow: hidden auto;
    }
  }

  .result {
    flex: 1;
    display: flex;
    flex-direction: column;
    overflow: hidden;

    .code {
      flex: 1;
      overflow-y: auto;
      width: 100%;
      background: rgb(54, 61, 68);
    }
    .text {
      word-break: break-all;
      color: #c3c6c4;
    }
  }
}

.demo-ruleForm {
  display: flex;
  flex-direction: row;
  gap: 20px;
  flex-wrap: wrap;

  ::v-deep {
    .el-form-item {
      width: 24%;
    }

    .el-select {
      width: 100%;
    }
  }
}
</style>

注意事项: 使用时记得用el-form和el-row包裹一下,因为默认使用布局和form包裹住