其实这些效果之前都有用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 "节点图片连线颜色 搜索查询导出PNG vue实现echarts树图修改节点图片,修改连线颜色,鼠标悬停显示详情,鼠标右键弹出菜单,搜索,导出PNG,高亮,查看节点是否还有子节点,修改树图的展示方式  第1张" + '' + params.data.name + '';

} if (deviceType === 'switch') {

return "节点图片连线颜色 搜索查询导出PNG vue实现echarts树图修改节点图片,修改连线颜色,鼠标悬停显示详情,鼠标右键弹出菜单,搜索,导出PNG,高亮,查看节点是否还有子节点,修改树图的展示方式  第1张" + '设备类型:' + params.data.name + '' + '
' +

'IP:' + params.data.IP + '' + '
' +

'MAC:' + params.data.MAC + '' + '
';

// +''

// +'';

} else {

return "节点图片连线颜色 搜索查询导出PNG vue实现echarts树图修改节点图片,修改连线颜色,鼠标悬停显示详情,鼠标右键弹出菜单,搜索,导出PNG,高亮,查看节点是否还有子节点,修改树图的展示方式  第1张" + '设备类型:' + params.data.name + '' + '
' +

'IP:' + params.data.IP + '' + '
' +

'MAC:' + params.data.MAC + '' + '
';

}

},

3.鼠标右键弹出菜单

代码布局如下:

 

主要代码:

/**

* 鼠标右键,显示右键菜单

*/

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.搜索

代码布局如下:

 主要代码:

搜索

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.修改树图的展示方式

注:起初只是简单的实现了切换的效果,并没有注重细节主要如下:

代码布局:

主要代码:

左到右

右到左

上到下

下到上

/**

* 左到右按钮点击

* @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

好文推荐

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。