import React, { useState, useEffect } from 'react'; import { Brain, Loader2, Sparkles, AlertCircle, CheckCircle, Settings, RefreshCw, Eye, X } from 'lucide-react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { ChineseButton } from './ChineseButton'; import { ChineseCard, ChineseCardContent, ChineseCardHeader, ChineseCardTitle } from './ChineseCard'; import { cn } from '../../lib/utils'; import { requestAIInterpretation, saveAIInterpretation, getAIInterpretation, AIInterpretationResult, AIInterpretationRequest, convertAnalysisToMarkdown } from '../../services/aiInterpretationService'; import { getAIConfig, validateAIConfig, getPromptTemplate } from '../../config/aiConfig'; import { toast } from 'sonner'; interface AIInterpretationButtonProps { analysisData?: any; // 分析数据对象(可选) analysisMarkdown?: string; // 直接传递的MD内容(可选) analysisType: 'bazi' | 'ziwei' | 'yijing'; analysisId?: string; // 用于缓存解读结果 className?: string; variant?: 'default' | 'outline' | 'ghost'; size?: 'sm' | 'md' | 'lg'; showConfigButton?: boolean; // 是否显示配置按钮 onConfigClick?: () => void; // 配置按钮点击回调 onAIInterpretationClick?: () => void; // AI解读按钮点击回调(可选,用于自定义行为) } const AIInterpretationButton: React.FC = ({ analysisData, analysisMarkdown, analysisType, analysisId, className, variant = 'default', size = 'md', showConfigButton = true, onConfigClick, onAIInterpretationClick }) => { const [isLoading, setIsLoading] = useState(false); const [interpretation, setInterpretation] = useState(null); const [showResult, setShowResult] = useState(false); const [isConfigValid, setIsConfigValid] = useState(false); const [requestStartTime, setRequestStartTime] = useState(null); const [streamingContent, setStreamingContent] = useState(''); // 流式内容 // 检查AI配置是否有效 useEffect(() => { const config = getAIConfig(); setIsConfigValid(validateAIConfig(config)); }, []); // 生成唯一的分析ID,包含分析数据的时间戳 const generateAnalysisId = () => { if (analysisId) { return analysisId; } // 尝试从分析数据中提取时间戳 let timestamp = ''; if (analysisData) { // 检查多种可能的时间戳字段 const timeFields = [ analysisData.created_at, analysisData.timestamp, analysisData.analysis_time, analysisData.basic_info?.created_at, analysisData.basic_info?.timestamp, analysisData.basic_info?.analysis_time ]; for (const field of timeFields) { if (field) { timestamp = new Date(field).getTime().toString(); break; } } // 如果没有找到时间戳,使用数据的哈希值作为标识 if (!timestamp) { const dataString = JSON.stringify(analysisData); // 使用简单的哈希算法替代btoa,避免Unicode字符问题 let hash = 0; for (let i = 0; i < dataString.length; i++) { const char = dataString.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // 转换为32位整数 } timestamp = Math.abs(hash).toString(36).slice(0, 16); // 使用36进制表示 } } return `${analysisType}-${timestamp || Date.now()}`; }; const uniqueAnalysisId = generateAnalysisId(); // 加载已保存的解读结果 useEffect(() => { const loadSavedInterpretation = async () => { if (uniqueAnalysisId) { const savedInterpretation = await getAIInterpretation(uniqueAnalysisId); if (savedInterpretation) { setInterpretation(savedInterpretation); } } }; loadSavedInterpretation(); }, [uniqueAnalysisId]); // 处理AI解读请求 const handleAIInterpretation = async () => { if (!isConfigValid) { toast.error('AI配置不完整,请先配置API设置'); if (onConfigClick) { onConfigClick(); } return; } if (!analysisData && !analysisMarkdown) { toast.error('没有可解读的分析数据'); return; } setIsLoading(true); setRequestStartTime(Date.now()); // 获取用户配置的AI设置 const currentConfig = getAIConfig(); try { const request: AIInterpretationRequest = { analysisType, analysisContent: analysisMarkdown || analysisData, // 优先使用MD字符串 onStreamUpdate: currentConfig.stream ? (content: string) => { setStreamingContent(content); setShowResult(true); // 开始流式输出时就显示结果区域 } : undefined }; const result = await requestAIInterpretation(request); if (result.success) { setInterpretation(result); setShowResult(true); setStreamingContent(''); // 清空流式内容,使用最终结果 // 保存解读结果 if (uniqueAnalysisId) { try { await saveAIInterpretation(uniqueAnalysisId, result, analysisType); } catch (saveError) { // 保存失败不影响用户体验,静默处理 } } toast.success('AI解读完成'); } else { toast.error(`AI解读失败: ${result.error}`); setStreamingContent(''); // 清空流式内容 } } catch (error: any) { toast.error(`解读过程出错: ${error.message || '未知错误'}`); setStreamingContent(''); // 清空流式内容 } finally { setIsLoading(false); setRequestStartTime(null); } }; // 重新解读 const handleReinterpret = () => { setInterpretation(null); setShowResult(false); handleAIInterpretation(); }; // 获取分析类型显示名称 const getAnalysisTypeName = (type: string) => { const names = { 'bazi': '八字', 'ziwei': '紫微斗数', 'yijing': '易经' }; return names[type as keyof typeof names] || '命理'; }; return (
{/* AI解读按钮区域 */}
{ if (onAIInterpretationClick) { onAIInterpretationClick(); } if (interpretation) { setShowResult(!showResult); } else if (!onAIInterpretationClick) { handleAIInterpretation(); } }} disabled={isLoading || (!isConfigValid && !interpretation)} className={cn( 'min-h-[40px] min-w-[100px] px-3 sm:px-6 text-xs sm:text-sm flex-shrink-0 whitespace-nowrap', !isConfigValid && !interpretation && 'opacity-50 cursor-not-allowed' )} > {isLoading ? ( ) : ( )} {isLoading ? 'AI解读中...' : interpretation ? (showResult ? '隐藏解读' : 'AI解读') : 'AI解读' } {/* 重新解读按钮 */} {interpretation && ( 重新解读 )} {/* 配置按钮 */} {showConfigButton && onConfigClick && ( 配置 )}
{/* 配置提示 */} {!isConfigValid && !interpretation && (

需要配置AI设置

请先配置API Key、API地址等信息才能使用AI解读功能

)} {/* AI解读结果显示 */} {(interpretation || streamingContent) && showResult && ( {isLoading ? ( ) : ( )} AI智能解读 - {getAnalysisTypeName(analysisType)} {isLoading && streamingContent && ( 正在生成中... )} {interpretation && (
解读时间: {new Date(interpretation.timestamp).toLocaleString('zh-CN')} {interpretation.model && 模型: {interpretation.model}} {interpretation.tokensUsed && 消耗Token: {interpretation.tokensUsed}}
)}
{interpretation && !interpretation.success ? (

解读失败

{interpretation.error}

) : (
(
), th: ({node, ...props}) => (
), td: ({node, ...props}) => ( ), // 自定义标题样式 h1: ({node, ...props}) => (

), h2: ({node, ...props}) => (

), h3: ({node, ...props}) => (

), // 自定义列表样式 ul: ({node, ...props}) => (
    ), ol: ({node, ...props}) => (
      ), // 自定义段落样式 p: ({node, ...props}) => (

      ), // 自定义强调样式 strong: ({node, ...props}) => ( ), em: ({node, ...props}) => ( ), // 自定义代码块样式 code: ({node, ...props}: any) => { const isInline = !props.className?.includes('language-'); return isInline ? ( ) : ( ); }, // 自定义引用样式 blockquote: ({node, ...props}) => (

      ) }} > {streamingContent || interpretation?.content || ''} {isLoading && streamingContent && ( )} )} )} ); }; export default AIInterpretationButton;