返回顶部
首页 > 资讯 > 前端开发 > JavaScript >vue用vis插件如何实现网络拓扑图
  • 399
分享到

vue用vis插件如何实现网络拓扑图

2024-04-02 19:04:59 399人浏览 安东尼
摘要

目录vis插件实现网络拓扑图安装引入visvis使用示例vis官方文档vis.js网络拓扑图点击折叠节点和展开节点首先看一下效果图vis插件实现网络拓扑图 安装引入vis 1.npm

vis插件实现网络拓扑图

安装引入vis

1.npm安装vis

npm install vis

2.引入vis

import { DataSet, Network } from 'vis';

vis使用示例

methods:{
   globalTrace () {
      // create an array with nodes
      var nodes = new DataSet([
        {id: 1, label: 'Node 1'},
        {id: 2, label: 'Node 2'},
        {id: 3, label: 'Node 3'},
        {id: 4, label: 'Node 4'},
        {id: 5, label: 'Node 5'}
      ]);
      // create an array with edges
      var edges = new DataSet([
        {from: 1, to: 3},
        {from: 1, to: 2},
        {from: 2, to: 4},
        {from: 2, to: 5}
      ]);
      // create a network
      var container = document.querySelector('#global_trace_content');
      // provide the data in the vis fORMat
      var data = {
          nodes: nodes,
          edges: edges
      };
      var options = {
      	//节点样式
        nodes: {
          shape: "box",//设置节点node样式为矩形
          fixed:true,//节点node固定不可移动
          font: {
			color: "white", //字体的颜色
			size: 30 //显示字体大小
		  },
          scaling: {
			min: 16,
			max: 32 //缩放效果比例
		  },
        },
        //连接线的样式
        edges: {
          color: {
            color: "rgb(97, 168, 224)",
            highlight: "rgb(97, 168, 224)",
            hover: "green",
            inherit: "from",
            opacity: 1.0
          },
          font: { 
            align: "top",//连接线文字位置
          },
          smooth: true, //是否显示方向箭头
          arrows: {to : true },//箭头指向from节点
        },
        layout: {
        //以分层方式定位节点
          hierarchical: {
            direction: "LR",//分层排序方向
            sortMethod: "directed",//分层排序方法
            levelSeparation:400//不同级别之间的距离
          },
        },
        interaction: {
          navigationButtons: true,
          // hover: true, //鼠标移过后加粗该节点和连接线
          selectConnectedEdges: false, //选择节点后是否显示连接线
        },
        
      };
      // initialize your network!
      this.network = new Network(container, data, options);
      this.network.on("click",e=> this.showDetail(e));//单击事件
      this.network.on("doubleClick",e=> this.enterService(e))//双击事件
    },
},
mounted(){
	this.globalTrace()
}

vis官方文档

使用文档

官方示例

vis.js网络拓扑图点击折叠节点和展开节点

首先看一下效果图

效果图

1.数据中要添加的属性如下图所示:

属性

2.数据中添加入上图属性后,添加点击事件即可,代码如下:

//todo 双击时折叠和展开
    network.on("doubleClick", function(params) {//双击事件
        if (params.nodes.length != 0) {//确定为节点双击事件
            var click_node_id = params.nodes[0];
            remove_all_sub_nodes(click_node_id);
        }
    });
 
    //todo 删除下级所有节点
    function remove_all_sub_nodes(node_id) {
        var sub_nodes = get_directly_sub_nodes(node_id);
        // console.log('sub_nodes',sub_nodes)
        if (sub_nodes.length == 0) {//当前点击的为叶子节点
            //判断是否有下级节点
            // console.log('sub',allNodes[node_id - 1]['subids']);
            if (typeof (allNodes[node_id - 1]['subids']) != 'undefined') {
                $.each(allNodes[node_id - 1]['subids'], function(index, item) {
                    // console.log('allNodes[item - 1]',allNodes[item - 1])
                    nodes.add(allNodes[item - 1]);
                    edges.add({id: node_id + '_' + item, from: node_id, to: item});
                });
            } else {
                alert('当前为叶子节点');
            }
        } else {
            $.each(sub_nodes, function(index, item) {
                var sub_sub_nodes = get_directly_sub_nodes(item);
                if (sub_sub_nodes.length == 0) {
                    nodes.remove({id: item});
                    edges.remove({id: node_id + '_' + item});
                } else {
                    remove_all_sub_nodes(item);
                }
                nodes.remove({id: item});
                edges.remove({id: node_id + '_' + item});
            });
        }
    }
 
    //todo 获取某节点直属下级节点
    function get_directly_sub_nodes(node_id) {
        var return_nodes = [];
        var connectedNodes = network.getConnectedNodes(node_id);//获取所有连接节点
        $.each(connectedNodes, function(index, item) {
            // console.log('allNodes',allNodes)
            if (item != allNodes[node_id - 1]['pid']) {//当前节点的父节点 ,不操作
                return_nodes.push(item);
            }
        });
        return return_nodes;
    }

