Vue3使用AntvG6写拓扑图,可添加修改删除节点和边

npm安装antv/g6

npm 复制代码
npm install @antv/g6 --save

上代码

vue 复制代码
<template>
    <div id="tpt1" ref="container" style="width: 100%;height: 100%;"></div>
</template>

<script setup>
    import { Renderer as SVGRenderer } from '@antv/g-svg';//通过svg方式呈现
    import { Graph,iconfont } from '@antv/g6';
    import { onMounted, onUnmounted, ref,nextTick,computed } from 'vue';
    const container = ref();
    const nodes = ref([
        { id: '1', style: { x: 550, y: 100 },data:{id:'1',name:"wifi路由器",type:"路由器"} }//数据格式
    ])
    const edges = ref([
        { id:'1-3-g1',source: '1', target: '3',data:{id:1,name:"线路1",status:0,outRate:"80MB/s",inRate:"50MB/s",scoureName:"url",scoureType:"",scoureUrl:"",targetName:"http",targetType:"",targetUrl:"",watchDevice:"",watchUrl:""} }//数据格式
    ])
    let graph:any = null
    
    onMounted(() => {
        updateChart();
    })
    onUnmounted(() => {
        graph && graph.clear();
    });
    //获取所有节点
    const getNodeData = () => {
        return graph.getNodeData()
    }
    //获取所有边
    const getEdgeData = () => {
        return graph.getEdgeData()
    }
    //获取缩放比例
    const getZoom = () => {
        return graph.getZoom();
    }
    const updateChart = () => {
        const style = document.createElement('style');
        style.innerHTML = `@import url(${iconfont.css});`;
        document.head.appendChild(style);
        graph = new Graph({
            container: container.value,
            data: {
                nodes: nodes.value,
                edges: edges.value,
            },
            edge: {
                // type:"extra-label-edge",
                style: {//线条的样式
                    cursor: 'pointer',
                    lineWidth: 1,
                    labelText: (d:any) => {
                        // 改变边上的第一行第二行颜色
                        nextTick(()=>{
                            let parentElement = document.getElementById(d.id)
                            let tspanList = parentElement.getElementsByTagName("tspan")
                            setTimeout(()=>{
                                if(tspanList.length>=2){
                                    tspanList[0].setAttribute("fill","#81f4f9")
                                    tspanList[1].setAttribute("fill","#eca13c")
                                }
                            })
                        })
                        return props.isEdit||(d.data.outRate==''&&d.data.inRate=='')?'':`⬆${d.data.outRate}\n⬇${d.data.inRate}`;
                    },
                    data:(d)=>{
                            return d.data
                    },
                    endArrow: props.isEdit?true:false,
                    endArrowType: (d) => d.id.split('-')[0],
                    increasedLineWidthForHitTesting:10,
                    stroke: '#83d6dc',
                    labelAutoRotate:true,//是否旋转与边一致
                    labelFill: '#fff',
                    labelFontSize: 11,
                    labelPadding:[3,7],
                    zIndex:2
                },
            },
            transforms: [
                {
                    type:"process-parallel-edges",
                    mode:"bundle",
                    distance: 50
                }
            ],
            node: {
                type: 'image',
                style:{
                    padding:[10,10],
                    size: [60,60],
                    labelText: (d:any) => {
                        return d.data.name;
                    },
                    src: (d)=>{
                        //通过类型自定义节点图片
                        let imgArr:any = {
                            "交换机":"tpt_jhj.png",
                            "路由器":"tpt_lyq.png",
                            "安全设备":"tpt_fhq.png",
                            "其他设备":"tpt_qt.png",
                        }
                        return (d.data.type?getImageUrl(imgArr[d.data.type]):getImageUrl('tpt_qt.png')) || getImageUrl('tpt_qt.png')
                    },
                    data:(d)=>{
                            return d.data
                    },
                    labelPosition: 'bottom',
                    labelFill: '#fff',
                    labelFontSize: 13,
                    labelBackground: false,//背景颜色
                    labelBackgroundFill: 'linear-gradient(#e66465, #9198e5)',
                    labelBackgroundStroke: '#9ec9ff',
                    labelBackgroundRadius: 2,
                    labelFontWeight: 600,
                    labelPadding:[3,10],
                    zIndex:3,
                    labelOffsetY:8,
                    badge: false, // 是否显示徽标
                    badges: [
                        { text: 'x', placement: 'right-top',padding:[2,5] },
                    ],
                    badgePalette: ['red'], // 徽标的背景色板
                    badgeFontSize: 10, // 徽标字体大小
                }
            },
            behaviors: [
                {
                    type:'zoom-canvas'//缩放
                },
                {
                    type: 'drag-canvas',
                    key: 'drag-canvas-1',
                },
                {
                    type:"drag-element",
                    key: 'drag-element-1',
                    enableAnimation:false,
                    shadow:false//拖动样式
                },
                {
                    type: 'create-edge',
                    key:"create-edge-1",
                    trigger: 'click',//drag
                    onCreate: (edge) => {//创建线的样式
                        const { style, ...rest } = edge;
                        return {
                            ...rest,
                            data:{
                                name:"",
                                status:0,
                                scoureName:"",
                                scoureType:"",
                                scoureUrl:"",
                                targetName:"",
                                targetType:"",
                                targetUrl:"",
                                watchDevice:"",
                                watchUrl:"",
                                inRate:"",
                                outRate:"",
                            },
                            style: {
                                ...style,
                                stroke: 'red',
                                lineWidth: 2,
                                endArrow: true,
                            },
                        };
                    },
                },
            ],
            renderer: () => new SVGRenderer(),
        });
        graph.render();
        //鼠标右键点击节点编辑
        graph.on('node:contextmenu', (e) => {
            //添加编辑设备不为0是修改
            form.value = e.target.config.style.data
            submitNode();
        });
    }
    //添加设备按钮
    const addNode = () => {
        form.value.id = 0
        submitNode();
    }
    //添加修改节点
    const submitNode = () => {
        if(form.value.id == 0){
            //添加
            nodes.value = graph.getNodeData();
            graph.addData({
                nodes:[{
                    id: nodes.value.length>0?`${graph.getNodeData().length+1}`:"1",
                    style:{ x: container.value.clientWidth/2, y: 30 },
                    data:{id:nodes.value.length>0?`${graph.getNodeData().length+1}`:"1",name:form.value.name,type:form.value.type}
                }]
            })
        }else{
            graph.updateNodeData([{id:form.value.id,data:form.value}])
        }
        graph.render();
    }
</script>

运行结果

不断更新

相关推荐
我不吃饼干4 小时前
在 React 中实现倒计时功能会有什么坑
前端·react.js
小小小小宇5 小时前
前端PerformanceObserver
前端
王者鳜錸5 小时前
PYTHON从入门到实践-18Django从零开始构建Web应用
前端·python·sqlite
拾光拾趣录5 小时前
ES6到HTTPS全链路连环拷问,99%人第3题就翻车?
前端·面试
haaaaaaarry6 小时前
Element Plus常见基础组件(二)
开发语言·前端·javascript
xyphf_和派孔明6 小时前
关于echarts的性能优化考虑
前端·性能优化·echarts
PyHaVolask6 小时前
HTML 表单进阶:用户体验优化与实战应用
前端·javascript·html·用户体验
A了LONE6 小时前
cv弹窗,退款确认弹窗
java·服务器·前端
AntBlack7 小时前
闲谈 :AI 生成视频哪家强 ,掘友们有没有推荐的工具?
前端·后端·aigc
花菜会噎住8 小时前
Vue3核心语法进阶(computed与监听)
前端·javascript·vue.js