微信读书30天阅读挑战打卡记录(本地版)

记录30天阅读挑战的打卡情况,自动统计阅读时长,数据保存在本地

目前为 2025-03-08 提交的版本。查看 最新版本

// ==UserScript==
// @name         微信读书30天阅读挑战打卡记录(本地版)
// @namespace    http://tampermonkey.net/
// @version      0.13
// @description  记录30天阅读挑战的打卡情况,自动统计阅读时长,数据保存在本地
// @icon         https://i.miji.bid/2025/03/08/990e81d6e8ebc90d181e091cc0c99699.jpeg
// @author       Charlie
// @match        https://weread.qq.com/web/reader/*
// @grant        GM_registerMenuCommand
// @run-at       document-end
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    console.log('30天阅读挑战脚本加载中...');

    // 总天数
    const TOTAL_DAYS = 30;
    let challengeData = JSON.parse(localStorage.getItem('challengeData')) || {
        startDate: new Date().toISOString().split('T')[0],
        completedDays: Array(TOTAL_DAYS).fill(false),
        dailyReadingTimes: Array(TOTAL_DAYS).fill(0)
    };

    // 记录页面打开时间
    let startTime = Date.now();

    // 统计阅读时长并保存
    function recordReadingTime() {
        const endTime = Date.now();
        const sessionTime = (endTime - startTime) / 1000 / 60; // 转换为分钟
        const todayIndex = Math.min(new Date().getDate() - new Date(challengeData.startDate).getDate(), TOTAL_DAYS - 1);

        // 累加当天的阅读时长
        challengeData.dailyReadingTimes[todayIndex] = (challengeData.dailyReadingTimes[todayIndex] || 0) + sessionTime;

        // 根据时长更新完成状态
        if (challengeData.dailyReadingTimes[todayIndex] >= 30) {
            challengeData.completedDays[todayIndex] = true;
        } else {
            challengeData.completedDays[todayIndex] = false;
        }

        localStorage.setItem('challengeData', JSON.stringify(challengeData));
        console.log(`本次阅读时长:${sessionTime.toFixed(2)}分钟,今日总时长:${challengeData.dailyReadingTimes[todayIndex].toFixed(2)}分钟`);
    }

    // 页面关闭或刷新时记录时长
    window.addEventListener('beforeunload', recordReadingTime);

    // 定时更新 UI(每分钟刷新一次)
    setInterval(() => {
        recordReadingTime();
        startTime = Date.now(); // 重置开始时间
        createChallengeUI();
    }, 60 * 1000); // 每分钟记录一次

    // 创建容器
    function createChallengeUI() {
        // 移除已有UI
        let existingUI = document.getElementById('challenge-container');
        if (existingUI) {
            existingUI.remove();
            console.log('移除旧UI');
        }

        const container = document.createElement('div');
        container.id = 'challenge-container';
        container.style.cssText = `
            position: fixed;
            top: 10px;
            left: 10px;
            background: rgba(255, 255, 255, 0.7);
            backdrop-filter: blur(10px);
            -webkit-backdrop-filter: blur(10px);
            color: #333;
            padding: 12px;
            z-index: 2147483648;
            width: 190px;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            border: 1px solid rgba(221, 221, 221, 0.5);
            border-radius: 8px;
        `;

        // 标题
        const title = document.createElement('h1');
        title.textContent = '30天阅读挑战';
        title.style.cssText = 'font-size: 1.1em; margin: 0 0 8px 0; color: #333;';

        // 网格
        const grid = document.createElement('div');
        grid.style.cssText = 'display: grid; grid-template-columns: repeat(6, 1fr); gap: 4px;';
        for (let i = 0; i < TOTAL_DAYS; i++) {
            const day = document.createElement('div');
            day.classList.add('day');
            day.style.cssText = `
                width: 24px;
                height: 24px;
                background-color: ${challengeData.completedDays[i] ? '#4a90e2' : '#ebedf0'};
                border-radius: 4px;
            `;
            grid.appendChild(day);
        }

        // 今日阅读时长显示
        const todayIndex = Math.min(new Date().getDate() - new Date(challengeData.startDate).getDate(), TOTAL_DAYS - 1);
        const readingTimeDisplay = document.createElement('div');
        readingTimeDisplay.id = 'reading-time-display';
        readingTimeDisplay.textContent = `今日已阅读:${challengeData.dailyReadingTimes[todayIndex].toFixed(2)}分钟`;
        readingTimeDisplay.style.cssText = 'font-size: 0.85em; color: #333; margin-top: 8px; text-align: center;';

        container.appendChild(title);
        container.appendChild(grid);
        container.appendChild(readingTimeDisplay);

        document.documentElement.appendChild(container);
        console.log('UI创建完成,附加到documentElement');
    }

    // 重置挑战
    function resetChallenge() {
        if (confirm('确定要重置挑战吗?所有打卡记录将清空!')) {
            challengeData = {
                startDate: new Date().toISOString().split('T')[0],
                completedDays: Array(TOTAL_DAYS).fill(false),
                dailyReadingTimes: Array(TOTAL_DAYS).fill(0)
            };
            localStorage.setItem('challengeData', JSON.stringify(challengeData));
            createChallengeUI();
            console.log('挑战已重置');
        }
    }

    // 注册(不可用)Tampermonkey菜单
    GM_registerMenuCommand('重置挑战', resetChallenge);

    // 延迟初始化
    function initialize() {
        if (!document.body) {
            console.log('body尚未加载,等待1秒后重试...');
            setTimeout(initialize, 1000);
            return;
        }

        console.log('body已加载,延迟2秒初始化UI');
        setTimeout(() => {
            createChallengeUI();
            const observer = new MutationObserver((mutations) => {
                if (!document.getElementById('challenge-container')) {
                    console.log('检测到UI丢失,重新创建...');
                    createChallengeUI();
                }
            });
            observer.observe(document.documentElement, { childList: true, subtree: true });
        }, 2000);
    }

    // 启动
    initialize();
    console.log('脚本初始化完成');
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址