1.实现背景
项目需要直观的展示元素之间的关系,需要实现一个树状图
数据可视化可以用Echarts HighCharts,但是相关树状图的示例不够直观,且不美观
几种工具之间比较,选择了蚂蚁金服的G6来实现(在开发期间有树状图的示例,之后再看发现这个例子没有了)
2.相关技术介绍
G6图可视化引擎(蚂蚁数据可视化 G6)
G6 是一个图可视化引擎。它提供了图的绘制、布局、分析、交互、动画等图可视化的基础能力。旨在让关系变得透明,简单。让用户获得关系数据的 Insight。
3.项目集成
3.1树状图渲染公共方法
因为页面有多处用到了树状图的逻辑,所以将方法封装成了公共的方法
文件路径 /src/utils/treeNode.js
3.1.2handleTree 处理渲染数据
主要用于将后端返回的数据处理为可以成功渲染树状图的数据格式
具体逻辑解释见代码中注释
export function handleTree(data,currentComponent){
// 判断数据类型 根节点只有一个,不是数组
if(Array.isArray(data)){
data.forEach(item => {
// data是后端返回数据 处理为有 id label style键值的对象,主要根据这几个值渲染树状图
item.id = item.component.componentCode;
item.label = item.component.name;
item.style = {fill:"#91d5ff",stroke: '#40a9ff',radius: 5};// 每个节点的背景色 边框颜色 矩形的圆角样式;可以根据具体的需求处理节点样式
// 当前节点高亮,区分于其他节点
if(item.component.componentCode == currentComponent){
item.style = {fill:"#45b8ff",stroke: '#2aa0fb',radius: 5};
}
// 判断是否还有子节点
if(item.children.length != 0 ){
// 递归渲染子节点
handleTree(item.children,currentComponent);
}
});
}else{
data.id = data.component.componentCode;
data.label = data.component.name;
data.style = {fill:"#91d5ff",stroke: '#40a9ff',radius: 5};
if(data.component.componentCode == currentComponent){
data.style = {fill:"#45b8ff",stroke: '#2aa0fb',radius: 5};
}
if(data.children.length !=0 ){
handleTree(data.children,currentComponent);
}
}
}
3.1.3renderTreeNode 处理渲染视图
export function renderTreeNode(data,currentComponent) {
// 渲染树状图方法
G6.Util.traverseTree(data)
// 树状图线的样式
G6.registerEdge('flow-line', {
draw(cfg, group) {
const startPoint = cfg.startPoint;
const endPoint = cfg.endPoint;
const { style } = cfg
const shape = group.addShape('path', {
attrs: {
stroke: style.stroke,
endArrow: style.endArrow,
path: [
['M', startPoint.x, startPoint.y],
['L', startPoint.x, (startPoint.y + endPoint.y) / 2],
['L', endPoint.x, (startPoint.y + endPoint.y) / 2,],
['L', endPoint.x, endPoint.y],
],
},
});
return shape;
}
});
// 定义节点样式
const defaultNodeStyle = {
stroke: '#40a9ff',
radius: 5
}
// 定义线的样式
const defaultEdgeStyle = {
stroke: '#91d5ff',
endArrow: {
path: 'M 0,0 L 12, 6 L 9,0 L 12, -6 Z',
fill: '#91d5ff',
d: -20
}
}
// 基本布局
const defaultLayout = {
type: 'compactBox',
direction: 'TB',
getId: function getId(d) {
return d.id;
},
getHeight: function getHeight() {
return 16;
},
getWidth: function getWidth() {
return 16;
},
getVGap: function getVGap() {
return 40;
},
getHGap: function getHGap() {
return 70;
},
}
// 定义文本样式
const defaultLabelCfg = { // label显示
style: {
fontSize: 12,
}
}
const graph = new G6.TreeGraph({
container: 'container', // 页面树状图渲染容器
width:500,
height:400,
linkCenter: true,
defaultNode: { // 节点样式
size: [120, 30],
shape:"rect",
style: defaultNodeStyle,// 节点样式
labelCfg: defaultLabelCfg// 文本样式
},
defaultEdge: { // 线的样式
type: 'flow-line',
style: defaultEdgeStyle,
},
layout: defaultLayout
});
graph.data(data);
graph.render();
graph.fitView();
}
3.2页面树状图容器
<div id="container"></div><div id="container"></div>
3.3在合适的时机调用方法
getTree(componentCode) {
// 传入的componentCode主要用来区分当前节点
handleTree(this.treeData,componentCode);
renderTreeNode(this.treeData,componentCode);
}
3.4注意点
项目中可以显示列表数据中每一条对应的树状图,所以存在树状图的切换;
注意要把容器内之前渲染的数据清除,重新渲染新的数据;否则会显示多个树状图
if (document.getElementById("container")) {
document.getElementById("container").innerHTML = "";
}
3.5树状图效果图