在vue中使用gojs实现流程图功能
记录一下项目中的需求完成流程图示例,使用的是使用vue-cli搭建的项目,配合gojs来实现的,附上截图一份:(gojs版本如果更迭可能会影响使用,如需达到下图效果,可以联系我留言或者邮箱:lihai987789@qq,com)
保存之后是JSON格式的数据 , 便于保存:
由于没有中文文档,所以也摸索了一天的时间,终于是完成了需求:
第一步:引入GOJS(官方文档上下载的有水印,我用的是破解版本的,需要的可以找我拿一下QQ:1013218132)
话不多说,直接上代码: gojs的引入和echarts有点类似,也是画布完成的,都是根据ID值获取页面节点去渲染标签的
1 <template> 2 <div class="modelingBox"> 3 <div class="modelingHead"> 4 <el-button id="SaveButton" @click="save">保存</el-button> 5 <el-button @click="load">清空</el-button> 6 <textarea id="mySavedModel" style="width:100%;height:300px;display:none">{ "class": "go.GraphLinksModel", 7 "linkFromPortIdProperty": "fromPort", 8 "linkToPortIdProperty": "toPort", 9 "nodeDataArray": [ 10 11 ], 12 "linkDataArray": [ 13 ]} 14 </textarea> 15 <div class="msg_"> 16 <p>流程</p> 17 <el-select v-model="valueOptions" @change="selectChange" filterable placeholder="请选择"> 18 <el-option v-for="item in options" 19 :key="item.value" 20 :label="item.label" 21 :value="item.value"></el-option> 22 </el-select> 23 </div> 24 </div> 25 <div id="sample"> 26 <div style="width: 100%;height:100%; display: flex; justify-content: space-between"> 27 <div id="PaletteDiv" style="width: 200px; margin-right: 2px; background-color: #fafafa;"></div> 28 <div id="DiagramDiv" style="flex-grow: 1; height: 100%; background-color: #fafafa;"></div> 29 </div> 30 31 </div> 32 33 <el-dialog title="开始节点" :visible.sync="StartShow" width="30%" :before-close="handleClose"> 34 <span>这是一段信息</span> 35 <span slot="footer" class="dialog-footer"> 36 <el-button @click="StartShow = false">取 消</el-button> 37 <el-button type="primary" @click="StartShow = false">确 定</el-button> 38 </span> 39 </el-dialog> 40 <el-dialog title="审批节点" :visible.sync="ActivityShow" width="30%" :before-close="handleClose"> 41 <span>这是一段信息</span> 42 <span slot="footer" class="dialog-footer"> 43 <el-button @click="ActivityShow = false">取 消</el-button> 44 <el-button type="primary" @click="ActivityShow = false">确 定</el-button> 45 </span> 46 </el-dialog> 47 </div> 48 </template> 49 <script> 50 // import { init } from "./test"; 51 import go from "./gojs/go-cracked"; 52 export default { 53 data() { 54 return { 55 StartShow: false, 56 ActivityShow: false, 57 options: [ 58 { 59 value: '0', 60 label:'选项一' 61 }, 62 { 63 value: '1', 64 label: '选项二' 65 } 66 ], 67 valueOptions:'' 68 }; 69 }, 70 methods: { 71 selectChange() { 72 console.log(this.valueOptions,'select') 73 }, 74 handleClose() { 75 this.StartShow = false 76 this.ActivityShow = false 77 }, 78 nodeDoubleClick(e, node) { 79 console.log(e, node.part.data.category); 80 if (node.part.data.category == "Start") { 81 this.StartShow = true; 82 } else if (node.part.data.category == "Activity") { 83 this.ActivityShow = true; 84 } 85 }, 86 nodeStyle() { 87 var this_ = this; 88 return [ 89 { 90 locationSpot: go.Spot.Center, 91 isShadowed: false, 92 doubleClick: function (e, node) { 93 if (!e.diagram) return; 94 if (!e.diagram.allowLink) return; 95 var nCategory = node.part.data.category; 96 if (nCategory != "Activity" && nCategory != "Start") return; 97 this_.nodeDoubleClick(e, node); 98 }, 99 100 //鼠标悬停时显示node上的点 101 mouseEnter: function (e, obj) { 102 // this.displayNodePorts(obj.part, true); 103 var diagram = obj.part.diagram; 104 if (!diagram || diagram.isReadOnly || !diagram.allowLink) return; 105 obj.part.ports.each(function (port) { 106 port.stroke = true ? "white" : null; 107 }); 108 }, 109 mouseLeave: function (e, obj) { 110 // this.displayNodePorts(obj.part, false); 111 var diagram = obj.part.diagram; 112 if (!diagram || diagram.isReadOnly || !diagram.allowLink) return; 113 obj.part.ports.each(function (port) { 114 port.stroke = false ? "white" : null; 115 }); 116 }, 117 }, 118 new go.Binding("location", "loc", go.Point.parse).makeTwoWay( 119 go.Point.stringify 120 ), 121 ]; 122 }, 123 // displayNodePorts(node, show) { 124 // var diagram = node.diagram; 125 // if (!diagram || diagram.isReadOnly || !diagram.allowLink) return; 126 // node.ports.each(function (port) { 127 // port.stroke = show ? "white" : null; 128 // }); 129 // }, 130 load() { 131 this.myDiagram.model = go.Model.fromJson( 132 document.getElementById("mySavedModel").value 133 ); 134 }, 135 // Show the diagram's model in JSON format that the user may edit 点击生产JSON 136 save() { 137 document.getElementById( 138 "mySavedModel" 139 ).value = this.myDiagram.model.toJson(); 140 this.myDiagram.isModified = false; 141 console.log(this.myDiagram.model.toJson(), "this.myDiagram"); 142 }, 143 }, 144 mounted() { 145 var mySelf = this; 146 // if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this 147 var $ = go.GraphObject.make; // for conciseness in defining templates 148 149 mySelf.myDiagram = $(go.Diagram, "myDiagramDiv", { 150 initialContentAlignment: go.Spot.Center, // 居中显示 154 "undoManager.isEnabled": true, // 支持 Ctrl-Z 和 Ctrl-Y 操作 157 "animationManager.isEnabled": true, 161 172 }); //构建gojs对象 173 174 var tbFontNormal = "normal normal normal 14px 'Microsoft YaHei',Arial"; 175 var tbFontBold = "normal normal bold 14px 'Microsoft YaHei',Arial"; 176 228 //创建node上的点,默认透明,悬停后显示白色 229 function makeNodePort(name, spot, output, input) { 230 return $(go.Shape, "Circle", { 231 fill: "transparent", 232 stroke: null, 233 desiredSize: new go.Size(8, 8), 234 cursor: "pointer", 235 portId: name,239 toSpot: spot, 240 fromLinkable: output, 241 toLinkable: input, 242 }); 243 } 244 245 function textStyle() { 246 return { 247 font: "bold 11pt Lato, Helvetica, Arial, sans-serif", 248 stroke: "#F8F8F8", 249 }; 250 } 251 252 // // define the Node templates for regular nodes 253 //审批节点 254 mySelf.myDiagram.nodeTemplateMap.add( 255 "Activity", 256 $( 257 go.Node, 258 "Spot", 259 mySelf.nodeStyle(), 260 $( 261 go.Panel, 262 "Auto", 263 $(go.Shape, "Rectangle", { 264 desiredSize: new go.Size(80, 40), 265 minSize: new go.Size(20, 10),268 }), 269 $( 270 go.TextBlock, 271 "上级审批", 272 { 273 stroke: "#FFFFFF", 274 font: tbFontNormal, 275 textAlign: "center", 276 margin: new go.Margin(7, 5, 5, 5), 277 maxSize: new go.Size(50, 20), 278 }, 279 new go.Binding("text").makeTwoWay() 280 ) 281 ), 282 makeNodePort("T", go.Spot.Top, false, true), 283 makeNodePort("R", go.Spot.Right, true, true), 284 makeNodePort("B", go.Spot.Bottom, true, false), 285 makeNodePort("L", go.Spot.Left, true, true) 286 ) 287 ); 288 mySelf.myDiagram.nodeTemplateMap.add( 289 "Conditional", 290 $( 291 go.Node, 292 "Spot", 293 mySelf.nodeStyle(), 294 // the main object is a Panel that surrounds a TextBlock with a rectangular Shape 295 $( 296 go.Panel, 297 "Auto", 298 $( 299 go.Shape, 300 "Terminator", 301 { 302 minSize: new go.Size(120, 38), 303 fill: "#4AAA4A", 304 stroke: "#4AAA4A", 305 strokeWidth: 3.5, 306 }, 307 new go.Binding("figure", "figure") 308 ), 309 $( 310 go.TextBlock, 311 // textStyle(), 312 { 313 stroke: "#FFFFFF",316 margin: new go.Margin(7, 5, 5, 5), 317 maxSize: new go.Size(160, NaN), 318 }, 319 new go.Binding("text").makeTwoWay() 320 ) 321 ), 322 // four named ports, one on each side: 323 makeNodePort("T", go.Spot.Top, false, true), 324 makeNodePort("R", go.Spot.Right, false, true), 325 makeNodePort("L", go.Spot.Left, false, true) 326 ) 327 ); 328 329 mySelf.myDiagram.nodeTemplateMap.add( 330 "Start", 331 $( 332 go.Node, 333 "Spot", 334 mySelf.nodeStyle(), 335 $( 336 go.Panel, 337 "Auto", 338 $(go.Shape, "Terminator", { 339 // desiredSize: new go.Size(80, 40), 340 minSize: new go.Size(120, 38), 341 fill: "#1E90FF", 342 stroke: "#1E90FF", 343 strokeWidth: 3, 344 }), 345 $(go.TextBlock, "Start", textStyle(), new go.Binding("text")) 346 ), 347 // three named ports, one on each side except the top, all output only: 348 makeNodePort("T", go.Spot.Top, false, true),351 makeNodePort("L", go.Spot.Left, true, true) 352 ) 353 ); 354 355 mySelf.myDiagram.nodeTemplateMap.add( 356 "End", 357 $( 358 go.Node, 359 "Auto", 360 mySelf.nodeStyle(), 361 // $( 362 // go.Panel, 363 // "Auto", 364 $(go.Shape, "StopSign", { 365 desiredSize: new go.Size(43, 43), 366 // minSize: new go.Size(43, 43), 367 fill: "#F37B1D", 368 stroke: null, 369 strokeWidth: 3, 370 }), 371 $( 372 go.TextBlock, 373 "End", 374 { stroke: "#FFFFFF", font: tbFontNormal, textAlign: "center" }, 375 new go.Binding("text") 376 ), 377 // ), 378 // three named ports, one on each side except the bottom, all input only: 379 makeNodePort("T", go.Spot.Top, false, true), 380 makeNodePort("R", go.Spot.Right, false, true), 381 makeNodePort("B", go.Spot.Bottom, false, true), 382 makeNodePort("L", go.Spot.Left, false, true) 383 ) 384 ); 385 386 // taken from ../extensions/Figures.js: 387 go.Shape.defineFigureGenerator("File", function (shape, w, h) { 388 var geo = new go.Geometry(); 389 var fig = new go.PathFigure(0, 0, true); // starting point 390 geo.add(fig); 391 fig.add(new go.PathSegment(go.PathSegment.Line, 0.75 * w, 0));394 fig.add(new go.PathSegment(go.PathSegment.Line, 0, h).close()); 395 var fig2 = new go.PathFigure(0.75 * w, 0, false); 396 geo.add(fig2); 397 // The Fold 398 fig2.add(new go.PathSegment(go.PathSegment.Line, 0.75 * w, 0.25 * h)); 399 fig2.add(new go.PathSegment(go.PathSegment.Line, w, 0.25 * h)); 400 geo.spot1 = new go.Spot(0, 0.25); 401 geo.spot2 = go.Spot.BottomRight; 402 return geo; 403 }); 404 405 mySelf.myDiagram.nodeTemplateMap.add( 406 "Comment", 407 $( 408 go.Node, 409 "Auto", 410 mySelf.nodeStyle(), 411 $(go.Shape, "File", { 412 minSize: new go.Size(80, 38), 413 fill: "#F8EBBF", 414 stroke: "#E1C76F", 415 strokeWidth: 0, 416 }), 417 $( 418 go.TextBlock, 419 textStyle(), 420 { 421 stroke: "#555555", 422 font: tbFontNormal,425 maxSize: new go.Size(200, NaN), 426 editable: true, 427 }, 428 new go.Binding("text").makeTwoWay() 429 ) 430 // no ports, because no links are allowed to connect with a comment 431 ) 432 ); 433 434 // // replace the default Link template in the linkTemplateMap 435 mySelf.myDiagram.linkTemplate = $( 436 go.Link, // the whole link panel 437 { 438 routing: go.Link.AvoidsNodes, 439 curve: go.Link.JumpOver, 440 corner: 2, 441 fromShortLength: 1, 442 toShortLength: 2, 443 relinkableFrom: true, 444 relinkableTo: true, 445 reshapable: true, 446 resegmentable: true, 447 mouseEnter: function (e, link) { 448 link.findObject("HIGHLIGHT").stroke = "rgba(30,144,255,0.5)"; 449 }, 450 mouseLeave: function (e, link) { 451 link.findObject("HIGHLIGHT").stroke = "transparent"; 452 }, 453 // selectionAdorned: false, 454 doubleClick: function (e, link) { 455 if (!e.diagram) return; 456 if (!e.diagram.allowLink) return;459 }, 460 new go.Binding("points").makeTwoWay(), 461 $( 462 go.Shape, // the highlight shape, normally transparent 463 { 464 isPanelMain: true, 465 strokeWidth: 8, 466 stroke: "transparent", 467 name: "HIGHLIGHT", 468 } 469 ), 470 $( 471 go.Shape, // the link path shape 472 { isPanelMain: true, stroke: "gray", strokeWidth: 2 }, 473 new go.Binding("stroke", "isSelected", function (sel) { 474 return sel ? "dodgerblue" : "gray"; 475 }).ofObject() 476 ), 477 $( 478 go.Shape, // the arrowhead 479 { toArrow: "standard", strokeWidth: 0, fill: "gray" } 480 ), 481 $( 482 go.Panel, 483 "Auto", // the link label, normally not visible 484 { 485 visible: false, 486 name: "LABEL", 487 segmentIndex: 2, 488 segmentFraction: 0.5, 489 }, 490 new go.Binding("visible", "visible").makeTwoWay(), 491 $( 492 go.Shape, 493 "RoundedRectangle", // the label shape 494 { fill: "#F8F8F8", strokeWidth: 0 } 495 497 go.TextBlock, 498 "Yes", // the label 499 { 500 textAlign: "center", 501 font: "10pt helvetica, arial, sans-serif", 502 stroke: "#333333", 503 editable: true, 504 }, 505 new go.Binding("text").makeTwoWay() 506 ) 507 ) 508 ); 509 510 // Make link labels visible if coming out of a "conditional" node. 511 // This listener is called by the "LinkDrawn" and "LinkRelinked" DiagramEvents. 512 function showLinkLabel(e) { 513 var label = e.subject.findObject("LABEL"); 514 if (label !== null) 515 label.visible = e.subject.fromNode.data.category === "Conditional"; 516 } 517 518 // temporary links used by LinkingTool and RelinkingTool are also orthogonal: 519 mySelf.myDiagram.toolManager.linkingTool.temporaryLink.routing = 520 go.Link.Orthogonal; 521 mySelf.myDiagram.toolManager.relinkingTool.temporaryLink.routing = 522 go.Link.Orthogonal; 523 524 mySelf.load(); // load an initial diagram from some JSON text 525 526 // initialize the Palette that is on the left side of the page 527 mySelf.myPalette = $( 528 go.Palette, 529 "myPaletteDiv", // must name or refer to the DIV HTML element 530 { 531 // Instead of the default animation, use a custom fade-down 532 // "animationManager.initialAnimationStyle": go.AnimationManager.None, 533 // "InitialAnimationStarting": animateFadeDown, // Instead, animate with this function 534 535 nodeTemplateMap: mySelf.myDiagram.nodeTemplateMap, // share the templates used by myDiagram 536 model: new go.GraphLinksModel([ 537 // specify the contents of the Palette 538 { category: "Start", text: "开始" }, 539 { category: "Activity", text: "审批" }, 540 { category: "Conditional", text: "完成" }, 541 { category: "End", text: "撤销" }, 542 { category: "Comment", text: "备注" }, 543 ]), 544 } 545 ); 546 547 // This is a re-implementation of the default animation, except it fades in from downwards, instead of upwards. 548 function animateFadeDown(e) { 549 var diagram = e.diagram; 550 var animation = new go.Animation(); 551 animation.isViewportUnconstrained = true; // So Diagram positioning rules let the animation start off-screen 552 animation.easing = go.Animation.EaseOutExpo; 553 animation.duration = 900; 554 // Fade "down", in other words, fade in from above 555 animation.add( 556 diagram, 557 "position", 558 diagram.position.copy().offset(0, 200), 559 diagram.position 560 ); 561 animation.add(diagram, "opacity", 0, 1); 562 animation.start(); 563 } 564 }, 565 }; 566 </script> 567 568 <style lang="less" scoped> 569 .modelingBox{ 570 width:100%; 571 height:96%; 572 } 573 .modelingHead{ 574 height:40px; 575 line-height:40px; 576 .msg_ { 577 float: right; 578 position: relative; 579 p { 580 display: inline; 581 margin-right: 15px; 582 position: absolute; 583 top: 2px; 584 left: -40px; 585 } 586 } 587 } 588 #sample { 589 width: 100%; 590 height: calc(100% - 40px); 591 } 592 </style>
需要转载的请表明出处,非本人同意禁止商业用途
选项一
技术讨论
QQ
1013218132