3.完整代码如下:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-Scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta Http-equiv="X-UA-Compatible" content="ie=edge">
    <title>vis.js</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/CSS/bootstrap.min.css" rel="external nofollow"  integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script type="text/javascript" src="js/Jquery-1.12.4.min.js"></script>
 
    <script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
    <!--<script type="text/javascript" src="js/vis-network.min.js"></script>-->
    <style type="text/css">
        #mynetwork {
            width: 600px;
            height: 600px;
            border: 1px solid lightgray;
        }
        *{padding: 0;margin: 0;}
        .menu{
            
            position: absolute;
            background: rgba(3,3,3,0.6);
            border-radius: 5px;
            left: -99999px;
            top: -999999px;
        }
        .menu ul{list-style: none}
        .menu ul li{
            padding: 5px 10px;
            color: #ffff;
            border-bottom: 1px solid #ffffff;
            font-size: 14px;
            cursor: pointer;
            list-style: none;
        }
        .menu ul li:hover{
            color: #659bc5;
        }
        .menu ul li:last-child{
            border-bottom: none;
            padding: 5px 10px 0 10px;
        }
    </style>
</head>
<body>
<div id="mynetwork"></div>
<!--菜单操作-->
<div id="menuOperation" class="menu" style="display: none;">
    <ul>
        <li><span class="glyphicon glyphicon-off" aria-hidden="true"></span> 下线</li>
        <li><span class="glyphicon glyphicon-road" aria-hidden="true"></span> 通行</li>
    </ul>
</div>
<!--节点悬停-->
<div class="menu" id="divHoverNode" style="display: none;">
    <!--<ul></ul>-->
