Bootstrap

使用el-form组件生成动态表单

当我们在后台管理系统中遇到很多表单需要填写的时候 使用el-from一行一行写出来 会导致代码又臭又长 难以维护 并且代码体积会非常大 现在可以看下面的代码开始操作了!

先看图
在这里插入图片描述

看到这里可能已经有朋友想起来用饿了么官网提供的el-form组件开始搓了 看代码

在这里插入图片描述
肯定是没问题的 但是想一下 如果这种表单 在一个页面同时出现N个类似的 或者在N个页面都需要这样的表单输入 怎么办呢 疯狂的定义el-form-item和校验规则疯狂的CV吗 肯定是不符合我们干完活想摸摸鱼的习惯了 此时此刻就可以看一下这个动态生成了
这里的关键内容是提前定义好一个el-form的HTML结构 然后准备一个json定义我们的表单内容 然后交给表单组件渲染就OK了 看代码
首先看我们的组件页面

<template>
  <div>
    <el-form
      :model="ruleForm"
      :rules="formRules"
      ref="ruleForm"
      :label-width="labelWidht ? labelWidht + 'px' : '180px'"
      class="demo-ruleForm"
      :disabled="isCheck"
      style
    >
      <div class="item_content">
        <el-row>
          <el-col
            :span="item.span ? item.span : 8"
            v-for="(item, index) in formDate"
            :key="index"
          >
            <el-form-item
              :label="item.label + ':'"
              :prop="item.prop"
              v-if="item.type == 'input'"
            >
              <el-input
                v-model="ruleForm[item.prop]"
                style="width: 100%"
                :value="item.defaultValue ? item.defaultValue : ''"
                :placeholder="item.placeholder"
                @input="handleInput(index, item.prop, $event)"
                :maxlength="item.maxlength"
                :disabled="item.disabled || false"
                :ref="'popoverRef_' + index"
                :rows="item.row ? item.row : 1"
              ></el-input>
            </el-form-item>
            <el-form-item
              :label="item.label + ':'"
              :prop="item.prop"
              v-if="item.type == 'date'"
            >
              <el-date-picker
                v-model="ruleForm[item.prop]"
                type="daterange"
                range-separator="至"
                start-placeholder="开始日期"
                end-placeholder="结束日期"
                style="width: 90%"
                :disabled="item.disabled || false"
              ></el-date-picker>
            </el-form-item>
            <el-form-item
              :label="item.label + ':'"
              :prop="item.prop"
              v-if="item.type == 'select'"
            >
              <div style="display: flex; justify-content: space-between">
                <el-select
                  v-model="ruleForm[item.prop]"
                  placeholder="请选择"
                  @change="selectChange(item, $event)"
                  :disabled="item.disabled || false"
                  style="width: 100%"
                  :style="{
                    width:
                      item.prop == 'thirdFrequency' &&
                      ruleForm[item.prop] == '其他'
                        ? '30%'
                        : '100%',
                  }"
                  :multiple="item.multiple"
                >
                  <el-option
                    v-for="item1 in item.options"
                    :key="item1.value"
                    :label="item1.label"
                    :value="item1.value"
                  ></el-option>
                </el-select>
              </div>
            </el-form-item>
            <el-form-item
              :label="item.label + ':'"
              :prop="item.prop"
              v-if="item.type == 'datePick'"
            >
              <el-date-picker
                v-model="ruleForm[item.prop]"
                type="date"
                placeholder="选择日期"
                style="width: 100%"
                :disabled="item.disabled || false"
                value-format="yyyy-MM-dd"
              ></el-date-picker>
            </el-form-item>
            <el-form-item
              :label="item.label + ':'"
              :prop="item.prop"
              v-if="item.type == 'datePickRange'"
            >
              <el-date-picker
                style="width: 100%"
                v-model="ruleForm[item.prop]"
                type="daterange"
                range-separator="至"
                start-placeholder="开始日期"
                end-placeholder="结束日期"
                unlink-panels
                :disabled="item.disabled || false"
                value-format="yyyy-MM-dd"
              ></el-date-picker>
            </el-form-item>
          </el-col>
          <el-col>
            <slot name="customItem"></slot>
          </el-col>
        </el-row>
      </div>
    </el-form>
  </div>
</template>

