定时提醒工具

提供定时提醒、倒计时功能,并支持自定义提醒事件。用户可以设置每日重复的提醒事件,每个事件在同一时间只会提醒一次;倒计时功能允许用户设置一个倒计时,时间到后会提醒用户。

当前为 2024-09-16 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         定时提醒工具
// @namespace    http://tampermonkey.net/
// @version      1.22
// @description  提供定时提醒、倒计时功能,并支持自定义提醒事件。用户可以设置每日重复的提醒事件,每个事件在同一时间只会提醒一次;倒计时功能允许用户设置一个倒计时,时间到后会提醒用户。
// @author       wll
// @match        *://*/*
// @grant        GM_notification
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @license      AGPL-3.0-or-later
// ==/UserScript==

(function() {
    'use strict';

    const defaultReminders = [
        { event: '喝水', time: '09:00', type: 'daily' },
        { event: '休息一下', time: '10:30', type: 'daily' },
        { event: '吃午饭', time: '12:00', type: 'daily' },
        { event: '喝水', time: '14:00', type: 'daily' },
        { event: '休息一下', time: '15:30', type: 'daily' },
        { event: '吃晚饭', time: '18:00', type: 'daily' },
        { event: '运动', time: '19:00', type: 'daily' },
    ];

    function loadReminders() {
        const savedReminders = GM_getValue('reminders');
        return savedReminders || defaultReminders;
    }

    function saveReminders(reminders) {
        GM_setValue('reminders', reminders);
    }

    function loadButtonPosition() {
        const position = GM_getValue('buttonPosition');
        return position || { top: '10px', left: '10px' };
    }

    function saveButtonPosition(position) {
        GM_setValue('buttonPosition', position);
    }

    function loadButtonVisibility() {
        return GM_getValue('buttonVisibility', true);
    }

    function saveButtonVisibility(visibility) {
        GM_setValue('buttonVisibility', visibility);
    }

    const settingsHTML = `
        <div id="reminder-settings" style="display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 350px; background: white; border: 1px solid #ccc; padding: 20px; z-index: 10000; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);">
            <div style="display: flex; justify-content: space-between; align-items: center;">
                <h3 style="margin: 0;">定时提醒设置</h3>
                <button id="close-settings" style="background: none; border: none; font-size: 18px; cursor: pointer;">&times;</button>
            </div>
            <div id="reminder-list" style="max-height: 200px; overflow-y: auto; margin-top: 10px; padding-right: 15px;"></div>
            <div style="text-align: center; margin-top: 10px;">
                <button id="add-reminder">添加提醒</button>
            </div>
            <hr>
            <h3>倒计时功能</h3>
            <div style="display: flex; justify-content: space-between; align-items: center;">
                <input type="number" id="countdown-minutes" placeholder="分钟数" style="width: 80px;">
                <button id="start-countdown">开始倒计时</button>
            </div>
        </div>
        <button id="open-settings" style="position: fixed; width: 60px; height: 30px; z-index: 10000;">设置</button>
    `;

    document.body.insertAdjacentHTML('beforeend', settingsHTML);

    const buttonPosition = loadButtonPosition();
    const openSettingsButton = document.getElementById('open-settings');
    openSettingsButton.style.top = buttonPosition.top;
    openSettingsButton.style.left = buttonPosition.left;

    const buttonVisibility = loadButtonVisibility();
    openSettingsButton.style.display = buttonVisibility ? 'block' : 'none';

    GM_addStyle(`
        #reminder-settings input, #reminder-settings select, #reminder-settings button {
            margin: 5px 0;
        }
        #reminder-list div {
            display: flex;
            align-items: center;
            margin-bottom: 5px;
        }
        #reminder-list div .event, #reminder-list div .time {
            margin-right: 10px;
            flex-grow: 1;
        }
        #reminder-list div .type {
            flex-grow: 2;
            margin-right: 10px;
        }
        #reminder-list div button {
            flex-grow: 0;
        }
    `);

    function loadReminderList() {
        const reminders = loadReminders();
        const reminderList = document.getElementById('reminder-list');
        reminderList.innerHTML = '';
        reminders.forEach((reminder, index) => {
            const reminderDiv = document.createElement('div');
            reminderDiv.innerHTML = `
                <input type="text" value="${reminder.event}" data-index="${index}" class="event" style="width: 80px;">
                <input type="time" value="${reminder.time}" data-index="${index}" class="time" style="width: 80px;">
                <select data-index="${index}" class="type" style="width: 100px;">
                    <option value="once" ${reminder.type === 'once' ? 'selected' : ''}>提醒一次</option>
                    <option value="daily" ${reminder.type === 'daily' ? 'selected' : ''}>每天提醒</option>
                </select>
                <button data-index="${index}" class="delete-reminder" style="width: 50px;">删除</button>
            `;
            reminderList.appendChild(reminderDiv);
        });
    }

    function autoSaveReminder() {
        const reminders = [];
        document.querySelectorAll('#reminder-list div').forEach(div => {
            const event = div.querySelector('.event').value;
            const time = div.querySelector('.time').value;
            const type = div.querySelector('.type').value;
            if (event.trim() !== '') {
                reminders.push({ event, time, type });
            }
        });
        saveReminders(reminders);
    }

    function showSettings() {
        document.getElementById('reminder-settings').style.display = 'block';
        loadReminderList();
    }

    function hideSettings() {
        document.getElementById('reminder-settings').style.display = 'none';
    }

    document.getElementById('open-settings').addEventListener('click', (e) => {
        e.stopPropagation();
        const settingsDiv = document.getElementById('reminder-settings');
        if (settingsDiv.style.display === 'none') {
            showSettings();
        } else {
            hideSettings();
        }
    });

    document.getElementById('close-settings').addEventListener('click', hideSettings);

    window.addEventListener('click', (event) => {
        const settingsDiv = document.getElementById('reminder-settings');
        if (settingsDiv.style.display !== 'none' && !settingsDiv.contains(event.target) && event.target !== openSettingsButton) {
            hideSettings();
        }
    });

    GM_registerMenuCommand('显示/隐藏提醒设置按钮', () => {
        const currentVisibility = openSettingsButton.style.display === 'block';
        openSettingsButton.style.display = currentVisibility ? 'none' : 'block';
        saveButtonVisibility(!currentVisibility);
    });

    function makeElementDraggable(el) {
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
        el.onmousedown = dragMouseDown;

        function dragMouseDown(e) {
            e = e || window.event;
            e.preventDefault();
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDragElement;
            document.onmousemove = elementDrag;
        }

        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;
            el.style.top = (el.offsetTop - pos2) + "px";
            el.style.left = (el.offsetLeft - pos1) + "px";

            saveButtonPosition({ top: el.style.top, left: el.style.left });
        }

        function closeDragElement() {
            document.onmouseup = null;
            document.onmousemove = null;
        }
    }

    makeElementDraggable(openSettingsButton);

    document.getElementById('add-reminder').addEventListener('click', () => {
        const reminders = loadReminders();
        const now = new Date();
        const currentTime = now.toTimeString().substr(0, 5);
        reminders.push({ event: '', time: currentTime, type: 'daily' });
        saveReminders(reminders);
        loadReminderList();
    });

    document.getElementById('reminder-list').addEventListener('input', autoSaveReminder);

    document.getElementById('reminder-list').addEventListener('click', (e) => {
        if (e.target.classList.contains('delete-reminder')) {
            e.stopPropagation();
            const index = e.target.getAttribute('data-index');
            const reminders = loadReminders();
            reminders.splice(index, 1);
            saveReminders(reminders);
            loadReminderList();
        }
    });

    const notifiedTimes = new Set();

    function checkAndNotify() {
        const now = new Date();
        const currentTime = now.toTimeString().substr(0, 5);
        let reminders = loadReminders();

        reminders.forEach(reminder => {
            if (currentTime === reminder.time && !notifiedTimes.has(reminder.time + reminder.event)) {
                if (reminder.event.trim() !== '') {
                    GM_notification({
                        title: '定时提醒',
                        text: `该${reminder.event}了!`,
                        timeout: 10000,
                        onclick: () => {
                            console.log(`你点击了${reminder.event}提醒`);
                        }
                    });
                    notifiedTimes.add(reminder.time + reminder.event);
                }
            }
        });

        // Remove entries from notifiedTimes set to avoid memory issues
        notifiedTimes.forEach(key => {
            if (!reminders.some(reminder => reminder.time + reminder.event === key && reminder.time === currentTime)) {
                notifiedTimes.delete(key);
            }
        });
    }

    setInterval(() => {
        notifiedTimes.clear();
    }, 60000);

    document.getElementById('start-countdown').addEventListener('click', () => {
        const minutes = parseInt(document.getElementById('countdown-minutes').value, 10);
        if (!isNaN(minutes) && minutes > 0) {
            GM_notification({
                title: '倒计时提醒',
                text: `倒计时${minutes}分钟开始!`,
                timeout: 5000
            });
            hideSettings();
            setTimeout(() => {
                GM_notification({
                    title: '倒计时提醒',
                    text: `倒计时${minutes}分钟结束!`,
                    timeout: 10000
                });
            }, minutes * 60000);
        } else {
            alert('请输入有效的分钟数');
        }
    });

    setInterval(checkAndNotify, 1000);
    checkAndNotify();
})();