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
源碼(https://github.com/noahlam/data-process) 記得star 和follow哦。
