G6 registerNode afterDraw createNodeBox contextMenu ToolBar 的综合使用

效果:

 

 

 

 代码:

  1 import G6 from "@antv/g6";
  2 
  3 // 提示
  4 const graphDiv = document.getElementById("container");
  5 const descriptionDiv = document.createElement("p");
  6 descriptionDiv.style.padding = 0;
  7 descriptionDiv.style.margin = 0;
  8 descriptionDiv.style.fontSize = "12px";
  9 descriptionDiv.style.color = "#999";
 10 descriptionDiv.innerHTML =
 11   "    *提示:灰色任务代表“禁用”状态,右键进行对任务的删除、启用/禁用操作。";
 12 graphDiv.appendChild(descriptionDiv);
 13 // 右键
 14 const contextMenu = new G6.Menu({
 15   getContent(evt) {
 16     const { dataType } = evt.item.getModel();
 17     const flag = dataType === "disabled";
 18     return `<div eventType = '1' style="cursor:pointer">删除</div>${
 19       flag
 20         ? '<div eventType="2" style="cursor:pointer">启用</div>'
 21         : '<div eventType="3" style="cursor:pointer">禁用</div>'
 22     }`;
 23   },
 24   shouldBegin(evt) {
 25     const { dataType } = evt.item.getModel();
 26     if (dataType && dataType === "root") {
 27       return false;
 28     } else {
 29       return true;
 30     }
 31   },
 32   handleMenuClick: (target, item) => {
 33     console.log(item.getModel());
 34     const eventType = target.getAttribute("eventType");
 35     switch (eventType) {
 36       default:
 37         return;
 38       case "1":
 39         console.log(1, eventType);
 40         break;
 41       case "2":
 42         console.log(2, eventType);
 43         break;
 44       case "3":
 45         console.log(3, eventType);
 46         break;
 47     }
 48   },
 49   offsetX: 16,
 50   offsetY: 0,
 51   itemTypes: ["node"]
 52 });
 53 // 工具栏按钮定制
 54 const toolbar = new G6.ToolBar({
 55   position: { x: 700, y: 10 },
 56   getContent: () => {
 57     return `
 58       <ul>
 59         <li code='switch'>切换</li>
 60         <li code='fitView'>适应</li>
 61       </ul>
 62     `;
 63   },
 64   handleClick(code, graph) {
 65     switch (code) {
 66       case "switch": // 横、纵切换
 67         const controler = graph.get("layoutController");
 68         const { rankdir } = controler.layoutMethod;
 69         if (rankdir === "H") {
 70           controler.updateLayoutCfg({ rankdir: "LR" });
 71         } else {
 72           controler.updateLayoutCfg({ rankdir: "H" });
 73         }
 74         controler.relayout();
 75         graph.fitView();
 76         break;
 77       case "fitView": // 适应屏幕
 78         graph.fitView();
 79         break;
 80       default:
 81         return;
 82     }
 83   }
 84 });
 85 
 86 const ERROR_COLOR = "#F5222D";
 87 const getNodeConfig = (node) => {
 88   if (node.nodeError) {
 89     return {
 90       basicColor: ERROR_COLOR,
 91       fontColor: "#FFF",
 92       borderColor: ERROR_COLOR,
 93       bgColor: "#E66A6C"
 94     };
 95   }
 96   let config = {
 97     basicColor: "#5B8FF9",
 98     fontColor: "#5B8FF9",
 99     borderColor: "#5B8FF9",
100     bgColor: "#C6E5FF"
101   };
102   return config;
103 };
104 const nodeBasicMethod = {
105   createNodeBox: (group, config, w, h, cfg) => {
106     const nodeError = cfg.nodeError;
107     /* 最外面的大矩形 */
108     const container = group.addShape("rect", {
109       attrs: {
110         x: 3,
111         y: 0,
112         width: w,
113         height: h,
114         fill: config.bgColor,
115         stroke: config.borderColor,
116         radius: 2,
117         cursor: "pointer"
118       },
119       name: "rect-shape"
120     });
121     /* 左边的粗线 */
122     group.addShape("rect", {
123       attrs: {
124         x: 3,
125         y: 0,
126         width: 3,
127         height: h,
128         fill: config.basicColor,
129         radius: 1.5
130       },
131       name: "left-border-shape"
132     });
133     // 启用、禁用 按钮 外框
134     const ipRect = group.addShape("rect", {
135       attrs: {
136         fill: nodeError ? "transparent" : "#FFF",
137         stroke: nodeError ? "rgba(255,255,255,0.65)" : null,
138         radius: 2,
139         cursor: "pointer",
140         opacity: 0
141       },
142       name: "ip-container-shape"
143     });
144     // 启用、禁用 按钮 文字
145     const ipText = group.addShape("text", {
146       attrs: {
147         text: cfg.ip,
148         x: 40,
149         y: 19,
150         fontSize: 12,
151         textAlign: "left",
152         textBaseline: "middle",
153         fill: nodeError ? "rgba(255,255,255,0.85)" : "rgba(0,0,0,0.65)",
154         cursor: "pointer",
155         opacity: 0
156       },
157       name: "ip-text-shape"
158     });
159     // 移除 按钮外框
160     const moveItemRect = group.addShape("rect", {
161       attrs: {
162         fill: nodeError ? "transparent" : "#E66A6C",
163         stroke: nodeError ? "rgba(255,255,255,0.65)" : null,
164         radius: 2,
165         cursor: "pointer",
166         opacity: 0
167       },
168       name: "remove-rect"
169     });
170     // 移除 按钮文字
171     const moveItemText = group.addShape("text", {
172       attrs: {
173         text: "移除",
174         x: 40,
175         y: 19,
176         fontSize: 12,
177         textAlign: "left",
178         textBaseline: "middle",
179         fill: nodeError ? "rgba(255,255,255,0.85)" : "rgba(0,0,0,0.65)",
180         cursor: "pointer",
181         opacity: 0
182       },
183       name: "remove-text"
184     });
185     const ipBBox = ipText.getBBox();
186     const ipTextX = w - 20 - 2 * ipBBox.width;
187     const ipTextY = ipBBox.minY - 5;
188     const moveItemTextX = w - 10 - ipBBox.width;
189     const moveItemTextY = ipBBox.minY - 5;
190     ipText.attr({
191       x: ipTextX
192     });
193     ipRect.attr({
194       x: ipTextX - 4,
195       y: ipTextY,
196       width: ipBBox.width + 8,
197       height: ipBBox.height + 10
198     });
199     moveItemText.attr({
200       x: moveItemTextX + 4
201     });
202     moveItemRect.attr({
203       x: moveItemTextX,
204       y: moveItemTextY,
205       width: ipBBox.width + 8,
206       height: ipBBox.height + 10
207     });
208     // 任务 名称文字
209     group.addShape("text", {
210       attrs: {
211         text: cfg.name,
212         x: 12,
213         y: 19,
214         fontSize: 14,
215         fontWeight: 700,
216         textAlign: "left",
217         textBaseline: "middle",
218         fill: config.fontColor,
219         cursor: "pointer"
220       },
221       name: "name-text-shape"
222     });
223     // 任务 内容文字
224     group.addShape("text", {
225       attrs: {
226         text: cfg.keyInfo,
227         x: 12,
228         y: 45,
229         fontSize: 14,
230         textAlign: "left",
231         textBaseline: "middle",
232         fill: config.fontColor,
233         cursor: "pointer"
234       },
235       name: "bottom-text-shape"
236     });
237     return container;
238   },
239   afterDraw: (cfg, group) => {
240     // 添加 移入/移出 事件
241     const ipLine = group.find(
242       (element) => element.get("name") === "ip-container-shape"
243     );
244     const ipBG = group.find(
245       (element) => element.get("name") === "ip-text-shape"
246     );
247     const removeRect = group.find(
248       (element) => element.get("name") === "remove-rect"
249     );
250     const removeText = group.find(
251       (element) => element.get("name") === "remove-text"
252     );
253     const onMouseEnter = () => {
254       ipLine && ipLine.attr("opacity", 1);
255       ipBG && ipBG.attr("opacity", 1);
256       removeRect && removeRect.attr("opacity", 1);
257       removeText && removeText.attr("opacity", 1);
258       graph.get("canvas").draw();
259     };
260     const onMouseLeave = () => {
261       ipLine && ipLine.attr("opacity", 0);
262       ipBG && ipBG.attr("opacity", 0);
263       removeRect && removeRect.attr("opacity", 0);
264       removeText && removeText.attr("opacity", 0);
265       graph.get("canvas").draw();
266     };
267     const lineclick = (e) => {
268       console.log(e.target.getParent().get("item").getModel());
269     };
270     group.on("mouseenter", () => {
271       onMouseEnter();
272     });
273     group.on("mouseleave", () => {
274       onMouseLeave();
275     });
276     ipBG &&
277       ipBG.on("click", (e) => {
278         lineclick(e);
279       });
280     ipLine &&
281       ipLine.on("click", (e) => {
282         lineclick(e);
283       });
284   }
285 };
286 G6.registerNode(
287   "card-node",
288   {
289     draw: (cfg, group) => {
290       const config = getNodeConfig(cfg);
291       /* 最外面的大矩形 */
292       const container = nodeBasicMethod.createNodeBox(
293         group,
294         config,
295         140,
296         60,
297         cfg
298       );
299       return container;
300     },
301     afterDraw: nodeBasicMethod.afterDraw,
302     getAnchorPoints() {
303       return [
304         [0.5, 0],
305         [0.5, 1],
306         [0, 0.5],
307         [1, 0.5]
308       ];
309     }
310   },
311   "rect"
312 );
313 G6.registerNode("sql", {
314   drawShape(cfg, group) {
315     const rect = group.addShape("rect", {
316       attrs: {
317         width: 100,
318         height: 50,
319         radius: 5,
320         stroke: "#5B8FF9",
321         fill: "#C6E5FF"
322       },
323       name: "rect-shape"
324     });
325     group.addShape("text", {
326       attrs: {
327         text: cfg.name,
328         x: 50,
329         y: 25,
330         fill: "#00287E",
331         fontSize: 14,
332         textAlign: "center",
333         textBaseline: "middle",
334         fontWeight: "bold"
335       },
336       name: "text-shape"
337     });
338     return rect;
339   },
340   // 设置 节点 之间 锚点 连接位置
341   getAnchorPoints() {
342     return [
343       [0.5, 0], // top-center
344       [0.5, 1] // bottom-center
345     ];
346   }
347 });
348 const data = {
349   nodes: [
350     {
351       id: "1",
352       dataType: "root",
353       name: "等级1",
354       type: "sql"
355     },
356     {
357       name: "任务1",
358       ip: "启用",
359       nodeError: true,
360       keyInfo:
361         "lsy3.novalocalsy3.novalocalsy3.novalocalsy3.novalocalsy3.novaloca",
362       id: "2",
363       comboId: "A",
364       dataType: "disabled",
365       type: "card-node"
366     },
367     {
368       name: "任务1",
369       ip: "启用",
370       nodeError: true,
371       keyInfo: "lsy3.novaloca",
372       id: "12",
373       comboId: "A",
374       dataType: "disabled",
375       type: "card-node"
376     },
377     {
378       name: "任务1",
379       ip: "启用",
380       nodeError: true,
381       keyInfo: "lsy3.novaloca",
382       id: "22",
383       comboId: "A",
384       dataType: "disabled",
385       type: "card-node"
386     },
387     {
388       name: "任务1",
389       ip: "启用",
390       nodeError: true,
391       keyInfo: "lsy3.novaloca",
392       id: "32",
393       comboId: "A",
394       dataType: "disabled",
395       type: "card-node"
396     },
397     {
398       id: "3",
399       name: "任务2",
400       comboId: "A",
401       ip: "禁用",
402       nodeError: false,
403       keyInfo: "lsy3.novaloca",
404       type: "card-node"
405     },
406     {
407       id: "13",
408       name: "任务2",
409       comboId: "A",
410       ip: "禁用",
411       nodeError: false,
412       keyInfo: "lsy3.novaloca",
413       type: "card-node"
414     },
415     {
416       id: "4",
417       dataType: "root",
418       name: "等级2",
419       type: "sql"
420     },
421     {
422       id: "5",
423       name: "任务1",
424       comboId: "B",
425       ip: "禁用",
426       nodeError: false,
427       keyInfo: "lsy3.novaloca",
428       type: "card-node"
429     },
430     {
431       id: "6",
432       name: "任务2",
433       comboId: "B",
434       ip: "禁用",
435       nodeError: false,
436       keyInfo: "lsy3.novaloca",
437       type: "card-node"
438     },
439     {
440       id: "7",
441       dataType: "root",
442       name: "等级3",
443       type: "sql"
444     },
445     {
446       id: "8",
447       name: "任务1",
448       comboId: "C",
449       ip: "禁用",
450       nodeError: false,
451       keyInfo: "lsy3.novaloca",
452       type: "card-node"
453     },
454     {
455       id: "9",
456       name: "任务2",
457       comboId: "C",
458       ip: "禁用",
459       nodeError: false,
460       keyInfo: "lsy3.novaloca",
461       type: "card-node"
462     }
463   ],
464   edges: [
465     {
466       source: "1",
467       target: "2"
468     },
469     {
470       source: "1",
471       target: "12"
472     },
473     {
474       source: "1",
475       target: "22"
476     },
477     {
478       source: "1",
479       target: "32"
480     },
481     {
482       source: "1",
483       target: "3"
484     },
485     {
486       source: "1",
487       target: "13"
488     },
489     {
490       source: "2",
491       target: "4"
492     },
493     {
494       source: "12",
495       target: "4"
496     },
497     {
498       source: "22",
499       target: "4"
500     },
501     {
502       source: "32",
503       target: "4"
504     },
505     {
506       source: "3",
507       target: "4"
508     },
509     {
510       source: "13",
511       target: "4"
512     },
513     {
514       source: "4",
515       target: "5"
516     },
517     {
518       source: "4",
519       target: "6"
520     },
521 
522     {
523       source: "5",
524       target: "7"
525     },
526     {
527       source: "6",
528       target: "7"
529     },
530     {
531       source: "7",
532       target: "8"
533     },
534     {
535       source: "7",
536       target: "9"
537     }
538   ],
539   combos: [
540     {
541       id: "A"
542     },
543     {
544       id: "B"
545     },
546     {
547       id: "C"
548     }
549   ]
550 };
551 
552 const width = 800;
553 const height = 800;
554 const graph = new G6.Graph({
555   container: "container",
556   width,
557   height,
558   layout: {
559     type: "dagre",
560     controlPoints: true,
561     rankdir: "H" // H 从上到下,LR 从左到右
562   },
563   plugins: [contextMenu, toolbar],
564   defaultEdge: {
565     style: {
566       endArrow: true,
567       offset: 45,
568       stroke: "#C2C8D5"
569     }
570   },
571   modes: {
572     default: ["drag-canvas", "zoom-canvas"]
573   },
574   fitView: true,
575   defaultCombo: {
576     type: "rect"
577   }
578 });
579 graph.data(data);
580 graph.render();
View Code

地址:https://codesandbox.io/s/suspicious-bose-qonry?file=/index.js

posted @ 2020-11-18 10:06  z春眠不觉晓z  阅读(1429)  评论(0编辑  收藏  举报