目录
一、需求背景与效果
二、echarts源码
一、需求背景与效果
根据服务关系制作关系图谱,echarts官方关系图没有想要的案例,网上也没有好的效果,故参考网上已有的部分案例,自行写了一个,效果还不错。
当前echarts版本信息:
话不多说,直接上效果:
1、默认展示查询到的所有路径列表
2、点击路径,可展开详细信息
二、echarts源码
源码与案例数据:
let data = {
nodes: [{
"cNumber": 20207,
"category": 0,
"name": "custom/data/service1-aliyun/vault-activemq"
}, {
"cNumber": 20207,
"category": 1,
"name": "service1-aliyun"
}, {
"cNumber": 2248,
"category": 2,
"name": "192.168.44.73"
}, {
"cNumber": 2247,
"category": 2,
"name": "192.168.44.76"
}, {
"cNumber": 2247,
"category": 2,
"name": "192.168.48.186"
}, {
"cNumber": 2246,
"category": 2,
"name": "192.168.44.77"
}, {
"cNumber": 2244,
"category": 2,
"name": "192.168.44.74"
}, {
"cNumber": 2244,
"category": 2,
"name": "192.168.44.75"
}, {
"cNumber": 2244,
"category": 2,
"name": "192.168.48.184"
}, {
"cNumber": 2244,
"category": 2,
"name": "192.168.48.187"
}, {
"cNumber": 2243,
"category": 2,
"name": "192.168.48.185"
}, {
"cNumber": 9743,
"category": 0,
"name": "cloud/data/devops-service1/tencent-service2"
}, {
"cNumber": 9743,
"category": 1,
"name": "devops-service1"
}, {
"cNumber": 2244,
"category": 2,
"name": "192.168.129.172"
}, {
"cNumber": 1893,
"category": 2,
"name": "192.168.0.117"
}, {
"cNumber": 1844,
"category": 2,
"name": "192.168.6.149"
}, {
"cNumber": 3869,
"category": 0,
"name": "custom/data/service1-tencent/vault-activemq"
}, {
"cNumber": 3869,
"category": 1,
"name": "service1-tencent"
}, {
"cNumber": 1926,
"category": 2,
"name": "192.169.35.217"
}, {
"cNumber": 150,
"category": 2,
"name": "192.169.32.2"
}, {
"cNumber": 150,
"category": 2,
"name": "192.169.33.118"
}, {
"cNumber": 150,
"category": 2,
"name": "192.169.33.59"
}, {
"cNumber": 150,
"category": 2,
"name": "192.169.34.153"
}, {
"cNumber": 150,
"category": 2,
"name": "192.169.34.99"
}, {
"cNumber": 150,
"category": 2,
"name": "192.169.35.122"
}, {
"cNumber": 149,
"category": 2,
"name": "192.169.32.151"
}, {
"cNumber": 149,
"category": 2,
"name": "192.169.33.165"
}, {
"cNumber": 149,
"category": 2,
"name": "192.169.33.197"
}, {
"cNumber": 149,
"category": 2,
"name": "192.169.33.238"
}, {
"cNumber": 149,
"category": 2,
"name": "192.169.33.84"
}, {
"cNumber": 149,
"category": 2,
"name": "192.169.35.152"
}, {
"cNumber": 149,
"category": 2,
"name": "192.169.35.172"
}, {
"cNumber": 9743,
"category": 0,
"name": "custom/data/devops-service1/ci-jiratool"
}, {
"cNumber": 1906,
"category": 2,
"name": "192.168.13.11"
}, {
"cNumber": 1856,
"category": 2,
"name": "192.168.13.95"
}, {
"cNumber": 9743,
"category": 0,
"name": "cloud/data/devops-service1/tencent-service1"
}],
links: [{
"name": "20207",
"source": "service1-aliyun",
"target": "custom/data/service1-aliyun/vault-activemq"
}, {
"name": "2248",
"source": "192.168.44.73",
"target": "service1-aliyun"
}, {
"name": "2247",
"source": "192.168.44.76",
"target": "service1-aliyun"
}, {
"name": "2247",
"source": "192.168.48.186",
"target": "service1-aliyun"
}, {
"name": "2246",
"source": "192.168.44.77",
"target": "service1-aliyun"
}, {
"name": "2244",
"source": "192.168.44.74",
"target": "service1-aliyun"
}, {
"name": "2244",
"source": "192.168.44.75",
"target": "service1-aliyun"
}, {
"name": "2244",
"source": "192.168.48.184",
"target": "service1-aliyun"
}, {
"name": "2244",
"source": "192.168.48.187",
"target": "service1-aliyun"
}, {
"name": "2243",
"source": "192.168.48.185",
"target": "service1-aliyun"
}, {
"name": "9743",
"source": "devops-service1",
"target": "cloud/data/devops-service1/tencent-service2"
}, {
"name": "2244",
"source": "192.168.129.172",
"target": "devops-service1"
}, {
"name": "1893",
"source": "192.168.0.117",
"target": "devops-service1"
}, {
"name": "1844",
"source": "192.168.6.149",
"target": "devops-service1"
}, {
"name": "3869",
"source": "service1-tencent",
"target": "custom/data/service1-tencent/vault-activemq"
}, {
"name": "1926",
"source": "192.169.35.217",
"target": "service1-tencent"
}, {
"name": "150",
"source": "192.169.32.2",
"target": "service1-tencent"
}, {
"name": "150",
"source": "192.169.33.118",
"target": "service1-tencent"
}, {
"name": "150",
"source": "192.169.33.59",
"target": "service1-tencent"
}, {
"name": "150",
"source": "192.169.34.153",
"target": "service1-tencent"
}, {
"name": "150",
"source": "192.169.34.99",
"target": "service1-tencent"
}, {
"name": "150",
"source": "192.169.35.122",
"target": "service1-tencent"
}, {
"name": "149",
"source": "192.169.32.151",
"target": "service1-tencent"
}, {
"name": "149",
"source": "192.169.33.165",
"target": "service1-tencent"
}, {
"name": "149",
"source": "192.169.33.197",
"target": "service1-tencent"
}, {
"name": "149",
"source": "192.169.33.238",
"target": "service1-tencent"
}, {
"name": "149",
"source": "192.169.33.84",
"target": "service1-tencent"
}, {
"name": "149",
"source": "192.169.35.152",
"target": "service1-tencent"
}, {
"name": "149",
"source": "192.169.35.172",
"target": "service1-tencent"
}, {
"name": "9743",
"source": "devops-service1",
"target": "custom/data/devops-service1/ci-jiratool"
}, {
"name": "1906",
"source": "192.168.13.11",
"target": "devops-service1"
}, {
"name": "1856",
"source": "192.168.13.95",
"target": "devops-service1"
}, {
"name": "9743",
"source": "devops-service1",
"target": "cloud/data/devops-service1/tencent-service1"
}]
}
const color1 = '#006acc';
const color2 = '#ff7d18';
const color3 = '#45b97c';
data.nodes.forEach(node => {
if (node.category === 0) {
node.symbolSize = 70;
node.itemStyle = {
color: color1
};
} else if (node.category === 1) {
node.symbolSize = 50;
node.itemStyle = {
color: color2
};
} else if (node.category === 2) {
node.symbolSize = 30;
node.itemStyle = {
color: color3
};
}
node.symbolSize = node.symbolSize + node.cNumber/1000
node.open = false
});
data.links.forEach(link => {
link.label = {
align: 'center',
fontSize: 12
};
});
let categories = [{
name: '路径',
itemStyle: {
color: color1
}
},
{
name: '目标对象',
itemStyle: {
color: color2
}
},
{
name: '客户端IP',
itemStyle: {
color: color3
}
}]
option = {
title: {
text: '关系图谱',
},
legend: [{
data: categories.map(x => x.name),
}],
series: [{
type: 'graph',
layout: 'force',
symbolSize: 58,
draggable: true,
roam: true,
focusNodeAdjacency: true,
categories: categories,
edgeSymbol: ['', 'arrow'],
// edgeSymbolSize: [80, 10],
edgeLabel: {
normal: {
show: true,
textStyle: {
fontSize: 20
},
formatter(x) {
return x.data.name;
}
}
},
label: {
fontSize: 12,
show: true
},
force: {
repulsion: 2000,
edgeLength: 120
},
data: data.nodes,
links: data.links
}]
}
myChart.setOption(option);
bindChartClickEvent(myChart);
/**
* 绑定图表的点击事件
* @param chart
*/
function bindChartClickEvent(chart) {
chart.on('click', function (params) {
var category = params.data.category,
nodeType = params.data.nodeType;
toggleShowNodes(chart, params);
});
for (var j = 0; j < data.nodes.length; j++) {
if (data.nodes[j].category !== 0 ) {
data.nodes[j].data = {};
data.nodes[j].seriesIndex=0;
data.nodes[j].open=false;
data.nodes[j].category=-1;
toggleShowNodes(chart, data.nodes[j]);
}
}
}
/**
* 展开或关闭节点
* @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);
}
}
serieDataMap.get(srcLinkName).open = false;
chart.setOption(options);
}
} else {
// 当前根节点是关闭的,那么就需要展开第一层根节点
findLinks(serieLinkArr, srcLinkName, serieLinks, true);
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.target === srcLinkName).forEach(link => {
targetLinks.push(link.source);
links.push(link.source)
});
if (deep) {
for (var i = 0; i < targetLinks.length; i++) {
findLinks(links, targetLinks[i], serieLinks, deep);
}
}
}