demo 3

31 阅读2分钟
<!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;
        }

        /* 添加模态框的样式 */
        .modal {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 1000;
        }
    </style>
</head>

<body>
    <div id="app">
        <div class="left">
            <div class="left-item" v-for="item in tasklist" @click="addNode(item)" @mouseover="handleMouseOver"
                @mouseleave="cancelModal">
                {{ item.name }}
            </div>
        </div>
        <div class="right" id="graph-container"></div>
        <!-- 在 #app 内部添加模态框的 HTML 结构 -->
        <div v-if="showModal" class="modal" @click="showModal = false">
            <p>模态框内容</p>
            <button @click.stop="goToOtherPage">前往其他页面</button>
        </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: 1, name: 'StartStartStartStartStart', color: 'orange' },
                    { id: 2, name: 'ProcessProcessProcessProcess', color: 'green' },
                    { id: 3, name: 'EndEndEndEndEndEndEndEndEnd', color: 'yellow' }
                ],
                graph: null,
                selectedNode: null,
                nodeArr: [],
                nodeRelation: [],

                showModal: false,
                modalTimeout: null,
                
                _tooltipActive:false
            },
            mounted() {
                this.initGraph();
            },
            methods: {
                handleMouseOver() {
                    this.modalTimeout = setTimeout(() => {
                        this.showModal = true;
                    }, 2000);
                },
                cancelModal() {
                    clearTimeout(this.modalTimeout);
                },
                goToOtherPage() {
                    location.href = '/paint3'; // 替换为目标页面的 URL
                },
                initGraph() {
                    G6.registerBehavior('show-tooltip', {
                        getEvents() {
                            return {
                                'node:mouseenter': 'onMouseEnter',
                                'node:mouseleave': 'onMouseLeave',
                            };
                        },
                        onMouseEnter(evt) {
                            const node = evt.item;
                            const model = node.getModel();
                            const label = model.label; // 完整的 label 文本
                            // 创建提示框
                            const tooltip = document.createElement('div');
                            tooltip.innerHTML = label;
                            // tooltip.innerHTML = `<span>${label}</span><button @click="copyText('${label}')">复制</button>`;;
                            tooltip.style.position = 'absolute';
                            tooltip.style.backgroundColor = 'white';
                            tooltip.style.border = '1px solid #e2e2e2';
                            tooltip.style.padding = '5px';
                            tooltip.style.borderRadius = '4px';
                            
                            tooltip.style.pointerEvents = 'auto'; // 确保提示框不会干扰其他交互

                            // 点击复制文本
                            tooltip.addEventListener('click', () => {
                                navigator.clipboard.writeText(label).then(() => {
                                    // alert('复制成功');
                                    tooltip.remove();
                                    this.graph.set('tooltip', null);
                                }).catch(err => {
                                    console.error('复制失败:', err);
                                });
                            });

                            tooltip.addEventListener('mouseenter', () => {
                                this._tooltipActive = true;
                            });

                            tooltip.addEventListener('mouseleave', () => {
                                this._tooltipActive = false;
                                tooltip.remove();
                                this.graph.set('tooltip', null);
                            });

                            // 保存 tooltip 元素到图形实例中,以便稍后移除
                            this.graph.set('tooltip', tooltip);
                            document.body.appendChild(tooltip);

                            // 定位提示框
                            const canvasPosition = this.graph.getCanvasByPoint(model.x, model.y);
                            tooltip.style.left = `${canvasPosition.x + 290}px`;  //加上左边menu的宽度
                            tooltip.style.top = `${canvasPosition.y - 30}px`;    //减去1.5倍半径

                        },

                        onMouseLeave() {
                            const tooltip = this.graph.get('tooltip');
                            if (tooltip && !this._tooltipActive) {
                                tooltip.remove();
                                this.graph.set('tooltip', null);
                            }
                            
                        },

                    });

                    G6.registerNode('customCircleNode', {
                        draw(cfg, group) {
                            const radius = 40; // 设置圆的半径

                            // 创建圆形
                            const circle = group.addShape('circle', {
                                attrs: {
                                    x: 0, // 圆心坐标
                                    y: 0,
                                    r: radius,
                                    // stroke: '#5B8FF9',
                                    fill: cfg.color, // 使用 cfg.color 设置填充颜色
                                },
                            });

                            // 添加文本
                            if (cfg.label) {
                                group.addShape('text', {
                                    attrs: {
                                        x: 0, // 居中
                                        y: 0,
                                        textAlign: 'center',
                                        textBaseline: 'middle',
                                        text: cfg.label.length > 10 ? cfg.label.slice(0, 10) + '...' : cfg.label,
                                        fill: '#333',
                                    },
                                });
                            }

                            return circle;
                        },
                    }, 'single-shape');

                    this.graph = new G6.Graph({
                        container: 'graph-container',
                        width: 1100,
                        height: 600,
                        fitView: true, // 确保图形适应视图
                        fitViewPadding: [20, 40, 50, 20], // 设置适应视图时的内边距
                        modes: {
                            default: ['drag-node', 'drag-canvas', 'zoom-canvas', 'show-tooltip']
                        },
                        layout: {
                            type: 'force', // 使用力导向布局
                            preventOverlap: true, // 防止节点重叠
                            linkDistance: 100, // 边的距离
                            // 可以根据需要配置更多布局参数
                        },

                        defaultNode: {
                            type: 'customCircleNode',
                        },
                        defaultEdge: {
                            style: {
                                endArrow: {
                                    path: G6.Arrow.triangle(10, 20, 0),
                                    fill: 'green',
                                    offset: -1 // 将箭头向后移动一点,使其更靠近节点
                                },
                                lineWidth: 3, // 设置边的宽度
                                stroke: 'green'
                            }
                        },
                    });
                    this.graph.on('node:click', evt => {
                        const clickedNode = evt.item;
                        if (this.selectedNode && this.selectedNode !== clickedNode) {
                            const sourceId = this.selectedNode.get('id');
                            const targetId = clickedNode.get('id');
                            const sourceLabel = this.selectedNode.getModel().label;
                            const targetLabel = clickedNode.getModel().label;
                            // 检查新边是否会形成闭环
                            if (!this.doesEdgeCreateLoop(sourceId, targetId)) {
                                this.graph.addItem('edge', {
                                    source: sourceId,
                                    target: targetId,
                                    style: {
                                        endArrow: {
                                            path: G6.Arrow.triangle(10, 20, 5),
                                            fill: 'green'
                                        }
                                    }
                                });
                                // 添加边关系到 nodeRelation
                                this.nodeRelation.push(`${sourceLabel}>>${targetLabel}`);
                                console.log(this.nodeRelation, 'this.nodeRelation--add')
                                // 添加node到 nodeArr
                                if (!this.nodeArr.includes(sourceLabel)) {
                                    this.nodeArr.push(sourceLabel);
                                }
                                if (!this.nodeArr.includes(targetLabel)) {
                                    this.nodeArr.push(targetLabel);
                                }
                                console.log(this.nodeArr, 'this.nodeArr--add')
                            }
                            this.selectedNode = null;
                        } else {
                            this.selectedNode = clickedNode;
                        }
                    });
                    this.graph.on('edge:click', (evt) => {
                        const edge = evt.item;
                        const sourceLabel = edge.getSource().getModel().label;
                        const targetLabel = edge.getTarget().getModel().label;

                        // 从 nodeRelation 中删除边
                        const edgeIndex = this.nodeRelation.indexOf(`${sourceLabel}>>${targetLabel}`);
                        if (edgeIndex > -1) {
                            this.nodeRelation.splice(edgeIndex, 1);
                        }

                        // 从 nodeArr 中删除不再参与连线的节点
                        [sourceLabel, targetLabel].forEach(label => {
                            if (!this.nodeRelation.some(rel => rel.includes(label))) {
                                const nodeIndex = this.nodeArr.indexOf(label);
                                if (nodeIndex > -1) {
                                    this.nodeArr.splice(nodeIndex, 1);
                                }
                            }
                        });
                        console.log(this.nodeRelation, 'this.nodeRelation--del')
                        console.log(this.nodeArr, 'this.nodeArr---del')

                        // 删除边
                        this.graph.removeItem(edge);
                    });

                },

                addNode(item) {
                    if (this.graph.findById(`node-${item.id}`)) return;
                    console.log(item.id, 'node-id-------')

                    this.cancelModal(); // 取消悬停事件的模态框显示

                    const model = {
                        id: `node-${item.id}`,
                        label: item.name,
                        color: item.color, // 将颜色传递给节点
                        x: Math.random() * 800,
                        y: Math.random() * 600,
                    };
                    this.graph.addItem('node', model);
                },
                doesEdgeCreateLoop(sourceId, targetId) {
                    const visited = new Set();
                    let hasLoop = false;

                    const dfs = (nodeId) => {
                        if (nodeId === sourceId) {
                            hasLoop = true;
                            return;
                        }
                        if (visited.has(nodeId)) {
                            return;
                        }
                        visited.add(nodeId);

                        const edges = this.graph.getEdges();
                        edges.forEach(edge => {
                            if (edge.getSource().getID() === nodeId) {
                                const nextNodeId = edge.getTarget().getID();
                                if (!visited.has(nextNodeId)) {
                                    dfs(nextNodeId);
                                }
                            }
                        });
                    };

                    dfs(targetId);
                    return hasLoop;
                }

            }
        });
    </script>
</body>

</html>