mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? Fix: Generate avatar; Add knowledge graph; Modify the style of the multi-select component [#3221](https://github.com/infiniflow/ragflow/issues/3221) ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
142 lines
3.6 KiB
TypeScript
142 lines
3.6 KiB
TypeScript
import { ElementDatum, Graph, IElementEvent } from '@antv/g6';
|
|
import isEmpty from 'lodash/isEmpty';
|
|
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
import { buildNodesAndCombos } from './util';
|
|
|
|
import styles from './index.less';
|
|
|
|
const TooltipColorMap = {
|
|
combo: 'red',
|
|
node: 'black',
|
|
edge: 'blue',
|
|
};
|
|
|
|
interface IProps {
|
|
data: any;
|
|
show: boolean;
|
|
}
|
|
|
|
const ForceGraph = ({ data, show }: IProps) => {
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
const graphRef = useRef<Graph | null>(null);
|
|
|
|
const nextData = useMemo(() => {
|
|
if (!isEmpty(data)) {
|
|
const graphData = data;
|
|
const mi = buildNodesAndCombos(graphData.nodes);
|
|
return { edges: graphData.edges, ...mi };
|
|
}
|
|
return { nodes: [], edges: [] };
|
|
}, [data]);
|
|
|
|
const render = useCallback(() => {
|
|
const graph = new Graph({
|
|
container: containerRef.current!,
|
|
autoFit: 'view',
|
|
autoResize: true,
|
|
behaviors: [
|
|
'drag-element',
|
|
'drag-canvas',
|
|
'zoom-canvas',
|
|
'collapse-expand',
|
|
{
|
|
type: 'hover-activate',
|
|
degree: 1, // 👈🏻 Activate relations.
|
|
},
|
|
],
|
|
plugins: [
|
|
{
|
|
type: 'tooltip',
|
|
enterable: true,
|
|
getContent: (e: IElementEvent, items: ElementDatum) => {
|
|
if (Array.isArray(items)) {
|
|
if (items.some((x) => x?.isCombo)) {
|
|
return `<p style="font-weight:600;color:red">${items?.[0]?.data?.label}</p>`;
|
|
}
|
|
let result = ``;
|
|
items.forEach((item) => {
|
|
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 + '</section>';
|
|
}
|
|
return undefined;
|
|
},
|
|
},
|
|
],
|
|
layout: {
|
|
type: 'combo-combined',
|
|
preventOverlap: true,
|
|
comboPadding: 1,
|
|
spacing: 100,
|
|
},
|
|
node: {
|
|
style: {
|
|
size: 150,
|
|
labelText: (d) => d.id,
|
|
// labelPadding: 30,
|
|
labelFontSize: 40,
|
|
// labelOffsetX: 20,
|
|
labelOffsetY: 20,
|
|
labelPlacement: 'center',
|
|
labelWordWrap: true,
|
|
},
|
|
palette: {
|
|
type: 'group',
|
|
field: (d) => {
|
|
return d?.entity_type as string;
|
|
},
|
|
},
|
|
},
|
|
edge: {
|
|
style: (model) => {
|
|
const weight: number = Number(model?.weight) || 2;
|
|
const lineWeight = weight * 4;
|
|
return {
|
|
stroke: '#99ADD1',
|
|
lineWidth: lineWeight > 10 ? 10 : lineWeight,
|
|
};
|
|
},
|
|
},
|
|
});
|
|
|
|
if (graphRef.current) {
|
|
graphRef.current.destroy();
|
|
}
|
|
|
|
graphRef.current = graph;
|
|
|
|
graph.setData(nextData);
|
|
|
|
graph.render();
|
|
}, [nextData]);
|
|
|
|
useEffect(() => {
|
|
if (!isEmpty(data)) {
|
|
render();
|
|
}
|
|
}, [data, render]);
|
|
|
|
return (
|
|
<div
|
|
ref={containerRef}
|
|
className={styles.forceContainer}
|
|
style={{
|
|
width: '100%',
|
|
height: '100%',
|
|
display: show ? 'block' : 'none',
|
|
}}
|
|
/>
|
|
);
|
|
};
|
|
|
|
export default ForceGraph;
|