<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flowchart Editor</title>
<style>
#app {
display: flex;
width: 100%;
height: 100%;
}
.left {
flex: 1;
border: 1px solid #ddd;
padding: 10px;
}
.left-item {
cursor: pointer;
padding: 5px;
border: 1px solid #ddd;
margin-bottom: 5px;
}
.right {
flex: 3;
border: 1px solid #ddd;
}
</style>
</head>
<body>
<div id="app">
<div class="left">
<div class="left-item" v-for="item in tasklist" @click="addNode(item)">
{{ item.name }}
</div>
</div>
<div class="right" id="graph-container"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.7/dist/g6.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
tasklist: [
{ id: 'Start1111111111111111', name: 'Start1111111111111111', color: 'orange' },
{ id: 'Process2222222222222', name: 'Process2222222222222', color: 'green' },
{ id: 'End33333333333333333333', name: 'End33333333333333333333', color: 'yellow' },
{ id: 'Java_144444444444444444444', name: 'Java_144444444444444444444', color: 'blue' },
{ id: 'Java_255555555555555555555', name: 'Java_255555555555555555555', color: 'blue' },
],
graph: null,
javaNodeEdges: {}, // 记录 Java_ 节点出边状态
},
mounted() {
this.initGraph();
},
methods: {
initGraph() {
this.graph = new G6.Graph({
container: 'graph-container',
width: 1100,
height: 600,
modes: {
default: [
'drag-node',
'drag-canvas',
'zoom-canvas',
{
type: 'create-edge',
key: 'shift', // 配置创建边的按键为 Shift 键
trigger: 'drag' // 修改为拖拽触发创建边
}
]
},
defaultNode: {
type: 'circle',
size: [60],
style: {
fill: '#9FD7FF',
stroke: '#5B8FF9',
},
labelCfg: {
position: 'center',
style: {
textAlign: 'center',
textBaseline: 'middle',
fill: '#000',
fontSize: 12
}
}
},
defaultEdge: {
style: {
lineWidth: 2,
lineAppendWidth: 5,
stroke: '#BDBEBF',
endArrow: {
path: G6.Arrow.triangle(5, 5, 5), // 自定义边的箭头样式
fill: '#BDBEBF',
},
},
},
});
// 监听节点按下事件,判断是否按下 Shift 键来锁定节点
this.graph.on('node:mousedown', (evt) => {
const mouseEvent = evt.originalEvent;
if (mouseEvent.shiftKey) {
evt.item.lock(); // 锁定节点
}
});
// 监听节点抬起事件,判断是否按下 Shift 键来解锁节点
this.graph.on('node:mouseup', (evt) => {
const mouseEvent = evt.originalEvent;
if (mouseEvent.shiftKey) {
evt.item.unlock(); // 解锁节点
}
});
// 监听边创建完成后的事件回调
this.graph.on('aftercreateedge', (evt) => {
const edge = evt.edge;
const sourceNode = edge.getSource();
const sourceLabel = sourceNode.getModel().label;
if (sourceLabel.startsWith('Java_')) {
const sourceId = sourceNode.get('id');
// 初始化 Java_ 节点的出边状态
if (!this.javaNodeEdges[sourceId]) {
this.javaNodeEdges[sourceId] = { count: 0, lastColor: null };
}
const edgeData = this.javaNodeEdges[sourceId];
if (edgeData.count >= 2) {
alert('Java_开头节点只能有两条出边');
this.graph.removeItem(edge);
return;
}
// 检查当前是否有剩余连线以及其颜色
const remainingEdges = this.graph.getEdges().filter((e) => e.getSource().getID() === sourceId);
let edgeColor = 'green'; // 默认绿色
if (remainingEdges.some((e) => e.getModel().style.stroke === 'green')) {
edgeColor = 'red'; // 如果有绿色边,下一条为红色
}
this.graph.updateItem(edge, {
style: { stroke: edgeColor },
});
// 更新出边状态
edgeData.count += 1;
edgeData.lastColor = edgeColor;
}
});
// 监听双击事件删除节点和连线
this.graph.on('node:dblclick', (evt) => {
const node = evt.item;
const nodeId = node.get('id');
// 获取所有与此节点相关的连线
const relatedEdges = this.graph.findAll('edge', (edge) => edge.getSource().getID() === nodeId || edge.getTarget().getID() === nodeId);
relatedEdges.forEach(edge => {
const sourceNode = edge.getSource();
const sourceId = sourceNode.get('id');
if (sourceNode.getModel().label.startsWith('Java_') && this.javaNodeEdges[sourceId]) {
this.javaNodeEdges[sourceId].count -= 1;
if (this.javaNodeEdges[sourceId].count === 0) {
this.javaNodeEdges[sourceId].lastColor = null;
} else {
const remainingEdges = this.graph.getEdges().filter((e) => e.getSource().getID() === sourceId);
if (remainingEdges.some((e) => e.getModel().style.stroke === 'green')) {
this.javaNodeEdges[sourceId].lastColor = 'green';
} else {
this.javaNodeEdges[sourceId].lastColor = 'red';
}
}
this.graph.removeItem(edge);
}
});
this.graph.removeItem(node);
if (this.javaNodeEdges[nodeId]) {
delete this.javaNodeEdges[nodeId];
}
});
this.graph.on('edge:dblclick', (evt) => {
const edge = evt.item;
const sourceNode = edge.getSource();
const sourceId = sourceNode.get('id');
if (sourceNode.getModel().label.startsWith('Java_') && this.javaNodeEdges[sourceId]) {
const edgeColor = edge.getModel().style.stroke;
if (edgeColor === 'red') {
this.javaNodeEdges[sourceId].lastColor = 'red';
} else if (edgeColor === 'green') {
this.javaNodeEdges[sourceId].lastColor = 'green';
}
this.javaNodeEdges[sourceId].count -= 1;
const remainingEdges = this.graph.getEdges().filter((e) => e.getSource().getID() === sourceId);
if (remainingEdges.some((e) => e.getModel().style.stroke === 'green')) {
this.javaNodeEdges[sourceId].lastColor = 'green';
} else {
this.javaNodeEdges[sourceId].lastColor = 'red';
}
}
this.graph.removeItem(edge);
});
},
addNode(item) {
if (this.graph.findById(item.name)) return;
// 处理文本长度,超出部分添加省略号
const MAX_LENGTH = 10;
const label = item.name.length > MAX_LENGTH ? item.name.substring(0, MAX_LENGTH) + '...' : item.name;
// 获取画布的中心坐标
const graphWidth = this.graph.get('width');
const graphHeight = this.graph.get('height');
const centerX = graphWidth / 2;
const centerY = graphHeight / 2;
// 轻微偏移量,防止节点完全重叠
const offset = Math.random() * 50; // 随机生成一个偏移量
this.graph.addItem('node', {
id: item.name,
label: label,
color: item.color,
x: centerX , // 添加偏移量
y: centerY ,
});
},
}
});
</script>
</body>
</html>