写点什么

可视化搭建数据大屏系统的前端实现

2021 年 3 月 17 日

可视化搭建数据大屏系统的前端实现

背景


随着公司业务的发展,经常会收到一些数据大屏的需求。目前我司有两种实现方案,一是人肉搭建,二是用阿里云 DataV 搭建。


人肉搭建,在本地脚手架开发环境中进行编码,有大量的重复劳动,能力复用性差,占用前端宝贵的开发时间。


DataV 功能强大,但需要付费使用,且好用的组件还要额外收费,不支持本地化部署,还需要维护两套数仓。


综上,如果此类大屏的需求较多,业务的重要性明显,就需要考虑是不是需要自己开发一套搭建大屏的系统,用以降低开发复杂度,提升研发效率,降低成本。本文尝试基于政采云前端团队的数据大屏搭建系统 Big 的拆解说明,为大家提供一种此类系统的设计和实施方案。


Big 是什么


Big 是基于政采云前端搭建系统 鲁班,和数据大屏组件库,进行快速搭建数据大屏的可视化系统。

为什么叫 Big 呢? 打开百度翻译,输入大屏,英文翻译是 Big screen,四舍五入叫 Big


自己做一套系统的优势


  • 可定制性:内部产品,组件和展示形式私人订制

  • 支持本地化部署:业务需要决定部分业务只能在内网访问,无法访问外网(包括阿里云)

  • 解决 DataV 需要维护两套数仓的问题

  • 节约公司成本,增强公司数据产品能力,助力营收


总览


数据大屏是用可视化的方式展示庞杂数据的产品,经常会用在会议展览、业务监控、风险预警、地理信息分析等多种业务场景。下图是阿里云 DataV 的一个模板:



从前端实现来看,大屏是由线图、柱状图、饼图、标题、背景、边框等基本元素组成。实现思路是以这些基本元素为组件,通过选择组件、拖拽方式布局,配置样式、数据来源,将这些数据保存在数据库中。展示页面获取依赖的组件、样式和数据信息,呈现给用户。


大屏按场景划分,可分为编辑和查看。

编辑:指的是大屏制作者制作大屏。

查看:包含两种情况,大屏制作者预览和实际用户查看大屏。


编辑


编辑大屏是数据可视化系统核心,页面布局参考 DataV:

拆解为 4 个部分:顶部、组件区、画布、数据配置区。先讲下设计思路,再依次分解各区。


