最終效果圖:
一、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>