总结form表单的三种封装方法(Vue+ElementUI)

2021-06-25 10:13:19 浏览数 (2)

1.首先是最普通,也是大家最先想到的方法,直接封装:

代码语言:javascript复制
<template>
  <el-form
    :inline="true"
    :model="value"
    label-position="right"
    :label-width="formConfig.labelWidth"
    :rules="rules"
    size='mini'
  >
    <slot name="formItem" />
    <el-form-item
      v-for="(item,index) in formConfig.formItemList"
      :key="index"
      :label="item.label"
      :prop="item.prop"
    >
      <el-input
        v-if="item.type=='input'"
        v-model="value[item.prop]"
        :disabled="item.disabled"
        :clearable="true"
        :placeholder="item.placeholder"
      ></el-input>
      <el-select
        :clearable="true"
        v-else-if="item.type=='select'"
        v-model="value[item.prop]"
        :disabled="item.disabled"
        :placeholder="item.placeholder"
      >
        <el-option
          v-for="(optItem,index) in item.optList"
          :key="index"
          :label="optItem.label"
          :value="optItem.value"
        ></el-option>
      </el-select>
      <el-date-picker
        :value-format="item.dateFormate"
        v-else
        v-model="value[item.prop]"
        :type="item.type"
        :disabled="item.disabled"
        :clearable="true"
        :placeholder="item.label"
      ></el-date-picker>
    </el-form-item>
    <div class="searchBtn">
      <el-button-group>
        <el-button
          v-for="(item, index) in formConfig.operate"
          :key="index"
          size="small"
          :type="item.type"
          :icon="item.icon"
          @click="item.handleClick"
        >{{item.name}}
        </el-button>
      </el-button-group>
      <slot name="operate"></slot>
    </div>
  </el-form>
</template>
<script>
export default {
  components: {},
  props: {
    formConfig: {
      type: Object,
      required: true
    },
    value: {
      type: Object,
      required: true
    },
    rules: {
      type: Object
    }
  },
  computed: {},
  methods: {
    setDefaultValue() {
      const formData = { ...this.value };
      // 设置默认值
      this.formConfig.formItemList.forEach(({ key, value }) => {
        if (formData[key] === undefined || formData[key] === null) {
          formData[key] = value;
        }
      });
      this.$emit("input", formData);
    }
  },
  mounted() {
    this.setDefaultValue();
  }
};
</script>

第一种方法简单明了,基本能够处理解决大多数的表单问题,并且与slot的完美组合,已经可以达到我们的需求要求。

2.实现表单动态渲染、可视化配置的方法,动态表单又可以分为两种方法:

首先,需要配置 el-form :

代码语言:javascript复制
<template>
  <el-form
          class="dynamic-form"
          :inline="formConfig.inline"
          :model="value"
          :label-position="formConfig.labelPosition"
          :label-width="formConfig.labelWidth"
          :size='formConfig.size'
          :status-icon="formConfig.statusIcon">
    <slot/>
  </el-form>
</template>
<script>
  export default {
    props: {
      formConfig: {
        type: Object,
        required: true
      },
      value: {
        type: Object,
        required: true
      }
    },
    methods: {
      setDefaultValue() {
        const formData = { ...this.value };
        // 设置默认值
        this.formConfig.formItemList.forEach(({ key, value }) => {
          if (formData[key] === undefined || formData[key] === null) {
            formData[key] = value
          }
        });
        this.$emit('input', formData)
      }
    },
    mounted() {
      this.setDefaultValue()
    },
  }
</script>

第二步,开始渲染 form-item :

  • 第一种,利用 vue 内置的 component 组件,写起来可能像这样:
代码语言:javascript复制
<el-form-item>
 <component :is="`el-${item.type}`" />
</el-form-item>
  • 第二种,使用 v-if 逐个判断:
代码语言:javascript复制
<el-form-item>
 <el-input v-if="item.type === 'input'" />
 <span v-else>未知控件类型</span>
</el-form-item>

考虑到每种表单控件的处理逻辑千差万别,这里本博主先介绍第一种(为了防止有人照搬,这里本博主只做简单介绍,不懂得私信):

代码语言:javascript复制
<template>
  <el-form-item :label="label">
    <el-select v-model="currentValue" @input="onInputEvent" size="small">
      <el-option
              v-for="item in options"
              :key="item.value"
              :label="item.label"
              :value="item.value">
      </el-option>
    </el-select>
  </el-form-item>
</template>

<script>
  import formMixins from '~/components/Form/iForm/form-model'
  export default {
    name: "SelectList",
    props: ['name', 'label', 'value', 'options'],
    mixins: [formMixins],
    data() {
      return {
        currentValue: this.value
      }
    }
  }
</script>

由于每个表单组件都是监听父元素的value值变化,数据变化时都是触发onInputEvent并执行this.$emit(‘input’),所以我们可以把这部分内容抽取出来放在mixins里面。

代码语言:javascript复制
export default {
    props: ['name', 'value'],

    data () {
        return {
            currentValue: this.value
        };
    },
    methods: {
        onInputEvent(value) {
            this.$emit('input', this.name, value);
        },
        reset() {
            this.currentValue = "";
        }
    },
    watch: {
        value (val) {
            this.currentValue = val;
        }
    }
};

动态生成表单

这里主要是根据配置的数据,循环生成表单组件。默认提供提交和重置按钮,如果不需要可以通过slot传递其他操作按钮。这里的要点主要有: 监听表单组件的数据变化: 每个表单组件都有一个name标识它的业务含义,绑定的数据也是formData[field.name],@input事件传递updateForm,在updateForm里面更新this.formData[name],保证了this.formData里面的数据是和表单组件选择/填写的内容一致。 重置时改变表单组件的数据: 因为组件内部会监听父元素的value,所以这里只要清空this.formData的值,组件内部的数据也会跟着清空。

代码语言:javascript复制
<template>
  <div>
    <el-form :inline="true" ref="form" :model="formData" class="demo-form-inline">
      <el-col :span="field.cols" v-for="(field, index) in config.fieldsConfig" v-bind:key="index">
        <component :key="index"
                   :is="field.fieldType"
                   :label="field.label"
                   :value="formData[field.name]"
                   :multiple="field.multiple"
                   @input="updateForm"
                   v-bind="field"
                   :options="field.options"
                   :ref="field.name"
        >
        </component>
      </el-col>
      <slot name="buttons">
        <el-button type="primary" @click="submit" size="small">{{onSubmitText}}</el-button>
        <el-button type="default" @click="reset" size="small">{{onResetText}}</el-button>
      </slot>
    </el-form>
  </div>
</template>

动态加载的第二种方法不多介绍,只提醒一句,注意 v-model 的绑定问题, 组件内不能直接修改props 。

温馨提示:

你需要知道 v-model 的工作原理 :

代码语言:javascript复制
<input v-model="something">

这不过是以下示例的语法糖:

代码语言:html复制
<input
 :value="something"
 @input="something = $event.target.value">

0 人点赞