傳送門
先放一些我認為非常優秀的參考文檔,作為傳送門集合,方便大家查看吧:
【1】Node.js中如何利用xlsx設置導出表格的樣式?
這個問題告訴了你,你用xlsx設置樣式不行,但是用xlsx-style導出樣式就ok
【2】純前端利用 js-xlsx 實現 Excel 文件導入導出功能示例
這算是全網,注意是全網講解js-xlsx比較全面系統的文章了,可以好好了解一下。
本系統使用node+express 作為后臺,導出excel使用的是xlsx-style。在開始之前,先簡單介紹一下node常用的一些導出excel的工具,因為這對我們后面的工作十分重要。
預備
(1)js-xlsx和xlsx。xlsx和js-xlsx可以理解為基本庫,API十分復雜,上手難度高,但是你想要的功能,基本上都有。但是文檔不好,看起來十分費力。不信你去點點下面的鏈接看看,嚇死你……
js-xlsx文檔:https://www.npmjs.com/package/js-xlsx
xlsx文檔:https://www.npmjs.com/package/xlsx
(2)node-xlsx。基于基本庫的封裝,底層的庫是用的xlsx。
node-xlsx npm文檔:https://www.npmjs.com/package/node-xlsx
十分操蛋的文檔,反正我是看完之后不知道怎么用,寫了跟沒寫一樣,關鍵的樣式都沒說。
(3)xlsx-style 基于xlsx封裝的樣式庫,可以在xlsx的基礎上設置樣式。
正文
一般在使用node的時候我們是直接使用node-xlsx庫的,關鍵的導出代碼如下:
const xlsx = require('node-xlsx').default;
const options = {
'!cols': [
{wpx: 300},//1-變更名稱
{wpx: 100},//2-變更描述
{wpx: 140},//3-計劃上線測試時間
{wpx: 140}, //4-計劃上線時間
{wpx: 250}, //5-子系統、模塊名稱
{wpx: 120}, //6-依賴模塊
{wpx: 195},//7-功能點
{wpx: 195}, //8-詳細描述
{wpx: 195}, //9-測試要點
{wpx: 205}, //10-對應需求
{wpx: 150}, //11-是否
{wpx: 150}, //12-開發A
{wpx: 150}, //13-開發B
{wpx: 110}, //14-關聯版本
{wpx: 110}, //15-代碼走查
],
'!rows': [
{hpx: 40,},
{hpx: 60},
{hpx: 80},
{hpx: 100},
],
'!margins': {left: 0.7, right: 0.7, top: 0.75, bottom: 0.75, header: 0.3, footer: 0.3},
}
const range = {s: {c: 0, r: 0}, e: {c: 0, r: 2}}; // A1:A4
options['!merges'] = [range]
const xlsxData = xlsx.build([form], options)
options是導出表格的一些樣式,!cows和!rows分別設置的是列寬和行高,可以成功設置,但是樣式不能成功設置,也就是說
s標簽里的東西通通無效。
在網上查詢了一些辦法后,發現主需要吧node-xlsx里對xlsx的引入改為xlsx-style就能夠成功設置樣式。
然后就發現了樣式成功設置,行高卻無效了……(我tm!@#¥%)
問題分析
我對比了xlsx-style和xlsx的源碼,發現兩個庫很多的方法和代碼都是一樣的,xlsx-style之所以能夠設置樣式,是因為它多了一個styleBuilder
而xlsx-style不能夠設置行高,是因為在write_ws_xml-data里沒有對rows進行處理,而在xlsx庫里可以清楚的看到它對!rows的處理
所以解決方法就必須去改庫(我認為),思路也有2中:
(1)在xlsx-style加xlsx里設置行高的代碼
(2)在xlsx里加入xlsx-style里設置樣式的代碼
用腳想也應該知道(1)的工作量最小,所以解決辦法就是在xlsx-style里加入行高設置的代碼。
解決方法:
先修改node-xlsx lib目錄下的hepler.js
var buildSheetFromMatrix = function buildSheetFromMatrix(data) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var workSheet = {};
var range = {s: {c: 1e7, r: 1e7}, e: {c: 0, r: 0}};
if (!Array.isArray(data)) throw new Error('sheet data is not array');
for (var R = 0; R !== data.length; R += 1) {
for (var C = 0; C !== data[R].length; C += 1) {
if (!Array.isArray(data[R])) throw new Error(R + 'th row data is not array');
if (range.s.r > R) range.s.r = R;
if (range.s.c > C) range.s.c = C;
if (range.e.r < R) range.e.r = R;
if (range.e.c < C) range.e.c = C;
if (data[R][C] === null) {
continue; // eslint-disable-line
}
var cell = isCellDescriptor(data[R][C]) ? data[R][C] : {v: data[R][C]};
var cellRef = _xlsx2.default.utils.encode_cell({c: C, r: R});
if (isNumber(cell.v)) {
cell.t = 'n';
} else if (isBoolean(cell.v)) {
cell.t = 'b';
} else if (cell.v instanceof Date) {
cell.t = 'n';
cell.v = buildExcelDate(cell.v);
cell.z = cell.z || _xlsx2.default.SSF._table[14]; // eslint-disable-line no-underscore-dangle
} else {
cell.t = 's';
}
if (isNumber(cell.z)) cell.z = _xlsx2.default.SSF._table[cell.z]; // eslint-disable-line no-underscore-dangle
workSheet[cellRef] = cell;
}
}
if (range.s.c < 1e7) {
workSheet['!ref'] = _xlsx2.default.utils.encode_range(range);
}
if (options['!cols']) {
workSheet['!cols'] = options['!cols'];
}
if (options['!rows']) {
workSheet['!rows'] = options['!rows'];
}
if (options['!merges']) {
workSheet['!merges'] = options['!merges'];
}
return workSheet;
};
讓opts里的rows能夠被添加至workSheet當中
然后再修改在xlsx-style里的xlsx.js write_ws_xml_data 方法,添加設置行高的代碼。
function write_ws_xml_data(ws, opts, idx, wb) {
var o = [], r = [], range = safe_decode_range(ws['!ref']), cell, ref, rr = "", cols = [], R, C,rows = ws['!rows'];
for(C = range.s.c; C <= range.e.c; ++C) cols[C] = encode_col(C);
for(R = range.s.r; R <= range.e.r; ++R) {
r = [];
rr = encode_row(R);
for(C = range.s.c; C <= range.e.c; ++C) {
ref = cols[C] + rr;
if(ws[ref] === undefined) continue;
if((cell = write_ws_xml_cell(ws[ref], ref, ws, opts, idx, wb)) != null) r.push(cell);
}
if(r.length > 0){
params = ({r:rr});
if(rows && rows[R]) {
row = rows[R];
if(row.hidden) params.hidden = 1;
height = -1;
if (row.hpx) height = px2pt(row.hpx);
else if (row.hpt) height = row.hpt;
if (height > -1) { params.ht = height; params.customHeight = 1; }
if (row.level) { params.outlineLevel = row.level; }
}
o[o.length] = (writextag('row', r.join(""), params));
}
}
if(rows) for(; R < rows.length; ++R) {
if(rows && rows[R]) {
params = ({r:R+1});
row = rows[R];
if(row.hidden) params.hidden = 1;
height = -1;
if (row.hpx) height = px2pt(row.hpx);
else if (row.hpt) height = row.hpt;
if (height > -1) { params.ht = height; params.customHeight = 1; }
if (row.level) { params.outlineLevel = row.level; }
o[o.length] = (writextag('row', "", params));
}
}
return o.join("");
}
結語
如果還有什么問題歡迎評論交流,最近一段時間都會折騰excel,所以會經常關注的。
詳細的樣式和完整的設置代碼過段時間再補上~
先附上導出表格的效果圖:
問題回復
【1】
這里再解釋一下node-xlsx和xlsx的關系。首先node-xlsx是對xlsx的封裝,簡化了一些操作(這是我們在node項目里使用它比較重要的原因)
但是由于xlsx庫沒有對樣式做處理,所以有人又在xlsx的基礎上封裝了樣式,也就是xlsx-style庫。
所以我們進行了從A到B的步驟,為的就是能夠使用樣式
在回到問題本身,只用xlsx-style的話肯定可以,修改的方法和上述一樣。因為這種方法的核心就是去修改xlsx-style里的xlsx文件
只不過,如果只使用xlsx-style的話,那你需要一些額外的步驟去調用build方法,可以參看node-xlsx的處理(因為node-xlsx就是對xlsx的封裝)
這里再多說一句,其實xlsx庫和xlsx-style庫很多很多地方都非常相似,而后者主要增加了對樣式的處理。
以上。(如果還有不明白的歡迎繼續留言~)
--------------------------------------4月5日更新--------------------------------------------------
增加github的demo供大家參考~
https://github.com/huyifan/NodeExcelStyleDemo
歡飲大家討論~