echart3 力引导布局实现节点的提示和折叠

     最近在项目中需要开发一个图表来显示人员的各种属性,类似于一种树形的结构进行显示数据。如果多个人员有同一个属性,那么需要将相同的属性进行连线,即关联起来。即形成一个关系图,由于我自身对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、节点名称显示的格式化



        富文本样式的使用 (series中label的设置

"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 += '<span style="background-color: ' + color + ';width: 10px;height: 10px;border-radius: 50%;display: inline-block"></span> ' + currentData.legendName + " : " + currentData.name + ' <br />';
                            }
                        }
                    }
                    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文件)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>echart3 力引导布局实现节点的提示和折叠</title>
    <script src="echarts.common.min.js"></script>
</head>
<body>
<div id="chart" style="width: 100%;height: 600px"></div>
<script type="text/javascript">
    var echart = echarts.init(document.getElementById('chart'));
    var options = {
        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 += '<span style="background-color: ' + color + ';width: 10px;height: 10px;border-radius: 50%;display: inline-block"></span> ' + currentData.legendName + " : " + currentData.name + ' <br />';
                            }
                        }
                    }
                    return tips;
                } else {
                    return '';
                }
            }
        },
        "series": [
            {
                "itemStyle": {
                    "normal": {
                        "label": {
                            "show": true
                        },
                        "borderType": "solid",
                        "borderColor": "rgba(182,215,0,0.5)",
                        "borderWidth": 2,
                        "opacity": 1
                    },
                    "emphasis": {
                        "borderWidth": 5,
                        "borderType": "solid",
                        "borderColor": "#40f492"
                    }
                },
                "lineStyle": {
                    "normal": {
                        "color": "rgba(182,0,255,0.5)",
                        "width": "3",
                        "type": "dotted",
                        "curveness": 0.1,
                        "opacity": 1
                    }
                },
                "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"
                            }
                        }
                    }
                },
                "layout": "force",
                "roam": true,
                "edgeSymbolSize": [
                    8,
                    10
                ],
                "edgeSymbol": [
                    "circle",
                    "arrow"
                ],
                "focusNodeAdjacency": false,
                "force": {
                    "repulsion": 300,
                    "edgeLength": 50
                },
                "links": [
                    {
                        "source": "3****************3",
                        "target": "3****************3-bank-card"
                    },
                    {
                        "source": "3****************3-bank-card",
                        "target": "工行卡:4077"
                    },
                    {
                        "source": "3****************3-bank-card",
                        "target": "建行卡:4078"
                    },
                    {
                        "source": "3****************3",
                        "target": "3****************3-basic-info"
                    },
                    {
                        "source": "3****************3-basic-info",
                        "target": "张三"
                    },
                    {
                        "source": "3****************3",
                        "target": "3****************3-contact"
                    },
                    {
                        "source": "3****************3-contact",
                        "target": "145157****@qq.com"
                    },
                    {
                        "source": "3****************3-contact",
                        "target": "14515783**"
                    }
                ],
                "categories": [
                    {
                        "name": "用户"
                    },
                    {
                        "name": "身份证"
                    },
                    {
                        "name": "姓名"
                    },
                    {
                        "name": "性别"
                    },
                    {
                        "name": "生日"
                    },
                    {
                        "name": "手机"
                    },
                    {
                        "name": "固定电话"
                    },
                    {
                        "name": "邮箱"
                    },
                    {
                        "name": "qq"
                    },
                    {
                        "name": "地址"
                    },
                    {
                        "name": "银行卡"
                    },
                    {
                        "name": "基本信息"
                    },
                    {
                        "name": "地址分类"
                    },
                    {
                        "name": "联系方式"
                    },
                    {
                        "name": "银行卡分类"
                    }
                ],
                "name": "人员关系图",
                "type": "graph",
                "showSymbol": true,
                "yAxisIndex": 0,
                "z": 2,
                "data": [
                    {
                        "name": "3****************3",
                        "symbolSize": 40,
                        "value": "3****************3",
                        "category": 0,
                        "draggable": true,
                        "label": {
                            "normal": {
                                "show": true,
                                "position": "inside"
                            }
                        },
                        "legendName": "用户",
                        "nodeType": 0,
                        "idCardNum": "3****************3"
                    },
                    {
                        "name": "3****************3-bank-card",
                        "symbolSize": 40,
                        "value": "银行卡分类",
                        "category": -14,
                        "draggable": true,
                        "label": {
                            "normal": {
                                "show": true,
                                "position": "inside"
                            }
                        },
                        "legendName": "银行卡分类",
                        "nodeType": 1
                    },
                    {
                        "name": "工行卡:4077",
                        "symbolSize": 20,
                        "value": "工行卡:4077",
                        "category": -10,
                        "draggable": true,
                        "legendName": "银行卡",
                        "nodeType": 0
                    },
                    {
                        "name": "建行卡:4078",
                        "symbolSize": 20,
                        "value": "建行卡:4078",
                        "category": -10,
                        "draggable": true,
                        "legendName": "银行卡",
                        "nodeType": 0
                    },
                    {
                        "name": "3****************3-basic-info",
                        "symbolSize": 40,
                        "value": "基本信息",
                        "category": -11,
                        "draggable": true,
                        "label": {
                            "normal": {
                                "show": true,
                                "position": "inside"
                            }
                        },
                        "legendName": "基本信息",
                        "nodeType": 1
                    },
                    {
                        "name": "张三",
                        "symbolSize": 20,
                        "value": "张三",
                        "category": -2,
                        "draggable": true,
                        "legendName": "姓名",
                        "nodeType": 0
                    },
                    {
                        "name": "3****************3-contact",
                        "symbolSize": 40,
                        "value": "联系方式",
                        "category": -13,
                        "draggable": true,
                        "label": {
                            "normal": {
                                "show": true,
                                "position": "inside"
                            }
                        },
                        "legendName": "联系方式",
                        "nodeType": 1
                    },
                    {
                        "name": "145157****@qq.com",
                        "symbolSize": 20,
                        "value": "145157****@qq.com",
                        "category": -7,
                        "draggable": true,
                        "legendName": "邮箱",
                        "nodeType": 0
                    },
                    {
                        "name": "14515783**",
                        "symbolSize": 20,
                        "value": "14515783**",
                        "category": -8,
                        "draggable": true,
                        "legendName": "qq",
                        "nodeType": 0
                    }
                ]
            }
        ],
        "legend": {
            "data": [
                "用户",
                "身份证",
                "姓名",
                "性别",
                "生日",
                "手机",
                "固定电话",
                "邮箱",
                "qq",
                "地址",
                "银行卡",
                "基本信息",
                "地址分类",
                "联系方式",
                "银行卡分类"
            ]
        },
        "title": [
            {
                "left": "left",
                "text": "人员关系图"
            }
        ]
    };
    echart.setOption(options);
    bindChartClickEvent(echart);

    /**
     * 获取颜色
     * @param colors
     * @param index
     * @returns {*}
     */
    function getColor(colors, index) {
        var length = colors.length,
            colorIndex = index;
        if (index >= length) {
            colorIndex = length - index;
        }
        return colors[colorIndex];
    }

    /**
     * 绑定图表的点击事件
     * @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);
            }
        }
    }
</script>
</body>
</html>
posted @ 2018-04-09 17:09  huan1993  阅读(232)  评论(0编辑  收藏  举报