其实这些效果之前都有用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 = falsethis.$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.offsetXconst 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 = thisvar 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.mainif (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 = falsethat.$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.mainif (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.mainif (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': '-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': '-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.mainif (!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.dataif (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.mainif (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 = falsethat.$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 = falsethis.$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 = thisvar 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.mainif (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.mainif (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>
vue实现echarts树图修改节点图片 修改连线颜色 鼠标悬停显示详情 鼠标右键弹出菜单 搜索 导出PNG 高亮 查看节点是否还有子节点 修改树图的展示方式