前端導(dǎo)出Excel實踐指南
點擊上方關(guān)注 前端技術(shù)江湖,一起學(xué)習(xí),天天進步
寫在前面
雙手奉上代碼鏈接
https://github.com/ajun568/export-excel
雙腳奉上最終效果圖

觀前提醒
?? 本文最終實現(xiàn)效果如上圖, 具體功能為: 導(dǎo)出Excel + 多個Sheet + 可合并的多行表頭. 代碼部分采用 React+TS 作為工具進行編寫.
準(zhǔn)備工作
?? 安裝 xlsx.js npm install xlsx
?? 寫入Excel文件: XLSX.write(workbook, writeOpts)
workbook ??
SheetNames @types string[]: 當(dāng)前 Sheet 的名稱Sheets: 當(dāng)前sheet的對象, 格式如下
[SheetNames]: {
"!refs": "A1:G7", // 表示從 第1行第A列 到 第7行第G列
"!cols": [{wpx: 80} ... ], // 表示 列寬 80px
"!rows": [{hpx: 20} ... ], // 表示 行高 20px
"!merges": [{s: {r: 0, c: 2}, e: {r: 0, c: 3}} ... ], // 表示 將 第0行第2列 和 第0行第3列 進行合并 (s: start, e: end, c: column, r: row)
"A1": {v: "姓名"}, // 表示第1行第A列 顯示數(shù)據(jù)為 "姓名", 以此類推 ...
...
}
writeOpts ??
{
type, // 數(shù)據(jù)編碼, 本文采用 binary 二進制格式
bookType, // 導(dǎo)出類型, 本文采用 xlsx 類型
compression, // 是否使用 Gzip 壓縮
}
下載文件
想要下載文件, 我小A第一個表示不服, 申請出戰(zhàn) <a 標(biāo)簽的 download 屬性>
通過 URL.createObjectURL(Object) 來創(chuàng)建下載所需的 URL. 由于每次調(diào)用都會產(chǎn)生新的 URL 對象, 故使用后記得釋放, 釋放方法 URL.revokeObjectURL(FileUrl)
通過模擬 click 事件觸發(fā) a 標(biāo)簽, 以實現(xiàn)下載
const saveAs = (obj: Blob, fileName?: string): void => {
const temp = document.createElement('a')
temp.download = fileName || 'download'
temp.href = URL.createObjectURL(obj)
temp.click()
setTimeout(() => { URL.revokeObjectURL(temp.href) }, 100)
}
頭部處理
Mock數(shù)據(jù): 詳細數(shù)據(jù)請?zhí)D(zhuǎn) Github, 在 mock.ts 中查看
Header 部分?jǐn)?shù)據(jù)格式
[
...
{
key: 'animal',
value: '動物',
child: [
{
key: 'dog',
value: '狗',
child: [
{
key: 'corgi',
value: '柯基',
},
{
key: 'husky',
value: '哈士奇',
},
],
},
{
key: 'tiger',
value: '老虎',
},
],
},
...
]
Data 部分?jǐn)?shù)據(jù)格式
[
{
name: '黃刀小五',
desc: '基于搜索引擎的復(fù)制粘貼攻城獅',
watermelon: '喜歡',
banana: '不喜歡',
corgi: '喜歡',
husky: '喜歡',
tiger: '不喜歡',
},
...
]
頭部數(shù)據(jù)處理
?? 分析
Header 數(shù)據(jù)為樹形結(jié)構(gòu), 其深度為頭部所占行數(shù) Header 數(shù)據(jù)要轉(zhuǎn)換成 Data 數(shù)據(jù)的格式, 并與 Data 數(shù)組合并, 共同處理成導(dǎo)出所需格式 轉(zhuǎn)換對象的 key 應(yīng)為最小葉子結(jié)點的 key 轉(zhuǎn)換對象的 value 應(yīng)為當(dāng)前層級的 value ( 即導(dǎo)出后當(dāng)前行所顯示的 value ) 既然是樹, 果斷遞歸, 準(zhǔn)沒錯
???♂? Code

???♂? Image

Merged 數(shù)據(jù)
{
s: { // start
r: x, // row
c: y, // column
},
e: { ... } // end
}
?? 分析
將處理后的頭部數(shù)據(jù)看成一個矩陣 行或列中, 相鄰元素若相同, 則進行合并
tips: 本文采用的是判斷相鄰 value 值是否相等進行合并, 若有需求, 建議改寫為對象形式加以完善.
???♂? Code

???♂? Image

生成sheet數(shù)據(jù)
利用 Object.assign進行對象合并利用 String.fromCharCode(65 + i)對列進行大寫字母的轉(zhuǎn)換
???♂? Code

???♂? Image

轉(zhuǎn)換字節(jié)流
利用 new ArrayBuffer(str) 創(chuàng)建一個緩沖區(qū), 使用 new Uint8Array(buf) 引用

因為 unicode 編碼是 0~65535, 而 Uint8Array 范圍為 0~255, 故需要按位與 0xFF, 以保持位數(shù)一致
const s2ab = (str: string): ArrayBuffer => {
let buf = new ArrayBuffer(str.length)
let view = new Uint8Array(buf)
for (let i = 0; i !== str.length; ++i) {
view[i] = str.charCodeAt(i) & 0xFF
}
return buf
}
導(dǎo)出文件
結(jié)合前文 準(zhǔn)備工作 部分所講, 導(dǎo)出的代碼邏輯就出來了, 直接上代碼

結(jié)束語
開源版本不支持設(shè)置樣式, 若有需求, 可采用 付費版本 或使用 xlsx-style, 使用方法與本文一致. 大家可參照文檔自行添加樣式部分.

參考??鏈接
https://github.com/SheetJS/sheetjs#writing-options
https://juejin.cn/post/6872375842358919175
http://www.seefly.top/archives/%E5%89%8D%E7%AB%AF%E4%BD%BF%E7%94%A8xlsxjs%E5%AF%BC%E5%87%BA%E6%9C%89%E5%A4%8D%E6%9D%82%E8%A1%A8%E5%A4%B4%E7%9A%84excelmd#6%E5%8D%95%E5%85%83%E6%A0%BC%E5%88%97%E5%AE%BD
The End
歡迎自薦投稿到《前端技術(shù)江湖》,如果你覺得這篇內(nèi)容對你挺有啟發(fā),記得點個 「在看」哦
點個『在看』支持下 
