electron中使用adm-zip將多個excel檔案壓縮排資料夾,使用XLSX以及XLSXStyle生成帶樣式excel檔案

zhangzuying發表於2022-12-27

需求:electron環境下想要實現根據多個表生成多個Excel檔案,打包存入資料夾內並壓縮下載到本地。(實際場景描述:介面中有軟體工程一班學生資訊、軟體工程二班學生資訊、軟體工程三班學生資訊,上方有“一鍵生成”的按鈕,點選時彈出檔案儲存位置選擇框選擇壓縮包所要儲存的位置,選擇完成後點選儲存後生成壓縮包中存放一、二、三班三張對應班級學生資訊Excel的檔案。)注:生成的Excel檔案需要按照對應模板進行顯示(合併單元格、修改excel單元格的高度和寬度...)

思路:使用electron中的dialog選擇儲存檔案的位置,判斷所選地址是否存在;如果存在使用XLSX、XLSXStyle進行檔案的生成;然後使用adm-zip將所有檔案存放在zip資料夾中儲存到本地。
1.下載xlsx xlsx-style adm-zip
 npm i xlsx
 npm i xlsx-style
 npm i adm-zip
 或
 yarn add xlsx
 yarn add xlsx-style
 yarn add adm-zip

注意:使用xlsx-style外掛的時候需要簡單修改下原始碼,第一種情況下會出現報錯,第二種是為了可以調整excel單元格的行高。
1)修改node_modules資料夾下的xlsx-style下的dist下的cpexcel.js檔案
807行:var cpt = cptable;
2)修改xlsx-style資料夾下面的xlsx.js檔案 替換write_ws_xml_data以下方法

// xlsx-style版本0.8.13
// xlsx版本0.14.1 
//這是xlsx-style檔案中的xlsx.js的需要修改的程式碼,是從xlsx資料夾中的xlsx.js中複製出來的
// write_ws_xml_data找到找個方法名字,全部替換
// 把xlsx中能修改高度的程式碼複製到xlsx-style中
var DEF_PPI = 96, PPI = DEF_PPI;
function px2pt(px) { return px * 96 / PPI; }
function pt2px(pt) { return pt * PPI / 96; }
function write_ws_xml_data(ws, opts, idx, wb) {
	var o = [], r = [], range = safe_decode_range(ws['!ref']), cell="", ref, rr = "", cols = [], R=0, C=0, rows = ws['!rows'];
	var dense = Array.isArray(ws);
	var params = ({r:rr}), row, height = -1;
	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;
			var _cell = dense ? (ws[R]||[])[C]: ws[ref];
			if(_cell === undefined) continue;
			if((cell = write_ws_xml_cell(_cell, ref, ws, opts, idx, wb)) != null) r.push(cell);
		}
		if(r.length > 0 || (rows && rows[R])) {
			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("");
}
2.可以在utils下新建一個js檔案用於寫入xlsx以及xlsx-style相關方法
// 匯出為excel
import XLSX from 'xlsx'
import XLSXStyle from 'xlsx-style'

