deepresearch/所有文件/research-tree.js
2025-07-02 15:35:36 +08:00

149 lines
5.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// app/static/js/research-tree.js
function renderTree(session, outline) {
const container = document.getElementById('treeContainer');
if (!container) {
console.error('Tree container not found!');
return;
}
container.innerHTML = '';
// 根节点 - 始终显示
const rootNode = document.createElement('div');
rootNode.className = 'root-node';
rootNode.innerHTML = `
<h2>${session.question || '研究问题'}</h2>
<p>状态:${getStatusText(session.status)} |
开始时间:${new Date(session.created_at).toLocaleString()}</p>
${session.error_message ? `<p style="color: #ff6b6b;">错误: ${session.error_message}</p>` : ''}
`;
container.appendChild(rootNode);
// 如果出错,显示错误信息
if (session.status === 'error') {
const errorNode = createTreeNode('研究出现错误', 'error');
container.appendChild(wrapInTreeNode(errorNode));
return;
}
// 研究准备节点
const prepNode = createTreeNode('研究准备', session.refined_questions ? 'completed' : session.status);
prepNode.onclick = () => showDetail('preparation', session);
if (session.refined_questions) {
const content = document.createElement('div');
content.className = 'node-content expanded';
content.innerHTML = `
<div class="phase-card">
<h4>🎯 问题细化</h4>
<ul>
${session.refined_questions.map(q => `<li>• ${q}</li>`).join('')}
</ul>
</div>
`;
prepNode.querySelector('.node-card').appendChild(content);
}
container.appendChild(wrapInTreeNode(prepNode));
// 大纲节点
if (outline) {
const outlineNode = createTreeNode('研究大纲', 'completed');
const outlineContent = document.createElement('div');
outlineContent.className = 'node-content expanded';
outlineContent.innerHTML = `
<div class="phase-card">
<h4>📋 主要研究问题</h4>
<ul>
${outline.research_questions.map(q => `<li>• ${q}</li>`).join('')}
</ul>
</div>
`;
outlineNode.querySelector('.node-card').appendChild(outlineContent);
const outlineWrapper = wrapInTreeNode(outlineNode);
container.appendChild(outlineWrapper);
// 子主题节点
outline.sub_topics.forEach((subtopic, idx) => {
const subtopicNode = createSubtopicNode(subtopic, idx + 1);
outlineWrapper.appendChild(wrapInTreeNode(subtopicNode, true));
});
} else {
// 显示大纲创建中或失败
const outlineStatus = session.status === 'outlining' ? 'processing' :
session.status === 'error' ? 'error' : 'pending';
const outlineNode = createTreeNode('研究大纲', outlineStatus);
container.appendChild(wrapInTreeNode(outlineNode));
}
// 最终报告节点
const reportNode = createTreeNode('研究报告生成', session.final_report ? 'completed' : 'pending');
container.appendChild(wrapInTreeNode(reportNode));
}
function createTreeNode(title, status) {
const node = document.createElement('div');
const statusInfo = getStatusInfo(status);
node.innerHTML = `
<div class="node-card ${statusInfo.className}">
<div class="node-header">
<div class="node-title-wrapper">
<span class="node-title">${title}</span>
</div>
<div class="node-status">
<span class="status-icon ${statusInfo.className}">${statusInfo.icon}</span>
</div>
</div>
</div>
`;
return node;
}
function createSubtopicNode(subtopic, index) {
const node = document.createElement('div');
const statusInfo = getStatusInfo(subtopic.status);
node.innerHTML = `
<div class="node-card ${statusInfo.className}" onclick="showDetail('subtopic', ${JSON.stringify(subtopic).replace(/"/g, '&quot;')})">
<div class="node-header">
<div class="node-title-wrapper">
<span class="node-title">子主题${index}${subtopic.topic}</span>
</div>
<div class="node-status">
<span class="priority-badge ${subtopic.priority}">
${subtopic.priority === 'high' ? '高' : subtopic.priority === 'medium' ? '中' : '低'}优先级
</span>
<span class="status-icon ${statusInfo.className}">${statusInfo.icon}</span>
</div>
</div>
</div>
`;
return node;
}
function wrapInTreeNode(node, isSubtopic = false) {
const wrapper = document.createElement('div');
wrapper.className = 'tree-node' + (isSubtopic ? ' subtopic-node' : '');
wrapper.appendChild(node);
return wrapper;
}
function getStatusInfo(status) {
const statusMap = {
'pending': { icon: '○', className: 'pending' },
'analyzing': { icon: '●', className: 'processing' },
'outlining': { icon: '●', className: 'processing' },
'researching': { icon: '●', className: 'processing' },
'writing': { icon: '●', className: 'processing' },
'reviewing': { icon: '●', className: 'processing' },
'completed': { icon: '✓', className: 'completed' },
'error': { icon: '✗', className: 'error' },
'cancelled': { icon: '⊘', className: 'cancelled' }
};
return statusMap[status] || statusMap['pending'];
}