一、技术背景
React Hook +Antv g6
二、需求
分别以主题--层次--任务--任务流的维度进行树形管理。并且可以已任务的维度进行检索,均展示名称:
三、选择工具
- 选择很重要,花了两天时间对比了echarts、antv里的G2、G6。这个过程有点烦,先是尝试echarts里的关系图,毕竟是大厂出来的,API文档还是挺全乎的,也易懂。深入研究后发现graph并不支持重复节点,无法实现需求,弃用。再看看echarts里的tree,echarts里的树图示例真的是好丑。
- 转入Antv里的G6看看,样例中G6对流程图、树图、文件系统图支持的很好,但有不如意的地方:G6的文档没有echarts完善、专业。但G6的优点也很明显,对此类图支持的很好、灵活,提供丰富的接口。
四、实现
1、安装
// 在项目目录下执行命令安装
npm install --save @antv/g6
// 引入依赖使用
import G6 from '@antv/g6';
2、在componentDidMount生命周期函数中调用接口获取数据
componentDidMount() {
this.handleSearch();
}
3、给子组件传递属性
render() {
const { g6Data } = this.state;
return (
<>
<Header title={intl.get('xdis.taskJob.view.title.treeGraph').d('任务关系')} />
<Content id="job-tree-content" className={styles['job-tree-content']}>
<G6TreeGraph g6Data={g6Data} />
</Content>
</>
);
}
4、在子组件useEffect方法中创建实例,监听g6Data属性是否发生变化,第一次变化创建实例,之后每一次更新数据,并重新渲染树图。
useEffect(() => {
if (!isEmpty(g6Data)) {
if (!graph.current) {
// 初始化画布
const container = document.getElementById('job-tree-graph');
const width = container.scrollWidth;
const height = container.scrollHeight || 600;
graph.current = new G6.TreeGraph({
container: 'job-tree-graph',
width,
height,
modes: {
default: ['zoom-canvas', 'drag-canvas'],
},
defaultNode: {
type: 'circle',
size: [20, 20],
style: {
stroke: '#73ADD9',
radius: 5,
fill: '#73ADD9',
},
},
defaultEdge: {
type: 'cubic-horizontal',
style: {
endArrow: true,
},
},
plugins: [tooltip],
layout: {
type: 'indented',
direction: 'LR',
dropCap: false,
indent: 200,
getHeight: () => {
return 40;
},
},
});
graph.current.data(g6Data);
graph.current.render();
graph.current.fitView();
graph.current.moveTo(10, 50);
if (typeof window !== 'undefined') {
window.onresize = () => {
if (!graph.current || graph.current.get('destroyed')) return;
if (!container || !container.scrollWidth || !container.scrollHeight) return;
graph.current.changeSize(container.scrollWidth, container.scrollHeight);
};
}
} else {
// 更新
graph.current.changeData(g6Data);
graph.current.paint();
graph.current.fitView();
graph.current.moveTo(10, 50);
}
}
}, [g6Data]);
注意,当前组件中需要有一个容器来挂载Graph
<div ref={ref} id="job-tree-graph" style={{ height: '100%', width: '100%' }} />
5、自定义节点,以任务类型节点为示例
6、自定义节点的点击事件,根据节点的类型和状态来判断,是否展开和收缩子节点。
graph.current.on('node:click', (e) => {
const {
objectType,
layerId,
themeId,
jobId,
collapsed,
id,
children = [],
} = e.item.getModel();
if (e.target.get('name') === 'collapse-icon') {
e.item.getModel().collapsed = !collapsed;
graph.current.setItemState(e.item, 'collapsed', !collapsed);
if (collapsed && isEmpty(children)) {
if (objectType === OBJECT_TYPE.LAYER) {
loadJobNodes({ themeId, layerId }, id, e);
} else if (objectType === OBJECT_TYPE.JOB) {
loadFlowNodes({ jobId }, id, e);
}
}
graph.current.layout();
}
});