<script>
export default {
  props: {
    formDate: {
      type: Array,
      default: () => [],
    },
    labelWidht: {
      type: Number,
    },
    isCheck: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      ruleForm: {}, // 初始化为空对象,以便动态绑定属性
      formRules: {}, // 用于存放动态生成的校验规则
    };
  },
  watch: {
    formDate: {
      handler(newval) {
        if (newval) {
          // console.log("我被通知修改了");
          this.initForm();
        }
      },
      deep: true,
    },
  },
  mounted() {
    this.initForm();
  },
  methods: {
  	/**
     考虑到很多内容应会根据select的变化而变化 所以这里添加了一个回调函数 直接在JSON中定义好函数名 
  	然后就可以在组件中使用了 
     */ 
    selectChange(item, event) {
      if (item.cb) {
        item.cb(this.ruleForm[item.prop], event);
      }
      this.$emit("redraw");
    },
    initForm() {
      // 初始化表单数据
      const initialData = {};
      this.formDate.forEach((item) => {
        if (item.defaultValue) {
          initialData[item.prop] = item.defaultValue;
        }
      });

      this.ruleForm = initialData;
      // 生成校验规则
      this.generateFormRules();
    },
    generateFormRules() {
      const rules = {};
      this.formDate.forEach((item) => {
        if (item.rules && item.rules.length > 0) {
          rules[item.prop] = item.rules.map((rulesItem) => {
            return {
              required: true,
              message: `${item.label}${rulesItem}`,
              trigger: "blur",
            };
          });
        }
      });
      this.formRules = rules;
      this.$nextTick(() => {
      	// 生成之后需要清空一下 否则会触发校验
        this.$refs["ruleForm"].clearValidate();
      });
    },
    handleInput(index, prop, event) {
      this.$forceUpdate();
    },
    submitForm(status) {
      let formData = {};
      this.$refs.ruleForm.validate((valid) => {
        if (valid) {
          formData = this.ruleForm;
        }
      });
      return formData;
    },
    resetForm() {
      this.$refs.ruleForm.resetFields();
    },
    clearRule() {
      this.$refs.ruleForm.clearValidate();
    },
    setOther(val) {
      this.$set(this.ruleForm, "thirdFrequencyOther", val);
    },
  },
};
</script>

<style scoped lang="scss">
.demo-ruleForm {
  padding: 6px 0 15px 0;
  font-weight: normal;
  ::v-deep label {
    font-weight: 500 !important;
  }
}
.tip_icon {
  position: absolute;
  right: -2%;
  top: -18%;
  background: #fff;
}
.item_content {
  // width: 90%;
}
.item_btn {
  width: 10%;
  line-height: 40px;
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
}
::v-deep {
  .el-form-item {
    margin-top: 10px !important;
    margin-bottom: 10px;
  }
}
</style>

引入组件
<template>
  <div class="form_container">
    <prodcutForm :labelWidht="180" :formDate="formData" ref="product">
    </prodcutForm>

    <div style="text-align: center">
      <el-button type="primary" @click="submit">确定</el-button>
    </div>
  </div>
</template>

<script>
import formData from "./mixin.js";
import prodcutForm from "@/components/customForm";
export default {
  mixins: [formData],
  components: { prodcutForm },
  data() {
    return {};
  },
  methods: {
    submit() {
      const data = this.$refs.product.submitForm();
      console.log(data);
    },
  },
  mounted() {},
};
</script>

<style lang="scss" scoped>
.form_container {
  background-color: #fff;
  padding: 20px;
}
</style>

JSON数据

这里的JSON数据我使用了mixin的方法 嫌麻烦的同学也可以直接在data中写入

export default {
  data() {
    return {
      formData: [
        {
          prop: "expectedProductName",
          label: "产品名称",
          defaultValue: "",
          placeholder: "请输入产品名称?",
          type: "input",
          rules: ["不能为空"],
        },
        {
          prop: "keyCustomerGroup",
          label: "产品面向的主要客群",
          defaultValue: "",
          placeholder: "是否有限定客群?",
          type: "input",
          rules: ["不能为空"],
        },
        {
          prop: "pricing",
          label: "产品定价",
          defaultValue: "",
          placeholder: "如价格固定20000",
          type: "input",
          rules: ["不能为空"],
        },
        {
          prop: "contentsInclude",
          label: "产品包含什么内容",
          defaultValue: "",
          placeholder: "请问要销售什么产品",
          type: "input",
        },
        {
          prop: "salesChannels",
          label: "产品的销售渠道",
          defaultValue: "",
          placeholder: "预计在哪个平台/渠道销售",
          type: "input",
        },
        {
          prop: "invoiceRequire",
          label: "发票要求",
          defaultValue: "",
          placeholder: "是否需要提供发票",
          type: "input",
        },
        {
          prop: "saleType",
          label: "产品销售方式",
          type: "select",
          options: [
            {
              label: "线上销售",
              value: "线上销售",
            },
            {
              label: "线下销售",
              value: "线下销售",
            },
          ],
          defaultValue: "",
        },
        {
          prop: "payMode",
          label: "产品支付方式",
          type: "select",
          multiple: true,
          options: [
            {
              label: "微信",
              value: "微信",
            },
            {
              label: "支付宝",
              value: "支付宝",
            },
            {
              label: "银行卡",
              value: "银行卡",
            },
            {
              label: "现金",
              value: "现金",
            },
          ],
          cb: (val) => {
            this.changePayMode(val);
          },
          defaultValue: "",
        },
        {
          prop: "saleTime",
          label: "产品销售日期",
          type: "datePickRange",
          defaultValue: "",
        },
      ],
    };
  },
};

觉得有用的可以点个赞嗷 谢谢各位!

;