本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫
正文
本文接下來的內容素材都是基於H5可視化編輯器(H5-Dooring)項目的截圖, 如果想實際體驗, 可以訪問H5-Dooring網站實際體驗. 接下來我們直接開始我們的方案實現.
1. 使用JavaScript實現前端導入excel文件並自動生成可編輯的Table組件
在開始實現之前, 我們先來看看實現效果.
1.1 實現效果
導入excel文件並通過antd的table組件渲染table:
編輯table組件:
保存table數據後實時渲染可視化圖表:
以上就是我們實現導入excel文件後, 編輯table, 最後動態生成圖表的完整流程.
1.2 實現一鍵導入excel文件並生成table表格
導入excel文件的功能我們可以用javascript原生的方式實現解析, 比如可以用fileReader這些原生api,但考慮到開發效率和後期的維護, 筆者這裡採用antd的Upload組件和XLSX來實現上傳文件並解析的功能. 由於我們採用antd的table組件來渲染數據, 所以我們需要手動將解析出來的數據轉換成table支持的數據格式.大致流程如下:
所以我們需要做的就是將Upload得到的文件數據傳給xlsx, 由xlsx生成解析對象, 最後我們利用javascript算法將xlsx的對象處理成ant-table支持的數據格式即可. 這裡我們用到了FileReader對象, 目的是將文件轉化為BinaryString, 然後我們就可以用xlsx的binary模式來讀取excel數據了, 代碼如下:
// 解析並提取excel數據let reader = new FileReader();reader.onload = function(e) {let data = e.target.result; let workbook = XLSX.read(data, {type: 'binary'}); let sheetNames = workbook.SheetNames; // 工作表名稱集合 let draftObj = {} sheetNames.forEach(name => { // 通過工作表名稱來獲取指定工作表 let worksheet = workbook.Sheets[name]; for(let key in worksheet) { // v是讀取單元格的原始值 if(key[0] !== '!') { if(draftObj[key[0]]) { draftObj[key[0]].push(worksheet[key].v) }else { draftObj[key[0]] = [worksheet[key].v] } } } }); // 生成ant-table支持的數據格式 let sourceData = Object.values(draftObj).map((item,i) => ({ key: i + '', name: item[0], value: item[1]}))
複製代碼
經過以上處理, 我們得到的sourceData即是ant-table可用的數據結構, 至此我們就實現了表格導入的功能.
1.3 table表格的編輯功能實現
table表格的編輯功能實現其實也很簡單, 我們只需要按照antd的table組件提供的自定義行和單元格的實現方式即可. antd官網上也有實現可編輯表格的實現方案, 如下:
大家感興趣的可以私下研究以下. 當然自己實現可編輯的表格也很簡單, 而且有很多方式, 比如用column的render函數來動態切換表格的編輯狀態, 或者使用彈窗編輯等都是可以的.
1.4 根據編輯的table數據動態生成圖表
根據table數據動態生成圖表這塊需要有一定的約定, 我們需要符合圖表庫的數據規範, 不過我們有了table數據, 處理數據規範當然是很簡單的事情了, 筆者的可視化庫採用antv的f2實現, 所以需要做一層適配來使得f2能消費我們的數據.
還有一點就是為了能使用多張圖表, 我們需要對f2的圖表進行統一封裝, 使其成為符合我們應用場景的可視化組件庫. 我們先看看f2的使用的數據格式:
const data = [{ genre: 'Sports', sold: 275 }, { genre: 'Strategy', sold: 115 }, { genre: 'Action', sold: 120 }, { genre: 'Shooter', sold: 350 }, { genre: 'Other', sold: 150 }];
複製代碼
此數據格式會渲染成如下的圖表:
所以說我們總結下來其主要有2個緯度的指標, 包括它們的面積圖, 餅圖, 折線圖, 格式都基本一直, 所以我們可以基於這一點封裝成組件的可視化組件, 如下:
import { Chart } from '@antv/f2';import React, { memo, useEffect, useRef } from 'react';import ChartImg from '@/assets/chart.png';import styles from './index.less';import { IChartConfig } from './schema';interface XChartProps extends IChartConfig {isTpl: boolean;}const XChart = (props: XChartProps) => { const { isTpl, data, color, size, paddingTop, title } = props; const chartRef = useRef(null); useEffect(() => { if (!isTpl) { const chart = new Chart({ el: chartRef.current || undefined, pixelRatio: window.devicePixelRatio, // 指定解析度 }); // step 2: 處理數據 const dataX = data.map(item => ({ ...item, value: Number(item.value) })); // Step 2: 載入數據源 chart.source(dataX); // Step 3:創建圖形語法,繪製柱狀圖,由 genre 和 sold 兩個屬性決定圖形位置,genre 映射至 x 軸,sold 映射至 y 軸 chart .interval() .position('name*value') .color('name'); // Step 4: 渲染圖表 chart.render(); } }, [data, isTpl]); return ( <div className={styles.chartWrap}> <div className={styles.chartTitle} style={{ color, fontSize: size, paddingTop }}> {title} </div> {isTpl ? <img src={ChartImg} alt="dooring chart" /> : <canvas ref={chartRef}></canvas>} </div> );};export default memo(XChart);
複製代碼
當然其他的可視化組件也可以用相同的模式封裝,這裡就不一一舉例了. 以上的組件封裝使用react的hooks組件, vue的也類似, 基本原理都一致.
2. 使用JavaScript實現前端基於Table數據一鍵導出excel文件
同樣的, 我們實現將table數據一鍵導出為excel也是類似, 不過方案有所不同, 我們先來看看在Dooring中的實現效果.
2.1 一鍵導出為excel實現效果
以上就是用戶基於後臺採集到的數據, 一鍵導出excel文件的流程, 最後一張圖是生成的excel文件在office軟體中的呈現.
2.2 使用javascript實現一鍵導出excel文件功能
一鍵導出功能主要用在H5-Dooring的後臺管理頁面中, 為用戶提供方便的導出數據能力. 我們這裡導出功能也依然能使用xlsx來實現, 但是綜合對比了一下筆者發現有更簡單的方案, 接下來筆者會詳細介紹, 首先我們還是來看一下流程:
很明顯我們的導出流程比導入流程簡單很多, 我們只需要將table的數據格式反編譯成插件支持的數據即可. 這裡筆者使用了js-export-excel來做文件導出, 使用它非常靈活,我們可以自定義:
自定義導出的excel文件名自定義excel的過濾欄位自定義excel文件中每列的表頭名稱由於js-export-excel支持的數據結構是數組對象, 所以我們需要花點功夫把table的數據轉換成數組對象, 其中需要注意的是ant的table數據結構中鍵對應的值可以是數組, 但是js-export-excel鍵對應的值是字符串, 所以我們要把數組轉換成字符串,如[a,b,c]變成'a,b,c', 所以我們需要對數據格式進行轉換, 具體實現如下:
const generateExcel = () => {let option = {}; //option代表的就是excel文件 let dataTable = []; //excel文件中的數據內容 let len = list.length; if (len) { for(let i=0; i<len; i++) { let row = list[i]; let obj:any = {}; for(let key in row) { if(typeof row[key] === 'object') { let arr = row[key]; obj[key] = arr.map((item:any) => (typeof item === 'object' ? item.label : item)).join(',') }else { obj[key] = row[key] } } dataTable.push(obj); //設置excel中每列所獲取的數據源 } } let tableKeys = Object.keys(dataTable[0]); option.fileName = tableName; //excel文件名稱 option.datas = [ { sheetData: dataTable, //excel文件中的數據源 sheetName: tableName, //excel文件中sheet頁名稱 sheetFilter: tableKeys, //excel文件中需顯示的列數據 sheetHeader: tableKeys, //excel文件中每列的表頭名稱 } ] let toExcel = new ExportJsonExcel(option); //生成excel文件 toExcel.saveExcel(); //下載excel文件 }
複製代碼
注意, 以上筆者實現的方案對任何table組件都使用, 可直接使用以上代碼在大多數場景下使用. 至此, 我們就實現了使用JavaScript實現前端導入和導出excel文件的功能.