示例中Gojs版本参考 GoJS v2.2.22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="go.js"></script>
<style>
#myDiagramDiv {
width: 100%;
height: 900px;
background-color: #DAE4E4;
}
</style>
</head>
<body>
<div id="myDiagramDiv"></div>
</body>
<script>
// 1.如果只是树形图表 接口只返回nodeDataArray就够了 linkDatasArray可以在前端拼接
var $ = go.GraphObject.make;
// link_color: 'red' //线条颜色
// figure: 'Ellipse'//形状 圆形 Ellipse
// isg 是否是根节点 (对接口时不需要设置该属性)
var nodeDataArray = [
{ key: "start", text: '根节点', isg: true },
{ key: "1", text: '节点1', parent: 'start'},
{ key: "2", text: '节点2', parent: 'start'},
{ key: "3", text: '节点3', parent: 'start'},
{ key: "4", text: '节点4', parent: 'start'},
{ key: "4.1", text: '节点4.1', parent: '4'},
{ key: "4.2", text: '节点4.2', parent: '4'},
{ key: "4.3", text: '节点4.3', parent: '4'},
{ key: "4.4", text: '节点4.4', parent: '4'},
{ key: "4.5", text: '节点4.5', parent: '4'},
];
var linkDatasArray = [
{from: 'start', to: '1'},
{from: 'start', to: '2'},
{from: 'start', to: '3'},
{from: 'start', to: '4'},
{from: '4', to: '4.1'},
{from: '4', to: '4.2'},
{from: '4', to: '4.3'},
{from: '4', to: '4.4'},
{from: '4', to: '4.5'},
]
/** 右键菜单按钮样式 */
const buttonStyle = {
"ButtonBorder.fill": "white",
// "ButtonBorder.stroke": "darkcyan",
// "ButtonBorder.strokeWidth": 3,
"_buttonFillOver": "#E9F5FF",
"_buttonStrokeOver": "darkcyan",
"_buttonStrokePressed": "darkcyan",
"_buttonFillPressed": "pink",
visible: true,
margin: new go.Margin(0, 0, 0, 0),
}
/** 右键功能配置 */
var myContextMenu = $("ContextMenu", "Vertical",
{
background: "transparent",
width: 100,
},
$("ContextMenuButton",
{...buttonStyle},
$(go.TextBlock, {
text: "新增子节点",
margin: new go.Margin(10, 0, 10, 0),
}),
{
cursor: "pointer",
click: (e, obj) => {
const key = obj.part.data.key
console.log(key)
var node = {
parent: key,
key: nodeDataArray.length+1,
text: `子节点${nodeDataArray.length+1}`,
// loc: '0 0',//节点位置
}
var linknode = {
from: key,
to: nodeDataArray.length+1
}
diagram.model.addNodeData(node);
diagram.model.addLinkData(linknode);
},
}
),
$("ContextMenuButton",
{...buttonStyle},
$(go.TextBlock, {
text: "删除节点",
margin: new go.Margin(10, 0, 10, 0),
}),
{
cursor: "pointer",
click: (e, obj) => {
const key = obj.part.data.key
// 删除功能应该走接口的 以下代码只是在不对后端接口情况下的效果
// 先删线
delLinks(key)
// 再删除单个节点
var nodeData = diagram.model.findNodeDataForKey(key);
diagram.model.removeNodeData(nodeData);
delNodeLoop()
},
}
),
)
/** 需要一层一层删除 每次只删除没有父级的节点 */
function delNodeLoop(){
var removenodes=[];//需要删除的节点
const keylist = []//所以节点的key
diagram.model.nodeDataArray.map(item=>{
keylist.push(item.key)
})
diagram.model.nodeDataArray.map(item=>{
//如果找不到当前节点父级 && 当前节点不是跟节点
if(!keylist.includes(item.parent) && !item.isg){
removenodes.push(item)
delLinks(item.key)
}
})
diagram.model.removeNodeDataCollection(removenodes);
if(removenodes.length>0){
delNodeLoop()
}
}
/** 删除单个节点关联的线(删线条时,节点必须存在) */
function delLinks(key){
//删除单个节点关联的线
var removeLinks=[];
//首先拿到这个节点的对象
var node = diagram.findNodeForKey(key);
node.findLinksConnected().each(function(link) {
removeLinks.push(link.data);
});
diagram.model.removeLinkDataCollection(removeLinks);
}
/** 节点点击 */
function nodeClicked(e, obj){
// var key='1';//节点key
// var nodeData = diagram.model.findNodeDataForKey(key);
// nodeData.text = '666';//text 是节点data中的一个属性
// diagram.model.updateTargetBindings(nodeData);
}
/** 节点点击 */
function nodeContextmenu(e, obj){
console.log('1111')
// var key='1';//节点key
// var nodeData = diagram.model.findNodeDataForKey(key);
// nodeData.text = '666';//text 是节点data中的一个属性
// diagram.model.updateTargetBindings(nodeData);
}
/** 线条点击 */
function linkClicked(e, obj){
}
var diagram = $(go.Diagram, "myDiagramDiv", {
layout: $(go.TreeLayout, {
angle: 0,
nodeSpacing: 20,
layerSpacing: 70
}),
isReadOnly: false, // 是否禁用编辑
"toolManager.mouseWheelBehavior": go.ToolManager.WheelNone,//画布禁止鼠标滚轮事件
});
// diagram.layout.isOngoing = false//操作后是否允许节点自动布局 [禁止后会影响新建节点]
function makePort(name, spot, outinput, input){
return $(
go.Shape,
'Circle', {
fill: null,
stroke: null,
desiredSize: new go.Size(7, 7),
alignment: spot,
portId: name,
fromSpot: spot,
toSpot: spot,
fromLinkable: outinput,
toLinkable: input,
cursor: 'pointer'
}
)
}
function showSmallPorts(node, show){
node.ports.each(function(port){
port.fill = show?'#fff':null
})
}
// 创建一个节点模版
diagram.nodeTemplate = $(go.Node, "Auto",
{ contextMenu: myContextMenu },
$(go.Shape, {
figure: "RoundedRectangle",
fill: '#022b4d',//背景
stroke: '#0f68b7',//边框
strokeWidth: 2//边框宽度
}, new go.Binding("figure", "figure"), new go.Binding("fill", "color"), new go.Binding("stroke", "color")),
$(go.TextBlock, {
margin: 8,
stroke: '#fff',//文字颜色
editable: true,
}, new go.Binding("text", "", (e)=>{
console.log('e', e)
// 纵向排列文字
return e.text.split('').join('\n')
})),
{
click: nodeClicked,
},
// 连线功能 因为是树形的节点图 不需要自己再加线
makePort('T', go.Spot.Top, true, true),
makePort('L', go.Spot.Left, true, true),
makePort('R', go.Spot.Right, true, true),
makePort('B', go.Spot.Bottom, true, true),
{
mouseEnter:(e, node)=>{
showSmallPorts(node, true)
},
mouseLeave:(e, node)=>{
showSmallPorts(node, false)
}
}
);
// 创建一个箭头模版
diagram.linkTemplate = $(go.Link,
{
routing: go.Link.Orthogonal,// go.Link.Normal 直线 go.Link.Orthogonal 折线
corner: 10,//折线圆角
curve: go.Link.JumpOver,// go.Link.Bezier 曲线
},
$(go.Shape, { stroke: '#000', strokeWidth: 2,}, new go.Binding('stroke', 'link_color')),//线条
$(go.Shape, { stroke: '#000', strokeWidth: 2, toArrow: "OpenTriangle" }, new go.Binding('stroke', 'link_color')),//箭头
{
click: linkClicked
}
);
// diagram.toolManager.contextMenuTool.showContextMenu = (cm, obj) => {
// go.ContextMenuTool.prototype.showContextMenu.call(diagram.toolManager.contextMenuTool, cm, obj);
// const curCM = this.diagram.toolManager.contextMenuTool.currentContextMenu;
// if (curCM instanceof go.Adornment && curCM.adornedPart) {
// const shape = curCM.adornedPart.elt(0);
// if (shape instanceof go.Shape) {
// shape.stroke = "red";
// shape.fill = "green";
// }
// }
// };
// diagram.toolManager.contextMenuTool.hideContextMenu = () => {
// const curCM = this.diagram.toolManager.contextMenuTool.currentContextMenu;
// if (curCM instanceof go.Adornment && curCM.adornedPart) {
// const shape = curCM.adornedPart.elt(0);
// if (shape instanceof go.Shape) {
// shape.stroke = "green";
// shape.fill = "red";
// }
// }
// go.ContextMenuTool.prototype.hideContextMenu.call(diagram.toolManager.contextMenuTool);
// };
// 这里的数据后期就可以通过后端来获取
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDatasArray)
// diagram.model = new go.TreeModel(nodeDataArray);
diagram.commandHandler.canDeleteSelection = function(e) {
//用例获取选中的节点或线
return diagram.selection.all(function(nodeOrLink) {
console.log(nodeOrLink.data);
//判断是否存在不允许删除的节点或线
return false;
});
}
// 动态控制节点颜色变化
// var node = diagram.model.findNodeDataForKey("zip");
// diagram.model.setDataProperty(node, "color", "lightgreen");
</script>
</html>