table表格动态合并
最终效果图:
一、JQ插件
参考链接:http://www.jq22.com/jquery-info9377
插件封装:
(function ($) { $.fn.tablesMergeCell = function (options) { let defaults = { data: {}, // 后台数据 automatic: true, // 是否自动合并 cols: null, // 用数组表示列的索引,从0开始,然后根据指定列来处理(合并)相同内容单元格 rows: null, // [[3, 4, 5], [6, 7]]自定义合并的行范围 }; let opts = $.extend(defaults, options); // 数据初始化 let init = () => { let title = document.createElement('tr'); for (let item of opts.data.title) { let th = document.createElement('th'); th.innerHTML = item; title.append(th); } $(this).append(title); for (let rows of opts.data.content) { let tr = document.createElement('tr'); for (let item of rows) { let td = document.createElement('td'); td.innerHTML = item; tr.append(td); } $(this).append(tr); } }; // 如果对javascript的closure和scope概念比较清楚, 这是个插件内部使用的private方法 let tablesMergeRows = ($table, colIndex, rowIndex) => { $table.data('col-content', ''); // 存放单元格内容 $table.data('col-rowspan', 1); // 存放计算的rowspan值 默认为1 $table.data('col-td', $()); // 存放发现的第一个与前一行比较结果不同td(jQuery封装过的), 默认一个"空"的jquery对象 $table.data('trNum', $('tbody tr', $table).length); // 要处理表格的总行数, 用于最后一行做特殊处理时进行判断之用 // 我们对每一行数据进行"扫描"处理 关键是定位col-td, 和其对应的rowspan $('tbody tr', $table).each(function (index) { let $tr = $(this); // td:eq中的colIndex即列索引 let $td = $('td:eq(' + colIndex + ')', $tr); let currentContent = $td.html(); if (opts.automatic) { // 内容 // 第一次时走此分支 // console.log($table.data('col-content')); if ($table.data('col-content') === '') { $table.data('col-content', currentContent); $table.data('col-td', $td); } else { // 上一行与当前行内容相同 if ($table.data('col-content') == currentContent) { addRowspan(); // 上一行与当前行内容相同则col-rowspan累加, 保存新值 } else { newRowspan(); // 上一行与当前行内容不同 } } } else { // 指定 if (opts.rows.length > 0) { if (opts.rows[0].length == undefined) { for (let i = 0; i < opts.rows.length; i++) { customRowspan(opts.rows[i], opts.rows.length); } } else { for (let i = 0; i < opts.rows[rowIndex].length; i++) { customRowspan(opts.rows[rowIndex][i], opts.rows[rowIndex].length); } } } } function customRowspan(val, len) { if (index == val) { if ($table.data('col-content') == '') { if (currentContent == '') { currentContent = true; } $table.data('col-content', currentContent); $td.attr('rowspan', len); } else { $td.hide(); } } } function addRowspan() { let rowspan = $table.data('col-rowspan') + 1; $table.data('col-rowspan', rowspan); // 值得注意的是 如果用了$td.remove()就会对其他列的处理造成影响 $td.hide(); // 最后一行的情况比较特殊一点 // 比如最后2行 td中的内容是一样的, 那么到最后一行就应该把此时的col-td里保存的td设置rowspan if (++index == $table.data('trNum')) { $table.data('col-td').attr('rowspan', $table.data('col-rowspan')); } } function newRowspan() { // col-rowspan默认为1, 如果统计出的col-rowspan没有变化, 不处理 if ($table.data('col-rowspan') !== 1) { $table.data('col-td').attr('rowspan', $table.data('col-rowspan')); } // 保存第一次出现不同内容的td, 和其内容, 重置col-rowspan $table.data('col-td', $td); $table.data('col-content', $td.html()); $table.data('col-rowspan', 1); } }); }; // 合并列 let tablesMergeCols = ($table, cols) => { $table.data('col-content', ''); // 存放单元格内容 $table.data('col-colspan', 1); // 存放计算的cols值 默认为1 $table.data('col-td', $()); // 存放发现的第一个与前一行比较结果不同td(jQuery封装过的), 默认一个"空"的jquery对象 $table.data('trNum', $('tbody tr', $table).length); // 要处理表格的总行数, 用于最后一行做特殊处理时进行判断之用 $('tbody tr', $table).each(function (index, item) { for (let j = 0; j < cols.length; j++) { let $td = $('td:eq(' + cols[j] + ')', item); let currentContent = $td.html(); if (opts.automatic) { // 内容 // 第一次时走此分支 if ($table.data('col-content') === '') { $table.data('col-content', currentContent); $table.data('col-td', $td); } else { // 上一列与当前行内容相同 if ($table.data('col-content') == currentContent) { // 上一列与当前列内容相同则col-rowspan累加, 保存新值 addColspan(); } else { // 上一列与当前列内容不同 newColspan(); } } } function addColspan() { let colspan = $table.data('col-colspan') + 1; $table.data('col-colspan', colspan); // 值得注意的是 如果用了$td.remove()就会对其他列的处理造成影响 $td.hide(); // 比如最后1行 连续多列td中的内容是一样的, 那么到最后一行就应该把此时的col-td里保存的td设置colspan if (index++ == $table.data('trNum') && j === cols.length - 1) { $table.data('col-td').attr('colspan', $table.data('col-colspan')); } } function newColspan() { if ($table.data('col-colspan') !== 1) { $table.data('col-td').attr('colspan', $table.data('col-colspan')); } // 保存第一次出现不同内容的td, 和其内容, 重置col-rowspan $table.data('col-td', $td); $table.data('col-content', $td.html()); $table.data('col-colspan', 1); } } }); }; // 同样是个private函数 清理内存之用 let dispose = $table => { $table.removeData(); }; return this.each(() => { let cols = opts.cols, rows = opts.rows; init(); if (rows === null) { for (let i = cols.length - 1; cols[i] != undefined; i--) { tablesMergeRows($(this), cols[i]); } } else { for (let i = cols.length - 1, k = opts.rows.length - 1; cols[i] !== undefined; i--, k--) { tablesMergeRows($(this), cols[i], k); } } if (cols !== null) { tablesMergeCols($(this), cols); } dispose($(this)); }); }; })(jQuery);
页面调用:
$('#table').tablesMergeCell({ data: data.data, // automatic: false, cols: [0, 1, 2, 3], // rows: [3, 4], });
二、element-ui动态合并行列
参考链接:https://www.jianshu.com/p/cd34129cbfce
element-ui通过给table
传入span-method
方法可以实现合并行或列,方法的参数是一个对象,里面包含当前行row
、当前列column
、当前行号rowIndex
、当前列号columnIndex
四个属性。该函数可以返回一个包含两个元素的数组,第一个元素代表rowspan
,第二个元素代表colspan
。 也可以返回一个键名为rowspan
和colspan
的对象。
// data private tableHeader: string[] = []; private tableData: object[] = []; private mergeConfig: any = { position: 0, rowsArr: [], colsArr: [], colsRange: [0, 3], }; created() { this.init(); } activated() { // } mounted() { // } // 初始化函数 init() { this.getData(); // 挂载函数 this.getRowsArr(this.tableData); this.getColsArr(this.tableData); } getData() { let title = ['系统', '楼层', '区域', '机组编号', '1号', '2号', '3号', '合计', ]; this.tableHeader = title; let data = [ ['系统1', '楼层1', '区域1', '设备1', 1, 2, 3, 4], ['系统1', '楼层1', '区域2', '设备2', 1, 2, 3, 4], ['系统1', '楼层1', '区域2', '设备3', 1, 2, 3, 4], ['系统1', '楼层2', '区域3', '设备4', 1, 2, 3, 4], ['系统1', '楼层2', '区域3', '设备5', 1, 2, 3, 4], ['系统1', '楼层2', '区域3', '设备6', 1, 2, 3, 4], ['系统1', '合计', '合计', '合计', 1, 2, 3, 4], ['系统2', '楼层A', '区域4', '设备7', 1, 2, 3, 4], ['系统2', '楼层B', '区域5', '设备8', 1, 2, 3, 4], ['系统2', '楼层B', '区域5', '设备9', 1, 2, 3, 4], ['系统2', '合计', '合计', '合计', 1, 2, 3, 4], ['系统3', '楼层C', '区域6', '设备10', 1, 2, 3, 4], ['系统3', '楼层C', '区域7', '设备11', 1, 2, 3, 4], ['系统3', '楼层C', '区域8', '设备12', 1, 2, 3, 4], ['系统3', '合计', '合计', '合计', 1, 2, 3, 4], ]; let obj = new Object(); data.forEach((item, index) = > { item.forEach((_item, _index) = > { obj[title[_index]] = _item; }); this.tableData.push(obj); // 清空对象 obj = new Object(); }); } // 处理纵向数据 getRowsArr(data: any[]) { let spanArr: number[] = []; for (let prop in data[0]) { for (let i = 0; i < data.length; i++) { if (i === 0) { spanArr.push(1); this.mergeConfig.position = 0; } else { if (data[i - 1][prop] === data[i][prop]) { spanArr.push(0); spanArr[this.mergeConfig.position] += 1; } else { spanArr.push(1); this.mergeConfig.position = i; } } } this.mergeConfig.rowsArr.push(spanArr); spanArr = []; } } // 处理横向数据 getColsArr(data: any[]) { let spanArr: number[] = []; let keyArr = Object.keys(data[0]); for (let item of data) { for (let i = this.mergeConfig.colsRange[0]; i <= this.mergeConfig.colsRange[1]; i++) { if (i === 0) { spanArr.push(1); this.mergeConfig.position = 0; } else { if (item[keyArr[i - 1]] === item[keyArr[i]]) { spanArr.push(0); spanArr[this.mergeConfig.position] += 1; } else { spanArr.push(1); this.mergeConfig.position = i; } } } this.mergeConfig.colsArr.push(spanArr); spanArr = []; } } // 返回合并数组 spanMethod({ row, column, rowIndex, columnIndex }) { if (this.mergeConfig.colsRange[0] <= columnIndex && columnIndex <= this.mergeConfig.colsRange[1]) { const _row = this.mergeConfig.rowsArr[columnIndex][rowIndex]; const _col = this.mergeConfig.colsArr[rowIndex][columnIndex]; return [_row, _col]; } }
页面调用:
<el-table :data="tableData" :span-method="spanMethod" border style="width: 1000px;"> <el-table-column v-for="(item, i) of tableHeader" :key="i" :label="item" :prop="item" align="center"></el-table-column> </el-table>