文章

Layui数据表格合并单元格

零、序言

最近做的一个项目中遇到一个需求,需要合并动态加载数据的数据表格中相同的单元格,但需要区分开不同的父级。

原始数据:

image.png

需要合并后的效果:

image.png

一、搜寻解决方案

项目使用的是 layui 前端框架,但是在官网的文档中没有找到可行的支持,不过最后在网上找到了 layui 单元格合并的解决方案。

原博客:layui 动态表格之合并单元格

作者使用 layui 数据表格的 done 事件,在数据表格渲染完成后执行自定义的合并单元格方法。我稍作修改后如下所示。

//layui数据表格渲染完成时触发的回调方法
done : function(res, curr, count) {
    //调用自定义的合并单元格方法
    mergeFields(res);
}

//自定义的合并单元格方法
function mergeFields(res) {
    //表格所有数据
    var data = res.data;
    //所有行
    var trArray = $(".layui-table-body>.layui-table").find("tr");
    //需要合并的列索引和列名称
    var mergeColums = [
        {
            columIndex: '0',
            columName: 'goodsName'
        },
        {
            columIndex: '1',
            columName: 'goodsNameSum'
        },
        {
            columIndex: '2',
            columName: 'goodsBrand'
        },
        {
            columIndex: '3',
            columName: 'goodsBrandSum'
        }
    ];

    //这里遍历所有要合并的列
    for (var i = 0; i < mergeColums.length; i++) {
        //需要合并的开始行索引
        var mergeStartIndex = 0;
        //需要合并的格子数
        var mergeCount = 1;

        //这里遍历表格所有数据,从第二行开始,第一行不需要判断
        for (var j = 1; j < res.data.length; j++) {
            //当前行的当前列
            var currentTrCurrentTd = trArray.eq(j).find("td").eq(mergeColums[i].columIndex);
            //需要合并的开始行的当前列
            var startTrCurrentTd = trArray.eq(mergeStartIndex).find("td").eq(mergeColums[i].columIndex);

            //后一行的值与前一行的值做比较,相同就需要合并
            if (data[j][mergeColums[i].columName] === data[j - 1][mergeColums[i].columName]) {
                mergeCount += 1;
                //相同列的第一列增加rowspan属性
                startTrCurrentTd.attr("rowspan", mergeCount);
                //当前行隐藏
                currentTrCurrentTd.css("display", "none");
            } else {
                //一旦前后两行的当前列的值不一样了,那么需要合并的开始行需要从当前的j行重新开始
                mergeStartIndex = j;
                //一旦前后两行的值不一样了,那么需要合并的格子数mark就需要重新计算
                mergeCount = 1;
            }
        }
    }
}

运行后发现,相同的单元格确实得到了合并。但我们可以发现 笔记本电脑台式电脑 下的相同品牌 戴尔 合并到了一起,这并不是我们的预期效果。

image.png

二、动手改善优化

所以自己动手修改优化算法,加入当前列和前一列之间的判断,防止非同一父级却合并到一起的情况出现。修改后如下所示。

function mergeFields(res) {
    //表格所有数据
    var data = res.data;
    //所有行
    var trArray = $(".layui-table-body>.layui-table").find("tr");
    //需要合并的列索引和列名称
    var mergeColums = [
        {
            columIndex: '0',
            columName: 'goodsName'
        },
        {
            columIndex: '1',
            columName: 'goodsNameSum'
        },
        {
            columIndex: '2',
            columName: 'goodsBrand'
        },
        {
            columIndex: '3',
            columName: 'goodsBrandSum'
        }
    ];

    //这里遍历所有要合并的列
    for (var i = 0; i < mergeColums.length; i++) {
        //需要合并的开始行索引
        var mergeStartIndex = 0;
        //需要合并的格子数
        var mergeCount = 1;

        //这里遍历表格所有数据,从第二行开始,第一行不需要判断
        for (var j = 1; j < res.data.length; j++) {
            //当前行的前一列
            var currentTrPreviousTd = i != 0 ? trArray.eq(j).find("td").eq(mergeColums[i].columIndex - 1) : null;
            //当前行的当前列
            var currentTrCurrentTd = trArray.eq(j).find("td").eq(mergeColums[i].columIndex);
            //需要合并的开始行的当前列
            var startTrCurrentTd = trArray.eq(mergeStartIndex).find("td").eq(mergeColums[i].columIndex);


            //后一行的值与前一行的值做比较,相同就需要合并
            if (data[j][mergeColums[i].columName] === data[j - 1][mergeColums[i].columName] && (currentTrPreviousTd == null || currentTrPreviousTd.css("display") == "none")) {
                mergeCount += 1;
                //相同列的第一列增加rowspan属性
                startTrCurrentTd.attr("rowspan", mergeCount);
                //当前行隐藏
                currentTrCurrentTd.css("display", "none");
            } else {
                //一旦前后两行的当前列的值不一样了,那么需要合并的开始行需要从当前的j行重新开始
                mergeStartIndex = j;
                //一旦前后两行的值不一样了,那么需要合并的格子数mark就需要重新计算
                mergeCount = 1;
            }
        }
    }
}

image.png

大功告成!

License:  CC BY 4.0