其他分享
首页 > 其他分享> > 基于 element ui 的多卡片组表单组件

基于 element ui 的多卡片组表单组件

作者:互联网

需求

设计一个基于 vue 和 element ui 的多卡片组单一表单组件,卡片组用于分类若干字段,比如个人信息、职业信息、技能信息。同时,将标签等文本抽离 HTML,方便后续增加语言模块。

分析

原始的 <el-form> 不支持批量设置字段,当页面中字段较多时,维护和修改 HTML 过于繁琐;原始的数据对象和规则对象完全扁平化,且无法分组,不便于与页面结构一 一对应,实现分层遍历的效果。

设计解决方案

方案 1

  1. 由于 HTML 是不需要动的,因此考虑将卡片的标题、表单字段的 label,model 和 rule 属性从 HTML 中抽离出来,作为配置参数,并单独创建一个结构化的对象(对应于卡片和字段的层级关系),存储以上 4 类信息
  2. 再通过 v-for 遍历结构化对象,并逐层生成卡片 <el-card> 和字段 <el-form-item>

相比于直接维护 HTML,维护 JS 对象需要的代码量少很多,且没有无关信息的干扰(属性绑定以及嵌套标签等,重复的任务很容易出错,且不易被发现)。

JS 对象结构:

// fields 下面的每个对象代表一个卡片,属性 label 是一个对象,用于存放各字段的标签名
fields {
  personalInfo {
    title: '',
    lable: {
      name: '',
      ...
    }
  ...
  }
},
// datas 对象用于存放字段绑定的数据
datas {
  personalInfo {
      name: '',
      ...
  }
},
// rules 对象用于存放字段绑定的数据
rules {
  personalInfo {
      name: '',
      ...
  }
}

存在的问题

方案 1 创建的结构化对象参考了 element ui ,将各信息分开存储,导致每次修改都要改多处,并且由于使用了嵌套结构,导致定位繁琐;且部分属性使用的数据类型不规范,如卡片组和标签组对象应该使用数组类型。

方案 2

统一成一个结构化对象:每个字段对象存储 label、model 和 rule 三个信息,字段外层再嵌套卡片对象

改进后的 JS 对象结构:

// fieldGroupList 作为整个结构化对象,存放所有数据,并根据所在卡片进行分组
fieldGroupList: [
 {
    fieldTitle: '个人基本信息',
    // fieldList 存放一个卡片内的多个字段,每个字段对象包含 label、val 和 rule 三个属性,对应标签名、数据和校验规则
    fieldList: [
      {
        key: 'name', // 用于标记一个字段
        label: '姓名',
        val: '',
        rule: [{ required: true, message: `请输入姓名`, trigger: 'blur' },],
      },
      ... // 其他字段
    ],
  },
  ... // 其他卡片
]

对应的 HTML:

<el-form label-width="100px">
  <div class="el-card-group">
    <el-card v-for="(fieldGroup, i) in fieldGroupList" :key="i">
      <div slot="header">
        <span>{{ fieldGroup.fieldTitle }}</span>
      </div>
      <el-form-item
        :label="fieldItem.label"
        :rules="fieldItem.rule"
        v-for="(fieldItem, i) in fieldGroup.fieldList"
        :key="i"
      >
        <el-input v-model="fieldItem.val"></el-input>
      </el-form-item>
    </el-card>
  </div>
  <div class="el-form-btn-group">
    <el-button type="primary" @click="submitForm('fieldGroupList')"
      >提交</el-button
    >
    <el-button @click="resetForm('fieldGroupList')">重置</el-button>
  </div>
</el-form>

存在的问题

由于不是直接通过原始的扁平化数据对象和规则对象进行验证,无法使用框架内置的验证、重置等方法,需要重写,因此还需要改进

方案3

在方案2的基础上:当初始化组件时,将结构化对象中 对应于各字段的数据变量和规则变量 还原成 element ui 可接受的扁平化数据对象和规则对象,以便直接使用框架内置的表单验证等方法。

data() {
  return {
    // 初始化数据对象和规则对象为空对象,并在 created 钩子方法中赋予其对应的数据
    ruleForm: {
      datas: {},
      rules: {},
    },
    fieldGroupList: [
      // 与方案2相同的结构化对象
    ],
  };
},
created() {
  this.setRuleForm(this.fieldGroupList, this.ruleForm); // 在初始化阶段调用转换方法,避免获取到空对象
},
methods: {
  // 定义转换方法
  setRuleForm(fieldGroupList, ruleForm) {
    let datas = {},
      rules = {};
    if (fieldGroupList) {
      fieldGroupList.map((fieldGroup) => {
        fieldGroup.fieldList.map((field) => {
          datas[field.key] = field.val;
          rules[field.key] = field.rule;
        });
      });
      ruleForm.datas = datas;
      ruleForm.rules = rules;
    } else {
      throw new Error('解析失败,无法显示表单项目');
    }
  },
  ... // 其他方法
}

对应的 HTML:

<el-form
  ref="ruleForm"
  :model="ruleForm.datas"
  :rules="ruleForm.rules"
  label-width="100px"
>
  <div class="el-card-group">
    <el-card v-for="(fieldGroup, i) in fieldGroupList" :key="i">
      <div slot="header">
        <span>{{ fieldGroup.fieldTitle }}</span>
      </div>
      <el-form-item
        :label="fieldItem.label"
        :rules="ruleForm.rules[fieldItem.key]"
        :prop="fieldItem.key"
        v-for="(fieldItem, i) in fieldGroup.fieldList"
        :key="i"
      >
        <el-input v-model="ruleForm.datas[fieldItem.key]"></el-input>
      </el-form-item>
    </el-card>
  </div>
  <div class="el-form-btn-group">
    <el-button type="primary" @click="submitForm('ruleForm')"
      >提交</el-button
    >
    <el-button @click="resetForm('ruleForm')">重置</el-button>
  </div>
</el-form>

至此,已经基本满足了需求。在保留了原组件方法的基础上,允许自定义多个卡片和多个字段,而且后续增删改字段也不需要动 HTML 。

目前尚未解决的问题

  1. 输入字段的类型 <input type="***"> 不能定制
    这个问题可以通过与上述类似的方式解决,即增加一个表示输入类型的属性并在 HTML 中绑定。但是这种实现方式会导致组件的维护越来越复杂和困难,因为标签拥有的属性都需要在 JS 对象中进行定义,每次扩展都需要直接修改 JS 对象,不符合开闭原则。
    稍微好一点的方法是通过单独定义一个对象,并注入到现有对象中,这样避免了直接修改原 JS 对象。
  2. 布局不能定制,比如需要某些特定字段显示在一行,某些字段独立成行。这些需求需要通过添加额外的 <el-row><el-col> 组件来解决,因此需要同步修改 HTML 部分,现有组件不能直接复用。目前的想法是通过分离卡片 <el-card> 和字段 <el-form-item> ,并在中间插入代表布局的行和列组件,同时,单独创建布局对象,存放布局相关参数并在行和列组件中绑定。同时,分离后的卡片和字段组件在其他场景下也能单独复用;行列组件也可以通过参数化的方式批量生成。

标签:...,卡片,对象,表单,rules,HTML,ui,组件,element
来源: https://www.cnblogs.com/cjc917/p/16146104.html