项目需要用到GoJs实现可视化,先说一下需求吧
1、实现可拖拽新建节点
2、点击节点可修改保存节点信息
3、可以手动新建节点之间的连线
4、点击连线可修改保存连线信息
5、节点鼠标右键菜单
网上的都是零零碎碎的,查了很多文档然后终于实现了上述的所有功能...
废话不多说,直接上代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>dsDemo</title>
<meta name="description" content="A workflow diagram showing navigation between web pages, with an editable list of comments and to-dos." />
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Copyright 1998-2019 by Northwoods Software Corporation. -->
</head>
<body>
<div id="container">
<div style="width: 100%; display: flex; justify-content: space-between">
<div id="myPaletteDiv" style="width: 150px; margin-right: 2px; background-color: whitesmoke; border: solid 1px black"></div>
<div id="myDiagramDiv" style="flex-grow: 1; height: 600px; border: solid 1px black"></div>
<div id="nodeDetail" style="width:200px; margin-right: 2px; background-color: whitesmoke; border: solid 1px black;padding:10px">
<p style="text-align: left">节点名称:<input type="text" v-model="selectNode.name"/></p>
<p style="text-align: left">节点描述:</p>
<textarea v-model="selectNode.description" style="width:98%;height:200px"></textarea>
<button id="SaveButton" @click="saveNode">保存</button>
</div>
<div id="linkDetail" style="width:200px; margin-right: 2px; background-color: whitesmoke; border: solid 1px black;padding:10px;display:none;">
<p style="text-align: left">连线名称:<input type="text" v-model="selectLink.name"/></p>
<p style="text-align: left">连线描述:</p>
<textarea v-model="selectLink.description" style="width:98%;height:200px"></textarea>
<button id="SaveButton" @click="saveLink">保存</button>
</div>
</div>
</div>
</body>
<script src="./go.js"></script>
<script src="./vue.min.js"></script>
<script id="code">
new Vue({
el:'#container',
data:{
selectNode:{
name:'',
description:''
},
selectLink:{
name:'',
description:''
}
},
methods:{
saveNode(){ //保存节点信息
this.myDiagram.model.updateTargetBindings(this.selectNode);
},
saveLink(){
this.myDiagram.model.updateTargetBindings(this.selectLink);
}
},
mounted(){
var mySelf = this;
const MAKE = go.GraphObject.make;
mySelf.myDiagram = MAKE(go.Diagram, "myDiagramDiv",{
"draggingTool.dragsLink": true,
"undoManager.isEnabled": true ,// 支持 Ctrl-Z 和 Ctrl-Y 操作
"toolManager.hoverDelay": 100,//tooltip提示显示延时
"toolManager.toolTipDuration": 10000,//tooltip持续显示时间
//isReadOnly:true,//只读
// "grid.visible":true,//显示网格
allowMove:true,//允许拖动
// allowDragOut:true,
allowDelete:false,
allowCopy:false,
allowClipboard:false,
"toolManager.mouseWheelBehavior": go.ToolManager.WheelZoom,//有鼠标滚轮事件放大和缩小,而不是向上和向下滚动
});
var that=this;
mySelf.myDiagram.addDiagramListener("ObjectSingleClicked",function (e) {
if(e.subject.part.data.key == undefined){
that.selectLink=e.subject.part.data;
document.getElementById("nodeDetail").style.display="none";
document.getElementById("linkDetail").style.display="block";
}else{
that.selectNode=e.subject.part.data;
document.getElementById("nodeDetail").style.display="block";
document.getElementById("linkDetail").style.display="none";
}
});
mySelf.myDiagram.addModelChangedListener(function (evt) {
if (!evt.isTransactionFinished) return;
var txn = evt.object;
if (txn === null) return;
txn.changes.each(function(e) {
if (e.modelChange !== "nodeDataArray") return;
if (e.change === go.ChangedEvent.Insert) {
that.selectNode=e.newValue;
}
});
});
// 定义个简单的 Node 模板
mySelf.myDiagram.nodeTemplate =
MAKE(go.Node, "Auto",
new go.Binding("location", "loc", go.Point.parse),
MAKE(go.Shape, "RoundedRectangle",
{
fill: "#fff",stroke: '#289de9',strokeWidth:1
}),
MAKE(go.TextBlock, "new node",
{
margin: 8 ,
font: "bold 14px Helvetica Neue",
},
new go.Binding("text", "name")));
function makePort(name, align, spot, output, input) {
var horizontal = align.equals(go.Spot.Top) || align.equals(go.Spot.Bottom);
return MAKE(go.Shape,
{
fill: "transparent", // changed to a color in the mouseEnter event handler
strokeWidth: 0, // no stroke
width: horizontal ? NaN : 8, // if not stretching horizontally, just 8 wide
height: !horizontal ? NaN : 8, // if not stretching vertically, just 8 tall
alignment: align, // align the port on the main Shape
stretch: (horizontal ? go.GraphObject.Horizontal : go.GraphObject.Vertical),
portId: name, // declare this object to be a "port"
fromSpot: spot, // declare where links may connect at this port
fromLinkable: output, // declare whether the user may draw links from here
toSpot: spot, // declare where links may connect at this port
toLinkable: input, // declare whether the user may draw links to here
cursor: "pointer", // show a different cursor to indicate potential link point
mouseEnter: function(e, port) { // the PORT argument will be this Shape
if (!e.diagram.isReadOnly) port.fill = "rgba(255,0,255,0.5)";
},
mouseLeave: function(e, port) {
port.fill = "transparent";
}
});
}
function textStyle() {
return {
font: "bold 11pt Helvetica, Arial, sans-serif",
stroke: "whitesmoke"
}
}
mySelf.myDiagram.nodeTemplateMap.add("", // the default category
MAKE(go.Node, "Auto",
{
contextMenu: // define a context menu for each node
MAKE("ContextMenu", // that has one button
MAKE("ContextMenuButton",
MAKE(go.TextBlock, "Undo"),
{
click: function(e, obj) {
console.log(1)
}
},
))
},
MAKE(go.Panel, "Auto",
MAKE(go.Shape, "RoundedRectangle",
{
fill: "#fff",stroke: '#289de9',strokeWidth:1
}),
MAKE(go.TextBlock, "new node",
{
margin: 8,
font: "bold 14px Helvetica Neue",
},
new go.Binding("text","name").makeTwoWay())
),
// four named ports, one on each side:
makePort("T", go.Spot.Top, go.Spot.TopSide, false, true),
makePort("L", go.Spot.Left, go.Spot.LeftSide, true, true),
makePort("R", go.Spot.Right, go.Spot.RightSide, true, true),
makePort("B", go.Spot.Bottom, go.Spot.BottomSide, true, false)
));
//定义连线
var linkSelectionAdornmentTemplate =
MAKE(go.Adornment, "Link",
MAKE(go.Shape,
// isPanelMain declares that this Shape shares the Link.geometry
{ isPanelMain: true, fill: null, stroke: "deepskyblue", strokeWidth: 0 }) // use selection object's strokeWidth
);
mySelf.myDiagram.linkTemplate =
MAKE("Link",
MAKE("Shape",
{ strokeWidth: 2,stroke: '#289de9'}),
MAKE("Shape",
{ toArrow: "Standard", fill:"#289de9",stroke:null}),
MAKE(go.TextBlock, "new link",
{
margin: 8,
font: "bold 14px Helvetica Neue",
segmentOffset: new go.Point(0, -10)
},
new go.Binding("text","name").makeTwoWay()),
);
var palette =
MAKE(go.Palette, "myPaletteDiv", // create a new Palette in the HTML DIV element
{
// share the template map with the Palette
nodeTemplateMap: mySelf.myDiagram.nodeTemplateMap,
autoScale: go.Diagram.Uniform // everything always fits in viewport
});
palette.model.nodeDataArray = [
{}, // default node
];
let myModel = MAKE(go.GraphLinksModel);
myModel.nodeDataArray =
[
// {key:"1", name: "a",age:12,description:'我是a节点'},
//{key:"2", parent:"1", name: "b",age:13 ,description:'我是b节点'},
//{key:"3", parent:"1", name: "c",age:13 ,description:'我是b节点'},
];
myModel.linkDataArray = [
//{from:"1",to:"2"},
//{from:"1",to:"3"},
// {from:"1",to:"4"},
// {from:"1",to:"5"},
];
mySelf.myDiagram.model = myModel;
}
})
</script>
</html>
关于GoJs的方法
(Ps:这里只写我觉得比较常用的,其他的请自行查阅其他文档,谢谢!)
1.更新数据
this.myDiagram.model.updateTargetBindings(this.selectNode);
参数是你当前编辑的数据,这里可以是节点信息也可以是连线信息
2.mySelf.myDiagram.addDiagramListener("ObjectSingleClicked",function (e) {
console.log(e.subject.part.data);
});
监听点击事件,可以是点击节点也可以是连线,如果要区分,可以判断获取的数据里面是否有key属性,节点有key属性,连线有from,to属性。
if(e.subject.part.data.key == undefined){
.......
}
3.监听新拖拽到画布的节点
mySelf.myDiagram.addModelChangedListener(function (evt) {
if (!evt.isTransactionFinished) return;
var txn = evt.object;
if (txn === null) return;
txn.changes.each(function(e) {
if (e.modelChange !== "nodeDataArray") return;
if (e.change === go.ChangedEvent.Insert) {
that.selectNode=e.newValue;
}
});
});
4、通过key值去查找节点
mySelf.myDiagram.findNodeForKey(key).data
这里的key值是节点的key值