</div>
<script type="text/javascript">
    // 创建节点数据数组
    var allNodes = [
        {id: 1,name: "外部网络",type: "Internet",ip:"1.1.1.1",port:"未知",pid:0,subids: [2]},
        {id: 2,name: "交换机",type: "switch",ip:"192.168.30.125",Mac:"48:de:3D:e2:49:a8",model:"H3C",uptime:"2020-09-03 10:50:50",port:"22",pid:1, subids: [3, 4, 5, 6]},
        {id: 3,name: "交换机",type: "switch",ip:"192.168.1.8",mac:"cd:bd:3d:e2:55:55",model:"pf",uptime:"2020-09-03 10:50:50",port:"33",pid: 2,subids: [7, 8]},
        {id:4,name: "计算机",type: "computer",ip:"192.168.1.8",mac:"dv:bd:fd:e2:df:fd",model:"pf",uptime:"2020-09-03 10:50:50",account:"xiaox",location:"xianm",port:"44",pid: 2},
        {id:5,name: "路由器",type: "rooter",ip:"192.168.1.8",mac:"ds:bd:3d:e2:ds:55",model:"pf",uptime:"2020-09-03 10:50:50",account:"xiaox",location:"xianm",port:"55",pid: 2},
        {id:6,name: "服务器",type: "service",ip:"192.168.1.8",mac:"vf:eq:dd:e2:55:55",model:"pf",uptime:"2020-09-03 10:50:50",account:"xiaox",location:"xianm",port:"66",pid:2},
        {id:7,name: "打印机",type: "print",ip:"192.168.1.8",mac:"ss:bd:3d:ju:55:55",model:"pf",uptime:"2020-09-03 10:50:50",account:"xiaox",location:"xianm",port:"77",pid: 3},
        {id:8,name: "手机",type: "phone",ip:"192.168.1.8",mac:"ju:ju:3d:e2:55:uy",model:"pf",uptime:"2020-09-03 10:50:50",account:"xiaox",location:"xianm",port:"88",pid:3}
    ];
    // 创建边数据数组
    var allEdges = [
        { id:"1_2",from: 1, to: 2 },
        { id:"2_3",from: 2, to: 3 },
        { id:"2_4",from: 2, to: 4 },
        { id:"2_5",from: 2, to: 5 },
        { id:"2_6",from: 2, to: 6 },
        { id:"3_7",from: 3, to: 7 },
        { id:"3_8",from: 3, to: 8 }
    ];
    
    for (var i = 0;i < allNodes.length;i++){
        if (allNodes[i].type == 'Internet'){
            allNodes[i].image = 'image/internet.png';
        }
        if (allNodes[i].type == 'switch'){
            allNodes[i].image = 'image/switch.png';
        }
        if (allNodes[i].type == 'hub'){
            allNodes[i].image = 'image/hub.png';
        }
        if (allNodes[i].type == 'computer'){
            allNodes[i].image = 'image/computer.png';
        }
        if (allNodes[i].type == 'rooter'){
            allNodes[i].image = 'image/rooter.png';
        }
        if (allNodes[i].type == 'service'){
            allNodes[i].image = 'image/service.png';
        }
        if (allNodes[i].type == 'print'){
            allNodes[i].image = 'image/print.png';
        }
        if (allNodes[i].type == 'phone'){
            allNodes[i].image = 'image/phone.png';
        }
    }
    // 获取容器
    var container = document.getElementById('mynetwork');
    // 创建节点对象
    var nodes = new vis.DataSet(allNodes);
    // 创建连线对象
    var edges = new vis.DataSet(allEdges);
    // 将数据赋值给vis 数据格式化器
    var data = {
        nodes: nodes,
        edges: edges
    };
    console.log('nodes',data.nodes);
 
    var options = {
        nodes:{
            shape: 'image'//设置图片
            // image:"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1599645215703&di=0cb23e74736a6a1222f35b822f5bf833&imgtype=0&src=http%3A%2F%2Fa1.att.hudong.com%2F05%2F00%2F01300000194285122188000535877.jpg"
        },
        interaction:{
            hover:true,
            hoverConnectedEdges: true
        },
        layout: {
            //树形
            // hierarchical: {direction: 'UD', sortMethod: 'hubsize'}
        },
    };
 
 
    // 初始化关系图
    var network = new vis.Network(container, data, options);
 
    
    function getNode(option) {
        for (var i = 0;i < allNodes.length;i++){
            if (option == allNodes[i].id){
                // console.log('i',allNodes[i]);
                return allNodes[i];
            }
        }
    }
    
    function getEdge(option){
        var linkId = option;
        var linkIdFirst = linkId.substring(0,1);//截取第一位
        var linkIdLast = linkId.substring(linkId.length-1,linkId.length);//截取最后一位
        var dataList = [];//存放线条两边的节点nodes数据
        for (var j =0;j<allNodes.length;j++){
            if (linkIdFirst == allNodes[j].id || linkIdLast == allNodes[j].id){
                dataList.push(allNodes[j]);
            }
        }
        return dataList;
    }
    //todo  悬停在节点上--显示弹框
    network.on('hoverNode',function(properties){
        // console.log('悬停节点',properties);
        var hoveNodeList = getNode(properties.node);
        // console.log('hoveNodeList',hoveNodeList);
        var deviceType = hoveNodeList.type;
        var imgPathSrc = hoveNodeList.image;
        if (deviceType == "Internet" || deviceType == "hub"){
            var $ul = "<ul>"
                +"<li><img src=' "+imgPathSrc+" ' width='30px' height='25px'><span> "+hoveNodeList.name+" </span> </li>"
                +"</ul>";
            $("#divHoverNode").append($ul);
        }
        else if (deviceType == "switch"){
            var $ul = "<ul>"
                +"<li><img src=' "+imgPathSrc+" ' width='30px' height='25px'><span> 设备类型: "+hoveNodeList.name+" </span> </li>"
                +"<li>IP:"+ hoveNodeList.ip+"</li>"
                +"<li>MAC:"+ hoveNodeList.mac+"</li>"
                +"<li>设备型号:"+ hoveNodeList.model+"</li>"
                +"</ul>";
            $("#divHoverNode").append($ul);
        }else{
            var $ul = "<ul>"
                +"<li><img src=' "+imgPathSrc+" ' width='30px' height='25px'><span> 设备类型:"+hoveNodeList.name+" </span> </li>"
                +"<li>IP:"+ hoveNodeList.ip+"</li>"
                +"<li>MAC:"+ hoveNodeList.mac+"</li>"
                +"<li>账号:"+ hoveNodeList.account+"</li>"
                +"<li>所在位置:"+ hoveNodeList.location+"</li>"
                +"<li>最后登录时间:"+ hoveNodeList.uptime+"</li>"
                +"</ul>";
            $("#divHoverNode").append($ul);
        }
        $('#divHoverNode').css({
            'display': 'block',
            'left': properties.event.offsetX + 15,
            'top' : properties.event.offsetY + 15
        });
        $('#menuOperation').hide();
    });
    //todo  从节点移开---隐藏弹框
    network.on('blurNode',function(){
        $("#divHoverNode").hide();
        $("#divHoverNode").empty();//移除之后清空div
    });
    //todo  悬停在边上--显示弹框
    network.on('hoverEdge',function(properties){
        // console.log('悬停边',properties);
        var hoveEdgeList = getEdge(properties.edge);
        // console.log('hoveEdgeList',hoveEdgeList);
        var $ul = "<ul>"
            +"<li>名称:"+ hoveEdgeList[0].name+"->"+hoveEdgeList[1].name+"</li>"
            +"<li>端口号:"+ hoveEdgeList[0].ip+"->"+hoveEdgeList[1].ip+"</li>"
            +"</ul>";
        $("#divHoverNode").append($ul);
        $('#divHoverNode').css({
            'display': 'block',
            'left': properties.event.offsetX + 15,
            'top' : properties.event.offsetY + 15
        });
        $('#menuOperation').hide();
    });
    //todo  从边上移开---隐藏弹框
    network.on('blurEdge',function(properties){
        $("#divHoverNode").hide();
        $("#divHoverNode").empty();//移除之后清空div
    });
    //todo  点击的判断是否选中节点时候显示隐藏
    network.on('click',function(properties){
        var clickNodeList = getNode(properties.nodes[0]);
        // console.log('clickNodeList',clickNodeList);
        if (typeof(clickNodeList) == "undefined") {
            $('#menuOperation').hide();
        }else{
            $('#menuOperation').css({
                'display': 'block',
                'left': properties.event.center.x + 15,
                'top' : properties.event.center.y + 15
            });
            $("#divHoverNode").hide();
        }
    });
    //todo 双击时折叠和展开
    network.on("doubleClick", function(params) {//双击事件
        if (params.nodes.length != 0) {//确定为节点双击事件
            var click_node_id = params.nodes[0];
            remove_all_sub_nodes(click_node_id);
        }
    });
    //todo 删除下级所有节点
    function remove_all_sub_nodes(node_id) {
        var sub_nodes = get_directly_sub_nodes(node_id);
        // console.log('sub_nodes',sub_nodes)
        if (sub_nodes.length == 0) {//当前点击的为叶子节点
            //判断是否有下级节点
            // console.log('sub',allNodes[node_id - 1]['subids']);
            if (typeof (allNodes[node_id - 1]['subids']) != 'undefined') {
                $.each(allNodes[node_id - 1]['subids'], function(index, item) {
                    // console.log('allNodes[item - 1]',allNodes[item - 1])
                    nodes.add(allNodes[item - 1]);
                    edges.add({id: node_id + '_' + item, from: node_id, to: item});
                });
            } else {
                alert('当前为叶子节点');
            }
        } else {
            $.each(sub_nodes, function(index, item) {
                var sub_sub_nodes = get_directly_sub_nodes(item);
                if (sub_sub_nodes.length == 0) {
                    nodes.remove({id: item});
                    edges.remove({id: node_id + '_' + item});
                } else {
                    remove_all_sub_nodes(item);
                }
                nodes.remove({id: item});
                edges.remove({id: node_id + '_' + item});
            });
        }
    }
    //todo 获取某节点直属下级节点
    function get_directly_sub_nodes(node_id) {
        var return_nodes = [];
        var connectedNodes = network.getConnectedNodes(node_id);//获取所有连接节点
        $.each(connectedNodes, function(index, item) {
            // console.log('allNodes',allNodes)
            if (item != allNodes[node_id - 1]['pid']) {//当前节点的父节点 ,不操作
                return_nodes.push(item);
            }
        });
        return return_nodes;
    }
