Bootstrap-table 合併相同單元格

劉小緒同學發表於2019-01-26

    Bootstrap-table 官方提供了合併單元格方法 mergeCells,它根據四個引數可以合併任意個單元格,我們要做的只是告訴它怎麼合併。

    要合併同一列相同的單元格,無非兩種辦法,一種是一邊遍歷一邊合併,遍歷完了再合併。這裡採用第二種辦法,這裡不需要遍歷所有資料,因為使用者只能看到當前頁的資料,所以只遍歷當前頁的資料更省時間。

    下面是我實現的獲取合併資訊演算法,最終返回的是一個雜湊表,比如下面的這個表格,如果要對「性別」這一列進行合併,很明顯前面兩個“男”需要合併成一個單元格,再去看下 Bootstrap-table 提供的 API,它需要的是從哪個單元格開始,合併多少個單元格,也就是它需要的是兩個數值型別的引數。

姓名 性別 年齡
張三 23
李四 19
王二 20
麻子 21

    所以我把雜湊表設定為,鍵存的是索引,值存的是從這個索引開始後面連續有多少個和它一樣的單元格,那麼上述表格性別這一列所得到的合併資訊雜湊表就為:

{
    0: 2,
    2: 1,
    3: 1
}

    下面演算法很簡單,使用兩個指標遍歷指定的列,如果兩個指標所指向的資料相同,那麼就將鍵所對應的值進行加一操作,整個方法只會對該列資料遍歷一邊,所以時間複雜度為 O(n)。

let getMergeMap = function (data, index: number) {
    let preMergeMap = {};
    // 第 0 項為表頭,索引從 2 開始為了防止陣列越界
    for (let i = 2; i < data.length; i++) {
        let preText = $(data[i-1]).find('td')[index].innerText;
        let curText = $(data[i]).find('td')[index].innerText;
        let key = i - 2;
        preMergeMap[key] = 1;
        while ((preText == curText) && (i < data.length-1)) {
            preMergeMap[key] = parseInt(preMergeMap[key]) + 1;
            i++;
            preText = $(data[i - 1]).find('td')[index].innerText;
            curText = $(data[i]).find('td')[index].innerText;
        }
        // while迴圈跳出後,陣列最後一項沒有判斷
        if (preText == curText) {
            preMergeMap[key] = parseInt(preMergeMap[key]) + 1;
        }
    }
    return preMergeMap;
}

    上述演算法得到了單列資料的合併資訊,下一步就是按照這個資訊進行相同單元格的合併了,因此封裝了下面的方法按照指定雜湊表進行合併。

let mergeCells = function (preMergeMap: Object, target, fieldName: string) {
    for (let prop in preMergeMap) {
        let count = preMergeMap[prop];
        target.bootstrapTable('mergeCells', { index: parseInt(prop), field: fieldName, rowspan: count });
    }
}

    到目前為止,我們實現的都只是對單列資料進行合併,要實現對多列資料進行合併,那麼只需要對所有列都進行相同的操作即可。

export let mergeCellsByFields = function (data: Object[], target, fields) {
    for (let i = 0; i < fields.length; i++) {
        let field = fields[i];
        // 保證 field 與 i 是相對應的
        let preMergeMap = getMergeMap(data, i);
        let table = target.bootstrapTable();
        mergeCells(preMergeMap, table, field);
    }
}

    因為我在程式中做了一點處理,保證了fields中每個值得索引與對應表頭的索引是一樣的,因此不需要額外傳入索引資訊。簡單來說就是我所實現的表格會根據fields的順序,實現列之間的動態排序。你需要注意的是這一點很可能和你不一樣。

    到現在已經能夠合併所有的列了,檢視 Bootstrap-table 的配置資訊發現,它有個屬性是 onPostBody 它會在 table body 載入完成是觸發,所以把這個屬性配置成我們的合併單元格方法即可。

// groups 為要合併的哪些列
onPostBody: function () {
  mergeCellsByFields($('#table' + ' tr'), $('#table'), groups);
}

    再說一點不太相關的,我實現的是讓使用者可以自己選可以合併多少列,即用了一個可多選的下拉選單框供使用者選擇,根據使用者選擇的數量去合併,所以傳入了一個groups引數。

    最後推薦一個排序外掛 thenBy,你可以用它進行多欄位排序,比如用在合併相同單元格的場景,在繪製表格前先對資料進行排序,那麼最後合併的結果就是把所有相同的資料聚合到一起了,並且還將它們合併到一起了,起到了一個隱形的過濾查詢功能。

相關文章