设计思路


  • 页面数据和依赖的组件由 SSR (https://juejin.im/post/5b063962f265da0ddb63dac3) 注入到 HTML 文件中

  • App 数据保存在 App state 中,未使用 Vuex(后续会考虑使用 Vuex)

  • 数据用 props 传递给子组件

  • 数据从子组件采用事件中心传递给祖父级组件

顶部


顶部区域包含三部分:左侧开关区、控制图层、组件列表、数据配置区的显示隐藏;中间是大屏的标题;右侧是保存和预览。

组件区


组件区分为左侧图层(已添加的组件)和右侧组件列表,具备添加组件、选择操作图层、分组对齐的功能。

图层

  • 图层支持上移、下移、置顶、删除的操作,支持右键显示操作菜单(暂不支持多选和分组)。实现原理是使用数组的基本方法改变数组

  • 单击组件选择该组件,画布区选中组件,数据配置区显示配置项

组件列表

  • 所有组件展示所有大屏组件,点击或拖动添加组件

  • 添加组件采用异步获取组件的 JS、CSS 、配置 Schema,将 CSS、JS 插入 DOM 中,配置传入属性配置区

  • 支持按组件类型分组,便于用户使用。


画布


画布用于实时展示大屏组件的位置、尺寸、属性和数据修改后的效果。


位置和尺寸改变通过注册组件 vue-draggable-resizable 的 drag 和 resize 方法,改变对应组件的属性。组件采用绝对定位,拖动时修改 top 和 left 的值。


属性改变通过修改对应组件的 props.models 的值修改。


数据分为静态数据和接口数据。启用静态数据时,数据从用户填写的数据获取。否则组件 watch 接口 id ,每次改变时重新发送请求获取数据。


画布上边和左边是标尺,画布缩放时标尺要跟随变动。在标尺上移动时显示一条移动的参考线。点击时增加一条参考线。双击参考线删除。标尺用 Canvas 画出,旋转 90 度可获得 Y 轴。


右下是缩放滑块,方便用户缩放查看。进入页面默认缩放到可查看全屏大小。缩放实现使用 CSS3 的transform: scale(${this.scale})


画布上未选择组件时,显示页面的基本配置,包括大屏的宽高、背景图。


选择组件后,高亮显示当前组件,标识位置,右侧数据配置区显示组件 Schema 定义的配置项。


核心代码


<div  v-for="item in preCompList"  :class="[    'data-com',    item.info.previewId === activePreviewId ? 'data-com-active' : ''  ]"  :key="item.info.activePreviewId">  <vue-draggable-resizable    :w="item.models.width || 100"    :h="item.models.height || 100"    :x="item.models.x || 0"    :y="item.models.y || 0"    :active="item.info.previewId === activePreviewId"    @dragging="onDrag"    @resizing="onResize"    @activated="      () => {        onCompActivated(item.info.previewId);      }"      :prevent-deactivation="true">    <navigator-line      :x="item.models.x"      :y="item.models.y"      :scale="scale"    />    <div       :is="item.info.name"       :models="item.models"       :extraProps="extraProps">    </div>    </vue-draggable-resizable></div>
复制代码

vue-draggable-resizable (https://npm.taobao.org/package/vue-draggable-resizable) 用于选择组件、缩放组件大小,可参考官方文档。这个组件不支持分组和多选对齐场景,需要定制开发。


navigator-line 显示组件当前的标尺位置。这里要注意避免因为画布缩小导致坐标看不清,除以缩放比例即可。

使用 Vue 动态组件 is (https://cn.vuejs.org/v2/api/#is) 控制组件显示。


数据配置区


数据配置区有 2 种情况:


  • 未选中组件展示页面级配置:大屏宽高、背景色、背景图等

  • 选中组件:展示组件配置信息


实现逻辑:根据当前用户的选择来动态渲染出组件的属性编辑域,并回填属性的初始值,从而达到良好的编辑交互效果。用户拖拽组件时同步更新编辑域中的属性值,在属性编辑域修改属性时通知大屏触发组件的刷新动作,达到实时编辑的效果。


数据配置区界面由组件 Schema 定义,props 定义展示,models 表示默认数据,详细介绍见下面 Schema。


编辑类型由 fileds 里的 type 决定,实现 Input、Select、Image、Border 等各种类型组件,再利用 Vue 的动态组件 is 属性来展示。


数据回传:每个子组件值的修改会通知父组件 <Setting /> 更新回传给父组件 App,这里采用全量回传,避免 App 对 models 查找更新数据。


查看


查看是将数据库里保存的数据,配合组件渲染出来。实现原理是通过页面 id 获取组件、数据渲染。代码如下:


<div class="preview">  <div class="layout">    <div      :class="[        'preview-line',        preComp.info.name + '-' + preComp.info.previewId      ]"      v-for="(preComp, index) in preCompList"      :key="preComp.info.previewId"      :style="formatCompStyle(preComp, index)"    >      <div        :is="preComp.info.name"        :models="preComp.models"        :isPreview="isPreview"        :extraProps="extraProps"></div>    </div>  </div></div>
复制代码

全屏展示


需要注意大屏是全屏展示,根据大屏配置的屏幕宽高、背景图、背景色设置 body 样式,设置 <meta name="viewport" content="width=' + window.screen.width + '"/> viewport 的 width 让屏幕占满全屏,再监听屏幕的变化设置压缩比例。自适应关键代码如下:


// 获取设置的大屏宽高、背景图、背景色if (window.__INITIAL_STATE__) {  const { width, height, backgroundImage, backgroundColor } =        __INITIAL_STATE__.preview.pageConfig.models;  window.scr = {    width: width,    height: height,    backgroundImage: `url(${backgroundImage})`,    backgroundColor: backgroundColor,  };} else {  window.scr = {    width: window.screen.width,    height: window.screen.height,  };}
// 全屏展示function resizeFull() { if (!window.scr.height || !window.scr.width) return resizeFullBak(); var ratioX = $(window).width() / window.scr.width; var ratioY = $(window).height() / window.scr.height; $('body').css({ transform: "scale(" + ratioX + ", " + ratioY + ")", transformOrigin: "left top", backgroundSize: "100% 100%", });}function resizeFullBak() { var ratioX = $(window).width() / $('body').width(); var ratioY = $(window).height() / $('body').height(); $('body').css({ transform: "scale(" + ratioX + ", " + ratioY + ")", transformOrigin: "left top", backgroundSize: "100% " + ratioY * 100 + "%", });}
复制代码

组件设计


组件是整个大屏设计的基础。组件由组件模板来初始化,模板提供了两个主要功能,一是实现一个可开发的简单 Demo,二是提供打包发布功能。


模板代码很简单,通过传入的 props 控制组件的展示和业务逻辑。组件自动安装,这样在异步加载组件的时候页面可以识别组件。重点讲下组件的 Schema 设计。


schema.json


schema.json 是用来定义组件的可编辑项和默认配置。决定组件哪些东西可以配置,配置的形式是什么样子的(Input、Select 等有默认值)。所以 Schema 包含 props 和 models 两个属性。


props: 数组,每个元素是 tab 的一项。info 是 tab 头部信息,fields 是配置项。fields 的 name 对应 models 的属性名,type 决定了配置的类型,title 是中文名。还可以定义其他属性,比如下拉框选择项、数字输入框最大最小值等。


models: 默认数据,props.fileds 里每个 name 的默认值。

下面是一个简单 Schema 的定义:


{  "props": [    {      "info": {        "title": "配置",        "icon": "icon-setting"      },      "fields": [        {          "title": "组件宽度",          "name": "width",          "description": "组件宽度",          "type": "number"        },        {          "title": "组件高度",          "name": "height",          "description": "组件高度",          "type": "number"        },        {          "title": "x轴坐标",          "name": "x",          "description": "组件x轴坐标",          "type": "number"        },        {          "title": "y轴坐标",          "name": "y",          "description": "组件y轴坐标",          "type": "number"        }      ]    }  ],  "models": {    "width": 300,    "height": 200,    "x": 0,    "y": 0  }}
复制代码


碰到的问题

通信

大屏组件之间如何通信?要确保大屏组件可以通信。

采用事件中心来处理组件间的通信。核心代码如下:

// 全局事件中心Vue.prototype.$eventBus = new Vue();// 触发, 在组件内部this.$eventBus.$emit('eventName', '这里传值');// 监听, 获取值this.$eventBus.on('eventName', v => {  console.log(v);})
// 组件通知父组件区划变动或其他变动this.$eventBus.$emit('component__update-extraProps', { dist: '选择的区划' });
复制代码

App 统一管理通信对象 extraProps,以 props 形式注入到每个组件。组件可以监听 extraProps 的属性变化。

// 组件代码{  ...,  props: {    extraProps: {      type: Object,      default: () => {}    }  },  computed: {    dist() {      return (this.extraProps && this.extraProps.dist) || '';    }  },  watch: {    dist(val, oldVal){      // 添加区划改变时获取新数据的逻辑    }  }}
复制代码


权限


大屏数据需要做权限控制,有权限的人才能查看大屏,而鲁班原来页面访问逻辑是没有权限的。实现方案是编辑、预览页面调用的免登接口访问中间 Server,中间 Server 实现登录,去 Server 请求数据。用户的查看页面内嵌鲁班 iframe,该地址由实际服务器提供并带上权限 token。访问该鲁班地址时先去 Server 鉴权,有权限返回大屏页面,否则返回 401。



待优化


Big 处于初级阶段,还有好多地方需要完善:


  • 分组:像 PS、Sketch 里一样分组,方便归类和操作

  • 多选:多选后选择对齐方式。也是方便用户操作

  • 代码优化

  • 体验优化


总结


DT 时代,数据可视化将会越来越重要。相信有越来越多的同学会遇到大屏的场景。通过可视化搭建大屏系统,可以赋能相关的业务方,让非专业人士做出专业的大屏效果,同时满足公司的一些定制化需求。这里做了一个比较浅的大屏构建方案,目前还在开发阶段,希望抛砖引玉,有更多的可视化数据搭建方案分享出来,谢谢阅读。



头图:Unsplash

作者:明明

原文:https://mp.weixin.qq.com/s/j2dx5S2TWXpznfh3zPthYA

原文:可视化搭建数据大屏系统的前端实现

来源:政采云前端团队 - 微信公众号 [ID:Zoo-Team]

转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2021 年 3 月 17 日 00:442217

评论

发布
暂无评论
发现更多内容

架构师训练营第1期第四周作业二

道长

极客大学架构师训练营

从联想ThinkStation工作站,窥见工具文明的新纪元

脑极体

架构师训练营第三周作业

Wee权

开源=免费?

Learun

PyFlink + 区块链?揭秘行业领头企业 BTC.com 如何实现实时计算

Apache Flink

flink

数字货币交易所源码开发,交易所APP搭建

135深圳3055源中瑞8032

关于国际化语言 Intl

西贝

Java 前端 国际化 格式化

从构建小系统到架构分布式大系统,Spring Boot2的精髓全在这里了

Java架构之路

Java 程序员 面试 Spring Boot 编程语言

程序员去外包真的不可取吗?

Java架构师迁哥

Spring Cloud 微服务实践(7) - 日志

xiaoboey

kafka 微服务 Spring Cloud 日志 spring cloud stream

架构师训练营第四章 系统架构总结

郎哲158

EffectiveJava读书笔记-01-对象创建与销毁

wander

读书笔记 编程开发

区块链支付系统开发公司,USDT承兑支付

135深圳3055源中瑞8032

开源数据库这么香,为什么我们还要下功夫自研?

华为云开发者社区

数据库 开源 数据

31道Java核心面试题,一次性打包送给你

小Q

Java 学习 程序员 架构 面试

架构1期第四周作业1-大型互联网系统技术梳理

道长

极客大学架构师训练营

JDK 中的栈竟然是这样实现的?

王磊

Java 数据结构和算法

spring-boot-route(十)多数据源切换

Java旅途

Java Spring Boot

震精,京东T8工程师每天熬夜到天明,竟只是为一套编程实战文档

周老师

Java 编程 程序员 架构 面试

做好分库分表其实很难之二

架构师修行之路

微服务 分库分表

区块链教育 丨 首批区块链专业新生正式入学

CECBC区块链专委会

区块链技术 区块链教育

架构师作业第三周学习总结

Wee权

用NOSql给高并发系统加速

架构师修行之路

nosql redis 分布式 微服务

区块链数字钱包技术开发,数字资产钱包

135深圳3055源中瑞8032

4年Java经验,备战两月成功拿到美团、京东、字节offer

Java成神之路

Java 面试 算法 编程语言 面试程序员

架构师训练营第四周作业

郎哲158

区块链是一个不知道要解决什么问题的解决方案吗?

CECBC区块链专委会

比特币 区块链 银行

建筑行业区块链应用场景是怎样的

CECBC区块链专委会

区块链 行业资讯

深入分析CRM系统对现代企业的作用

Learun

来不及解释了,快上车!力软快速开发平台,助力企业搭乘万物互联的顺风车

Learun

Nacos-技术专题-配置中心实现

李浩宇/Alex

4月17日 HarmonyOS 开发者日·上海站

4月17日 HarmonyOS 开发者日·上海站

可视化搭建数据大屏系统的前端实现-InfoQ