ECharts关系图,节点可拖拽,添加节点和边的点击事件


来源于  https://blog.csdn.net/Mu_Yue_Yue/article/details/90375758

 

最近在做一个neo4j前端可视化的任务,对比了ECharts.js,Cytoscape.js和D3.js几个前端可视化工具,最终选择了ECharts.js,百度的一款开源工具,简单好用,比较容易上手。后台采用Python的Django框架,主要用于接收前端的请求,从neo4j数据库获取数据,并将数据传给前端服务器展示。这里只展示ECharts的用法以及自己的一些总结,为以后打下一个基础。首先展示下效果图:

 

EChart.js下载:https://www.echartsjs.com/download.html
        点击进入下载页面后,选择一个版本,点击进入相应的github地址,在点击source code(zip),下载完成后将其解压,在dist文件夹下又一个echarts.js文件,这个文件就是我们需要的,将其复制到项目中相应的js目录中即可。

 

        

由于这里采用的布局是force,不是通过坐标进行节点的生成,所以不能参考网上其他的采用坐标的节点拖拽。对此百度了许多,最后找到了一种合适的方法。我们需要对源码进行一些修改,只需要注释掉一行代码即可进行拖拽,这个方法主要参考另一位作者:https://blog.csdn.net/qq_38880340/article/details/85683322。打开源码,大概在1317行,将下图所示注释掉之后,选择保存即可。       

