nodejs使用xlsx和xlsx-style導(dǎo)出Excel文件

nodejs 導(dǎo)出 Excel


安裝依賴(lài)

  • npm install xlsx --save
  • npm install xlsx-style --save
  • npm install fs --save

修改文件

  • 在導(dǎo)出 xlsx 文件中 表格展示內(nèi)容樣式錯(cuò)誤 請(qǐng)修改下列文件部分內(nèi)容路召。

  • 在 node_modules/xlsx-style下的xlsx.js文件中别洪,把 write_ws_xml_data()替換成下列這個(gè),導(dǎo)出excel沒(méi)有樣式淹仑,直接修改方法显押。

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 = 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 = 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("");
}

使用 導(dǎo)出Excel 功能

  • ExcelData :[{},{},{}] —— 需要自定義單元格樣式 或者 合并單元 使用下列格式
    let ExcelData = [{
    value : String // 單元格內(nèi)容
    style :Object //單元格樣式
    colSpan : Number //以當(dāng)前單元格為起點(diǎn) 合并列的單元格
    rowSpan : Number //以當(dāng)前單元格為起點(diǎn) 合并行的單元格
    }];
  • workSheet :需要自定義行高或者列表 合并單元等等
    let workSheet = {
    "!merges" : [ {
    s: { //s為開(kāi)始 c: 1,//開(kāi)始列 r: 0//可以看成開(kāi)始行,實(shí)際是取值范圍 },
    e: { // e結(jié)束 c: 4,//結(jié)束列 r: 0//結(jié)束行 }
    }],
    "!cols" : [{wch:10},{wch:10}],
    "!rows" : [{hpx:20},{hpx:20}],
    };
// 使用 導(dǎo)出Excel 功能
const excel = async () => {
    let ExcelHeaders = ['標(biāo)題1','標(biāo)題2','標(biāo)題3','標(biāo)題4'];
    let ExcelData = [
        [1,11,111,1111],
        [2,22,222,2222],
        [3,33,333,3333],
        [4,44,444,4444],
    ];

    // 創(chuàng)建工作簿
    const buffer = await new ExportExcel()
        .createWorkBook(
            [
                { ExcelHeaders,ExcelData, sheetName: '車(chē)輛入場(chǎng)通知單', workSheet : {} }, // sheet1
                // { ExcelData, sheetName: '車(chē)輛入場(chǎng)通知單', workSheet : {} }, // sheet2
            ]
        );
    // const filename = `${DateUtils.format(new Date(), 'yyyyMMdd')}.xlsx`;
    // await fs.writeFileSync(`./${filename}`, buffer, { flag: 'w' });
}

功能實(shí)現(xiàn)詳細(xì)代碼

import * as xlsx from 'xlsx';
import * as XLSX_STYLE from 'xlsx-style';
import * as fs from 'fs';


// 導(dǎo)出并生成Excel
class ExportExcel {

    /**
     * 邊框樣式
     */
    BorderStyle = {
        border: {
            color: {auto: 1},
            top: {style: 'thin'},
            bottom: {style: 'thin'},
            left: {style: 'thin'},
            right: {style: 'thin'}
        }
    };

    /** 默認(rèn)樣式 */
    defaultStyle = {
        ...this.BorderStyle,
        alignment: {
            /// 自動(dòng)換行
            wrapText: true,
            // 內(nèi)容在單元格  居中
            horizontal: "center",
            vertical: "center",
        },
        font: {
            name: "宋體",
            sz: 12, // 字體大小
            color: {auto: 1},
            bgColor : '#fff'
        },
    };

    /**
     * 默認(rèn)標(biāo)題樣式
     */
    defaultTitleStyle = {
        ...this.defaultStyle,
        font: {
            name: "宋體",
            sz: 12,
            color: {auto: 1},
            bold : true,  // 字體加粗
        },
    };

    /**
     * 將對(duì)象數(shù)組按指定屬性順序轉(zhuǎn)為二維數(shù)組
     * @param objects 對(duì)象數(shù)組
     * @param props 對(duì)象的屬性列表,屬性可寫(xiě)為數(shù)組,數(shù)組只包含兩個(gè)值,
     *          第一個(gè)值為對(duì)象屬性名,第二個(gè)值為過(guò)濾函數(shù)
     */
    static objectsToRows(objects, props) {
        const rows = [];
        for (let i = 0; i < objects.length; i++) {
            const row = [];
            const item = objects[i];
            for (const key of props) {
                if (key instanceof Array) {
                    row.push(key[1](item[key[0]]));
                } else {
                    row.push(item[key]);
                }
            }
            
            rows.push(row);
        }
        return rows;
    }

    /**
     * 設(shè)置顯示數(shù)據(jù)樣式顯示
     * @param RowData  數(shù)據(jù)
     * @param isBold   顯示內(nèi)容是否加粗
     * @returns {*}
     */
    setRowData(RowData = [],isBold = false,index = 0){
        for (let i = 0; i < RowData.length; i++) {
            if(RowData[i] instanceof Array){
                this.setRowData(RowData[i],isBold,(i + index));
            }
            else if(RowData[i] instanceof Object){
                if(!this.merges){
                    this.merges = [];
                }
                let {value = '', style = {}, colSpan = 0, rowSpan = 0} = RowData[i];
                RowData[i] = {v: value, s: {...this.defaultStyle, ...style}};

                if(rowSpan > 0){
                    this.merges.push({
                        s: {c: i,r: index},
                        e: {c: i,r: (index + rowSpan) }
                    })
                }
                if(rowSpan === 0 && colSpan > 0){
                    this.merges.push({
                        s: {c: i,r: index},
                        e: {c: (colSpan + i),r: index }
                    })
                }
            }
            else {
                if(isBold){
                    RowData[i] = {v: RowData[i] || '', s: this.defaultTitleStyle};
                }else {
                    RowData[i] = {v: RowData[i] || '', s: this.defaultStyle};
                }
            }
        }
        return RowData;
    }


    /** 
     * 
     ExcelData = [
     {
         value : String 單元格內(nèi)容
         style :Object   單元格樣式
         colSpan : Number  以當(dāng)前單元格為起點(diǎn)  合并列的單元格
         rowSpan : Number  以當(dāng)前單元格為起點(diǎn)  合并行的單元格
       }
     ]
     workSheet = {
     "!merges" : [ {
       s: { //s為開(kāi)始 c: 1,//開(kāi)始列 r: 0//可以看成開(kāi)始行,實(shí)際是取值范圍 },
       e: { // e結(jié)束 c: 4,//結(jié)束列 r: 0//結(jié)束行 }
       }];
   }
     */
    createSheet = async ({ExcelHeaders = [], ExcelData = [] , workSheet = {}}) => {
        const data = [];
        if(ExcelHeaders.length){
            const headers = await this.setRowData(ExcelHeaders, true );
            data.push(headers);
        }

        // 表格數(shù)據(jù)內(nèi)容
        let rowData = await this.setRowData(ExcelData, false, data.length );
        data.push(...rowData);
        let sheet = xlsx.utils.aoa_to_sheet(data, {cellDates: true, cellStyles: true});

        // 表單合并情況
        sheet["!merges"] = this.merges || [];

        // 表格每列顯示的列寬
        let cols = [];
        if(workSheet['cols']){
            for(let i = 0; i < workSheet['cols'].length; i++){
                cols.push({wch: workSheet['cols'][i] || 10})
            }
            workSheet['!cols'] = cols;
        }else {
            for(let i = 0; i < ExcelHeaders.length; i++){
                cols.push({wch: ExcelHeaders[i] || 10})
            }
        }
        sheet["!cols"] = cols;

        // 表格顯示  行高
        let rows = [];
        if(ExcelHeaders.length) {
            rows.push({hpx: 30});
        }
        for(let i = 0; i < ExcelData.length; i++){
            rows.push({hpx: 24})
        }
        sheet['!rows'] = rows;

        return {...sheet, ...workSheet};
    };

    /**
     * 創(chuàng)建 Excel 工作簿
     * @param data Array 可創(chuàng)建一個(gè)或者多個(gè) sheet
     * @returns {Promise<*>}
     */
    createWorkBook = async (data = []) => {
        //  創(chuàng)建一個(gè)工作簿
        const workBook = xlsx.utils.book_new();
        for(let i = 0; i < data.length; i++){
            let sheet = await this.createSheet(data[i]);
            if(sheet){
                let sheetName = `sheet${i}`;
                if(data[i].sheetName){
                    sheetName = data[i].sheetName;
                }
                xlsx.utils.book_append_sheet(workBook, sheet, sheetName);
            }
        }
        // 返回寫(xiě)入數(shù)據(jù)  buffer
        return XLSX_STYLE.write(workBook, {type: 'buffer'});
    };

}

export {ExportExcel};
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載柳洋,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。
  • 序言:七十年代末孙援,一起剝皮案震驚了整個(gè)濱河市害淤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拓售,老刑警劉巖窥摄,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異邻辉,居然都是意外死亡溪王,警方通過(guò)查閱死者的電腦和手機(jī)腮鞍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)莹菱,“玉大人移国,你說(shuō)我怎么就攤上這事〉牢埃” “怎么了迹缀?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蜜徽。 經(jīng)常有香客問(wèn)我祝懂,道長(zhǎng),這世上最難降的妖魔是什么拘鞋? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任砚蓬,我火速辦了婚禮,結(jié)果婚禮上盆色,老公的妹妹穿的比我還像新娘灰蛙。我一直安慰自己,他們只是感情好隔躲,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布摩梧。 她就那樣靜靜地躺著,像睡著了一般宣旱。 火紅的嫁衣襯著肌膚如雪仅父。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天浑吟,我揣著相機(jī)與錄音笙纤,去河邊找鬼。 笑死买置,一個(gè)胖子當(dāng)著我的面吹牛粪糙,可吹牛的內(nèi)容都是我干的强霎。 我是一名探鬼主播忿项,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼城舞!你這毒婦竟也來(lái)了轩触?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤家夺,失蹤者是張志新(化名)和其女友劉穎脱柱,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體拉馋,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡榨为,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年惨好,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片随闺。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡日川,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出矩乐,到底是詐尸還是另有隱情龄句,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布散罕,位于F島的核電站分歇,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏欧漱。R本人自食惡果不足惜职抡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望误甚。 院中可真熱鬧繁调,春花似錦、人聲如沸靶草。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)奕翔。三九已至裕寨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間派继,已是汗流浹背宾袜。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留驾窟,地道東北人庆猫。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像绅络,于是被迫代替她去往敵國(guó)和親月培。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348