来试试这个用 Vue 撸的数据可视化后台吧
国际惯例:项目Github地址,欢迎 Star
首先放个线上地址大家感受一下(由于后端用的是 leancloud 的免费套餐,因此可能会比较慢):
P.S. 建议大家尽量自己注册一个账号(可以随便填一个密码),如果用默认的测试账号,不要乱改东西,否则别人进来就没得看了,因为你做的任何改动都会保存到后端数据库里。
缘起
也不知道为什么,公司里对数据分析、数据可视化的需求越来越多。这部分需求有的来自数据分析师(如果公司有数据分析师的话),有的来自产品、运营、开发,还有的来自公司的中层、高层领导,有的只是要把某一个表的数据导出,也有的希望能从多个维度对数据进行分析、可视化,还有的希望把多个可视化图表整合在一个页面中形成可视化的报表,有的是一次性的需求,有的是周期性的常态化需求,这些需求常常杂乱无章,而且数据的维度很多,交互比较复杂,可视化图表的各种参数众多,很难做到通用,而且很多数据较为敏感又需要严格的权限控制,时间一长,权限的管理又成了一个头疼的事情,种种因素导致各种数据需求耗费了前端开发的大量精力,发量日渐稀薄。
相信很多公司都会有类似的数据需求,零散却必须,虽然市面上也有不少产品提供了类似的数据管理功能,但是一来由于数据的敏感性,许多公司不愿意将自己的数据放到别的平台上,二来这些数据管理的产品大多收费昂贵且功能难以定制。所以很多公司里这种数据需求就会落到前端的头上,这些需求虽然零散甚至临时,但是实现起来却并不简单,可视化的视觉效果需要各种参数去配置,而大量的数据需要验证、清洗、转换又使得接口调试和对接变得十分麻烦,增加了前端的工作量,也让前端的 coding 变得十分恼人。
这时候做一个强大灵活易用的数据分析后台就十分有用了,通过傻瓜式地拖拽生成图表的方式,可以让没有数据基础的同事也能很快上手;通过将公司的数据源整合到一起,可以方便地对数据进行管理;通过构建看板整合多个图表,可以快速生成报表,便于查看、分享;通过用户自定义图表参数,可以有效地解决前端调参的痛苦。(一个十分工整的排比句,oh yeah!)
我在我司搭建数据分析后台也将近一年了,实际上我从第一份实习开始就在接触BI、数据相关的事情,在数据分析后台的搭建过程中也踩了许多的坑,期间经历了同事的各种吐槽,尝试了各种不同的解决方案,如今虽然代码能力不见多少提升,但是对于业务需求的杂乱无章、变幻莫测可谓多有体会。
在经历了半年多痛苦的折磨之后,我根据业务需求,参考其他数据分析产品,搭建了全新的数据分析平台,这一平台如今在我司运转良好,一方面已经满足了一部分数据需求,另一方面也有很好的可扩展性,可以应对更复杂的业务场景。我将前端部分抽离出来,形成了这个开源项目:
首先介绍下这个项目的基本情况,这个项目主要是用 Vue+ElementUI 搭建的,可视化部分是用的echarts,拖拽和布局部分是 vue-grid-layout 和 vuedraggable这两个库。这个项目一些技术、思路以及一部分辅助代码是参考了潘神的这个后台项目:
项目的后端部分除了少数数据是用了 mock 数据之外,都是用了 [leancloud](http://leancloud.cn) 进行搭建, Demo 数据的数据库部分则是用了国外的一个免费数据库 Free MySQL Hosting 速度比较慢,也没有 root 权限,但是测试足够用了,其他的用户数据、图表、看板等则是用了 leancloud 的对象存储。
主要功能点和实现原理
首先介绍项目的主要功能点及实现原理:
1. 通过拖拽字段查询数据
这个功能主要是通过构建 SQL 语句来查询数据库,在后端的查询是用 Presto 来做的,Presto 可以统一查询多种数据库,除了传统的关系型数据库还可以查询 HDFS,查询能力比较强大。前端部分数据查询主要是通过对 SQL 语句的解构,划分成维度、数据、筛选、排序、数据条数等交互元素,便于没有 SQL 基础的用户使用。用户在前端的交互按照特定语法规则动态生成 SQL 发送到后端进行数据库的查询。当然目前 SQL 的构建功能还不完整,有一些 SQL 语法还有待支持,已经列入了开发计划中。
2. 对数据进行可视化渲染
对数据进行可视化渲染主要是通过数据查询的维度和数值来判断可用的图表类型,然后利用 vue.js 的动态组件来渲染对应的图表组件:
// Vue.js 动态组件渲染对应的图表类型 <component :is="currentType.componentName" :data="chartData" :schema="schema" :chart-style="chartStyle" class="visualize-window" />
这里有一个问题是图表与数据的映射关系的问题,不同的数据适合用不同的图表来展示,例如饼图的数据跟堆叠柱状图的数据就不一样,因此需要对各个图表所需要的数据结构进行定义:
// 这是饼图的匹配规则定义 matchRule: { desc: '1个维度1个数值;0个维度多个数值', isUsable(dimensions, calculs) { return (dimensions.length === 1 && calculs.length === 1) || (dimensions.length === 0 && calculs.length >= 1) } },
然后根据匹配规则来判断图表类型是否可用。
3. 图表的保存和回显
前端生成图表后就可以保存到后端了,由于定义一个图表所需要的字段太多,而且这些字段还可能会经常增减改动,因此不太可能让后端在数据库一一定义这些字段,因此我们采用的方案是由前端来维护这些字段,后端统一将所有的内容以字符串或者 json 对象的方式存在数据库的一个字段(如 content)中。
这样一来图表的回显问题也就没有任何问题了,按照生成图表的逻辑把 content 字段的内容解析出来就好了。
4. 图表整合到看板
很多时候业务都需要同时查看多个图表,这个时候就需要一个看板来整合多个图表。看板其实是指 Dashboard,中文其实没有很贴切的翻译可以对应,看板勉强达意。
看板整合多个图表在后端而言只是一个关联关系的管理,对前端而言则需要根据看板关联的图表来对页面进行布局,然后根据保存的图表来查询数据、可视化渲染。页面布局主要是利用了 vue-grid-layout 进行 grid 布局,同时也支持拖拽和大小调整等。可视化的逻辑跟创建图表时的可视化是一样的,不再赘述。
这里遇到的问题是对于已有布局的看板,添加新图表时,新图表的定位要如何计算。这其实跟图片瀑布流的问题有些相似,但是由于各个 item 不定宽高,其实相对更难一些,我已经找到了思路,做了一些计算,但是目前还不完善,完善后我会再写文章来介绍。
5. 数据的权限问题
公司的数据其实相当敏感,对于上市公司而言,数据会影响股价走势,对非上市公司来说,会影响投融资的进度,都是关系到公司财务甚至生死存亡的大事,因此数据的权限管理是十分重要的。在这个项目里,这一部分的解决方案其实并不简单,但是复杂度主要在于后端而不是前端。简单来说,我们的权限是做到了数据表这一层级,由管理员向系统中添加数据源,添加的同时定义好该数据源的权限范围,如产品、运营、开发等权限角色。用户进入系统后,由管理员给用户分配权限角色,用户只能查询其自身对应角色所能查看的数据。由于权限和数据源管理的部分暂时还没有添加到这个开源项目中来,因此也就不细说了,先挖个坑,以后有机会再填。
6. 其他技术点:
首先项目的构建是用了 [email protected],除了默认的配置之外,还通过 vue.config.js 做了一些自定义的配置。
项目中用到了很多 icon,虽然Element UI 的 icon 在2.8.x版本之后增加了不少, 但是还是不能满足我们的需求(一些常用的 icon 仍然没有,比如保存),因此需要自己来解决 icon 的问题,这里要感谢 iconfont 上的设计师如山提供的这套漂亮的数据可视化图标库。项目的后端接口文档我是用的 postman 进行的管理,其实 postman 有很多强大的功能,不仅仅是一个接口测试工具,接口文档管理就是其中一个。我在开发时一般是在本地同时起前端和后端两个项目,本地开发时通过环境变量判断接入本地的后端接口地址:
const fetchInstance = axios.create({ baseURL: process.env.VUE_APP_BASE_API *}*)
本地开发完成后,前端打包发布到 gh-page 分支( git subtree push --prefix docs origin gh-pages ),后端通过 leancloud 提供的 cli 工具一键部署,还是挺方便的。前端的打包发布其实可以配置 Travis-ci 来实现自动部署,我暂时还没有配(已弃疗的拖延症患者在此)。
项目的登录授权、图表和看板的增删改查等都是使用了 leancloud 提供的解决方案,其实利用 leancloud 的 js-sdk ,前端也可以很方便地实现对象存储的增删改查,不需要写后端接口。但是为了保持项目代码的纯洁,避免代码中引入奇奇怪怪的 AV.query 这种东西,我还是自己做了后端的部分,当然这部分也做的比较简单,毕竟只是一个 Demo,主要就是基于 koa.js 做的的一些增删改查,基本上是面向文档编程。
项目里的状态管理用到了 vuex, 但是其实目前为止并没有很多全局状态管理的需求,只有用户 token 保存在了 store 里。另外关于状态管理,我在这个项目的创建图表的部分还尝试用了Vue 的简单状态管理模式( 代码在此 ),这个模式用起来是没问题的,对于大型项目里的复杂组件来说是很好用的,很方便地解决了复杂组件内部的状态共享问题。但是目前对于我这个项目而言并不是特别适用,因为这部分的状态管理放在全局状态也挺合适,当然这里是见仁见智了,我觉得就目前这样也挺好。