一翎朱、技術(shù)背景
React Hook +Antv g6
二尺铣、需求
分別以主題--層次--任務(wù)--任務(wù)流的維度進(jìn)行樹形管理迄埃。并且可以已任務(wù)的維度進(jìn)行檢索,均展示名稱:
image.png
三蕉汪、選擇工具
- 選擇很重要逞怨,花了兩天時間對比了echarts、antv里的G2驹马、G6除秀。這個過程有點(diǎn)煩,先是嘗試echarts里的關(guān)系圖泳姐,畢竟是大廠出來的暂吉,API文檔還是挺全乎的缎患,也易懂阎肝。深入研究后發(fā)現(xiàn)graph并不支持重復(fù)節(jié)點(diǎn),無法實(shí)現(xiàn)需求芭析,棄用桨昙。再看看echarts里的tree,echarts里的樹圖示例真的是好丑。
- 轉(zhuǎn)入Antv里的G6看看朦蕴,樣例中G6對流程圖弟头、樹圖、文件系統(tǒng)圖支持的很好赴恨,但有不如意的地方:G6的文檔沒有echarts完善伦连、專業(yè)。但G6的優(yōu)點(diǎn)也很明顯额港,對此類圖支持的很好歧焦、靈活,提供豐富的接口向瓷。
四舰涌、實(shí)現(xiàn)
1、安裝
// 在項(xiàng)目目錄下執(zhí)行命令安裝
npm install --save @antv/g6
// 引入依賴使用
import G6 from '@antv/g6';
2超升、在componentDidMount生命周期函數(shù)中調(diào)用接口獲取數(shù)據(jù)
componentDidMount() {
this.handleSearch();
}
3、給子組件傳遞屬性
render() {
const { g6Data } = this.state;
return (
<>
<Header title={intl.get('xdis.taskJob.view.title.treeGraph').d('任務(wù)關(guān)系')} />
<Content id="job-tree-content" className={styles['job-tree-content']}>
<G6TreeGraph g6Data={g6Data} />
</Content>
</>
);
}
4乾闰、在子組件useEffect方法中創(chuàng)建實(shí)例盈滴,監(jiān)聽g6Data屬性是否發(fā)生變化,第一次變化創(chuàng)建實(shí)例病苗,之后每一次更新數(shù)據(jù)症汹,并重新渲染樹圖。
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]);
注意,當(dāng)前組件中需要有一個容器來掛載Graph
<div ref={ref} id="job-tree-graph" style={{ height: '100%', width: '100%' }} />
5破婆、自定義節(jié)點(diǎn)胸囱,以任務(wù)類型節(jié)點(diǎn)為示例
1625411287644.png
6烹笔、自定義節(jié)點(diǎn)的點(diǎn)擊事件,根據(jù)節(jié)點(diǎn)的類型和狀態(tài)來判斷嚎朽,是否展開和收縮子節(jié)點(diǎn)柬帕。
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();
}
});