我们在echarts.js同级目录下新建一个html文件,在head中引入echarts.js,在body中设置一个div,一定要设置这个div的宽和高,不然不会显示出关系图,完整代码如下所示。

        

  1 <!DOCTYPE html>
  2 <html lang="en">
  3 <head>
  4 <meta charset="UTF-8">
  5 <title>Echarts测试</title>
  6 <script src="echarts.js"></script>
  7 </head>
  8 <body>
  9 <div id="chart1" style="width: 1600px; height: 900px; margin: 0 auto; border: 2px solid #FF0000;"></div>
 10 </body>
 11 
 12 <script type="text/javascript">
 13 var entityRelation = [{"r": {"name": "推荐食谱"}, "m": {"name": "绿豆薏米饭"}}, {"r": {"name": "推荐食谱"}, "m": {"name": "姜丝萝卜汤"}},
 14 {"r": {"name": "推荐食谱"}, "m": {"name": "葱蒜粥"}}, {"r": {"name": "推荐食谱"}, "m": {"name": "薏米莲子粥"}},
 15 {"r": {"name": "推荐食谱"}, "m": {"name": "赤小豆粥"}}, {"r": {"name": "推荐食谱"}, "m": {"name": "香椿芽粥"}},
 16 {"r": {"name": "推荐食谱"}, "m": {"name": "凉拌香椿"}}, {"r": {"name": "推荐食谱"}, "m": {"name": "醋熘土豆丝"}},
 17 {"r": {"name": "忌吃"}, "m": {"name": "猪油(板油)"}}, {"r": {"name": "忌吃"}, "m": {"name": "咸鱼"}}, {"r": {"name": "忌吃"}, "m": {"name": "白扁豆"}},
 18 {"r": {"name": "忌吃"}, "m": {"name": "油条"}}, {"r": {"name": "宜吃"}, "m": {"name": "芝麻"}}, {"r": {"name": "宜吃"}, "m": {"name": "鸡蛋"}},
 19 {"r": {"name": "宜吃"}, "m": {"name": "南瓜子仁"}}, {"r": {"name": "宜吃"}, "m": {"name": "鹌鹑蛋"}}, {"r": {"name": "所属科室"}, "m": {"name": "呼吸内科"}},
 20 {"r": {"name": "常用药品"}, "m": {"name": "感冒灵颗粒"}}, {"r": {"name": "常用药品"}, "m": {"name": "利巴韦林颗粒"}}, {"r": {"name": "好评药品"}, "m": {"name": "伤风停胶囊"}},
 21 {"r": {"name": "好评药品"}, "m": {"name": "感冒灵颗粒"}}, {"r": {"name": "好评药品"}, "m": {"name": "喉痛灵片"}}, {"r": {"name": "好评药品"}, "m": {"name": "阿莫西林颗粒"}},
 22 {"r": {"name": "好评药品"}, "m": {"name": "洛索洛芬钠胶囊"}}, {"r": {"name": "好评药品"}, "m": {"name": "酚咖片"}},
 23 {"r": {"name": "好评药品"}, "m": {"name": "洛索洛芬钠片"}}, {"r": {"name": "好评药品"}, "m": {"name": "风油精"}}, {"r": {"name": "好评药品"}, "m": {"name": "匹多莫德分散片"}},
 24 {"r": {"name": "好评药品"}, "m": {"name": "依托红霉素片"}}, {"r": {"name": "好评药品"}, "m": {"name": "穿心莲片"}}, {"r": {"name": "好评药品"}, "m": {"name": "头孢丙烯分散片"}},
 25 {"r": {"name": "好评药品"}, "m": {"name": "麻黄止嗽丸"}}, {"r": {"name": "好评药品"}, "m": {"name": "肺宁片"}}, {"r": {"name": "好评药品"}, "m": {"name": "抗病毒口服液"}},
 26 {"r": {"name": "好评药品"}, "m": {"name": "消炎片"}}, {"r": {"name": "好评药品"}, "m": {"name": "头孢拉定胶囊"}}, {"r": {"name": "好评药品"}, "m": {"name": "蒲公英颗粒"}},
 27 {"r": {"name": "好评药品"}, "m": {"name": "银芩胶囊"}}, {"r": {"name": "好评药品"}, "m": {"name": "愈美胶囊"}}, {"r": {"name": "诊断检查"}, "m": {"name": "白细胞计数(WBC)"}},
 28 {"r": {"name": "诊断检查"}, "m": {"name": "血常规"}}, {"r": {"name": "诊断检查"}, "m": {"name": "肺和胸膜听诊"}}, {"r": {"name": "诊断检查"}, "m": {"name": "尿常规"}},
 29 {"r": {"name": "诊断检查"}, "m": {"name": "内科检查"}}, {"r": {"name": "症状"}, "m": {"name": "鼻塞"}}, {"r": {"name": "症状"}, "m": {"name": "头痛"}},
 30 {"r": {"name": "症状"}, "m": {"name": "浑身忽冷忽热"}}, {"r": {"name": "症状"}, "m": {"name": "情绪性感冒"}}, {"r": {"name": "症状"}, "m": {"name": "咽喉干燥及灼热感"}},
 31 {"r": {"name": "症状"}, "m": {"name": "发烧"}}, {"r": {"name": "症状"}, "m": {"name": "发热伴寒战"}}, {"r": {"name": "症状"}, "m": {"name": "咽痛"}},
 32 {"r": {"name": "症状"}, "m": {"name": "流鼻涕"}}];
 33 // entityRelation中的r代表的关系,m代表的是第二个实体,这个数据是从neo4j数据库查询后进行json格式化后的,原数据过多,就删减了一部分。
 34 var key_word = '感冒';
 35 
 36 var data = []; // echarts中的节点数组
 37 var links = []; // echarts中边的数组
 38 
 39 // 获取实体1, 也就是中心节点
 40 var node = {} ;
 41 node['name'] = key_word;
 42 node['draggable'] = true; // 设置中心节点是否可以拖拽
 43 var id = 0;
 44 node['id'] = id.toString();
 45 data.push(node);
 46 
 47 // 获取实体2, 存储在data中, 如果实体2已经存在于data中, 则不push
 48 var maxDisPlayNode = 16; // 对展示节点的数量进行限制
 49 // console.log("实体长度----->", entityRelation.length); // 显示关系个数
 50 for(var i = 0; i < Math.min(maxDisPlayNode, entityRelation.length); i++) {
 51 node = {};
 52 node['name'] = entityRelation[i]['m']['name'];
 53 node['draggable'] = true;
 54 
 55 if(entityRelation[i]['r']['name'] == '所属科室') {
 56 node['category'] = 1; // 节点分类, 数字代表option.series.categories中的索引
 57 } else if (entityRelation[i]['r']['name'] == '常用药品') {
 58 node['category'] = 2;
 59 } else if (entityRelation[i]['r']['name'] == '宜吃') {
 60 node['category'] = 3;
 61 } else if (entityRelation[i]['r']['name'] == '诊断检查') {
 62 node['category'] = 4;
 63 } else if (entityRelation[i]['r']['name'] == '忌吃') {
 64 node['category'] = 5;
 65 } else if (entityRelation[i]['r']['name'] == '好评药品') {
 66 node['category'] = 6;
 67 } else if (entityRelation[i]['r']['name'] == '推荐食谱') {
 68 node['category'] = 7;
 69 } else if (entityRelation[i]['r']['name'] == '症状') {
 70 node['category'] = 8;
 71 } else if (entityRelation[i]['r']['name'] == '并发症') {
 72 node['category'] = 9;
 73 } else if (entityRelation[i]['r']['name'] == '生产药品') {
 74 node['category'] = 10;
 75 }
 76 
 77 id = i + 1;
 78 node['id'] = id.toString();
 79 
 80 var flag = 1;
 81 relationTarget = id.toString();
 82 for (var j = 0; j < data.length; j++) {
 83 if (data[j]['name'] === node['name']) {
 84 flag = 0;
 85 relationTarget = data[j]['id'];
 86 break;
 87 }
 88 }
 89 
 90 relation = {};
 91 relation['source'] = 0; // 关系起点, 数字代表的是节点的id, 即node[id]
 92 relation['target'] = relationTarget; // 关系指向的终点, 也是id, 即通过节点的id描述两个节点以及关系
 93 relation['category'] = 0;
 94 
 95 if (flag === 1) {
 96 data.push(node);
 97 relation['value'] = entityRelation[i]['r']['name'];
 98 relation['symbolSize'] = 10;
 99 links.push(relation);
100 } else {
101 maxDisPlayNode += 1;
102 for (var k = 0; k < links.length; k++) {
103 if (links[k]['target'] === relationTarget) {
104 links[k]['value'] = links[k]['value'] + " | " + entityRelation[i]['r']['name'];
105 break;
106 }
107 }
108 }
109 }
110 // console.log("data====>>>", data);
111 // console.log("links====>>>", links);
112 
113 // 初始化echarts实例
114 var myChart = echarts.init(document.getElementById("chart1"));
115 
116 option = {
117 title: {
118 text: ''
119 },
120 tooltip: {},
121 animationDurationUpdate: 1500,
122 animationEasingUpdate: 'quinticInOut',
123 label: {
124 normal: {
125 show: true,
126 textStyle: {
127 fontSize: 12
128 },
129 }
130 },
131 legend: {
132 x: "center",
133 show: false
134 },
135 series: [
136 
137 {
138 type: 'graph',
139 layout: 'force',
140 symbolSize: 45,
141 focusNodeAdjacency: false, // 是否在鼠标移到节点上的时候突出显示节点以及节点的边和邻接节点, 默认是true
142 roam: true,
143 edgeSymbol: ['none', 'arrow'],
144 categories: [{
145 name: '查询实体',
146 itemStyle: {
147 normal: {
148 color: "#FF0000",
149 }
150 }
151 }, {
152 name: '所属科室',
153 itemStyle: {
154 normal: {
155 color: "#4592FF",
156 }
157 }
158 }, {
159 name: '常用药品',
160 itemStyle: {
161 normal: {
162 color: "#FFFF00",
163 }
164 }
165 }, {
166 name: '宜吃',
167 itemStyle: {
168 normal: {
169 color: "#66CD00",
170 }
171 }
172 }, {
173 name: '诊断检查',
174 itemStyle: {
175 normal: {
176 color: "#B8860B",
177 }
178 }
179 }, {
180 name: '忌吃',
181 itemStyle: {
182 normal: {
183 color: "#B23AEE",
184 }
185 }
186 }, {
187 name: '好评药品',
188 itemStyle: {
189 normal: {
190 color: "#FFB90F",
191 }
192 }
193 }, {
194 name: '推荐食谱',
195 itemStyle: {
196 normal: {
197 color: "#7FFF00",
198 }
199 }
200 },{
201 name: '症状',
202 itemStyle: {
203 normal: {
204 color: "#F08080",
205 }
206 }
207 }, {
208 name: '并发症',
209 itemStyle: {
210 normal: {
211 color: "#87CEFA",
212 }
213 }
214 }, {
215 name: '生产药品',
216 itemStyle: {
217 normal: {
218 color: "#FFFF00",
219 }
220 }
221 }],
222 label: {
223 normal: {
224 show: true,
225 textStyle: {
226 fontSize: 12,
227 fontStyle : 'normal',
228 fontWeight : 'bolder',
229 fontFamily : 'sans-serif',
230 },
231 }
232 },
233 force: {
234 repulsion: 1000,
235 edgeLength: [150, 100],
236 layoutAnimation : false
237 // 因为力引导布局会在多次迭代后才会稳定, 这个参数决定是否显示布局的迭代动画(节点数量过多, 图在迭代的过程中会旋转),
238 // 在浏览器端节点数据较多(>100)的时候不建议关闭, 布局过程会造成浏览器假死。
239 },
240 edgeSymbolSize: [4, 50],
241 edgeLabel: {
242 normal: {
243 show: true,
244 textStyle: {
245 fontSize: 10
246 },
247 formatter: "{c}" // 标签内容格式器。模板变量有 {a}、{b}、{c},分别表示系列名,数据名,数据值。
248 }
249 },
250 data: data, // 节点
251 links: links, // 边或者关系
252 lineStyle: {
253 normal: {
254 opacity: 0.5,
255 width: 1.0,
256 curveness: 0,
257 color: "#262626"
258 }
259 }
260 }
261 ]
262 };
263 myChart.setOption(option); // 这步很重要, 必须设置才可以有效
264 myChart.on('click', function (param){
265 console.log('param---->', param); // 打印出param, 可以看到里边有很多参数可以使用
266 //获取节点点击的数组序号
267 var arrayIndex = param.dataIndex;
268 console.log('arrayIndex---->', arrayIndex);
269 console.log('name---->', param.name);
270 if (param.dataType == 'node') {
271 alert("点击了节点" + param.name)
272 } else {
273 alert("点击了边" + param.value)
274 }
275 });
276 </script>
277 
278 </html>

 


        其中的entityRelation即为我们从后台获得的经过处理后的数据(这里只是为了展示最后的关系图,所以直接将数据写在了这里),r 代表的关系,m 代表的是第二个实体,原数据过多,就删除了其中的一部分。key_word为第一个实体,也是中心节点,entityRelation中的 r 指的就是中心点key_word到第二个实体 m 的关系。

        data[]是echart中所有的节点的数组,包括所有的实体,links[]即为实体之间所有关系的数组。每个节点都有id,name,category等参数,category主要用于节点(实体)的分类,不同种类的实体用不同的颜色区分出来,相关注释可以在代码中查看。relation主要描述的是起始节点和指向的结束节点,是links[]数组的参数。

       echarts中的参数过多,可以参考官方文档,或者下载的source code中的例子,或者参考下边的文章,对echarts的参数进行了总结:http://www.cnblogs.com/koala2016/archive/2016/12/01/6123003.html。

点击事件:
        对节点和边添加点击事件,主要通过click方法实现,代码可以参考上边给出的。可以通过将param打印出来查看里边的具体参数,更方便下一步的处理。
————————————————
版权声明:本文为CSDN博主「沐玥」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Mu_Yue_Yue/article/details/90375758


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM