// ==UserScript== // @name 网页定时管理器 // @namespace http://tampermonkey.net/ // @version 1.1 // @description 网页定时关闭和定时跳转 // @author huihuia24 // @homepage https://github.com/huihuia24 // @source https://github.com/huihuia24/-/tree/main // @match *://*/* // @grant GM_openInTab // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @grant GM_registerMenuCommand // @run-at document-idle // @icon https://github.com/huihuia24/Web-Timer-Manager/blob/28a6ac6404aa154168b731a7d632b306febdc7d2/%E7%BD%91%E9%A1%B5%E5%AE%9A%E6%97%B6%E7%AE%A1%E7%90%86%E5%99%A8.png?raw=true // @noframes // 禁止在iframe中执行 // @downloadURL https://update.greasyfork.icu/scripts/555269/%E7%BD%91%E9%A1%B5%E5%AE%9A%E6%97%B6%E7%AE%A1%E7%90%86%E5%99%A8.user.js // @updateURL https://update.greasyfork.icu/scripts/555269/%E7%BD%91%E9%A1%B5%E5%AE%9A%E6%97%B6%E7%AE%A1%E7%90%86%E5%99%A8.meta.js // ==/UserScript== (function() { 'use strict'; // 全局单例检测:若已有实例则直接退出 if (window.timerManagerInstance) { return; } window.timerManagerInstance = true; // 修复 UI 重复问题:初始化前移除已存在的管理器元素 function cleanDuplicateUI() { const existingMain = document.getElementById('timer-manager'); const existingMini = document.getElementById('timer-mini'); const existingThemeStyle = document.getElementById('timer-theme-styles'); // 移除重复的主界面、最小化界面和主题样式 if (existingMain) existingMain.remove(); if (existingMini) existingMini.remove(); if (existingThemeStyle) existingThemeStyle.remove(); } // 执行清理 cleanDuplicateUI(); // 加载Font Awesome function loadFontAwesome() { if (document.querySelector('link[href*="font-awesome"]')) return; const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css'; document.head.appendChild(link); } loadFontAwesome(); // 存储管理模块 const Storage = { get(key, defaultValue) { const value = GM_getValue(key); return value === undefined ? defaultValue : value; }, set(key, value) { GM_setValue(key, value); }, savePos(elementId, x, y) { this.set(`${elementId}_x`, x); this.set(`${elementId}_y`, y); }, getPos(elementId, defaultX, defaultY) { return { x: this.get(`${elementId}_x`, defaultX), y: this.get(`${elementId}_y`, defaultY) }; }, saveTasks(tasks) { this.set('timer_tasks', JSON.stringify(tasks)); }, getTasks() { try { return JSON.parse(this.get('timer_tasks', '[]')); } catch (e) { return []; } }, saveMultiState(state) { this.set('multi_state', JSON.stringify(state)); }, getMultiState() { try { return JSON.parse(this.get('multi_state', '{"running":false,"index":0,"remaining":0}')); } catch (e) { return { running: false, index: 0, remaining: 0 }; } }, getTheme() { return this.get('timer_theme', 'light'); // light/dark }, setTheme(theme) { this.set('timer_theme', theme); } }; // 基础样式定义(核心修改:提升层级并隔离层叠上下文) GM_addStyle(` #timer-manager { position: fixed; width: 380px; max-width: calc(100% - 40px); border-radius: 10px; box-shadow: 0 4px 20px rgba(0,0,0,0.15); z-index: 9999999; /* 超高层级确保置顶 */ padding: 15px; display: none; transition: all 0.3s ease; box-sizing: border-box; will-change: transform, z-index; /* 强制创建独立层叠上下文 */ transform: translateZ(0); /* 硬件加速渲染,避免被网页遮挡 */ } #timer-mini { position: fixed; border-radius: 8px; padding: 8px 15px; font-size: 20px; font-weight: bold; box-shadow: 0 3px 12px rgba(0,0,0,0.2); z-index: 9999999; /* 超高层级确保置顶 */ cursor: pointer; user-select: none; transition: all 0.2s ease; white-space: nowrap; display: flex; align-items: center; gap: 8px; will-change: transform, z-index; /* 强制创建独立层叠上下文 */ transform: translateZ(0); /* 硬件加速渲染 */ } .timer-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid; } .timer-title { font-size: 18px; font-weight: 600; display: flex; align-items: center; gap: 8px; } .timer-controls { display: flex; gap: 8px; } .timer-btn { cursor: pointer; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border-radius: 50%; font-size: 16px; transition: all 0.2s; } .timer-btn:hover { transform: scale(1.1); } .timer-tabs { display: flex; margin-bottom: 15px; border-radius: 6px; overflow: hidden; } .timer-tab { flex: 1; padding: 10px 0; text-align: center; cursor: pointer; transition: all 0.3s; font-weight: 500; font-size: 14px; display: flex; align-items: center; justify-content: center; gap: 6px; } .timer-tab.active { font-weight: 600; } .timer-tab-content { display: none; animation: fadeIn 0.3s ease; } .timer-tab-content.active { display: block; } .form-group { margin-bottom: 15px; } .form-label { display: block; margin-bottom: 6px; font-weight: 500; font-size: 14px; display: flex; align-items: center; gap: 6px; } .form-control { width: 100%; padding: 9px 12px; border: 1px solid; border-radius: 6px; box-sizing: border-box; font-size: 14px; transition: border-color 0.2s, box-shadow 0.2s; pointer-events: auto !important; user-select: text !important; } .form-control:focus { outline: none; box-shadow: 0 0 0 3px; } .time-inputs { display: flex; gap: 8px; } .time-input-group { flex: 1; } .time-label { display: block; margin-bottom: 4px; font-size: 13px; text-align: center; } .time-input { width: 100%; padding: 10px 5px; border: 1px solid; border-radius: 6px; font-size: 22px; text-align: center; transition: border-color 0.2s; -moz-appearance: textfield; pointer-events: auto !important; user-select: text !important; box-sizing: border-box; } .time-input::-webkit-outer-spin-button, .time-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } .time-input:focus { outline: none; } .btn { padding: 8px 16px; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.2s; display: flex; align-items: center; justify-content: center; gap: 6px; } .btn-primary:hover, .btn-secondary:hover { transform: translateY(-1px); } .task-list { margin: 15px 0; max-height: 200px; overflow-y: auto; border: 1px solid; border-radius: 6px; } .task-item { display: flex; align-items: center; padding: 10px 12px; border-bottom: 1px solid; flex-wrap: nowrap; } .task-item:last-child { border-bottom: none; } .task-item:hover { transition: background-color 0.2s; } .task-url { flex: 1; word-break: break-all; font-size: 13px; min-width: 0; display: flex; align-items: center; gap: 6px; } .task-time { margin: 0 8px; font-size: 14px; min-width: 80px; text-align: center; display: flex; align-items: center; gap: 4px; } .task-remove { cursor: pointer; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; border-radius: 50%; flex-shrink: 0; transition: all 0.2s; } .task-remove:hover { transform: scale(1.1); } .actions { display: flex; gap: 10px; margin-top: 10px; flex-wrap: wrap; } .status { margin-top: 12px; padding: 8px; border-radius: 6px; font-size: 13px; text-align: center; display: flex; align-items: center; justify-content: center; gap: 6px; border: 1px solid; } .radio-group { display: flex; gap: 12px; margin-top: 6px; flex-wrap: wrap; } .radio-item { display: flex; align-items: center; gap: 5px; cursor: pointer; white-space: nowrap; } .radio-item input { margin: 0; width: 14px; height: 14px; } .checkbox-item { display: flex; align-items: center; gap: 6px; margin-top: 10px; cursor: pointer; font-size: 14px; } .checkbox-item input { width: 14px; height: 14px; } #target-url { margin-top: 10px; display: none; } .overflow-ellipsis { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } `); // 创建主题样式 function createThemeStyles() { const style = document.createElement('style'); style.id = 'timer-theme-styles'; style.textContent = ` /* 浅色主题 */ .timer-light { --bg-primary: #ffffff; --bg-secondary: #f8f9fa; --bg-tertiary: #ecf0f1; --text-primary: #2c3e50; --text-secondary: #7f8c8d; --border-color: #ddd; --border-dark: #ccc; --accent-color: #3498db; --accent-hover: #2980b9; --accent-light: rgba(52, 152, 219, 0.1); --danger-color: #e74c3c; --danger-light: rgba(231, 76, 60, 0.1); --shadow: 0 4px 20px rgba(0,0,0,0.15); } /* 深色主题 */ .timer-dark { --bg-primary: #1e1e1e; --bg-secondary: #2d2d2d; --bg-tertiary: #3a3a3a; --text-primary: #f0f0f0; --text-secondary: #ccc; --border-color: #444; --border-dark: #555; --accent-color: #3498db; --accent-hover: #2980b9; --accent-light: rgba(52, 152, 219, 0.2); --danger-color: #e74c3c; --danger-light: rgba(231, 76, 60, 0.2); --shadow: 0 4px 20px rgba(0,0,0,0.5); } /* 主题应用 */ .timer-theme { background: var(--bg-primary); color: var(--text-primary); border-color: var(--border-color); box-shadow: var(--shadow); } #timer-manager.timer-theme { border: 1px solid var(--border-color); } #timer-mini.timer-theme { background: var(--bg-secondary); color: var(--text-primary); } .timer-header.timer-theme { border-bottom-color: var(--border-color); } .timer-btn.timer-theme { background: var(--bg-secondary); color: var(--text-secondary); } .timer-btn.timer-theme:hover { background: var(--bg-tertiary); color: var(--text-primary); } .timer-tabs.timer-theme { background: var(--bg-secondary); } .timer-tab.timer-theme { color: var(--text-secondary); } .timer-tab.timer-theme.active { background: var(--accent-color); color: white; } .form-label.timer-theme { color: var(--text-primary); } .form-control.timer-theme { background: var(--bg-secondary); color: var(--text-primary); border-color: var(--border-color); } .form-control.timer-theme:focus { border-color: var(--accent-color); box-shadow: 0 0 0 3px var(--accent-light); } .time-label.timer-theme { color: var(--text-secondary); } .time-input.timer-theme { background: var(--bg-secondary); color: var(--text-primary); border-color: var(--border-color); } .time-input.timer-theme:focus { border-color: var(--accent-color); } .btn-primary.timer-theme { background: var(--accent-color); color: white; } .btn-primary.timer-theme:hover { background: var(--accent-hover); } .btn-secondary.timer-theme { background: var(--bg-secondary); color: var(--text-primary); border: 1px solid var(--border-color); } .btn-secondary.timer-theme:hover { background: var(--bg-tertiary); } .task-list.timer-theme { background: var(--bg-secondary); border-color: var(--border-color); } .task-item.timer-theme { border-bottom-color: var(--border-color); } .task-item.timer-theme:hover { background: var(--bg-tertiary); } .task-url.timer-theme { color: var(--text-primary); } .task-time.timer-theme { color: var(--text-secondary); } .task-remove.timer-theme { color: var(--danger-color); background: var(--bg-secondary); } .task-remove.timer-theme:hover { background: var(--danger-light); } .status.timer-theme { background: var(--bg-secondary); color: var(--text-secondary); border-color: var(--border-color); } .radio-item.timer-theme input, .checkbox-item.timer-theme input { accent-color: var(--accent-color); } `; document.head.appendChild(style); } createThemeStyles(); // 创建UI元素 function createUI(theme) { // 主界面 const mainUI = document.createElement('div'); mainUI.id = 'timer-manager'; mainUI.className = `timer-theme timer-${theme}`; mainUI.innerHTML = `
网页定时管理器
当前网页定时
多网址定时跳转
定时时长
小时
定时结束后操作
未开始定时
网址
停留时长(跳转后开始计时)
小时
未开始执行
`; // 最小化界面 const miniUI = document.createElement('div'); miniUI.id = 'timer-mini'; miniUI.className = `timer-theme timer-${theme}`; miniUI.innerHTML = ' 00:00:00'; document.body.appendChild(mainUI); document.body.appendChild(miniUI); // 定位UI(基于浏览器视口,不受网页布局影响) const mainPos = Storage.getPos('timer_manager', window.innerWidth - 410, 20); mainUI.style.left = `${mainPos.x}px`; mainUI.style.top = `${mainPos.y}px`; const miniPos = Storage.getPos('timer_mini', window.innerWidth - 180, 20); miniUI.style.left = `${miniPos.x}px`; miniUI.style.top = `${miniPos.y}px`; return { mainUI, miniUI }; } // 定时器控制器 class TimerController { constructor(ui) { this.ui = ui; this.currentTimer = null; this.multiTimer = null; this.remainingTime = 0; this.currentTaskIndex = -1; this.tasks = Storage.getTasks(); this.isMinimized = true; this.isMultiRunning = false; this.lastUpdateTime = 0; this.isCurrentTimerRunning = false; this.miniTimeElement = document.getElementById('mini-time'); this.currentTheme = Storage.getTheme(); this.doubleClickDelay = 300; // 双击检测延迟 this.lastClickTime = 0; // 恢复多任务状态 const savedState = Storage.getMultiState(); if (savedState.running && savedState.index >= 0 && this.tasks.length > savedState.index) { this.isMultiRunning = true; this.currentTaskIndex = savedState.index; this.remainingTime = savedState.remaining; this.startTaskTimer(); } this.initEvents(); this.renderTasks(); this.fixInputFields(); } // 切换主题 toggleTheme() { const newTheme = this.currentTheme === 'light' ? 'dark' : 'light'; // 更新存储的主题 Storage.setTheme(newTheme); this.currentTheme = newTheme; // 更新所有主题相关元素的类 document.querySelectorAll('.timer-theme').forEach(el => { el.classList.remove(`timer-${this.currentTheme === 'light' ? 'dark' : 'light'}`); el.classList.add(`timer-${newTheme}`); }); // 更新主题图标 const themeIcon = document.querySelector('#theme-toggle i'); themeIcon.className = `fas ${newTheme === 'light' ? 'fa-moon' : 'fa-sun'}`; } // 修复输入框交互 fixInputFields() { const inputs = document.querySelectorAll('.time-input, .form-control'); inputs.forEach(input => { input.readOnly = false; input.disabled = false; input.style.pointerEvents = 'auto'; input.style.fontSize = input.classList.contains('time-input') ? '22px' : '14px'; input.addEventListener('focus', () => { input.style.borderColor = 'var(--accent-color)'; }); if (input.type === 'number') { input.addEventListener('input', (e) => { const value = parseInt(e.target.value) || 0; if (e.target.max) e.target.value = Math.min(value, parseInt(e.target.max)); if (e.target.min) e.target.value = Math.max(value, parseInt(e.target.min)); }); } }); } initEvents() { // 主题切换 document.getElementById('theme-toggle').addEventListener('click', (e) => { e.stopPropagation(); this.toggleTheme(); }); // 标签页切换 document.querySelectorAll('.timer-tab').forEach(tab => { tab.addEventListener('click', (e) => { e.stopPropagation(); const tabId = tab.getAttribute('data-tab'); document.querySelectorAll('.timer-tab').forEach(t => t.classList.remove('active')); document.querySelectorAll('.timer-tab-content').forEach(c => c.classList.remove('active')); tab.classList.add('active'); document.getElementById(`${tabId}-tab`).classList.add('active'); }); }); // 结束操作选择 const actionUrl = document.getElementById('action-url'); const targetUrl = document.getElementById('target-url'); document.getElementById('action-blank').addEventListener('change', () => { targetUrl.style.display = 'none'; }); actionUrl.addEventListener('change', () => { targetUrl.style.display = 'block'; }); // 当前网页定时控制 document.getElementById('start-current').addEventListener('click', () => this.startCurrentTimer()); document.getElementById('stop-current').addEventListener('click', () => this.stopCurrentTimer()); // 多网址任务控制 document.getElementById('add-task').addEventListener('click', () => this.addTask()); document.getElementById('clear-tasks').addEventListener('click', () => this.clearTasks()); document.getElementById('start-multi').addEventListener('click', () => this.startMultiTimer()); document.getElementById('stop-multi').addEventListener('click', () => this.stopMultiTimer()); // 最小化/恢复 - 单击最小化,双击恢复 document.getElementById('minimize-btn').addEventListener('click', () => this.minimize()); // 双击最小化界面打开UI this.ui.miniUI.addEventListener('click', (e) => { const now = Date.now(); // 检测双击(300ms内连续点击) if (now - this.lastClickTime < this.doubleClickDelay) { this.restore(); this.lastClickTime = 0; // 重置双击检测 } else { this.lastClickTime = now; } }); // 使UI可拖动 this.makeDraggable(this.ui.mainUI, 'timer_manager'); this.makeDraggable(this.ui.miniUI, 'timer_mini'); // 防止拖动事件影响输入框 const inputs = document.querySelectorAll('input, button, .timer-btn'); inputs.forEach(input => { input.addEventListener('mousedown', (e) => e.stopPropagation()); input.addEventListener('touchstart', (e) => e.stopPropagation()); }); } // 优化拖动性能 makeDraggable(element, storageKey) { let isDragging = false; let offsetX, offsetY; const originalZIndex = element.style.zIndex; let dragStartX, dragStartY; let lastDragTime = 0; element.addEventListener('pointerdown', startDrag); document.addEventListener('pointermove', drag); document.addEventListener('pointerup', endDrag); document.addEventListener('pointercancel', endDrag); function startDrag(e) { // 忽略按钮元素的拖动 if (e.target.closest('.timer-btn, button, input')) return; e.preventDefault(); const rect = element.getBoundingClientRect(); offsetX = e.clientX - rect.left; offsetY = e.clientY - rect.top; dragStartX = e.clientX; dragStartY = e.clientY; isDragging = true; element.style.zIndex = '9999999'; // 拖动时临时提升层级 element.style.transition = 'none'; lastDragTime = Date.now(); } function drag(e) { if (!isDragging) return; // 限制拖动频率,提升性能 const now = Date.now(); if (now - lastDragTime < 16) return; // 约60fps lastDragTime = now; requestAnimationFrame(() => { const x = e.clientX - offsetX; const y = e.clientY - offsetY; const maxX = window.innerWidth - element.offsetWidth; const maxY = window.innerHeight - element.offsetHeight; const constrainedX = Math.max(0, Math.min(x, maxX)); const constrainedY = Math.max(0, Math.min(y, maxY)); element.style.left = `${constrainedX}px`; element.style.top = `${constrainedY}px`; }); } function endDrag() { if (!isDragging) return; isDragging = false; element.style.zIndex = originalZIndex; element.style.transition = ''; // 只有明显移动后才保存位置(防止误触) Storage.savePos( storageKey, parseInt(element.style.left), parseInt(element.style.top) ); } } // 格式化时间 formatTime(seconds) { const h = Math.floor(seconds / 3600); const m = Math.floor((seconds % 3600) / 60); const s = seconds % 60; return [ h.toString().padStart(2, '0'), m.toString().padStart(2, '0'), s.toString().padStart(2, '0') ].join(':'); } // 开始当前网页定时 startCurrentTimer() { this.stopCurrentTimer(); this.stopMultiTimer(); const hours = Math.max(0, parseInt(document.getElementById('current-hours').value) || 0); const minutes = Math.max(0, Math.min(59, parseInt(document.getElementById('current-minutes').value) || 0)); const seconds = Math.max(0, Math.min(59, parseInt(document.getElementById('current-seconds').value) || 0)); this.remainingTime = hours * 3600 + minutes * 60 + seconds; if (this.remainingTime <= 0) { document.getElementById('current-status').innerHTML = ' 请设置有效的时间(大于0秒)'; return; } this.isCurrentTimerRunning = true; const timeStr = this.formatTime(this.remainingTime); document.getElementById('current-status').innerHTML = ` 定时中: ${timeStr}`; this.miniTimeElement.textContent = timeStr; this.lastUpdateTime = Date.now(); this.currentTimer = requestAnimationFrame(this.updateCurrentTimer.bind(this)); } // 更新当前定时器 updateCurrentTimer() { if (!this.currentTimer || !this.isCurrentTimerRunning) return; const now = Date.now(); const elapsed = Math.floor((now - this.lastUpdateTime) / 1000); if (elapsed >= 1) { this.remainingTime -= elapsed; this.lastUpdateTime = now; if (this.remainingTime <= 0) { this.stopCurrentTimer(); this.executeCurrentAction(); document.getElementById('current-status').innerHTML = ' 定时结束'; this.miniTimeElement.textContent = '00:00:00'; return; } const timeStr = this.formatTime(this.remainingTime); document.getElementById('current-status').innerHTML = ` 定时中: ${timeStr}`; this.miniTimeElement.textContent = timeStr; } this.currentTimer = requestAnimationFrame(this.updateCurrentTimer.bind(this)); } // 停止当前定时器 stopCurrentTimer() { if (this.currentTimer) { cancelAnimationFrame(this.currentTimer); this.currentTimer = null; } this.isCurrentTimerRunning = false; document.getElementById('current-status').innerHTML = ' 已停止'; } // 执行当前定时结束操作 executeCurrentAction() { const action = document.querySelector('input[name="end-action"]:checked').value; if (action === 'blank') { window.location.href = 'about:blank'; } else if (action === 'url') { const url = document.getElementById('target-url').value.trim(); if (url) { this.navigateToUrl(url, false); } } } // 添加多网址任务 addTask() { const url = document.getElementById('multi-url').value.trim(); if (!url) { document.getElementById('multi-status').innerHTML = ' 请输入有效的网址'; return; } const hours = Math.max(0, parseInt(document.getElementById('multi-hours').value) || 0); const minutes = Math.max(0, Math.min(59, parseInt(document.getElementById('multi-minutes').value) || 0)); const seconds = Math.max(0, Math.min(59, parseInt(document.getElementById('multi-seconds').value) || 0)); if (hours === 0 && minutes === 0 && seconds === 0) { document.getElementById('multi-status').innerHTML = ' 请设置有效的停留时间(大于0秒)'; return; } const noClose = document.getElementById('no-close').checked; this.tasks.push({ url, time: hours * 3600 + minutes * 60 + seconds, noClose }); Storage.saveTasks(this.tasks); this.renderTasks(); // 清空输入 document.getElementById('multi-url').value = ''; document.getElementById('multi-hours').value = '0'; document.getElementById('multi-minutes').value = '0'; document.getElementById('multi-seconds').value = '30'; document.getElementById('multi-status').innerHTML = ' 任务已添加'; } // 渲染任务列表 renderTasks() { const taskList = document.getElementById('task-list'); taskList.innerHTML = ''; if (this.tasks.length === 0) { taskList.innerHTML = '
任务列表为空
'; return; } this.tasks.forEach((task, index) => { const taskItem = document.createElement('div'); taskItem.className = 'task-item timer-theme'; taskItem.innerHTML = `
${task.url}
${this.formatTime(task.time)}
`; taskList.appendChild(taskItem); }); // 添加删除事件 document.querySelectorAll('.task-remove').forEach(btn => { btn.addEventListener('click', (e) => { const index = parseInt(e.currentTarget.getAttribute('data-index')); this.tasks.splice(index, 1); Storage.saveTasks(this.tasks); this.renderTasks(); }); }); } // 清空任务列表 clearTasks() { this.tasks = []; Storage.saveTasks(this.tasks); this.renderTasks(); document.getElementById('multi-status').innerHTML = ' 任务列表已清空'; } // 开始多网址定时跳转 startMultiTimer() { this.stopCurrentTimer(); this.stopMultiTimer(); if (this.tasks.length === 0) { document.getElementById('multi-status').innerHTML = ' 任务列表为空'; return; } this.isMultiRunning = true; this.currentTaskIndex = 0; this.navigateToCurrentTaskUrl(); } // 跳转到当前任务的URL navigateToCurrentTaskUrl() { if (this.currentTaskIndex >= this.tasks.length || !this.isMultiRunning) { this.finishMultiTasks(); return; } const task = this.tasks[this.currentTaskIndex]; document.getElementById('multi-status').innerHTML = ` 正在跳转到: ${task.url}(第 ${this.currentTaskIndex + 1}/${this.tasks.length} 个任务)`; // 保存状态 Storage.saveMultiState({ running: this.isMultiRunning, index: this.currentTaskIndex, remaining: task.time }); // 执行跳转 this.navigateToUrl(task.url, task.noClose, () => { if (task.noClose) { this.startTaskTimer(); } }); } // 开始当前任务的倒计时 startTaskTimer() { if (this.currentTaskIndex >= this.tasks.length || !this.isMultiRunning) { this.finishMultiTasks(); return; } const task = this.tasks[this.currentTaskIndex]; this.remainingTime = this.remainingTime > 0 ? this.remainingTime : task.time; const timeStr = this.formatTime(this.remainingTime); document.getElementById('multi-status').innerHTML = ` 停留中: ${timeStr}(第 ${this.currentTaskIndex + 1}/${this.tasks.length} 个任务)`; this.miniTimeElement.textContent = timeStr; Storage.saveMultiState({ running: this.isMultiRunning, index: this.currentTaskIndex, remaining: this.remainingTime }); this.lastUpdateTime = Date.now(); this.multiTimer = requestAnimationFrame(this.updateMultiTimer.bind(this)); } // 更新多任务定时器 updateMultiTimer() { if (!this.multiTimer || !this.isMultiRunning) return; const now = Date.now(); const elapsed = Math.floor((now - this.lastUpdateTime) / 1000); if (elapsed >= 1) { this.remainingTime -= elapsed; this.lastUpdateTime = now; Storage.saveMultiState({ running: this.isMultiRunning, index: this.currentTaskIndex, remaining: this.remainingTime }); if (this.remainingTime <= 0) { cancelAnimationFrame(this.multiTimer); this.multiTimer = null; this.currentTaskIndex++; this.navigateToCurrentTaskUrl(); return; } const timeStr = this.formatTime(this.remainingTime); document.getElementById('multi-status').innerHTML = ` 停留中: ${timeStr}(第 ${this.currentTaskIndex + 1}/${this.tasks.length} 个任务)`; this.miniTimeElement.textContent = timeStr; } this.multiTimer = requestAnimationFrame(this.updateMultiTimer.bind(this)); } // 完成所有多任务 finishMultiTasks() { document.getElementById('multi-status').innerHTML = ' 所有任务执行完毕'; this.miniTimeElement.textContent = '00:00:00'; this.isMultiRunning = false; Storage.saveMultiState({ running: false, index: 0, remaining: 0 }); } // 跳转至URL navigateToUrl(url, noClose = false, callback) { try { let targetUrl = url; // 简单补全协议 if (!targetUrl.startsWith('http://') && !targetUrl.startsWith('https://')) { targetUrl = 'https://' + targetUrl; } if (noClose) { const newTab = GM_openInTab(targetUrl, { active: true }); if (callback) callback(); } else { window.location.href = targetUrl; } } catch (e) { console.error('跳转失败,尝试备用方法:', e); if (noClose) { window.open(url, '_blank'); if (callback) callback(); } else { window.location.href = url; } } } // 停止多任务定时器 stopMultiTimer() { if (this.multiTimer) { cancelAnimationFrame(this.multiTimer); this.multiTimer = null; } this.isMultiRunning = false; document.getElementById('multi-status').innerHTML = ' 已停止'; this.miniTimeElement.textContent = '00:00:00'; Storage.saveMultiState({ running: false, index: 0, remaining: 0 }); } // 最小化 minimize() { this.ui.mainUI.style.display = 'none'; this.ui.miniUI.style.display = 'flex'; this.isMinimized = true; } // 恢复 restore() { this.ui.mainUI.style.display = 'block'; this.ui.miniUI.style.display = 'none'; this.isMinimized = false; } } // 初始化应用 function initApp() { const theme = Storage.getTheme(); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { const ui = createUI(theme); new TimerController(ui); }); } else { const ui = createUI(theme); const controller = new TimerController(ui); controller.minimize(); } } // 添加菜单命令 GM_registerMenuCommand("显示定时管理器", () => { const miniUI = document.getElementById('timer-mini'); if (miniUI) { // 模拟双击打开 miniUI.click(); setTimeout(() => miniUI.click(), 100); } else { const theme = Storage.getTheme(); const ui = createUI(theme); const controller = new TimerController(ui); controller.restore(); } }); initApp(); })();