跳到主要内容
版本:6.5.0

可视化插件实战教程

1. 概述

在搭建图表时,面对不同的业务场景,我们经常会有一些特殊图表的需求。这种情况下,我们可以通过观远 BI 内的自定义图表功能,进行这些特殊图表的创建,满足我们的需求。事实上,对于这些创建好的“特殊图表”,我们还可以通过一些操作配置,将其封装为“可视化插件”,再安装入 BI 中,使其得到无限复用。

本文则会对这些操作配置,进行详细介绍。

2. 创建流程

可视化插件的创建流程大体分为以下三步:

1.png

生成自定义图表:通过观远 BI 平台内的自定义图表功能进行个性化展示样式的创建,具体步骤可参考《自定义图表》

生成可视化插件:按照观远 BI 定义的格式内容以 JSON 文件的形式生成可视化插件,具体步骤可参考本文 2.2 生成可视化插件

封装图表配置项:设计前端界面配置项展示效果、将图表配置项进行封装,本文将重点介绍。

2.1 生成自定义图表

2.1.1 功能入口

仪表板-新建卡片-自定义图表

2.png

2.1.2 编辑区域

编辑区域共分为以下三个部分:

  • 图库引用及容器定义(HTML)主要用于定义图表渲染的容器以及图表渲染时需要引用的图表库。

  • 主体样式设置(CSS)静态样式设置。一般情况下不需要进行额外修改,容器的自适应可在此处进行设置。例如在引用 Amcharts 的图表时,其 CSS 中对容器“chartdiv”的height默认为500px。可以通过修改为95%来实现自定义图表随着卡片大小的调节而进行高度自适应。

  • 图表渲染逻辑(JAVASCRIPT)图表数据的接入以及如何渲染都在此处进行定义。

3.png

2.1.3 绘制示例

下面我们以 Amcharts 的柱状折断图举例说明如何生成自定义图表。

温馨提示:Amcharts,是一家图表组件开发与扩展工具服务商,与观远数据产品与服务无关,其提供 JavaScript/HTML5 Charts、Javascript/HTML5 Stock Chart、JavaScript Maps 三种图表组件,如您需要使用其产品或服务,请自行了解其产品或服务相关价格等事宜。

Step1. 图表拷贝

首先,我们从 Amcharts官网 的 Demos 中选择柱状折断图,进入后选择以 CodePen 模式打开;

4.png

在 CodePen 模式下,复制 HTML、CSS 与 JS 的内容到观远自定义图表的对应区域;

  • JS的内容不要直接对自定义图表JAVASCRIPT中的内容进行替换,需要粘贴在 function renderChart 下的“/* ------ Custom Code Start ------ /”与“/ ------ Custom Code End ------ */”中间

  • 如果是内网环境,还需要将 HTML 里的图表库引用地址替换为内网离线图表库地址

5.png

上述操作做完后,就完成了图表的拷贝。

Step2. 数据接入

接下来我们需要将观远BI中的数据接入自定义图表中进行渲染。

在“数据视图”中准备好数据。

6.png

在“图表视图”中可以通过右上角的“查看视图数据结构”对“数据视图”中传入的数据进行预览。

7.png

Demo中提供的样例数据如下所示,我们需要将chart.data数组中的数据替换为我们准备好的数据。

8.png

如下图所示,将BI系统中的数据灌入chart.data中。

9.png

此时我们就完成了数据的接入。

Step3. 自定义修改

最后,可以根据不同图表库的配置修改教程,对不同图表的配置项进行自定义调整。

  • 图表样式修改

10.png

  • 对图表工具提示的展示内容设置

11.png

2.2 生成可视化插件

创建好自定义图表后,为便于再次使用,我们可以将其封装为可视化插件。

2.2.1 文件格式

