ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2022-04-14 19:00:43  阅读:255  来源: 互联网

标签:... 卡片 对象 表单 rules HTML ui 组件 element


需求

设计一个基于 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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有