From 7dd25066ad566a0163084c2bf3696daef21757da Mon Sep 17 00:00:00 2001 From: wonder Date: Mon, 6 Apr 2026 15:39:28 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=B8=85=E9=99=A4=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E5=86=97=E4=BD=99=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/GraphView/index.tsx | 8 +- frontend/src/components/NodeTooltip/index.tsx | 102 ------------------ frontend/src/config/constants.ts | 17 --- frontend/src/config/layout.ts | 3 - frontend/src/hooks/useGraphData.ts | 8 -- frontend/src/hooks/useGraphLayout.ts | 18 ++-- frontend/src/services/graphApi.ts | 32 +----- frontend/src/types/graph.ts | 7 +- frontend/src/utils/helpers.ts | 70 ------------ 9 files changed, 11 insertions(+), 254 deletions(-) delete mode 100644 frontend/src/components/NodeTooltip/index.tsx delete mode 100644 frontend/src/utils/helpers.ts diff --git a/frontend/src/components/GraphView/index.tsx b/frontend/src/components/GraphView/index.tsx index 5332411..8d51dcf 100644 --- a/frontend/src/components/GraphView/index.tsx +++ b/frontend/src/components/GraphView/index.tsx @@ -9,7 +9,6 @@ interface GraphViewProps { data: GraphData; layout?: GraphLayoutConfig; onNodeClick?: (node: Node | null) => void; - onEdgeClick?: (edge: Edge) => void; height?: number; } @@ -21,7 +20,6 @@ export const GraphView = ({ data, layout = FORCE_LAYOUT_CONFIG, onNodeClick, - onEdgeClick, height = GRAPH_DEFAULT_HEIGHT, }: GraphViewProps) => { const containerRef = useRef(null); @@ -31,12 +29,10 @@ export const GraphView = ({ const renderPromiseRef = useRef | null>(null); const onNodeClickRef = useRef(onNodeClick); - const onEdgeClickRef = useRef(onEdgeClick); useEffect(() => { onNodeClickRef.current = onNodeClick; - onEdgeClickRef.current = onEdgeClick; - }, [onNodeClick, onEdgeClick]); + }, [onNodeClick]); useEffect(() => { if (!containerRef.current) return; @@ -219,7 +215,7 @@ export const GraphView = ({ graphRef.current = null; } }; - }, [height, layout.type, data.id, onNodeClickRef, onEdgeClickRef]); + }, [height, layout.type, data.id, onNodeClickRef]); // 窗口大小调整响应 useEffect(() => { diff --git a/frontend/src/components/NodeTooltip/index.tsx b/frontend/src/components/NodeTooltip/index.tsx deleted file mode 100644 index 4dc1655..0000000 --- a/frontend/src/components/NodeTooltip/index.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { useState, useEffect, useMemo } from 'react'; -import type { Node } from '../../types/graph'; - -interface NodeTooltipProps { - node: Node | null; -} - -export const NodeTooltip = ({ node }: NodeTooltipProps) => { - const [isExpanded, setIsExpanded] = useState(false); - - useEffect(() => { - setIsExpanded(false); - }, [node?.id]); - - const propertiesList = useMemo(() => { - if (!node?.properties) return []; - return Object.entries(node.properties); - }, [node?.properties]); - - const hasProperties = propertiesList.length > 0; - - if (!node) return null; - - const handleExpand = () => setIsExpanded(true); - const handleCollapse = () => setIsExpanded(false); - - return ( -
- {!isExpanded ? ( -
-
-
{node.label}
-
- {node.type && ( -
- {node.type} -
- )} - {hasProperties && ( -
- {propertiesList.slice(0, 2).map(([key, value]) => ( -
- {key}: - - {typeof value === 'object' ? JSON.stringify(value) : String(value)} - -
- ))} - {propertiesList.length > 2 && ( -
还有 {propertiesList.length - 2} 项属性...
- )} -
- )} - {hasProperties && ( - - )} -
- ) : ( -
-
-
{node.label}
- -
- {node.type && ( -
- {node.type} -
- )} - {hasProperties && ( -
-
- 属性 ({propertiesList.length}) -
-
- {propertiesList.map(([key, value]) => ( -
- {key}: - - {typeof value === 'object' ? JSON.stringify(value) : String(value)} - -
- ))} -
-
- )} -
- )} -
- ); -}; - -export default NodeTooltip; \ No newline at end of file diff --git a/frontend/src/config/constants.ts b/frontend/src/config/constants.ts index d100222..6b07d04 100644 --- a/frontend/src/config/constants.ts +++ b/frontend/src/config/constants.ts @@ -3,24 +3,7 @@ export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localho export const API_TIMEOUT = 10000; // 图谱渲染配置 -export const GRAPH_CONTAINER_MIN_WIDTH = 600; export const GRAPH_DEFAULT_HEIGHT = 600; // 搜索相关常量 -export const SEARCH_DEBOUNCE_MS = 300; export const SEARCH_RESULT_MAX_HEIGHT = 256; // 16rem - -// UI 相关常量 -export const NODE_PANEL_WIDTH = 320; // 20rem -export const HOVER_PANEL_MAX_WIDTH = 320; // 20rem - -// 性能优化常量 -export const RESIZE_DEBOUNCE_MS = 200; -export const MAX_NODES_FOR_ANIMATION = 500; - -// 错误消息 -export const ERROR_MESSAGES = { - LOAD_GRAPH_FAILED: '加载图谱数据失败', - SEARCH_FAILED: '搜索节点失败', - NETWORK_ERROR: '网络连接错误', -} as const; diff --git a/frontend/src/config/layout.ts b/frontend/src/config/layout.ts index 626ac96..9a7fbc9 100644 --- a/frontend/src/config/layout.ts +++ b/frontend/src/config/layout.ts @@ -1,8 +1,5 @@ import type { GraphLayoutConfig } from '../types/graph'; -// 支持的布局类型(保留 d3-force 力导向布局) -export const SUPPORTED_LAYOUTS = ['d3-force'] as const; - // 力导向布局默认配置 export const FORCE_LAYOUT_CONFIG = { type: 'd3-force' as const, diff --git a/frontend/src/hooks/useGraphData.ts b/frontend/src/hooks/useGraphData.ts index b7bfc0f..a569397 100644 --- a/frontend/src/hooks/useGraphData.ts +++ b/frontend/src/hooks/useGraphData.ts @@ -11,7 +11,6 @@ import { resetColorAssignment } from '../config/colors'; export const useGraphData = () => { const [graphData, setGraphData] = useState({ nodes: [], edges: [] }); const [selectedNode, setSelectedNode] = useState(null); - const [hoveredNode, setHoveredNode] = useState(null); const [searchResults, setSearchResults] = useState([]); const [loading, setLoading] = useState(true); @@ -78,11 +77,6 @@ export const useGraphData = () => { console.log('[useGraphData] setSelectedNode called'); }, []); - // 悬停节点(用于图谱悬停) - const handleNodeHover = useCallback((node: Node | null) => { - setHoveredNode(node); - }, []); - // 监听 selectedNode 变化,用于调试 useEffect(() => { console.log('[useGraphData] selectedNode state changed:', selectedNode); @@ -97,13 +91,11 @@ export const useGraphData = () => { return { graphData, selectedNode, - hoveredNode, searchResults, loading, setSelectedNode, handleSearch, handleNodeSelectById, handleNodeSelect, - handleNodeHover, }; }; diff --git a/frontend/src/hooks/useGraphLayout.ts b/frontend/src/hooks/useGraphLayout.ts index 62bf09f..7a66069 100644 --- a/frontend/src/hooks/useGraphLayout.ts +++ b/frontend/src/hooks/useGraphLayout.ts @@ -1,21 +1,15 @@ -import { useState, useCallback } from 'react'; import type { GraphLayoutConfig } from '../types/graph'; -import { DEFAULT_LAYOUT } from '../config/layout'; /** * 知识图谱布局管理 Hook - * 负责布局状态的管理和切换 + * 返回固定的默认布局配置 */ export const useGraphLayout = () => { - const [layout, setLayout] = useState(DEFAULT_LAYOUT); - - // 切换布局类型(目前只支持 force 力导向布局) - const handleLayoutChange = useCallback((newLayoutType: GraphLayoutConfig['type']) => { - setLayout({ type: newLayoutType }); - }, []); - return { - layout, - handleLayoutChange, + layout: { + type: 'd3-force' as const, + collide: { radius: 60 } as const, + link: { distance: 150 } as const, + } as GraphLayoutConfig, }; }; diff --git a/frontend/src/services/graphApi.ts b/frontend/src/services/graphApi.ts index 5a1d407..5a40847 100644 --- a/frontend/src/services/graphApi.ts +++ b/frontend/src/services/graphApi.ts @@ -1,5 +1,5 @@ import axios from 'axios'; -import type { GraphData, Node, Edge } from '../types/graph'; +import type { GraphData, Node } from '../types/graph'; import { API_BASE_URL, API_TIMEOUT } from '../config/constants'; /** @@ -33,21 +33,6 @@ export const graphApi = { } }, - /** - * 搜索节点(后端搜索) - * @param query 搜索关键词 - * @returns 匹配的节点列表 - */ - async searchNodes(query: string): Promise { - try { - const response = await apiClient.get(`/search?q=${encodeURIComponent(query)}`); - return response.data; - } catch (error) { - console.error('Failed to search nodes:', error); - return []; - } - }, - /** * 根据 ID 获取节点详情 * @param id 节点 ID @@ -62,21 +47,6 @@ export const graphApi = { return null; } }, - - /** - * 获取节点的邻居节点和连接边 - * @param nodeId 节点 ID - * @returns 包含邻居节点和边的对象 - */ - async getNeighbors(nodeId: string): Promise<{ nodes: Node[]; edges: Edge[] }> { - try { - const response = await apiClient.get(`/nodes/${nodeId}/neighbors`); - return response.data; - } catch (error) { - console.error('Failed to fetch neighbors:', error); - return { nodes: [], edges: [] }; - } - }, }; export default apiClient; \ No newline at end of file diff --git a/frontend/src/types/graph.ts b/frontend/src/types/graph.ts index 6730ab9..fd68b21 100644 --- a/frontend/src/types/graph.ts +++ b/frontend/src/types/graph.ts @@ -51,13 +51,10 @@ export interface GraphData { /** * 图谱布局配置 - * 支持 force 力导向布局和 d3-force 力导向布局 + * 支持 d3-force 力导向布局 */ export interface GraphLayoutConfig { - type: 'force' | 'd3-force'; - nodeSize?: number; - linkDistance?: number; - center?: [number, number]; + type: 'd3-force'; collide?: { radius: number; strength?: number }; } diff --git a/frontend/src/utils/helpers.ts b/frontend/src/utils/helpers.ts deleted file mode 100644 index 23e2971..0000000 --- a/frontend/src/utils/helpers.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * 通用工具函数 - * 虽然当前未被使用,但保留以备未来需求 - */ - -/** - * 防抖函数 - * 延迟执行函数,如果在延迟时间内再次调用则重置计时器 - * @param func 要执行的函数 - * @param wait 延迟时间(毫秒) - * @returns 防抖后的函数 - */ -export const debounce = unknown>( - func: T, - wait: number -): ((...args: Parameters) => void) => { - let timeout: ReturnType | null = null; - - return (...args: Parameters) => { - if (timeout) { - clearTimeout(timeout); - } - timeout = setTimeout(() => func(...args), wait); - }; -}; - -/** - * 节流函数 - * 限制函数执行频率,在指定时间内只执行一次 - * @param func 要执行的函数 - * @param limit 时间间隔(毫秒) - * @returns 节流后的函数 - */ -export const throttle = unknown>( - func: T, - limit: number -): ((...args: Parameters) => void) => { - let inThrottle: boolean = false; - - return (...args: Parameters) => { - if (!inThrottle) { - func(...args); - inThrottle = true; - setTimeout(() => (inThrottle = false), limit); - } - }; -}; - -/** - * 生成 UUID - * @returns UUID 字符串 - */ -export const generateUUID = (): string => { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { - const r = (Math.random() * 16) | 0; - const v = c === 'x' ? r : (r & 0x3) | 0x8; - return v.toString(16); - }); -}; - -/** - * 根据重要性计算节点大小 - * @param importance 重要性系数(默认为 1) - * @returns 节点大小 - */ -export const calculateNodeSize = (importance: number = 1): number => { - const baseSize = 40; - const multiplier = importance * 20; - return baseSize + multiplier; -}; \ No newline at end of file