export function exportExcel(filename, data) {
  var sheet_name = 'Sheet1'
  var work_book = XLSX.utils.book_new()
  var sheet = XLSX.utils.aoa_to_sheet(data)
  XLSX.utils.book_append_sheet(work_book, sheet, sheet_name)
  XLSXStyle.writeFile(work_book, filename) //匯出Excel
}
// 將workbook裝化成ArrayBuffer物件
export function workbook2ArrayBuffer(workbook) {
  var wopts = {
    bookType: 'xlsx',
    bookSST: false,
    type: 'binary',
  }
  var wbout = XLSXStyle.write(workbook, wopts)
  // 將字串轉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 s2ab(wbout)
}
3.在要進行一鍵生成的介面進行相關邏輯的編寫。
<button @click='ClickGenerate'>一鍵生成</button>
...
...
methods: {
    // 一鍵生成按鈕方法
	ClickGenerate(){
            const fs = require('fs')
            let zipName = '軟體工程學生資訊.zip'
            // 彈出dialog選擇儲存檔案的位置
            const { dialog } = require("electron").remote
            let options = {
                title: "軟體工程學生資訊", //下載檔名稱
                defaultPath: zipName //下載檔案title
            }
            dialog.showSaveDialog(options, (result) => {
                if (!result) {
                    this.$message.warning('下載任務已取消')
                    return
                }
                let path = getDirectory(result)
                if (!fs.existsSync(path)) {
                    this.$message.warning('所選路徑不存在!')
                    return
                }
                // 準備下載zip檔案
                this.downloadZip(result)
            })
	},
	// 匯出涉密人員zip
        async downloadZip(savePath) {
            // 準備生成zip
            let AdmZip = require("adm-zip")
            let zip = new AdmZip()
            zip.writeZip(savePath)
            //#region 
            // 生成軟體工程一班學生資訊並放入zip包中
            let soft1buffer = this.creatSoftbuffer('1')
            if (soft1buffer) {
                zip.addFile('軟體工程一班學生資訊' + ".xlsx", soft1buffer, "")
            }
            // #endregion
            zip.writeZip(savePath, (error) => {
                if (error) {
                    this.$message.error('[下載異常]' + error.message)
                    return
                }
                this.$message.success('下載已完成')
            })
        },
	// 生成excel檔案流
	creatSoftbuffer(name) {
            let list = []
            let date = new Date()
            let Y = date.getFullYear() + '-';
            let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
            let D = date.getDate() + ' ';
            let dqsj = Y + M + D // 獲取當前時間
            let soft1Datas = [
                { id: 1, name: "張三", age: '24', address: '哈爾濱市南崗區', grade: '軟體一班' },
                { id: 2, name: "里斯", age: '22', address: '哈爾濱市呼蘭區', grade: '軟體一班' },
                { id: 3, name: "王二", age: '21', address: '哈爾濱市松北區', grade: '軟體一班' },
                ...
	 	] // 此處資料可從後臺獲取
            switch (name) {
                case '1':
                    list.push(["軟體工程一班學生資訊"]) //確定列名
                    list.push(["統計時間:", dqsj, "", "", ""]) //確定列名
                    list.push(["序號", "姓名", "年齡", "地址", "年級"]) //確定列名
                    let lock1 = 0
                    for (let i in soft1Datas) { //每一行的值
                        lock1 = lock1 + 1
                        let item = soft1Datas[i]
                        let column = [lock1, item["name"], item["age"], item["address"], item["grade"]]
                        list.push(column)
                    }
                    //Excel sheet頁的名稱
                    var sheet_name = "軟體工程一班學生資訊"
                    var sum = 5 // 列的數量,根據自身專案進行資料的更改
                    break
                default:
                    console.log(0)
            }
            var sheet = XLSX.utils.aoa_to_sheet(list)
            // 設定列寬(這裡用到列的數量是用來設定不同列的不同寬度的)
            let counts = soft1Datas.length + 5
            sheet['!cols'] = []
            sheet['!rows'] = []
            for (let i = 1; i < sum; i++) {
                sheet['!cols'].push({ wpx: 150 }) // 設定列寬
            }
            for (let j = 1; j < counts; j++) {
                sheet['!rows'].push({ hpx: 14 }) // 設定列高
            }
            sheet['!rows'][0].hpx = 35
            sheet['!rows'][1].hpx = 20
            sheet['!rows'][2].hpx = 20
            sheet['!rows'][3].hpx = 20
            // 所有設定邊框字型水平居中等樣式
            for (let key in sheet) {
                if (sheet[key] instanceof Object) {
                    sheet[key].s = {
                        alignment: {
                            horizontal: 'center', // 水平居中
                            vertical: 'center' // 垂直居中
                        },
                        font: {
                            sz: 11, // 字號
                            name: '宋體' // 字型
                        },
                        border: {  // 邊框
                            top: {
                                style: 'thin'
                            },
                            bottom: {
                                style: 'thin'
                            },
                            left: {
                                style: 'thin'
                            },
                            right: {
                                style: 'thin'
                            }
                        }
                    }
                }
            }
            // 標題樣式修改
            sheet.A1.s = {
                font: {
                    name: '宋體',
                    sz: 16, // 字號
                    bold: true,
                },
                alignment: {
                    horizontal: 'center', // 水平居中
                    vertical: 'center' // 垂直居中
                }
            }
            sheet['!merges'] = [
                { s: { r: 0, c: 0 }, e: { r: 0, c: 5 } }
            ];
            //新建book
            var work_book = XLSX.utils.book_new()
            //將資料新增到工作薄
            XLSX.utils.book_append_sheet(work_book, sheet, sheet_name)
            const workbookArrayBuffer = workbook2ArrayBuffer(work_book)
            //arrayBuffer轉Buffer
            var buf = new Buffer(workbookArrayBuffer.byteLength)
            var view = new Uint8Array(workbookArrayBuffer)
            for (var i = 0; i < buf.length; ++i) {
                buf[i] = view[i]
            }
            return buf
        }
}

相關文章