fix: 修复知识图谱节点信息无法正常渲染问题
This commit is contained in:
@@ -26,85 +26,134 @@ export const GraphView = ({
|
|||||||
if (!containerRef.current) return;
|
if (!containerRef.current) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const graphData = {
|
console.log('🎨 GraphView - Initializing graph with data:', data);
|
||||||
nodes: data.nodes.map((node: Node) => ({
|
console.log('🎨 GraphView - Container dimensions:', {
|
||||||
...node,
|
width: containerRef.current.clientWidth,
|
||||||
id: node.id,
|
height: height
|
||||||
})),
|
});
|
||||||
edges: data.edges.map((edge: Edge) => ({
|
|
||||||
...edge,
|
|
||||||
id: edge.id,
|
|
||||||
source: edge.source,
|
|
||||||
target: edge.target,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (graphRef.current) {
|
if (graphRef.current) {
|
||||||
graphRef.current.destroy();
|
try {
|
||||||
|
graphRef.current.destroy();
|
||||||
|
} catch (e) {
|
||||||
|
console.log('🎨 GraphView - Destroy error:', e);
|
||||||
|
}
|
||||||
graphRef.current = null;
|
graphRef.current = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const processedGraphData = {
|
const processedGraphData = {
|
||||||
nodes: graphData.nodes.map((node: any) => {
|
nodes: data.nodes.map((node: Node) => {
|
||||||
const typeColors: Record<string, Record<string, string>> = {
|
const typeColors: Record<string, { fill: string; stroke: string }> = {
|
||||||
'概念': { fill: '#4ECDC4', stroke: '#45B7B0', text: '#2C3E50' },
|
'概念': { fill: '#4ECDC4', stroke: '#45B7B0' },
|
||||||
'工具': { fill: '#FFA07A', stroke: '#FF7F50', text: '#2C3E50' },
|
'工具': { fill: '#FFA07A', stroke: '#FF7F50' },
|
||||||
'应用': { fill: '#2ECC71', stroke: '#27AE60', text: '#2C3E50' },
|
'应用': { fill: '#2ECC71', stroke: '#27AE60' },
|
||||||
};
|
};
|
||||||
|
|
||||||
const colors = typeColors[node.type] || { fill: '#C6E5FF', stroke: '#5B8FF9', text: '#2C3E50' };
|
const colors = typeColors[node.type] || { fill: '#C6E5FF', stroke: '#5B8FF9' };
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...node,
|
id: node.id,
|
||||||
type: 'circle',
|
data: {
|
||||||
|
id: node.id,
|
||||||
|
label: node.label,
|
||||||
|
type: node.type,
|
||||||
|
properties: node.properties,
|
||||||
|
style: node.style,
|
||||||
|
},
|
||||||
style: {
|
style: {
|
||||||
size: 40,
|
|
||||||
fill: colors.fill,
|
fill: colors.fill,
|
||||||
stroke: colors.stroke,
|
stroke: colors.stroke,
|
||||||
lineWidth: 2,
|
lineWidth: 2,
|
||||||
cursor: 'pointer',
|
labelText: node.label,
|
||||||
},
|
labelFill: '#2C3E50',
|
||||||
data: {
|
labelFontSize: 14,
|
||||||
...node,
|
labelFontWeight: 'bold',
|
||||||
originalType: node.type,
|
labelPlacement: 'bottom',
|
||||||
colors,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
edges: graphData.edges.map((edge: any) => ({
|
edges: data.edges.map((edge: Edge) => ({
|
||||||
...edge,
|
id: edge.id,
|
||||||
type: 'cubic-vertical',
|
source: edge.source,
|
||||||
|
target: edge.target,
|
||||||
|
label: edge.label,
|
||||||
|
data: {
|
||||||
|
source: edge.source,
|
||||||
|
target: edge.target,
|
||||||
|
label: edge.label,
|
||||||
|
type: edge.type,
|
||||||
|
properties: edge.properties,
|
||||||
|
},
|
||||||
style: {
|
style: {
|
||||||
stroke: '#95A5A6',
|
stroke: '#95A5A6',
|
||||||
lineWidth: 2,
|
lineWidth: 2,
|
||||||
cursor: 'pointer',
|
labelText: edge.label,
|
||||||
|
labelFill: '#666',
|
||||||
|
labelFontSize: 10,
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
const graph = new Graph({
|
console.log('🎨 GraphView - Processed graph data nodes:', processedGraphData.nodes.length);
|
||||||
|
console.log('🎨 GraphView - Processed graph data edges:', processedGraphData.edges.length);
|
||||||
|
|
||||||
|
const graphConfig: any = {
|
||||||
container: containerRef.current,
|
container: containerRef.current,
|
||||||
width: containerRef.current.clientWidth,
|
width: containerRef.current.clientWidth,
|
||||||
height: height,
|
height: height,
|
||||||
data: processedGraphData as any,
|
data: processedGraphData,
|
||||||
autoFit: 'view',
|
autoFit: 'view',
|
||||||
|
node: {
|
||||||
|
type: 'circle',
|
||||||
|
style: {
|
||||||
|
size: 50,
|
||||||
|
lineWidth: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
edge: {
|
||||||
|
type: 'line',
|
||||||
|
style: {
|
||||||
|
lineWidth: 1,
|
||||||
|
endArrow: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
layout: {
|
layout: {
|
||||||
type: layout.type,
|
type: layout.type,
|
||||||
|
nodeSize: 60,
|
||||||
preventOverlap: true,
|
preventOverlap: true,
|
||||||
nodeSize: layout.nodeSize || 30,
|
linkDistance: 150,
|
||||||
linkDistance: layout.linkDistance || 100,
|
|
||||||
},
|
},
|
||||||
behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element', 'hover-activate'],
|
behaviors: ['drag-canvas', 'zoom-canvas', 'drag-element'],
|
||||||
});
|
};
|
||||||
|
|
||||||
|
console.log('🎨 GraphView - Creating graph with config:', graphConfig);
|
||||||
|
|
||||||
|
const graph = new Graph(graphConfig);
|
||||||
|
|
||||||
graphRef.current = graph;
|
graphRef.current = graph;
|
||||||
graph.render();
|
|
||||||
|
console.log('🎨 GraphView - Starting render');
|
||||||
|
graph.render()
|
||||||
|
.then(() => {
|
||||||
|
console.log('🎨 GraphView - Graph rendered successfully');
|
||||||
|
})
|
||||||
|
.catch((renderError) => {
|
||||||
|
console.error('🎨 GraphView - Render error:', renderError);
|
||||||
|
});
|
||||||
|
|
||||||
graph.on('node:click', (evt) => {
|
graph.on('node:click', (evt) => {
|
||||||
if (evt.itemId && onNodeClick) {
|
console.log('🔗 G6 - Node clicked, evt:', evt);
|
||||||
const nodeData = data.nodes.find((n: Node) => n.id === evt.itemId);
|
const nodeId = evt.itemId;
|
||||||
|
console.log('🔗 G6 - Node ID:', nodeId);
|
||||||
|
|
||||||
|
if (nodeId && onNodeClick) {
|
||||||
|
const nodeData = data.nodes.find((n: Node) => n.id === nodeId);
|
||||||
|
console.log('🔗 G6 - Found node data:', nodeData);
|
||||||
if (nodeData) {
|
if (nodeData) {
|
||||||
|
console.log('🔗 G6 - Node properties:', nodeData.properties);
|
||||||
onNodeClick(nodeData);
|
onNodeClick(nodeData);
|
||||||
|
} else {
|
||||||
|
console.log('🔗 G6 - Node data not found for ID:', nodeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -154,6 +203,7 @@ export const GraphView = ({
|
|||||||
onNodeClick,
|
onNodeClick,
|
||||||
onEdgeClick,
|
onEdgeClick,
|
||||||
onNodeHover,
|
onNodeHover,
|
||||||
|
data,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ interface NodePanelProps {
|
|||||||
export const NodePanel = ({ node, onClose }: NodePanelProps) => {
|
export const NodePanel = ({ node, onClose }: NodePanelProps) => {
|
||||||
if (!node) return null;
|
if (!node) return null;
|
||||||
|
|
||||||
|
console.log('📋 NodePanel - Rendering node:', node);
|
||||||
|
console.log('📋 NodePanel - Node properties:', node.properties);
|
||||||
|
console.log('📋 NodePanel - Has properties:', !!node.properties);
|
||||||
|
console.log('📋 NodePanel - Properties keys:', node.properties ? Object.keys(node.properties) : []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-80 bg-white border-l border-gray-200 shadow-lg overflow-y-auto">
|
<div className="w-80 bg-white border-l border-gray-200 shadow-lg overflow-y-auto">
|
||||||
<div className="p-4 border-b border-gray-200 flex justify-between items-center">
|
<div className="p-4 border-b border-gray-200 flex justify-between items-center">
|
||||||
@@ -46,9 +51,30 @@ export const NodePanel = ({ node, onClose }: NodePanelProps) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{node.properties && Object.keys(node.properties).length > 0 && (
|
{!node.properties && (
|
||||||
<div>
|
<div>
|
||||||
<div className="text-xs text-gray-500 uppercase tracking-wide mb-2">属性</div>
|
<div className="text-xs text-gray-500 uppercase tracking-wide mb-2">属性</div>
|
||||||
|
<div className="text-sm text-gray-400 italic">
|
||||||
|
暂无属性数据
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-gray-300 italic mt-1">
|
||||||
|
Debug: Node ID: {node.id}, Has properties?: {node.properties ? 'Yes' : 'No'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{node.properties && Object.keys(node.properties).length === 0 && (
|
||||||
|
<div>
|
||||||
|
<div className="text-xs text-gray-500 uppercase tracking-wide mb-2">属性</div>
|
||||||
|
<div className="text-sm text-gray-400 italic">
|
||||||
|
属性对象存在但为空
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{node.properties && Object.keys(node.properties).length > 0 && (
|
||||||
|
<div>
|
||||||
|
<div className="text-xs text-gray-500 uppercase tracking-wide mb-2">属性 ({Object.keys(node.properties).length})</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{Object.entries(node.properties).map(([key, value]) => (
|
{Object.entries(node.properties).map(([key, value]) => (
|
||||||
<div key={key} className="flex justify-between items-start">
|
<div key={key} className="flex justify-between items-start">
|
||||||
|
|||||||
@@ -49,8 +49,13 @@ export const KnowledgeGraph = () => {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
const data = await graphApi.getData();
|
const data = await graphApi.getData();
|
||||||
|
|
||||||
|
console.log('🎯 KnowledgeGraph - Received data:', data);
|
||||||
|
console.log('🎯 KnowledgeGraph - Node count:', data.nodes.length);
|
||||||
|
console.log('🎯 KnowledgeGraph - First node properties:', data.nodes[0]?.properties);
|
||||||
|
|
||||||
// 添加一些示例数据,如果 API 返回空数据
|
// 添加一些示例数据,如果 API 返回空数据
|
||||||
if (data.nodes.length === 0) {
|
if (data.nodes.length === 0) {
|
||||||
|
console.log('⚠️ API returned empty data, using sample data');
|
||||||
const sampleData: GraphData = {
|
const sampleData: GraphData = {
|
||||||
nodes: [
|
nodes: [
|
||||||
{ id: '1', label: '人工智能', type: '概念', properties: { importance: 1 } },
|
{ id: '1', label: '人工智能', type: '概念', properties: { importance: 1 } },
|
||||||
@@ -74,10 +79,11 @@ export const KnowledgeGraph = () => {
|
|||||||
};
|
};
|
||||||
setGraphData(sampleData);
|
setGraphData(sampleData);
|
||||||
} else {
|
} else {
|
||||||
|
console.log('✅ Setting graph data from API');
|
||||||
setGraphData(data);
|
setGraphData(data);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load graph data:', error);
|
console.error('❌ Failed to load graph data:', error);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ export const graphApi = {
|
|||||||
async getData(): Promise<GraphData> {
|
async getData(): Promise<GraphData> {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get<GraphData>('/graph');
|
const response = await apiClient.get<GraphData>('/graph');
|
||||||
|
console.log('🔍 API Response - Graph Data:', JSON.stringify(response.data, null, 2));
|
||||||
|
console.log('🔍 API Response - First Node:', response.data.nodes[0]);
|
||||||
|
console.log('🔍 API Response - First Node Properties:', response.data.nodes[0]?.properties);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching graph data:', error);
|
console.error('Error fetching graph data:', error);
|
||||||
|
|||||||
Reference in New Issue
Block a user