天天看點

layui 複雜表頭多層表頭的表格資料非背景導出excel1 史前2 蛻變

用過layui table的都了解,layui的表格自帶導出功能,隻要在屬性toolbar裡配置即可。

如果嫌toolbar裡的導出按鈕不夠美觀,layui也友好的提供了自行定義按鈕後調用導出頁面資料的方法,詳見:官方文檔

遺憾的是:

該方法暫時并不支援對多層表頭的導出。

而多層複雜表頭在很多系統中都有展現,而若不想通過背景代碼去做導出,隻在前端頁面上該如何做呢?

目錄&曆程

  • 1 史前
    • 1.1 直接使用(兩種方式)
    • 1.2 上述插件使用場景解析
    • 1.3 不可用場景處理
    • 1.4 處理後的使用
    • 1.5 新的情況相容:固定列
  • 2 蛻變
    • 2.1 table2excel_ext.js的誕生
    • 2.2 新的情況相容:合計行
    • 2.3 新的情況相容:排除列

1 史前

1.1 直接使用(兩種方式)

1.

下載下傳插件:

位址:github table2excel

下載下傳下來項目後,在項目的

dist

中就有該js,在自己表格html頁面引用即可使用。

連結:百度雲下載下傳js

提取碼:1f52

/**
 * 使用方法該js的方法(兩行代碼)
 */
let table2excel = new Table2Excel();
// 傳入你的tableId即可導出
table2excel.export($('#tableId'), "your filename");
           
  1. 一個解析頁面元素方法:

    複制走即可使用
/**
 * 
 * @param id:表格的id
 * @param fileName:導出的excel的名字
 */
 function  exportForExcle(id,fileName){
        var table = $(document.getElementById(id)).clone();
        table.attr('id','datatab');
        table.appendTo('body');
        method5('datatab');
        datatab.remove();
        function method5(tableid) {
            var idTmr;
            var tableToExcel = (function() {
                var uri = 'data:application/vnd.ms-excel;base64,',
                    template = '<html><head><meta charset="UTF-8"></head><body><table >{table}</table></body></html>',
                    base64 = function(s) { return window.btoa(unescape(encodeURIComponent(s))) },
                    format = function(s, c) {return s.replace(/{(\w+)}/g, function(m, p) { return c[p]; }) }
                return function(table, name) {
                    if (!table.nodeType) table = document.getElementById(table)
                    var ctx = {worksheet: name || 'Worksheet', table: table.innerHTML}
                    var aTag = document.createElement('a');
                    aTag.download = fileName;
                    aTag.href = uri + base64(format(template, ctx));
                    document.body.appendChild(aTag);
                    aTag.onclick = function () {
                        document.body.removeChild(aTag);
                    }
                    aTag.click();
                }
            })()
            if(getExplorer()=='ie')
            {
                var curTbl = document.getElementById(tableid);
                var oXL = new ActiveXObject("Excel.Application");
                var oWB = oXL.Workbooks.Add();
                var xlsheet = oWB.Worksheets(1);
                var sel = document.body.createTextRange();
                sel.moveToElementText(curTbl);
                sel.select();
                sel.execCommand("Copy");
                xlsheet.Paste();
                oXL.Visible = true;
                try {
                    var fname = oXL.Application.GetSaveAsFilename("Excel.xls", "Excel Spreadsheets (*.xls), *.xls");
                } catch (e) {
                    print("Nested catch caught " + e);
                } finally {
                    oWB.SaveAs(fname);
                    oWB.Close(savechanges = false);
                    oXL.Quit();
                    oXL = null;
                    idTmr = window.setInterval("Cleanup();", 1);
                }
            }
            else
            {
                tableToExcel(tableid)
            }
            function Cleanup() {
                window.clearInterval(idTmr);
                CollectGarbage();
            }
            function  getExplorer() {
                var explorer = window.navigator.userAgent ;
                //ie
                if (explorer.indexOf("MSIE") >= 0) {
                    return 'ie';
                }
                //firefox
                else if (explorer.indexOf("Firefox") >= 0) {
                    return 'Firefox';
                }
                //Chrome
                else if(explorer.indexOf("Chrome") >= 0){
                    return 'Chrome';
                }
                //Opera
                else if(explorer.indexOf("Opera") >= 0){
                    return 'Opera';
                }
                //Safari
                else if(explorer.indexOf("Safari") >= 0){
                    return 'Safari';
                }
            }
        }
    }
           

1.2 上述插件使用場景解析

可用的情況:

表格的渲染方式為靜态渲染,即在html的table标簽中渲染好了表頭與資料。

不可用的情況:

表格的渲染方式為動态渲染,即從背景查詢資料來渲染表格,包括渲染動态的表頭。

不可用的情況原因:

用layui.table動态渲染的表格,不會在頁面的html table标簽中添加doom元素。

如下圖的表格(表頭和資料均為動态渲染),layui渲染資料後,并不會在

table

标簽中append doom元素,而是會在table 标簽的下面追加一個新的div,将tr th td 和資料全部存放在裡面。

這也就導緻了上述的兩種方法均無法下載下傳excel,就是因為:該插件在頁面上找不到插件所需的doom節點與資料。

layui 複雜表頭多層表頭的表格資料非背景導出excel1 史前2 蛻變
layui 複雜表頭多層表頭的表格資料非背景導出excel1 史前2 蛻變

1.3 不可用場景處理

在表格渲染完成後的

done

方法中,去周遊頁面上layui生成的div,抓取裡面的格式與資料,來手動append到table中,再控制該table隐藏,來為導出做準備。
  1. 分析doom元素:能清晰看到表頭和body的doom結構,裡面有你需要的所有資訊,包括表頭的各種合并操作。
layui 複雜表頭多層表頭的表格資料非背景導出excel1 史前2 蛻變
  1. 手動向table标簽裡append html。
table.render({
	...          
    done: function (res, curr, count) {
        let header_tr = $("#tableId").next().find(".layui-table-header").find("tr");
        let body_tr = $("#tableId").next().find(".layui-table-body").find("tr");
        let header_html = "";
        let body_html = "";
        // 擷取表頭html,包括單元格的合并
        $.each(header_tr,function (i,tr) {
            let header_th = $(tr).find("th");
            header_html += "<tr>";
            $.each(header_th,function (j,th) {
                let rowspan_num = $(th).attr("rowspan");// 行合并數
                let colspan_num = $(th).attr("colspan");// 列合并數
                if (rowspan_num && !colspan_num){// 隻有行合并時
                    header_html += '<th rowspan= "'+ rowspan_num +'">';
                } else if (colspan_num && !rowspan_num){// 隻有列合并時
                    header_html += '<th colspan= "'+ colspan_num +'">';
                } else if (rowspan_num && colspan_num){// 行列合并均有時
                    header_html += '<th rowspan= "'+ rowspan_num +'" colspan="'+ colspan_num +'">';
                } else {// 沒有發生單元格合并
                    header_html += '<th>';
                }
                header_html += $(th).children().children().text() + '</th>';// 擷取表頭名稱并拼接th标簽
            })
            header_html += '</tr>';
        })
        // 擷取表格body資料
        $.each(body_tr,function (i,tr) {
            let body_td = $(tr).find("td");
            body_html += '<tr>';
            $.each(body_td,function (j,td) {
                body_html += '<td>' + $(td).children().text() + '</td>';
            })
            body_html += '</tr>';
        })
        $("#tableId tr").remove();// 清除之前的doom結構
        $("#tableId").append(header_html).append(body_html);
        $("#tableId").hide();
    }
});
           

如果把最後hide()方法去掉,可以看到表格doom元素已經添加到table标簽中。

layui 複雜表頭多層表頭的表格資料非背景導出excel1 史前2 蛻變
layui 複雜表頭多層表頭的表格資料非背景導出excel1 史前2 蛻變

1.4 處理後的使用

做了上面的處理後,就可使用第一步的方法做導出了

下面是導出方法和導出後的截圖

導出的時候需要做一點小小的處理:

let table2excel = new Table2Excel();
 /**
 * 此處的show()是為了避免table2excel将hide屬性帶入excel中
 * 導緻下載下傳後的excel中所有資料被隐藏
 */
$('#tableId').show();
table2excel.export($('#tableId'), "your filename");
$('#tableId').hide();
           
layui 複雜表頭多層表頭的表格資料非背景導出excel1 史前2 蛻變

說明:

1、上面在導出的時候如果不先把表格show顯示出來。wps的excel會正常顯示,而office的excel則會把所有資料隐藏。這也是這個插件強大的地方,屬性也會copy到excel中去。

2、可能有的同學會覺得在這裡顯示出來自己添加的表格doom元素,會在頁面上有一閃而過的視覺效果,親測過,并沒有。可能在網速超級卡頓的時候會出現。

