前端導(dǎo)出Excel實(shí)踐指北
點(diǎn)擊上方 程序員成長(zhǎng)指北,關(guān)注公眾號(hào)
回復(fù)1,加入高級(jí)Node交流群

Hello, 各位勇敢的小伙伴, 大家好, 我是你們的嘴強(qiáng)王者小五, 身體健康, 腦子沒病.
本人有豐富的脫發(fā)技巧, 能讓你一躍成為資深大咖.
一看就會(huì)一寫就廢是本人的主旨, 菜到摳腳是本人的特點(diǎn), 卑微中透著一絲絲剛強(qiáng), 傻人有傻福是對(duì)我最大的安慰.
歡迎來到
小五的隨筆系列之前端導(dǎo)出Excel在線指北.
寫在前面
雙手奉上代碼鏈接 傳送門 - ajun568
雙腳奉上最終效果圖
觀前提醒
?? 本文最終實(shí)現(xiàn)效果如上圖, 具體功能為: 導(dǎo)出Excel + 多個(gè)Sheet + 可合并的多行表頭. 代碼部分采用 React+TS 作為工具進(jìn)行編寫.
準(zhǔn)備工作
?? 安裝 xlsx.js npm install xlsx
?? 寫入Excel文件: XLSX.write(workbook, writeOpts)
workbook ??
-
SheetNames @types string[]: 當(dāng)前 Sheet 的名稱 -
Sheets: 當(dāng)前sheet的對(duì)象, 格式如下
[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列 進(jìn)行合并 (s: start, e: end, c: column, r: row)
"A1": {v: "姓名"}, // 表示第1行第A列 顯示數(shù)據(jù)為 "姓名", 以此類推 ...
...
}
writeOpts ??
{
type, // 數(shù)據(jù)編碼, 本文采用 binary 二進(jìn)制格式
bookType, // 導(dǎo)出類型, 本文采用 xlsx 類型
compression, // 是否使用 Gzip 壓縮
}
下載文件
想要下載文件, 我小A第一個(gè)表示不服, 申請(qǐng)出戰(zhàn) <a 標(biāo)簽的 download 屬性>
通過 URL.createObjectURL(Object) 來創(chuàng)建下載所需的 URL. 由于每次調(diào)用都會(huì)產(chǎn)生新的 URL 對(duì)象, 故使用后記得釋放, 釋放方法 URL.revokeObjectURL(FileUrl)
通過模擬 click 事件觸發(fā) a 標(biāo)簽, 以實(shí)現(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ù): 詳細(xì)數(shù)據(jù)請(qǐng)?zhí)D(zhuǎn) Github, 在 mock.ts 中查看
Header 部分?jǐn)?shù)據(jù)格式
[
...
{
key: 'animal',
value: '動(dòng)物',
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)換對(duì)象的 key 應(yīng)為最小葉子結(jié)點(diǎn)的 key -
轉(zhuǎn)換對(duì)象的 value 應(yīng)為當(dāng)前層級(jí)的 value ( 即導(dǎo)出后當(dāng)前行所顯示的 value ) -
既然是樹, 果斷遞歸, 準(zhǔn)沒錯(cuò)
???♂? Code
???♂? Image
Merged 數(shù)據(jù)
{
s: { // start
r: x, // row
c: y, // column
},
e: { ... } // end
}
?? 分析
-
將處理后的頭部數(shù)據(jù)看成一個(gè)矩陣 -
行或列中, 相鄰元素若相同, 則進(jìn)行合并
tips: 本文采用的是判斷相鄰 value 值是否相等進(jìn)行合并, 若有需求, 建議改寫為對(duì)象形式加以完善.
???♂? Code
???♂? Image
生成sheet數(shù)據(jù)
-
利用 Object.assign進(jìn)行對(duì)象合并 -
利用 String.fromCharCode(65 + i)對(duì)列進(jìn)行大寫字母的轉(zhuǎn)換
???♂? Code
???♂? Image
轉(zhuǎn)換字節(jié)流
利用 new ArrayBuffer(str) 創(chuàng)建一個(gè)緩沖區(qū), 使用 new Uint8Array(buf) 引用
因?yàn)?nbsp;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è)置樣式, 若有需求, 可采用 付費(fèi)版本 或使用 xlsx-style, 使用方法與本文一致. 大家可參照文檔自行添加樣式部分.
參考??鏈接
【Github】 SheetJS ~ js-xlsx
【mySoul】 優(yōu)雅 | 前后端優(yōu)雅的導(dǎo)入導(dǎo)出Excel
【Seefly】 前端使用xlsx.js導(dǎo)出有復(fù)雜表頭的excel

“分享、點(diǎn)贊、在看” 支持一波 
