html界面引入<script src="https://cdn.bootcdn.net/ajax/libs/echarts/5.3.3/echarts.min.js"></script>
定义一个绘制树图的容器 <div id="chart-container" style="height: 800px;width: 100%;"></div>
js代码:
option = {
tooltip: { //鼠标悬停在节点效果
trigger: 'item',
triggerOn: 'mousemove'
},
series: [
{
// 当此节点下还有子节点时候,设置的节点样式,用于区别 没有子节点的节点的样式
itemStyle: {
normal: {
color: '#06c',
borderColor: '#06c',
},
emphasis: {
color: '#ffffff',
borderColor: '#06c',
}
},
lineStyle: { //线的样式
color: '#0dc6e4',
height:50,
},
type: 'tree',
data: [datascource], //data为树图的数据,格式:{id:111,name:xxx,children:[{id:222,name:xxx,children:[...]},...]}
left: '2%',
right: '2%',
top: '8%',
bottom: '20%',
// symbol: 'rect',
symbolSize:7, //节点大小
orient: 'vertical',
expandAndCollapse: true, //是否开启折叠功能
edgeShape: 'polyline',
label: { //节点样式
fontFamily: 'KaiTi',
position: 'top', //节点文字在节点的上方
// verticalAlign: 'middle',
// align: 'right',
overflow : 'hidden',
margin: [2, 4],
borderWidth: 1, //文字添加边框
borderColor: '#FFFFFF', //边框颜色
backgroundColor: '#0060fb', //节点模块背景色
borderRadius: 2, //圆角
padding: [5, 4],
rich: {
keywords: {
color: "red",
fontSize: 12,
},
index: {
fontSize: 12,
color: '#2979ff',
position: '10%'
},
},
textStyle:{
width:100, //定义节点文字长度
fontSize: 18, //文字大小
color: '#FFFFFF', //文字颜色
align:'center', //文字的位置
},
formatter:function (value){
if(value.data.name.length>6){
value.data.name = value.data.name.substring(0,5) + '..' //如果超出6个字符,显示5个+..
}
return value.data.name
}
},
leaves: {
label: {
position: 'top',
verticalAlign: 'middle',
// align: 'left'
}
},
animationDurationUpdate: 10 //动画过渡效果的时间,毫秒
}
]
};
var chartDom = document.getElementById('chart-container');
var myChart = echarts.init(chartDom);
myChart.setOption(option);
以上代码可以实现树形图的绘制啦!效果图如下:
实现左键显示选择节点、右键单击出现操作菜单:(前提要有菜单控件)
js代码:
myChart.on('mousedown',function (param){
let algorithm_name;
let weight_name;
node_obj = param;
$('#floatingNode').modal('hide'); //左键隐藏菜单//已选节点赋值
$('#selected-node').val(param.data.name);
if (3 == param.event.which){
$('#floatingNode').css("left", document.body.scrollLeft + event.clientX + 1);
$('#floatingNode').css("top", document.body.scrollLeft + event.clientY + 10);
$('#floatingNode').modal('show'); //右键显示菜单
}
});
//取消树图内右键的浏览器默认点击事件
$('#chart-container').bind("contextmenu",function(){
return false;
});
// 点击树图隐藏右键打开的菜单
$('#chart-container').on('click',function(){
$('#floatingNode').modal('hide');
});
以下为实现增删改节点,默认展开操作的分支并关闭其他分支
js代码:
//点击增删改隐藏右键菜单,并打开操作表单页
$('.control_a').unbind('click').on('click', function(){
$('#floatingNode').modal('hide');
if($(this).attr('id')=='add_btn'){
$('#node_input').html(add_row_value);
$('#add_new_row').css("display","")
$('#sub_new_row').css("display","none")
$('#new_node').val('');
$('#modal-title').text('添加子节点');
$('#submit_btn').unbind('click').on('click', function(){
var new_node_list = "";
$('.new_node').each(function(){
if($(this).val()!=""){
new_node_list+=($(this).val()+";");
}
});
$.ajax({
method: 'post',
type: 'json',
data: {
"target_id":current_nodeID,
"assess_name_list_str":new_node_list,
},
url: '/assess/add_target_system_children/',
success: function (result) {
if(result.flag){
$.ajax({
async:false,
method:'post',
type:'json',
data:{
"target_id":target_id,
},
url:'/assess/initCharts/',
success:function(result){
//获取树图数据datascource
datascource=result.data;
let open_node_id = [];
id_and_pid_list = result.id_and_pid_list;
var operation_node_id = current_nodeID;
open_node_id.push(operation_node_id);
open_node_ergodic(id_and_pid_list,operation_node_id,open_node_id);
if (datascource.children.length!=0){
var children_list = datascource.children;
node_ergodic(children_list,open_node_id);
}
myChart.clear();
option.series[0].data = [datascource];
myChart.setOption(option,true);
},
error:function(e){
datascource=null;
}
});
}else {
$('#message_tips').modal('show');
$('#message').text(result.data);
}
},
});
});
}else if ($(this).attr('id')=='alt_btn'){
$('#node_input').html(add_row_value);
$('#add_new_row').css("display","none")
$('#new_node').val($('.focused').text());
$('#modal-title').text('修改节点');
$('#submit_btn').unbind('click').on('click', function(){
if(target_id==current_nodeID){
alert('Please do not delete the root node directly. If you need to modify it, please process it in the build indicator function');
}else {
$('#process_tips').modal('show');
$('#process_msg').text("确定将名称为("+node_obj.data.name+")的节点修改成("+$('#new_node').val()+")吗?");
$('#process_submit').unbind('click').on('click', function(){
//调用修改节点方法,参数需要该节点的target_id也就是id和要修改的名称
$.ajax({
method: 'post',
type: 'json',
data: {
"target_id":current_nodeID,
"assess_name":$('#new_node').val(),
},
url: '/assess/alt_nodes_node/',
success: function (result) {
if(result.flag){
$.ajax({
async:false,
method:'post',
type:'json',
data:{
"target_id":target_id,
},
url:'/assess/initCharts/',
success:function(result){
//获取树图数据datascource
datascource=result.data;
let open_node_id = [];
id_and_pid_list = result.id_and_pid_list;
var operation_node_id = current_nodeID;
open_node_ergodic(id_and_pid_list,operation_node_id,open_node_id);
if (datascource.children.length!=0){
var children_list = datascource.children;
node_ergodic(children_list,open_node_id);
}
myChart.clear();
option.series[0].data = [datascource];
myChart.setOption(option,true);
},
error:function(e){
datascource=null;
}
});
}else {
$('#add_new_node').val('');
$('#message_tips').modal('show');
$('#message').text(result.data);
}
},
});
});
}
});
}else if ($(this).attr('id')=='del_btn'){
if(target_id==current_nodeID){
alert('Please do not delete the root node directly. If you need to delete it, please process it in the build indicator function');
}else {
$('#process_tips').modal('show');
$('#process_msg').text("确定删除名称为("+node_obj.data.name+")的节点及其子节点?");
$('#process_submit').unbind('click').on('click', function(){
//调用删除节点方法,参数需要该节点的target_id也就是id
$.ajax({
method: 'post',
type: 'json',
data: {
"target_id":current_nodeID,
},
url: '/assess/delete_nodes_node/',
success: function (result) {
if(result.flag){
$.ajax({
async:false,
method:'post',
type:'json',
data:{
"target_id":target_id,
},
url:'/assess/initCharts/',
success:function(result){
//获取树图数据datascource
datascource=result.data;
let open_node_id = [];
var operation_node_id = 0;
for (var i in id_and_pid_list){
if (current_nodeID == id_and_pid_list[i].id){
operation_node_id = id_and_pid_list[i].pid;
continue;
}
}
open_node_id.push(operation_node_id);
open_node_ergodic(id_and_pid_list,operation_node_id,open_node_id);
if (datascource.children.length!=0){
var children_list = datascource.children;
node_ergodic(children_list,open_node_id);
}
myChart.clear();
option.series[0].data = [datascource];
myChart.setOption(option,true);
},
error:function(e){
datascource=null;
}
});
}else {
$('#delete_nodes').modal('hide');
$('#message_tips').modal('show');
$('#message').text(result.data);
}
},
});
});
}
}else{
//根据主机名获取所有的原子名称
$('#host_name').change(function(){
$.ajax({
method: 'post',
type: 'json',
data: {
"hostId":$('#host_name').val(),
},
url: '/assess/get_atom_by_hostId/',
success: function (result) {
//获取主机列表
var atom_data = result.data;
var atom_class = '<option style="background-color: #747779" value=""></option>';
for (var i = 0; i < atom_data.length; i++) {
atom_class += '<option value="' + atom_data[i].itemid + '">' + atom_data[i].name + '</option>';
}
$("#atom_name").html(atom_class);
},
});
});
//点击确定按钮,执行数据绑定
$('#commit_btn').unbind('click').on('click', function(){
$.ajax({
method: 'post',
type: 'json',
data: {
"host_id":$('#host_name').val(),
"atom_id":$('#atom_name').val(),
"selectNode_id":current_nodeID,
},
url: '/assess/atomic_data_binding/',
success: function (result) {
if(result.flag){
$('#message_tips').modal('show');
$('#message').text(result.data);
//执行原子绑定后
if(result.atom_name!=""){
$('#add_btn').css("display","none");
}else {
$('#add_btn').css("display","");
}
$('#bind_host_name').val(result.host_name!=""?result.host_name:"未绑定");
$('#bind_atom_name').val(result.atom_name!=""?result.atom_name:"未绑定");
}
},
});
});
}
});
大致思路是:执行数据库增删改节点后,再次调用查询树图数据的接口,重新渲染Echarts树形图(这里需要保留操作节点和其父节点、父节点的父节点一直到根节点的标识,这里我用一个[ ]存储)
执行树形图不同节点的展开项算法:(仅供参考)
// 节点展开隐藏状态递归算法
function node_ergodic(children_list,open_node_id){
if (children_list.length > 0){
for (var i in children_list){
for (var j in open_node_id){
if (children_list[i].id == open_node_id[j]){
children_list[i].collapsed = false;
continue;
}
}
var new_children_list = children_list[i].children;
node_ergodic(new_children_list,open_node_id)
}
}
}
// 递归得到需要展开节点的id
function open_node_ergodic(id_and_pid_list,operation_node_id,open_node_id){
if (id_and_pid_list.length>0){
for(var i in id_and_pid_list){
if(id_and_pid_list[i].id == operation_node_id){
operation_node_id = id_and_pid_list[i].pid;
if (operation_node_id == 0){
break;
}
open_node_id.push(operation_node_id);
open_node_ergodic(id_and_pid_list,operation_node_id,open_node_id);
}
}
}
}
主要说的是树形图的处理,原子数据绑定和聚合算法在这里就不提了
最后说一下途中踩到的坑。
在重新渲染树形图的时候,使用myChart.setOption有线残留的问题,查了很久网页上的解决办法只有在第二个参数上加true。
官网给出的解释是不整合之前的option数据,但是线残留并没有解决,后来在渲染前加.clear()方法得以解决。
但是导致了动画的过渡效果没有了,不能像展开收起那样看起来平滑。
就说这么多了,如果有更好的解决办法欢迎在下方留言哈,感激不尽!