js-xlsx + handsontable + echarts 實現在前端匯入excel資料並生成echart報表
前言
最近都在做類似 ERP 的專案,所以呢,又碰到一個比較變態的需求(至少對我來說是),在前端匯入 excel 檔案,
然後在瀏覽器裡面預覽和編輯, 最後再選擇一些資料,用echarts生成報表.
依賴
js-xlsx 讀取excel資料到js
handsontable 類似Excel一樣顯示和編輯列表資料
echarts 一個生成各種報表的庫
資料匯入
資料匯入這邊需要用到 瀏覽器的 FileReader物件
的 readAsBinaryString()
函式, 把選擇的檔案讀取出來,
然後再監聽 FileReader 物件的 onload 事件
, 在 onload 事件 的回撥函式中,我們可以獲取到 讀取的二進位制資料.
這裡順便提一下, FileReader 物件提供以下方法,用來讀取各種格式的資料(參考自MDN)
FileReader.readAsArrayBuffer() // 讀取檔案的 ArrayBuffer 資料物件.
FileReader.readAsBinaryString() // 讀取檔案的原始二進位制資料
FileReader.readAsDataURL() // 返回一個URL格式的字串以表示所讀取檔案的內容
FileReader.readAsText() // 返回一個字串以表示所讀取的檔案內容
tips: 需要注意的是 readXxxxx() 函式,是不直接返回讀取結果的,因為讀取這個動作非同步的.
readAsBinaryString 讀取到的內容應該是一個二進位制的字串,這個時候,需要呼叫 js-xlsx
的 read
方法, read 返回的是一個可讀性很強的物件了,我看了一下,裡面有關於表格的屬性很多都有
,如 樣式, vsb巨集, sheets等等 (反正我對excel也不熟,認識的也就這些哈),
npm i xlsx
複製程式碼
import XLSX from `xlsx`
...
let res = XLSX.read(data, {type: `binary`})
let sheetName = res.Sheets[res.SheetNames[0]]
let table = XLSX.utils.sheet_to_json(sheetName, {header: `A`, raw: true, defval: ` `})
複製程式碼
這裡的 res 得到的我猜是 excel 表格原有的資料,裡面包含上面說的很多種資料,而正常情況下,
我們需要的往往只是第一個 sheet 裡面的 純資料
, 所以呼叫 XLSX.utils.sheet_to_json
獲取第一個 sheet 的資料, table 拿到的應該是一個我們熟悉的陣列了.這個時候如果你只是單純的渲染的話,
你甚至可以就此打住,自己寫一個渲染方法(比如字串拼接哈)把資料渲染出來即可.
如果單純的顯示無法滿足你的需求,那麼你可能需要 handsontable 了.
tips: sheet_to_json 的 第二個引數裡面的 header,可以傳數字,也可以轉 `A`, 兩個的主要區別在於轉化出來的表第一行如果是空行會不會正常顯示,
傳 `A` 會正常顯示,傳數字的話,如果表格的第一行有空白單元格,表格會錯亂。
資料展示
首先當然是安裝,我的專案是基於 vue 的,所以要安裝 vue 版本的,其他框架的,只要安裝響應的版本即可.
npm i @handsontable/vue
複製程式碼
然後就可以直接這麼用
<template>
<HotTable :settings="settings"></HotTable>
</template>
<script>
import HotTable from `@handsontable/vue`
export default {
...
components: {HotTable}
...
}
</script>
複製程式碼
模板裡面的 settings 是 handsontable 的一些配置, 每個專案的需求不同,配置也不同,這裡就不列舉出來了, 上面獲取到的 table 在這裡要賦值給 settings.data
我這裡用 handsontable 顯示資料的目的,是讓使用者可以清晰的看到上傳的表的資料和結構,可以刪除屌部分無用的資料,減少冗餘。
生成報表
資料都處理完了之後,就是生成報表了, 報表這裡稍微做的靈活了一點,是要讓使用者根據上傳的資料,自己選擇欄位,然後用 echart 去生成對應的報表。
為了偷懶降低複雜度,我這裡只提供了3種報表型別供選擇,分別是 餅圖,柱狀圖,折線圖,稍微分析 echart 的配置手冊,我發現各種型別的圖表,
其實主要的區別在 series 配置項下面,其他位置幾乎沒啥區別 就算有區別,也被我無視 。最終的實現大概是這樣
let option = {
title: {...},
tooltip: {...},
xAxis: {...},
yAxis: {...},
toolbox: {...},
}
switch (type) {
case `pie` :
option.series = {...}
break
case `pie` :
option.series = {...}
break
case `pie` :
option.series = {...}
break
}
myChart.setOption(option)
複製程式碼
echart 第一次渲染完以後,如果改變 option 的資料然後重新渲染,新的 option 資料是採用追加的方式加進去的,類似 javascript 的 Object.assign(),
所以如果新的資料沒辦法完全覆蓋掉就舊的資料的話,舊的那些沒有被覆蓋掉的資料,還會渲染出來. 我對這種情況的處理方法是呼叫dispose.dispose()
把例項銷燬掉,
然後重新建立一個新例項,設定新的 option
最後
本文用的程式碼都比較基礎,就不上傳了,有需要的童鞋可以私下發郵件找我要。