"use client"; import React, { useState, useEffect } from 'react'; import { Button } from '@/components/ui/button'; import { ErrorMessage } from '@/components/ui/error-message'; // 声明全局window属性 declare global { interface Window { sseRetryCount?: number; } } // 定义类型 interface ResearchFormProps { className?: string; onSubmit?: (data: any) => void; } // 定义模态框组件的Props接口 interface PapersListModalProps { directionIndex: number; directionTitle: string; papers: any[]; onClose: () => void; } // 论文列表模态框组件 - 只显示论文,不显示研究报告 function PapersListModal({ directionIndex, directionTitle, papers, onClose }: PapersListModalProps) { if (!papers || papers.length === 0) return null; return (
{/* 标题和关闭按钮 */}

研究方向 {directionIndex + 1}: {directionTitle}

{/* 论文列表部分 */} {papers && papers.length > 0 && (

论文列表 ({papers.length})

{papers.map((paper: any, index: number) => (
{/* 论文标题 */}
{paper.link || paper.url ? ( {paper.title} ) : (
{paper.title}
)}
{/* 作者信息 */} {paper.authors && paper.authors.length > 0 && (
作者:

{paper.authors.join(', ')}

)} {/* 年份 */}
发表年份:

{paper.year || (paper.published && paper.published.substring(0, 4)) || "未知"}

{/* 摘要 */}
摘要:
{paper.abstract || paper.summary || "此论文暂无摘要"}
{/* 查看原文按钮 */} {(paper.link || paper.url) && ( 查看原文 )}
))}
)}
); } // 定义研究报告模态框Props接口 interface ReportModalProps { directionIndex: number; directionTitle: string; report: any; onClose: () => void; } // 研究报告模态框组件 - 专门显示研究报告 function ReportModal({ directionIndex, directionTitle, report, onClose }: ReportModalProps) { if (!report) { return (
{/* 标题和关闭按钮 */}

研究报告: {directionTitle}

无法加载报告

方向 {directionIndex + 1} 的报告数据不可用或连接中断。

可能原因: 连接中断、报告生成失败或服务器错误。

); } // 状态保存解析后的报告内容 const [parsedReport, setParsedReport] = useState(""); // 增强错误处理 useEffect(() => { const extractReportContent = () => { console.log(`处理方向${directionIndex + 1}的报告数据:`, report); try { if (!report) { return `无法加载报告内容。`; } let content = ""; // 处理多种可能的数据格式 if (typeof report === 'string') { // 直接是字符串的情况 content = report; } else if (typeof report === 'object') { // 1. 有直接内容字段的情况 if (report.content) { content = report.content; } // 2. 有英文或翻译内容字段的情况 else if (report.english_content || report.translated_content) { content = report.translated_content || report.english_content; } // 3. 有report子对象的情况 else if (report.report) { if (typeof report.report === 'string') { // report字段是字符串 content = report.report; } else if (typeof report.report === 'object' && report.report !== null) { // report字段是对象,尝试提取其内容 content = report.report.english_content || report.report.translated_content || report.report.content || JSON.stringify(report.report); } } // 4. 没有找到任何内容字段,尝试寻找最长的字符串字段 if (!content) { let maxLength = 0; let bestField = ''; for (const key in report) { if (typeof report[key] === 'string' && report[key].length > maxLength) { maxLength = report[key].length; bestField = key; content = report[key]; } } console.log(`使用最长字符串字段: ${bestField}`); } // 5. 仍然没找到,转为JSON if (!content) { content = JSON.stringify(report, null, 2); } } // 处理特殊字符 if (content) { content = content .replace(/\\n/g, '\n') .replace(/\\"/g, '"') .replace(/\\\\/g, '\\'); } return content || `无法解析报告内容。`; } catch (error) { console.error("解析报告出错:", error); return `处理报告时出现错误: ${error}`; } }; // 解析内容并更新状态 const content = extractReportContent(); setParsedReport(content); }, [report, directionIndex]); // 创建HTML格式的报告内容用于下载 const createHtmlReport = () => { // 创建HTML格式 let htmlContent = ` 研究报告: ${directionTitle}

研究报告: ${directionTitle}

`; // 转换文本内容为HTML格式 // 替换标题 let content = parsedReport .replace(/\*\*(\d+)\.\s+([^\*]+)\*\*/g, '

$1. $2

') // 替换小节标题 .replace(/\*\*(\d+\.\d+)\s+([^\*]+)\*\*/g, '

$1 $2

') // 替换段落 .split('\n\n').join('

'); htmlContent += `

${content}

