自定义控件简介
DWF表单定制功能提供了基础的可视化组件,但是当遇到高级可视化的需求时,使用普通可视化控件就难以完成任务了。
为了解决这个问题,DWF在表单控件区的可视化部分里提供了自定义控件,该控件可以将Apache Echart项目中的配置嵌入到表单中。
图-自定义可视化控件的位置
利用自定义控件可以基于DWF提供的restful api查询数据,然后展示丰富多彩的形式,如下图所示:
图- 自定义可视化控件示例
基本概念
在介绍自定义控件的使用方法之前,先了解一些基本概念,涉及可视化这件事可能有3件事情,第一、可视化的数据来自哪里,第二、如何绘制出来,第三、怎么把数据和可视化编排在一起,其中:
第一、数据访问的问题,可以调用在DWF里已经配置好了数据服务实现。
第二、如何绘制出来,在DWF内采用了Apache Echarts项目实现。
第三、最后Echart和数据的结合将在自定义控件内完成。
下面介绍一些基本概念:
Apache Echarts
在了解自定义控件的使用之前,先介绍一下开源项目—Apache Echarts项目,该项目是由百度主导贡献到Apache开源社区基金会的一个项目,是一个专门的免费可视化组件库,下面是一些Echarts的示例:
如果希望了解Echarts可以实现的可视化效果可以去官方网站:https://echarts.apache.org/examples/zh/index.html,在那里你可以看到丰富的可视化元素。
自定义控件和Echarts实例
在DWF表单中拖入一个自定义控件就可以在上面创建一个 echarts 实例,打开自定义控件的编辑开关,就可以编辑Echart的配置。
每个自定义控件中包含的echarts实例 中可以创建多个图表和坐标系等等,这些配置用JSON对象描述(用 option 来描述)。
图-Echart实例和配置的关系,来源:https://echarts.apache.org/zh/tutorial.html
系列(series)
在 echarts 里,系列(series)是指:一组数值以及他们映射成的图。“系列”这个词原本可能来源于“一系列的数据”,而在 echarts 中取其扩展的概念,不仅表示数据,也表示数据映射成为的图。所以,一个 系列 包含的要素至少有:一组数值、图表类型(series.type)、以及其他的关于这些数据如何映射成图的参数。
echarts 里系列类型(series.type)就是图表类型。系列类型(series.type)至少有:line(折线图)、bar(柱状图)、pie(饼图)、scatter(散点图)、graph(关系图)、tree(树图)、...
如下图,右侧的 option 中声明了三个 系列(series):pie(饼图系列)、line(折线图系列)、bar(柱状图系列),每个系列中有他所需要的数据(series.data)。
图-系列和可视化之间的关系,来源:https://echarts.apache.org/zh/tutorial.html
用 option 对象描述图表
上面已经出现了 option 这个概念。在自定义控件中打开编辑开关会弹出一个脚本编辑框,在这里使用 option 来描述其对图表的各种需求,包括:有什么数据、要画什么图表、图表长什么样子、含有什么组件、组件能操作什么事情等等。简而言之,option 表述了:数据、数据如何映射成图形、交互行为。
图-拖入自定义控件,打开编辑
图-设置Echart配置,即:option
如何使用自定义控件
接下来通过几个案例介绍自定义控件的使用,在介绍案例的同时我们将介绍一些DWF自带的restful api的用法用于获取数据。
注:下面将要介绍的例子是基于本教程使用的设备管理系统中的数据实现的,所以在进一步讨论之前,如果是从空白的DWF起步,需要将之前教程中提到的模型包导入DWF实例中。
例1:基本用法
首先,在表单中拖入一个自定义控件,看看如何设置一个基本的饼状图,形式如下:
打开,编辑开关,弹出脚本编辑对话框,输入如下的代码:
option = { series : [ { name: '访问来源', type: 'pie', data:[ {value:235, name:'视频广告'}, {value:274, name:'联盟广告'}, {value:310, name:'邮件营销'}, {value:335, name:'直接访问'}, {value:400, name:'搜索引擎'} ], } ] };
上述代码中使用了一个变量option是一个JSON对象,内部包含一个系列,名称为访问来源,类型为饼状图,数据包括:视频广告,联盟广告,邮件影响,直接访问,搜索引擎。
点击保存即可看到本节开头的饼图,从这个例子中可以看到:
- 自定义控件约定了脚本里只要给名字为option的对象赋值即可实现图像的显示。
- 在这个例没有访问数据库,只是给出了一个数组用于显示即可。
接下来的例子将展示如何将DWF中的数据和自定义控件联系在一起。
例2:用DWF中的数据初始化自定义控件
接下来,做一个稍微有趣一点的例子,用热度图展示各地开工数据,先看一下效果,如下图所示。
图-全国开工热度可视化
热度数据表示
全国开工热度显示使用了,热度图案例:https://echarts.apache.org/examples/zh/editor.html?c=heatmap-bmap
其需要接受的数据格式为一个二维数组,形式如下:[精度,维度,数值],将这个数组作为系列的data属性即可实现。
那么问题来了,这个二维数组如何获得呢?这就涉及到DWF的SDK了:
调用DWF的restful api
在 DWF默认提供了一系列restful api,如果打开DWF建模工具,点击右上角admin按钮可以看到api说明页面。
图-打开API说明页
选择对象类菜单可以打开用于进行数据访问的restful api接口说明,如下图所示:
回顾一下,第六章 表单中的事件脚本 中1.2节,介绍了restful api调用方法,当选择合适的restful api以后,在热度图的案例里,我们计划提取设备类“Asset”的所有对象:objects,所以接入的路径为:“/omf/entities/Asset/objects”,设置参数为空,调用方式如下:
let param = {}; this.dwf_axios.post(`/omf/entities/Asset/objects`,param).then(initRes => { //在回调中处理 console.log(initRes.data.data); myChart.setOption(option ={ ... }); });
通过console.log输出,可以看到如下的结果:
[ { "woCount": 0, "workLoc": "山东", "lastModifier": "9C92E891E9AE534DB685737DE467A9D0", "assetState": "已安装", "oid": "07CDDDE7922570429C1BB03C5CC98314", "alarmCount": 7, "ownerName": "", "id": "10GJ10313115", "workHour": 732, "locationY": 37.71827, "creator": "9C92E891E9AE534DB685737DE467A9D0", "assetImgIdentified": "07CDDDE7922570429C1BB03C5CC983142005988314350236070png", "locationX": 112.550575, "installationDate": 1578499200000, "assetIcon": "https://s2.ax1x.com/2019/08/26/mfw3Ae.png", "partOid": "34AC734FBFB2294E852EE50B54C00831", "assetType": "搅拌车", "assetImg": "2013-04-26_19-12-11_555.jpg", "lastModifyTime": 1593679314208, "assetName": "搅拌车", "assetDesc": "黑色保时捷919" }, ... ]
很显然,这个结果是一个一维数组,并不能直接输出到Echart进行处理,因此,需要在查出对象的基础上对齐进行转换。
数组的变换
实现数组转换可以利用javascript的映射函数完成,对于javascript数组如果希望将元素逐个变换为另外的形式,那么可以使用映射函数map实现,其接受一段程序要求这段程序返回一个元素,map函数会将这个返回值组成另一个数组,返回。
以刚才讨论的设备信息为例,我们希望将设备的精度:locationX,维度:locationY和工作小时数:workHours,拼接为数组,典型调用方式如下:
var objs = res.data.data; points = objs.map(x => { return [x.locationX, x.locationY, x.workHours] });
至此,points可以直接作为输入作为系列的data属性的取值用于可视化。
设置配置项
最后,为了把数据输入到自定义控件,需要设置正确的配置项,在此我们直接拷贝了Echart热度图的配置项(option)的设定。
由于,数据的获取是通过异步请求实现的,所以,在刷新自定义控件的前端显示时不能直接初始化option变量,而应该通过getAddinById获得EChart控件的引用,然后设置其属性chart.setOption( option= {})实现更新。
完整的代码如下所示:
let param = {}; this.dwf_axios.post("/omf/entities/Asset/objects", param).then(res => { var objs = res.data.data; console.log(objs); points = objs.map(x => { return [x.locationX, x.locationY, x.workHours] }); var selfOption = { animation: false, bmap: { center: [104.114129, 37.550339], zoom: 5, roam: true }, visualMap: { show: false, top: 'top', min: 0, max: 5, seriesIndex: 0, calculable: true, inRange: { color: ['blue', 'blue', 'green', 'yellow', 'red'] } }, series: [{ type: 'heatmap', coordinateSystem: 'bmap', data: points, pointSize: 5, blurSize: 8 }] } myChart.setOption(selfOption); })
将上述代码放入自定义控件,最终效果是:
关于热度图的具体使用,可以进一步参考:https://echarts.apache.org/zh/option.html#series-heatmap.type
例3:产品结构树形结构展示
在热力图的例子中,读者进一步接触了DWF提供的restful api,接下来,在使用一个高级的数据访问api:树形结构查询。
在设备管理的案例里我们还搭建了一个搅拌车的产品结构,如下图所示:
图-零部件产品结构
这种树形结构展示方式很难看到产品的全貌,利用Echarts的树形结构可视化控件可以将这样的树形结构展示为下面的样子:
图-在一幅图中显示搅拌车产品结构
下面看看如何实现。
Echart数据格式
首先,到Echart官方网站上看看树形结构信息的表现方式:https://echarts.apache.org/examples/zh/editor.html?c=tree-polyline
要在一个自定义控件里显示树形结构,需要将数据整理成如下形式:
data = { "name": "flare", "children": [ { "name": "data", "children": [ { "name": "converters", "children": [ {"name": "Converters", "value": 721}, {"name": "DelimitedTextConverter", "value": 4294} ] }, { "name": "DataUtil", "value": 3322 } ] }, ... }
可以看到,节点的格式很简单,每一个节点有name和一个可选的value属性,一个节点的子节点用名字为children的数组表示,子节点的格式又是重复name, value, children,如此往复。
调用DWF restful api
搞清楚可视化需要的数据结构以后,看看DWF的restful api,在dwf中关联类对象上也存在一个tree的接口,其返回数据格式基本和Echarts树形结构要求的格式相同。
接口的接入点链接为:`/omf/relations/${relationName}/tree`,其中relationName如果左右类均为相同的实体类,那么就可以以树形方式展示出来。
除此之外,接口还需要接受如下的几个参数,用来告诉DWF如何构建一个树,这些参数和说明如下所示:
let param = { "rootCondition":"and leftclass.plt_partType='产品'", //表明需要哪些根节点 "childrenCondition":"",//在逐层展开的时候用什么条件过滤孩子节点 "leafCondition":"",//展开到叶子的时候用什么条件过滤叶子节点 "queryDirection":"LEFT_TO_RIGHT", //展开树形结构的边是从从左到右展开,还是从右到左展开 "recursiveLevel":"6", //一次展开几层 "startIndex":0}; //如果有多个根节点从第几个返回
以父件到子件(PartToPart)为例,完整的调用方式如下:
let param = { "childrenCondition":"", "leafCondition":"", "queryDirection":"LEFT_TO_RIGHT", "recursiveLevel":"6", "rootCondition":"and leftclass.plt_partType='产品'", "startIndex":0}; this.dwf_axios.post(`/omf/relations/PartToPart/tree`,param).then(res => { //在回调中处理 let itemRes = res.data.data; console.log(itemRes); myChart.setOption(option ={ ... }); }
数据转换
上述代码调用以后执行结果,从日志输出以后格式化显示如下图所示,其中包含1个根节点,以及一个children属性,展开后仍然含有children属性,直到叶子节点。
结构已经十分接近Echart树结构需要的格式,因此数据处理的目标是:遍历整个树结构,把每个节点的right_partName对应到name,每个节点的children个数设置为节点的value,每个节点的children直接设置为childrens属性,因此可以编写一个递归调用函数如下:
function mapChildren(tree = []){ let arr = []; if(!!tree) { tree.forEach(item => { let obj = {}; obj['name'] = item.right_partName; if('children' in item) { obj['value'] = item.children.length; obj['children'] = mapChildren(item.children); } else { obj['value'] = 0; } arr.push(obj); }) } return arr }
设置配置项
最后,为了把数据输入到自定义控件,需要设置正确的配置项,直接拷贝了Echart树的配置项(option)的设定。
完整的代码如下所示:
function mapChildren(tree = []){ let arr = []; if(!!tree) { tree.forEach(item => { let obj = {}; obj['name'] = item.right_partName; obj['value'] = item.childrenCount; if('children' in item) { obj['children'] = mapChildren(item.children); } arr.push(obj); }) } return arr } let param = { "childrenCondition":"", "leafCondition":"", "queryDirection":"LEFT_TO_RIGHT", "recursiveLevel":"6", "rootCondition":"and leftclass.plt_partType='产品'", "startIndex":0}; this.dwf_axios.post(`/omf/relations/PartToPart/tree`,param).then(res => { //在回调中处理 let temRes = res.data.data; let data = mapChildren(temRes); let option = { tooltip: { trigger: 'item', triggerOn: 'mousemove' }, series:[ { type: 'tree', data: data, edgeShape: 'polyline', left: '2%', right: '2%', top: '8%', bottom: '20%', symbol: 'emptyCircle', orient: 'vertical', expandAndCollapse: true, label: { position: 'top', rotate: -90, verticalAlign: 'middle', align: 'right', fontSize: 9 }, leaves: { label: { position: 'bottom', rotate: -90, verticalAlign: 'middle', align: 'left' } }, animationDurationUpdate: 750 } ] }; myChart.setOption(option); });
最终结果如下所示:
关于树的细节配置详见:https://echarts.apache.org/zh/option.html#series-tree.type
小结
本文重点讨论了DWF中自定义控件的用法,包括:
- Echarts项目的基本概念;
- DWF数据访问restful api的调用方法;
- 自定义控件的基本用法;
- 列举了饼状图、热度图、树状图的使用方法。
关于Echarts项目还有大量有趣的可视化案例,有兴趣的读者可以进一步了解一些案例。
https://gallery.echartsjs.com/explore.html#sort=rank~timeframe=all~author=all