可视化插件是以约定格式存储的json文件,格式如下:

  • 可视化插件JSON文件模版
{
   "name": "", //可视化插件名称,自定义
   "version": "", //可视化插件模版,自定义
   "author": "", //可视化插件作者,自定义
   "description": "", //可视化插件描述,自定义
   "logo": "", //可视化插件图标,base64格式
   "css": "", //同自定义图表
   "html": "", //同自定义图表
   "subType": "PLUGIN", //类型为插件,无需修改
   "libs": [], //引用js依赖
   "script": "" //同自定义图表
}
  • 可视化插件JSON文件样例
{
   "name": "柱状折断图",
   "version": "V1.0",
   "author": "Guandata-wangping",
   "description": "簇状柱状折断图",
   "logo": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/....../gAooooAKKKKACiiigAooooA//Z",
   "css": "body
   {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n}\n\n#chartdiv {\n  width: 100%;\n  height: 95%;\n}\n\n#container table {\n    border-collapse: collapse;\n    border-color: #E4E7F0;\n}\n\n#container table th {\n    padding: 4px 0;\n}\n\n#container table td {\n    min-width: 120px;\n    padding: 4px 0;\n    text-align: center;\n}\n",
   "html": "\n\n\n",
   "subType": "PLUGIN",
   "libs": [],
   "script": "function renderChart(data, clickFunc, config) {\n  /* ------ Custom Code Start ------ */\n\n  am4core.ready(function () {\n    // Themes begin\n    am4core.useTheme(am4themes_animated);\n    // Themes end\n\n    var chart = am4core.create(\"chartdiv\", am4charts.XYChart);\n    chart.hiddenState.properties.opacity = 0; // this creates initial fade-in\n    chart.colors.step = 2;\n    function createSeries(value, name) {\n      var series = chart.series.push(new am4charts.ColumnSeries());\n      series.dataFields.categoryX = 'Cat';\n      series.dataFields.valueY = value;\n      series.name = name;\n      series.columns.template.tooltipText = \"{categoryX} {name} : {valueY.value}\";\n      series.columns.template.tooltipY = 0;\n      series.columns.template.strokeOpacity = 0;\n  \n      // as by default columns of the same series are of the same color, we add adapter which takes colors from chart.colors color set\n      series.columns.template.adapter.add(\"fill\", function (fill, target) {\n        return chart.colors.getIndex(target.dataItem.index);\n      });\n  \n      return series;\n    }\n    chart.data = [];\n    data[0][0].data.forEach((v,i) => chart.data.push({\"Cat\":v,\"Value1\": data[0][1].data[i]}));\n\n    let index;\n    ValueList = [];\n    for(index in chart.data) {\n    ValueList.push(chart.data[index].Value1)\n\n    }\n    maxValue = Math.ceil(Math.max.apply(null,ValueList));\n    console.log(maxValue);\n/*\n    chart.data = [\n      {\n        country: \"USA\",\n        visits: 23725,\n        data1: 12340,\n      },\n      {\n        country: \"China\",\n        visits: 1882,\n        data1: 1230,\n      },\n      {\n        country: \"Japan\",\n        visits: 1809,\n        data1: 1230,\n      }\n    ];\n  */\n    // var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());\n    // categoryAxis.renderer.grid.template.location = 0;\n    // categoryAxis.dataFields.category = \"country\";\n    // categoryAxis.renderer.minGridDistance = 40;\n    // categoryAxis.fontSize = 11;\n\n    var xAxis = chart.xAxes.push(new am4charts.CategoryAxis())\n    xAxis.dataFields.category = 'Cat'\n    xAxis.renderer.cellStartLocation = 0.1\n    xAxis.renderer.cellEndLocation = 0.9\n    xAxis.renderer.grid.template.location = 0;\n\n\n    var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());\n    valueAxis.min = 0;\n    valueAxis.max = maxValue;\n    valueAxis.strictMinMax = true;\n    valueAxis.renderer.minGridDistance = 30;\n    // axis break\n    var axisBreak = valueAxis.axisBreaks.create();\n    axisBreak.startValue = maxValue * 0.3;\n    axisBreak.endValue = maxValue * 0.9;\n    //axisBreak.breakSize = 0.005;\n\n    // fixed axis break\n    var d =\n      (axisBreak.endValue - axisBreak.startValue) /\n      (valueAxis.max - valueAxis.min);\n    axisBreak.breakSize = (0.05 * (1 - d)) / d; // 0.05 means that the break will take 5% of the total value axis height\n\n    // make break expand on hover\n    var hoverState = axisBreak.states.create(\"hover\");\n    hoverState.properties.breakSize = 1;\n    hoverState.properties.opacity = 0.1;\n    hoverState.transitionDuration = 1500;\n\n    axisBreak.defaultState.transitionDuration = 1000;\n    createSeries('Value1', 'Value1');\n  }); // end am4core.ready()\n\n  /* ------ Custom Code End ------ */\n}\nnew GDPlugin().init(renderChart);\n"
}