`; htmlContent += `
`; return htmlContent; }; // 创建下载链接 const createDownloadLink = () => { const htmlContent = createHtmlReport(); const blob = new Blob([htmlContent], { type: 'text/html' }); const url = URL.createObjectURL(blob); return { url, filename: `研究报告_${directionTitle.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '_')}.html`, cleanup: () => URL.revokeObjectURL(url) }; }; // 下载链接状态 const [downloadLink, setDownloadLink] = useState<{ url: string; filename: string; cleanup: () => void; } | null>(null); // 创建下载链接 useEffect(() => { if (parsedReport) { const link = createDownloadLink(); setDownloadLink(link); // 清理函数 return () => { if (link && link.cleanup) link.cleanup(); }; } }, [parsedReport, directionTitle]); // 直接显示原始内容以便调试 const showRawContent = () => { console.log("显示原始内容"); alert(JSON.stringify(report, null, 2)); }; return (
{/* 标题和关闭按钮 */}

研究报告: {directionTitle}

{/* 下载和调试按钮 */}
{downloadLink && ( 下载HTML格式报告 )}
{/* 研究报告内容 */}
{/* 如果解析出的内容为空,显示待处理信息 */} {!parsedReport && (

正在处理报告内容...

)} {/* 显示解析后的内容,使用预格式化文本保留格式 */} {parsedReport && (
$1. $2') .replace(/\*\*(\d+\.\d+)\s+([^\*]+)\*\*/g, '

$1 $2

') .replace(/\n\n/g, '

') }} >
)}
); } export function ResearchForm({ className = "", onSubmit }: ResearchFormProps) { // 基本状态 const [researchTopic, setResearchTopic] = useState(''); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [processMessage, setProcessMessage] = useState(''); const [researchCompleted, setResearchCompleted] = useState(false); // 新增:标记研究是否已完成 // 研究数据 const [keywordsData, setKeywordsData] = useState(null); const [directionsData, setDirectionsData] = useState(null); const [papersData, setPapersData] = useState([]); const [reportsData, setReportsData] = useState([]); const [researchData, setResearchData] = useState(null); // 模态框状态 const [showPapersModal, setShowPapersModal] = useState(false); const [showReportModal, setShowReportModal] = useState(false); const [selectedDirectionIndex, setSelectedDirectionIndex] = useState(null); // 添加新函数,用于开始新的研究 const startNewResearch = () => { // 重置所有状态 setResearchTopic(''); setKeywordsData(null); setDirectionsData(null); setPapersData([]); setReportsData([]); setResearchData(null); setProcessMessage(''); setError(null); setResearchCompleted(false); // 重置全局重试计数 if (window.sseRetryCount) { window.sseRetryCount = 0; } }; // 表单提交 const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!researchTopic.trim()) { setError('请输入研究主题'); return; } // 如果已经有研究完成,先重置状态 if (researchCompleted) { startNewResearch(); // 延迟设置新主题并提交,确保状态重置完成 setTimeout(() => { const newTopic = researchTopic; // 保存当前主题 const newEvent = new Event('submit') as React.FormEvent; setResearchTopic(newTopic); handleSubmit(newEvent); }, 10); return; } setLoading(true); setError(null); setProcessMessage('正在连接服务器...'); // 创建EventSource连接 try { const safeResearchTopic = encodeURIComponent(researchTopic.trim()); const url = `/api/research/enhanced-process-stream?research_intent=${safeResearchTopic}`; const eventSource = new EventSource(url); // 连接事件处理 eventSource.onopen = () => { setProcessMessage('连接成功,开始处理研究内容...'); }; // 消息事件处理 eventSource.onmessage = (event) => { try { const data = JSON.parse(event.data); console.log("收到事件数据:", data); // 更新处理消息 if (data.message) { setProcessMessage(data.message); } // 处理不同类型的事件 switch(data.status) { case 'base_keywords_extracted': if (data.data && data.data.keywords) { setKeywordsData(data.data); console.log("关键词数据:", data.data); } break; case 'research_topics_generated': if (data.data && data.data.research_topics) { setDirectionsData(data.data); console.log("研究方向数据:", data.data); } break; case 'papers_ready': if (data.data) { console.log("论文数据:", data.data); setPapersData(prev => { const newData = [...prev]; if (data.data.direction_index !== undefined) { newData[data.data.direction_index] = data.data; } return newData; }); } break; case 'report_ready': if (data.data) { console.log("报告数据:", data.data); // 使用函数式更新确保报告数据正确合并 setReportsData((prevReports: any[]) => { const newReports = [...prevReports]; if (data.data.direction_index !== undefined) { newReports[data.data.direction_index] = data.data; } return newReports; }); // 如果是最后一个报告已生成,标记研究为完成状态 if (data.data.direction_index === 4 || (directionsData?.research_topics && data.data.direction_index === directionsData.research_topics.length - 1)) { console.log("最后一个报告已生成,研究完成"); // 使用setTimeout确保报告数据先被更新完成 setTimeout(() => { setResearchData({ research_intent: researchTopic, keywords: keywordsData?.keywords || [], directions: directionsData?.research_topics || [], papers_by_direction: papersData, reports: reportsData // 直接使用当前状态 }); setProcessMessage("研究已完成,您可以查看各个方向的报告和论文"); setResearchCompleted(true); eventSource.close(); setLoading(false); }, 100); } } break; case 'completed': setResearchData({ research_intent: researchTopic, keywords: keywordsData?.keywords || [], directions: directionsData?.research_topics || [], papers_by_direction: papersData, reports: reportsData }); setProcessMessage("研究已完成,您可以查看各个方向的报告和论文"); setResearchCompleted(true); eventSource.close(); setLoading(false); break; case 'error': setError(data.message || "处理过程中发生错误"); eventSource.close(); setLoading(false); break; } } catch (error) { console.error("处理事件数据时出错:", error); } }; // 错误处理 - 增强版 eventSource.onerror = (error: Event) => { console.error("与研究服务连接失败:", error); // 重连尝试计数 if (!window.sseRetryCount) { window.sseRetryCount = 0; } // 检查是否已完成大部分研究 (即已经生成了最后一个方向的报告) const isLastReportGenerated = reportsData.length > 0 && directionsData?.research_topics && reportsData.length >= directionsData.research_topics.length; // 如果已经生成了最后一个报告,直接标记为完成 if (isLastReportGenerated) { console.log("所有报告都已生成,标记研究完成"); // 构建最终研究数据 setResearchData({ research_intent: researchTopic, keywords: keywordsData?.keywords || [], directions: directionsData?.research_topics || [], papers_by_direction: papersData, reports: reportsData }); // 显示完成消息 setProcessMessage("研究已完成,您可以查看各个方向的报告和论文"); setResearchCompleted(true); setLoading(false); // 确保关闭连接 try { eventSource.close(); } catch (e) { // 忽略关闭错误 } return; } // 增加重连机制 - 但只在未生成足够报告时尝试 if (window.sseRetryCount < 3) { console.log(`尝试重新连接 (${window.sseRetryCount + 1}/3)...`); window.sseRetryCount++; // 关闭当前连接 try { eventSource.close(); } catch (e) { // 忽略关闭错误 } // 等待1秒后重连 setTimeout(() => { const newUrl = `/api/research/enhanced-process-stream?research_intent=${safeResearchTopic}`; const newEventSource = new EventSource(newUrl); // 设置新的事件监听器 // ...复制原来的所有事件监听器... setProcessMessage(`尝试重新连接 (${window.sseRetryCount}/3)...`); }, 1000); return; } // 检查处理是否已基本完成 const hasCompletedMostWork = reportsData.some(report => report && (report.report || report.content)); if (hasCompletedMostWork) { console.log("尽管连接中断,但大部分报告已生成,尝试恢复..."); // 所有研究方向都有数据了,认为是基本完成 if (reportsData.length >= directionsData?.research_topics?.length) { console.log("所有报告都已生成,手动完成流程"); // 构建最终研究数据 setResearchData({ research_intent: researchTopic, keywords: keywordsData?.keywords || [], directions: directionsData?.research_topics || [], papers_by_direction: papersData, reports: reportsData }); // 显示提示但继续 setProcessMessage("研究已完成,您可以查看各个方向的报告和论文"); setResearchCompleted(true); setLoading(false); } else { // 还有部分未完成,给出警告 setError("与研究服务的连接中断,但部分报告已生成。您可以查看现有结果,或开始新的研究。"); setResearchCompleted(true); setLoading(false); } } else { // 完全失败的情况 setError("与研究服务的连接中断,可能原因:1) 后端服务未运行 2) 网络连接问题 3) 服务器错误"); setProcessMessage("您可以开始新的研究,或联系管理员获取帮助。"); setLoading(false); } // 确保关闭连接 try { eventSource.close(); } catch (e) { // 忽略关闭错误 } }; } catch (error) { console.error("创建EventSource失败:", error); setError("无法连接到研究服务"); setLoading(false); } }; // 取消处理 const handleCancel = () => { setLoading(false); }; // 打开论文列表模态框 - 修复论文模态框打开问题 const openPapersModal = (directionIndex: number) => { console.log("打开论文模态框,方向索引:", directionIndex); setSelectedDirectionIndex(directionIndex); setShowPapersModal(true); setShowReportModal(false); }; // 关闭论文列表模态框 const closePapersModal = () => { setShowPapersModal(false); setSelectedDirectionIndex(null); }; // 打开研究报告模态框 - 增加日志以便调试 const openReportModal = (directionIndex: number) => { console.log("打开报告模态框,方向索引:", directionIndex); console.log("报告数据:", reportsData[directionIndex]); setSelectedDirectionIndex(directionIndex); setShowReportModal(true); setShowPapersModal(false); }; // 关闭研究报告模态框 const closeReportModal = () => { setShowReportModal(false); setSelectedDirectionIndex(null); }; return (
{/* 标题和说明 */}

研究助手

{/* 研究表单 */}
{/* 研究主题输入 */}