赞
踩
其实这些效果之前都有用js写过,但是最近在写vue项目,里面的些许语法还是有些不一样的,所以还是写一遍文章总结一下,下次遇到就可以直接用了。
如果想看js写法,可以看我别的文章
首先,实现效果入下图:
下面简单介绍一下每种实现方式
1.将树图的节点修改成图片以及修改连线的颜色
代码布局如下:
主要代码:
symbolSize: [30, 30],
- /**
- * 遍历树节点信息
- * @param nodes 拓扑图节点数据
- * 通过某种状态区分显示拓扑图的节点图片或连线颜色
- * */
- readNodes(nodes) {
- for (const item of nodes) { // js遍历树形数组结构
- if (item.children && item.children.length) {
- this.readNodes(item.children)
- }
- if (item.name == '服务器') {
- item.symbol = 'image://' + require('@/assets/topo/server_online_unreg.png');
- // 修改连线颜色
- item.lineStyle = {
- color: '#ff0000'
- }
- } else {
- item.symbol = 'image://' + require('@/assets/topo/internet.png');
- item.lineStyle = {
- color: '#2E8B57'
- }
- }
- }
- },
2.添加鼠标悬停事件
代码布局如下:
主要代码:
formatter: this.formatterHover// 修改鼠标悬停显示的内容
- /**
- * 鼠标悬停显示详情
- * @param params
- * @returns {string}
- */
- formatterHover(params) {
- // console.log(params);
- var deviceType = params.data.type;
- var imgPath = params.data.symbol;
- // 图片地址截取,因为echarts修改图片的时候有一个------image://---前缀,前缀后面的才是图片真正的地址
- var imgPathSrc = imgPath.split('image://')[1];
- // console.log('str',imgPathSrc);
- if (deviceType === 'Internet' || deviceType === 'hub') {
- return "<img src='" + imgPathSrc + " ' width='30px' height='30px'>" + '<span style="padding:0 5px;font-size: 14px;">' + params.data.name + '</span>';
- } if (deviceType === 'switch') {
- return "<img src='" + imgPathSrc + " ' width='30px' height='30px'>" + '<span style="padding: 0 5px;font-size: 14px;">设备类型:' + params.data.name + '</span>' + '<br>' +
- '<span style="padding-left:5px;height:30px;line-height:30px;display: inline-block;font-size: 14px;">IP:' + params.data.IP + '</span>' + '<br>' +
- '<span style="padding-left:5px;height:30px;line-height:30px;display: inline-block;font-size: 14px;">MAC:' + params.data.MAC + '</span>' + '<br>';
- // +'<button style="padding:2px 5px;border:none;outline:none;color:#ffffff;border-radius: 5px;background:rgba(0,0,0,0.5);"><span class="glyphicon glyphicon-list-alt" aria-hidden="true"></span> IP列表</button>'
- // +'<button style="padding:2px 5px;border:none;outline:none;color:#ffffff;border-radius: 5px;background:rgba(0,0,0,0.5);margin-left: 10px;"><span class="glyphicon glyphicon-list-alt" aria-hidden="true"></span> MAC列表</button>';
- } else {
- return "<img src='" + imgPathSrc + " ' width='30px' height='30px'>" + '<span style="padding: 0 5px;font-size: 14px;">设备类型:' + params.data.name + '</span>' + '<br>' +
- '<span style="padding-left:5px;height:30px;line-height:30px;display: inline-block;font-size: 14px;">IP:' + params.data.IP + '</span>' + '<br>' +
- '<span style="padding-left:5px;height:30px;line-height:30px;display: inline-block;font-size: 14px;">MAC:' + params.data.MAC + '</span>' + '<br>';
- }
- },
3.鼠标右键弹出菜单
代码布局如下:
主要代码:
- <style scoped>
- .menu{
- /*这个样式不写,右键弹框会一直显示在画布的左下角*/
- position: absolute;
- background: rgba(255, 255, 255, 1);
- border-radius: 5px;
- left: -99999px;
- top: -999999px;
- }
- .menu ul{
- list-style: none;
- padding: 0;
- margin: 0;
- }
- .menu ul li{
- cursor: pointer;
- padding: 5px 10px;
- color: #000000;
- border-bottom: 1px dashed #a9a9a9;
- font-size: 14px;
- }
- .menu ul li:last-child{
- border-bottom: none;
- }
- </style>
- <!--右键弹出菜单-->
- <div ref="rightMenu" class="menu" style="display: none;">
- <ul>
- <li>下线</li>
- <li>上线</li>
- <li>掉线</li>
- </ul>
- </div>
- <div class="app-container" @contextmenu.prevent="" @click="mainClick">
-
- </div>
- /**
- * 鼠标右键,显示右键菜单
- */
- echarts.init(chart).on('contextmenu', function(params) {
- // console.log('params', params)
- that.contextmenu = true
- // 去掉悬停
- that.$refs.main.children[1].style.display = 'none'
- that.$refs.rightMenu.style.display = 'block';
- // // //让自定义菜单随鼠标的箭头位置移动
- that.$refs.rightMenu.style.left = params.event.offsetX + 45 + 'px'
- that.$refs.rightMenu.style.top = params.event.offsetY + 45 + 'px'
- });
- /**
- * 点击页面其它地方时,隐藏右键菜单
- * */
- mainClick() {
- this.contextmenu = false
- this.$refs.rightMenu.style.display = 'none';
- },
注:以上代码实现右键菜单是没有问题的,但是后期在开发的过程中,发现当在浏览器左下角,右下角,最右边点击时,菜单会被覆盖显示不全,所以对菜单显示的位置做了一些调整,调整完后效果如下:
主要代码:<至于里面的数字,具体要根据自己的项目进行修改>
- /**
- * smallWidth:即能容下弹框的最小宽度=document.body.clientWidth<浏览器窗口宽度> - 210<左侧导航菜单宽度> - params.event.offsetX<当前点击距离左上角宽度>
- * smallHeight:即能容下弹框的最小高度=document.body.clientHeight<浏览器窗口高度> - 120<上方导航菜单宽度> - params.event.offsetY<当前点击距离左上角高度>
- * 140,在浏览器中console.log打印,差不多smallWidth小于140的时候,右键菜单宽度就放不下了
- * 256,在浏览器中console.log打印,差不多smallHeight小于256的时候,右键菜单高度就放不下了
- * 接下来右键菜单的显示,差不多就是当前点击的位置加或减去右键菜单宽或高的长度
- * */
- const smallWidth = document.body.clientWidth - 210 - params.event.offsetX
- const smallHeight = document.body.clientHeight - 120 - params.event.offsetY
- // console.log('smallWidth', smallWidth)
- // console.log('smallHeight', smallHeight)
- if (smallHeight <= 256 && smallWidth > 140) { // 高度不够,宽度够
- // 让自定义菜单随鼠标的箭头位置移动
- that.$refs.rightMenu.style.left = params.event.offsetX + 45 + 'px'
- that.$refs.rightMenu.style.top = params.event.offsetY - 200 + 'px'
- } else if (smallHeight > 256 && smallWidth <= 140) { // 高度够,宽度不够
- that.$refs.rightMenu.style.left = params.event.offsetX - 52 + 'px'
- that.$refs.rightMenu.style.top = params.event.offsetY + 45 + 'px'
- } else if (smallHeight <= 256 && smallWidth <= 140) { // 高度不够,宽度不够
- that.$refs.rightMenu.style.left = params.event.offsetX - 52 + 'px'
- that.$refs.rightMenu.style.top = params.event.offsetY - 200 + 'px'
- } else { // 高度和宽度都够
- that.$refs.rightMenu.style.left = params.event.offsetX + 45 + 'px'
- that.$refs.rightMenu.style.top = params.event.offsetY + 45 + 'px'
- }
4.搜索
代码布局如下:
主要代码:
- <el-input v-model="inputSearch" type="text" placeholder="请输入名称" style="width: 200px;" @keyup.enter.native="handleFilter"/>
- <el-button type="primary" icon="el-icon-search" size="mini" @click="handleFilter">搜索</el-button>
inputSearch: '', // 搜索框
- /**
- * 搜索,输入名称时,实现搜索功能
- * */
- handleFilter() {
- const that = this
- var num = 0; // 记录查询到节点的数量
- function readNodes(nodes) {
- for (const item of nodes) { // js遍历树形数组结构
- if (item.children && item.children.length) {
- readNodes(item.children)
- }
- // 查询,名称中包含输入值就修改label颜色和字体大小
- if (item.name.indexOf(that.inputSearch) >= 0 && that.inputSearch != '') {
- item.label = {
- color: 'red',
- fontSize: '15'
- };
- num++;
- } else { // 否则为默认颜色和大小
- item.label = {
- color: '#666',
- fontSize: '9'
- };
- }
- }
- }
- readNodes(this.dataTree); // 调用时,要给data加[],因为原本的data不是一个正常的数组结构
- if (num > 0) { // 查询节点数量大于0的时候展开所有层级
- this.defaultOpt.series[0].initialTreeDepth = -1;
- } else { // 否则恢复初始的层级
- this.defaultOpt.series[0].initialTreeDepth = 2;
- }
- const chart = this.$refs.main
- if (chart) {
- echarts.init(chart).setOption(this.defaultOpt); // 重新设置一遍树图,不然不起效果
- }
- },
5.导出PNG
代码布局:
主要代码:
- toolbox: {
- show: true,
- top: 20,
- left: 20,
- feature: {
- restore: {
- title: '刷新'// 刷新echarts图标
- },
- saveAsImage: {
- title: '下载图片', // 鼠标悬停在下载图标上时,显示的文字
- name: 'network-topology'// 下载图片的文件名为network-topology.png
- }
- }
- },
6.高亮:这个高亮的效果我感觉不是很好,但是如果有朋友需要的话,就是下面用法
代码布局:
主要代码:
- emphasis: { // 高亮,个人感觉这个高亮的效果不是很好
- focus: 'descendant'
- },
7.查看节点是否还有子节点
代码布局:
主要代码:
- symbol: function(params, params1) {
- /**
- * 自定义图片后,不知道是否还有子节点未展开
- * 通过打印,发现没有展开的节点,和最后一层子节点的collapsed属性都为true
- * 所以判断collapsed为true,并且没有孩子节点的时候,就是还有没展开的子节点
- * 修改label的样式,来判断是否还存在子节点没展开
- */
- // console.log('params11111111111111111', params1)
- if (params1.collapsed == true && params1.data.children.length > 0) {
- params1.data.label = {
- color: '#0099cc',
- fontSize: '13',
- fontWeight: 'bold'
- }
- }
- },
- /**
- * 鼠标单机节点
- * (1)隐藏右键菜单
- * (2)判断子节点是否展开,修改label的样式
- */
- echarts.init(chart).on('click', function(params) {
- that.contextmenu = false
- that.$refs.rightMenu.style.display = 'none';
- // 解决树图点击展开图片不显示问题
- // echarts.init(chart).resize()
- console.log('params', params)
- if (params.collapsed == true && params.data.children.length > 0) {
- params.data.label = {
- color: '#0099cc',
- fontSize: '13',
- fontWeight: 'bold'
- }
- } else {
- params.data.label = {
- color: '#ffffff',
- fontSize: '9',
- fontWeight: 'normal'
- }
- }
- echarts.init(chart).resize()
- });
8.修改树图的展示方式
注:起初只是简单的实现了切换的效果,并没有注重细节主要如下:
代码布局:
主要代码:
- <el-button size="mini" @click="lrClick('LR')">左到右</el-button>
- <el-button size="mini" @click="rlClick('RL')">右到左</el-button>
- <el-button size="mini" @click="tbClick('TB')">上到下</el-button>
- <el-button size="mini" @click="btClick('BT')">下到上</el-button>
- /**
- * 左到右按钮点击
- * @param option 树图属性LR
- * */
- lrClick(option) {
- this.treeDisplay(option)
- },
- /**
- * 右到左按钮点击
- * @param option 树图属性RL
- * */
- rlClick(option) {
- this.treeDisplay(option)
- },
- /**
- * 上到下按钮点击
- * @param option 树图属性TB
- * */
- tbClick(option) {
- this.treeDisplay(option)
- },
- /**
- * 下到上按钮点击
- * @param option 树图属性BT
- * */
- btClick(option) {
- this.treeDisplay(option)
- },
- /**
- * 左到右,右到左,上到下,下到上按钮点击时,对拓扑属性的设置
- * */
- treeDisplay(option) {
- this.defaultOpt.series[0].orient = option;
- const chart = this.$refs.main
- if (chart) {
- echarts.init(chart).setOption(this.defaultOpt);
- }
- }
后期在实际开发中,发现,切换后,结点的label会挤在一起,非常丑,所以要对每种展示方式的label也要做调整,具体如下:
代码布局:
主要代码:
- type: 'tree',
- data: this.dataTree,
- left: '10%',
- right: '2%',
- top: '16%',
- bottom: '8%',
- label: {
- position: 'top',
- rotate: -90,
- verticalAlign: 'middle',
- align: 'right',
- color: '#fff',
- fontSize: 9
- },
- orient: 'TB',
- leaves: {
- label: {
- position: 'bottom',
- rotate: -90,
- verticalAlign: 'middle',
- align: 'left'
- }
- },
- symbolSize: [30, 30],
- /**
- * 左到右按钮点击
- * @param option 树图属性LR
- * */
- lrClick(option) {
- this.treeDisplay(option)
- },
- /**
- * 右到左按钮点击
- * @param option 树图属性RL
- * */
- rlClick(option) {
- this.treeDisplay(option)
- },
- /**
- * 上到下按钮点击
- * @param option 树图属性TB
- * */
- tbClick(option) {
- this.treeDisplay(option)
- },
- /**
- * 下到上按钮点击
- * @param option 树图属性BT
- * */
- btClick(option) {
- this.treeDisplay(option)
- },
- /**
- * 左到右,右到左,上到下,下到上按钮点击时,对拓扑属性的设置
- * orient:修改拓扑图展示的方向
- * label、leaves修改结点旁边的文字展示
- * */
- treeDisplay(option) {
- this.defaultOpt.series[0].orient = option;
- // console.log('option', option)
- if (option == 'LR') {
- this.defaultOpt.series[0].label = {
- position: 'left',
- rotate: 0,
- verticalAlign: 'middle',
- align: 'right',
- color: '#fff',
- fontSize: 9
- }
- this.defaultOpt.series[0].leaves = {
- label: {
- position: 'right',
- rotate: 0,
- verticalAlign: 'middle',
- align: 'left'
- }
- }
- }
- if (option == 'RL') {
- this.defaultOpt.series[0].label = {
- position: 'right',
- rotate: 0,
- verticalAlign: 'middle',
- align: 'left',
- color: '#fff',
- fontSize: 9
- }
- this.defaultOpt.series[0].leaves = {
- label: {
- position: 'left',
- rotate: 0,
- verticalAlign: 'middle',
- align: 'right'
- }
- }
- }
- if (option == 'TB') {
- this.defaultOpt.series[0].label = {
- position: 'top',
- rotate: -90,
- verticalAlign: 'middle',
- align: 'right',
- color: '#fff',
- fontSize: 9
- }
- this.defaultOpt.series[0].leaves = {
- label: {
- position: 'bottom',
- rotate: -90,
- verticalAlign: 'middle',
- align: 'left'
- }
- }
- }
- if (option == 'BT') {
- this.defaultOpt.series[0].label = {
- position: 'bottom',
- rotate: 90,
- verticalAlign: 'middle',
- align: 'right',
- color: '#fff',
- fontSize: 9
- }
- this.defaultOpt.series[0].leaves = {
- label: {
- position: 'top',
- rotate: 90,
- verticalAlign: 'middle',
- align: 'left'
- }
- }
- }
- const chart = this.$refs.main
- if (chart) {
- echarts.init(chart).setOption(this.defaultOpt);
- }
- },
完整代码:
topoTree.js
- import Mock from 'mockjs'
-
- const tree = [
- {
- 'id': '0',
- 'name': '外部网络',
- 'type': 'Internet',
- 'children': [
- {
- 'id': '1',
- 'name': '交换机',
- 'type': 'switch',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c',
- 'deviceType': '交换机',
- 'deviceNum': 'HUAWEI',
- 'children': [
- {
- 'id': '2',
- 'name': '笔记本',
- 'type': 'switch',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c'
- },
- {
- 'id': '3',
- 'name': '计算机',
- 'type': 'computer',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c',
- 'children': [
- {
- 'id': '4',
- 'name': '计算机1',
- 'type': 'computer',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c'
- },
- {
- 'id': '5',
- 'name': '计算机2',
- 'type': 'computer',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c'
- },
- {
- 'id': '6',
- 'name': '计算机3',
- 'type': 'computer',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c'
- },
- {
- 'id': '7',
- 'name': '计算机4',
- 'type': 'computer',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c',
- 'lastLoginTime': '2020-8-26'
- }
- ]
- },
- {
- 'id': '8',
- 'name': '路由器',
- 'type': 'rooter',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c',
- 'deviceType': '路由器'
- },
- {
- 'id': '9',
- 'name': '服务器',
- 'type': 'service',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c',
- 'deviceType': '服务器'
- },
- {
- 'id': '10',
- 'name': '打印机',
- 'type': 'print',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c',
- 'deviceType': '打印机'
- },
- {
- 'id': '11',
- 'name': '计算机',
- 'type': 'computer',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c',
- 'lastLoginTime': '2020-8-26'
- }
- ]
- },
- {
- 'id': '12',
- 'name': '无线交换机',
- 'type': 'switch',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c',
- 'deviceType': '交换机',
- 'deviceNum': 'HUAWEI',
- 'children': [
- {
- 'id': '13',
- 'name': '手机',
- 'type': 'phone',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c',
- 'deviceType': '手机'
- },
- {
- 'id': '14',
- 'name': '平板',
- 'type': 'phone',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c',
- 'deviceType': '平板'
- }
- ]
- },
- {
- 'id': '15',
- 'name': 'hub',
- 'type': 'hub',
- 'children': [
- {
- 'id': '16',
- 'name': '计算机',
- 'type': 'computer',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c',
- 'deviceType': '计算机'
- },
- {
- 'id': '17',
- 'name': '笔记本',
- 'type': 'phone',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c',
- 'deviceType': '手机'
- },
- {
- 'id': '18',
- 'name': '打印机',
- 'type': 'print',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c',
- 'deviceType': '打印机'
- },
- {
- 'id': '19',
- 'name': '路由器',
- 'type': 'rooter',
- 'IP': '192.168.30.126',
- 'MAC': 'b0:98:6e:bf:6r:4c',
- 'deviceType': '路由器'
- }
- ]
- }
- ]
- }
- ]
-
- Mock.mock('/topo/tree', 'get', tree)
- export default [
-
- ]
index.vue
- <template>
- <div class="app-container" @contextmenu.prevent="" @click="mainClick">
- <el-input v-model="inputSearch" type="text" placeholder="请输入名称" style="width: 200px;" @keyup.enter.native="handleFilter"/>
- <el-button type="primary" icon="el-icon-search" size="mini" @click="handleFilter">搜索</el-button>
- <el-button size="mini" @click="lrClick('LR')">左到右</el-button>
- <el-button size="mini" @click="rlClick('RL')">右到左</el-button>
- <el-button size="mini" @click="tbClick('TB')">上到下</el-button>
- <el-button size="mini" @click="btClick('BT')">下到上</el-button>
- <div ref="main" class="app-container"></div>
- <!--右键弹出菜单-->
- <div ref="rightMenu" class="menu" style="display: none;">
- <ul>
- <li>下线</li>
- <li>上线</li>
- <li>掉线</li>
- </ul>
- </div>
- </div>
- </template>
-
- <script>
- import * as echarts from 'echarts'
- require('echarts/theme/macarons')
- import axios from 'axios'
-
- export default {
- name: 'TopologyDisplay',
- props: {
- },
- data() {
- return {
- inputSearch: '', // 搜索框
- dataTree: [], // data数据
- defaultOpt: {}, // 拓扑图属性设置
- contextmenu: false // 鼠标右键事件
- }
- },
- watch: {
- },
- mounted: function() {
- this.setOptions()
- this.day_init()
- },
- created() {
- },
- methods: {
- /**
- * 实现自适应
- * */
- day_init() {
- const self = this; // 因为箭头函数会改变this指向,指向windows。所以先把this保存
- const todaypieId = this.$refs.main
- if (!todaypieId) {
- return false;
- } else {
- setTimeout(() => {
- window.onresize = function() {
- // self.chart = echarts.init(self.$refs.myEchart);
- self.chart_today = echarts.init(
- todaypieId
- );
- self.chart_today.resize();
- };
- }, 20);
- }
- },
- /**
- * 遍历树节点信息
- * @param nodes 拓扑图节点数据
- * 通过某种状态区分显示拓扑图的节点图片或连线颜色
- * */
- readNodes(nodes) {
- for (const item of nodes) { // js遍历树形数组结构
- if (item.children && item.children.length) {
- this.readNodes(item.children)
- }
- if (item.name == '服务器') {
- item.symbol = 'image://' + require('@/assets/topo/server_online_unreg.png');
- // 修改连线颜色
- item.lineStyle = {
- color: '#ff0000'
- }
- } else {
- item.symbol = 'image://' + require('@/assets/topo/internet.png');
- item.lineStyle = {
- color: '#2E8B57'
- }
- }
- }
- },
- /**
- * 对拓扑图属性的设置
- * */
- setOptions() {
- axios.get('/topo/tree').then(res => {
- console.log('树节点数据', JSON.parse(JSON.stringify(res.data)));
- this.dataTree = res.data
- if (this.dataTree) {
- this.readNodes(this.dataTree) // 在设置属性之前遍历显示图片,否则节点图片不生效
- this.defaultOpt = {
- tooltip: {
- trigger: 'item',
- triggerOn: 'mousemove',
- enterable: true, // 鼠标是否可进入提示框浮层中
- formatter: this.formatterHover// 修改鼠标悬停显示的内容
- },
- toolbox: {
- show: true,
- top: 20,
- left: 20,
- feature: {
- restore: {
- title: '刷新'// 刷新echarts图标
- },
- saveAsImage: {
- title: '下载图片', // 鼠标悬停在下载图标上时,显示的文字
- name: 'network-topology'// 下载图片的文件名为network-topology.png
- }
- }
- },
- series: [
- {
- type: 'tree',
- data: this.dataTree,
- left: '10%',
- right: '2%',
- top: '16%',
- bottom: '8%',
- label: {
- position: 'top',
- rotate: -90,
- verticalAlign: 'middle',
- align: 'right',
- color: '#fff',
- fontSize: 9
- },
- orient: 'TB',
- leaves: {
- label: {
- position: 'bottom',
- rotate: -90,
- verticalAlign: 'middle',
- align: 'left'
- }
- },
- symbolSize: [30, 30],
- symbol: function(params, params1) {
- /**
- * 自定义图片后,不知道是否还有子节点未展开
- * 通过打印,发现没有展开的节点,和最后一层子节点的collapsed属性都为true
- * 所以判断collapsed为true,并且没有孩子节点的时候,就是还有没展开的子节点
- * 修改label的样式,来判断是否还存在子节点没展开
- */
- // console.log('params11111111111111111', params1)
- if (params1.collapsed == true && params1.data.children.length > 0) {
- params1.data.label = {
- color: '#0099cc',
- fontSize: '13',
- fontWeight: 'bold'
- }
- }
- },
- edgeForkPosition: '72%',
- emphasis: { // 高亮,个人感觉这个高亮的效果不是很好
- focus: 'descendant'
- },
- initialTreeDepth: 2, // 树图初始展开层级
- roam: true, // 鼠标缩放,拖拽整颗树
- expandAndCollapse: true, // 无关的子树折叠收起
- animationDuration: 550,
- animationDurationUpdate: 750,
- width: '85%'// 组件宽度
- }
- ]
- }
- const chart = this.$refs.main
- if (chart) {
- const that = this
- // console.log('echarts', echarts)
- echarts.init(chart).setOption(this.defaultOpt) // 将画布添加到页面中
- /**
- * 鼠标右键,显示右键菜单
- */
- echarts.init(chart).on('contextmenu', function(params) {
- // console.log('params', params)
- that.contextmenu = true
- // 去掉悬停
- that.$refs.main.children[1].style.display = 'none'
- that.$refs.rightMenu.style.display = 'block';
- // // //让自定义菜单随鼠标的箭头位置移动
- that.$refs.rightMenu.style.left = params.event.offsetX + 45 + 'px'
- that.$refs.rightMenu.style.top = params.event.offsetY + 45 + 'px'
- });
- /**
- * 鼠标单机节点
- * (1)隐藏右键菜单
- * (2)判断子节点是否展开,修改label的样式
- */
- echarts.init(chart).on('click', function(params) {
- that.contextmenu = false
- that.$refs.rightMenu.style.display = 'none';
- // 解决树图点击展开图片不显示问题
- // echarts.init(chart).resize()
- // console.log('params.collapsed', params.collapsed)
- if (params.collapsed == true) {
- params.data.label = {
- color: '#0099cc',
- fontSize: '13',
- fontWeight: 'bold'
- }
- } else {
- params.data.label = {
- color: '#ffffff',
- fontSize: '9',
- fontWeight: 'normal'
- }
- }
- echarts.init(chart).resize() // 不写修改的样式不生效
- });
- // 解决树图首次加载图片不显示问题
- echarts.init(chart).resize()
- }
- }
- })
- },
- /**
- * 点击页面其它地方时,隐藏右键菜单
- * */
- mainClick() {
- this.contextmenu = false
- this.$refs.rightMenu.style.display = 'none';
- },
- /**
- * 鼠标悬停显示详情
- * @param params
- * @returns {string}
- */
- formatterHover(params) {
- // console.log(params);
- var deviceType = params.data.type;
- var imgPath = params.data.symbol;
- // 图片地址截取,因为echarts修改图片的时候有一个------image://---前缀,前缀后面的才是图片真正的地址
- var imgPathSrc = imgPath.split('image://')[1];
- // console.log('str',imgPathSrc);
- if (deviceType === 'Internet' || deviceType === 'hub') {
- return "<img src='" + imgPathSrc + " ' width='30px' height='30px'>" + '<span style="padding:0 5px;font-size: 14px;">' + params.data.name + '</span>';
- } if (deviceType === 'switch') {
- return "<img src='" + imgPathSrc + " ' width='30px' height='30px'>" + '<span style="padding: 0 5px;font-size: 14px;">设备类型:' + params.data.name + '</span>' + '<br>' +
- '<span style="padding-left:5px;height:30px;line-height:30px;display: inline-block;font-size: 14px;">IP:' + params.data.IP + '</span>' + '<br>' +
- '<span style="padding-left:5px;height:30px;line-height:30px;display: inline-block;font-size: 14px;">MAC:' + params.data.MAC + '</span>' + '<br>';
- // +'<button style="padding:2px 5px;border:none;outline:none;color:#ffffff;border-radius: 5px;background:rgba(0,0,0,0.5);"><span class="glyphicon glyphicon-list-alt" aria-hidden="true"></span> IP列表</button>'
- // +'<button style="padding:2px 5px;border:none;outline:none;color:#ffffff;border-radius: 5px;background:rgba(0,0,0,0.5);margin-left: 10px;"><span class="glyphicon glyphicon-list-alt" aria-hidden="true"></span> MAC列表</button>';
- } else {
- return "<img src='" + imgPathSrc + " ' width='30px' height='30px'>" + '<span style="padding: 0 5px;font-size: 14px;">设备类型:' + params.data.name + '</span>' + '<br>' +
- '<span style="padding-left:5px;height:30px;line-height:30px;display: inline-block;font-size: 14px;">IP:' + params.data.IP + '</span>' + '<br>' +
- '<span style="padding-left:5px;height:30px;line-height:30px;display: inline-block;font-size: 14px;">MAC:' + params.data.MAC + '</span>' + '<br>';
- }
- },
- /**
- * 搜索,输入名称时,实现搜索功能
- * */
- handleFilter() {
- const that = this
- var num = 0; // 记录查询到节点的数量
- function readNodes(nodes) {
- for (const item of nodes) { // js遍历树形数组结构
- if (item.children && item.children.length) {
- readNodes(item.children)
- }
- // 查询,名称中包含输入值就修改label颜色和字体大小
- if (item.name.indexOf(that.inputSearch) >= 0 && that.inputSearch != '') {
- item.label = {
- color: 'red',
- fontSize: '15'
- };
- num++;
- } else { // 否则为默认颜色和大小
- item.label = {
- color: '#666',
- fontSize: '9'
- };
- }
- }
- }
- readNodes(this.dataTree); // 调用时,要给data加[],因为原本的data不是一个正常的数组结构
- if (num > 0) { // 查询节点数量大于0的时候展开所有层级
- this.defaultOpt.series[0].initialTreeDepth = -1;
- } else { // 否则恢复初始的层级
- this.defaultOpt.series[0].initialTreeDepth = 2;
- }
- const chart = this.$refs.main
- if (chart) {
- echarts.init(chart).setOption(this.defaultOpt); // 重新设置一遍树图,不然不起效果
- }
- },
- /**
- * 左到右按钮点击
- * @param option 树图属性LR
- * */
- lrClick(option) {
- this.treeDisplay(option)
- },
- /**
- * 右到左按钮点击
- * @param option 树图属性RL
- * */
- rlClick(option) {
- this.treeDisplay(option)
- },
- /**
- * 上到下按钮点击
- * @param option 树图属性TB
- * */
- tbClick(option) {
- this.treeDisplay(option)
- },
- /**
- * 下到上按钮点击
- * @param option 树图属性BT
- * */
- btClick(option) {
- this.treeDisplay(option)
- },
- /**
- * 左到右,右到左,上到下,下到上按钮点击时,对拓扑属性的设置
- * orient:修改拓扑图展示的方向
- * label、leaves修改结点旁边的文字展示
- * */
- treeDisplay(option) {
- this.defaultOpt.series[0].orient = option;
- // console.log('option', option)
- if (option == 'LR') {
- this.defaultOpt.series[0].label = {
- position: 'left',
- rotate: 0,
- verticalAlign: 'middle',
- align: 'right',
- color: '#fff',
- fontSize: 9
- }
- this.defaultOpt.series[0].leaves = {
- label: {
- position: 'right',
- rotate: 0,
- verticalAlign: 'middle',
- align: 'left'
- }
- }
- }
- if (option == 'RL') {
- this.defaultOpt.series[0].label = {
- position: 'right',
- rotate: 0,
- verticalAlign: 'middle',
- align: 'left',
- color: '#fff',
- fontSize: 9
- }
- this.defaultOpt.series[0].leaves = {
- label: {
- position: 'left',
- rotate: 0,
- verticalAlign: 'middle',
- align: 'right'
- }
- }
- }
- if (option == 'TB') {
- this.defaultOpt.series[0].label = {
- position: 'top',
- rotate: -90,
- verticalAlign: 'middle',
- align: 'right',
- color: '#fff',
- fontSize: 9
- }
- this.defaultOpt.series[0].leaves = {
- label: {
- position: 'bottom',
- rotate: -90,
- verticalAlign: 'middle',
- align: 'left'
- }
- }
- }
- if (option == 'BT') {
- this.defaultOpt.series[0].label = {
- position: 'bottom',
- rotate: 90,
- verticalAlign: 'middle',
- align: 'right',
- color: '#fff',
- fontSize: 9
- }
- this.defaultOpt.series[0].leaves = {
- label: {
- position: 'top',
- rotate: 90,
- verticalAlign: 'middle',
- align: 'left'
- }
- }
- }
- const chart = this.$refs.main
- if (chart) {
- echarts.init(chart).setOption(this.defaultOpt);
- }
- }
- }
- }
- </script>
-
- <style scoped>
- .menu{
- /*这个样式不写,右键弹框会一直显示在画布的左下角*/
- position: absolute;
- background: rgba(255, 255, 255, 1);
- border-radius: 5px;
- left: -99999px;
- top: -999999px;
- }
- .menu ul{
- list-style: none;
- padding: 0;
- margin: 0;
- }
- .menu ul li{
- cursor: pointer;
- padding: 5px 10px;
- color: #000000;
- border-bottom: 1px dashed #a9a9a9;
- font-size: 14px;
- }
- .menu ul li:last-child{
- border-bottom: none;
- }
- </style>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。