Table表格合并

picture

vue+elementUI 中的 Table 表格的合并功能,可配置,使用方便。

1、合并前后效果图

beforeMerge.png
afterMerge.png

2、代码实现

<template>
  <div class="hello">
    {{msg}}
    <div>
      <el-table
        :data="tableData"
        :span-method="arraySpanMethod"
        border
        style="width: 1000px;margin:30px auto;"
        height="550"
      >
        <el-table-column
          v-for="(item, index) in tableTitleData"
          :key="index"
          :prop="item.prop"
          :label="item.label"
          :min-width="item.minWidth"
        >
          <template slot-scope="scope">
            <span>{{scope.row[item.prop]}}</span>
          </template>
        </el-table-column>
      </el-table>
    </div>
  </div>
</template>

<script>
  import { tableTitleData, tableData } from './js/options';
  export default {
    name: 'mergeTable',
    data() {
      return {
        msg: '表格合并',
        tableData,
        tableTitleData
      };
    },
    methods: {
      // ====================================== 合并多列单元格逻辑
      arraySpanMethod({ row, column, rowIndex, columnIndex }) {
        // 合并单元格
        /**
         * desc 该方法会触发n次,n是表格data的length*表头data的length;
         * @param  {String} row         [ 当前遍历到的行数据 (后台接口返回的) ]
         * @param  {Object} column      [ 当前遍历到的列数据 (elemen表格对象内置的) ]
         * @param  {Number} rowIndex    [ 当前遍历到的行下标 ]
         * @param  {Number} columnIndex [ 当前遍历到的列下标 ]
         * @return {Object} 包含合并的行和列信息
         */
        //  ================================================================ 分割线
        if (!(this.realTitleData || []).length) {
          this.realTitleData = []; // 这个数组是带children的表头data展开children后的数组
          this.tableTitleData.forEach(val => {
            // 展开存在children的titleData
            if (val.children && (val.children || []).length) {
              val.children.forEach(child => {
                this.realTitleData.push(child);
              });
            } else {
              this.realTitleData.push(val);
            }
          });
        }
        if (!(this.mergeTabelArr || []).length) {
          // 只运行一次,获得要合并的信息数组
          const keywords = ['areaName']; // (这个数组要根据不同表格来配置!!!) 一些prop名的集合,这些porp名代表的字段 在每个做过合并的行数据集合里 它的的值 都是唯一的(比如区域、片区、中心名称之类的)。这个集合用于确保合并信息的唯一性,确保表格不会错乱
          this.mergeTabelArr = this.getMergeTabelArr(
            this.tableData,
            this.realTitleData,
            keywords,
            [0, 1]
          );
          // console.log('完整的合并表格的信息集合', this.mergeTabelArr);
        }
        // if (columnIndex > 0) { // 除掉序号那一列(这里用于自定义过滤某列),都要合并
        const _rowArr = (
          this.mergeTabelArr.find(n => n.key === column.property) || {}
        ).connect; // connect数组
        if (_rowArr) {
          const _row = _rowArr[rowIndex];
          return {
            rowspan: _row, // 0代表删除这列,1代表展示这列,>1代表合并列
            colspan: _row > 0 ? 1 : 0 // 0代表删除这列,1代表展示这列,>1代表合并列
          };
        }
        // }
      },
      getMergeTabelArr(
        data = [],
        titleData = [],
        keywords = [],
        colIndexs = []
      ) {
        // 获取合并表格的信息集合
        /**
         * desc 该方法为最终组合‘合并表格的信息集合’的方法
         * @param  {Array}  data                   [ 表格的data ]
         * @param  {Array}  titleData              [ 表格的表头的data ]
         * @param  {Array}  keywords  ['1','a'...] [ 一些prop名的集合,这些porp名代表的字段 在每个做过合并的行数据集合里 它的的值 都是唯一的(比如区域、片区、中心名称之类的)。这个集合用于确保合并信息的唯一性,确保表格不会错乱 ]
         * @param  {Array}  colIndexs [0,1,-2,...] [ 合并规则数组,这个数组里所有的负数值代表不合并的列的下标,正数代表合并的列的下标 ]
         * @return {Array} arr  示例:
         * [
         *   {
         *     key: 'nextSite', // 对应列的prop名
         *     connect: [2, 0, 2, 0, 1]  // connect数组
         *   },
         *   ...
         * ];
         */
        //  ================================================================ 分割线

        const columnArr = colIndexs.length
          ? titleData.filter((n, i) => {
              const index = colIndexs.findIndex(realV => {
                if (realV < 0) return false;
                const v = Math.abs(realV);
                return i === v;
              });
              return index !== -1;
            })
          : []; // 按传递进来的规则过滤

        this.oldMergeTabel = []; // 给this添加oldMergeTabel属性,防止后面报错
        const arr = columnArr.map(v1 => {
          const obj = this.getConnectArr(
            data,
            v1.prop,
            keywords,
            this.oldMergeTabel
          );
          this.oldMergeTabel = obj.connect; // 保存前一条的connect数组信息
          return obj;
        });
        return arr;
      },
      getConnectArr(data, key, keywords, oldMergeTabel) {
        // 获取connect数组
        /**
         * desc 该方法会触发n次,n是过滤后的,要合并的表头data的length;
         * @param  {Array}  data          [ 表格的data数据 ]
         * @param  {String} key           [ 要合并的表头的prop名 ]
         * @param  {Array}  keywords      [ 一些prop名的集合,这些porp名代表的字段 在每个做过合并的行数据集合里 它的的值 都是唯一的(比如区域、片区、中心名称之类的)。这个集合用于确保合并信息的唯一性,确保表格不会错乱 ]
         * @param  {Object} oldMergeTabel [ 前一条组合好的connect数组信息,用于确保表格不会错乱 ]
         * @return {Object} 包含合并的行和列信息(其实就是 ‘合并表格的信息数组’ 的一个成员,看上面getMergeTabelArr方法的示例)
         */
        //  ================================================================ 分割线

        const oldArr = []; // 保存上一个数组
        let oldObj = {}; // 控制只跟上一个元素做对比,不跟整个数组对比

        // 正确的代码
        data.forEach((v, i) => {
          let newObj;
          let newKey = ''; // 保证唯一性的key
          keywords.forEach(k => {
            newKey += `${v[k]}-`;
          });
          newKey += v[key];

          const oldIndex = oldArr.findIndex(n => n.key === newKey); // 保存的数组里是否存在与当前表头数据一样的元素(注意,findIndex找到的是第一个匹配的元素的下标)
          if (oldIndex !== -1 && oldObj.key === newKey) {
            newObj = {
              key: newKey,
              value: 0
            };
            oldArr[oldIndex].value++; // 找到重复项的第一项,值加1
          } else {
            newObj = {
              key: newKey,
              value: 1
            };
          }
          oldArr.push(newObj);
          oldObj = newObj; // 保存当前数据,用作与下一条数据的对比,看是否相同,相同把下一条数据的占用列置成0,这样表格不会错乱
        });
        const connect = oldArr.map(n => n.value);

        if (oldMergeTabel.length) {
          // 控制数组合并。如果有重复项也不能跨越过上一条的合并行
          oldMergeTabel.forEach((oldVal, i) => {
            const newVal = connect[i]; // 新值
            let count = 0; // connect数组各项值的和,
            const rule1 = newVal > oldVal && oldVal > 0;
            const rule2 = newVal < oldVal && newVal === 0;
            if (rule1 || rule2) {
              connect[i] = oldVal;
              connect.forEach(v => {
                count += v;
              }); // 赋值后计算connect数组各项值的和
              if (count > data.length) {
                // connect数组各项值加起来不能超过表格数据data的length,(可能有bug,以后再解决吧,哈哈哈)
                connect[i] = count - data.length;
              }
            }
          });
        }
        return {
          key: key,
          connect: connect
        };
      },
      // 查询数据
      queryData() {
        // 在工作中,一般是要调接口去查询数据,在查询之前,需要将mergeTabelArr置为空数组,然后在执行之后的操作。
        this.mergeTabelArr = [];
        // ...
      }
    }
  };
