【vue】基于element-ui动态表单的封装

1,345 阅读1分钟

项目介绍

由于公司目前做的是一个后台管理系统、大部分页面是基于表单+表格的形式作为展现。所以在为了不重复做工的情况下、封装了一个模板页、一个动态表单、一个动态表格。

下面主要介绍一下,如何封装一个动态表单。

  • 动态配置
  • 插件扩展
  • 校验规则 动态配置,我们想要实现的表单应该是一种类似JSON的一串参数,根据传入的参数表单会进行自动的配置展现。那接下来我们来看看一下如何做到传入即展示。 首先应该把拆分为两个部分。form文件主要作为

form.vue文件

<template>
  <div class="eppForm">
    <nb-form v-bind="config" @submit.native.prevent>
      <nb-row :gutter="config.gutter">
        <template v-for="(item, index) in config.formItem">
          <nb-col  :key="index" :span="config.span">
            <nb-form-item :label="item.label" :prop="item.model">
              <eppFormComponent
                :value="config.formData[item.model]"
                :componentConfig="item"
                @update="handleUpdateFormComponent"
              >
              </eppFormComponent>
            </nb-form-item>
          </nb-col>
        </template>
        <nb-form-item v-if="!!config.btn.length">
          <nb-button
            v-for="(button, index) in config.btn"
            :key="index"
            :type="button.type"
            :size="button.size"
            @click="handleClick(button)"
            >{{ button.name }}</nb-button
          >
        </nb-form-item>
      </nb-row>
    </nb-form>
  </div>
</template>

<script>
import eppFormComponent from './epp-form-component';
export default {
  name: 'eppForm',
  props: {
    formConfig: {
      type: Object,
      required: true,
      default: () => {},
    },
  },
  components: { eppFormComponent },
  data() {
    return {
      defaultConfig: {
        ref: 'eppForm',
        labelPosition: 'right', // 默认为右对齐
        inline: true, // 默认为横向展示
        size: 'small',
        labelWidth: '80px', // 默认label为80
        buttonShow: true, // 设置查询按钮默认存在
        btn: [], // 表单按钮
        gutter: 0, // 属性来指定每一栏之间的间隔,默认间隔为 0
        span: 8, // 默认每行配置为8的行宽
      },
    };
  },
  computed: {
    // 为props增加默认配置
    config() {
      return Object.assign(this.defaultConfig, this.formConfig);
    },
  },
  methods: {
    handleClick(btn) {
      btn.onClick && btn.onClick();
    },
    handleUpdateFormComponent({ key, value }) {
      this.formConfig.formData[key] = value;
      this.formConfig.onUpdateData
        && this.formConfig.onUpdateData(...arguments);
    },
    getFormData() {
      return this.$props.formConfig.formData || {};
    },
    validate(callback) {
      const { ref } = this.config;
      this.$refs[ref].validate(callback);
    },
    resetForm() {
      const { ref } = this.config;
      this.$refs[ref].resetFields();
      return this.config.formData;
    },
  },
};
</script>

<style  scoped>
/* /deep/ .nb-form--inline .nb-form-item{
  width:100%;
}
/deep/.nb-form--inline .nb-form-item__content {
  width:calc(100% - 80px);
} */
</style>


formComponent.vue文件

<template>
    <component
      v-bind="componentConfig"
      :is="componentType(componentConfig.componentType)"
      v-model="controlModel"
    >
      <!-- 判断是否有对应的children、并且是否有对应的options -->
      <template
        v-if="componentConfig.children && componentConfig.children.options"
      >
        <component
          v-for="(op, index) in componentConfig.children.options"
          :label="op.label || op[componentConfig.children.label]"
          :value="op.value || op[componentConfig.children.value]"
          :is="componentType(componentConfig.children.type || 'option')"
          :key="index"
        ></component>
      </template>
    </component>
</template>

<script>
export default {
  name: 'eppFormComponent',
  props: {
    componentConfig: {
      type: Object,
      required: true,
      default: () => {},
    },
    value: [Array, String, Number, Object, Boolean, Date],
  },
  data() {
    return {
      controlModel: this.value,
    };
  },
  watch: {
    value(newValue) {
      this.controlModel = newValue;
    },
    controlModel(newValue) {
      const key = this.$props.componentConfig.model;
      this.$emit('update', { key, value: newValue });
    },
  },
  computed: {
    formData() {
      console.log(this.$parent.$props);
    },
  },
  methods: {
    componentType(type) {
      return `nb-${type}`;
    },
  },
};
</script>