// ==UserScript== // @name 中南林一键自动教评(自适应双端) // @namespace https://github.com/jinnianliuxing/ // @version 2.0 // @description 自动填写教学评价分数,自适应电脑和移动设备,支持拖动面板和网络检测 // @author IKUN-91-张 // @match https://jxzlpt.csuft.edu.cn/* // @match *://jxzlpt-443.webvpn.csuft.edu.cn/* // @icon https://raw.githubusercontent.com/jinnianliuxing/my-script-icons/refs/heads/main/IMG_202507232298_120x120.png // @grant none // @run-at document-end // ==/UserScript== (function() { 'use strict'; // 从元数据中提取版本号 const scriptVersion = (() => { // 获取当前脚本元素 const scripts = document.getElementsByTagName('script'); for (let script of scripts) { if (script.textContent.includes('@name 中南林一键自动教评')) { const versionMatch = script.textContent.match(/\/\/\s*@version\s+(\d+\.\d+)/); if (versionMatch) { return versionMatch[1]; } } } return "2.0"; // 默认值 })(); // 设备检测函数 function isMobileDevice() { return (window.innerWidth <= 768) || /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); } // 全局状态变量 const isMobile = isMobileDevice(); let isRunning = false; let panelCreated = false; let currentEvaluationId = null; let retryTimer = null; let statusUpdateTimer = null; let dialogDetectionInterval = null; let submissionConfirmTimer = null; let isDragging = false; let isResizing = false; let dragOffsetX = 0, dragOffsetY = 0; let panelX = 30, panelY = 30; let panelWidth = isMobile ? 280 : 0, panelHeight = isMobile ? 220 : 0; let dialogOpenFailCount = 0; let networkErrorCount = 0; const MAX_DIALOG_FAIL_RETRIES = 10; const MAX_NETWORK_ERRORS = 3; const TOUCH_MOVE_THRESHOLD = 5; const MIN_PANEL_WIDTH = 200; const MIN_PANEL_HEIGHT = 160; // DOM观察器 let domObserver = null; // 缓存常用DOM元素 let panel, statusElem, counterElem, resizeHandle, panelContent; let activeOperation = null; // 主初始化函数 function mainInit() { if (window.location.href.includes('/dfpj')) { initApp(); } bindRouteEvents(); setupNetworkMonitoring(); } // 初始化应用 function initApp() { if (!window.location.href.includes('/dfpj') || panelCreated) return; // 从localStorage加载面板设置 const savedSettings = localStorage.getItem('autoEvalPanelSettings'); if (savedSettings) { const settings = JSON.parse(savedSettings); panelX = settings.x; panelY = settings.y; if (isMobile) { panelWidth = settings.width || panelWidth; panelHeight = settings.height || panelHeight; } } // 使用MutationObserver监听DOM变化 if (domObserver) domObserver.disconnect(); domObserver = new MutationObserver(mutations => { const targetElement = document.querySelector('.btn_theme'); if (targetElement && getComputedStyle(targetElement).display !== 'none') { if (!panelCreated) { createControlPanel(); } } }); domObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); // 立即检查一次 if (document.querySelector('.btn_theme')) { createControlPanel(); } // 30秒后自动停止观察 setTimeout(() => { if (domObserver && !panelCreated) { domObserver.disconnect(); } }, 30000); } // 设置网络监控 function setupNetworkMonitoring() { window.addEventListener('online', handleNetworkOnline); window.addEventListener('offline', handleNetworkOffline); if (!navigator.onLine) handleNetworkOffline(); } // 处理网络在线 function handleNetworkOnline() { if (!statusElem) return; statusElem.textContent = '状态: 网络已恢复'; statusElem.style.background = '#e8f5e9'; setTimeout(() => { if (statusElem) { statusElem.textContent = '状态: 网络已恢复 - 请手动启动评价'; } // 网络恢复后尝试重新初始化面板 if (!panelCreated && window.location.href.includes('/dfpj')) { initApp(); } }, 2000); } // 处理网络离线 function handleNetworkOffline() { if (isRunning) pauseEvaluation('网络已断开'); if (statusElem) { statusElem.textContent = '状态: 网络已断开 - 请检查网络连接'; statusElem.style.background = '#ffebee'; } } // 路由变化处理 function handleRouteChange() { const isTargetPage = /\/dfpj/.test(window.location.pathname); if (!isTargetPage && panel) { panel.remove(); panelCreated = false; } else if (isTargetPage && !panelCreated) { initApp(); } if (isRunning) tryClickFirstEvaluateBtn(); } // 绑定路由事件 function bindRouteEvents() { window.addEventListener('popstate', handleRouteChange); window.addEventListener('hashchange', handleRouteChange); const originalPushState = history.pushState; const originalReplaceState = history.replaceState; history.pushState = function() { const result = originalPushState.apply(history, arguments); handleRouteChange(); return result; }; history.replaceState = function() { const result = originalReplaceState.apply(history, arguments); handleRouteChange(); return result; }; } // 创建控制面板(自适应双端) function createControlPanel() { if (panelCreated) return; panelCreated = true; // 停止DOM观察器 if (domObserver) { domObserver.disconnect(); domObserver = null; } // 创建面板容器 panel = document.createElement('div'); panel.id = 'auto-eval-panel'; if (isMobile) { // 移动端样式 panel.style.cssText = ` position: fixed; left: ${panelX}px; top: ${panelY}px; z-index: 9999; background: #fff; border: 2px solid #4CAF50; border-radius: 10px; box-shadow: 0 4px 12px rgba(0,0,0,0.2); overflow: hidden; width: ${panelWidth}px; height: ${panelHeight}px; user-select: none; touch-action: none; `; // 创建内容容器 panelContent = document.createElement('div'); panelContent.style.cssText = ` width: 100%; height: 100%; padding: 15px; overflow: auto; box-sizing: border-box; scrollbar-width: thin; -ms-overflow-style: none; `; // 美化滚动条 const scrollbarStyle = document.createElement('style'); scrollbarStyle.textContent = ` #auto-eval-panel div::-webkit-scrollbar { width: 12px; } #auto-eval-panel div::-webkit-scrollbar-thumb { background-color: #4CAF50; border-radius: 6px; border: 3px solid #fff; } #auto-eval-panel div::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 6px; } `; document.head.appendChild(scrollbarStyle); panel.appendChild(panelContent); } else { // 电脑端样式 panel.style.cssText = ` position: fixed; left: ${panelX}px; top: ${panelY}px; z-index: 9999; background: #fff; border: 2px solid #4CAF50; padding: 15px 25px; border-radius: 10px; box-shadow: 0 4px 12px rgba(0,0,0,0.2); font-family: Arial, sans-serif; display: flex; flex-direction: column; gap: 10px; min-width: 280px; cursor: default; user-select: none; `; panelContent = panel; } // 标题栏 - 使用动态获取的版本号 const title = document.createElement('div'); title.textContent = `教学评价助手 v${scriptVersion}${isMobile ? ' (移动版)' : ' (电脑版)'}`; title.style.cssText = ` font-weight: bold; font-size: 18px; color: #2196F3; text-align: center; margin-bottom: 10px; padding: ${isMobile ? '12px' : '5px 10px'}; background: #f5f5f5; border-radius: 6px; cursor: move; user-select: none; `; panelContent.appendChild(title); // 网络指示器 const networkIndicator = document.createElement('div'); networkIndicator.id = 'network-indicator'; networkIndicator.style.cssText = ` width: ${isMobile ? '14' : '12'}px; height: ${isMobile ? '14' : '12'}px; border-radius: 50%; background-color: ${navigator.onLine ? '#4CAF50' : '#f44336'}; display: inline-block; margin-left: 10px; vertical-align: middle; `; title.appendChild(networkIndicator); // 按钮容器 const btnContainer = document.createElement('div'); btnContainer.style.display = 'flex'; btnContainer.style.gap = isMobile ? '12px' : '10px'; btnContainer.style.justifyContent = 'center'; btnContainer.style.flexWrap = 'wrap'; // 按钮样式 const btnStyle = ` padding: ${isMobile ? '14px 12px' : '8px 15px'}; border: none; border-radius: ${isMobile ? '8px' : '6px'}; cursor: pointer; font-size: ${isMobile ? '16px' : '14px'}; font-weight: bold; transition: all 0.3s; min-width: ${isMobile ? '45%' : '120px'}; box-sizing: border-box; flex-grow: ${isMobile ? '1' : '0'}; min-height: ${isMobile ? '44px' : 'auto'}; `; const startBtn = document.createElement('button'); startBtn.textContent = isMobile ? '▶ 启动' : '▶ 启动自动评价'; startBtn.style.cssText = btnStyle + 'background-color: #4CAF50; color: white;'; const pauseBtn = document.createElement('button'); pauseBtn.textContent = isMobile ? '⏸ 暂停' : '⏸ 暂停自动评价'; pauseBtn.style.cssText = btnStyle + 'background-color: #f44336; color: white;'; // 状态显示 statusElem = document.createElement('div'); statusElem.id = 'auto-eval-status'; statusElem.textContent = navigator.onLine ? '状态: 等待启动' : '状态: 网络已断开'; statusElem.style.cssText = ` margin-top: 8px; padding: ${isMobile ? '10px' : '8px'}; background: ${navigator.onLine ? '#f9f9f9' : '#ffebee'}; border-radius: ${isMobile ? '6px' : '4px'}; text-align: center; font-size: ${isMobile ? '14px' : '13px'}; border: 1px solid #eee; min-height: ${isMobile ? '24px' : '20px'}; `; // 计数器 counterElem = document.createElement('div'); counterElem.id = 'auto-eval-counter'; counterElem.textContent = '已评价: 0 | 未评价: 0'; counterElem.style.cssText = ` margin-top: 8px; padding: ${isMobile ? '8px' : '5px'}; background: #e8f5e9; border-radius: ${isMobile ? '6px' : '4px'}; text-align: center; font-size: ${isMobile ? '14px' : '12px'}; `; panelContent.appendChild(counterElem); // 按钮事件 const startHandler = () => { if (!isRunning && navigator.onLine) { isRunning = true; dialogOpenFailCount = 0; networkErrorCount = 0; statusElem.textContent = '状态: 运行中 - 扫描未评价课程'; statusElem.style.background = '#e8f5e9'; updateCourseCounter(); tryClickFirstEvaluateBtn(); } else if (!navigator.onLine) { statusElem.textContent = '状态: 网络已断开 - 无法启动'; statusElem.style.background = '#ffebee'; } }; const pauseHandler = () => pauseEvaluation('用户手动暂停'); if (isMobile) { startBtn.addEventListener('touchend', startHandler); pauseBtn.addEventListener('touchend', pauseHandler); } else { startBtn.addEventListener('click', startHandler); pauseBtn.addEventListener('click', pauseHandler); // 添加全局暂停快捷键 (ESC) document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { pauseEvaluation('用户手动暂停'); } }); } btnContainer.appendChild(startBtn); btnContainer.appendChild(pauseBtn); panelContent.appendChild(btnContainer); panelContent.appendChild(statusElem); document.body.appendChild(panel); // 移动端添加调整大小手柄 if (isMobile) { addResizeHandle(); } // 添加拖动事件 if (isMobile) { title.addEventListener('touchstart', startDrag, { passive: false }); } else { panel.addEventListener('mousedown', startDrag); title.addEventListener('mousedown', startDrag); // 存储面板尺寸 panelWidth = panel.offsetWidth; panelHeight = panel.offsetHeight; adjustPanelPosition(); } } // 移动端添加调整大小手柄 function addResizeHandle() { resizeHandle = document.createElement('div'); resizeHandle.style.cssText = ` position: absolute; right: 0; bottom: 0; width: 24px; height: 24px; background: #4CAF50; border-radius: 50% 0 0 0; cursor: nwse-resize; touch-action: none; z-index: 10000; `; resizeHandle.addEventListener('touchstart', startResize, { passive: false }); panel.appendChild(resizeHandle); } // 开始调整大小(移动端) function startResize(e) { if (!panel) return; const touch = e.touches[0]; if (!touch) return; isResizing = true; const startX = touch.clientX; const startY = touch.clientY; const startWidth = panel.offsetWidth; const startHeight = panel.offsetHeight; document.addEventListener('touchmove', moveResize, { passive: false }); document.addEventListener('touchend', stopResize, { passive: false }); function moveResize(e) { if (!isResizing) return; const touch = e.touches[0]; if (!touch) return; const dx = touch.clientX - startX; const dy = touch.clientY - startY; let newWidth = Math.max(MIN_PANEL_WIDTH, startWidth + dx); let newHeight = Math.max(MIN_PANEL_HEIGHT, startHeight + dy); newWidth = Math.min(window.innerWidth - panel.offsetLeft, newWidth); newHeight = Math.min(window.innerHeight - panel.offsetTop, newHeight); panel.style.width = newWidth + 'px'; panel.style.height = newHeight + 'px'; panelWidth = newWidth; panelHeight = newHeight; e.preventDefault(); } function stopResize() { isResizing = false; document.removeEventListener('touchmove', moveResize); document.removeEventListener('touchend', stopResize); savePanelSettings(); } e.preventDefault(); } // 保存面板设置 function savePanelSettings() { localStorage.setItem('autoEvalPanelSettings', JSON.stringify({ x: panelX, y: panelY, width: panelWidth, height: panelHeight })); } // 开始拖动 function startDrag(e) { // 排除按钮和输入元素 if (e.target.tagName === 'BUTTON' || e.target.tagName === 'INPUT' || isResizing) return; if (isMobile && e.touches?.[0]?.target.tagName === 'BUTTON') return; if (!isMobile && e.button !== 0) return; const clientX = isMobile ? e.touches[0].clientX : e.clientX; const clientY = isMobile ? e.touches[0].clientY : e.clientY; isDragging = false; const startX = clientX; const startY = clientY; const startLeft = panel.offsetLeft; const startTop = panel.offsetTop; const moveEvent = isMobile ? 'touchmove' : 'mousemove'; const endEvent = isMobile ? 'touchend' : 'mouseup'; document.addEventListener(moveEvent, moveDrag); document.addEventListener(endEvent, stopDrag); function moveDrag(e) { const currentX = isMobile ? e.touches[0].clientX : e.clientX; const currentY = isMobile ? e.touches[0].clientY : e.clientY; const dx = currentX - startX; const dy = currentY - startY; // 检测是否达到移动阈值 if (!isDragging && (Math.abs(dx) > TOUCH_MOVE_THRESHOLD || Math.abs(dy) > TOUCH_MOVE_THRESHOLD)) { isDragging = true; panel.style.cursor = 'grabbing'; panel.style.boxShadow = '0 8px 24px rgba(0,0,0,0.3)'; panel.style.opacity = '0.9'; } if (!isDragging) return; let newLeft = startLeft + dx; let newTop = startTop + dy; // 边界检查 newLeft = Math.max(0, Math.min(window.innerWidth - panel.offsetWidth, newLeft)); newTop = Math.max(0, Math.min(window.innerHeight - panel.offsetHeight, newTop)); panel.style.left = newLeft + 'px'; panel.style.top = newTop + 'px'; panelX = newLeft; panelY = newTop; if (isMobile) e.preventDefault(); } function stopDrag(e) { document.removeEventListener(moveEvent, moveDrag); document.removeEventListener(endEvent, stopDrag); if (isDragging) { panel.style.cursor = 'default'; panel.style.boxShadow = '0 4px 12px rgba(0,0,0,0.2)'; panel.style.opacity = '1'; savePanelSettings(); } isDragging = false; if (isMobile) e.preventDefault(); } if (isMobile) e.preventDefault(); } // 调整面板位置到视口内(电脑端) function adjustPanelPosition() { if (!panel || isMobile) return; const rect = panel.getBoundingClientRect(); let newLeft = rect.left; let newTop = rect.top; if (rect.right > window.innerWidth) { newLeft = window.innerWidth - panel.offsetWidth; } if (rect.bottom > window.innerHeight) { newTop = window.innerHeight - panel.offsetHeight; } if (newLeft < 0) newLeft = 0; if (newTop < 0) newTop = 0; if (newLeft !== rect.left || newTop !== rect.top) { panel.style.left = newLeft + 'px'; panel.style.top = newTop + 'px'; panelX = newLeft; panelY = newTop; savePanelSettings(); } } // 暂停评价(立即停止所有操作) function pauseEvaluation(reason = '未知原因') { if (!isRunning) return; isRunning = false; activeOperation = null; // 清除所有定时器和间隔 clearTimers(); if (dialogDetectionInterval) { clearInterval(dialogDetectionInterval); dialogDetectionInterval = null; } // 尝试关闭可能打开的评价对话框 closeDialog(); if (statusElem) { statusElem.textContent = `状态: 已暂停 (${reason})`; statusElem.style.background = '#ffebee'; } } // 清除定时器 function clearTimers() { if (retryTimer) clearTimeout(retryTimer); if (statusUpdateTimer) clearTimeout(statusUpdateTimer); if (submissionConfirmTimer) clearTimeout(submissionConfirmTimer); retryTimer = null; statusUpdateTimer = null; submissionConfirmTimer = null; } // 更新课程计数器 function updateCourseCounter() { if (!counterElem) return; const evaluated = document.querySelectorAll('.ypj').length; const notEvaluated = document.querySelectorAll('.wpj').length; counterElem.textContent = `已评价: ${evaluated} | 未评价: ${notEvaluated}`; if (isRunning) { statusUpdateTimer = setTimeout(updateCourseCounter, 5000); } } // 尝试点击评价按钮 function tryClickFirstEvaluateBtn() { if (!isRunning || !navigator.onLine) { if (!navigator.onLine) handleNetworkOffline(); return; } const evaluateBtns = document.querySelectorAll('.btn_theme'); const notEvaluatedBtns = Array.from(evaluateBtns).filter(btn => { const row = btn.closest('tr'); return row.querySelector('.wpj')?.textContent.includes('未评价'); }); if (notEvaluatedBtns.length === 0) { const remaining = document.querySelectorAll('.wpj').length; if (remaining > 0) { statusElem.textContent = `状态: 检测到 ${remaining} 个未评价课程 - 重新尝试`; statusElem.style.background = '#fffde7'; retryTimer = setTimeout(() => { if (isRunning) tryClickFirstEvaluateBtn(); }, 2000); } else { pauseEvaluation('所有课程已评价完毕'); statusElem.textContent = '状态: 所有课程已评价完毕'; statusElem.style.background = '#e8f5e9'; } return; } const evaluateBtn = notEvaluatedBtns[0]; const row = evaluateBtn.closest('tr'); currentEvaluationId = evaluateBtn.getAttribute('data-id') || row.getAttribute('data-id'); statusElem.textContent = '状态: 正在打开评价对话框...'; activeOperation = 'dialogDetection'; evaluateBtn.click(); startDialogDetection(); updateCourseCounter(); } // 检测对话框 function startDialogDetection() { if (dialogDetectionInterval) clearInterval(dialogDetectionInterval); dialogDetectionInterval = setInterval(() => { if (!isRunning || !navigator.onLine) { clearInterval(dialogDetectionInterval); return; } const dialog = document.getElementById('pjcz'); if (dialog && window.getComputedStyle(dialog).display !== 'none') { clearInterval(dialogDetectionInterval); dialogOpenFailCount = 0; networkErrorCount = 0; activeOperation = 'fillingForm'; setTimeout(() => { if (isRunning) fillEvaluationForm(); }, 800); } }, 500); // 超时处理 setTimeout(() => { if (dialogDetectionInterval && isRunning) { clearInterval(dialogDetectionInterval); if (!document.getElementById('pjcz') || window.getComputedStyle(document.getElementById('pjcz')).display === 'none') { handleDialogOpenFailed(); } } }, 15000); } // 处理对话框打开失败 function handleDialogOpenFailed() { dialogOpenFailCount++; networkErrorCount++; if (networkErrorCount >= MAX_NETWORK_ERRORS) { pauseEvaluation('多次网络错误'); statusElem.textContent = '状态: 多次网络错误 - 已暂停'; statusElem.style.background = '#ffebee'; return; } if (dialogOpenFailCount >= MAX_DIALOG_FAIL_RETRIES) { statusElem.textContent = '状态: 多次对话框打开失败 - 继续尝试'; statusElem.style.background = '#fffde7'; } else { statusElem.textContent = `状态: 对话框打开失败 - 第${dialogOpenFailCount}次重试`; statusElem.style.background = '#fffde7'; } retryTimer = setTimeout(() => { if (isRunning) tryClickFirstEvaluateBtn(); }, 2000); } // 填写评价表单 function fillEvaluationForm() { if (!isRunning) return; const dialog = document.getElementById('pjcz'); if (!dialog || window.getComputedStyle(dialog).display === 'none') { return handleDialogOpenFailed(); } const inputs = Array.from(dialog.querySelectorAll('input')) .filter(input => !input.readOnly && input.offsetParent && (input.type === 'text' || input.type === 'number')); if (inputs.length === 0) { statusElem.textContent = '状态: 未找到评分输入框 - 跳过'; statusElem.style.background = '#ffebee'; closeDialog(); retryTimer = setTimeout(() => { if (isRunning) tryClickFirstEvaluateBtn(); }, 2000); return; } const count = inputs.length; const skipIndex = Math.floor(Math.random() * count); inputs.forEach((input, index) => { let score = 10; let node = input.nextSibling; while (node) { if (node.nodeType === Node.TEXT_NODE && /\d+/.test(node.textContent)) { score = parseInt(node.textContent.match(/\d+/)[0], 10); break; } node = node.nextSibling; } input.value = index === skipIndex ? score - 1 : score; ['focus', 'input', 'change', 'blur'].forEach(evt => { input.dispatchEvent(new Event(evt, {bubbles: true})); }); }); const textarea = dialog.querySelector('textarea'); if (textarea) { textarea.value = '老师教学认真负责,课程内容充实,讲解清晰易懂'; ['focus', 'input', 'change', 'blur'].forEach(evt => { textarea.dispatchEvent(new Event(evt, {bubbles: true})); }); } statusElem.textContent = '状态: 填分完成 - 等待验证'; statusElem.style.background = '#fffde7'; retryTimer = setTimeout(() => { if (!isRunning) return; const errorElements = dialog.querySelectorAll('.el-form-item__error'); if (errorElements.length > 0) { statusElem.textContent = '状态: 表单验证失败 - 尝试修正'; fillEvaluationForm(); } else { statusElem.textContent = '状态: 验证通过 - 3秒后提交'; retryTimer = setTimeout(() => { if (isRunning) submitEvaluation(); }, 3000); } }, 3000); } // 提交评价 function submitEvaluation() { if (!isRunning) return; const dialog = document.getElementById('pjcz'); if (!dialog || window.getComputedStyle(dialog).display === 'none') { return handleDialogOpenFailed(); } const submitButtons = Array.from(dialog.querySelectorAll('button, span')); const submitButton = submitButtons.find(el => ['提交', '确定', '提交评价'].includes(el.textContent.trim()) ); if (submitButton) { statusElem.textContent = '状态: 提交中...'; statusElem.style.background = '#e3f2fd'; activeOperation = 'submitting'; // 电脑端滚动到按钮位置 if (!isMobile) { submitButton.scrollIntoView({behavior: 'smooth', block: 'center'}); } const delay = 1000 + Math.random() * 2000; const submitTimeout = setTimeout(() => { submitButton.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true })); startSubmissionConfirmation(); }, delay); retryTimer = submitTimeout; } else { statusElem.textContent = '状态: 提交失败 - 跳过'; statusElem.style.background = '#ffebee'; closeDialog(); retryTimer = setTimeout(() => { if (isRunning) tryClickFirstEvaluateBtn(); }, 2000); } } // 启动提交确认 function startSubmissionConfirmation() { submissionConfirmTimer = setTimeout(() => { if (isRunning) checkSubmissionStatus(); }, 5000); } // 检查提交状态 function checkSubmissionStatus() { if (!isRunning) return; activeOperation = 'confirming'; const dialog = document.getElementById('pjcz'); if (!dialog || window.getComputedStyle(dialog).display === 'none') { statusElem.textContent = '状态: 提交成功 - 等待刷新'; statusElem.style.background = '#e8f5e9'; retryTimer = setTimeout(() => { if (isRunning) tryClickFirstEvaluateBtn(); }, 3000); return; } const hasError = dialog.querySelector('.el-form-item__error') || /(错误|失败|请重新|提交失败)/.test(dialog.textContent); if (hasError) { statusElem.textContent = '状态: 提交失败 - 发现错误'; } else { statusElem.textContent = '状态: 提交未完成 - 跳过'; } statusElem.style.background = '#ffebee'; closeDialog(); retryTimer = setTimeout(() => { if (isRunning) tryClickFirstEvaluateBtn(); }, 2000); } // 关闭对话框 function closeDialog() { const dialog = document.getElementById('pjcz'); if (dialog) { const closeBtn = dialog.querySelector('.close'); if (closeBtn) closeBtn.click(); } } // 清理函数 function cleanup() { clearTimers(); if (domObserver) domObserver.disconnect(); window.removeEventListener('online', handleNetworkOnline); window.removeEventListener('offline', handleNetworkOffline); window.removeEventListener('popstate', handleRouteChange); window.removeEventListener('hashchange', handleRouteChange); if (!isMobile) { document.removeEventListener('keydown', pauseEvaluation); document.removeEventListener('mousemove', dragPanel); document.removeEventListener('mouseup', stopDrag); } } // 初始化 window.addEventListener('beforeunload', cleanup); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', mainInit); } else { mainInit(); } })();