最近在项目中需要开发一个图表来显示人员的各种属性,类似于一种树形的结构进行显示数据。如果多个人员有同一个属性,那么需要将相同的属性进行连线,即关联起来。即形成一个关系图,由于我自身对echarts稍微熟悉一下,因此采用echarts3来完成此图表的开发。
注意:echarts的不同版本api有些稍微的不同。
完成效果:
需求:
1、点击父节点
|- 该父节点的子节点是没有显示的,那么显示它的子节点
|- 该父节点的子节点是显示的,那么隐藏它的子节点和子孙节点
2、对于父节点显示出它的分类名称,比如 用户信息(父节点)下有用户名、性别、生日、身份证(子节点)等
|- 父节点显示 用户信息
|- 子节点显示 用户名 : 名称
性别 : 名称
3、鼠标移动到父节点上,显示出它下方子节点的具体信息
预备知识:
1、节点的隐藏和实现:在 series[index].data[index]中存在category 值,如果它的值和series[index].categories中的角标没有对应起来,那么此节点是不显示的(即隐藏,将category的值改成负数,显示 改成整数,值要和categories的角标对应起来)
2、2个节点要连接起来,那么 links 中的 source,target 的值只需要和 data 中的 name 属性的值对应起来即可
3、需要了解一下echarts的富文本样式,用于格式化节点上显示的值
4、了解一点es5,es6的语法
图片解释:(下方的 lengedName 实际是data中的 legendName ,图片上写错了)
具体实现:
1、点击显示和隐藏节点
|- 找到点击节点的 open 的值(第一次点击是不存在的,点击完增加这个属性)
> true(存在,即点过一回)
* 从links中找到所有的子节点和子孙节点的 name(links中的target属性)的值,需要递归获取
* 从data中获取获取关联的节点
* 将节点的category 的值改成 负数
* 如果节点的nodeType === 1(即上面图片解释中的父节点), 那么需要将 open的值设置成false
* 将当前点击的节点的 open 属性改成 false
* 重新渲染echarts图表
* 此时图表的节点就折叠起来了
> false(即不存在或后续赋值为false)
* 从links中找到所有的子节点的 name(links中的target属性)的值
* 从data中获取获取关联的节点
* 将节点的category 的值改成 整数
* 将当前点击的节点的 open 属性改成 true
* 重新渲染echarts图表
* 此时图表的节点就展开了
/** * 绑定图表的点击事件 * @param chart */ function bindChartClickEvent(chart) { chart.on('click', function (params) { var category = params.data.category, nodeType = params.data.nodeType; if (category === 0 || nodeType === 1) { toggleShowNodes(chart, params); } }); } /** * 展开或关闭节点 * @param chart * @param params */ function toggleShowNodes(chart, params) { var open = !!params.data.open, options = chart.getOption(), seriesIndex = params.seriesIndex, srcLinkName = params.name, serieLinks = options.series[seriesIndex].links, serieData = options.series[seriesIndex].data, serieDataMap = new Map(), serieLinkArr = []; // 当前根节点是展开的,那么就需要关闭所有的根节点 if (open) { // 递归找到所有的link节点的target的值 findLinks(serieLinkArr, srcLinkName, serieLinks, true); if (serieLinkArr.length) { serieData.forEach(sd => serieDataMap.set(sd.name, sd)); for (var i = 0; i < serieLinkArr.length; i++) { if (serieDataMap.has(serieLinkArr[i])) { var currentData = serieDataMap.get(serieLinkArr[i]); currentData.category = -Math.abs(currentData.category); if (currentData.nodeType === 1) { currentData.open = false; } } } serieDataMap.get(srcLinkName).open = false; chart.setOption(options); } } else { // 当前根节点是关闭的,那么就需要展开第一层根节点 findLinks(serieLinkArr, srcLinkName, serieLinks, false); if (serieLinkArr.length) { serieData.forEach(sd => serieDataMap.set(sd.name, sd)); for (var j = 0; j < serieLinkArr.length; j++) { if (serieDataMap.has(serieLinkArr[j])) { var currentData = serieDataMap.get(serieLinkArr[j]); currentData.category = Math.abs(currentData.category); } } serieDataMap.get(srcLinkName).open = true; chart.setOption(options); } } } /** * 查找连接关系 * @param links 返回的节点放入此集合 * @param srcLinkName 源线的名称 * @param serieLinks 需要查找的集合 * @param deep 是否需要递归进行查找 */ function findLinks(links, srcLinkName, serieLinks, deep) { var targetLinks = []; serieLinks.filter(link => link.source === srcLinkName).forEach(link => { targetLinks.push(link.target); links.push(link.target) }); if (deep) { for (var i = 0; i < targetLinks.length; i++) { findLinks(links, targetLinks[i], serieLinks, deep); } } }
2、节点名称显示的格式化
"label": { "normal": { "show": true, "position": "top", "formatter": function (args) { if (args.data.nodeType === 1) { return "{prefixClassName|" + args.data.legendName + "}"; } else { return "{prefixClassName|" + args.data.legendName + " :}\r\n " + args.name; } }, "rich": { "prefixClassName": { color: "#FF9301", fontWeight: "bold" } } } }
3、鼠标移动到父节点上显示子节点的信息
找到当前节点关联的所有的子节点,通过links来进行查找,当前节点的name属性的值等于links中source中的值,那么target就是关联的子节点的name的值,遍历data数据,如果name属性的值等于target的值,就找到了关联节点的数据。
注意: 在显示的时候需要注意一下获取前面颜色的获取(当categories中的值过多时需要注意一下)
tooltip: { "formatter": function (arg) { var nodeType = arg.data.nodeType, srcName = arg.name, seriesIndex = arg.seriesIndex, options = echart.getOption(), serieData = options.series[seriesIndex].data, serieLinks = options.series[seriesIndex].links, colors = options.color, serieDataMap = new Map(), serieLinkArr = [], tips = ''; // 父节点,排除根节点 if (nodeType === 1) { serieLinks.filter(link => link.source === srcName).forEach(link => serieLinkArr.push(link.target)); if (serieLinkArr.length) { serieData.forEach(sd => serieDataMap.set(sd.name, sd)); for (var i = 0; i < serieLinkArr.length; i++) { if (serieDataMap.has(serieLinkArr[i])) { var currentData = serieDataMap.get(serieLinkArr[i]), color = getColor(colors, currentData.category); tips += ' ' + currentData.legendName + " : " + currentData.name + '
'; } } } return tips; } else { return ''; } } } /** * 获取颜色 * @param colors * @param index * @returns {*} */ function getColor(colors, index) { var length = colors.length, colorIndex = index; if (index >= length) { colorIndex = length - index; } return colors[colorIndex]; }
完成代码如下:(如需运行,下载附件中的即可,或自定导入echarts3的js文件)
echart3 力引导布局实现节点的提示和折叠