js-xlsx + handsontable + echarts 實如今前端導入excel數據並生成echart報表javascript
最近都在作相似 ERP 的項目,因此呢,又碰到一個比較變態的需求(至少對我來講是),在前端導入 excel 文件,
而後在瀏覽器裏面預覽和編輯, 最後再選擇一些數據,用echarts生成報表.前端
js-xlsx 讀取excel數據到jsvue
handsontable 相似Excel同樣顯示和編輯列表數據java
echarts 一個生成各類報表的庫git
數據導入這邊須要用到 瀏覽器的 FileReader對象
的 readAsBinaryString()
函數, 把選擇的文件讀取出來,
而後再監聽 FileReader 對象的 onload 事件
, 在 onload 事件 的回調函數中,咱們能夠獲取到 讀取的二進制數據.
這裏順便提一下, FileReader 對象提供如下方法,用來讀取各類格式的數據(參考自MDN)github
FileReader.readAsArrayBuffer() // 讀取文件的 ArrayBuffer 數據對象.apache
FileReader.readAsBinaryString() // 讀取文件的原始二進制數據npm
FileReader.readAsDataURL() // 返回一個URL格式的字符串以表示所讀取文件的內容json
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
源碼記得star 和follow哦。