Greasy Fork

Greasy Fork is available in English.

页面时间追踪器及摘要与饼图

在标题栏中显示页面上的花费时间,按域名分组,显示摘要并包括百分比的饼图,并允许删除数据

当前为 2024-07-08 提交的版本,查看 最新版本

// ==UserScript==
// @name         页面时间追踪器及摘要与饼图
// @name:zh      页面时间追踪器及摘要与饼图
// @name:en      Page Time Tracker with Summary and Pie Chart
// @namespace    http://tampermonkey.net/
// @license      MIT
// @version      1.6
// @description  在标题栏中显示页面上的花费时间,按域名分组,显示摘要并包括百分比的饼图,并允许删除数据
// @description:en  Track and display time spent on the page in the title bar, group by domain, show a summary with a pie chart including percentages, and allow data deletion
// @author       lbihhe
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_openInTab
// @grant        GM_registerMenuCommand
// ==/UserScript==

(function() {
    'use strict';

    // 初始化开始时间和原始标题
    const startTime = new Date();
    const originalTitle = document.title;

    // 格式化时间为分钟和秒
    function formatTimeMS(seconds) {
        const minutes = Math.floor(seconds / 60);
        const remainingSeconds = seconds % 60;
        return `${minutes}m ${remainingSeconds}s`;
    }

    // 更新标题栏中的时间
    function updateTitle() {
        const currentTime = new Date();
        const timeSpent = Math.floor((currentTime - startTime) / 1000);
        const formattedTime = formatTimeMS(timeSpent);
        document.title = `${originalTitle} - ${formattedTime}`;
    }

    // 存储访问数据
    function storeVisit() {
        const endTime = new Date();
        const timeSpent = Math.floor((endTime - startTime) / 1000);
        const pageData = {
            url: window.location.href,
            title: originalTitle,
            timeSpent: timeSpent,
            date: startTime.toDateString()
        };

        let visits = GM_getValue('pageVisits', []);
        visits.push(pageData);
        GM_setValue('pageVisits', visits);
    }

    // 获取域名
    function getDomain(url) {
        const a = document.createElement('a');
        a.href = url;
        return a.hostname;
    }

    // 清除旧数据(仅保留当天的数据)
    function clearOldData() {
        const today = new Date().toDateString();
        let visits = GM_getValue('pageVisits', []);
        visits = visits.filter(visit => visit.date === today);
        GM_setValue('pageVisits', visits);
    }

    // 删除所有数据
    function deleteAllData() {
        GM_setValue('pageVisits', []);
        alert('所有访问数据已被删除。');
    }

    // 每天清除一次旧数据
    const lastClearTime = GM_getValue('lastClearTime', '');
    const today = new Date().toDateString();
    if (lastClearTime !== today) {
        clearOldData();
        GM_setValue('lastClearTime', today);
    }

    // 在页面卸载时存储访问数据
    window.addEventListener('beforeunload', storeVisit);

    // 每秒更新一次标题栏中的时间
    setInterval(() => {
        updateTitle();
    }, 1000);

    // 注册菜单命令以显示每日摘要
    GM_registerMenuCommand('显示每日摘要', function() {
        const summaryPageContent = generateSummaryPage();
        const summaryWindow = window.open('', '_blank');
        summaryWindow.document.write(summaryPageContent);
        summaryWindow.document.close();
    });

    // 注册菜单命令以删除所有数据
    GM_registerMenuCommand('删除所有数据', deleteAllData);

    // 生成摘要页面
    function generateSummaryPage() {
        const visits = GM_getValue('pageVisits', []);
        const today = new Date().toDateString();
        const todayVisits = visits.filter(visit => visit.date === today);

        // 按域名分组访问数据
        const domainVisits = todayVisits.reduce((acc, visit) => {
            const domain = getDomain(visit.url);
            if (!acc[domain]) {
                acc[domain] = { domain, timeSpent: 0, pages: [] };
            }
            acc[domain].timeSpent += visit.timeSpent;
            acc[domain].pages.push(visit);
            return acc;
        }, {});

        const totalSeconds = Object.values(domainVisits).reduce((sum, domain) => sum + domain.timeSpent, 0);

        let summaryHTML = '<html><head><title>每日摘要</title><style>';
        summaryHTML += 'canvas { max-width: 800px; max-height: 600px; }';
        summaryHTML += '</style></head><body>';
        summaryHTML += '<h1>每日摘要</h1>';
        summaryHTML += '<table border="1"><tr><th>域名</th><th>花费时间</th><th>百分比</th></tr>';

        Object.values(domainVisits).forEach(domain => {
            const percentage = ((domain.timeSpent / totalSeconds) * 100).toFixed(2);
            summaryHTML += `<tr><td>${domain.domain}</td><td>${formatTimeMS(domain.timeSpent)}</td><td>${percentage}%</td></tr>`;
        });

        summaryHTML += '</table><canvas id="timeChart" width="800" height="600"></canvas>';
        summaryHTML += '<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>';
        summaryHTML += '<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels"></script>';
        summaryHTML += '<script>';
        summaryHTML += 'const ctx = document.getElementById("timeChart").getContext("2d");';
        summaryHTML += 'const data = {';
        summaryHTML += 'labels: ' + JSON.stringify(Object.keys(domainVisits)) + ',';
        summaryHTML += 'datasets: [{';
        summaryHTML += 'label: "花费时间",';
        summaryHTML += 'data: ' + JSON.stringify(Object.values(domainVisits).map(domain => domain.timeSpent)) + ',';
        summaryHTML += 'backgroundColor: ' + JSON.stringify(Object.values(domainVisits).map((_, i) => `hsl(${i * 360 / Object.values(domainVisits).length}, 100%, 75%)`)) + ',';
        summaryHTML += '}]';
        summaryHTML += '};';
        summaryHTML += 'const config = { type: "pie", data: data, options: { plugins: { datalabels: { formatter: (value, ctx) => {';
        summaryHTML += 'let total = ctx.chart.data.datasets[0].data.reduce((a, b) => a + b, 0);';
        summaryHTML += 'let percentage = (value / total * 100).toFixed(2) + "%";';
        summaryHTML += 'return percentage; } } } } };';
        summaryHTML += 'Chart.register(ChartDataLabels);';
        summaryHTML += 'new Chart(ctx, config);';
        summaryHTML += '</script>';
        summaryHTML += '</body></html>';

        return summaryHTML;
    }
})();