</script>
<style>
  .tableHeader {
    background: red !important;
  }
</style>

3、options.js 数据来源文件

/**
 * 表头配置
 */

const tableTitleData = [
  {
    minWidth: '100',
    prop: 'areaName',
    label: '片区'
  },
  {
    minWidth: '100',
    prop: 'managementAreaName',
    label: '省区'
  },
  {
    minWidth: '100',
    prop: 'centerName',
    label: '市区'
  }
];
const tableData = [
  {
    areaName: '东北',
    managementAreaName: '黑龙江',
    centerName: '哈尔滨市'
  },
  {
    areaName: '东北',
    managementAreaName: '黑龙江',
    centerName: '齐齐哈尔市'
  },
  {
    areaName: '东北',
    managementAreaName: '黑龙江',
    centerName: '鹤岗市'
  },
  {
    areaName: '东北',
    managementAreaName: '黑龙江',
    centerName: '牡丹江市'
  },
  {
    areaName: '东北',
    managementAreaName: '黑龙江',
    centerName: '佳木斯市'
  },
  {
    areaName: '东北',
    managementAreaName: '黑龙江',
    centerName: '绥化市'
  },
  {
    areaName: '东北',
    managementAreaName: '吉林',
    centerName: '长春市'
  },
  {
    areaName: '东北',
    managementAreaName: '吉林',
    centerName: '磐石市'
  },
  {
    areaName: '东北',
    managementAreaName: '吉林',
    centerName: '舒兰市'
  },
  {
    areaName: '华东',
    managementAreaName: '上海',
    centerName: '青浦区'
  },
  {
    areaName: '华东',
    managementAreaName: '上海',
    centerName: '徐汇区'
  },
  {
    areaName: '华东',
    managementAreaName: '上海',
    centerName: '黄浦区'
  },
  {
    areaName: '华东',
    managementAreaName: '上海',
    centerName: '浦东新区'
  },
  {
    areaName: '华东',
    managementAreaName: '上海',
    centerName: '松江区'
  },
  {
    areaName: '华东',
    managementAreaName: '上海',
    centerName: '嘉定区'
  }
];
export { tableTitleData, tableData };

4、完整 Table 合并代码 Demo

Github Demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342