import React, { useState, useRef, useEffect, useCallback } from 'react'; import { ExternalLink, Image as ImageIcon, FileText } from 'lucide-react'; import { GroundingSource } from '../types/ragGrounding'; /** * 引用角标属性接口 */ interface ReferenceFootnoteProps { /** 角标编号 */ index: number; /** 引用来源列表 */ sources: GroundingSource[]; /** 自定义样式类名 */ className?: string; /** 是否显示详细信息 */ showDetails?: boolean; /** 点击回调 */ onClick?: (sources: GroundingSource[]) => void; } /** * 引用来源详情弹窗属性接口 */ interface ReferenceTooltipProps { sources: GroundingSource[]; isVisible: boolean; position: { top: number; left: number }; onClose: () => void; } /** * 引用来源详情弹窗组件 */ const ReferenceTooltip: React.FC = ({ sources, isVisible, position, onClose }) => { const tooltipRef = useRef(null); // 点击外部关闭弹窗 useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (tooltipRef.current && !tooltipRef.current.contains(event.target as Node)) { onClose(); } }; if (isVisible) { document.addEventListener('mousedown', handleClickOutside); } return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [isVisible, onClose]); if (!isVisible) return null; return (

引用来源

{sources.map((source, index) => (
{/* 来源类型图标 */}
{source.content?.type === 'image' ? ( ) : ( )}
{/* 来源信息 */}

{source.title || '未知来源'}

{source.content?.description && (

{source.content.description}

)} {source.uri && (
{source.uri}
)}
{/* 图片预览 */} {source.content?.type === 'image' && source.content?.image_url && (
{source.title
)}
))}
); }; /** * 引用角标组件 * 遵循 frontend-developer 规范的设计标准 */ export const ReferenceFootnote: React.FC = ({ index, sources, className = '', showDetails = true, onClick }) => { const [showTooltip, setShowTooltip] = useState(false); const [tooltipPosition, setTooltipPosition] = useState({ top: 0, left: 0 }); const footnoteRef = useRef(null); // 处理角标点击 const handleClick = useCallback((event: React.MouseEvent) => { event.preventDefault(); event.stopPropagation(); if (onClick) { onClick(sources); } if (showDetails && footnoteRef.current) { const rect = footnoteRef.current.getBoundingClientRect(); setTooltipPosition({ top: rect.top + window.scrollY - 8, left: rect.left + window.scrollX + rect.width / 2 }); setShowTooltip(!showTooltip); } }, [sources, onClick, showDetails, showTooltip]); // 处理悬停 const handleMouseEnter = useCallback(() => { if (!showTooltip && showDetails && footnoteRef.current) { const rect = footnoteRef.current.getBoundingClientRect(); setTooltipPosition({ top: rect.top + window.scrollY - 8, left: rect.left + window.scrollX + rect.width / 2 }); } }, [showTooltip, showDetails]); return ( <> {/* 引用详情弹窗 */} setShowTooltip(false)} /> ); }; export default ReferenceFootnote;