├── .gitignore ├── display_img └── display_force1.gif ├── 力向导图(forceSimulation) ├── d3_v5_forceSimulation.html └── main.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | -------------------------------------------------------------------------------- /display_img/display_force1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BATFOR/D3.js_Visualization/HEAD/display_img/display_force1.gif -------------------------------------------------------------------------------- /力向导图(forceSimulation)/d3_v5_forceSimulation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | D3_study 7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 此代码基于前端可视化框架[d3.js](https://github.com/xswei/d3js_doc)。 2 | d3个人感觉和JQuery很像,主要对DOM进行数据绑定且进行相关操作。在学习D3之前,除了对HTML、css、js有相关应用了解外,还需对[SVG](https://www.runoob.com/svg/svg-rect.html)进行相关了解。d3中大部分函数库都是对数据进行处理 3 | ,然后将这这些处理了数据应用到画图展示上。 4 | 5 | ### 力向导图 ### 6 | **使用d3 v5版** 7 | **参考:** 此代码主要参考[https://github.com/zhaoluo123/vue-d3-force](https://github.com/zhaoluo123/vue-d3-force),去除了其中的vue模块,将d3版本从v3提升到v5。 8 | 9 | 力向导图可视,主要采用d3中力学仿真([**forceSimulation**](https://github.com/xswei/d3-force/blob/master/README.md#forceSimulation))模块,这个模块主要功能就是对数据不断进行仿真(可以根据需求添加多个具体的仿真模型,通过force函数),它会将每次仿真结果直接 10 | 以属性的方式直接加入(更新)到用户传入的**节点对象和边对象数据**中,比如会添加节点的x,y坐标等,每次仿真都会触发tick事件。而开发者需要做的就是在tick事件中进行图形的绘制渲染。 11 | 另外,[选择器(selection)](https://github.com/xswei/d3-selection/blob/master/README.md#selection)模块是d3中最常用的模块,和jQuery选择器一样,可对页面元素进行获取、属性更改、数据绑定等操作。 12 | 13 | **功能:** 14 | 1. 节点之间多关系绘制,节点自我关系绘制 15 | 2. 节点拖动放缩 16 | 3. 节点隐藏、解除固定(初始全部固定、拖动节点会触发当前节点固定)、删除(只编写函数接口)功能 17 | ![效果展示](https://github.com/BATFOR/D3.js_Visualization/blob/master/display_img/display_force1.gif?raw=true) 18 | -------------------------------------------------------------------------------- /力向导图(forceSimulation)/main.js: -------------------------------------------------------------------------------- 1 | /***设备联系***/ 2 | 3 | getNodeSelfPath = d => { 4 | if (d.relation.lineNumber === 1) { 5 | return "M" + (d.target.x + 25) + " " + (d.target.y - 10) + " C " + (d.target.x + 125) + " " + (d.target.y - 60) + 6 | "," + (d.target.x + 125) + " " + (d.target.y + 60) + "," + (d.target.x + 30) + " " + (d.target.y + 10); 7 | } 8 | if (d.relation.lineNumber === 2) { 9 | return "M" + (d.target.x - 10) + " " + (d.target.y - 25) + " C " + (d.target.x - 60) + " " + (d.target.y - 125) + 10 | "," + (d.target.x + 60) + " " + (d.target.y - 125) + "," + (d.target.x + 10) + " " + (d.target.y - 30); 11 | } 12 | if (d.relation.lineNumber === 3) { 13 | return "M" + (d.target.x - 25) + " " + (d.target.y + 10) + " C " + (d.target.x - 125) + " " + (d.target.y + 60) + 14 | "," + (d.target.x - 125) + " " + (d.target.y - 60) + "," + (d.target.x - 30) + " " + (d.target.y - 10); 15 | } 16 | if (d.relation.lineNumber === 4) { 17 | return "M" + (d.target.x + 10) + " " + (d.target.y + 25) + " C " + (d.target.x + 60) + " " + (d.target.y + 125) + 18 | "," + (d.target.x - 60) + " " + (d.target.y + 125) + "," + (d.target.x - 10) + " " + (d.target.y + 30); 19 | } 20 | }; 21 | 22 | getNodesLine = d => { 23 | 24 | d.sourceRadius = 26; 25 | d.targetRadius = 31; 26 | let tan = Math.abs((d.target.y - d.source.y) / (d.target.x - d.source.x)); //圆心连线tan值 27 | let x1 = d.target.x - d.source.x > 0 ? Math.sqrt(d.sourceRadius * d.sourceRadius / (tan * tan + 1)) + d.source.x : 28 | d.source.x - Math.sqrt(d.sourceRadius * d.sourceRadius / (tan * tan + 1)); //起点x坐标 29 | let y1 = d.target.y - d.source.y > 0 ? Math.sqrt(d.sourceRadius * d.sourceRadius * tan * tan / (tan * tan + 1)) + d.source.y : 30 | d.source.y - Math.sqrt(d.sourceRadius * d.sourceRadius * tan * tan / (tan * tan + 1)); //起点y坐标 31 | let x2 = d.target.x - d.source.x > 0 ? d.target.x - Math.sqrt(d.targetRadius * d.targetRadius / (1 + tan * tan)) : 32 | d.target.x + Math.sqrt(d.targetRadius * d.targetRadius / (1 + tan * tan)); //终点x坐标 33 | let y2 = d.target.y - d.source.y > 0 ? d.target.y - Math.sqrt(d.targetRadius * d.targetRadius * tan * tan / (1 + tan * tan)) : 34 | d.target.y + Math.sqrt(d.targetRadius * d.targetRadius * tan * tan / (1 + tan * tan)); //终点y坐标 35 | if (d.target.x - d.source.x === 0 || tan === 0) { //斜率无穷大的情况或为0时 36 | y1 = d.target.y - d.source.y > 0 ? d.source.y + d.sourceRadius : d.source.y - d.sourceRadius; 37 | y2 = d.target.y - d.source.y > 0 ? d.target.y - d.targetRadius : d.target.y + d.targetRadius; 38 | } 39 | if (d.linknum === 0) { //设置编号为0的连接线为直线,其他连接线会均分在两边 40 | d.x_start = x1; 41 | d.y_start = y1; 42 | d.x_end = x2; 43 | d.y_end = y2; 44 | return 'M' + x1 + ' ' + y1 + ' L ' + x2 + ' ' + y2; 45 | } 46 | let a = d.sourceRadius > d.targetRadius ? d.targetRadius * d.linknum / 3 : d.sourceRadius * d.linknum / 3; 47 | let xm = d.target.x - d.source.x > 0 ? d.source.x + Math.sqrt((d.sourceRadius * d.sourceRadius - a * a) / (1 + tan * tan)) : 48 | d.source.x - Math.sqrt((d.sourceRadius * d.sourceRadius - a * a) / (1 + tan * tan)); 49 | let ym = d.target.y - d.source.y > 0 ? d.source.y + Math.sqrt((d.sourceRadius * d.sourceRadius - a * a) * tan * tan / (1 + tan * tan)) : 50 | d.source.y - Math.sqrt((d.sourceRadius * d.sourceRadius - a * a) * tan * tan / (1 + tan * tan)); 51 | let xn = d.target.x - d.source.x > 0 ? d.target.x - Math.sqrt((d.targetRadius * d.targetRadius - a * a) / (1 + tan * tan)) : 52 | d.target.x + Math.sqrt((d.targetRadius * d.targetRadius - a * a) / (1 + tan * tan)); 53 | let yn = d.target.y - d.source.y > 0 ? d.target.y - Math.sqrt((d.targetRadius * d.targetRadius - a * a) * tan * tan / (1 + tan * tan)) : 54 | d.target.y + Math.sqrt((d.targetRadius * d.targetRadius - a * a) * tan * tan / (1 + tan * tan)); 55 | if (d.target.x - d.source.x === 0 || tan === 0) { //斜率无穷大或为0时 56 | ym = d.target.y - d.source.y > 0 ? d.source.y + Math.sqrt(d.sourceRadius * d.sourceRadius - a * a) : d.source.y - Math.sqrt(d.sourceRadius * d.sourceRadius - a * a); 57 | yn = d.target.y - d.source.y > 0 ? d.target.y - Math.sqrt(d.targetRadius * d.targetRadius - a * a) : d.target.y + Math.sqrt(d.targetRadius * d.targetRadius - a * a); 58 | } 59 | 60 | let k = (x1 - x2) / (y2 - y1); //连线垂线的斜率 61 | let dx = Math.sqrt(a * a / (1 + k * k)); //相对垂点x轴距离 62 | let dy = Math.sqrt(a * a * k * k / (1 + k * k)); //相对垂点y轴距离 63 | if ((y2 - y1) === 0) { 64 | dx = 0; 65 | dy = Math.sqrt(a * a); 66 | } 67 | let xs, ys, xt, yt; 68 | if (a > 0) { 69 | xs = k > 0 ? xm - dx : xm + dx; 70 | ys = ym - dy; 71 | xt = k > 0 ? xn - dx : xn + dx; 72 | yt = yn - dy; 73 | } else { 74 | xs = k > 0 ? xm + dx : xm - dx; 75 | ys = ym + dy; 76 | xt = k > 0 ? xn + dx : xn - dx; 77 | yt = yn + dy; 78 | } 79 | 80 | //记录连线起始和终止坐标,用于定位线上文字 81 | d.x_start = xs; 82 | d.y_start = ys; 83 | d.x_end = xt; 84 | d.y_end = yt; 85 | 86 | //return 'M' + xs + ' ' + ys + "L" + + xt + ' ' + yt;//绘制直线 87 | let NodesDistance = Math.sqrt(Math.pow(d.source.x - d.target.x, 2) + Math.pow(d.source.y - d.target.y, 2)); 88 | let rad = 200; 89 | if (300 >= NodesDistance && NodesDistance > 150) { 90 | rad = 300 91 | } else if (450 >= NodesDistance && NodesDistance > 300) { 92 | rad = 400 93 | } else if (600 >= NodesDistance && NodesDistance > 450) { 94 | rad = 500 95 | } else if (750 >= NodesDistance && NodesDistance > 600) { 96 | rad = 600 97 | } else if (900 >= NodesDistance && NodesDistance > 750) { 98 | rad = 700 99 | } else if (1050 >= NodesDistance && NodesDistance > 900) { 100 | rad = 800 101 | } else if (1200 >= NodesDistance && NodesDistance > 1050) { 102 | rad = 900 103 | } 104 | 105 | if (d.source.x < d.target.x) { //绘制曲线 106 | if (d.linknum < 0) { 107 | return "M" + xs + "," + ys + "A" + rad + "," + rad + " 0 0,0 " + xt + "," + yt; 108 | } else { 109 | return "M" + xs + "," + ys + "A" + rad + "," + rad + " 0 0,1 " + xt + "," + yt; 110 | } 111 | } else { 112 | if (d.linknum < 0) { 113 | return "M" + xs + "," + ys + "A" + rad + "," + rad + " 0 0,1 " + xt + "," + yt; 114 | } else { 115 | return "M" + xs + "," + ys + "A" + rad + "," + rad + " 0 0,0 " + xt + "," + yt; 116 | } 117 | } 118 | }; 119 | 120 | setLinkNumber = (group) => { 121 | if (group.length === 0) return; 122 | if (group.length === 1) { 123 | group[0].linknum = 0; 124 | return; 125 | } 126 | let maxLinkNumber = group.length % 2 === 0 ? group.length / 2 : (group.length - 1) / 2; 127 | if (group.length % 2 === 0) { 128 | let startLinkNum = -maxLinkNumber + 0.5; 129 | for (let i = 0; i < group.length; i++) { 130 | group[i].linknum = startLinkNum++; 131 | } 132 | } else { 133 | let startLinkNum = -maxLinkNumber; 134 | for (let i = 0; i < group.length; i++) { 135 | group[i].linknum = startLinkNum++; 136 | } 137 | } 138 | }; 139 | 140 | setLinkGroup = links => { 141 | let linkGroup = {}; 142 | //对连接线进行统计和分组,不区分连接线的方向,只要属于同两个实体,即认为是同一组 143 | let linkmap = {}; 144 | for (let i = 0; i < links.length; i++) { 145 | let key = links[i].source < links[i].target ? links[i].source + ':' + links[i].target : links[i].target + ':' + links[i].source; 146 | if (!linkmap.hasOwnProperty(key)) { 147 | linkmap[key] = 0; 148 | } 149 | linkmap[key] += 1; 150 | if (!linkGroup.hasOwnProperty(key)) { 151 | linkGroup[key] = []; 152 | } 153 | linkGroup[key].push(links[i]); 154 | } 155 | //为每一条连接线分配size属性,同时对每一组连接线进行编号 156 | for (let i = 0; i < links.length; i++) { 157 | let key = links[i].source < links[i].target ? links[i].source + ':' + links[i].target : links[i].target + ':' + links[i].source; 158 | links[i].size = linkmap[key]; 159 | //同一组的关系进行编号 160 | let group = linkGroup[key]; 161 | //给节点分配编号 162 | setLinkNumber(group); 163 | } 164 | 165 | }; 166 | 167 | var my_data = { 168 | "links": [{ 169 | "source": 0, 170 | "target": 1, 171 | "relation": { 172 | "relationshipId": 200, 173 | "relationship": "BELONG_APPLICATION", 174 | "created": 1574822650825, 175 | "lineNumber": null, 176 | "isSelf": false 177 | } 178 | }, { 179 | "source": 2, 180 | "target": 3, 181 | "relation": { 182 | "relationshipId": 135, 183 | "relationship": "EFFECT_LOCATION", 184 | "created": 1574821264333, 185 | "lineNumber": null, 186 | "isSelf": false 187 | } 188 | }, { 189 | "source": 6, 190 | "target": 7, 191 | "relation": { 192 | "relationshipId": 123, 193 | "relationship": "BELONG_MANUFACTURER", 194 | "created": 1574143813834, 195 | "lineNumber": null, 196 | "isSelf": false 197 | } 198 | }, { 199 | "source": 8, 200 | "target": 9, 201 | "relation": { 202 | "relationshipId": 132, 203 | "relationship": "PARENT", 204 | "created": 1571109959476, 205 | "lineNumber": null, 206 | "isSelf": false 207 | } 208 | }, { 209 | "source": 10, 210 | "target": 11, 211 | "relation": { 212 | "relationshipId": 145, 213 | "relationship": "AUTHORIZE_APPLICATION", 214 | "created": 1574409066966, 215 | "lineNumber": null, 216 | "isSelf": false 217 | } 218 | }, { 219 | "source": 12, 220 | "target": 13, 221 | "relation": { 222 | "relationshipId": 103, 223 | "relationship": "BELONG_APPLICATION", 224 | "created": 1574229503622, 225 | "lineNumber": null, 226 | "isSelf": false 227 | } 228 | }, { 229 | "source": 14, 230 | "target": 15, 231 | "relation": { 232 | "relationshipId": 95, 233 | "relationship": "AUTHORIZE_APPLICATION", 234 | "created": 1574218526058, 235 | "lineNumber": null, 236 | "isSelf": false 237 | } 238 | }, { 239 | "source": 0, 240 | "target": 16, 241 | "relation": { 242 | "relationshipId": 202, 243 | "relationship": "EFFECT_LOCATION", 244 | "created": 1574825180239, 245 | "lineNumber": null, 246 | "isSelf": false 247 | } 248 | }, { 249 | "source": 19, 250 | "target": 20, 251 | "relation": { 252 | "relationshipId": 180, 253 | "relationship": "CONTROL", 254 | "created": 1574652545781, 255 | "lineNumber": null, 256 | "isSelf": false 257 | } 258 | }, { 259 | "source": 2, 260 | "target": 2, 261 | "relation": { 262 | "relationshipId": 198, 263 | "relationship": "PARENT", 264 | "created": 1574821366951, 265 | "lineNumber": 1, 266 | "isSelf": true 267 | } 268 | }, { 269 | "source": 8, 270 | "target": 22, 271 | "relation": { 272 | "relationshipId": 141, 273 | "relationship": "PARENT", 274 | "created": 1571189421890, 275 | "lineNumber": null, 276 | "isSelf": false 277 | } 278 | }, { 279 | "source": 10, 280 | "target": 10, 281 | "relation": { 282 | "relationshipId": 163, 283 | "relationship": "PARENT", 284 | "created": 1574409116976, 285 | "lineNumber": 1, 286 | "isSelf": true 287 | } 288 | }, { 289 | "source": 9, 290 | "target": 8, 291 | "relation": { 292 | "relationshipId": 131, 293 | "relationship": "CONTROL", 294 | "created": 1571109777550, 295 | "lineNumber": null, 296 | "isSelf": false 297 | } 298 | }, { 299 | "source": 25, 300 | "target": 26, 301 | "relation": { 302 | "relationshipId": 182, 303 | "relationship": "EFFECT_LOCATION", 304 | "created": 1574734111195, 305 | "lineNumber": null, 306 | "isSelf": false 307 | } 308 | }, { 309 | "source": 6, 310 | "target": 6, 311 | "relation": { 312 | "relationshipId": 12, 313 | "relationship": "PARENT", 314 | "created": 1574142984212, 315 | "lineNumber": 1, 316 | "isSelf": true 317 | } 318 | }, { 319 | "source": 8, 320 | "target": 28, 321 | "relation": { 322 | "relationshipId": 60, 323 | "relationship": "PARENT", 324 | "created": 1571189421890, 325 | "lineNumber": null, 326 | "isSelf": false 327 | } 328 | }, { 329 | "source": 29, 330 | "target": 22, 331 | "relation": { 332 | "relationshipId": 118, 333 | "relationship": "PARENT", 334 | "created": 1571281100918, 335 | "lineNumber": null, 336 | "isSelf": false 337 | } 338 | }, { 339 | "source": 30, 340 | "target": 31, 341 | "relation": { 342 | "relationshipId": 82, 343 | "relationship": "BELONG_MANUFACTURER", 344 | "created": 1574842004656, 345 | "lineNumber": null, 346 | "isSelf": false 347 | } 348 | }, { 349 | "source": 0, 350 | "target": 32, 351 | "relation": { 352 | "relationshipId": 199, 353 | "relationship": "CONNECT_PLATFORM", 354 | "created": 1574822498689, 355 | "lineNumber": null, 356 | "isSelf": false 357 | } 358 | }, { 359 | "source": 33, 360 | "target": 34, 361 | "relation": { 362 | "relationshipId": 68, 363 | "relationship": "CONTROL", 364 | "created": 1570777779101, 365 | "lineNumber": null, 366 | "isSelf": false 367 | } 368 | }, { 369 | "source": 29, 370 | "target": 35, 371 | "relation": { 372 | "relationshipId": 3, 373 | "relationship": "PARENT", 374 | "created": 1571281100918, 375 | "lineNumber": null, 376 | "isSelf": false 377 | } 378 | }, { 379 | "source": 2, 380 | "target": 36, 381 | "relation": { 382 | "relationshipId": 194, 383 | "relationship": "CONNECT_PLATFORM", 384 | "created": 1574821180519, 385 | "lineNumber": null, 386 | "isSelf": false 387 | } 388 | }, { 389 | "source": 37, 390 | "target": 38, 391 | "relation": { 392 | "relationshipId": 190, 393 | "relationship": "BELONG_ADMIN", 394 | "created": 1574745879106, 395 | "lineNumber": null, 396 | "isSelf": false 397 | } 398 | }, { 399 | "source": 40, 400 | "target": 41, 401 | "relation": { 402 | "relationshipId": 1, 403 | "relationship": "BELONG_MANUFACTURER", 404 | "created": 1574130631578, 405 | "lineNumber": null, 406 | "isSelf": false 407 | } 408 | }, { 409 | "source": 8, 410 | "target": 35, 411 | "relation": { 412 | "relationshipId": 98, 413 | "relationship": "PARENT", 414 | "created": 1571189421890, 415 | "lineNumber": null, 416 | "isSelf": false 417 | } 418 | }, { 419 | "source": 10, 420 | "target": 42, 421 | "relation": { 422 | "relationshipId": 138, 423 | "relationship": "BELONG_APPLICATION", 424 | "created": 1574408689645, 425 | "lineNumber": null, 426 | "isSelf": false 427 | } 428 | }, { 429 | "source": 33, 430 | "target": 34, 431 | "relation": { 432 | "relationshipId": 66, 433 | "relationship": "PARENT", 434 | "created": 1570777635645, 435 | "lineNumber": null, 436 | "isSelf": false 437 | } 438 | }, { 439 | "source": 2, 440 | "target": 2, 441 | "relation": { 442 | "relationshipId": 192, 443 | "relationship": "CONTROL", 444 | "created": 1574821284400, 445 | "lineNumber": 2, 446 | "isSelf": true 447 | } 448 | }, { 449 | "source": 14, 450 | "target": 15, 451 | "relation": { 452 | "relationshipId": 94, 453 | "relationship": "BELONG_APPLICATION", 454 | "created": 1574218478704, 455 | "lineNumber": null, 456 | "isSelf": false 457 | } 458 | }, { 459 | "source": 44, 460 | "target": 9, 461 | "relation": { 462 | "relationshipId": 11, 463 | "relationship": "PARENT", 464 | "created": 1571189497514, 465 | "lineNumber": null, 466 | "isSelf": false 467 | } 468 | }, { 469 | "source": 10, 470 | "target": 45, 471 | "relation": { 472 | "relationshipId": 183, 473 | "relationship": "PARENT", 474 | "created": 1574734473815, 475 | "lineNumber": null, 476 | "isSelf": false 477 | } 478 | }, { 479 | "source": 46, 480 | "target": 47, 481 | "relation": { 482 | "relationshipId": 108, 483 | "relationship": "BELONG_MANUFACTURER", 484 | "created": 1574401675435, 485 | "lineNumber": null, 486 | "isSelf": false 487 | } 488 | }, { 489 | "source": 10, 490 | "target": 48, 491 | "relation": { 492 | "relationshipId": 185, 493 | "relationship": "EFFECT_LOCATION", 494 | "created": 1574735098436, 495 | "lineNumber": null, 496 | "isSelf": false 497 | } 498 | }, { 499 | "source": 30, 500 | "target": 49, 501 | "relation": { 502 | "relationshipId": 92, 503 | "relationship": "BELONG_ADMIN", 504 | "created": 1574842038292, 505 | "lineNumber": null, 506 | "isSelf": false 507 | } 508 | }, { 509 | "source": 30, 510 | "target": 50, 511 | "relation": { 512 | "relationshipId": 102, 513 | "relationship": "BELONG_ADMIN", 514 | "created": 1574842038292, 515 | "lineNumber": null, 516 | "isSelf": false 517 | } 518 | }, { 519 | "source": 2, 520 | "target": 1, 521 | "relation": { 522 | "relationshipId": 196, 523 | "relationship": "BELONG_APPLICATION", 524 | "created": 1574821237662, 525 | "lineNumber": null, 526 | "isSelf": false 527 | } 528 | }, { 529 | "source": 51, 530 | "target": 52, 531 | "relation": { 532 | "relationshipId": 39, 533 | "relationship": "PARENT", 534 | "created": 1571040502085, 535 | "lineNumber": null, 536 | "isSelf": false 537 | } 538 | }, { 539 | "source": 37, 540 | "target": 53, 541 | "relation": { 542 | "relationshipId": 191, 543 | "relationship": "BELONG_ADMIN", 544 | "created": 1574745879106, 545 | "lineNumber": null, 546 | "isSelf": false 547 | } 548 | }, { 549 | "source": 8, 550 | "target": 54, 551 | "relation": { 552 | "relationshipId": 77, 553 | "relationship": "PARENT", 554 | "created": 1571189421890, 555 | "lineNumber": null, 556 | "isSelf": false 557 | } 558 | }, { 559 | "source": 55, 560 | "target": 56, 561 | "relation": { 562 | "relationshipId": 130, 563 | "relationship": "EFFECT_LOCATION", 564 | "created": 1574322233908, 565 | "lineNumber": null, 566 | "isSelf": false 567 | } 568 | }, { 569 | "source": 30, 570 | "target": 13, 571 | "relation": { 572 | "relationshipId": 133, 573 | "relationship": "AUTHORIZE_APPLICATION", 574 | "created": 1574842153352, 575 | "lineNumber": null, 576 | "isSelf": false 577 | } 578 | }, { 579 | "source": 0, 580 | "target": 58, 581 | "relation": { 582 | "relationshipId": 201, 583 | "relationship": "BELONG_MANUFACTURER", 584 | "created": 1574825160187, 585 | "lineNumber": null, 586 | "isSelf": false 587 | } 588 | }, { 589 | "source": 34, 590 | "target": 33, 591 | "relation": { 592 | "relationshipId": 100, 593 | "relationship": "CONTROL", 594 | "created": 1570779022030, 595 | "lineNumber": null, 596 | "isSelf": false 597 | } 598 | }, { 599 | "source": 19, 600 | "target": 59, 601 | "relation": { 602 | "relationshipId": 99, 603 | "relationship": "BELONG_APPLICATION", 604 | "created": 1574652203566, 605 | "lineNumber": null, 606 | "isSelf": false 607 | } 608 | }, { 609 | "source": 8, 610 | "target": 44, 611 | "relation": { 612 | "relationshipId": 143, 613 | "relationship": "PARENT", 614 | "created": 1571189421890, 615 | "lineNumber": null, 616 | "isSelf": false 617 | } 618 | }, { 619 | "source": 44, 620 | "target": 8, 621 | "relation": { 622 | "relationshipId": 139, 623 | "relationship": "PARENT", 624 | "created": 1571122040940, 625 | "lineNumber": null, 626 | "isSelf": false 627 | } 628 | }, { 629 | "source": 44, 630 | "target": 22, 631 | "relation": { 632 | "relationshipId": 124, 633 | "relationship": "PARENT", 634 | "created": 1571189497514, 635 | "lineNumber": null, 636 | "isSelf": false 637 | } 638 | }, { 639 | "source": 45, 640 | "target": 61, 641 | "relation": { 642 | "relationshipId": 187, 643 | "relationship": "BELONG_MANUFACTURER", 644 | "created": 1574736042772, 645 | "lineNumber": null, 646 | "isSelf": false 647 | } 648 | }, { 649 | "source": 30, 650 | "target": 62, 651 | "relation": { 652 | "relationshipId": 181, 653 | "relationship": "AUTHORIZE_APPLICATION", 654 | "created": 1574842153352, 655 | "lineNumber": null, 656 | "isSelf": false 657 | } 658 | }, { 659 | "source": 44, 660 | "target": 28, 661 | "relation": { 662 | "relationshipId": 10, 663 | "relationship": "PARENT", 664 | "created": 1571189497514, 665 | "lineNumber": null, 666 | "isSelf": false 667 | } 668 | }, { 669 | "source": 44, 670 | "target": 54, 671 | "relation": { 672 | "relationshipId": 128, 673 | "relationship": "PARENT", 674 | "created": 1571189497514, 675 | "lineNumber": null, 676 | "isSelf": false 677 | } 678 | }, { 679 | "source": 10, 680 | "target": 63, 681 | "relation": { 682 | "relationshipId": 184, 683 | "relationship": "EFFECT_LOCATION", 684 | "created": 1574735076416, 685 | "lineNumber": null, 686 | "isSelf": false 687 | } 688 | }, { 689 | "source": 14, 690 | "target": 14, 691 | "relation": { 692 | "relationshipId": 93, 693 | "relationship": "PARENT", 694 | "created": 1574217786986, 695 | "lineNumber": 1, 696 | "isSelf": true 697 | } 698 | }, { 699 | "source": 64, 700 | "target": 65, 701 | "relation": { 702 | "relationshipId": 44, 703 | "relationship": "CONNECT_PLATFORM", 704 | "created": 1574758583985, 705 | "lineNumber": null, 706 | "isSelf": false 707 | } 708 | }, { 709 | "source": 29, 710 | "target": 8, 711 | "relation": { 712 | "relationshipId": 43, 713 | "relationship": "PARENT", 714 | "created": 1571281100918, 715 | "lineNumber": null, 716 | "isSelf": false 717 | } 718 | }, { 719 | "source": 10, 720 | "target": 67, 721 | "relation": { 722 | "relationshipId": 23, 723 | "relationship": "BELONG_MANUFACTURER", 724 | "created": 1574408725149, 725 | "lineNumber": null, 726 | "isSelf": false 727 | } 728 | }, { 729 | "source": 2, 730 | "target": 68, 731 | "relation": { 732 | "relationshipId": 195, 733 | "relationship": "BELONG_ADMIN", 734 | "created": 1574821199052, 735 | "lineNumber": null, 736 | "isSelf": false 737 | } 738 | }, { 739 | "source": 10, 740 | "target": 10, 741 | "relation": { 742 | "relationshipId": 9, 743 | "relationship": "CONTROL", 744 | "created": 1574408971838, 745 | "lineNumber": 2, 746 | "isSelf": true 747 | } 748 | }, { 749 | "source": 71, 750 | "target": 71, 751 | "relation": { 752 | "relationshipId": 46, 753 | "relationship": "CONTROL", 754 | "created": 1571721495529, 755 | "lineNumber": 1, 756 | "isSelf": true 757 | } 758 | }, { 759 | "source": 2, 760 | "target": 73, 761 | "relation": { 762 | "relationshipId": 197, 763 | "relationship": "BELONG_MANUFACTURER", 764 | "created": 1574821249233, 765 | "lineNumber": null, 766 | "isSelf": false 767 | } 768 | }, { 769 | "source": 74, 770 | "target": 10, 771 | "relation": { 772 | "relationshipId": 45, 773 | "relationship": "CONTROL", 774 | "created": 1574401909220, 775 | "lineNumber": null, 776 | "isSelf": false 777 | } 778 | }, { 779 | "source": 19, 780 | "target": 75, 781 | "relation": { 782 | "relationshipId": 117, 783 | "relationship": "CONTROL", 784 | "created": 1574652545781, 785 | "lineNumber": null, 786 | "isSelf": false 787 | } 788 | }, { 789 | "source": 6, 790 | "target": 76, 791 | "relation": { 792 | "relationshipId": 144, 793 | "relationship": "CONTROL", 794 | "created": 1574142879454, 795 | "lineNumber": null, 796 | "isSelf": false 797 | } 798 | }, { 799 | "source": 8, 800 | "target": 8, 801 | "relation": { 802 | "relationshipId": 142, 803 | "relationship": "PARENT", 804 | "created": 1571189421890, 805 | "lineNumber": 1, 806 | "isSelf": true 807 | } 808 | }, { 809 | "source": 10, 810 | "target": 63, 811 | "relation": { 812 | "relationshipId": 186, 813 | "relationship": "INSTALL_LOCATION", 814 | "created": 1574735183907, 815 | "lineNumber": null, 816 | "isSelf": false 817 | } 818 | }, { 819 | "source": 52, 820 | "target": 51, 821 | "relation": { 822 | "relationshipId": 122, 823 | "relationship": "CONTROL", 824 | "created": 1571040489911, 825 | "lineNumber": null, 826 | "isSelf": false 827 | } 828 | }, { 829 | "source": 2, 830 | "target": 77, 831 | "relation": { 832 | "relationshipId": 193, 833 | "relationship": "INSTALL_LOCATION", 834 | "created": 1574821301840, 835 | "lineNumber": null, 836 | "isSelf": false 837 | } 838 | }, { 839 | "source": 44, 840 | "target": 35, 841 | "relation": { 842 | "relationshipId": 79, 843 | "relationship": "PARENT", 844 | "created": 1571189497514, 845 | "lineNumber": null, 846 | "isSelf": false 847 | } 848 | }], 849 | "nodes": [{ 850 | "id": 1, 851 | "deviceId": "002101025d09c809e4b0ca3a6a0966d2", 852 | "factoryCode": "061901", 853 | "modelCode": "00210102", 854 | "label": "Device", 855 | "linkId": 0 856 | }, { 857 | "id": 43, 858 | "applicationName": "测试盘古", 859 | "appId": "com.huawei.pangu", 860 | "label": "Application", 861 | "linkId": 1 862 | }, { 863 | "id": 18, 864 | "deviceId": "004801055dd4f157e4b04845806d6671", 865 | "factoryCode": "1120device002", 866 | "modelCode": "00480105", 867 | "label": "Device", 868 | "linkId": 2 869 | }, { 870 | "id": 140, 871 | "positionNumber": "887", 872 | "positionCode": "098", 873 | "label": "Position", 874 | "linkId": 3 875 | }, { 876 | "id": 86, 877 | "deviceId": "huo1a1005d5647ed270e07fad941eb33", 878 | "factoryCode": "huo11111111ww", 879 | "modelCode": "huoaa01a100", 880 | "label": "Device", 881 | "linkId": 4 882 | }, { 883 | "id": 125, 884 | "deviceId": "hye029015dc3b5df270e2cc0b2a2641c", 885 | "factoryCode": "dsafafaf666", 886 | "modelCode": "hye02901", 887 | "label": "Device", 888 | "linkId": 5 889 | }, { 890 | "id": 24, 891 | "deviceId": "000801025d0c7e438466e74289f900ef", 892 | "factoryCode": "buchongceshi2", 893 | "modelCode": "00080102", 894 | "label": "Device", 895 | "linkId": 6 896 | }, { 897 | "id": 163, 898 | "manufacturerCode": "HJJc", 899 | "manufacturerName": "测试厂商", 900 | "label": "Manufacturer", 901 | "linkId": 7 902 | }, { 903 | "id": 118, 904 | "deviceId": "004901015da53819e4b0bf772bd36775", 905 | "factoryCode": "13241341324143", 906 | "modelCode": null, 907 | "label": "Device", 908 | "linkId": 8 909 | }, { 910 | "id": 121, 911 | "deviceId": "004901015da53785e4b0bf772bd36774", 912 | "factoryCode": "58656745", 913 | "modelCode": "00490101", 914 | "label": "Device", 915 | "linkId": 9 916 | }, { 917 | "id": 2, 918 | "deviceId": "004101015dd7502ae4b0c5015d3aebe8", 919 | "factoryCode": "test1234", 920 | "modelCode": "00410101", 921 | "label": "Device", 922 | "linkId": 10 923 | }, { 924 | "id": 122, 925 | "applicationName": "测试2", 926 | "appId": "ceshi2", 927 | "label": "Application", 928 | "linkId": 11 929 | }, { 930 | "id": 105, 931 | "deviceId": "aa01a1005dce4dde0bfb1be4b75abe40", 932 | "factoryCode": "632598", 933 | "modelCode": "00080101", 934 | "label": "Device", 935 | "linkId": 12 936 | }, { 937 | "id": 131, 938 | "applicationName": "云企划2", 939 | "appId": "experienceUser", 940 | "label": "Application", 941 | "linkId": 13 942 | }, { 943 | "id": 111, 944 | "deviceId": "000901015d269b8bf140f7aa0df8b95a", 945 | "factoryCode": "0711002", 946 | "modelCode": "00090101", 947 | "label": "Device", 948 | "linkId": 14 949 | }, { 950 | "id": 113, 951 | "applicationName": "天行健111", 952 | "appId": "123344121", 953 | "label": "Application", 954 | "linkId": 15 955 | }, { 956 | "id": 165, 957 | "positionNumber": "009", 958 | "positionCode": "980", 959 | "label": "Position", 960 | "linkId": 16 961 | }, { 962 | "id": 137, 963 | "deviceId": "004101015dd64d90e4b0c5015d3aebe1", 964 | "factoryCode": "191018002", 965 | "modelCode": "00410101", 966 | "label": "Device", 967 | "linkId": 17 968 | }, { 969 | "id": 19, 970 | "deviceId": "pin1a1005d5647ed270e07fad941eb33", 971 | "factoryCode": "pin11111ww", 972 | "modelCode": "pin1a100", 973 | "label": "Device", 974 | "linkId": 18 975 | }, { 976 | "id": 20, 977 | "deviceId": "005601015dc50794e4b080f28a960a2c", 978 | "factoryCode": "0513", 979 | "modelCode": "00560101", 980 | "label": "Device", 981 | "linkId": 19 982 | }, { 983 | "id": 112, 984 | "deviceId": "000101035dc5310ce4b041b08987f1f8", 985 | "factoryCode": "05131", 986 | "modelCode": "00010103", 987 | "label": "Device", 988 | "linkId": 20 989 | }, { 990 | "id": 147, 991 | "deviceId": "sha1a1005d5647ed270e07fad941eb33", 992 | "factoryCode": "sha11111ww", 993 | "modelCode": "sha1a100", 994 | "label": "Device", 995 | "linkId": 21 996 | }, { 997 | "id": 158, 998 | "deviceId": "004901015da536e3e4b0bf772bd36773", 999 | "factoryCode": "dnc12332112", 1000 | "modelCode": "00490101", 1001 | "label": "Device", 1002 | "linkId": 22 1003 | }, { 1004 | "id": 123, 1005 | "deviceId": "0001011005dbd3acde4b00740880802d9", 1006 | "factoryCode": "23534546", 1007 | "modelCode": "000101100", 1008 | "label": "Device", 1009 | "linkId": 23 1010 | }, { 1011 | "id": 99, 1012 | "deviceId": "000101485dd207aee4b03622287ebb9d", 1013 | "factoryCode": "757869876876", 1014 | "modelCode": "00010148", 1015 | "label": "Device", 1016 | "linkId": 24 1017 | }, { 1018 | "id": 4, 1019 | "deviceId": "004101015dd64ea1e4b0c5015d3aebe2", 1020 | "factoryCode": "123", 1021 | "modelCode": "00410101", 1022 | "label": "Device", 1023 | "linkId": 25 1024 | }, { 1025 | "id": 142, 1026 | "positionNumber": "001", 1027 | "positionCode": "002", 1028 | "label": "Position", 1029 | "linkId": 26 1030 | }, { 1031 | "id": 44, 1032 | "deviceId": "004901025d84c8bfe4b00b0c3273832a", 1033 | "factoryCode": "1234132413241234", 1034 | "modelCode": "00490102", 1035 | "label": "Device", 1036 | "linkId": 27 1037 | }, { 1038 | "id": 160, 1039 | "deviceId": "004501035da43ba1e4b0940ae0681255", 1040 | "factoryCode": "889900666666555", 1041 | "modelCode": "00450103", 1042 | "label": "Device", 1043 | "linkId": 28 1044 | }, { 1045 | "id": 164, 1046 | "deviceId": "005401015da67cd0e4b05e140edc73b1", 1047 | "factoryCode": "201910161", 1048 | "modelCode": "00540101", 1049 | "label": "Device", 1050 | "linkId": 29 1051 | }, { 1052 | "id": 96, 1053 | "deviceId": "aa01a1005ddc9af2a973297a1cf341f3", 1054 | "factoryCode": "001", 1055 | "modelCode": "aa01a100", 1056 | "label": "Device", 1057 | "linkId": 30 1058 | }, { 1059 | "id": 151, 1060 | "manufacturerCode": "aa01", 1061 | "manufacturerName": "厂商aa01", 1062 | "label": "Manufacturer", 1063 | "linkId": 31 1064 | }, { 1065 | "id": 172, 1066 | "iotInfrastructureName": "test20190619", 1067 | "iotInfrastructureCode": "20190619", 1068 | "factoryType": "Thingworx", 1069 | "label": "IotInfrastructure", 1070 | "linkId": 32 1071 | }, { 1072 | "id": 154, 1073 | "deviceId": "aa01a1815d9feb526152de91705f9f6a", 1074 | "factoryCode": "zyzy00781", 1075 | "modelCode": "aa01a181", 1076 | "label": "Device", 1077 | "linkId": 33 1078 | }, { 1079 | "id": 153, 1080 | "deviceId": "aa01a1815d9fec4f6152de91705f9f6b", 1081 | "factoryCode": "zyzy0078234", 1082 | "modelCode": "aa01a181", 1083 | "label": "Device", 1084 | "linkId": 34 1085 | }, { 1086 | "id": 159, 1087 | "deviceId": "004901015da44155e4b02ff365d70baa", 1088 | "factoryCode": "432141211", 1089 | "modelCode": null, 1090 | "label": "Device", 1091 | "linkId": 35 1092 | }, { 1093 | "id": 103, 1094 | "iotInfrastructureName": "测试816iot-center", 1095 | "iotInfrastructureCode": "20190816", 1096 | "factoryType": "IoT-Center", 1097 | "label": "IotInfrastructure", 1098 | "linkId": 36 1099 | }, { 1100 | "id": 60, 1101 | "deviceId": "000101035ddc95a7e4b0fcf4c7a7dc3a", 1102 | "factoryCode": "764563445", 1103 | "modelCode": "00010103", 1104 | "label": "Device", 1105 | "linkId": 37 1106 | }, { 1107 | "id": 27, 1108 | "name": "吾问无为谓", 1109 | "employeeNumber": "142314", 1110 | "phone": "13218787857", 1111 | "label": "DeviceAdmin", 1112 | "linkId": 38 1113 | }, { 1114 | "id": 28, 1115 | "deviceId": "tui1a1005d5647ed270e07fad941eb33", 1116 | "factoryCode": "yui11111ww", 1117 | "modelCode": "tui1a100", 1118 | "label": "Device", 1119 | "linkId": 39 1120 | }, { 1121 | "id": 34, 1122 | "deviceId": "000801025d0c75f98466a48347b9be1e", 1123 | "factoryCode": "buchongceshi4", 1124 | "modelCode": "00080102", 1125 | "label": "Device", 1126 | "linkId": 40 1127 | }, { 1128 | "id": 45, 1129 | "manufacturerCode": "95664", 1130 | "manufacturerName": "12", 1131 | "label": "Manufacturer", 1132 | "linkId": 41 1133 | }, { 1134 | "id": 29, 1135 | "applicationName": "Test请勿删除此业务应用", 1136 | "appId": "190708", 1137 | "label": "Application", 1138 | "linkId": 42 1139 | }, { 1140 | "id": 33, 1141 | "deviceId": "aa01a1935d78cb6644527d9ccfdda354", 1142 | "factoryCode": "qwertyuio", 1143 | "modelCode": "5698", 1144 | "label": "Device", 1145 | "linkId": 43 1146 | }, { 1147 | "id": 157, 1148 | "deviceId": "004101015da43846e4b02ff365d70ba8", 1149 | "factoryCode": "wdnmddnc", 1150 | "modelCode": "00410101", 1151 | "label": "Device", 1152 | "linkId": 44 1153 | }, { 1154 | "id": 143, 1155 | "deviceId": "004901015dd648bae4b0c5015d3aebbf", 1156 | "factoryCode": "dnb", 1157 | "modelCode": "00490101", 1158 | "label": "Device", 1159 | "linkId": 45 1160 | }, { 1161 | "id": 133, 1162 | "deviceId": "000101035dce3585e4b024fa72195ac6", 1163 | "factoryCode": "20191115", 1164 | "modelCode": "00010103", 1165 | "label": "Device", 1166 | "linkId": 46 1167 | }, { 1168 | "id": 126, 1169 | "manufacturerCode": "0001", 1170 | "manufacturerName": "测试厂商", 1171 | "label": "Manufacturer", 1172 | "linkId": 47 1173 | }, { 1174 | "id": 145, 1175 | "positionNumber": "01", 1176 | "positionCode": "02", 1177 | "label": "Position", 1178 | "linkId": 48 1179 | }, { 1180 | "id": 116, 1181 | "name": "lili", 1182 | "employeeNumber": "1123", 1183 | "phone": "18236592287", 1184 | "label": "DeviceAdmin", 1185 | "linkId": 49 1186 | }, { 1187 | "id": 170, 1188 | "name": "2423", 1189 | "employeeNumber": "432432", 1190 | "phone": "13213243567", 1191 | "label": "DeviceAdmin", 1192 | "linkId": 50 1193 | }, { 1194 | "id": 6, 1195 | "deviceId": "004101015da3d9cee4b05637b9776da8", 1196 | "factoryCode": "111111111111122222222333333334444444", 1197 | "modelCode": "00410101", 1198 | "label": "Device", 1199 | "linkId": 51 1200 | }, { 1201 | "id": 35, 1202 | "deviceId": "000101035da3e713e4b07ee3503d6bb8", 1203 | "factoryCode": "235435", 1204 | "modelCode": "00010103", 1205 | "label": "Device", 1206 | "linkId": 52 1207 | }, { 1208 | "id": 39, 1209 | "name": "测试", 1210 | "employeeNumber": "20191120", 1211 | "phone": "13218787851", 1212 | "label": "DeviceAdmin", 1213 | "linkId": 53 1214 | }, { 1215 | "id": 161, 1216 | "deviceId": "000101035da43615e4b0940ae0681254", 1217 | "factoryCode": "9767456", 1218 | "modelCode": "00010103", 1219 | "label": "Device", 1220 | "linkId": 54 1221 | }, { 1222 | "id": 155, 1223 | "deviceId": "000101035dd6024de4b0b7a69b2099c6", 1224 | "factoryCode": "7567435345334", 1225 | "modelCode": "00010103", 1226 | "label": "Device", 1227 | "linkId": 55 1228 | }, { 1229 | "id": 15, 1230 | "positionNumber": "21212", 1231 | "positionCode": "1212", 1232 | "label": "Position", 1233 | "linkId": 56 1234 | }, { 1235 | "id": 114, 1236 | "deviceId": "doo1a1005d5647ed270e07fad941eb33", 1237 | "factoryCode": "doo11111ww", 1238 | "modelCode": "aa01a100", 1239 | "label": "Device", 1240 | "linkId": 57 1241 | }, { 1242 | "id": 107, 1243 | "manufacturerCode": "0021", 1244 | "manufacturerName": "0619设备厂商测试", 1245 | "label": "Manufacturer", 1246 | "linkId": 58 1247 | }, { 1248 | "id": 110, 1249 | "applicationName": "达西创意园", 1250 | "appId": "000888", 1251 | "label": "Application", 1252 | "linkId": 59 1253 | }, { 1254 | "id": 127, 1255 | "deviceId": "guo1a1005d5647ed270e07fad941eb33", 1256 | "factoryCode": "guo11111ww", 1257 | "modelCode": "guo1a100", 1258 | "label": "Device", 1259 | "linkId": 60 1260 | }, { 1261 | "id": 146, 1262 | "manufacturerCode": "0049", 1263 | "manufacturerName": "无锡爱玛有限公司", 1264 | "label": "Manufacturer", 1265 | "linkId": 61 1266 | }, { 1267 | "id": 171, 1268 | "applicationName": "0129", 1269 | "appId": "000111", 1270 | "label": "Application", 1271 | "linkId": 62 1272 | }, { 1273 | "id": 144, 1274 | "positionNumber": "", 1275 | "positionCode": "", 1276 | "label": "Position", 1277 | "linkId": 63 1278 | }, { 1279 | "id": 115, 1280 | "deviceId": "aa01a1005d5647ed270e07fad941eb33", 1281 | "factoryCode": "11111111ww", 1282 | "modelCode": "aa01a100", 1283 | "label": "Device", 1284 | "linkId": 64 1285 | }, { 1286 | "id": 149, 1287 | "iotInfrastructureName": "Oc测试平台", 1288 | "iotInfrastructureCode": "Oc_Test", 1289 | "factoryType": "OceanConnect", 1290 | "label": "IotInfrastructure", 1291 | "linkId": 65 1292 | }, { 1293 | "id": 85, 1294 | "deviceId": "aa01a1005d79f79b7e86cb64421d5f2e", 1295 | "factoryCode": "oc09121544", 1296 | "modelCode": "aa01a100", 1297 | "label": "Device", 1298 | "linkId": 66 1299 | }, { 1300 | "id": 10, 1301 | "manufacturerCode": "0041", 1302 | "manufacturerName": "江南烟草厂", 1303 | "label": "Manufacturer", 1304 | "linkId": 67 1305 | }, { 1306 | "id": 148, 1307 | "name": "测试", 1308 | "employeeNumber": "20191118", 1309 | "phone": "13218787857", 1310 | "label": "DeviceAdmin", 1311 | "linkId": 68 1312 | }, { 1313 | "id": 109, 1314 | "deviceId": "aa01a2165dce164af140638d567c5c0c", 1315 | "factoryCode": "1115factory001", 1316 | "modelCode": "aa01a216", 1317 | "label": "Device", 1318 | "linkId": 69 1319 | }, { 1320 | "id": 31, 1321 | "deviceId": "004501015d8493e1e4b07c14d91f6d0a", 1322 | "factoryCode": "123312abcd", 1323 | "modelCode": "00450101", 1324 | "label": "Device", 1325 | "linkId": 70 1326 | }, { 1327 | "id": 17, 1328 | "deviceId": "004901015da91703e4b0311aeee1075a", 1329 | "factoryCode": "4321412111", 1330 | "modelCode": "00490101", 1331 | "label": "Device", 1332 | "linkId": 71 1333 | }, { 1334 | "id": 102, 1335 | "deviceId": "000201025dd20c32e4b03622287ebb9e", 1336 | "factoryCode": "2019111801", 1337 | "modelCode": "00020102", 1338 | "label": "Device", 1339 | "linkId": 72 1340 | }, { 1341 | "id": 138, 1342 | "manufacturerCode": "0048", 1343 | "manufacturerName": "测试816", 1344 | "label": "Manufacturer", 1345 | "linkId": 73 1346 | }, { 1347 | "id": 0, 1348 | "deviceId": "000101035dd7772fe4b052fe501d9f6f", 1349 | "factoryCode": "test", 1350 | "modelCode": "00010103", 1351 | "label": "Device", 1352 | "linkId": 74 1353 | }, { 1354 | "id": 167, 1355 | "deviceId": "000101035dd3551de4b0502efc76fee0", 1356 | "factoryCode": "20191119002", 1357 | "modelCode": "00010103", 1358 | "label": "Device", 1359 | "linkId": 75 1360 | }, { 1361 | "id": 41, 1362 | "deviceId": "000801025d0c7f1a8466e74289f900f0", 1363 | "factoryCode": "buchongceshi5", 1364 | "modelCode": "00080102", 1365 | "label": "Device", 1366 | "linkId": 76 1367 | }, { 1368 | "id": 141, 1369 | "positionNumber": "987", 1370 | "positionCode": "091", 1371 | "label": "Position", 1372 | "linkId": 77 1373 | }] 1374 | }; 1375 | 1376 | 1377 | ~~ function renderGraph() { 1378 | let links = my_data.links; //经过力学仿真后,links里面的source和target节点会引用对应的nodes对象 1379 | let nodes = my_data.nodes; 1380 | 1381 | //关系分组 1382 | setLinkGroup(links); 1383 | 1384 | //点击节点后弹出的灰色圆圈以及图标的定义,分为三组【1.删除、2.联系、3.解除位置锁定】 1385 | let [removePath, remove_rect1, remove_rect2, remove_line1, remove_line2, remove_line3] = [null]; 1386 | let [unlockPath, unlock_path1, unlock_rect1, unlock_line1] = [null]; 1387 | let [hidePath, hide_path1, hide_path2, hide_path3, hide_path4, hide_circle1, hide_line1] = [null]; 1388 | let rightArc = d3.arc().outerRadius(60).innerRadius(30).startAngle(0).endAngle(1.85); 1389 | let leftArc = d3.arc().outerRadius(60).innerRadius(30).startAngle(4.5).endAngle(6.23); 1390 | let bottomArc = d3.arc().outerRadius(60).innerRadius(30).startAngle(1.9).endAngle(4.45); 1391 | 1392 | var simulation = null; 1393 | var zoom = null; 1394 | var svg = null; 1395 | var edges_line = null; 1396 | var edges_text = null; 1397 | var circle_g = null; 1398 | var circle = null; 1399 | var text = null; 1400 | 1401 | d3.select("#graph").html(''); 1402 | 1403 | simulation = d3.forceSimulation() //启动力学仿真 //force之后返回的依然是forceSimulation对象 1404 | .force("charge", d3.forceManyBody()) //添加力学仿真模型 1405 | .force("links", d3.forceLink()) 1406 | .force("center", d3.forceCenter(800 / 2, 600 / 2)) 1407 | .force("collision", d3.forceCollide(30)) 1408 | .nodes(nodes); //设定节点数组 //将json格式转化为力学图可用的格式 1409 | 1410 | //下面力学模型的参数配置也可以在初始化时就设置好,如向心力center的初始化 1411 | simulation.force("links").links(links).distance(150); //设置弹簧力模型参数,连线数组和连线长度 1412 | simulation.force("charge").strength(-200); //顶点的电荷数。该参数决定是排斥还是吸引,数值越小越互相排斥 1413 | 1414 | console.log(links) 1415 | console.log(nodes) 1416 | 1417 | zoom = d3.zoom() //创建一个放缩对象 1418 | .scaleExtent([.4, 2]) 1419 | .on("zoom", zoomed); 1420 | 1421 | //配置画板svg属性 1422 | svg = d3.select("#graph").append("svg") 1423 | .attr("pointer-event", "all") 1424 | .attr("preserveAspectRatio", "xMidYMid meet") //自适应容器大小 1425 | .attr("viewPort", "0 0 800 600") 1426 | .attr("viewBox", "0 0 800 600") 1427 | .call(zoom); 1428 | 1429 | //设置连接线 1430 | edges_line = svg.append("g").selectAll(".edgepath") 1431 | .data(links) //此时的links属性已经经过力学仿真进行了相关数据修改 1432 | .enter() 1433 | .append("path") 1434 | .attr("marker-end", function(d, i) { return getMarkerArrow(i); }) //根据箭头标记的id号标记箭头 1435 | .style("stroke", '#9aaabf') 1436 | .style("stroke-width", 1) //线条粗细 1437 | .style("fill-opacity", 0) 1438 | .style("cursor", "pointer") 1439 | .attr("id", function(d, i) { return 'edgepath' + i; }) 1440 | .on("mouseover", function(d) { return getStrokeWidth(d); }) 1441 | .on("mouseout", function() { edges_line.style("stroke-width", 1) }) 1442 | .on('click', (d, i) => { deleteLine(d, i); }); 1443 | 1444 | //设置连接线上的文字 1445 | edges_text = svg.append("g").selectAll(".edgetext") 1446 | .data(links) 1447 | .enter() 1448 | .append("text") 1449 | .style("pointer-events", "none") 1450 | .attr("class", "linetext") 1451 | .attr('text-anchor', "middle") 1452 | .attr("fill-opacity", 1) 1453 | .style("cursor", "pointer") 1454 | .attr('class', 'edgelabel') 1455 | .attr('id', function(d, i) { return 'edgepath' + i; }) 1456 | .attr('dx', 50) 1457 | .attr('dy', 0) 1458 | .attr('fill', '#9aaabf'); 1459 | 1460 | //设置线条上的文字 1461 | edges_text.append('textPath') 1462 | .attr('xlink:href', function(d, i) { return '#edgepath' + i }) 1463 | .attr("pointer-events", "none").attr("font-size", 9) 1464 | .text(function(d) { return d.relation.relationship; }); 1465 | 1466 | circle_g = svg.selectAll("circle") 1467 | .data(nodes) //使用仿真后的nodes数据 1468 | .enter() 1469 | .append("g") 1470 | .attr("class", function(node, i) { return "g_circle_" + i; }) //标记circle的父节点 1471 | .style("cursor", "pointer") 1472 | .call(drag()) //将当前选中的元素传到drag函数中,使顶点可以被拖动 1473 | .on("click", (node, i) => { 1474 | if (d3.event.defaultPrevented) return; //阻止click事件和拖拽事件冲突 1475 | circleClick(node, i, this); 1476 | }) 1477 | .on('mouseover', (node) => { 1478 | if (d3.event.defaultPrevented) return; 1479 | showNodeInfo(node, this); 1480 | // showCircleBorderOuterArc(node, i); 1481 | }); 1482 | 1483 | svg.on("dblclick.zoom", null); //取消svg和圆圈的双击放大事件(d3中默认开启7个事件,关闭防止与上面的双击事件冲突) 1484 | circle_g.on("dblclick.zoom", null); 1485 | 1486 | //圆圈 1487 | circle = circle_g.append("circle") 1488 | .style("stroke-width", "2px") 1489 | .attr("r", 25) //设置圆圈半径 1490 | .style("fill", function(node) { return getCircleColor(node); }); 1491 | 1492 | text = circle_g.append("text") 1493 | .attr("dy", ".35em") 1494 | .attr("text-anchor", "middle") //在圆圈中加上数据 1495 | .style('fill', "#FFFFFF") 1496 | .attr('x', function(d) { appendCircleText(d, this); }); 1497 | 1498 | //圆圈的提示文字 1499 | circle.append("svg:title").text(function(node) { 1500 | switch (node.label) { 1501 | case 'Device': 1502 | return node.factoryCode; 1503 | case 'Application': 1504 | return node.applicationName; 1505 | case 'Position': 1506 | return node.positionCode; 1507 | case 'Manufacturer': 1508 | return node.manufacturerName; 1509 | case 'IotInfrastructure': 1510 | return node.iotInfrastructureName; 1511 | case 'DeviceAdmin': 1512 | return node.name; 1513 | } 1514 | }); 1515 | text.append("svg:title").text(function(node) { 1516 | switch (node.label) { 1517 | case 'Device': 1518 | return node.factoryCode; 1519 | case 'Application': 1520 | return node.applicationName; 1521 | case 'Position': 1522 | return node.positionCode; 1523 | case 'Manufacturer': 1524 | return node.manufacturerName; 1525 | case 'IotInfrastructure': 1526 | return node.iotInfrastructureName; 1527 | case 'DeviceAdmin': 1528 | return node.name; 1529 | } 1530 | }); 1531 | 1532 | function zoomed() { //svg下的g标签移动大小 1533 | svg.selectAll("g").attr("transform", d3.event.transform); 1534 | } 1535 | 1536 | 1537 | function getMarkerArrow(i) { 1538 | svg.append("defs").append("marker") //箭头 1539 | .attr("id", "arrow" + i) 1540 | .attr("markerUnits", "strokeWidth") //设置为strokeWidth箭头会随着线的粗细发生变化 1541 | .attr("markerUnits", "userSpaceOnUse") 1542 | .attr("markerWidth", 10) //标识的大小 1543 | .attr("markerHeight", 10) 1544 | .attr("viewBox", "0 -4 12 15") //坐标系的区域 1545 | .attr("refX", 0) //箭头坐标 1546 | .attr("refY", 0) 1547 | .attr("orient", 'auto') //绘制方向,可设定为:auto(自动确认方向)和 角度值 1548 | .append("svg:path") 1549 | .attr("stroke-width", 1) //箭头宽度 1550 | .attr("d", "M0,-5L10,0L0,5") //箭头的路径 1551 | .attr('fill', 'rgba(0,0,0, 0.4)'); //箭头颜色 1552 | return "url(#arrow" + i + ")"; 1553 | } 1554 | 1555 | function getStrokeWidth(d) { 1556 | edges_line.style("stroke-width", function(edge) { 1557 | return edge.relation.relationshipId === d.relation.relationshipId ? 3 : 1; 1558 | }); 1559 | } 1560 | 1561 | function drag() { //拖拽函数 1562 | return d3.drag().on("start", function(d) { 1563 | if (!d3.event.active) { 1564 | simulation.alphaTarget(0.05).restart(); // 设置衰减系数,对节点位置移动过程的模拟,数值越高移动越快,数值范围[0,1] 1565 | } 1566 | d3.event.sourceEvent.stopPropagation(); //取消默认事件 1567 | d.fixed = true; //拖拽开始后设定被拖拽对象为固定 1568 | d.fx = d.x; //通过设置fx值来固定节点位置 https://github.com/xswei/d3-force/blob/master/README.md#simulation_nodes 1569 | d.fy = d.y; 1570 | }) 1571 | .on("drag", function(d) { 1572 | d.fx = d3.event.x; 1573 | d.fy = d3.event.y; 1574 | }) 1575 | .on("end", function(d) { 1576 | if (!d3.event.active) { 1577 | simulation.alphaTarget(0.05); 1578 | } 1579 | d.fx = d.x; //设置为null则取消固定 1580 | d.fy = d.y; 1581 | }); 1582 | } 1583 | 1584 | function circleClick(node, i, _this) { 1585 | //获取当前节点是否包含圆环 1586 | let existedRing = svg.select('.g_circle_' + i).selectAll('.g_circle_path'); 1587 | //清除上个节点的圆圈以及图标 1588 | svg.selectAll('.g_circle_path').remove(); 1589 | svg.selectAll('.remove_a').remove(); 1590 | svg.selectAll('.hide_a').remove(); 1591 | svg.selectAll('.unlock_a').remove(); 1592 | removePath = null; 1593 | unlockPath = null; 1594 | hidePath = null; 1595 | if (existedRing && existedRing["_groups"][0].length === 0) { 1596 | //showCircleBorderOuterArc(node, i); 1597 | //绘制灰色可点击圆圈,分为三部分分别绘制 1598 | showRemove(node, i, _this); 1599 | showUnlock(node, i, _this); 1600 | showHide(node, i, _this); 1601 | } 1602 | } 1603 | 1604 | function getCircleColor(node) { 1605 | let color = { 1606 | 'Device': "#FF9D00", 1607 | 'Position': "#C477E9", 1608 | 'Work': '#67CAF4', 1609 | 'Install': '#BCDD73', 1610 | 'Application': '#DD1E9E', 1611 | 'Manufacturer': '#DDBA9E', 1612 | 'IotInfrastructure': '#6ca5dd', 1613 | 'DeviceAdmin': '#50DD87', 1614 | }; //圆圈背景色 1615 | return color[node.label] || '#C477E9'; 1616 | } 1617 | 1618 | function appendCircleText(d, _this) { 1619 | let circleText = ''; 1620 | if (d.label === 'Device' && d.factoryCode) { 1621 | circleText = d.factoryCode; 1622 | } else if (d.label === 'Position') { 1623 | circleText = d.positionCode; 1624 | } else if (d.label === 'Application') { 1625 | circleText = d.applicationName; 1626 | } else if (d.label === 'Manufacturer') { 1627 | circleText = d.manufacturerName; 1628 | } else if (d.label === 'IotInfrastructure') { 1629 | circleText = d.iotInfrastructureName; 1630 | } else if (d.label === 'DeviceAdmin') { 1631 | circleText = d.name; 1632 | } 1633 | //如果小于四个字符,不换行 1634 | if (circleText && circleText.length > 4) { 1635 | circleText = circleText.substring(0, 4) + "..."; 1636 | d3.select(_this).text(function() { return ''; }); 1637 | } 1638 | d3.select(_this).append('tspan').attr('x', 0).attr('y', 0).attr("font-size", 12) 1639 | .text(function() { return circleText; }); 1640 | } 1641 | 1642 | function deleteLine(d, i) { 1643 | alert('delete this ' + d + ',' + i); 1644 | } 1645 | 1646 | function tick() { 1647 | //每个一小段时间此函数会被调用,用以绘图。alpha值越小表示力学仿真布局越合理,为了减少不必要的渲染,同时加快画面展示流畅度 1648 | //设置满足一定阈值时,才渲染图形 1649 | if (simulation.alpha() > 0.1) return; // 足够稳定时,才渲染一次.此阈值的设定要大于等于drag函数里面的alphaTarget值。 1650 | 1651 | circle.attr("transform", transform1); //圆圈 1652 | text.attr("transform", transform2); //顶点文字 1653 | edges_line.attr('d', function(d) { 1654 | //绘制自己指向自己的路径 1655 | if (d.relation.isSelf) { return getNodeSelfPath(d) } 1656 | //绘制两个节点之间的路径 1657 | return getNodesLine(d); //曲线路径 1658 | }); 1659 | 1660 | // if(circleBorderOuterArcObj !== null) { 1661 | // circleBorderOuterArcObj.attr("transform", function (d){ return "translate("+d.x+","+d.y+")" }); 1662 | // } 1663 | 1664 | //绘制节点删除功能半圆环 1665 | if (removePath !== null) { 1666 | removePath.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")" }); 1667 | 1668 | let r1 = null; 1669 | remove_rect1.attr('d', function(d) { r1 = d; }) 1670 | .attr("x", r1.x + 33 + 6.75).attr("y", r1.y - 36 + 9.75).attr("width", 5).attr("height", 3).attr("rx", 1.5).attr("ry", 1.5); 1671 | 1672 | let r2 = null; 1673 | remove_rect2.attr('d', function(d) { r2 = d; }) 1674 | .attr("x", r2.x + 30 + 6.75).attr("y", r2.y - 33 + 9.75).attr("width", 12).attr("height", 14).attr("rx", 1.5).attr("ry", 1.5); 1675 | 1676 | let l1 = null; 1677 | remove_line1.attr('d', function(d) { l1 = d; }) 1678 | .attr("x1", l1.x + 28 + 6.75).attr("y1", l1.y - 33 + 9.75).attr("x2", l1.x + 45 + 6.75).attr("y2", l1.y - 33 + 9.75); 1679 | 1680 | let l2 = null; 1681 | remove_line2.attr('d', function(d) { l2 = d; }) 1682 | .attr("x1", l2.x + 34 + 6.75).attr("y1", l2.y - 30 + 9.75).attr("x2", l2.x + 34 + 6.75).attr("y2", l2.y - 23 + 9.75); 1683 | 1684 | let l3 = null; 1685 | remove_line3.attr('d', function(d) { l3 = d; }) 1686 | .attr("x1", l3.x + 38 + 6.75).attr("y1", l3.y - 30 + 9.75).attr("x2", l3.x + 38 + 6.75).attr("y2", l3.y - 23 + 9.75); 1687 | } 1688 | 1689 | //绘制解除节点位置锁定半圆环 1690 | if (unlockPath !== null) { 1691 | unlockPath.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")" }); 1692 | unlock_path1.attr("transform", function(d) { return "translate(" + (d.x - 45) + "," + (d.y - 23) + ")" }); 1693 | 1694 | let r1 = null; 1695 | unlock_rect1.attr('d', function(d) { r1 = d; }) 1696 | .attr("x", r1.x - 52 + 6.75).attr("y", r1.y - 30 + 9.75).attr("width", 15).attr("height", 12).attr("rx", 1.5).attr("ry", 1.5); 1697 | 1698 | let l1 = null; 1699 | unlock_line1.attr('d', function(d) { l1 = d; }) 1700 | .attr("x1", l1.x - 52 + 15).attr("y1", l1.y - 30 + 15).attr("x2", l1.x - 52 + 15).attr("y2", l1.y - 30 + 18); 1701 | } 1702 | 1703 | //绘制隐藏节点和关系半圆环 1704 | if (hidePath !== null) { 1705 | hidePath.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")" }); 1706 | 1707 | hide_path1 1708 | .attr("d", function(d) { 1709 | return "M" + (d.x - 13) + "," + (d.y + 42) + "A15,15,0,0,1" + (d.x + 13) + "," + (d.y + 42) 1710 | }); 1711 | hide_path2 1712 | .attr("d", function(d) { 1713 | return "M" + (d.x - 12 + 10.4) + "," + (d.y + 34 + 10.937) + "A" + 3.749 + "," + 3.749 + ",0,1,1," + (d.x - 12 + 15.338) + "," + (d.y + 33 + 9) 1714 | }); 1715 | hide_path3 1716 | .attr("d", function(d) { 1717 | return "M" + (d.x - 12) + "," + (d.y + 42) + "A20,20,0,0,0" + (d.x - 4) + "," + (d.y + 47) 1718 | }); 1719 | hide_path4 1720 | .attr("d", function(d) { 1721 | return "M" + (d.x + 11) + "," + (d.y + 44) + "A20,20,0,0,0" + (d.x + 13) + "," + (d.y + 42) 1722 | }); 1723 | 1724 | let c1 = null; 1725 | hide_circle1.attr('d', function(d) { c1 = d; }) 1726 | .attr("cx", c1.x - 12 + 17.15).attr("cy", c1.y + 33 + 17.25).attr("r", 6); 1727 | 1728 | let l1 = null; 1729 | hide_line1.attr('d', function(d) { l1 = d; }) 1730 | .attr("x1", l1.x - 12 + 14.15).attr("y1", l1.y + 33 + 17.25).attr("x2", l1.x - 12 + 20.15).attr("y2", l1.y + 33 + 17.25); 1731 | 1732 | } 1733 | 1734 | } 1735 | simulation.on("tick", tick); //指时间间隔,隔一段时间刷新一次画面 1736 | 1737 | //设置圆圈和文字的坐标 1738 | function transform1(d) { 1739 | //设置节点固定 初始全部固定,拖动节点后会固定当前节点 1740 | if (d.fixed != undefined && d.fixed == false) { 1741 | d.fx = null; 1742 | d.fy = null; 1743 | } else { 1744 | d.fx = d.x; 1745 | d.fy = d.y; 1746 | } 1747 | return "translate(" + d.x + "," + d.y + ")"; 1748 | } 1749 | 1750 | function transform2(d) { 1751 | return "translate(" + (d.x) + "," + d.y + ")"; 1752 | } 1753 | 1754 | 1755 | function showRemove(node, i, _this) { 1756 | removePath = d3.select('.g_circle_' + i).append("path").attr('class', 'g_circle_path') 1757 | .attr("transform", "translate(" + node.x + "," + node.y + ")").attr("d", rightArc) 1758 | .attr("fill", "rgb(210, 213, 218)") 1759 | .style("cursor", "pointer") 1760 | .on("click", () => { removeNodeAndPath(node, _this); }) 1761 | .on("mouseover", function() { removePath.attr("fill", "rgb(185, 185, 185)") }) 1762 | .on("mouseout", function() { removePath.attr("fill", "rgb(210, 213, 218)") }); 1763 | 1764 | d3.select('.g_circle_' + i).append("g").attr("class", "remove_a") 1765 | .attr("transform", "translate(" + (node.x + 33) + "," + (node.y - 24) + ") scale(0.7)") 1766 | .append("defs").append("style") 1767 | .text(".remove_a{fill:none;stroke:#FFFFFF;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px;}"); 1768 | 1769 | removePath.append("title").attr("class", "remove_a").text("Remove"); 1770 | remove_rect1 = d3.select('.g_circle_' + i).append("rect").attr("class", "remove_a").attr("x", node.x + 33 + 6.75).attr("y", node.y - 36 + 9.75) 1771 | .attr("width", 5).attr("height", 3).attr("rx", 1.5).attr("ry", 1.5); 1772 | remove_rect2 = d3.select('.g_circle_' + i).append("rect").attr("class", "remove_a").attr("x", node.x + 30 + 6.75).attr("y", node.y - 33 + 9.75) 1773 | .attr("width", 12).attr("height", 14).attr("rx", 1.5).attr("ry", 1.5); 1774 | remove_line1 = d3.select('.g_circle_' + i).append("line").attr("class", "remove_a").attr("x1", node.x + 28 + 6.75).attr("y1", node.y - 33 + 9.75).attr("x2", node.x + 45 + 6.75).attr("y2", node.y - 33 + 9.75); 1775 | remove_line2 = d3.select('.g_circle_' + i).append("line").attr("class", "remove_a").attr("x1", node.x + 34 + 6.75).attr("y1", node.y - 30 + 9.75).attr("x2", node.x + 34 + 6.75).attr("y2", node.y - 23 + 9.75); 1776 | remove_line3 = d3.select('.g_circle_' + i).append("line").attr("class", "remove_a").attr("x1", node.x + 38 + 6.75).attr("y1", node.y - 30 + 9.75).attr("x2", node.x + 38 + 6.75).attr("y2", node.y - 23 + 9.75); 1777 | 1778 | d3.selectAll(".remove_a").on("click", () => { removeNodeAndPath(node, _this); }) 1779 | } 1780 | 1781 | function showHide(node, i) { 1782 | hidePath = d3.select('.g_circle_' + i).append("path").attr('class', 'g_circle_path') 1783 | .attr("transform", "translate(" + node.x + "," + node.y + ")").attr("d", bottomArc) 1784 | .attr("fill", "rgb(210, 213, 218)") 1785 | .style("cursor", "pointer") 1786 | .on("click", function() { hideNodeAndLinks(node) }) 1787 | .on("mouseover", function() { hidePath.attr("fill", "rgb(185, 185, 185)") }) 1788 | .on("mouseout", function() { hidePath.attr("fill", "rgb(210, 213, 218)") }); 1789 | 1790 | d3.select('.g_circle_' + i).append("g").attr("class", "hide_a") 1791 | .attr("transform", "translate(" + (node.x - 8) + "," + (node.y + 38) + ") scale(0.7)") 1792 | .append("defs").append("style") 1793 | .text(".hide_a{fill:none;stroke:#FFFFFF;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px;}"); 1794 | 1795 | hidePath.append("title").attr("class", "hide_a").text("Hide"); 1796 | 1797 | hide_path1 = d3.select('.g_circle_' + i).append("path").attr("class", "hide_a") 1798 | .attr("d", function(d) { 1799 | return "M" + (d.x - 13) + "," + (d.y + 42) + "A15,15,0,0,1" + (d.x + 13) + "," + (d.y + 42) 1800 | }); 1801 | hide_path2 = d3.select('.g_circle_' + i).append("path").attr("class", "hide_a") 1802 | .attr("d", function(d) { 1803 | return "M" + (d.x - 12 + 10.4) + "," + (d.y + 34 + 10.937) + "A" + 3.749 + "," + 3.749 + ",0,1,1," + (d.x - 12 + 15.338) + "," + (d.y + 33 + 9) 1804 | }); 1805 | hide_path3 = d3.select('.g_circle_' + i).append("path").attr("class", "hide_a") 1806 | .attr("d", function(d) { 1807 | return "M" + (d.x - 12) + "," + (d.y + 42) + "A20,20,0,0,0" + (d.x - 4) + "," + (d.y + 47) 1808 | }); 1809 | hide_path4 = d3.select('.g_circle_' + i).append("path").attr("class", "hide_a") 1810 | .attr("d", function(d) { 1811 | return "M" + (d.x + 11) + "," + (d.y + 44) + "A20,20,0,0,0" + (d.x + 13) + "," + (d.y + 42) 1812 | }); 1813 | hide_circle1 = d3.select('.g_circle_' + i).append("circle").attr("class", "hide_a").attr("cx", node.x - 12 + 17.15).attr("cy", node.y + 33 + 17.25).attr("r", 6); 1814 | hide_line1 = d3.select('.g_circle_' + i).append("line").attr("class", "hide_a").attr("x1", node.x - 12 + 14.15).attr("y1", node.y + 33 + 17.25).attr("x2", node.x - 12 + 20.15).attr("y2", node.y + 33 + 17.25); 1815 | d3.selectAll(".hide_a").on("click", (d) => { hideNodeAndLinks(d); }) 1816 | } 1817 | 1818 | function showUnlock(node, i) { 1819 | unlockPath = d3.select('.g_circle_' + i).append("path").attr('class', 'g_circle_path') 1820 | .attr("transform", "translate(" + node.x + "," + node.y + ")").attr("d", leftArc) 1821 | .attr("fill", "rgb(210, 213, 218)").style("cursor", "pointer") 1822 | .on("click", function(d) { unlockNodeFixed(d, this); }) 1823 | .on("mouseover", function() { unlockPath.attr("fill", "rgb(185, 185, 185)") }) 1824 | .on("mouseout", function() { unlockPath.attr("fill", "rgb(210, 213, 218)") }); 1825 | 1826 | d3.select('.g_circle_' + i).append("g").attr("class", "unlock_a") 1827 | .attr("transform", "translate(" + (node.x - 52) + "," + (node.y - 30) + ") scale(0.7)") 1828 | .append("defs").append("style") 1829 | .text(".unlock_a{fill:none;stroke:#FFFFFF;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px;}"); 1830 | 1831 | unlockPath.append("title").attr("class", "unlock_a").text("Unlock"); 1832 | unlock_path1 = d3.select('.g_circle_' + i).append("path").attr("class", "unlock_a") 1833 | .attr("transform", "translate(" + (node.x - 45) + "," + (node.y - 23) + ")") 1834 | .attr("d", d3.arc().outerRadius(5.3).innerRadius(5).startAngle(-1.8).endAngle(1.8)) 1835 | .attr("fill", "#FFFFFF"); 1836 | unlock_rect1 = d3.select('.g_circle_' + i).append("rect").attr("class", "unlock_a").attr("x", node.x - 52 + 6.75).attr("y", node.y - 30 + 9.75) 1837 | .attr("width", 15).attr("height", 12).attr("rx", 1.5).attr("ry", 1.5); 1838 | unlock_line1 = d3.select('.g_circle_' + i).append("line").attr("class", "unlock_a").attr("x1", node.x - 52 + 15).attr("y1", node.y - 30 + 15).attr("x2", node.x - 52 + 15).attr("y2", node.y - 30 + 18); 1839 | 1840 | d3.selectAll(".unlock_a").on("click", (d) => { unlockNodeFixed(d, this); }) 1841 | 1842 | } 1843 | 1844 | function hideNodeAndLinks(node) { 1845 | d3.event.stopPropagation(); 1846 | circle_g.attr('node', function(n) { 1847 | if (n.id === node.id) { 1848 | d3.select(this).remove(); 1849 | } 1850 | }); 1851 | edges_line.attr('d', function(d) { 1852 | if (d.source.id === node.id || d.target.id === node.id) { 1853 | d3.select(this).remove(); 1854 | } 1855 | }); 1856 | } 1857 | 1858 | function removeNodeAndPath(node, _this) { 1859 | d3.event.stopPropagation(); 1860 | alert('delete this ' + node + ',' + _this); 1861 | } 1862 | 1863 | function showNodeInfo(node, _this) { 1864 | window.console.log(node); 1865 | window.console.log(_this); 1866 | 1867 | } 1868 | 1869 | function unlockNodeFixed(d, _this) { 1870 | d3.event.stopPropagation(); 1871 | d.fixed = false; //解除节点位置锁定 1872 | //清除上个节点的圆圈以及图标 1873 | svg.selectAll('.g_circle_path').remove(); 1874 | svg.selectAll('.remove_a').remove(); 1875 | svg.selectAll('.hide_a').remove(); 1876 | svg.selectAll('.unlock_a').remove(); 1877 | removePath = null; 1878 | unlockPath = null; 1879 | hidePath = null; 1880 | } 1881 | 1882 | }(); --------------------------------------------------------------------------------