摘要:前言最近公司需要將幾張統(tǒng)計(jì)表格導(dǎo)出到由于公司現(xiàn)有導(dǎo)出功能是前后端配合的導(dǎo)出,覺(jué)得麻煩,所以想找一個(gè)純前端導(dǎo)出的工具,最后找到了,評(píng)價(jià)還是挺高的,但是中文文檔沒(méi)找到百度也沒(méi)有找到一個(gè)比較全面的教程所以踩了很多坑,自己記錄下,方便以后使用。
前言
最近公司需要將幾張統(tǒng)計(jì)表格導(dǎo)出到excel,由于公司現(xiàn)有導(dǎo)出excel功能是前后端配合的導(dǎo)出,覺(jué)得麻煩,所以想找一個(gè)純前端導(dǎo)出的工具,最后找到了js-xlsx,評(píng)價(jià)還是挺高的,但是中文文檔沒(méi)找到,百度也沒(méi)有找到一個(gè)比較全面的教程,所以踩了很多坑,自己記錄下,方便以后使用。
環(huán)境由于我業(yè)務(wù)只用到將table標(biāo)簽內(nèi)的內(nèi)容導(dǎo)出到excel,所以只會(huì)寫(xiě)如何將一個(gè)table元素里的內(nèi)容導(dǎo)出到excel。也可以通過(guò)json導(dǎo)出,貌似還會(huì)更簡(jiǎn)單些。
安裝GitHub地址
npm安裝
npm install xlsx
安裝后dist文件夾下有一個(gè)文件xlsx.full.min.js,就是它了,引入到項(xiàng)目中
第一個(gè)例子先上代碼
Document
序號(hào) | 姓名 | 年齡 | 興趣 |
1 | 張三 | 18 | 打游戲 |
2 | 李四 | 88 | 看電影 |
3 | 王五 | 81 | 睡覺(jué) |
運(yùn)行效果
導(dǎo)出結(jié)果:
你可能注意到了,我這里引入了一個(gè)export.js文件,這個(gè)export.js文件里面只有2個(gè)方法,就是上面代碼用到的openDownloadDialog(sheet2blob(sheet),"下載.xlsx");
這是export.js的代碼:
// 將一個(gè)sheet轉(zhuǎn)成最終的excel文件的blob對(duì)象,然后利用URL.createObjectURL下載 function sheet2blob(sheet, sheetName) { sheetName = sheetName || "sheet1"; var workbook = { SheetNames: [sheetName], Sheets: {} }; workbook.Sheets[sheetName] = sheet; // 生成excel的配置項(xiàng) var wopts = { bookType: "xlsx", // 要生成的文件類(lèi)型 bookSST: false, // 是否生成Shared String Table,官方解釋是,如果開(kāi)啟生成速度會(huì)下降,但在低版本IOS設(shè)備上有更好的兼容性 type: "binary" }; var wbout = XLSX.write(workbook, wopts); var blob = new Blob([s2ab(wbout)], { type: "application/octet-stream" }); // 字符串轉(zhuǎn)ArrayBuffer function s2ab(s) { var buf = new ArrayBuffer(s.length); var view = new Uint8Array(buf); for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF; return buf; } return blob; } function openDownloadDialog(url, saveName) { if (typeof url == "object" && url instanceof Blob) { url = URL.createObjectURL(url); // 創(chuàng)建blob地址 } var aLink = document.createElement("a"); aLink.href = url; aLink.download = saveName || ""; // HTML5新增的屬性,指定保存文件名,可以不要后綴,注意,file:///模式下不會(huì)生效 var event; if (window.MouseEvent) event = new MouseEvent("click"); else { event = document.createEvent("MouseEvents"); event.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); } aLink.dispatchEvent(event); }
PS: 這2個(gè)方法是網(wǎng)上當(dāng)?shù)?原文地址。作者寫(xiě)的挺好,也是從這里找到了頭緒。
如果你的table標(biāo)簽內(nèi)有合并單元格的操作,XLSX.utils.table_to_sheet(*)也能夠讀取出來(lái),并且你打印出來(lái)的結(jié)果也能夠顯示出來(lái),效果圖:
可以看到,excel中的表格也已經(jīng)合并了。
但是實(shí)際的情況,客戶覺(jué)得這行字沒(méi)有居中,他就會(huì)向你嘮叨,為啥不居中,所以我們現(xiàn)在解決文字不居中的問(wèn)題。
這里就不繞圈子了,設(shè)置樣式的話,上面的xlsx.full.min.js是無(wú)法生效的,
必須安裝xlsx-style
好像只有npm安裝,github我沒(méi)找到地址
npm install xlsx-style
同樣,安裝目錄下dist文件夾下有一個(gè)xlsx.full.min.js,嗯?名字一模一樣?怎么用?好吧,無(wú)從下手,只好硬著頭皮引入了,注意,我將xlsx-style的js文件放在下方:
還有btn_export()方法要變一下,加一下樣式。
具體的單元格樣式說(shuō)明可以看下這篇文章 xlsx-style單元格樣式參考表
function btn_export() { var table1 = document.querySelector("#table1"); var sheet = XLSX.utils.table_to_sheet(table1); //這個(gè)就是修改格式的代碼 sheet["A5"].s = { font: { sz: 13, bold: true, }, alignment: { horizontal: "center", vertical: "center", wrap_text: true } }; openDownloadDialog(sheet2blob(sheet),"下載.xlsx"); }
改完之后,點(diǎn)擊運(yùn)行,果不其然,報(bào)錯(cuò)了:
原因是什么呢,原因是2個(gè)js文件暴露出來(lái)的變量都叫‘XLSX’,但是xlsx-style這個(gè)js文件里沒(méi)有XLSX.utils這個(gè)方法,而且xlsx-style這個(gè)js文件是后引入的,就把前面的XLSX給覆蓋了,所以報(bào)錯(cuò)。
XLSX.utils里面有很多可用的方法,但是按照這種方式無(wú)法進(jìn)行調(diào)用:
你可能想到把2個(gè)js文件調(diào)換一下位置,但是結(jié)果是xlsx暴露的變量覆蓋了xlsx-style暴露的變量。你的樣式還是改變不了。
不用XLSX.utils的方式
由于這2個(gè)js都是加密之后的內(nèi)容,無(wú)法解讀,不能在這2個(gè)js上找到什么有用的東西。好在在xlsx dist文件夾下找到了xlsx.extendscript.js,看這個(gè)文件就像個(gè)工具類(lèi),由于我上面用到了table_to_sheet方法,在xlsx.extendscript上面的搜索了一下,果然發(fā)現(xiàn)了這個(gè)方法,二話不說(shuō),將xlsx的js引用刪除,引入xlsx.extendscript:
運(yùn)行。結(jié)果你應(yīng)該已經(jīng)猜到了,樣式并沒(méi)有發(fā)生改變。什么原因呢,xlsx.extendscript.js暴露出來(lái)的變量仍然是"XLSX",下面的變量還是覆蓋了上面的變量。
注意!!! 如果你的項(xiàng)目中使用了webpack、babel等,可以直接import,不用改變變量名好在這個(gè)xlsx.extendscript.js不是壓縮版本,可以對(duì)內(nèi)容進(jìn)行修改,就把暴露出來(lái)的變量修改為"XLSX2"吧。這樣我們只有在使用utils工具的時(shí)候才用到xlsx.extendscript.js,其余都用的是xlsx-style這個(gè)js,這樣總該可以了吧 。
修改完之后別忘了將XSLX.utils.table_to_sheet()改成XLSX2.utils.table_to_sheet()。
(不建議修改源碼,由于工作需要不修改源碼無(wú)法使用才做的修改)
function btn_export() { var table1 = document.querySelector("#table1"); var sheet = XLSX2.utils.table_to_sheet(table1); sheet["A5"].s = { font: { sz: 13, bold: true, color: { rgb: "FFFFAA00" } }, alignment: { horizontal: "center", vertical: "center", wrap_text: true } }; openDownloadDialog(sheet2blob(sheet), "下載.xlsx"); }
運(yùn)行:
可以看到,你所做的樣式更改已經(jīng)生效了。
客戶需求增加:我想要前面幾行空出來(lái),并且寫(xiě)上打印公司名。
觀察xlsx.extendscript.js源碼,發(fā)現(xiàn)table_to_sheet,也就是parse_dom_table,并沒(méi)有設(shè)置起始行的參數(shù),下面給出parse_dom_table的代碼:
function parse_dom_table(table, _opts) { var opts = _opts || {}; if(DENSE != null) opts.dense = DENSE; var ws = opts.dense ? ([]) : ({}); var rows = table.getElementsByTagName("tr"); var sheetRows = opts.sheetRows || 10000000; var range = {s:{r:0,c:0},e:{r:0,c:0}}; var merges = [], midx = 0; var rowinfo = []; var _R = 0, R = 0, _C, C, RS, CS; for(; _R < rows.length && R < sheetRows; ++_R) { var row = rows[_R]; if (is_dom_element_hidden(row)) { if (opts.display) continue; rowinfo[R] = {hidden: true}; } var elts = (row.children); for(_C = C = 0; _C < elts.length; ++_C) { var elt = elts[_C]; if (opts.display && is_dom_element_hidden(elt)) continue; var v = htmldecode(elt.innerHTML); for(midx = 0; midx < merges.length; ++midx) { var m = merges[midx]; if(m.s.c == C && m.s.r <= R && R <= m.e.r) { C = m.e.c+1; midx = -1; } } /* TODO: figure out how to extract nonstandard mso- style */ CS = +elt.getAttribute("colspan") || 1; if((RS = +elt.getAttribute("rowspan"))>0 || CS>1) merges.push({s:{r:R,c:C},e:{r:R + (RS||1) - 1, c:C + CS - 1}}); var o = {t:"s", v:v}; var _t = elt.getAttribute("t") || ""; if(v != null) { if(v.length == 0) o.t = _t || "z"; else if(opts.raw || v.trim().length == 0 || _t == "s"){} else if(v === "TRUE") o = {t:"b", v:true}; else if(v === "FALSE") o = {t:"b", v:false}; else if(!isNaN(fuzzynum(v))) o = {t:"n", v:fuzzynum(v)}; else if(!isNaN(fuzzydate(v).getDate())) { o = ({t:"d", v:parseDate(v)}); if(!opts.cellDates) o = ({t:"n", v:datenum(o.v)}); o.z = opts.dateNF || SSF._table[14]; } } if(opts.dense) { if(!ws[R]) ws[R] = []; ws[R][C] = o; } else ws[encode_cell({c:C, r:R})] = o; if(range.e.c < C) range.e.c = C; C += CS; } ++R; } if(merges.length) ws["!merges"] = merges; if(rowinfo.length) ws["!rows"] = rowinfo; range.e.r = R - 1; ws["!ref"] = encode_range(range); if(R >= sheetRows) ws["!fullref"] = encode_range((range.e.r = rows.length-_R+R-1,range)); // We can count the real number of rows to parse but we don"t to improve the performance return ws; }
那自己加一個(gè)吧
可以看到,里面的R變量 這是控制起始行的關(guān)鍵所在,好吧,我們?cè)僮鲆幌滦薷?
var _R = 0, R = _opts.rowIndex || 0, _C, C, RS, CS;
這里我們給_opts增加一個(gè)屬性rowIndex,在調(diào)用table_to_sheet方法的時(shí)候傳入這個(gè)屬性。下面是變更后的代碼:
function btn_export() { var table1 = document.querySelector("#table1"); var opt = { rowIndex: 4 }; //開(kāi)頭空4行 var sheet = XLSX2.utils.table_to_sheet(table1, opt); sheet["A1"] = { t: "s", v: "三鹿集團(tuán)有限公司" }; //給A1單元格賦值 sheet["A1"].s = { font: { name: "宋體", sz: 24, bold: true, underline: true, color: { rgb: "FFFFAA00" } }, alignment: { horizontal: "center", vertical: "center", wrap_text: true }, fill: { bgColor: { rgb: "ffff00" } } }; //["!merges"]這個(gè)屬性是專(zhuān)門(mén)用來(lái)進(jìn)行單元格合并的 sheet["!merges"].push({//如果不為空push 為空 = 賦值 //合并單元格 index都從0開(kāi)始 s: { //s開(kāi)始 c: 0, //開(kāi)始列 r: 0 //開(kāi)始行 }, e: { //e結(jié)束 c: 3, //結(jié)束列 r: 2 //結(jié)束行 } }); sheet["A9"].s = { //樣式 font: { sz: 13, bold: true, color: { rgb: "FFFFAA00" } }, alignment: { horizontal: "center", vertical: "center", wrap_text: true } }; openDownloadDialog(sheet2blob(sheet), "下載.xlsx"); }
運(yùn)行結(jié)果:
可以看到,你所做的更改生效了。
客戶又提新需求了,要加上2個(gè)字段,身份證號(hào)和手機(jī)號(hào)。
這還不簡(jiǎn)單?加上2個(gè)字段不就好了。2分鐘搞定,導(dǎo)出:
???
身份證號(hào)怎么變成了科學(xué)計(jì)數(shù)法,什么鬼(后來(lái)發(fā)現(xiàn)百分比也會(huì)直接給你換算成0~1的小數(shù),統(tǒng)計(jì)沒(méi)法搞)
怎么回事?還是parse_dom_table的杰作!
注意這一行:
else if(!isNaN(fuzzynum(v))) o = {t:"n", v:fuzzynum(v)};
意思是只要從td的text里讀取到的值,只要轉(zhuǎn)換之后是一個(gè)number,(不管你是string類(lèi)型),都會(huì)給你來(lái)一個(gè)fuzzynum(v),轉(zhuǎn)換成一個(gè)number類(lèi)型。
做下修改,結(jié)果:
function parse_dom_table(table, _opts) { var opts = _opts || {}; if(DENSE != null) opts.dense = DENSE; var ws = opts.dense ? ([]) : ({}); var rows = table.getElementsByTagName("tr"); var sheetRows = opts.sheetRows || 10000000; var range = {s:{r:0,c:0},e:{r:0,c:0}}; var merges = [], midx = 0; var rowinfo = []; var _R = 0, R = _opts.rowIndex || 0, _C, C, RS, CS; for(; _R < rows.length && R < sheetRows; ++_R) { var row = rows[_R]; if (is_dom_element_hidden(row)) { if (opts.display) continue; rowinfo[R] = {hidden: true}; } var elts = (row.children); for(_C = C = 0; _C < elts.length; ++_C) { var elt = elts[_C]; if (opts.display && is_dom_element_hidden(elt)) continue; var v = htmldecode(elt.innerHTML); for(midx = 0; midx < merges.length; ++midx) { var m = merges[midx]; if(m.s.c == C && m.s.r <= R && R <= m.e.r) { C = m.e.c+1; midx = -1; } } /* TODO: figure out how to extract nonstandard mso- style */ CS = +elt.getAttribute("colspan") || 1; if((RS = +elt.getAttribute("rowspan"))>0 || CS>1) merges.push({s:{r:R,c:C},e:{r:R + (RS||1) - 1, c:C + CS - 1}}); var o = {t:"s", v:v}; var _t = elt.getAttribute("t") || ""; if(v != null) { if(v.length == 0) o.t = _t || "z"; else if(opts.raw || v.trim().length == 0 || _t == "s"){} else if(v === "TRUE") o = {t:"b", v:true}; else if(v === "FALSE") o = {t:"b", v:false}; //else if(!isNaN(fuzzynum(v))) o = {t:"n", v:fuzzynum(v)}; else if(!isNaN(fuzzynum(v))) o = {t:"s", v:v};//不自動(dòng)格式化number類(lèi)型 else if(!isNaN(fuzzydate(v).getDate())) { o = ({t:"d", v:parseDate(v)}); if(!opts.cellDates) o = ({t:"n", v:datenum(o.v)}); o.z = opts.dateNF || SSF._table[14]; } } if(opts.dense) { if(!ws[R]) ws[R] = []; ws[R][C] = o; } else ws[encode_cell({c:C, r:R})] = o; if(range.e.c < C) range.e.c = C; C += CS; } ++R; } if(merges.length) ws["!merges"] = merges; if(rowinfo.length) ws["!rows"] = rowinfo; range.e.r = R - 1; ws["!ref"] = encode_range(range); if(R >= sheetRows) ws["!fullref"] = encode_range((range.e.r = rows.length-_R+R-1,range)); // We can count the real number of rows to parse but we don"t to improve the performance return ws; }
將轉(zhuǎn)換的語(yǔ)句注釋掉,重寫(xiě)這行代碼,如果是number類(lèi)型,不做任何修改,該是什么值還是什么值。
現(xiàn)在再重新運(yùn)行,結(jié)果:
可以看到,數(shù)字能夠正常顯示了。但是這個(gè)單元格好像并不會(huì)自動(dòng)展開(kāi),永遠(yuǎn)都這么大,xlsx-style 也提供了控制單元格寬度的方法:
sheet["!cols"] = [{ wpx: 70 }, { wpx: 70 }, { wpx: 70 }, { wpx: 70 }, { wpx: 150 }, { wpx: 120 }]; //單元格列寬
注意,設(shè)置單元格列寬要從第一行開(kāi)始設(shè)置
結(jié)果:
完整前端代碼:
Document
序號(hào) | 姓名 | 年齡 | 興趣 | 身份證號(hào) | 手機(jī)號(hào) |
1 | 張三 | 18 | 打游戲 | 320322184087562589 | 1374569821 |
2 | 李四 | 88 | 看電影 | 420322184087562589 | 2374569821 |
3 | 王五 | 81 | 睡覺(jué) | 520322184087562589 | 3374569821 |
這是一個(gè)合并單元格 |
github地址 完整實(shí)例
總結(jié)不是特殊情況不建議修改源碼
因?yàn)楫吘剐薷牧舜a,所以這種方法只能面向小眾
聽(tīng)過(guò)收費(fèi)版功能很全,建議如果有需要的話還是購(gòu)買(mǎi)收費(fèi)版本,但是地址沒(méi)找到...
可以根據(jù)自己需求對(duì)xlsx源碼進(jìn)行修改,以便滿足自己工作的需求。但是這樣較難以維護(hù),如何取舍還是自行斟酌。
我這里只列取了我實(shí)際工作中所需要的功能,xlsx的功能很豐富,有空可以多琢磨琢磨。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/105428.html
摘要:使用時(shí),前端可以將后端返回的數(shù)據(jù)拼接成自己需要導(dǎo)出的格式,下載到電腦中,完全不依賴后端。 前言 github: https://github.com/stardew516... 以往做excel表格下載功能的時(shí)候,都是后端生成好表格后,存儲(chǔ)在某個(gè)地方,然后給前端一個(gè)鏈接,前端使用a標(biāo)簽加download下載,或者使用node。其實(shí)純前端也是可以做表格下載的,有一個(gè)很好用的javascr...
摘要:簡(jiǎn)介是前端操作以及類(lèi)似的二維表的最佳選擇之一而是它的社區(qū)版本將注意力集中到了數(shù)據(jù)轉(zhuǎn)換和導(dǎo)出上所以它支持相當(dāng)多種類(lèi)的數(shù)據(jù)解析和導(dǎo)出不僅僅局限于支持格式支持的導(dǎo)入格式支持的導(dǎo)出格式它可以解析符合格式的數(shù)據(jù)導(dǎo)出符合格式的數(shù)據(jù)利用中間層操作數(shù)據(jù) 簡(jiǎn)介 SheetJS是前端操作Excel以及類(lèi)似的二維表的最佳選擇之一,而js-xlsx是它的社區(qū)版本. js-xlsx將注意力集中到了數(shù)據(jù)轉(zhuǎn)換和導(dǎo)出...
摘要:前言最近公司需要將幾張統(tǒng)計(jì)表格導(dǎo)出到由于公司現(xiàn)有導(dǎo)出功能是前后端配合的導(dǎo)出,覺(jué)得麻煩,所以想找一個(gè)純前端導(dǎo)出的工具,最后找到了,評(píng)價(jià)還是挺高的,但是中文文檔沒(méi)找到百度也沒(méi)有找到一個(gè)比較全面的教程所以踩了很多坑,自己記錄下,方便以后使用。 前言 最近公司需要將幾張統(tǒng)計(jì)表格導(dǎo)出到excel,由于公司現(xiàn)有導(dǎo)出excel功能是前后端配合的導(dǎo)出,覺(jué)得麻煩,所以想找一個(gè)純前端導(dǎo)出的工具,最后找到了...
摘要:前言最近公司需要將幾張統(tǒng)計(jì)表格導(dǎo)出到由于公司現(xiàn)有導(dǎo)出功能是前后端配合的導(dǎo)出,覺(jué)得麻煩,所以想找一個(gè)純前端導(dǎo)出的工具,最后找到了,評(píng)價(jià)還是挺高的,但是中文文檔沒(méi)找到百度也沒(méi)有找到一個(gè)比較全面的教程所以踩了很多坑,自己記錄下,方便以后使用。 前言 最近公司需要將幾張統(tǒng)計(jì)表格導(dǎo)出到excel,由于公司現(xiàn)有導(dǎo)出excel功能是前后端配合的導(dǎo)出,覺(jué)得麻煩,所以想找一個(gè)純前端導(dǎo)出的工具,最后找到了...
閱讀 2144·2023-04-26 00:38
閱讀 1930·2021-09-07 10:17
閱讀 887·2021-09-02 15:41
閱讀 637·2021-08-30 09:45
閱讀 541·2019-08-29 17:25
閱讀 3204·2019-08-29 15:07
閱讀 2182·2019-08-29 12:52
閱讀 3734·2019-08-26 13:35