Showing
1 changed file
with
90 additions
and
38 deletions
| @@ -317,26 +317,35 @@ | @@ -317,26 +317,35 @@ | ||
| 317 | /* 控制台输出 */ | 317 | /* 控制台输出 */ |
| 318 | .console-output { | 318 | .console-output { |
| 319 | flex: 1; | 319 | flex: 1; |
| 320 | - padding: 15px; | ||
| 321 | background-color: #000000; | 320 | background-color: #000000; |
| 322 | color: #00ff00; | 321 | color: #00ff00; |
| 323 | font-family: 'Courier New', monospace; | 322 | font-family: 'Courier New', monospace; |
| 324 | font-size: 12px; | 323 | font-size: 12px; |
| 325 | - overflow-y: auto; | ||
| 326 | - overflow-x: hidden; | 324 | + overflow: hidden; /* 【图层优化】容器本身不滚动,由console-layer滚动 */ |
| 327 | white-space: pre-wrap; | 325 | white-space: pre-wrap; |
| 328 | word-break: break-all; | 326 | word-break: break-all; |
| 329 | min-height: 0; /* 允许内容缩小 */ | 327 | min-height: 0; /* 允许内容缩小 */ |
| 328 | + position: relative; /* 【图层优化】作为定位上下文,让console-layer相对于此容器定位 */ | ||
| 330 | } | 329 | } |
| 331 | 330 | ||
| 332 | .console-layer { | 331 | .console-layer { |
| 333 | - display: none; | 332 | + visibility: hidden; /* 使用visibility替代display,避免重排 */ |
| 333 | + position: absolute; /* 相对于.console-output绝对定位 */ | ||
| 334 | + top: 0; | ||
| 335 | + left: 0; | ||
| 334 | width: 100%; | 336 | width: 100%; |
| 335 | - min-height: 100%; | 337 | + height: 100%; /* 填满整个黑色框 */ |
| 338 | + padding: 15px; /* 图层内边距 */ | ||
| 339 | + overflow-y: auto; /* 允许独立滚动 */ | ||
| 340 | + overflow-x: hidden; | ||
| 341 | + pointer-events: none; /* 隐藏层不响应交互 */ | ||
| 342 | + box-sizing: border-box; /* 包含padding在width/height内 */ | ||
| 336 | } | 343 | } |
| 337 | 344 | ||
| 338 | .console-layer.active { | 345 | .console-layer.active { |
| 339 | - display: block; | 346 | + visibility: visible; /* 显示活动层 */ |
| 347 | + pointer-events: auto; /* 活动层响应交互 */ | ||
| 348 | + z-index: 1; /* 置顶显示 */ | ||
| 340 | } | 349 | } |
| 341 | 350 | ||
| 342 | .console-line { | 351 | .console-line { |
| @@ -1363,7 +1372,8 @@ | @@ -1363,7 +1372,8 @@ | ||
| 1363 | class LogVirtualList { | 1372 | class LogVirtualList { |
| 1364 | constructor(container) { | 1373 | constructor(container) { |
| 1365 | this.container = container; | 1374 | this.container = container; |
| 1366 | - this.scrollElement = document.getElementById('consoleOutput') || container; | 1375 | + // 【图层优化】scrollElement 就是 container(.console-layer),因为每个图层独立滚动 |
| 1376 | + this.scrollElement = container; | ||
| 1367 | this.lines = []; | 1377 | this.lines = []; |
| 1368 | this.pending = []; | 1378 | this.pending = []; |
| 1369 | this.pool = []; | 1379 | this.pool = []; |
| @@ -1400,6 +1410,10 @@ | @@ -1400,6 +1410,10 @@ | ||
| 1400 | this.renderTime = 0; // 渲染耗时 | 1410 | this.renderTime = 0; // 渲染耗时 |
| 1401 | this.lastRenderLineCount = 0; // 上次渲染的行数 | 1411 | this.lastRenderLineCount = 0; // 上次渲染的行数 |
| 1402 | 1412 | ||
| 1413 | + // 【图层优化】窗口激活状态管理 | ||
| 1414 | + this.isActive = false; // 当前窗口是否为活动窗口 | ||
| 1415 | + this.needsRender = false; // 非活动窗口是否有待渲染内容 | ||
| 1416 | + | ||
| 1403 | this.attachScroll(); | 1417 | this.attachScroll(); |
| 1404 | } | 1418 | } |
| 1405 | 1419 | ||
| @@ -1585,6 +1599,24 @@ | @@ -1585,6 +1599,24 @@ | ||
| 1585 | if (px > 0) this.lineHeight = px; | 1599 | if (px > 0) this.lineHeight = px; |
| 1586 | } | 1600 | } |
| 1587 | 1601 | ||
| 1602 | + /** | ||
| 1603 | + * 【图层优化】设置窗口激活状态 | ||
| 1604 | + * @param {boolean} active - 是否为活动窗口 | ||
| 1605 | + */ | ||
| 1606 | + setActive(active) { | ||
| 1607 | + this.isActive = active; | ||
| 1608 | + if (active && this.needsRender) { | ||
| 1609 | + // 窗口激活时,如果有待渲染内容,异步渲染 | ||
| 1610 | + requestIdleCallback(() => { | ||
| 1611 | + if (this.pending.length > 0) { | ||
| 1612 | + this.flush(); | ||
| 1613 | + } | ||
| 1614 | + this.scheduleRender(true); | ||
| 1615 | + this.needsRender = false; | ||
| 1616 | + }, { timeout: 50 }); | ||
| 1617 | + } | ||
| 1618 | + } | ||
| 1619 | + | ||
| 1588 | append(text, className = 'console-line') { | 1620 | append(text, className = 'console-line') { |
| 1589 | // 在添加内容前检查是否在底部,如果是则标记需要滚动 | 1621 | // 在添加内容前检查是否在底部,如果是则标记需要滚动 |
| 1590 | if (this.autoScrollEnabled && this.isNearBottom()) { | 1622 | if (this.autoScrollEnabled && this.isNearBottom()) { |
| @@ -1598,7 +1630,18 @@ | @@ -1598,7 +1630,18 @@ | ||
| 1598 | this.pendingHighWaterMark = this.pending.length; | 1630 | this.pendingHighWaterMark = this.pending.length; |
| 1599 | } | 1631 | } |
| 1600 | 1632 | ||
| 1601 | - // 【优化】智能批处理策略 | 1633 | + // 【图层优化】非活动窗口处理策略 |
| 1634 | + if (!this.isActive) { | ||
| 1635 | + // 非活动窗口:只累积数据,不触发渲染 | ||
| 1636 | + // 设置队列上限,防止内存溢出 | ||
| 1637 | + if (this.pending.length >= 1000) { | ||
| 1638 | + this.flush(); // 定期flush避免内存溢出 | ||
| 1639 | + this.needsRender = true; // 标记需要渲染 | ||
| 1640 | + } | ||
| 1641 | + return; // 跳过后续渲染逻辑 | ||
| 1642 | + } | ||
| 1643 | + | ||
| 1644 | + // 【优化】活动窗口:智能批处理策略 | ||
| 1602 | const now = Date.now(); | 1645 | const now = Date.now(); |
| 1603 | const timeSinceLastFlush = now - this.lastFlushTime; | 1646 | const timeSinceLastFlush = now - this.lastFlushTime; |
| 1604 | 1647 | ||
| @@ -1691,6 +1734,16 @@ | @@ -1691,6 +1734,16 @@ | ||
| 1691 | if (!this.container) return; | 1734 | if (!this.container) return; |
| 1692 | if (!force && this.rafId) return; | 1735 | if (!force && this.rafId) return; |
| 1693 | 1736 | ||
| 1737 | + // 【图层优化】检查窗口是否可见 | ||
| 1738 | + if (!force && !this.isActive) { | ||
| 1739 | + // 非活动窗口:只flush数据,不渲染DOM | ||
| 1740 | + if (this.pending.length > 0) { | ||
| 1741 | + this.flush(); // 保存数据到lines | ||
| 1742 | + this.needsRender = true; // 标记需要渲染 | ||
| 1743 | + } | ||
| 1744 | + return; // 跳过DOM操作 | ||
| 1745 | + } | ||
| 1746 | + | ||
| 1694 | // 取消之前的请求 | 1747 | // 取消之前的请求 |
| 1695 | if (this.rafId) { | 1748 | if (this.rafId) { |
| 1696 | cancelAnimationFrame(this.rafId); | 1749 | cancelAnimationFrame(this.rafId); |
| @@ -2698,17 +2751,16 @@ | @@ -2698,17 +2751,16 @@ | ||
| 2698 | // 更新当前应用 | 2751 | // 更新当前应用 |
| 2699 | currentApp = app; | 2752 | currentApp = app; |
| 2700 | 2753 | ||
| 2701 | - // 切换控制台层(不添加系统提示,避免频繁输出) | 2754 | + // 【图层优化】切换控制台层(纯CSS图层切换,瞬间完成) |
| 2702 | setActiveConsoleLayer(app); | 2755 | setActiveConsoleLayer(app); |
| 2703 | 2756 | ||
| 2704 | // 更新嵌入页面(右侧内容区域) | 2757 | // 更新嵌入页面(右侧内容区域) |
| 2705 | updateEmbeddedPage(app); | 2758 | updateEmbeddedPage(app); |
| 2706 | 2759 | ||
| 2707 | - // 加载对应的控制台输出(只在必要时加载) | ||
| 2708 | - if (app === 'forum') { | ||
| 2709 | - loadForumLog(); | ||
| 2710 | - } else if (app === 'report') { | ||
| 2711 | - loadReportLog(); | 2760 | + // 【图层优化】移除重复加载逻辑 |
| 2761 | + // 日志数据已通过Socket.IO/SSE实时同步,无需重新加载 | ||
| 2762 | + // 仅保留特殊页面的初始化逻辑 | ||
| 2763 | + if (app === 'report') { | ||
| 2712 | // 只在报告界面未初始化时才重新加载 | 2764 | // 只在报告界面未初始化时才重新加载 |
| 2713 | const reportContent = document.getElementById('reportContent'); | 2765 | const reportContent = document.getElementById('reportContent'); |
| 2714 | if (!reportContent || reportContent.children.length === 0) { | 2766 | if (!reportContent || reportContent.children.length === 0) { |
| @@ -2718,8 +2770,6 @@ | @@ -2718,8 +2770,6 @@ | ||
| 2718 | setTimeout(() => { | 2770 | setTimeout(() => { |
| 2719 | checkReportLockStatus(); | 2771 | checkReportLockStatus(); |
| 2720 | }, 500); | 2772 | }, 500); |
| 2721 | - } else { | ||
| 2722 | - loadConsoleOutput(app); | ||
| 2723 | } | 2773 | } |
| 2724 | } | 2774 | } |
| 2725 | 2775 | ||
| @@ -2806,16 +2856,19 @@ | @@ -2806,16 +2856,19 @@ | ||
| 2806 | layer.dataset.app = app; | 2856 | layer.dataset.app = app; |
| 2807 | if (app === currentApp) { | 2857 | if (app === currentApp) { |
| 2808 | layer.classList.add('active'); | 2858 | layer.classList.add('active'); |
| 2809 | - layer.style.display = 'block'; | ||
| 2810 | activeConsoleLayer = app; | 2859 | activeConsoleLayer = app; |
| 2811 | - } else { | ||
| 2812 | - layer.style.display = 'none'; | ||
| 2813 | } | 2860 | } |
| 2861 | + // 【图层优化】不再设置style.display,完全由CSS类控制 | ||
| 2814 | 2862 | ||
| 2815 | container.appendChild(layer); | 2863 | container.appendChild(layer); |
| 2816 | consoleLayers[app] = layer; | 2864 | consoleLayers[app] = layer; |
| 2817 | logRenderers[app] = new LogVirtualList(layer); | 2865 | logRenderers[app] = new LogVirtualList(layer); |
| 2818 | 2866 | ||
| 2867 | + // 【图层优化】标记活动窗口 | ||
| 2868 | + if (app === currentApp) { | ||
| 2869 | + logRenderers[app].isActive = true; | ||
| 2870 | + } | ||
| 2871 | + | ||
| 2819 | // 【FIX Bug #3】初始提示立即渲染,避免黑屏 | 2872 | // 【FIX Bug #3】初始提示立即渲染,避免黑屏 |
| 2820 | logRenderers[app].clear(`[系统] ${appNames[app] || app} 日志就绪`); | 2873 | logRenderers[app].clear(`[系统] ${appNames[app] || app} 日志就绪`); |
| 2821 | logRenderers[app].render(); // 立即同步渲染 | 2874 | logRenderers[app].render(); // 立即同步渲染 |
| @@ -2835,7 +2888,7 @@ | @@ -2835,7 +2888,7 @@ | ||
| 2835 | const layer = document.createElement('div'); | 2888 | const layer = document.createElement('div'); |
| 2836 | layer.className = 'console-layer'; | 2889 | layer.className = 'console-layer'; |
| 2837 | layer.dataset.app = app; | 2890 | layer.dataset.app = app; |
| 2838 | - layer.style.display = app === currentApp ? 'block' : 'none'; | 2891 | + // 【图层优化】不再设置style.display,完全由CSS类控制 |
| 2839 | if (app === currentApp) { | 2892 | if (app === currentApp) { |
| 2840 | layer.classList.add('active'); | 2893 | layer.classList.add('active'); |
| 2841 | activeConsoleLayer = app; | 2894 | activeConsoleLayer = app; |
| @@ -2844,6 +2897,12 @@ | @@ -2844,6 +2897,12 @@ | ||
| 2844 | container.appendChild(layer); | 2897 | container.appendChild(layer); |
| 2845 | consoleLayers[app] = layer; | 2898 | consoleLayers[app] = layer; |
| 2846 | logRenderers[app] = new LogVirtualList(layer); | 2899 | logRenderers[app] = new LogVirtualList(layer); |
| 2900 | + | ||
| 2901 | + // 【图层优化】标记活动窗口 | ||
| 2902 | + if (app === currentApp) { | ||
| 2903 | + logRenderers[app].isActive = true; | ||
| 2904 | + } | ||
| 2905 | + | ||
| 2847 | return layer; | 2906 | return layer; |
| 2848 | } | 2907 | } |
| 2849 | 2908 | ||
| @@ -2852,43 +2911,36 @@ | @@ -2852,43 +2911,36 @@ | ||
| 2852 | if (!container) return; | 2911 | if (!container) return; |
| 2853 | 2912 | ||
| 2854 | // 如果已经是当前激活的层,跳过 | 2913 | // 如果已经是当前激活的层,跳过 |
| 2855 | - if (activeConsoleLayer === app && consoleLayers[app] && consoleLayers[app].style.display === 'block') { | 2914 | + if (activeConsoleLayer === app && consoleLayers[app] && consoleLayers[app].classList.contains('active')) { |
| 2856 | return; | 2915 | return; |
| 2857 | } | 2916 | } |
| 2858 | 2917 | ||
| 2859 | - // 隐藏旧的激活层 | 2918 | + // 【图层优化】标记旧窗口为非活动 |
| 2860 | if (activeConsoleLayer && consoleLayers[activeConsoleLayer]) { | 2919 | if (activeConsoleLayer && consoleLayers[activeConsoleLayer]) { |
| 2861 | consoleLayers[activeConsoleLayer].classList.remove('active'); | 2920 | consoleLayers[activeConsoleLayer].classList.remove('active'); |
| 2862 | - consoleLayers[activeConsoleLayer].style.display = 'none'; | 2921 | + if (logRenderers[activeConsoleLayer]) { |
| 2922 | + logRenderers[activeConsoleLayer].setActive(false); | ||
| 2923 | + } | ||
| 2863 | } | 2924 | } |
| 2864 | 2925 | ||
| 2865 | // 获取或创建目标层 | 2926 | // 获取或创建目标层 |
| 2866 | const targetLayer = getConsoleLayer(app); | 2927 | const targetLayer = getConsoleLayer(app); |
| 2867 | if (!targetLayer) return; | 2928 | if (!targetLayer) return; |
| 2868 | 2929 | ||
| 2869 | - // 显示新的激活层 | ||
| 2870 | - targetLayer.style.display = 'block'; | 2930 | + // 【图层优化】显示新的激活层(纯CSS类切换,不修改style.display) |
| 2871 | targetLayer.classList.add('active'); | 2931 | targetLayer.classList.add('active'); |
| 2872 | activeConsoleLayer = app; | 2932 | activeConsoleLayer = app; |
| 2873 | 2933 | ||
| 2874 | - // 触发一次渲染以确保内容正确显示 | 2934 | + // 【图层优化】标记新窗口为活动,触发异步渲染 |
| 2875 | const renderer = logRenderers[app]; | 2935 | const renderer = logRenderers[app]; |
| 2876 | if (renderer) { | 2936 | if (renderer) { |
| 2877 | - // 【FIX Bug #1/#3】如果已有数据,立即同步渲染,避免黑屏 | ||
| 2878 | - if (renderer.lines.length > 0 || renderer.pending.length > 0) { | ||
| 2879 | - renderer.flush(); // 先将pending数据合并到lines | ||
| 2880 | - renderer.render(); // 立即同步渲染,不使用异步 | ||
| 2881 | - } else { | ||
| 2882 | - // 如果没有数据,显示加载提示(同步) | ||
| 2883 | - renderer.clear(`[系统] 正在加载 ${appNames[app] || app} 日志...`); | ||
| 2884 | - renderer.render(); // 立即渲染加载提示 | ||
| 2885 | - } | 2937 | + renderer.setActive(true); // 会在内部异步渲染待处理内容 |
| 2938 | + | ||
| 2886 | // 确保滚动到底部 | 2939 | // 确保滚动到底部 |
| 2887 | if (renderer.autoScrollEnabled) { | 2940 | if (renderer.autoScrollEnabled) { |
| 2888 | - // 使用setTimeout确保DOM更新后再滚动 | ||
| 2889 | - setTimeout(() => { | 2941 | + requestAnimationFrame(() => { |
| 2890 | renderer.scrollToBottom(); | 2942 | renderer.scrollToBottom(); |
| 2891 | - }, 0); | 2943 | + }); |
| 2892 | } | 2944 | } |
| 2893 | } | 2945 | } |
| 2894 | } | 2946 | } |
-
Please register or login to post a comment