🙏向这次肺炎疫情中逝世的同胞表示哀悼。css
本文首发于政采云前端团队博客: 可视化搭建数据大屏系统的前端实现
随着公司业务的发展,常常会收到一些数据大屏的需求。目前我司有两种实现方案,一是人肉搭建,二是用阿里云 DataV 搭建。html
人肉搭建,在本地脚手架开发环境中进行编码,有大量的重复劳动,能力复用性差,占用前端宝贵的开发时间。前端
DataV 功能强大,但须要付费使用,且好用的组件还要额外收费,不支持本地化部署,还须要维护两套数仓。vue
综上,若是此类大屏的需求较多,业务的重要性明显,就须要考虑是否是须要本身开发一套搭建大屏的系统,用以下降开发复杂度,提高研发效率,下降成本。本文尝试基于政采云前端团队的数据大屏搭建系统 Big 的拆解说明,为你们提供一种此类系统的设计和实施方案。数据库
Big 是基于政采云前端搭建系统 鲁班,和数据大屏组件库,进行快速搭建数据大屏的可视化系统。npm
为何叫 Big 呢? 打开百度翻译,输入 大屏
,英文翻译是 Big screen
,四舍五入叫 Big
。json
数据大屏是用可视化的方式展现庞杂数据的产品,常常会用在会议展览、业务监控、风险预警、地理信息分析等多种业务场景。下图是阿里云 DataV 的一个模板:api
从前端实现来看,大屏是由线图、柱状图、饼图、标题、背景、边框等基本元素组成。实现思路是以这些基本元素为组件,经过选择组件、拖拽方式布局,配置样式、数据来源,将这些数据保存在数据库中。展现页面获取依赖的组件、样式和数据信息,呈现给用户。数组
大屏按场景划分,可分为编辑和查看。服务器
编辑:指的是大屏制做者制做大屏。
查看:包含两种状况,大屏制做者预览和实际用户查看大屏。
编辑大屏是数据可视化系统核心,页面布局参考 DataV:
拆解为 4 个部分:顶部、组件区、画布、数据配置区。先讲下设计思路,再依次分解各区。
顶部区域包含三部分:左侧开关区、控制图层、组件列表、数据配置区的显示隐藏;中间是大屏的标题;右侧是保存和预览。
组件区分为左侧图层(已添加的组件)和右侧组件列表,具有添加组件、选择操做图层、分组对齐的功能。
图层
组件列表
画布用于实时展现大屏组件的位置、尺寸、属性和数据修改后的效果。
位置和尺寸改变经过注册组件 vue-draggable-resizable
的 drag
和 resize
方法,改变对应组件的属性。组件采用绝对定位,拖动时修改 top 和 left 的值。
属性改变经过修改对应组件的 props.models 的值修改。
数据分为静态数据和接口数据。启用静态数据时,数据从用户填写的数据获取。不然组件 watch 接口 id ,每次改变时从新发送请求获取数据。
画布上边和左边是标尺,画布缩放时标尺要跟随变更。在标尺上移动时显示一条移动的参考线。点击时增长一条参考线。双击参考线删除。标尺用 Canvas 画出,旋转 90 度可得到 Y 轴。
右下是缩放滑块,方便用户缩放查看。进入页面默认缩放到可查看全屏大小。缩放实现使用 CSS3 的 transform: scale(${this.scale})
。
画布上未选择组件时,显示页面的基本配置,包括大屏的宽高、背景图。
选择组件后,高亮显示当前组件,标识位置,右侧数据配置区显示组件 Schema 定义的配置项。
核心代码
<div :class="['data-com', item.info.previewId === activePreviewId ? 'data-com-active' : '']" v-for="item in preCompList" :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 用于选择组件、缩放组件大小,可参考官方文档。这个组件不支持分组和多选对齐场景,须要定制开发。
navigator-line
显示组件当前的标尺位置。这里要注意避免由于画布缩小致使坐标看不清,除以缩放比例便可。
使用 Vue 动态组件 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 是用来定义组件的可编辑项和默认配置。决定组件哪些东西能够配置,配置的形式是什么样子的(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 处于初级阶段,还有好多地方须要完善:
DT 时代,数据可视化将会愈来愈重要。相信有愈来愈多的同窗会遇到大屏的场景。经过可视化搭建大屏系统,能够赋能相关的业务方,让非专业人士作出专业的大屏效果,同时知足公司的一些定制化需求。这里作了一个比较浅的大屏构建方案,目前还在开发阶段,但愿抛砖引玉,有更多的可视化数据搭建方案分享出来,谢谢阅读。
政采云前端团队(ZooTeam),一个年轻富有激情和创造力的前端团队,隶属于政采云产品研发部,Base 在风景如画的杭州。团队现有 50 余个前端小伙伴,平均年龄 27 岁,近 3 成是全栈工程师,妥妥的青年风暴团。成员构成既有来自于阿里、网易的“老”兵,也有浙大、中科大、杭电等校的应届新人。团队在平常的业务对接以外,还在物料体系、工程平台、搭建平台、性能体验、云端应用、数据分析及可视化等方向进行技术探索和实战,推进并落地了一系列的内部技术产品,持续探索前端技术体系的新边界。
若是你想改变一直被事折腾,但愿开始能折腾事;若是你想改变一直被告诫须要多些想法,却无从破局;若是你想改变你有能力去作成那个结果,却不须要你;若是你想改变你想作成的事须要一个团队去支撑,但没你带人的位置;若是你想改变既定的节奏,将会是“5 年工做时间 3 年工做经验”;若是你想改变原本悟性不错,但老是有那一层窗户纸的模糊… 若是你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的本身。若是你但愿参与到随着业务腾飞的过程,亲手推进一个有着深刻的业务理解、完善的技术体系、技术创造价值、影响力外溢的前端团队的成长历程,我以为咱们该聊聊。任什么时候间,等着你写点什么,发给 ZooTeam@cai-inc.com