'use client' import React, { useMemo } from 'react' import { Node, Handle, Position } from '@xyflow/react' import { Badge } from '@/components/ui/badge' import { FileCode, FileX } from 'lucide-react' import { AdaptiveGraph } from '@/components/features/adaptive-graph' // 节点数据类型 interface GraphNodeData { id: string path: string fileName: string fileTypeId: string fileTypeName: string summary: string | null dependencyCount: number isDeleted: boolean } // 组件 Props interface FileDependencyGraphProps { nodes: GraphNodeData[] edges: Array<{ source: string; target: string; label?: string }> onNodeClick?: (node: GraphNodeData) => void } // 自定义节点组件 const CustomNode = ({ data }: { data: GraphNodeData & { isHighlighted?: boolean; isDimmed?: boolean } }) => { const bgColor = data.isDeleted ? 'bg-red-50 dark:bg-red-950/20' : data.isHighlighted ? 'bg-blue-50 dark:bg-blue-950/50' : data.isDimmed ? 'bg-gray-50 dark:bg-gray-900/30' : 'bg-white dark:bg-gray-800' const borderColor = data.isDeleted ? 'border-red-300 dark:border-red-700' : data.isHighlighted ? 'border-blue-500 dark:border-blue-400' : 'border-gray-200 dark:border-gray-700' const opacity = data.isDimmed ? 'opacity-40' : 'opacity-100' return (
{/* Handle 组件用于连接边,没有Handle看不见连边,!opacity-0用来隐藏卡片上用来连接的小点 */}
{data.isDeleted ? ( ) : ( )}
{data.fileName}
{data.path}
{data.fileTypeName} {data.dependencyCount > 0 && ( {data.dependencyCount} 依赖 )} {data.isDeleted && ( 已删除 )}
{/* Handle 组件用于连接边,没有Handle看不见连边,!opacity-0用来隐藏卡片上用来连接的小点 */}
) } // 节点类型定义 const nodeTypes = { custom: CustomNode, } export function FileDependencyGraph({ nodes: rawNodes, edges: rawEdges, onNodeClick }: FileDependencyGraphProps) { // 构建依赖关系映射 const dependencyMap = useMemo(() => { const map = new Map>() const reverseDependencyMap = new Map>() rawEdges.forEach((edge) => { // 正向依赖:source 依赖 target if (!map.has(edge.source)) { map.set(edge.source, new Set()) } map.get(edge.source)!.add(edge.target) // 反向依赖:target 被 source 依赖 if (!reverseDependencyMap.has(edge.target)) { reverseDependencyMap.set(edge.target, new Set()) } reverseDependencyMap.get(edge.target)!.add(edge.source) }) return { dependencies: map, reverseDependencies: reverseDependencyMap } }, [rawEdges]) // 过滤函数:根据搜索查询返回过滤后的节点和高亮节点 const handleFilter = useMemo( () => (nodes: GraphNodeData[], query: string) => { if (!query.trim()) { return { filteredNodeIds: new Set(nodes.map((n) => n.id)), highlightedNodeIds: new Set(), } } const lowerQuery = query.toLowerCase() const matchedNodes = nodes.filter( (node) => node.fileName.toLowerCase().includes(lowerQuery) || node.path.toLowerCase().includes(lowerQuery) || node.summary?.toLowerCase().includes(lowerQuery) ) // 包含匹配的节点及其依赖和被依赖的节点 const resultSet = new Set() matchedNodes.forEach((node) => { resultSet.add(node.id) // 添加该节点依赖的节点 const deps = dependencyMap.dependencies.get(node.id) if (deps) { deps.forEach((depId) => resultSet.add(depId)) } // 添加依赖该节点的节点 const reverseDeps = dependencyMap.reverseDependencies.get(node.id) if (reverseDeps) { reverseDeps.forEach((depId) => resultSet.add(depId)) } }) return { filteredNodeIds: resultSet, highlightedNodeIds: new Set(matchedNodes.map((n) => n.id)), } }, [dependencyMap] ) // 节点转换函数 const transformNode = ( node: GraphNodeData, options: { isHighlighted: boolean; isDimmed: boolean } ): Node => ({ id: node.id, type: 'custom', data: { ...node, isHighlighted: options.isHighlighted, isDimmed: options.isDimmed, }, position: { x: 0, y: 0 }, // 将由布局算法设置 }) // MiniMap 节点颜色函数 const getNodeColor = (node: Node) => { const data = node.data as unknown as GraphNodeData & { isHighlighted?: boolean; isDeleted?: boolean } if (data.isDeleted) return '#fca5a5' if (data.isHighlighted) return '#60a5fa' return '#cbd5e1' } // 统计信息渲染 const renderStats = (filteredCount: number, totalCount: number, matchedCount: number) => (
显示 {filteredCount} / {totalCount} 个文件 {matchedCount > 0 && ` (${matchedCount} 个匹配)`}
) return ( ) }