import React, { useState, useCallback } from 'react'; import { Copy, Check, User, Bot, Clock, ChevronDown, ChevronUp, ExternalLink, Grid3X3, List } from 'lucide-react'; import { EnhancedMarkdownRenderer } from './EnhancedMarkdownRenderer'; import { ChatMaterialCard } from './ChatMaterialCard'; import { GroundingSource } from '../types/ragGrounding'; /** * 聊天消息接口 */ interface ChatMessage { id: string; type: 'user' | 'assistant'; content: string; timestamp: Date; status?: 'sending' | 'sent' | 'error'; metadata?: { responseTime?: number; modelUsed?: string; sources?: Array<{ title: string; uri?: string; content?: any; }>; grounding_metadata?: any; }; } /** * 增强聊天消息组件属性接口 */ interface EnhancedChatMessageV2Props { /** 消息数据 */ message: ChatMessage; /** 是否显示来源信息 */ showSources?: boolean; /** 是否启用素材卡片 */ enableMaterialCards?: boolean; /** 是否启用角标引用 */ enableReferences?: boolean; /** 自定义样式类名 */ className?: string; } /** * 增强聊天消息组件 V2 * 支持角标引用、优化素材展示和Markdown渲染 */ export const EnhancedChatMessageV2: React.FC = ({ message, showSources = true, enableMaterialCards = true, enableReferences = true, className = '' }) => { const [copied, setCopied] = useState(false); const [expandedSources, setExpandedSources] = useState(false); const [materialViewMode, setMaterialViewMode] = useState<'grid' | 'list'>('grid'); const [selectedReference, setSelectedReference] = useState(null); const isUser = message.type === 'user'; const isAssistant = message.type === 'assistant'; const groundingMetadata = message.metadata?.grounding_metadata; const sources = message.metadata?.sources || []; // 调试信息 console.log('🔍 EnhancedChatMessageV2 Debug:', { messageId: message.id, messageType: message.type, contentLength: message.content.length, hasMetadata: !!message.metadata, hasGroundingMetadata: !!groundingMetadata, groundingSupportsCount: groundingMetadata?.grounding_supports?.length || 0, sourcesCount: sources.length, enableReferences, groundingMetadata }); // 复制消息内容 const handleCopy = useCallback(async () => { try { await navigator.clipboard.writeText(message.content); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch (error) { console.error('复制失败:', error); } }, [message.content]); // 处理引用点击 const handleReferenceClick = useCallback((sources: GroundingSource[], index: number) => { setSelectedReference(selectedReference === index ? null : index); console.log('引用点击:', { index, sources }); }, [selectedReference]); // 处理素材卡片点击 const handleMaterialClick = useCallback((source: GroundingSource) => { console.log('素材点击:', source); // TODO: 实现素材详情查看逻辑 }, []); // 格式化时间 const formatTime = (date: Date) => { return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }); }; return (
{/* 头像 */}
{isUser ? : }
{/* 消息内容 */}
{/* 消息气泡 */}
{/* 消息内容渲染 */}
{isAssistant && enableReferences ? ( ) : (
{message.content}
)}
{/* 消息状态 */} {message.status === 'sending' && (
发送中...
)}
{/* 消息元信息 */}
{formatTime(message.timestamp)}
{isAssistant && message.metadata?.responseTime && ( • 响应时间: {message.metadata.responseTime}ms )} {isAssistant && message.metadata?.modelUsed && ( • {message.metadata.modelUsed} )} {/* 复制按钮 */}
{/* 素材来源展示 */} {isAssistant && showSources && sources.length > 0 && enableMaterialCards && (

相关素材 ({sources.length})

{expandedSources && (
)}
{expandedSources && (
{sources.map((source, index) => ( ))}
)}
)}
); }; export default EnhancedChatMessageV2;