</script>
</body>

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: vue用vis插件如何实现网络拓扑图

本文链接: https://lsjlt.com/news/169027.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • vue用vis插件如何实现网络拓扑图
    目录vis插件实现网络拓扑图安装引入visvis使用示例vis官方文档vis.js网络拓扑图点击折叠节点和展开节点首先看一下效果图vis插件实现网络拓扑图 安装引入vis 1.npm...
    99+
    2024-04-02
  • 如何开发基 HTML5网络拓扑图的应用
    这篇文章主要介绍如何开发基 HTML5网络拓扑图的应用,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!今天开始我们就从最基础解析如何构建 HTML5 Canvas 拓扑图应用,HT 内部封装了一个拓扑图形组件 ht.g...
    99+
    2023-06-09
  • 怎么应用html5实现网络拓扑图上文本
    本篇内容主要讲解“怎么应用html5实现网络拓扑图上文本”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么应用html5实现网络拓扑图上文本”吧!从上图可以看出...
    99+
    2024-04-02
  • 如何使用HTML5中Canvas创建电信网络拓扑图
    小编给大家分享一下如何使用HTML5中Canvas创建电信网络拓扑图,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!效果图http://www.hightopo.c...
    99+
    2023-06-09
  • Cacti系统如何处理网络拓扑图的展示
    Cacti系统通过使用SNMP协议来监控网络设备,获取设备的各种性能数据,并根据这些数据生成网络拓扑图。在Cacti系统中,用户可以...
    99+
    2024-04-02
  • 基于vue如何实现tree插件
    这篇文章将为大家详细讲解有关基于vue如何实现tree插件,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。iview提供的控件iview已经很成熟了,如果说我写的控件和iv...
    99+
    2024-04-02
  • vue使用swiper插件实现垂直轮播图
    目录使用swiper插件做垂直轮播图swiper轮播插件使用 一次显示多个slidesSwiper 动态加载数据遇到的坑总结使用swiper插件做垂直轮播图 1.下载安装 cnpm ...
    99+
    2023-01-14
    vue使用swiper插件 vue垂直轮播图 vue swiper插件
  • vue中如何使用轮播图插件vue-awesome-swiper
    这篇文章主要介绍了vue中如何使用轮播图插件vue-awesome-swiper,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Vue-Awe...
    99+
    2024-04-02
  • vue-cropper插件如何实现图片截取上传组件封装
    小编给大家分享一下vue-cropper插件如何实现图片截取上传组件封装,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!需求场景:后台开发需要上传图片并进行相应比例尺寸图片的截取,本组件开发采用Ant Design Vue组...
    99+
    2023-06-15
  • vue如何实现一个弹窗插件
    这篇文章主要讲解了“vue如何实现一个弹窗插件”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“vue如何实现一个弹窗插件”吧!popup.vue<template> &l...
    99+
    2023-07-04
  • Vue中如何使用百度地图插件
    这期内容当中小编将会给大家带来有关Vue中如何使用百度地图插件,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。安装CDN全局安装<script src=&q...
    99+
    2024-04-02
  • vue中如何使用图片懒加载vue-lazyload插件
    这篇文章给大家分享的是有关vue中如何使用图片懒加载vue-lazyload插件的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。具体如下:说明当网络请求比较慢的时候,提前给这张图片...
    99+
    2024-04-02
  • vue使用swiper插件实现轮播图的示例
    目录vue - 使用swiper插件实现轮播图 使用watch与$nextTick解决轮播的Bug hello大家好,最近我在做一个仿饿了么的项目,我会将我的项目经验同步到这里,与大...
    99+
    2024-04-02
  • vue利用插件实现按比例切割图片
    本文实例为大家分享了vue利用插件实现按比例切割图片的具体代码,供大家参考,具体内容如下 1.使用插件——vueCropper 安装该插件:npm install vue-cropp...
    99+
    2024-04-02
  • 如何用Vue实现图片裁剪组件
    这篇文章主要介绍“如何用Vue实现图片裁剪组件”,在日常操作中,相信很多人在如何用Vue实现图片裁剪组件问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何用Vue实现图片裁剪组件”的疑惑有所帮助!接下来,请跟...
    99+
    2023-06-20
  • 如何使用jQuery插件imgAreaSelect实现截图功能
    这篇文章主要介绍了如何使用jQuery插件imgAreaSelect实现截图功能的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇如何使用jQuery插件imgAreaSelect实现截图功能文章都会有所收获,下面...
    99+
    2023-07-04
  • vue如何用DataTable插件实现表格动态刷新
    今天小编给大家分享一下vue如何用DataTable插件实现表格动态刷新的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。我这边...
    99+
    2023-07-04
  • Vue插件如何实现从封装到发布
    这篇文章主要介绍Vue插件如何实现从封装到发布,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!插件的分类添加全局的方法或者属性 比如:vue-element添加全局的资源 比如:指令 ...
    99+
    2024-04-02
  • 如何在TensorFlow中实现图卷积网络
    在TensorFlow中实现图卷积网络(Graph Convolutional Network, GCN)可以通过以下步骤实现: ...
    99+
    2024-03-01
    TensorFlow
  • 如何使用node实现一个图片拼接插件
    今天小编给大家分享一下如何使用node实现一个图片拼接插件的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作