数据可视化系统 Big 的前端实现

栏目: IT技术 · 发布时间: 5年前

内容简介:随着公司业务的发展,经常会收到一些数据大屏的需求。目前我司有两个实现方案,一是人肉搭建,二是用阿里云 DataV 搭建。人肉搭建,大量重复劳动,复用性差,占用前端宝贵的开发时间。DataV 功能强大,缺点是收费,好用的组件需要额外收费,不支持本地化部署,还需要维护两套数仓。

背景

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

人肉搭建,大量重复劳动,复用性差,占用前端宝贵的开发时间。

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

Big 是什么

Big 是基于 鲁班 和大屏组件快速搭建数据大屏的可视化系统。

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

Big 的优势

  • 可定制性:内部产品,组件和展示形式私人订制
  • 支持本地化部署:业务需要决定部分业务只能在内网访问,无法访问外网(包括阿里云)
  • 解决 DataV 需要维护两套数仓的问题
  • 节约公司成本,增强公司数据产品能力,助力营收
  • 鲁班 优势:稳定性、高性能、业务赋能

总览

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

数据可视化系统 Big 的前端实现

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

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

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

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

编辑

数据可视化系统 Big 的前端实现

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

数据可视化系统 Big 的前端实现

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

设计思路

  • 页面数据和依赖的组件由 ssr 注入到 html 文件中
  • app 数据保存在 App state 中,未使用 Vuex(后续会考虑使用 Vuex)
  • 数据用 props 传递给子组件
  • 数据从子组件采用事件中心传递给祖父级组件

顶部

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

组件区

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

图层

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

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

组件列表

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

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

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

画布

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

位置和尺寸改变通过注册组件 vue-draggable-resizabledragresize 方法,改变对应组件的属性。组件采用绝对定位,拖动时修改 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>
  <div>
    <div
      :
      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 + '"/> vieport 的 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 的前端实现

待优化

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

  • 分组:像 PS、sketch 里一样分组,方便归类和操作
  • 多选:多选后选择对齐方式。也是方便用户操作
  • 代码优化
  • 体验优化

总结

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

❉ 作者介绍 ❉

数据可视化系统 Big 的前端实现

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Sovereign Individual

The Sovereign Individual

James Dale Davidson、William Rees-Mogg / Free Press / 1999-08-26 / USD 16.00

Two renowned investment advisors and authors of the bestseller The Great Reckoning bring to light both currents of disaster and the potential for prosperity and renewal in the face of radical changes ......一起来看看 《The Sovereign Individual》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具