3、當然,這并不是最佳的解決方法。歡迎讨論。

----------------------------------------------我是分割線------------------------------------------------

更新時間:2019年10月17日 10點00分

感謝:@weixin_40915415 同學提出的

固定列

的特殊情況

1.5 新的情況相容:固定列

layui在處理固定列時候的doom渲染跟活動列是不同的

如下所示:我們将第一列

承攬組

固定,再用之前版本的js去append html時會發現,固定列的header和body發生了重複(兩次)

layui 複雜表頭多層表頭的表格資料非背景導出excel1 史前2 蛻變

分析doom元素:

layui 複雜表頭多層表頭的表格資料非背景導出excel1 史前2 蛻變

我們看到這裡出現了

兩個header

原來layui在渲染有固定列的表頭時,會把固定列的header和body再多渲染一次,放到樣式為

layui-table-fixed

div

裡。

知道這種結構,我們就好做新的處理了。

新的擷取header和body doom元素的js更改如下以相容有固定列的layui table:

let header_tr = $($("#tableId").next().find(".layui-table-header")[0]).find("tr");
let body_tr = $($("#tableId").next().find(".layui-table-body")[0]).find("tr");
           

----------------------------------------------我是分割線------------------------------------------------

2 蛻變

2.1 table2excel_ext.js的誕生

更新時間:2020年07月28日

💕能耐心看到這兒的同學真的是愛學習的好同學,不過這兒我也給你準備了驚喜(本來覺得上面的那些是不是可以删掉了,後來一想也是自己當初的一個思路曆程,就先留着吧)💕

筆者将上訴的相關操作抽出了兩個方法

append

exportLayTable

添加到了table2excel.js中,直接将該修改後的js引入到需導出excel資料的頁面使用即可。

連結:百度雲下載下傳 table2excel_ext.js

提取碼:4lsc

使用方法:

var table2Excel ;
table.render({
	elem: '#tableId',
	...          
	done: function (res, curr, count) {
		table2Excel = new Table2Excel();
		table2Excel.append($("#tableId"));
	})    
});
// 導出excel點選事件
$('#exportElemId').click(function(){
	let fileName = 'XXX-20200728';
	table2Excel.exportLayTable($('#tableId'),fileName);
});
           

----------------------------------------------我是分割線------------------------------------------------

2.2 新的情況相容:合計行

更新時間:2020年10月16日

感謝:@zhang_pengpeng 同學提出的

合計行

的情況

layui table 自動在前端計算添加的合計行(關于怎麼開啟合計行,請自行檢視layui 官網文檔)會在body的下面重新渲染一段新的dom結構。如下圖所示:
layui 複雜表頭多層表頭的表格資料非背景導出excel1 史前2 蛻變
layui 複雜表頭多層表頭的表格資料非背景導出excel1 史前2 蛻變

針對此種情況,筆者已經将table2excel_ext.js做了新的相容,請傳回目錄

2.1

進行下載下傳使用即可。

----------------------------------------------我是分割線------------------------------------------------

2.3 新的情況相容:排除列

更新時間:2020年11月25日

感謝:@qq_26097713 同學提出的

排除列(清單中一些不想被導出的列)

的情況。

對于表格中的操作列,或者一些隐藏的列,又或者是一些不想導出的列。筆者對

table2excel_ext.js

做了新的相容,來處理這種情況,下面将給出示例代碼。

ps:引入新的js不會對以前的調用産生任何影響。

var table2Excel ;
table.render({
	elem: '#tableId',
	...          
	done: function (res, curr, count) {
		// 排除掉的列的field數組(此參數可以不傳,預設導出所有列)
		let exceptColumns = ["field1","field2"];
		table2Excel = new Table2Excel();
		table2Excel.append($("#tableId"), exceptColumns);
	})    
});
           

說明:

  1. table2Excel.append(table,exceptColumns)

    方法中的第二個參數exceptColumns可以不傳,不傳表示導出所有列。
  2. 其中exceptColumns數組中的值對應為渲染表格列時的

    field

    的值(表格中的操作列也請添加上一個field值,用于定位),如下:
...
{
    title : "姓名",
    field : "username",// 此處的field
    width : "10",
    align : "center"
}
...
           
  1. js下載下傳連結已做更新,請傳回目錄

    2.1

    進行下載下傳。