2.2.2 自定义图表内容引用

通过 Chrome浏览器控制台中的接口返回信息,获取自定义图表内容。

具体操作:【F12/鼠标右键选择“检查”,打开控制台】--> 【切换到“网络/Network”页签】 --> 【自定义图表编辑界面点击“保存”】 --> 【从接口列表中选中“save”接口】 --> 【选择“预览/Preview”页签】 --> 【复制 content 下 html、css 与 script 中的内容】 --> 【粘贴到可视化插件JSON文件对应区域】

12.png

2.2.3 Logo生成与转换

首先,从网上下载(推荐: https://www.iconfont.cn/)或自己制作(推荐:Axure RP9工具)一个Logo图标。

13.png

之后,为适应观远两种不同颜色主题,需要将制作好的图标的纯色背景替换为透明背景,可使用美图秀秀等工具实现期望效果。

14.jpg

替换为透明背景后,将Logo图片转换为Base64格式的编码(推荐 https://c.runoob.com/front-end/59/)。

最后,将转换后的Base64编码粘贴到可视化插件JSON文件对应区域,即可生成可视化插件。

2.2.4 可视化插件安装与应用

经过上述步骤后,我们的可视化插件就构建完成了,接下来需要将可视化插件导入观远BI平台。

在仪表板中,点击“新建卡片”,将弹窗滚动到最下方,点击“安装插件”。选择之前生成的可视化插件JSON文件,便会将插件导入与安装,新增的插件会显示在当前卡片列表最末尾。

15.png

点击插件,如同普通卡片一样,选择分析数据集,进入卡片编辑界面后,按照界面指示拖拽数据,即可完成可视化展示。

16.png

2.3 封装图表配置项

在上文中,我们向大家介绍了如何生成可视化插件,并且通过安装与应用这些插件,我们可以快速实现期望的可视化效果。但这样的实现方式依旧存在某些局限性,例如界面上缺少直接自定义图表配置项的能力,这就导致当我们想要修改颜色、标签显示、图例样式时,需要回到JSON文件中进行代码修改,然后再进行插件的更新。

为了解决这个问题,我们需要对一些常用的配置项进行界面创建和渲染时的应用。

2.3.1 图表配置项创建

内置的渲染组件类型如下所示:

  • STRING // 文本输入框

  • SELECT // 下拉选择器

  • NUMBER // 数字输入框

  • BOOLEAN // switch开关

  • DIVIDER // 分割线(用于美化)

  • MODEL // 灵活的Model定义

  • GROUP // 分组

若想要展示界面创建和图表配置项,需要在可视化插件JSON文件中增加“chartConfigurations”。“chartConfigurations”中的内容模版如下所示:

  • 可视化插件JSON文件模版增加“chartConfigurations”
{
   "name": "", //可视化插件名称,自定义
   "version": "", //可视化插件模版,自定义
   "author": "", //可视化插件作者,自定义
   "description": "", //可视化插件描述,自定义
   "logo": "", //可视化插件图标,base64格式
   "css": "", //同自定义图表
   "html": "", //同自定义图表
   "subType": "PLUGIN", //类型为插件,无需修改
   "libs": [], //引用js依赖
   "script": "" //同自定义图表
   "chartConfigurations": [
       {
           "fieldName": "", //必填,组件名称,作为图表绘制时config中传入的名称
           "label": "", //必填,前端界面展示名称
           "type": "", //必填,渲染组件类型
           "defaultValue": "", //可选,默认值
          "dependsOnMap": {}, //可选,依赖项
          /*
           示例
          "dependsOnMap": {
              "配置项fieldName1": [ true ]
           },
           */
           "model": {} //可选,模型
          /*
          示例
          "model": {
              "modelType": "SELECT",
              "labels": [ "数据标签外", "内部居中" ],
              "values": [ "outside", "center" ],
              "size": "small"
           }
           */
       },
       {
           配置项2
       },
      ...
   ]
}

以上文提到的柱状折断图举例,我们在可视化插件JSON文件中增加chartConfigurations,并且构建了两个配置项“startValue”与“endValue”,来定义截断区间的起始值与结束值。如下所示:

  • 可视化插件JSON文件模版增加“chartConfigurations”样例
{
   "name": "柱状折断图",
   "version": "V1.1",
   "author": "Guandata-wangping",
   "description": "簇状柱状折断图",
   "logo": "data:image/png;base64,iVBORw0KGgoAAAANSU....../g+iCTPPrp+uQAAAAABJRU5ErkJggg==",
   "css": "body {\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n}\n\n#chartdiv {\n  width: 100%;\n  height: 95%;\n}\n\n#container table {\n    border-collapse: collapse;\n    border-color: #E4E7F0;\n}\n\n#container table th {\n    padding: 4px 0;\n}\n\n#container table td {\n    min-width: 120px;\n    padding: 4px 0;\n    text-align: center;\n}\n",
   "html": "\n\n\n",
   "subType": "PLUGIN",
   "libs": [],
   "chartConfigurations":[{"fieldName":"startValue","label":"截断起始","type":"NUMBER","model":{"size":"small"}},{"fieldName":"endValue","label":"截断终点","type":"NUMBER","model":{"size":"small"}}],
   "script": "const DEDAULT_COLORS = ['#2f7ed8', '#f28f43', '#1aadce', '#492970', '#f28f43', '#77a1e5', '#c42525', '#a6c96a']\nconst DEFALUT_THEME = 'LIGHT'\nfunction renderChart(data, clickFunc, config) {\n  /* ------ Custom Code Start ------ */\n  if (!data || data[0].length < 2) {\n\t\treturn void(document.getElementById(\"chartdiv\")\n\t\t\t.innerHTML = \"

需要2列数据(1个维度,1个数值)
第1列为维度
第2列为数值

\")\n\t}\n  const {\n\t\ttheme: i = DEFALUT_THEME,\n\t\tcolors: s = DEDAULT_COLORS,\n\t\tcustomOptions: o = {}\n\t} = config || {}, n = \"LIGHT\" !== i, {\n\t\tstartValue: a,\n\t\tendValue: l\n\t} = o;\n\n\n  am4core.ready(function () {\n    // Themes begin\n    am4core.useTheme(am4themes_animated);\n    // Themes end\n\n    var chart = am4core.create(\"chartdiv\", am4charts.XYChart);\n    chart.hiddenState.properties.opacity = 0; // this creates initial fade-in\n    chart.colors.step = 2;\n    function createSeries(value, name) {\n\n//change theme colors\n    s.forEach((v)=>\n      {chart.colors.list.push(am4core.color(v))}\n    );\n\nconsole.log(am4core.color(s[0]));\n      var series = chart.series.push(new am4charts.ColumnSeries());\n      series.dataFields.categoryX = 'Cat';\n      series.dataFields.valueY = value;\n      series.name = name;\n      series.columns.template.tooltipText = \"{categoryX} {name} : {valueY.value}\";\n      series.columns.template.tooltipY = 0;\n      series.columns.template.strokeOpacity = 0;\n  \n      // as by default columns of the same series are of the same color, we add adapter which takes colors from chart.colors color set\n      series.columns.template.adapter.add(\"fill\", function (fill, target) {\n        return chart.colors.getIndex(target.dataItem.index);\n      });\n  \n      return series;\n    }\n    chart.data = [];\n    value1Name = data[0][1].name;\n    data[0][0].data.forEach((v,i) => chart.data.push({\"Cat\":v,[value1Name]: data[0][1].data[i]}));\n\n    ValueList = [];\n    data[0][0].data.forEach((v,i) => ValueList.push(data[0][1].data[i]));\n    maxValue = Math.ceil(Math.max.apply(null,ValueList));\n/*\n    chart.data = [\n      {\n        country: \"USA\",\n        visits: 23725,\n        data1: 12340,\n      },\n      {\n        country: \"China\",\n        visits: 1882,\n        data1: 1230,\n      },\n      {\n        country: \"Japan\",\n        visits: 1809,\n        data1: 1230,\n      }\n    ];\n  */\n    // var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());\n    // categoryAxis.renderer.grid.template.location = 0;\n    // categoryAxis.dataFields.category = \"country\";\n    // categoryAxis.renderer.minGridDistance = 40;\n    // categoryAxis.fontSize = 11;\n\n    var xAxis = chart.xAxes.push(new am4charts.CategoryAxis())\n    xAxis.dataFields.category = 'Cat'\n    xAxis.renderer.cellStartLocation = 0.1\n    xAxis.renderer.cellEndLocation = 0.9\n    xAxis.renderer.grid.template.location = 0;\n\n\n    var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());\n    valueAxis.min = 0;\n    valueAxis.max = maxValue;\n    valueAxis.strictMinMax = true;\n    valueAxis.renderer.minGridDistance = 30;\n    // axis break\n    var axisBreak = valueAxis.axisBreaks.create();\n    axisBreak.startValue = a || maxValue * 0.3;\n    axisBreak.endValue = l || maxValue * 0.9;\n    //axisBreak.breakSize = 0.005;\n\n    // fixed axis break\n    var d =\n      (axisBreak.endValue - axisBreak.startValue) /\n      (valueAxis.max - valueAxis.min);\n    axisBreak.breakSize = (0.05 * (1 - d)) / d; // 0.05 means that the break will take 5% of the total value axis height\n\n    // make break expand on hover\n    var hoverState = axisBreak.states.create(\"hover\");\n    hoverState.properties.breakSize = 1;\n    hoverState.properties.opacity = 0.1;\n    hoverState.transitionDuration = 1500;\n\n    axisBreak.defaultState.transitionDuration = 1000;\n    createSeries(value1Name, value1Name);\n  }); // end am4core.ready()\n\n  /* ------ Custom Code End ------ */\n}\nnew GDPlugin().init(renderChart);\n"
}

JSON文件中增加了 chartConfigurations 内容后,更新插件。我们会发现在通过可视化插件创建图表时,右侧的图表配置区域增加了“配置扩展”模块,并且我们刚刚定义的“startValue”与“endValue”两个配置项都已经展示出来了。至此我们已经完成了图表配置项的前端界面准备工作。

JS区域绘制图表的函数 renderChart 中共有三个入参:data、clickFunc 以及 config。

  • data 为数据数组,负责传递我们已经准备好的数据,上文已经介绍过;

  • clickFunc 为交互回调,传递的是用户交互事件信息;

  • config 为图表配置,传递的是图表的各类配置信息。

我们通过控制台输出一下 config 中的内容,发现 config 中一共传递了如下内容:colors、customOption、 theme 和 language(5.6.0 版本支持)。

  • colors 中包含的是“主题/颜色”中对应的颜色信息;

  • customOption 中包含有自定义的两个配置项“endValue”与“startValue”信息;

  • theme 中包含主题信息;

  • language 中包含当前系统使用语言(5.6.0 版本支持)。

17.png

2.3.2 配置项接入

从 config 入参中我们可以获取到 colors、customOption、 theme 与 language(5.6.0 版本支持)信息。我们仍以柱状截断图为例,针对colors 与 customOption 进行配置说明。

Step1. 从config中取出图表配置信息赋予变量

18.png

Step2. 将变量值应用于图表渲染

颜色应用

在 Amcharts 中,图表渲染时会从系统预设的 chart.colors.list 中取数,而 hart.colors.list 中存放的是通过am4core.color($hex_color) 生成的 object。因此我们通过循环遍历将 config 中的 colors 值依次传入其中。如下所示

19.png

自定义配置项应用

我们之前定义了两个配置项“startValue”和“endValue”,用于设置截断区间。尽管之前已经按照最大值的30%-90% 做了一个截断区间的自动化处理,但某些情况下还是需要手动调整截断区间。

因此这里做了一个优化,当“startValue”与“endValue”两个配置项中有值传入时,取手动配置的区间值。当“startValue”与“endValue”为空时,按照最大值的30%-90%自动截断。如下图所示:

20.png

Step3. 展示效果验证

至此我们完成了所有的工作。将结果保存为JSON格式文件并且更新插件后,可以对最终的可视化插件展示效果进行查看与验证。

21.png