// ==UserScript== // @name 智慧党校培训平台网络安全测试脚本 // @namespace http://tampermonkey.net/ // @version 1.0.0 // @description 网络安全测试脚本,用于测试智慧党校培训平台的视频播放和学习进度功能 // @author liubai // @match https://szdx.hainanu.edu.cn/videodetails/* // @icon https://szdx.hainanu.edu.cn/ai_logo.png // @grant none // ==/UserScript== (function() { 'use strict'; console.log('智慧党校培训平台测试脚本已加载'); // 脚本状态监控对象 const scriptStatus = { videoLoaded: false, autoMute: false, autoResume: false, popupHandling: false, videoSwitching: false, focusProtection: false, isSwitching: false, // 视频切换状态锁 errors: [], panelCollapsed: false }; // 等待页面加载完成 function waitForElement(selector, callback, timeout = 10000) { const startTime = Date.now(); const interval = setInterval(() => { const element = document.querySelector(selector); if (element) { clearInterval(interval); callback(element); } else if (Date.now() - startTime > timeout) { clearInterval(interval); const errorMsg = `Timeout waiting for element: ${selector}`; addErrorToMonitor(errorMsg); } }, 100); } // 模拟鼠标点击事件 function simulateClick(element) { if (!element) return; const event = new MouseEvent('click', { view: window, bubbles: true, cancelable: true }); element.dispatchEvent(event); } // 处理网络不稳定弹窗 function handleNetworkUnstablePopup() { // 检查弹窗内容是否包含网络不稳定相关文本 const networkKeywords = ['网络不稳定', '刷新页面', '连接失败', '网络错误']; const popupSelectors = ['.el-message-box', '.el-dialog', '.el-notification', '[role="dialog"]']; for (const selector of popupSelectors) { const popups = document.querySelectorAll(selector); for (const popup of popups) { if (popup.offsetParent !== null) { const popupText = popup.textContent || ''; const hasNetworkText = networkKeywords.some(keyword => popupText.includes(keyword)); if (hasNetworkText) { console.log('检测到网络不稳定弹窗,尝试关闭'); // 尝试点击弹窗内的所有可见按钮,直接关闭弹窗 const popupButtons = popup.querySelectorAll('button, input[type="button"], input[type="submit"]'); for (const btn of popupButtons) { try { if (btn.offsetParent !== null) { console.log('点击弹窗按钮关闭弹窗'); simulateClick(btn); return true; } } catch (e) { // 静默错误,继续尝试 console.debug('点击弹窗按钮失败:', e.message); } } } } } } return false; } // 关闭弹窗 function closePopups() { scriptStatus.popupHandling = false; try { // 添加功能开关,允许在配置中禁用弹窗处理 if (typeof scriptStatus.closePopupsEnabled !== 'undefined' && !scriptStatus.closePopupsEnabled) { return; } // 优化选择器,按功能分组并合并相关选择器,减少DOM查询次数 const popupSelectors = ['.el-message-box', '.el-dialog', '.el-notification', '[role="dialog"]']; // 检查是否存在弹窗 const hasActivePopup = () => { for (const selector of popupSelectors) { const elements = document.querySelectorAll(selector); for (const element of elements) { if (element.offsetParent !== null) { return true; } } } return false; }; // 如果没有活跃弹窗,直接返回 if (!hasActivePopup()) { scriptStatus.popupHandling = true; return; } let popupClosed = false; // 优先处理网络不稳定弹窗 popupClosed = handleNetworkUnstablePopup(); // 如果网络不稳定弹窗已处理,直接返回 if (popupClosed) { scriptStatus.popupHandling = true; return; } // 合并的关闭按钮选择器,优先级排序 const closeButtonGroups = [ // Element UI组件关闭按钮 (最高优先级) '.el-message-box__close, .el-dialog__close, .el-notification__closeBtn', // 确认/提交按钮 (第二优先级) '.el-button--primary, .el-button--success, .confirm, .ok, .confirm-btn', // 通用关闭按钮 (第三优先级) '[aria-label="Close"], .close, .cancel, .primary-button, .btn-primary', // 包含特定文本的类名按钮 '[class*="confirm"]:not([class*="open"]), [class*="submit"]' ]; // 尝试通过选择器查找并点击关闭按钮 for (const selectorGroup of closeButtonGroups) { const buttons = document.querySelectorAll(selectorGroup); for (const button of buttons) { try { if (button.offsetParent !== null) { simulateClick(button); console.log(`已关闭弹窗: ${selectorGroup}`); popupClosed = true; // 点击一个按钮后立即跳出循环,避免同时点击多个按钮 break; } } catch (e) { // 静默错误,继续尝试其他按钮 } } // 如果已找到并点击按钮,跳出选择器组循环 if (popupClosed) break; } // 如果通过选择器没有关闭弹窗,尝试通过文本内容查找确认按钮 if (!popupClosed) { // 优化的文本按钮查找函数,限制查找范围 const findAndClickButtonByText = (texts) => { // 首先获取活跃的弹窗容器 const activeContainers = []; for (const selector of popupSelectors) { const elements = document.querySelectorAll(selector); for (const element of elements) { if (element.offsetParent !== null) { activeContainers.push(element); } } } // 优先在活跃弹窗容器内查找按钮 if (activeContainers.length > 0) { for (const container of activeContainers) { const containerButtons = container.querySelectorAll('button, input[type="button"], input[type="submit"]'); for (const button of containerButtons) { try { if (button.offsetParent === null) continue; const buttonText = (button.textContent || button.value || '').toLowerCase(); for (const text of texts) { if (buttonText.includes(text.toLowerCase())) { simulateClick(button); console.log(`已点击确认按钮: "${buttonText}"`); return true; } } } catch (e) { // 静默错误,继续检查 } } } } // 如果在弹窗内没有找到,再查找整个文档 const allButtons = document.querySelectorAll('button, input[type="button"], input[type="submit"]'); for (const button of allButtons) { try { if (button.offsetParent === null) continue; const buttonText = (button.textContent || button.value || '').toLowerCase(); for (const text of texts) { if (buttonText.includes(text.toLowerCase())) { simulateClick(button); console.log(`已点击确认按钮: "${buttonText}"`); return true; } } } catch (e) { // 静默错误,继续检查 } } return false; }; // 针对网络不稳定弹窗的确认按钮文本 const confirmTexts = ['确定', '确认', 'OK', '继续', '刷新', '确定刷新', '知道了']; popupClosed = findAndClickButtonByText(confirmTexts); } // 如果存在无法关闭的弹窗,提示用户(只在检测到弹窗但未成功关闭时) let uncloseablePopupFound = false; if (!popupClosed && hasActivePopup()) { // 检查每个活跃弹窗是否有关闭按钮 for (const selector of popupSelectors) { const popups = document.querySelectorAll(selector); for (const popup of popups) { if (popup.offsetParent !== null) { // 检查是否有任何按钮(不只是关闭按钮) const anyButton = popup.querySelector('button, input[type="button"], input[type="submit"]'); if (!anyButton || anyButton.offsetParent === null) { uncloseablePopupFound = true; console.warn('检测到无法自动关闭的弹窗,请手动处理!'); // 避免重复创建提示元素 const existingAlert = document.getElementById('popup-handling-alert'); if (!existingAlert) { const alertDiv = document.createElement('div'); alertDiv.id = 'popup-handling-alert'; alertDiv.style.cssText = ` position: fixed; top: 20px; right: 20px; background: #ff6b6b; color: white; padding: 10px; border-radius: 5px; z-index: 9999; font-weight: bold; box-shadow: 0 2px 10px rgba(0,0,0,0.3); `; alertDiv.textContent = '⚠️ 请手动关闭弹窗!'; document.body.appendChild(alertDiv); // 使用弱引用确保DOM节点被正确移除 setTimeout(() => { const element = document.getElementById('popup-handling-alert'); if (element) { element.remove(); } }, 3000); } break; } } } if (uncloseablePopupFound) break; } } // 更新状态 scriptStatus.popupHandling = popupClosed || !uncloseablePopupFound; } catch (e) { console.error(`弹窗处理异常: ${e.message}`); // 减少错误报告,避免日志污染 if (Math.random() < 0.1) { // 只记录10%的错误 addErrorToMonitor(`弹窗处理异常: ${e.message}`); } scriptStatus.popupHandling = false; } } // 检测并恢复视频播放 function checkAndResumeVideo() { scriptStatus.autoResume = false; scriptStatus.autoMute = false; const video = document.getElementById('video'); if (!video) return; try { // 自动静音 video.muted = true; scriptStatus.autoMute = true; // 检查视频是否加载失败 if (video.networkState === video.NETWORK_ERROR || video.readyState === video.HAVE_NOTHING) { console.log('检测到视频加载失败,尝试重新加载...'); // 尝试重新加载视频 video.load(); // 延迟一段时间后再次尝试播放 setTimeout(() => { checkAndResumeVideo(); }, 2000); return; } // 如果视频暂停,尝试恢复播放 if (video.paused) { video.play(); console.log('视频已恢复播放'); scriptStatus.autoResume = true; } else { scriptStatus.autoResume = true; } } catch (e) { addErrorToMonitor(`视频恢复播放失败: ${e.message}`); scriptStatus.autoResume = false; scriptStatus.autoMute = false; // 模拟点击视频区域 try { const rect = video.getBoundingClientRect(); const clickEvent = new MouseEvent('click', { clientX: rect.left + rect.width / 2, clientY: rect.top + rect.height / 2, view: window, bubbles: true, cancelable: true }); video.dispatchEvent(clickEvent); scriptStatus.autoResume = true; } catch (clickError) { addErrorToMonitor(`模拟点击失败: ${clickError.message}`); // 添加视频加载失败重试机制 console.log('视频播放恢复失败,将在5秒后重试...'); setTimeout(() => { checkAndResumeVideo(); }, 5000); } } } // 检测视频是否播放完毕并切换到下一视频 function checkVideoCompletion() { const video = document.getElementById('video'); if (!video) return; // 监听视频结束事件 video.addEventListener('ended', () => { console.log('当前视频已播放完毕,尝试切换到下一视频'); // 检查是否正在进行视频切换 if (!scriptStatus.isSwitching) { switchToNextVideo(); } else { console.log('视频切换已在进行中,跳过ended事件触发的切换'); } }); // 定期检查视频进度是否已达100% setInterval(() => { // 检查是否正在进行视频切换 if (scriptStatus.isSwitching) { console.log('视频切换正在进行中,跳过定期进度检查'); return; } if (video.duration > 0 && video.currentTime >= video.duration - 1) { console.log('视频进度已达100%,尝试切换到下一视频'); switchToNextVideo(); } }, 5000); } // 切换到下一个未完成的视频 function switchToNextVideo() { // 添加状态锁,确保同一时间只能执行一次视频切换 if (scriptStatus.isSwitching) { console.log('视频切换已在进行中,跳过本次调用'); return false; } // 设置切换状态为true scriptStatus.isSwitching = true; scriptStatus.videoSwitching = false; try { console.log('开始尝试切换视频...'); // 使用与getCurrentVideoProgress相同的多种选择器查找视频目录 const allVideoItems = []; const selectors = ['.catalogue li', '.videoListContBlock li', '.videoNavLists li']; selectors.forEach(selector => { const items = document.querySelectorAll(selector); items.forEach(item => { // 避免重复添加 if (!allVideoItems.includes(item)) { allVideoItems.push(item); } }); }); if (allVideoItems.length === 0) { const errorMsg = '未找到视频目录项,尝试了多种选择器'; addErrorToMonitor(errorMsg); scriptStatus.videoSwitching = false; scriptStatus.isSwitching = false; // 释放锁 return false; } console.log(`找到 ${allVideoItems.length} 个视频项`); // 按视频序号升序查找所有未完成的视频,无论当前正在观看哪个视频 // 从第一个视频开始检查,找到第一个未完成的视频 for (let i = 0; i < allVideoItems.length; i++) { const item = allVideoItems[i]; if (!isVideoItemCompleted(item)) { console.log(`找到未完成视频,按序号升序切换到第 ${i + 1} 个视频: ${item.textContent.trim()}`); if (trySwitchToVideo(item)) { // 视频切换需要时间,延迟3秒释放锁(增加冷却时间) setTimeout(() => { scriptStatus.isSwitching = false; }, 3000); return true; } } } // 检查是否所有视频都已完成 const allCompleted = allVideoItems.every(item => { const isCompleted = isVideoItemCompleted(item); console.log(`视频项 ${item.textContent.trim()} - 已完成: ${isCompleted}`); return isCompleted; }); if (allCompleted) { // 如果所有视频都已完成 console.log('所有视频都已完成!'); scriptStatus.videoSwitching = true; // 所有视频完成也是一种成功状态 const allCompleteDiv = document.createElement('div'); allCompleteDiv.style.cssText = ` position: fixed; top: 20px; right: 20px; background: #4CAF50; color: white; padding: 15px; border-radius: 5px; z-index: 9999; font-weight: bold; box-shadow: 0 2px 10px rgba(0,0,0,0.3); `; allCompleteDiv.textContent = '🎉 所有视频学习已完成!'; document.body.appendChild(allCompleteDiv); setTimeout(() => { if (allCompleteDiv.parentNode) { allCompleteDiv.parentNode.removeChild(allCompleteDiv); } }, 5000); scriptStatus.isSwitching = false; // 释放锁 return true; // 返回true表示操作成功(即使没有更多视频) } // 找到了未完成的视频但无法切换 const errorMsg = '找到未完成视频但无法切换'; addErrorToMonitor(errorMsg); scriptStatus.videoSwitching = false; scriptStatus.isSwitching = false; // 释放锁 return false; } catch (e) { console.error('视频切换异常:', e); addErrorToMonitor(`视频切换失败: ${e.message}`); scriptStatus.videoSwitching = false; scriptStatus.isSwitching = false; // 释放锁 return false; } } // 检查视频项是否已完成(与getCurrentVideoProgress保持一致的逻辑) function isVideoItemCompleted(item) { try { // 检查是否已完成(通过.completed类) const isCompleted = item.classList.contains('completed'); // 获取进度百分比 const percentageElement = item.querySelector('.percentage-value') || item.querySelector('.el-progress__text') || item.querySelector('.progress-value'); // 默认进度为0 let percentage = 0; if (percentageElement) { const percentageText = percentageElement.textContent.trim(); // 提取数字部分 const match = percentageText.match(/\d+/); if (match) { percentage = parseInt(match[0], 10); } } // 使用与getCurrentVideoProgress相同的99%阈值 return isCompleted || percentage >= 99; } catch (e) { console.debug('检查视频完成状态异常:', e.message); return false; } } // 尝试切换到指定视频 function trySwitchToVideo(item) { try { // 检查是否已完成 if (!isVideoItemCompleted(item)) { // 找到未完成的视频,点击它 // 尝试多种方式找到可点击的元素 let clickableElement = null; // 优先尝试直接点击链接 clickableElement = item.querySelector('a'); // 如果没有链接,尝试点击li元素本身 if (!clickableElement) { clickableElement = item; } // 如果还是没有,尝试点击内部的其他可点击元素 if (!clickableElement || !clickableElement.offsetParent) { clickableElement = item.querySelector('div') || item.querySelector('span'); } if (clickableElement && clickableElement.offsetParent) { // 确保元素可见 console.log(`尝试切换到视频: ${item.textContent.trim()}`); simulateClick(clickableElement); scriptStatus.videoSwitching = true; // 显示切换成功提示 const switchDiv = document.createElement('div'); switchDiv.style.cssText = ` position: fixed; top: 20px; right: 20px; background: #2196F3; color: white; padding: 10px; border-radius: 5px; z-index: 9999; font-weight: bold; box-shadow: 0 2px 10px rgba(0,0,0,0.3); `; switchDiv.textContent = '✅ 正在切换到下一视频...'; document.body.appendChild(switchDiv); setTimeout(() => { if (switchDiv.parentNode) { switchDiv.parentNode.removeChild(switchDiv); } }, 3000); return true; } else { console.log('找到未完成视频但无有效可点击元素'); return false; } } return false; } catch (e) { console.debug('尝试切换视频异常:', e.message); return false; } } // 防止页面失去焦点时暂停 function preventPauseOnBlur() { scriptStatus.focusProtection = false; try { // 覆盖原生的visibilitychange事件 document.addEventListener('visibilitychange', function(e) { e.stopImmediatePropagation(); e.preventDefault(); return false; }, true); // 覆盖blur事件 window.addEventListener('blur', function(e) { e.stopImmediatePropagation(); e.preventDefault(); return false; }, true); // 覆盖focus事件 window.addEventListener('focus', function(e) { e.stopImmediatePropagation(); e.preventDefault(); return false; }, true); scriptStatus.focusProtection = true; } catch (e) { addErrorToMonitor(`焦点保护设置失败: ${e.message}`); scriptStatus.focusProtection = false; } } // 网络状态监听,实现网络中断恢复后的继续播放逻辑 function setupNetworkMonitoring() { // 记录网络状态 let isOnline = navigator.onLine; // 网络恢复时的处理函数 function handleOnline() { if (!isOnline) { console.log('网络已恢复,检查并恢复视频播放'); isOnline = true; // 延迟执行,确保网络连接稳定 setTimeout(() => { checkAndResumeVideo(); }, 1000); } } // 网络断开时的处理函数 function handleOffline() { console.log('网络连接已断开'); isOnline = false; } // 添加网络状态监听 window.addEventListener('online', handleOnline); window.addEventListener('offline', handleOffline); console.log('网络状态监听已设置'); } // 防抖函数,避免过于频繁的DOM操作 function debounce(func, wait) { let timeout; return function executedFunction(...args) { clearTimeout(timeout); timeout = setTimeout(() => func(...args), wait); }; } // 获取当前视频的进度百分比值 function getCurrentVideoProgress() { try { // 尝试多种方法获取视频进度 // 方法1: 通过目录项获取(原始方法) let percentage = 0; let isCompleted = false; let currentVideoTitle = ''; try { // 尝试查找当前播放的视频项(支持多种选择器) const currentVideoItem = document.querySelector('.catalogue li.on') || document.querySelector('.videoListContBlock li.on') || document.querySelector('.videoNavLists li.on'); if (currentVideoItem) { // 获取当前视频标题 currentVideoTitle = currentVideoItem.textContent.trim(); // 检查是否已完成(通过.completed类) isCompleted = currentVideoItem.classList.contains('completed'); // 获取进度百分比 const percentageElement = currentVideoItem.querySelector('.percentage-value') || currentVideoItem.querySelector('.el-progress__text') || currentVideoItem.querySelector('.progress-value'); if (percentageElement) { const percentageText = percentageElement.textContent.trim(); // 提取数字部分 const match = percentageText.match(/\d+/); if (match) { percentage = parseInt(match[0], 10); // 确保百分比在合理范围内 percentage = Math.min(Math.max(percentage, 0), 100); } } } } catch (domError) { // 静默处理DOM操作错误,避免重复触发错误 console.debug('DOM操作异常:', domError.message); } // 方法2: 如果目录项未找到或进度为0,尝试从视频元素本身获取进度 if (percentage === 0) { try { const videoElement = document.getElementById('video'); if (videoElement && !isNaN(videoElement.duration) && videoElement.duration > 0) { percentage = (videoElement.currentTime / videoElement.duration) * 100; isCompleted = videoElement.ended; // 确保百分比在合理范围内 percentage = Math.min(Math.max(percentage, 0), 100); } } catch (videoError) { console.debug('视频元素获取异常:', videoError.message); } } // 方法3: 尝试从网站原生的进度显示元素获取 try { // 查找网站原生的进度显示元素 const nativeProgressElement = document.querySelector('.video-progress span') || document.querySelector('.progress-bar .current-progress') || document.querySelector('.player-progress .progress-value'); if (nativeProgressElement) { const nativeProgressText = nativeProgressElement.textContent.trim(); const match = nativeProgressText.match(/\d+/); if (match) { const nativePercentage = parseInt(match[0], 10); // 如果原生进度值更准确,使用原生进度值 if (nativePercentage > 0) { percentage = nativePercentage; } } } } catch (nativeError) { console.debug('原生进度获取异常:', nativeError.message); } // 确保百分比在合理范围内 percentage = Math.min(Math.max(percentage, 0), 100); // 判断是否完成(进度99%以上或视频已结束) const isCompletedFinal = isCompleted || percentage >= 99; // 记录详细日志,便于调试和监控 console.debug(`[进度跟踪] 当前视频: ${currentVideoTitle}, 进度: ${percentage}%, 已完成: ${isCompletedFinal}`); return { percentage, isCompleted: isCompletedFinal, title: currentVideoTitle }; } catch (e) { // 最小化错误处理,避免与网站原生机制冲突 console.debug('视频进度获取异常:', e.message); // 避免频繁添加错误到监控器 return { percentage: 0, isCompleted: false, title: '' }; } } // 创建防抖版本的视频进度获取函数 const debouncedGetCurrentVideoProgress = debounce(getCurrentVideoProgress, 200); // 检查当前视频是否完成(兼容现有代码) function checkCurrentVideoStatus() { const progress = getCurrentVideoProgress(); return progress.isCompleted; } // 优化的视频进度跟踪函数,与网站原生20秒保存机制兼容 function trackVideoProgress() { try { const video = document.getElementById('video'); if (!video) return; // 记录视频开始播放时间 let startTime = Date.now(); // 监听视频播放事件 video.addEventListener('play', () => { startTime = Date.now(); console.log('视频开始播放,开始跟踪进度'); }); // 监听视频进度更新事件,与网站原生20秒保存机制兼容 video.addEventListener('timeupdate', () => { // 每20秒记录一次进度,与网站原生机制保持一致 const now = Date.now(); if (now - startTime >= 20000) { const progress = getCurrentVideoProgress(); console.log(`[进度保存] 自动保存进度: ${progress.percentage}% (${progress.title})`); startTime = now; } }); // 监听视频结束事件,确保最终进度被保存 video.addEventListener('ended', () => { const progress = getCurrentVideoProgress(); console.log(`[进度保存] 视频结束,最终进度: ${progress.percentage}% (${progress.title})`); }); console.log('视频进度跟踪已设置'); } catch (e) { console.error('视频进度跟踪设置失败:', e.message); addErrorToMonitor(`视频进度跟踪设置失败: ${e.message}`); } } // 添加错误到监控器 function addErrorToMonitor(errorMsg) { console.error(errorMsg); scriptStatus.errors.push({ message: errorMsg, timestamp: new Date().toLocaleTimeString() }); // 保持最多10条错误记录 if (scriptStatus.errors.length > 10) { scriptStatus.errors.shift(); } } // 创建监控面板 function createMonitorPanel() { const panel = document.createElement('div'); panel.id = 'script-monitor-panel'; panel.style.cssText = ` position: fixed; top: 10px; left: 10px; background: rgba(0, 0, 0, 0.85); color: #0f0; padding: 10px; border-radius: 5px; z-index: 9999; font-family: monospace; font-size: 12px; max-width: 300px; max-height: 400px; overflow-y: auto; box-shadow: 0 0 10px rgba(0, 255, 0, 0.3); cursor: move; `; // 添加标题栏和收起按钮 const titleBar = document.createElement('div'); titleBar.style.cssText = 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px;'; titleBar.innerHTML = '脚本监控面板'; const toggleButton = document.createElement('button'); toggleButton.textContent = '−'; toggleButton.style.cssText = ` background: rgba(255, 255, 255, 0.2); border: 1px solid #0f0; color: #0f0; width: 20px; height: 20px; border-radius: 3px; cursor: pointer; font-size: 12px; padding: 0; `; toggleButton.addEventListener('click', () => { scriptStatus.panelCollapsed = !scriptStatus.panelCollapsed; updateMonitorPanel(panel); }); titleBar.appendChild(toggleButton); panel.appendChild(titleBar); // 添加内容容器 const contentDiv = document.createElement('div'); contentDiv.id = 'monitor-content'; panel.appendChild(contentDiv); // 添加拖拽功能 let isDragging = false; let dragOffsetX = 0; let dragOffsetY = 0; titleBar.addEventListener('mousedown', (e) => { // 只有在展开状态下才允许拖拽 if (!scriptStatus.panelCollapsed) { isDragging = true; dragOffsetX = e.clientX - panel.getBoundingClientRect().left; dragOffsetY = e.clientY - panel.getBoundingClientRect().top; e.preventDefault(); } }); document.addEventListener('mousemove', (e) => { if (isDragging) { const x = e.clientX - dragOffsetX; const y = e.clientY - dragOffsetY; panel.style.left = Math.max(0, x) + 'px'; panel.style.top = Math.max(0, y) + 'px'; } }); document.addEventListener('mouseup', () => { isDragging = false; }); document.body.appendChild(panel); return panel; } // 更新监控面板 function updateMonitorPanel(panel) { if (!panel) return; if (scriptStatus.panelCollapsed) { // 收起状态:只显示一个方框 panel.style.width = '20px'; panel.style.height = '20px'; panel.style.padding = '0'; panel.style.borderRadius = '3px'; panel.style.cursor = 'pointer'; panel.style.display = 'flex'; panel.style.alignItems = 'center'; panel.style.justifyContent = 'center'; panel.style.color = '#0f0'; panel.style.fontSize = '16px'; panel.style.fontWeight = 'bold'; panel.innerHTML = '+'; // 移除所有事件监听器(避免重复绑定) panel.onclick = null; // 添加点击事件以展开面板 panel.addEventListener('click', function expandPanel(event) { // 阻止事件冒泡 event.stopPropagation(); // 重置panelCollapsed状态 scriptStatus.panelCollapsed = false; // 重新更新面板 updateMonitorPanel(panel); }, { once: true }); // 使用once选项确保只触发一次 return; } else { // 展开状态 panel.style.width = ''; panel.style.height = ''; panel.style.padding = '10px'; panel.style.borderRadius = '5px'; panel.style.cursor = 'move'; panel.style.display = 'block'; panel.style.color = '#0f0'; // 确保文本颜色正确 panel.innerHTML = ''; // 创建标题栏和内容 const titleBar = document.createElement('div'); titleBar.style.cssText = 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px;'; titleBar.innerHTML = '脚本监控面板'; const toggleButton = document.createElement('button'); toggleButton.textContent = '−'; toggleButton.style.cssText = ` background: rgba(255, 255, 255, 0.2); border: 1px solid #0f0; color: #0f0; width: 20px; height: 20px; border-radius: 3px; cursor: pointer; font-size: 12px; padding: 0; `; toggleButton.addEventListener('click', (event) => { event.stopPropagation(); scriptStatus.panelCollapsed = true; updateMonitorPanel(panel); }); titleBar.appendChild(toggleButton); panel.appendChild(titleBar); // 创建内容容器 const contentDiv = document.createElement('div'); contentDiv.id = 'monitor-content'; panel.appendChild(contentDiv); // 内容生成逻辑 const statusItems = [ { name: '视频加载', status: scriptStatus.videoLoaded }, { name: '自动静音', status: scriptStatus.autoMute }, { name: '自动恢复', status: scriptStatus.autoResume }, { name: '弹窗处理', status: scriptStatus.popupHandling }, { name: '视频切换', status: scriptStatus.videoSwitching }, { name: '焦点保护', status: scriptStatus.focusProtection } ]; // 获取最新的视频进度 const currentProgress = scriptStatus.currentVideoProgress || 0; const lastCheckTime = scriptStatus.lastProgressCheck ? new Date(scriptStatus.lastProgressCheck).toLocaleTimeString() : '未检查'; let html = '