Fixed the Front-End Progress Bar Display Logic

This commit is contained in:
马一丁
2025-11-18 13:53:15 +08:00
parent eb036655a2
commit 85d75d6f74

View File

@@ -1212,6 +1212,13 @@
forum: 'stopped', // 前端启动后再标记为 running
report: 'stopped' // Report Engine
};
// 为每个Engine存储进度条状态
let engineProgress = {
insight: null,
media: null,
query: null,
report: null
};
let customTemplate = ''; // 存储用户上传的自定义模板内容
let configValues = {};
let configDirty = false;
@@ -1228,6 +1235,122 @@
let activeConsoleLayer = currentApp;
const logRenderers = {};
// 页面可见性状态管理
let isPageVisible = !document.hidden;
let allTimers = {
updateTime: null,
checkStatus: null,
refreshConsole: null,
refreshForum: null,
reportLockCheck: null,
connectionProbe: null,
updateEngineProgress: null // 新增更新所有Engine进度的定时器
};
// 页面可见性变化处理
function handleVisibilityChange() {
isPageVisible = !document.hidden;
if (isPageVisible) {
console.log('页面可见,恢复定时器');
startAllTimers();
} else {
console.log('页面隐藏,暂停定时器以节省资源');
pauseAllTimers();
}
}
// 启动所有定时器
function startAllTimers() {
// 清理旧定时器
stopAllTimers();
// 时间更新定时器 - 只在页面可见时更新
if (isPageVisible) {
allTimers.updateTime = setInterval(updateTime, 1000);
}
// 状态检查定时器 - 从5秒增加到10秒
allTimers.checkStatus = setInterval(checkStatus, 10000);
// 控制台刷新定时器 - 从2秒增加到3秒只在有运行中应用时执行
allTimers.refreshConsole = setInterval(() => {
if (appStatus[currentApp] === 'running' || appStatus[currentApp] === 'starting') {
refreshConsoleOutput();
}
}, 3000);
// 论坛刷新定时器 - 从3秒增加到5秒
allTimers.refreshForum = setInterval(() => {
if (currentApp === 'forum' || appStatus.forum === 'running') {
refreshForumMessages();
}
}, 5000);
// 报告锁定检查定时器 - 从10秒增加到15秒
allTimers.reportLockCheck = setInterval(checkReportLockStatus, 15000);
// 更新所有Engine进度的定时器 - 每5秒更新一次
allTimers.updateEngineProgress = setInterval(updateAllEngineProgress, 5000);
}
// 暂停所有定时器
function pauseAllTimers() {
// 只保留关键的连接检查定时器,其他全部暂停
Object.keys(allTimers).forEach(key => {
if (key !== 'connectionProbe' && allTimers[key]) {
clearInterval(allTimers[key]);
allTimers[key] = null;
}
});
}
// 停止所有定时器
function stopAllTimers() {
Object.keys(allTimers).forEach(key => {
if (allTimers[key]) {
clearInterval(allTimers[key]);
allTimers[key] = null;
}
});
}
// 页面卸载时清理资源
function cleanupOnUnload() {
console.log('页面卸载,清理所有资源');
// 停止所有定时器
stopAllTimers();
// 清理所有日志渲染器
Object.values(logRenderers).forEach(renderer => {
if (renderer && typeof renderer.dispose === 'function') {
renderer.dispose();
}
});
// 卸载所有iframe
Object.keys(preloadedIframes).forEach(app => {
unloadIframe(app);
});
// 关闭Socket连接
if (socket) {
socket.close();
}
// 关闭SSE连接
safeCloseReportStream();
// 清理全局变量
Object.keys(consoleLayers).forEach(key => {
delete consoleLayers[key];
});
Object.keys(logRenderers).forEach(key => {
delete logRenderers[key];
});
}
// 轻量日志虚拟渲染器:可视窗口渲染 + 节流 + 包级别截断,降低内存占用
class LogVirtualList {
constructor(container) {
@@ -1238,8 +1361,9 @@
this.pool = [];
this.lineHeight = 18;
this.maxVisible = 120;
this.maxLines = 2000; // 硬性保留的最大行数,超出时裁剪老旧数据
this.trimTarget = 1500; // 裁剪后保留的目标行数,避免频繁裁剪
this.maxLines = 500; // 减少到500行降低75%内存占用
this.trimTarget = 300; // 裁剪后保留300行
this.maxPoolSize = 200; // 限制DOM节点池大小
this.rafId = null;
this.autoScrollEnabled = true;
this.resumeDelay = 3000; // 手动滚动后重新自动滚动的延迟降低到3秒
@@ -1249,19 +1373,57 @@
this.needsScroll = false; // 标记是否需要滚动
this.lastScrollTime = 0; // 上次滚动时间,用于节流
this.scrollThrottle = 100; // 滚动节流时间(毫秒)
this.scrollHandler = null; // 存储滚动处理器引用
this.attachScroll();
}
attachScroll() {
if (!this.scrollElement) return;
if (this.scrollHandler) return; // 防止重复绑定
let scrollTimer = null;
this.scrollElement.addEventListener('scroll', () => {
this.scrollHandler = () => {
// 防抖处理,避免频繁触发
if (scrollTimer) clearTimeout(scrollTimer);
scrollTimer = setTimeout(() => {
this.handleUserScroll();
}, 100);
}, { passive: true });
}, 150); // 增加防抖时间到150ms
};
this.scrollElement.addEventListener('scroll', this.scrollHandler, { passive: true });
}
// 添加清理方法
dispose() {
// 清理定时器
if (this.rafId) {
cancelAnimationFrame(this.rafId);
this.rafId = null;
}
this.clearResumeTimer();
// 移除事件监听器
if (this.scrollElement && this.scrollHandler) {
this.scrollElement.removeEventListener('scroll', this.scrollHandler);
this.scrollHandler = null;
}
// 清空数据结构
this.lines = [];
this.pending = [];
// 清空并释放DOM节点池
this.pool.forEach(node => {
if (node && node.parentNode) {
node.parentNode.removeChild(node);
}
});
this.pool = [];
// 清空容器
if (this.container) {
this.container.innerHTML = '';
}
}
handleUserScroll() {
@@ -1352,8 +1514,8 @@
}
this.pending.push({ text, className });
// 降低批处理阈值到 50更快响应
if (this.pending.length > 50) {
// 增加批处理阈值到 100减少渲染频率
if (this.pending.length > 100) {
this.flush();
}
this.maybeTrim();
@@ -1409,6 +1571,8 @@
if (!total) {
if (this.container.innerHTML !== '') {
this.container.innerHTML = '';
// 清空时也清理pool
this.pool = [];
}
return;
}
@@ -1438,19 +1602,29 @@
const needed = Math.max(0, end - start);
// 限制DOM节点池大小防止内存泄漏
if (this.pool.length > this.maxPoolSize) {
const excess = this.pool.length - this.maxPoolSize;
// 移除多余的节点
this.pool.splice(this.maxPoolSize, excess).forEach(node => {
if (node && node.parentNode) {
node.parentNode.removeChild(node);
}
});
}
// 复用现有的 DOM 节点池
while (this.pool.length < needed) {
while (this.pool.length < needed && this.pool.length < this.maxPoolSize) {
const node = document.createElement('div');
node.className = 'console-line';
this.pool.push(node);
}
// 不要完全清空容器,而是更新现有节点
const existingChildren = Array.from(this.container.children);
// 使用DocumentFragment来减少DOM重绘
const fragment = document.createDocumentFragment();
// 更新或创建前置占位符
let beforeSpacer = existingChildren.find(el => el.dataset.spacer === 'before');
let beforeSpacer = this.container.querySelector('[data-spacer="before"]');
if (!beforeSpacer) {
beforeSpacer = document.createElement('div');
beforeSpacer.dataset.spacer = 'before';
@@ -1458,7 +1632,7 @@
beforeSpacer.style.height = `${beforeHeight}px`;
// 更新或创建后置占位符
let afterSpacer = existingChildren.find(el => el.dataset.spacer === 'after');
let afterSpacer = this.container.querySelector('[data-spacer="after"]');
if (!afterSpacer) {
afterSpacer = document.createElement('div');
afterSpacer.dataset.spacer = 'after';
@@ -1468,7 +1642,8 @@
// 只更新可见区域的节点
for (let idx = start; idx < end; idx++) {
const line = this.lines[idx];
const node = this.pool[idx - start];
const poolIdx = idx - start;
const node = this.pool[poolIdx];
if (!node) continue;
// 只在内容或类名变化时才更新节点
@@ -1603,40 +1778,40 @@
initializeEventListeners();
ensureSystemReadyOnLoad();
loadConsoleOutput(currentApp);
updateTime();
setInterval(updateTime, 1000);
checkStatus();
setInterval(checkStatus, 5000);
startConnectionProbe();
// 使用新的定时器管理系统
updateTime(); // 立即更新一次
checkStatus(); // 立即检查一次
checkReportLockStatus(); // 立即检查一次
// 启动所有定时器
startAllTimers();
// 立即更新一次所有Engine的进度,恢复刷新前的状态
updateAllEngineProgress();
// 监听页面可见性变化
document.addEventListener('visibilitychange', handleVisibilityChange);
// 监听页面卸载事件
window.addEventListener('beforeunload', cleanupOnUnload);
window.addEventListener('unload', cleanupOnUnload);
// 初始化密码切换功能(事件委托,只需调用一次)
attachConfigPasswordToggles();
// 初始化Report Engine锁定状态检查
checkReportLockStatus();
reportLockCheckInterval = setInterval(checkReportLockStatus, 10000); // 每10秒检查一次
// 优化控制台刷新频率:从 1 秒改为 2 秒,减少不必要的 API 调用
setInterval(() => {
if (appStatus[currentApp] === 'running' || appStatus[currentApp] === 'starting') {
refreshConsoleOutput();
}
}, 2000);
// 优化论坛对话刷新频率:从 2 秒改为 3 秒
setInterval(() => {
if (currentApp === 'forum' || appStatus.forum === 'running') {
refreshForumMessages();
}
}, 3000);
// 初始化论坛相关功能
initializeForum();
// 延迟预加载iframe以确保应用启动完成
// 延迟预加载iframe以确保应用启动完成,并且只在页面可见时加载
setTimeout(() => {
preloadIframes();
}, 3000);
if (isPageVisible) {
preloadIframes();
}
}, 5000); // 延迟时间从3秒增加到5秒减少初始加载压力
// 连接探测定时器(保持运行)
startConnectionProbe();
});
// Socket.IO连接
@@ -2228,45 +2403,48 @@
if (reportPreview) {
reportPreview.innerHTML = '<div class="report-loading">等待新的搜索结果生成报告...</div>';
}
// 清除任务进度显示
const taskProgressArea = document.getElementById('taskProgressArea');
if (taskProgressArea) {
taskProgressArea.innerHTML = '';
}
// 重置自动生成相关标志
autoGenerateTriggered = false;
reportTaskId = null;
// 停止可能正在进行的轮询
if (reportPollingInterval) {
clearInterval(reportPollingInterval);
reportPollingInterval = null;
}
// 确保所有iframe已初始化
if (!iframesInitialized) {
preloadIframes();
}
// 向所有运行中的应用发送搜索请求通过刷新iframe传递参数
let totalRunning = 0;
const ports = { insight: 8501, media: 8502, query: 8503 };
Object.keys(appStatus).forEach(app => {
if (appStatus[app] === 'running' && preloadedIframes[app]) {
if (appStatus[app] === 'running' && ports[app]) {
totalRunning++;
// 构建搜索URL
const searchUrl = `http://${window.location.hostname}:${ports[app]}?query=${encodeURIComponent(query)}&auto_search=true`;
console.log(`${app} 发送搜索请求: ${searchUrl}`);
// 直接更新主iframe的src来传递搜索参数
preloadedIframes[app].src = searchUrl;
// 懒加载iframe如果还没有加载
let iframe = preloadedIframes[app];
if (!iframe) {
iframe = lazyLoadIframe(app);
}
if (iframe) {
// 构建搜索URL
const searchUrl = `http://${window.location.hostname}:${ports[app]}?query=${encodeURIComponent(query)}&auto_search=true`;
console.log(`${app} 发送搜索请求: ${searchUrl}`);
// 直接更新iframe的src来传递搜索参数
iframe.src = searchUrl;
}
}
});
if (totalRunning === 0) {
button.disabled = false;
button.innerHTML = '搜索';
@@ -2291,6 +2469,12 @@
}
}
// 隐藏当前Engine的进度条
const engines = ['insight', 'media', 'query'];
if (engines.includes(currentApp)) {
hideEngineProgress(currentApp);
}
// 更新按钮状态
document.querySelectorAll('.app-button').forEach(btn => {
btn.classList.remove('active');
@@ -2304,49 +2488,56 @@
if (app === 'forum') {
// 切换到论坛模式
document.getElementById('embeddedHeader').textContent = 'Forum Engine - 多智能体交流';
// 显示论坛容器,隐藏其他内容
document.getElementById('forumContainer').classList.add('active');
document.getElementById('reportContainer').classList.remove('active');
// 追加提示并加载forum日志
appendConsoleTextLine('forum', '[系统] 切换到论坛模式');
loadForumLog();
} else if (app === 'report') {
// 切换到报告模式
document.getElementById('embeddedHeader').textContent = 'Report Agent - 最终报告生成';
// 显示报告容器,隐藏其他内容
document.getElementById('reportContainer').classList.add('active');
document.getElementById('forumContainer').classList.remove('active');
// 追加提示并加载report日志
appendConsoleTextLine('report', '[系统] 切换到报告生成模式');
loadReportLog();
// 只在报告界面未初始化时才重新加载
const reportContent = document.getElementById('reportContent');
if (!reportContent || reportContent.children.length === 0) {
loadReportInterface();
}
// 切换到report页面时检查是否可以自动生成报告
setTimeout(() => {
checkReportLockStatus();
}, 500);
} else {
// 切换到普通Engine模式
document.getElementById('embeddedHeader').textContent = agentTitles[app] || appNames[app];
// 隐藏论坛和报告容器
document.getElementById('forumContainer').classList.remove('active');
document.getElementById('reportContainer').classList.remove('active');
// 追加提示并加载新的控制台输出
appendConsoleTextLine(app, '[系统] 切换到 ' + appNames[app]);
loadConsoleOutput(app);
// 显示该Engine的进度条(如果有)
if (engines.includes(app)) {
showEngineProgress(app);
// 立即更新一次进度,确保显示最新状态
updateEngineProgress(app);
}
}
// 更新嵌入页面
@@ -2542,123 +2733,194 @@
// 预加载的iframe存储
let preloadedIframes = {};
let iframesInitialized = false;
// 预加载所有iframe只执行一次
function preloadIframes() {
if (iframesInitialized) return;
const ports = { insight: 8501, media: 8502, query: 8503 };
const content = document.getElementById('embeddedContent');
for (const [app, port] of Object.entries(ports)) {
const iframe = document.createElement('iframe');
iframe.src = `http://${window.location.hostname}:${port}`;
iframe.style.width = '100%';
iframe.style.height = '100%';
iframe.style.border = 'none';
iframe.style.position = 'absolute';
iframe.style.top = '0';
iframe.style.left = '0';
iframe.style.display = 'none';
iframe.id = `iframe-${app}`;
// 直接添加到content区域
content.appendChild(iframe);
preloadedIframes[app] = iframe;
console.log(`预加载 ${app} 应用完成`);
let currentVisibleIframe = null; // 跟踪当前可见的iframe
// 懒加载iframe - 只在真正需要时才创建
function lazyLoadIframe(app) {
// 如果iframe已存在直接返回
if (preloadedIframes[app]) {
return preloadedIframes[app];
}
const ports = { insight: 8501, media: 8502, query: 8503 };
if (!ports[app]) {
console.warn(`未知的应用: ${app}`);
return null;
}
const content = document.getElementById('embeddedContent');
const iframe = document.createElement('iframe');
iframe.src = `http://${window.location.hostname}:${ports[app]}`;
iframe.style.width = '100%';
iframe.style.height = '100%';
iframe.style.border = 'none';
iframe.style.position = 'absolute';
iframe.style.top = '0';
iframe.style.left = '0';
iframe.style.display = 'none';
iframe.id = `iframe-${app}`;
// 添加加载完成事件
iframe.addEventListener('load', () => {
console.log(`${app} iframe 加载完成`);
});
content.appendChild(iframe);
preloadedIframes[app] = iframe;
console.log(`懒加载 ${app} iframe`);
return iframe;
}
// 卸载不需要的iframe以释放内存
function unloadIframe(app) {
if (!preloadedIframes[app]) return;
const iframe = preloadedIframes[app];
// 先隐藏iframe
iframe.style.display = 'none';
// 清空iframe内容以释放内存
if (iframe.contentWindow) {
try {
// 尝试清空iframe的DOM
iframe.src = 'about:blank';
} catch (e) {
console.warn(`无法清空 ${app} iframe:`, e);
}
}
// 从DOM中移除iframe
if (iframe.parentNode) {
iframe.parentNode.removeChild(iframe);
}
// 从缓存中删除
delete preloadedIframes[app];
console.log(`卸载 ${app} iframe释放内存`);
}
// 卸载所有非当前应用的iframe
function unloadInactiveIframes(currentApp) {
const apps = ['insight', 'media', 'query'];
apps.forEach(app => {
if (app !== currentApp && preloadedIframes[app]) {
// 延迟卸载,给一些缓冲时间
setTimeout(() => {
if (currentApp !== app) { // 再次确认没有切换回来
unloadIframe(app);
}
}, 30000); // 30秒后卸载不活跃的iframe
}
});
}
// 预加载所有iframe只执行一次- 已废弃,改用懒加载
function preloadIframes() {
// 不再预加载所有iframe改用懒加载机制
console.log('使用懒加载机制不再预加载所有iframe');
iframesInitialized = true;
console.log('所有iframe预加载完成准备进行无缝切换');
}
// 更新嵌入页面
function updateEmbeddedPage(app) {
const header = document.getElementById('embeddedHeader');
const content = document.getElementById('embeddedContent');
// 如果是Forum Engine直接显示论坛界面
if (app === 'forum') {
header.textContent = 'Forum Engine - 多智能体交流';
// 隐藏所有iframe
if (typeof preloadedIframes !== 'undefined') {
Object.values(preloadedIframes).forEach(iframe => {
iframe.style.display = 'none';
});
}
Object.values(preloadedIframes).forEach(iframe => {
iframe.style.display = 'none';
});
// 移除占位符
const placeholder = content.querySelector('.status-placeholder');
if (placeholder) {
placeholder.remove();
}
// 显示论坛容器,隐藏报告容器
document.getElementById('forumContainer').classList.add('active');
document.getElementById('reportContainer').classList.remove('active');
// 卸载不活跃的iframe
unloadInactiveIframes(null);
currentVisibleIframe = null;
return;
}
// 如果是Report Engine显示报告界面
if (app === 'report') {
header.textContent = 'Report Agent - 最终报告生成';
// 隐藏所有iframe
if (typeof preloadedIframes !== 'undefined') {
Object.values(preloadedIframes).forEach(iframe => {
iframe.style.display = 'none';
});
}
Object.values(preloadedIframes).forEach(iframe => {
iframe.style.display = 'none';
});
// 移除占位符
const placeholder = content.querySelector('.status-placeholder');
if (placeholder) {
placeholder.remove();
}
// 显示报告容器,隐藏论坛容器
document.getElementById('reportContainer').classList.add('active');
document.getElementById('forumContainer').classList.remove('active');
// 卸载不活跃的iframe
unloadInactiveIframes(null);
currentVisibleIframe = null;
return;
}
// 隐藏论坛和报告容器
document.getElementById('forumContainer').classList.remove('active');
document.getElementById('reportContainer').classList.remove('active');
header.textContent = agentTitles[app] || appNames[app] || app;
// 如果应用正在运行显示对应的iframe
// 如果应用正在运行显示对应的iframe(使用懒加载)
if (appStatus[app] === 'running') {
// 确保iframe已初始化
if (!iframesInitialized) {
preloadIframes();
// 懒加载当前应用的iframe
const iframe = lazyLoadIframe(app);
if (!iframe) {
console.error(`无法加载 ${app} iframe`);
return;
}
// 隐藏所有iframe
Object.values(preloadedIframes).forEach(iframe => {
iframe.style.display = 'none';
Object.values(preloadedIframes).forEach(otherIframe => {
otherIframe.style.display = 'none';
});
// 移除占位符
const placeholder = content.querySelector('.status-placeholder');
if (placeholder) {
placeholder.remove();
}
// 显示当前应用的iframe
if (preloadedIframes[app]) {
preloadedIframes[app].style.display = 'block';
console.log(`切换到 ${app} 应用 - 无刷新切换`);
}
iframe.style.display = 'block';
currentVisibleIframe = app;
console.log(`切换到 ${app} 应用 - 懒加载模式`);
// 卸载不活跃的iframe30秒后
unloadInactiveIframes(app);
} else {
// 隐藏所有iframe
Object.values(preloadedIframes).forEach(iframe => {
iframe.style.display = 'none';
});
// 显示状态信息
let placeholder = content.querySelector('.status-placeholder');
if (!placeholder) {
@@ -2667,11 +2929,13 @@
placeholder.style.cssText = 'display: flex; align-items: center; justify-content: center; height: 100%; color: #666; flex-direction: column; position: absolute; top: 0; left: 0; width: 100%;';
content.appendChild(placeholder);
}
placeholder.innerHTML = `
<div style="margin-bottom: 10px;">${appNames[app]} 未运行</div>
<div style="font-size: 12px;">状态: ${appStatus[app]}</div>
`;
currentVisibleIframe = null;
}
}
@@ -2840,7 +3104,164 @@
// Forum Engine 相关函数
let forumLogLineCount = 0;
// 更新所有Engine的进度条
function updateAllEngineProgress() {
// 通过现有的status API获取所有Engine的状态
fetch('/api/status')
.then(response => response.json())
.then(data => {
// 为每个需要进度显示的Engine更新状态
const engines = ['insight', 'media', 'query'];
engines.forEach(engine => {
if (data[engine]) {
const info = data[engine];
const status = info.status === 'running' ? 'running' : 'stopped';
// 如果Engine正在运行,显示进度条
if (status === 'running') {
// 尝试从API获取详细进度,如果失败则显示基本运行状态
updateEngineProgress(engine);
} else {
// Engine未运行,清除进度信息
engineProgress[engine] = null;
const progressContainer = document.getElementById(`progress-${engine}`);
if (progressContainer && progressContainer.parentNode) {
progressContainer.parentNode.removeChild(progressContainer);
}
}
}
});
})
.catch(error => {
console.log('获取Engine状态失败:', error);
});
}
// 更新单个Engine的进度
function updateEngineProgress(engine) {
// 先尝试从专用进度API获取
fetch(`/api/${engine}/progress`)
.then(response => {
if (!response.ok) {
throw new Error('Progress API not available');
}
return response.json();
})
.then(data => {
if (data.success && data.progress) {
// 存储进度信息
engineProgress[engine] = {
status: data.progress.status || 'running',
progress: data.progress.progress || 0,
message: data.progress.message || '正在处理...',
updated_at: new Date().toISOString()
};
// 如果当前正在查看该Engine,更新显示
if (currentApp === engine) {
displayEngineProgress(engine);
}
}
})
.catch(error => {
// 如果专用API不可用,使用基本的运行状态
if (appStatus[engine] === 'running') {
// 使用基本的进度信息
if (!engineProgress[engine]) {
engineProgress[engine] = {
status: 'running',
progress: 50, // 默认显示50%表示运行中
message: '正在分析中...',
updated_at: new Date().toISOString()
};
}
// 如果当前正在查看该Engine,更新显示
if (currentApp === engine) {
displayEngineProgress(engine);
}
}
});
}
// 在嵌入页面区域显示Engine进度
function displayEngineProgress(engine) {
const progress = engineProgress[engine];
if (!progress) return;
// 查找或创建进度显示容器
let progressContainer = document.getElementById(`progress-${engine}`);
if (!progressContainer) {
// 在嵌入内容区域的顶部创建进度条容器
const embeddedContent = document.getElementById('embeddedContent');
if (!embeddedContent) return;
progressContainer = document.createElement('div');
progressContainer.id = `progress-${engine}`;
progressContainer.className = 'task-progress-container';
progressContainer.style.position = 'absolute';
progressContainer.style.top = '10px';
progressContainer.style.left = '10px';
progressContainer.style.right = '10px';
progressContainer.style.zIndex = '100';
progressContainer.style.backgroundColor = '#f5f5f0';
embeddedContent.insertBefore(progressContainer, embeddedContent.firstChild);
}
// 更新进度条内容
const loadingIndicator = progress.status !== 'completed' && progress.status !== 'error'
? '<span class="report-loading-spinner"></span>'
: '';
progressContainer.innerHTML = `
<div class="task-progress-header">
<div class="task-progress-title">
${loadingIndicator}${appNames[engine] || engine} - ${progress.message}
</div>
<div class="task-progress-bar">
<div class="task-progress-fill" style="width: ${Math.min(Math.max(progress.progress || 0, 0), 100)}%"></div>
<div class="task-progress-text">${progress.progress || 0}%</div>
</div>
</div>
`;
// 如果任务已完成,5秒后淡出进度条
if (progress.status === 'completed') {
setTimeout(() => {
if (progressContainer && progressContainer.parentNode) {
progressContainer.style.transition = 'opacity 1s';
progressContainer.style.opacity = '0';
setTimeout(() => {
if (progressContainer && progressContainer.parentNode) {
progressContainer.parentNode.removeChild(progressContainer);
}
}, 1000);
}
}, 5000);
}
}
// 隐藏指定Engine的进度条(切换时使用)
function hideEngineProgress(engine) {
const progressContainer = document.getElementById(`progress-${engine}`);
if (progressContainer) {
progressContainer.style.display = 'none';
}
}
// 显示指定Engine的进度条(切换时使用)
function showEngineProgress(engine) {
const progressContainer = document.getElementById(`progress-${engine}`);
if (progressContainer) {
progressContainer.style.display = 'block';
} else if (engineProgress[engine]) {
// 如果有缓存的进度信息但容器不存在,重新创建
displayEngineProgress(engine);
}
}
// Report Engine 相关函数
let reportLogLineCount = 0;
let reportLockCheckInterval = null;