// ==UserScript== // @name 华医网课程自动切换工具(增强优化版) // @namespace https://github.com/user/auto-next-lesson-tool // @version 3.1.0 // @description 自动检测华医网视频播放状态并在结束时切换到下一节课程,优化了跳转逻辑和控制面板 // @author Your Name // @match *://*.91huayi.com/* // @match *://yxpk.91huayi.com/* // @match *://xuexi.91huayi.com/* // @match *://*.huayiwang91.com/* // @icon data:image/svg+xml,🎓 // @grant none // @license MIT // @run-at document-end // ==/UserScript== class HuaYiAutoNextLesson { constructor(options = {}) { this.options = { switchDelay: 2, // 切换延迟时间(秒) checkInterval: 0.8, // 优化检测频率 progressThreshold: 0.95, // 进度阈值,95%视为可切换 debugMode: false, autoMute: true, showControlPanel: true, panelPosition: 'top-right', onStateChange: null, onVideoDetected: null, onVideoEnded: null, onLessonSwitched: null, onError: null }; Object.assign(this.options, options); // 状态变量 this.isRunning = false; this.checkTimer = null; this.switchTimer = null; this.lastVideoState = null; this.switchHistory = []; this.currentCourseTitle = ''; this.currentLessonTitle = ''; this.controlPanel = null; this.dragState = { isDragging: false, offsetX: 0, offsetY: 0 }; // 选择器优化,增加更多可能的下一课按钮选择器 this.selectors = { videoElements: [ 'video', '.video-js video', '.vjs-tech', '#video-player video', '.video-container video', '.player-container video', '[id*="video"] video', '[class*="video"] video' ], nextLessonButtons: [ '.next-btn', '.next-step', '.next-lesson', '.next-button', '[id*="next"]', '[class*="next"]', '[onclick*="next"]', '[href*="next"]', 'button:contains("下一步")', 'button:contains("下一课")', 'button:contains("继续学习")', 'a:contains("下一步")', 'a:contains("下一课")', 'a:contains("继续学习")', '[id*="nextLesson"]', '[class*="nextLesson"]', '.btn-next', '.step-next', '.lesson-next', '.btn-primary:contains("下一节")', // 新增常见按钮样式 '.btn-success:contains("继续")', '[data-action="next"]', '.pagination-next', '.nav-next' ], popupCloseButtons: [ '.close-btn', '.close', '[data-dismiss="modal"]', '.modal-close', '.popup-close', '[id*="close"]', '[class*="close"]', 'button:contains("确定")', 'button:contains("知道了")', 'button:contains("关闭")', 'button:contains("继续")', '.layui-layer-close', // 新增常见弹窗关闭按钮 '.ui-dialog-close' ], progressBars: [ '.progress-bar', '.progress', '.vjs-progress-bar', '.vjs-play-progress', '[class*="progress"]', '[id*="progress"]' ], courseTitle: [ '.course-title', '.course-name', 'h1', 'h2', '[id*="course"]', '[class*="course"]' ], lessonTitle: [ '.lesson-title', '.lesson-name', 'h3', 'h4', '[id*="lesson"]', '[class*="lesson"]' ] }; this.log('华医网课程自动切换工具已初始化', 'info'); this._detectCurrentPage(); if (this.options.showControlPanel) { this._createControlPanel(); } } // 检测当前页面类型 _detectCurrentPage() { const url = window.location.href.toLowerCase(); if (url.includes('/course/') || url.includes('/lesson/')) { this.log('检测到课程播放页面', 'info'); this._extractPageInfo(); } else if (url.includes('/course/list') || url.includes('/courses')) { this.log('检测到课程列表页面', 'info'); } else if (url.includes('/exam/') || url.includes('/test/')) { this.log('检测到考试页面', 'info'); } else { this.log('检测到其他页面', 'info'); } } // 提取页面信息 _extractPageInfo() { // 提取课程标题 for (const selector of this.selectors.courseTitle) { try { const element = document.querySelector(selector); if (element && element.textContent.trim()) { this.currentCourseTitle = element.textContent.trim(); break; } } catch (error) { this.log(`提取课程标题出错: ${error.message}`, 'debug'); } } // 提取课时标题 for (const selector of this.selectors.lessonTitle) { try { const element = document.querySelector(selector); if (element && element.textContent.trim()) { this.currentLessonTitle = element.textContent.trim(); break; } } catch (error) { this.log(`提取课时标题出错: ${error.message}`, 'debug'); } } this.log(`当前课程: ${this.currentCourseTitle || '未知'}`, 'info'); this.log(`当前课时: ${this.currentLessonTitle || '未知'}`, 'info'); this._updateControlPanelInfo(); } // 创建控制面板 _createControlPanel() { this.controlPanel = document.createElement('div'); this.controlPanel.id = 'huayi-auto-next-control-panel'; this.controlPanel.className = 'huayi-control-panel'; // 优化面板样式,更简洁紧凑 const panelStyles = ` position: fixed; background: rgba(255, 255, 255, 0.95); border: 1px solid #e5e7eb; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); padding: 10px; width: 280px; z-index: 99999; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 12px; transition: all 0.3s ease; backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); `; let positionStyles = ''; switch (this.options.panelPosition) { case 'top-left': positionStyles = 'top: 20px; left: 20px;'; break; case 'bottom-left': positionStyles = 'bottom: 20px; left: 20px;'; break; case 'bottom-right': positionStyles = 'bottom: 20px; right: 20px;'; break; case 'top-right': default: positionStyles = 'top: 20px; right: 20px;'; break; } this.controlPanel.style.cssText = panelStyles + positionStyles; // 面板内容 this.controlPanel.innerHTML = `
华医网自动切换工具
状态: 未启动
视频检测: 未检测
当前课程: ${this.currentCourseTitle || '未知'}
%
最近日志:
工具已初始化
`; document.body.appendChild(this.controlPanel); this._bindControlPanelEvents(); this._updateControlPanelState(); } // 绑定控制面板事件 _bindControlPanelEvents() { if (!this.controlPanel) return; // 启动按钮 document.getElementById('huayi-btn-start')?.addEventListener('click', () => { this.start(); this._updateControlPanelState(); }); // 停止按钮 document.getElementById('huayi-btn-stop')?.addEventListener('click', () => { this.stop(); this._updateControlPanelState(); }); // 静音按钮 const muteBtn = document.getElementById('huayi-btn-mute'); muteBtn?.addEventListener('click', () => { this.options.autoMute = !this.options.autoMute; muteBtn.textContent = this.options.autoMute ? '静音: 开' : '静音: 关'; this.updateOptions({ autoMute: this.options.autoMute }); this.log(`${this.options.autoMute ? '开启' : '关闭'}自动静音`, 'info'); }); // 调试按钮 const debugBtn = document.getElementById('huayi-btn-debug'); debugBtn?.addEventListener('click', () => { this.options.debugMode = !this.options.debugMode; debugBtn.textContent = this.options.debugMode ? '调试: 开' : '调试: 关'; this.updateOptions({ debugMode: this.options.debugMode }); this.log(`${this.options.debugMode ? '开启' : '关闭'}调试模式`, 'info'); }); // 切换延迟输入 document.getElementById('huayi-switch-delay')?.addEventListener('change', (e) => { const value = parseFloat(e.target.value); if (!isNaN(value) && value >= 0) { this.updateOptions({ switchDelay: value }); this.log(`切换延迟已设置为 ${value} 秒`, 'info'); } }); // 进度阈值输入 document.getElementById('huayi-progress-threshold')?.addEventListener('change', (e) => { const value = parseFloat(e.target.value) / 100; if (!isNaN(value) && value >= 0.8 && value <= 1) { this.updateOptions({ progressThreshold: value }); this.log(`进度阈值已设置为 ${value * 100}%`, 'info'); } }); // 检查间隔输入 document.getElementById('huayi-check-interval')?.addEventListener('change', (e) => { const value = parseFloat(e.target.value); if (!isNaN(value) && value > 0) { this.updateOptions({ checkInterval: value }); this.log(`检查间隔已设置为 ${value} 秒`, 'info'); } }); // 清空日志按钮 document.getElementById('huayi-btn-clear-logs')?.addEventListener('click', () => { this._clearLogs(); }); // 最小化按钮 const minimizeBtn = document.getElementById('huayi-btn-minimize'); minimizeBtn?.addEventListener('click', () => { this._togglePanelMinimize(); }); // 优化面板拖动功能 this._makePanelDraggable(); } // 优化面板拖动功能 _makePanelDraggable() { if (!this.controlPanel) return; const header = this.controlPanel.querySelector('.panel-header'); if (!header) return; // 拖动开始 header.addEventListener('mousedown', (e) => { if (e.target.tagName === 'BUTTON' || e.target.closest('button')) { return; // 避免点击按钮时触发拖动 } this.dragState.isDragging = true; const rect = this.controlPanel.getBoundingClientRect(); this.dragState.offsetX = e.clientX - rect.left; this.dragState.offsetY = e.clientY - rect.top; this.controlPanel.style.cursor = 'grabbing'; header.style.userSelect = 'none'; // 添加视觉反馈 this.controlPanel.style.boxShadow = '0 6px 16px rgba(0, 0, 0, 0.2)'; this.controlPanel.style.transform = 'scale(1.02)'; }); // 拖动过程 - 使用requestAnimationFrame优化性能 const handleMouseMove = (e) => { if (!this.dragState.isDragging) return; requestAnimationFrame(() => { const x = e.clientX - this.dragState.offsetX; const y = e.clientY - this.dragState.offsetY; // 限制在视口内 const maxX = window.innerWidth - this.controlPanel.offsetWidth; const maxY = window.innerHeight - this.controlPanel.offsetHeight; const boundedX = Math.max(0, Math.min(x, maxX)); const boundedY = Math.max(0, Math.min(y, maxY)); this.controlPanel.style.left = boundedX + 'px'; this.controlPanel.style.top = boundedY + 'px'; this.controlPanel.style.right = 'auto'; this.controlPanel.style.bottom = 'auto'; }); }; // 拖动结束 const handleMouseUp = () => { if (this.dragState.isDragging) { this.dragState.isDragging = false; this.controlPanel.style.cursor = 'default'; header.style.userSelect = ''; // 恢复样式 this.controlPanel.style.boxShadow = ''; this.controlPanel.style.transform = ''; } }; // 绑定事件 document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); // 防止拖动时选中文本 header.addEventListener('dragstart', (e) => e.preventDefault()); } // 切换面板最小化状态 _togglePanelMinimize() { const content = document.getElementById('huayi-panel-content'); const minimizeBtn = document.getElementById('huayi-btn-minimize'); if (!content || !minimizeBtn) return; if (content.style.display === 'none') { content.style.display = 'block'; minimizeBtn.textContent = '▼'; } else { content.style.display = 'none'; minimizeBtn.textContent = '▲'; } } // 更新控制面板状态 _updateControlPanelState() { if (!this.controlPanel) return; // 更新状态指示器 const statusIndicator = document.getElementById('huayi-status-indicator'); const statusValue = document.getElementById('huayi-status-value'); if (statusIndicator && statusValue) { if (this.isRunning) { statusIndicator.style.backgroundColor = '#10b981'; // 绿色 statusValue.textContent = '运行中'; statusValue.style.color = '#10b981'; } else { statusIndicator.style.backgroundColor = '#6b7280'; // 灰色 statusValue.textContent = '已停止'; statusValue.style.color = '#6b7280'; } } // 更新按钮状态 const startBtn = document.getElementById('huayi-btn-start'); const stopBtn = document.getElementById('huayi-btn-stop'); if (startBtn && stopBtn) { if (this.isRunning) { startBtn.style.backgroundColor = '#9ca3af'; startBtn.disabled = true; stopBtn.style.backgroundColor = '#ef4444'; stopBtn.disabled = false; } else { startBtn.style.backgroundColor = '#10b981'; startBtn.disabled = false; stopBtn.style.backgroundColor = '#9ca3af'; stopBtn.disabled = true; } } } // 更新控制面板信息 _updateControlPanelInfo() { if (!this.controlPanel) return; // 更新课程标题 const courseTitleElement = document.getElementById('huayi-course-title'); if (courseTitleElement) { courseTitleElement.textContent = this.currentCourseTitle || '未知'; } } // 更新视频状态显示 _updateVideoStatusDisplay(videoState) { if (!this.controlPanel) return; const videoStatusElement = document.getElementById('huayi-video-status'); if (!videoStatusElement) return; if (videoState.exists) { const progressPercent = Math.round(videoState.progress * 100); videoStatusElement.textContent = `播放中 ${progressPercent}%`; videoStatusElement.style.color = videoState.isPaused ? '#f59e0b' : '#10b981'; } else { videoStatusElement.textContent = '未检测'; videoStatusElement.style.color = '#9ca3af'; } } // 添加日志到面板 _addLogToPanel(message, level = 'info') { if (!this.controlPanel) return; const logsContainer = document.getElementById('huayi-logs-container'); if (!logsContainer) return; const logEntry = document.createElement('div'); logEntry.className = `log-entry ${level}`; logEntry.style.marginBottom = '2px'; logEntry.style.padding = '1px 0'; // 根据级别设置颜色 switch (level) { case 'error': logEntry.style.color = '#dc2626'; break; case 'warning': logEntry.style.color = '#d97706'; break; case 'success': logEntry.style.color = '#059669'; break; case 'debug': logEntry.style.color = '#6366f1'; break; case 'info': default: logEntry.style.color = '#374151'; break; } // 限制消息长度 const shortMessage = message.length > 60 ? message.substring(0, 60) + '...' : message; logEntry.textContent = `[${new Date().toLocaleTimeString()}] ${shortMessage}`; // 添加到容器开头 logsContainer.insertBefore(logEntry, logsContainer.firstChild); // 限制日志数量 const logEntries = logsContainer.querySelectorAll('.log-entry'); if (logEntries.length > 10) { logsContainer.removeChild(logEntries[logEntries.length - 1]); } // 滚动到底部 logsContainer.scrollTop = 0; } // 清空日志 _clearLogs() { const logsContainer = document.getElementById('huayi-logs-container'); if (logsContainer) { logsContainer.innerHTML = '
日志已清空
'; } } // 启动工具 start() { if (this.isRunning) { this.log('工具已经在运行', 'warning'); return false; } this.isRunning = true; this.log('工具已启动', 'success'); this._notifyStateChange(); // 开始检测视频 this._startVideoCheck(); // 显示启动通知 this._showNotification('华医网课程自动切换工具已启动', '将自动检测视频并切换到下一课', 3000); // 更新控制面板状态 this._updateControlPanelState(); return true; } // 停止工具 stop() { if (!this.isRunning) { this.log('工具未运行', 'warning'); return false; } this.isRunning = false; this._clearTimers(); this.log('工具已停止', 'info'); this._notifyStateChange(); // 更新控制面板状态 this._updateControlPanelState(); return true; } // 更新配置 updateOptions(newOptions) { Object.assign(this.options, newOptions); this.log('配置已更新: ' + JSON.stringify(newOptions), 'info'); // 如果工具正在运行,重新启动检测 if (this.isRunning) { this._clearTimers(); this._startVideoCheck(); } return true; } // 获取当前状态 getState() { return { isRunning: this.isRunning, options: { ...this.options }, switchHistory: [...this.switchHistory], currentCourseTitle: this.currentCourseTitle, currentLessonTitle: this.currentLessonTitle }; } // 获取切换历史 getSwitchHistory() { return [...this.switchHistory]; } // 查找视频元素 _findVideoElement() { // 尝试每个视频选择器 for (const selector of this.selectors.videoElements) { try { const elements = document.querySelectorAll(selector); if (elements.length > 0) { this.log(`使用选择器 "${selector}" 找到 ${elements.length} 个视频元素`, 'debug'); // 返回第一个可见的视频元素 for (const element of elements) { if (this._isElementVisible(element)) { return element; } } } } catch (error) { this.log(`选择器 "${selector}" 执行出错: ${error.message}`, 'debug'); } } // 如果没有找到视频元素,尝试查找其他可能的视频播放元素 const videoContainers = document.querySelectorAll('.video-container, .player-container, .video-player'); if (videoContainers.length > 0) { this.log(`找到 ${videoContainers.length} 个视频容器`, 'debug'); // 尝试在容器内查找视频元素 for (const container of videoContainers) { const containerVideo = container.querySelector('video'); if (containerVideo && this._isElementVisible(containerVideo)) { this.log('在视频容器内找到视频元素', 'debug'); return containerVideo; } } } return null; } // 检查元素是否可见 _isElementVisible(element) { // 检查元素是否在DOM中 if (!document.body.contains(element)) { return false; } // 检查元素的尺寸 const rect = element.getBoundingClientRect(); if (rect.width <= 0 || rect.height <= 0) { return false; } // 检查元素是否被隐藏 const computedStyle = window.getComputedStyle(element); if (computedStyle.display === 'none' || computedStyle.visibility === 'hidden' || computedStyle.opacity === '0') { return false; } // 检查元素是否在视口中(放宽条件,只要部分可见即可) const isInViewport = ( rect.top < (window.innerHeight || document.documentElement.clientHeight) && rect.bottom > 0 && rect.left < (window.innerWidth || document.documentElement.clientWidth) && rect.right > 0 ); return isInViewport; } // 检查视频状态 - 优化版本 _checkVideoState(videoElement) { try { // 检查视频元素的基本属性 const currentTime = videoElement.currentTime || 0; const duration = videoElement.duration || 0; const progress = duration > 0 ? currentTime / duration : 0; const isPaused = videoElement.paused; // 优化结束判断逻辑:结合视频自带的ended属性和进度阈值 const isEnded = videoElement.ended || progress >= this.options.progressThreshold; // 检查是否被暂停但视频未结束(可能是网站自动暂停) const isUnexpectedPause = isPaused && !isEnded && currentTime > 0 && progress < this.options.progressThreshold; return { exists: true, currentTime: currentTime, duration: duration, progress: progress, isPaused: isPaused, isEnded: isEnded, isUnexpectedPause: isUnexpectedPause, volume: videoElement.volume }; } catch (error) { this.log(`检查视频状态出错: ${error.message}`, 'error'); return { exists: false, error: error.message }; } } // 开始视频检测 _startVideoCheck() { if (!this.isRunning) return; this.log(`开始检测视频,检查间隔: ${this.options.checkInterval}秒`, 'debug'); const checkVideo = () => { if (!this.isRunning) return; try { // 检查并关闭弹窗 this._closePopups(); // 查找视频元素 const videoElement = this._findVideoElement(); if (videoElement) { this.log('检测到视频元素', 'debug'); // 如果启用了自动静音,检查并设置静音 if (this.options.autoMute && videoElement.volume > 0) { videoElement.volume = 0; this.log('已将视频设置为静音', 'info'); } // 检查视频状态 const videoState = this._checkVideoState(videoElement); // 更新视频状态显示 this._updateVideoStatusDisplay(videoState); // 通知视频检测状态 if (this.options.onVideoDetected) { this.options.onVideoDetected(videoState); } // 处理意外暂停(自动播放) if (videoState.isUnexpectedPause) { this.log('检测到视频被意外暂停,尝试继续播放', 'info'); try { videoElement.play().catch(err => { this.log(`尝试播放视频失败: ${err.message}`, 'warning'); }); } catch (playErr) { this.log(`播放视频时出错: ${playErr.message}`, 'error'); } } // 检查视频是否结束(且状态有变化) if (videoState.isEnded && this.lastVideoState && !this.lastVideoState.isEnded) { this.log(`检测到视频播放结束 (进度: ${Math.round(videoState.progress * 100)}%)`, 'info'); // 通知视频结束 if (this.options.onVideoEnded) { this.options.onVideoEnded(videoState); } // 处理视频结束后的操作 this._handleVideoEnd(); } // 保存当前状态作为下次检查的参考 this.lastVideoState = videoState; } else { // 未找到视频元素 this._updateVideoStatusDisplay({ exists: false }); this.lastVideoState = null; } } catch (error) { this.log(`视频检测出错: ${error.message}`, 'error'); if (this.options.onError) { this.options.onError(error); } } // 设置下一次检查 if (this.isRunning) { this.checkTimer = setTimeout(checkVideo, this.options.checkInterval * 1000); } }; // 立即执行一次检查 checkVideo(); } // 处理视频结束 _handleVideoEnd() { if (!this.isRunning) return; this.log(`将在 ${this.options.switchDelay} 秒后尝试切换到下一课`, 'info'); // 清除可能存在的切换计时器 if (this.switchTimer) { clearTimeout(this.switchTimer); } // 设置切换计时器 this.switchTimer = setTimeout(() => { if (!this.isRunning) return; // 尝试切换到下一课 const switched = this._switchToNextLesson(); if (switched) { // 记录切换历史 this.switchHistory.push({ lesson: this.currentLessonTitle || '未知课时', time: new Date().toISOString() }); // 限制历史记录长度 if (this.switchHistory.length > 10) { this.switchHistory.shift(); } // 通知课程切换 if (this.options.onLessonSwitched) { this.options.onLessonSwitched(this.currentLessonTitle); } // 切换后重新加载页面信息 setTimeout(() => { this._extractPageInfo(); }, 2000); } else { this.log('未能找到下一课按钮,将继续尝试', 'warning'); // 继续尝试,每5秒一次 this.switchTimer = setTimeout(() => { this._handleVideoEnd(); }, 5000); } }, this.options.switchDelay * 1000); } // 优化切换到下一课的逻辑 _switchToNextLesson() { // 再次关闭可能弹出的弹窗 this._closePopups(); // 尝试所有可能的下一课按钮选择器 for (const selector of this.selectors.nextLessonButtons) { try { // 尝试直接选择 let buttons = document.querySelectorAll(selector); // 过滤可见的按钮 buttons = Array.from(buttons).filter(btn => this._isElementVisible(btn)); if (buttons.length > 0) { this.log(`找到 ${buttons.length} 个可能的下一课按钮 (选择器: ${selector})`, 'info'); // 优先点击包含特定文本的按钮 const priorityTexts = ['下一课', '下一步', '继续学习', '下一节']; let targetButton = null; for (const text of priorityTexts) { targetButton = buttons.find(btn => btn.textContent.includes(text) && this._isElementVisible(btn) ); if (targetButton) break; } // 如果没有找到优先按钮,使用第一个可见按钮 if (!targetButton) { targetButton = buttons[0]; } if (targetButton) { this.log('尝试点击下一课按钮', 'info'); // 尝试多种点击方式 try { // 方式1: 直接点击 targetButton.click(); this.log('已点击下一课按钮', 'success'); return true; } catch (clickErr1) { try { // 方式2: 触发click事件 const event = new MouseEvent('click', { bubbles: true, cancelable: true, view: window }); targetButton.dispatchEvent(event); this.log('已触发下一课按钮点击事件', 'success'); return true; } catch (clickErr2) { try { // 方式3: 如果是链接,直接跳转 if (targetButton.tagName === 'A' && targetButton.href) { window.location.href = targetButton.href; this.log('已通过链接跳转到下一课', 'success'); return true; } } catch (clickErr3) { this.log(`点击下一课按钮失败: ${clickErr3.message}`, 'error'); } } } } } } catch (error) { this.log(`选择器 "${selector}" 执行出错: ${error.message}`, 'debug'); } } // 如果没有找到按钮,尝试查找分页控件 this.log('尝试查找分页控件', 'debug'); const paginationNext = document.querySelector('.pagination .next, .pager .next, .page-next'); if (paginationNext && this._isElementVisible(paginationNext)) { try { paginationNext.click(); this.log('已点击分页控件的下一页', 'success'); return true; } catch (err) { this.log(`点击分页控件失败: ${err.message}`, 'error'); } } this.log('未找到可点击的下一课按钮', 'error'); return false; } // 关闭弹窗 _closePopups() { let closed = false; // 尝试所有可能的关闭按钮选择器 for (const selector of this.selectors.popupCloseButtons) { try { const closeButtons = document.querySelectorAll(selector); // 过滤可见的按钮 const visibleButtons = Array.from(closeButtons).filter(btn => this._isElementVisible(btn)); if (visibleButtons.length > 0) { this.log(`找到 ${visibleButtons.length} 个弹窗关闭按钮 (选择器: ${selector})`, 'debug'); // 点击每个可见的关闭按钮 visibleButtons.forEach(button => { try { button.click(); closed = true; this.log('已关闭弹窗', 'info'); } catch (error) { this.log(`关闭弹窗失败: ${error.message}`, 'debug'); } }); } } catch (error) { this.log(`关闭弹窗选择器 "${selector}" 执行出错: ${error.message}`, 'debug'); } } // 尝试关闭常见的弹窗容器 const modalContainers = ['.modal', '.popup', '.dialog', '.layer', '.alert']; modalContainers.forEach(selector => { try { const modals = document.querySelectorAll(`${selector}:not([style*="display: none"])`, `${selector}:not([hidden])`); modals.forEach(modal => { try { // 尝试直接隐藏 modal.style.display = 'none'; closed = true; this.log(`已隐藏弹窗容器: ${selector}`, 'info'); } catch (error) { this.log(`隐藏弹窗容器失败: ${error.message}`, 'debug'); } }); } catch (error) { this.log(`处理弹窗容器 "${selector}" 出错: ${error.message}`, 'debug'); } }); return closed; } // 清除计时器 _clearTimers() { if (this.checkTimer) { clearTimeout(this.checkTimer); this.checkTimer = null; } if (this.switchTimer) { clearTimeout(this.switchTimer); this.switchTimer = null; } } // 记录日志 log(message, level = 'info') { // 控制台输出 if (this.options.debugMode || level !== 'debug') { const prefix = `[华医网自动切换]`; switch (level) { case 'error': console.error(`${prefix} ${message}`); break; case 'warning': console.warn(`${prefix} ${message}`); break; case 'success': console.log(`${prefix} %c${message}`, 'color: #059669'); break; case 'debug': console.debug(`${prefix} %c${message}`, 'color: #6366f1'); break; case 'info': default: console.log(`${prefix} ${message}`); break; } } // 添加到面板 this._addLogToPanel(message, level); } // 显示通知 _showNotification(title, message, duration = 3000) { // 检查浏览器是否支持通知 if (typeof Notification !== 'undefined' && Notification.permission === 'granted') { new Notification(title, { body: message }); } else if (typeof Notification !== 'undefined' && Notification.permission !== 'denied') { // 请求通知权限 Notification.requestPermission(); } // 创建页面内通知 const notification = document.createElement('div'); notification.style.cssText = ` position: fixed; bottom: 20px; left: 20px; background: rgba(31, 41, 55, 0.9); color: white; padding: 10px 15px; border-radius: 6px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); z-index: 99999; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 13px; transition: all 0.3s ease; transform: translateY(100px); opacity: 0; `; notification.innerHTML = `
${title}
${message}
`; document.body.appendChild(notification); // 显示通知 setTimeout(() => { notification.style.transform = 'translateY(0)'; notification.style.opacity = 1; }, 10); // 自动隐藏 setTimeout(() => { notification.style.transform = 'translateY(100px)'; notification.style.opacity = 0; setTimeout(() => { document.body.removeChild(notification); }, 300); }, duration); } // 通知状态变化 _notifyStateChange() { if (this.options.onStateChange) { this.options.onStateChange(this.getState()); } } } // 初始化工具 document.addEventListener('DOMContentLoaded', () => { // 等待页面完全加载 setTimeout(() => { const autoNextLesson = new HuaYiAutoNextLesson({ debugMode: false, autoMute: true, switchDelay: 2, checkInterval: 0.8, progressThreshold: 0.95 }); // 暴露到window方便调试 window.huayiAutoNext = autoNextLesson; // 自动启动(可根据需要改为手动启动) autoNextLesson.start(); }, 1000); });