背景
随着公司业务的发展, 经常会收到一些数据大屏的需求. 目前我司有两个实现方案, 一是人肉搭建, 二是用阿里云 DataV 搭建.
人肉搭建, 大量重复劳动, 复用性差, 占用前端宝贵的开发时间.
DataV 功能强大, 缺点是收费, 好用的组件需要额外收费, 不支持本地化部署, 还需要维护两套数仓.
Big 是什么
Big 是基于 鲁班 https://juejin.im/post/5d8774bff265da03ae78b2a1 和大屏组件快速搭建数据大屏的可视化系统.
为什么叫 Big 呢? 打开百度翻译, 输入 大屏 , 英文翻译是 Big screen , 四舍五入叫 Big .
Big 的优势
可定制性: 内部产品, 组件和展示形式私人订制
支持本地化部署: 业务需要决定部分业务只能在内网访问, 无法访问外网(包括阿里云)
解决 DataV 需要维护两套数仓的问题
节约公司成本, 增强公司数据产品能力, 助力营收
鲁班 https://juejin.im/post/5d8774bff265da03ae78b2a1 优势: 稳定性, 高性能, 业务赋能
总览
数据大屏是用可视化的方式展示庞杂数据的产品, 经常会用在会议展览, 业务监控, 风险预警, 地理信息分析等多种业务场景. 下图是阿里云 DataV 的一个模板:
从前端实现来看, 大屏是由线图, 柱状图, 饼图, 标题, 背景, 边框等基本元素组成. 实现思路是以这些基本元素为组件, 通过选择组件, 拖拽方式布局, 配置样式, 数据来源, 将这些数据保存在数据库中. 展示页面获取依赖的组件, 样式和数据信息, 呈现给用户.
大屏按场景划分, 可分为编辑和查看.
- <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>
- <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>
- // 获取设置的大屏宽高, 背景图, 背景色
- if(Windows.__INITIAL_STATE__) {
- const { width, height, backgroundImage, backgroundColor } = __INITIAL_STATE__.preview.pageConfig.models;
- Windows.scr = {
- width: width,
- height: height,
- backgroundImage: `url(${backgroundImage})`,
- backgroundColor: backgroundColor,
- };
- } else {
- Windows.scr = {
- width: Windows.screen.width,
- height: Windows.screen.height,
- };
- }
- // 全屏展示
- function resizeFull() {
- if (!Windows.scr.height || !Windows.scr.width) return resizeFullBak();
- var ratioX = $(Windows).width() / Windows.scr.width;
- var ratioY = $(Windows).height() / Windows.scr.height;
- $('body').CSS({
- transform: "scale(" + ratioX + "," + ratioY + ")",
- transformOrigin: "left top",
- backgroundSize: "100% 100%",
- });
- }
- function resizeFullBak() {
- var ratioX = $(Windows).width() / $('body').width();
- var ratioY = $(Windows).height() / $('body').height();
- $('body').CSS({
- transform: "scale(" + ratioX + "," + ratioY + ")",
- transformOrigin: "left top",
- backgroundSize: "100%" + ratioY * 100 + "%",
- });
- }
- {
- "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: '选择的区划' });
- // 组件代码
- {
- ...,
- props: {
- extraProps: {
- type: Object,
- default: () => {}
- }
- },
- computed: {
- dist() {
- return (this.extraProps && this.extraProps.dist) || '';
- }
- },
- watch: {
- dist(val, oldVal){
- // 添加区划改变时获取新数据的逻辑
- }
- }
- }
来源: http://www.tuicool.com/articles/miQ7J3y