|
|
|
@@ -1,25 +1,19 @@ |
|
|
|
import { Graph } from '@antv/g6'; |
|
|
|
import { useSize } from 'ahooks'; |
|
|
|
import { useFetchKnowledgeGraph } from '@/hooks/chunk-hooks'; |
|
|
|
import { ElementDatum, Graph, IElementEvent } from '@antv/g6'; |
|
|
|
import { useCallback, useEffect, useMemo, useRef } from 'react'; |
|
|
|
import { graphData } from './constant'; |
|
|
|
import { Converter, buildNodesAndCombos, isDataExist } from './util'; |
|
|
|
import { buildNodesAndCombos, isDataExist } from './util'; |
|
|
|
|
|
|
|
import { useFetchKnowledgeGraph } from '@/hooks/chunk-hooks'; |
|
|
|
import styles from './index.less'; |
|
|
|
|
|
|
|
const converter = new Converter(); |
|
|
|
|
|
|
|
const nextData = converter.buildNodesAndCombos( |
|
|
|
graphData.nodes, |
|
|
|
graphData.edges, |
|
|
|
); |
|
|
|
console.log('🚀 ~ nextData:', nextData); |
|
|
|
|
|
|
|
const finalData = { ...graphData, ...nextData }; |
|
|
|
const TooltipColorMap = { |
|
|
|
combo: 'red', |
|
|
|
node: 'black', |
|
|
|
edge: 'blue', |
|
|
|
}; |
|
|
|
|
|
|
|
const ForceGraph = () => { |
|
|
|
const containerRef = useRef<HTMLDivElement>(null); |
|
|
|
const size = useSize(containerRef); |
|
|
|
const graphRef = useRef<Graph | null>(null); |
|
|
|
const { data } = useFetchKnowledgeGraph(); |
|
|
|
|
|
|
|
const nextData = useMemo(() => { |
|
|
|
@@ -30,12 +24,12 @@ const ForceGraph = () => { |
|
|
|
} |
|
|
|
return { nodes: [], edges: [] }; |
|
|
|
}, [data]); |
|
|
|
console.log('🚀 ~ nextData ~ nextData:', nextData); |
|
|
|
|
|
|
|
const render = useCallback(() => { |
|
|
|
const graph = new Graph({ |
|
|
|
container: containerRef.current!, |
|
|
|
autoFit: 'view', |
|
|
|
autoResize: true, |
|
|
|
behaviors: [ |
|
|
|
'drag-element', |
|
|
|
'drag-canvas', |
|
|
|
@@ -49,16 +43,23 @@ const ForceGraph = () => { |
|
|
|
plugins: [ |
|
|
|
{ |
|
|
|
type: 'tooltip', |
|
|
|
getContent: (e, items) => { |
|
|
|
if (items.every((x) => x?.description)) { |
|
|
|
enterable: true, |
|
|
|
getContent: (e: IElementEvent, items: ElementDatum) => { |
|
|
|
if (Array.isArray(items)) { |
|
|
|
let result = ``; |
|
|
|
items.forEach((item) => { |
|
|
|
result += `<h3>${item?.id}</h3>`; |
|
|
|
result += `<section style="color:${TooltipColorMap[e['targetType'] as keyof typeof TooltipColorMap]};"><h3>${item?.id}</h3>`; |
|
|
|
if (item?.entity_type) { |
|
|
|
result += `<div style="padding-bottom: 6px;"><b>Entity type: </b>${item?.entity_type}</div>`; |
|
|
|
} |
|
|
|
if (item?.weight) { |
|
|
|
result += `<div><b>Weight: </b>${item?.weight}</div>`; |
|
|
|
} |
|
|
|
if (item?.description) { |
|
|
|
result += `<p>${item?.description}</p>`; |
|
|
|
} |
|
|
|
}); |
|
|
|
return result; |
|
|
|
return result + '</section>'; |
|
|
|
} |
|
|
|
return undefined; |
|
|
|
}, |
|
|
|
@@ -68,44 +69,44 @@ const ForceGraph = () => { |
|
|
|
type: 'combo-combined', |
|
|
|
preventOverlap: true, |
|
|
|
comboPadding: 1, |
|
|
|
spacing: 20, |
|
|
|
spacing: 100, |
|
|
|
}, |
|
|
|
node: { |
|
|
|
style: { |
|
|
|
size: 20, |
|
|
|
size: 150, |
|
|
|
labelText: (d) => d.id, |
|
|
|
labelPadding: 30, |
|
|
|
// labelPadding: 30, |
|
|
|
labelFontSize: 40, |
|
|
|
// labelOffsetX: 20, |
|
|
|
// labelOffsetY: 5, |
|
|
|
labelOffsetY: 20, |
|
|
|
labelPlacement: 'center', |
|
|
|
labelWordWrap: true, |
|
|
|
}, |
|
|
|
palette: { |
|
|
|
type: 'group', |
|
|
|
field: (d) => d.combo, |
|
|
|
field: (d) => { |
|
|
|
return d?.entity_type as string; |
|
|
|
}, |
|
|
|
}, |
|
|
|
// state: { |
|
|
|
// highlight: { |
|
|
|
// fill: '#D580FF', |
|
|
|
// halo: true, |
|
|
|
// lineWidth: 0, |
|
|
|
// }, |
|
|
|
// dim: { |
|
|
|
// fill: '#99ADD1', |
|
|
|
// }, |
|
|
|
// }, |
|
|
|
}, |
|
|
|
edge: { |
|
|
|
style: (model) => { |
|
|
|
const { size, color } = model.data; |
|
|
|
const weight: number = Number(model?.weight) || 2; |
|
|
|
const lineWeight = weight * 4; |
|
|
|
return { |
|
|
|
stroke: color || '#99ADD1', |
|
|
|
lineWidth: size || 1, |
|
|
|
stroke: '#99ADD1', |
|
|
|
lineWidth: lineWeight > 10 ? 10 : lineWeight, |
|
|
|
}; |
|
|
|
}, |
|
|
|
}, |
|
|
|
}); |
|
|
|
|
|
|
|
if (graphRef.current) { |
|
|
|
graphRef.current.destroy(); |
|
|
|
} |
|
|
|
|
|
|
|
graphRef.current = graph; |
|
|
|
|
|
|
|
graph.setData(nextData); |
|
|
|
|
|
|
|
graph.render(); |
|
|
|
@@ -117,7 +118,13 @@ const ForceGraph = () => { |
|
|
|
} |
|
|
|
}, [data, render]); |
|
|
|
|
|
|
|
return <div ref={containerRef} className={styles.forceContainer} />; |
|
|
|
return ( |
|
|
|
<div |
|
|
|
ref={containerRef} |
|
|
|
className={styles.forceContainer} |
|
|
|
style={{ width: '100%', height: '80vh' }} |
|
|
|
/> |
|
|
|
); |
|
|
|
}; |
|
|
|
|
|
|
|
export default ForceGraph; |