最近笔者终于把H5-Dooring的后台管理系统初步搭建完成, 有了初步的数据采集和数据分析能力, 接下来我们就复盘一下其中涉及的几个知识点,并一一阐述其在Dooring H5可视化编辑器中的解决方案. 笔者将分成3篇文章来复盘, 主要解决场景如下: 如何使用JavaScript实现前端导入和导出excel文件(H5编辑器实战复盘) 前端如何基于table中的数据一键生成多维度数据可视化分析报表 * 如何实现会员管理系统下的权限路由和权限菜单
以上场景也是前端工程师在开发后台管理系统中经常遇到的或者即将遇到的问题, 本文是上述介绍中的第一篇文章, 你将收获: 使用JavaScript实现前端导入excel文件并自动生成可编辑的Table组件 使用JavaScript实现前端基于Table数据一键导出excel文件 * XLSX和js-export-excel基本使用
本文接下来的内容素材都是基于H5可视化编辑器(H5-Dooring)项目的截图, 如果想实际体验, 可以访问H5-Dooring网站实际体验. 接下来我们直接开始我们的方案实现.
在开始实现之前, 我们先来看看实现效果.
导入excel文件并通过antd的table组件渲染table:
编辑table组件:
保存table数据后实时渲染可视化图表:
以上就是我们实现导入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可用的数据结构, 至此我们就实现了表格导入的功能.
table表格的编辑功能实现其实也很简单, 我们只需要按照antd的table组件提供的自定义行和单元格的实现方式即可. antd官网上也有实现可编辑表格的实现方案, 如下:
大家感兴趣的可以研究一下. 当然自己实现可编辑的表格也很简单, 而且有很多方式, 比如用column的render函数来动态切换表格的编辑状态, 或者使用弹窗编辑等都是可以的.
根据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的也类似, 基本原理都一致.
同样的, 我们实现将table数据一键导出为excel也是类似, 不过方案有所不同, 我们先来看看在Dooring中的实现效果.
以上就是用户基于后台采集到的数据, 一键导出excel文件的流程, 最后一张图是生成的excel文件在office软件中的呈现.
一键导出功能主要用在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文件的功能.
所以, 今天你又博学了吗?
以上教程笔者已经集成到H5-Dooring中,对于一些更复杂的交互功能,通过合理的设计也是可以实现的,大家可以自行探索研究。
地址:H5-Dooring | 一款强大的H5编辑器
如果想学习更多H5游戏, webpack,node,gulp,css3,javascript,nodeJS,canvas数据可视化等前端知识和实战,欢迎在《趣谈前端》一起学习讨论,共同探索前端的边界。
有天老板找我到办公室跟我说要做一个商城,商城卖出去东西就有佣金可以拿。我听着就头大。老板打开电脑给我看了网站:你看一下这个网站,照着它的流程就可以拥有一个商城了。我靠过去一看,大概了解一下:原来是利用第三方工具就可以构建一个导购网站,只要消费者在网站领取优惠券就会自动跳转到某bao的购买页面,购买成功后就可以有佣金了。我看了一下觉得可以,只要不让我敲代码一切好说。于是我照着流程构建了一个网站,然后勾选了很多零食进行推广,然后我就发现了一个问题:我只勾选了一些零食啊,商城怎么还有其它类型的商品?我思索了一下就明白了,这网站还是挺流氓的,还掺杂着其他人的推广链接,我一想这样不行,转化率肯定低啊。果不其然,试用了一天就只有5个单子,因为公司的网站还是挺有流量的,所以这转化率不可能这么低。老板看了一下,觉得没什么用让我把商城入口给关了,我只好照做,但是我心里对这流氓网站不服啊,于是我打算自己做一个导购网站。(最后还是要敲代码(。・_・)/~~~)
1. 安装xlrd
cmd窗口: pip install xlrd
2.创建index.py,导入模块
import xlrd
3.打开Excel文件读取数据
wb= xlrd.open_workbook('文件路径')
4.获取表格
sheet1 = wb.sheet_by_index(0) #这里的excel文档内只有一个表格,0代表第一个
5.获取表格的行数
rows = sheet1.nrows
6.获取表格中的类目
商品一级类目
住宅家具
影音电器
影音电器
美容护肤
厨房电器
运动服/休闲服装
餐饮具
category0 = sheet1.col_values(4) #获取列内容(类目),这里excel文档的第四列是类目 del category0[0] #删除列表中的 "商品一级类目" category = sorted(set(category0),key=category0.index) #类目列表->去除重复
7.整理数据
[ [ 分类名, [商品信息] ], [ 分类名, [商品信息] ] ] data = [] for i,v in enumerate(category): data.append([v,[]]) for i,v in enumerate(data): for x in range(rows): if v[0] == sheet1.cell(x,4).value: data[i][1].append(sheet1.row_values(x))
8.导出json文件
jsonData = json.dumps(data, ensure_ascii=False) with open('results.json', 'w',encoding="utf-8") as f: f.write(jsonData)
9.运行index.py,获得json文件
cmd窗口:python index.py
10.创建html页面,并引用json文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script> <script>//引用jquery $(function(){ function color16(){//十六进制颜色随机 var r = Math.floor(Math.random()*256); var g = Math.floor(Math.random()*256); var b = Math.floor(Math.random()*256); var color = '#'+r.toString(16)+g.toString(16)+b.toString(16); return color; } var navo = '';//类目导航 var info = '';//商品内容 $.get('./results.json', function(data) {//请求json文件 $.each(data, function(index, val) { navo+="<a href='#"+val[0]+"''>"+val[0]+"</a> " });//把json文件中的类目数组遍历出来,并用锚定位 $.each(data, function(index, val) { var div_title = "<div id='"+val[0]+"' style='float:left;'>"; var div_content = ""; $.each(val[1], function(index, val) { div_content+="<div style='background:"+color16()+"' onclick=\"location.href=\'"+val[21]+"\'\" class='pro_img'>"+val[1]+"<span class='money'>¥"+val[6]+"</span><\/div>" }); var div_footer ="</div><br>"; info+=div_title+div_content+div_footer }); $('#nav').html(navo);//把导航显示出来 $('#content').html(info);//把商品显示出来 },'json'); }) </script> <style> #content{ margin-top: 10px } .money{ position: absolute; left: 0; bottom: 0; height: 30px; line-height: 30px; color: #e22a40; font-weight: 700 } .pro_img{ position: relative; float: left; width: 220px; height: 220px; line-height: 220px; text-align: center; border: 1px solid #eee; cursor: pointer; font-size: 30px; white-space:normal; overflow:hidden; /*超过部分不显示*/ text-overflow:ellipsis; /*超过部分用点点表示*/ white-space:nowrap;*//*不换行 } </style> </head> <body> <div id="nav"></div> <div id="content"></div> </body> </html>
https://fjxasdf.github.io/daogou (github比较卡)
目开发中遇到导入表格常见的就是excel和csv 格式,一般情况下我们会前端首先得到这个表格里面的数据,然后再把数据发送给后端,也有的是直接上传文件传给后台后台自己处理,这样就不好控制上传前预览和处理数据,如果不需要预览直接上传的需求,那还是直接上传文件交给后台处理,本文实例是基于vue+elementu-ui 表格去实现。
https://www.papaparse.com/
1.安装
*请认真填写需求信息,我们会在24小时内与您取得联系。