Greasy Fork

Greasy Fork is available in English.

做题计划管理器

洛谷做题计划怎么不支持所有OJ?哦咦game不是这么玩的!你应该写一个支持导入导出做题计划管理脚本,后面忘了。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         做题计划管理器
// @namespace    http://tampermonkey.net/
// @version      1.7
// @description  洛谷做题计划怎么不支持所有OJ?哦咦game不是这么玩的!你应该写一个支持导入导出做题计划管理脚本,后面忘了。
// @author       Nuclear_Fish_cyq
// @match        *://*/*
// @license        MIT
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_addValueChangeListener
// ==/UserScript==

(function() {
    'use strict';

    // 只在顶层窗口中运行,避免在iframe中重复创建
    if (window.self !== window.top) {
        return;
    }

    // 预定义颜色选项
    const colorOptions = [
        '#FE4C61', '#F39C11', '#FFC116', '#52C41A',
        '#3498DB', '#9D3DCF', '#0E1D69', '#BFBFBF'
    ];

    // 存储键名
    const STORAGE_KEY = 'problemPlanner_data';
    const COUNT_KEY = 'problemPlanner_completedCount';

    // 状态变量
    let problems = [];
    let completedCount = 0;
    let isPanelVisible = false;

    // 从GM存储加载数据(兼容旧版本)
    function loadData() {
        try {
            const savedData = GM_getValue(STORAGE_KEY);
            problems = savedData ? JSON.parse(savedData) : [];

            // 向下兼容:确保每个题目都有 understood 字段(默认为 false)
            problems = problems.map(p => ({
                ...p,
                understood: p.understood || false
            }));

            const savedCount = GM_getValue(COUNT_KEY);
            completedCount = savedCount || 0;
        } catch (e) {
            console.error('加载数据失败:', e);
            problems = [];
            completedCount = 0;
        }
    }

    // 保存数据到GM存储
    function saveData() {
        try {
            GM_setValue(STORAGE_KEY, JSON.stringify(problems));
            GM_setValue(COUNT_KEY, completedCount);
        } catch (e) {
            console.error('保存数据失败:', e);
        }
    }

    // 监听其他标签页的数据变化
    GM_addValueChangeListener(STORAGE_KEY, function(key, oldValue, newValue, remote) {
        if (remote) {
            try {
                problems = newValue ? JSON.parse(newValue) : [];
                // 向下兼容
                problems = problems.map(p => ({
                    ...p,
                    understood: p.understood || false
                }));
                if (isPanelVisible) {
                    renderProblems();
                }
                showSyncNotification();
            } catch (e) {
                console.error('同步数据解析失败:', e);
            }
        }
    });

    GM_addValueChangeListener(COUNT_KEY, function(key, oldValue, newValue, remote) {
        if (remote) {
            completedCount = newValue || 0;
            if (isPanelVisible) {
                updateCounters();
            }
        }
    });

    // 初始化加载数据
    loadData();

    // 显示同步提示
    function showSyncNotification() {
        let notification = document.querySelector('.sync-notification');
        if (!notification) {
            notification = document.createElement('div');
            notification.className = 'sync-notification';
            notification.style.cssText = `
                position: fixed;
                top: 10px;
                right: 10px;
                background: #3498DB;
                color: white;
                padding: 10px 15px;
                border-radius: 5px;
                font-family: 'Segoe UI', sans-serif;
                font-size: 14px;
                z-index: 1000000;
                box-shadow: 0 2px 10px rgba(0,0,0,0.2);
                opacity: 0;
                transform: translateY(-20px);
                transition: opacity 0.3s, transform 0.3s;
            `;
            document.body.appendChild(notification);
        }
        notification.textContent = '数据已同步更新';
        notification.style.opacity = '1';
        notification.style.transform = 'translateY(0)';
        setTimeout(() => {
            notification.style.opacity = '0';
            notification.style.transform = 'translateY(-20px)';
        }, 3000);
    }

    // 创建主样式
    const style = document.createElement('style');
    style.textContent = `
        .problem-planner-container {
            position: fixed;
            bottom: 20px;
            left: 20px;
            z-index: 999999;
            font-family: 'Segoe UI', sans-serif;
        }

        .problem-planner-button {
            width: 60px;
            height: 60px;
            border-radius: 50%;
            background: linear-gradient(135deg, #00d2ff, #3a7bd5);
            color: white;
            border: none;
            cursor: pointer;
            font-weight: bold;
            font-size: 20px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
            transition: transform 0.2s, box-shadow 0.2s;
            font-family: 'Segoe UI', sans-serif;
        }

        .problem-planner-button:hover {
            transform: scale(1.1);
            box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
        }

        .problem-planner-panel {
            position: absolute;
            bottom: 70px;
            left: 0;
            width: 420px;
            background: white;
            border-radius: 12px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
            display: none;
            overflow: hidden;
            font-family: 'Segoe UI', sans-serif;
        }

        .problem-planner-panel .panel-header {
            background: linear-gradient(135deg, #3a7bd5, #00d2ff);
            color: white;
            padding: 20px;
            text-align: center;
            font-weight: bold;
            font-size: 18px;
            position: relative;
        }

        .problem-planner-panel .add-problem-form {
            padding: 20px;
            border-bottom: 1px solid #eee;
        }

        .problem-planner-panel .form-group {
            margin-bottom: 15px;
        }

        .problem-planner-panel .form-group label {
            display: block;
            margin-bottom: 5px;
            font-weight: 600;
            color: #333;
        }

        .problem-planner-panel .form-input {
            width: 100%;
            padding: 10px;
            border: 2px solid #e0e0e0;
            border-radius: 6px;
            font-family: 'Segoe UI', sans-serif;
            font-size: 14px;
            box-sizing: border-box;
        }

        .problem-planner-panel .form-input:focus {
            outline: none;
            border-color: #3a7bd5;
        }

        .problem-planner-panel .color-selection {
            display: grid;
            grid-template-columns: repeat(8, 1fr);
            gap: 8px;
            margin-top: 10px;
        }

        .problem-planner-panel .color-option {
            width: 30px;
            height: 30px;
            border-radius: 6px;
            cursor: pointer;
            border: 2px solid transparent;
            transition: transform 0.2s, border-color 0.2s;
        }

        .problem-planner-panel .color-option:hover {
            transform: scale(1.1);
        }

        .problem-planner-panel .color-option.selected {
            border-color: #333 !important;
            transform: scale(1.1);
            box-shadow: 0 0 0 2px white, 0 0 0 4px #333 !important;
        }

        .problem-planner-panel .add-button {
            width: 100%;
            padding: 12px;
            background: linear-gradient(135deg, #52C41A, #3498DB);
            color: white;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-weight: bold;
            font-size: 16px;
            font-family: 'Segoe UI', sans-serif;
            transition: transform 0.2s;
        }

        .problem-planner-panel .add-button:hover {
            transform: translateY(-2px);
        }

        .problem-planner-panel .problems-list {
            max-height: 300px;
            overflow-y: auto;
            padding: 20px;
        }

        .problem-planner-panel .problem-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 12px;
            margin-bottom: 10px;
            background: #f8f9fa;
            border-radius: 8px;
            transition: transform 0.2s;
        }

        .problem-planner-panel .problem-item.understood {
            opacity: 0.8;
            background: #f0f0f0;
            border-left: 4px solid #FFC116;
        }

        .problem-planner-panel .problem-item:hover {
            transform: translateX(5px);
            background: #e9ecef;
        }

        .problem-planner-panel .problem-link {
            text-decoration: none;
            font-weight: bold;
            font-size: 14px;
            display: block;
            word-break: break-all;
            flex-grow: 1;
            margin-right: 10px;
        }

        .problem-planner-panel .understood-badge {
            font-size: 12px;
            color: #FFC116;
            margin-left: 5px;
            font-weight: normal;
        }

        .problem-planner-panel .problem-actions {
            display: flex;
            gap: 5px;
            flex-wrap: wrap;
            justify-content: flex-end;
            min-width: 150px;
        }

        .problem-planner-panel .giveup-btn {
            background: #BFBFBF;
            color: white;
            border: none;
            border-radius: 4px;
            width: 45px;
            height: 30px;
            cursor: pointer;
            font-weight: bold;
            font-family: 'Segoe UI', sans-serif;
            font-size: 12px;
            transition: background 0.2s;
        }

        .problem-planner-panel .giveup-btn:hover {
            background: #a0a0a0;
        }

        .problem-planner-panel .understand-btn {
            background: #FFC116;
            color: white;
            border: none;
            border-radius: 4px;
            width: 45px;
            height: 30px;
            cursor: pointer;
            font-weight: bold;
            font-family: 'Segoe UI', sans-serif;
            font-size: 12px;
            transition: background 0.2s;
        }

        .problem-planner-panel .understand-btn:hover {
            background: #e6a800;
        }

        .problem-planner-panel .complete-btn {
            background: #52C41A;
            color: white;
            border: none;
            border-radius: 4px;
            width: 45px;
            height: 30px;
            cursor: pointer;
            font-weight: bold;
            font-family: 'Segoe UI', sans-serif;
            font-size: 12px;
            transition: background 0.2s;
        }

        .problem-planner-panel .complete-btn:hover {
            background: #3da814;
        }

        .problem-planner-panel .panel-footer {
            padding: 15px 20px;
            background: #f8f9fa;
            border-top: 1px solid #eee;
            display: flex;
            justify-content: space-between;
            align-items: center;
            font-weight: 600;
        }

        .problem-planner-panel .stats {
            display: flex;
            flex-direction: column;
            gap: 5px;
        }

        .problem-planner-panel .stat-item {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .problem-planner-panel .stat-label {
            color: #666;
        }

        .problem-planner-panel .stat-value {
            color: #3a7bd5;
            font-size: 18px;
            font-weight: bold;
        }

        .problem-planner-panel .completed-count {
            color: #52C41A;
        }

        .problem-planner-panel .action-buttons {
            display: flex;
            gap: 8px;
        }

        .problem-planner-panel .clear-btn {
            background: #FE4C61;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 6px;
            cursor: pointer;
            font-weight: bold;
            font-family: 'Segoe UI', sans-serif;
            transition: background 0.2s;
        }

        .problem-planner-panel .clear-btn:hover {
            background: #e43a4d;
        }

        .problem-planner-panel .export-btn {
            background: #3498DB;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 6px;
            cursor: pointer;
            font-weight: bold;
            font-family: 'Segoe UI', sans-serif;
            transition: background 0.2s;
        }

        .problem-planner-panel .export-btn:hover {
            background: #2980b9;
        }

        .problem-planner-panel .import-btn {
            background: #9D3DCF;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 6px;
            cursor: pointer;
            font-weight: bold;
            font-family: 'Segoe UI', sans-serif;
            transition: background 0.2s;
        }

        .problem-planner-panel .import-btn:hover {
            background: #8e44ad;
        }

        .problem-planner-panel .empty-message {
            text-align: center;
            color: #999;
            padding: 40px 20px;
            font-style: italic;
        }

        .problem-planner-panel .sync-badge {
            position: absolute;
            right: 20px;
            top: 50%;
            transform: translateY(-50%);
            background: rgba(255, 255, 255, 0.2);
            padding: 2px 8px;
            border-radius: 10px;
            font-size: 12px;
            display: flex;
            align-items: center;
            gap: 5px;
        }

        .problem-planner-panel .sync-badge::before {
            content: '🔄';
        }

        .problem-planner-panel .current-url-hint {
            font-size: 12px;
            color: #3498DB;
            margin-top: 5px;
            display: flex;
            align-items: center;
            gap: 5px;
        }

        .problem-planner-panel .current-url-hint::before {
            content: '📌';
        }

        .problem-planner-panel .use-current-btn {
            background: transparent;
            border: 1px solid #3498DB;
            color: #3498DB;
            border-radius: 4px;
            padding: 2px 8px;
            font-size: 12px;
            cursor: pointer;
            margin-left: 5px;
            transition: all 0.2s;
        }

        .problem-planner-panel .use-current-btn:hover {
            background: #3498DB;
            color: white;
        }

        .problem-planner-panel .file-input {
            display: none;
        }

        .problem-planner-panel .backup-section {
            padding: 15px 20px;
            border-top: 1px solid #eee;
            background: #f8f9fa;
        }

        .problem-planner-panel .backup-title {
            font-weight: bold;
            margin-bottom: 10px;
            color: #333;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .problem-planner-panel .backup-title::before {
            content: '💾';
        }

        .problem-planner-panel .backup-buttons {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
        }

        .problem-planner-panel .backup-notice {
            font-size: 12px;
            color: #666;
            margin-top: 10px;
            font-style: italic;
        }

        .problem-planner-panel .download-link {
            display: none;
        }

        .problem-planner-panel .data-preview {
            background: #f8f9fa;
            border: 1px solid #ddd;
            border-radius: 6px;
            padding: 15px;
            margin-top: 15px;
            max-height: 200px;
            overflow-y: auto;
            font-family: monospace;
            font-size: 12px;
            display: none;
        }

        .problem-planner-panel .data-preview.show {
            display: block;
        }
    `;
    document.head.appendChild(style);

    // 创建容器
    const container = document.createElement('div');
    container.className = 'problem-planner-container';

    // 创建主按钮
    const mainButton = document.createElement('button');
    mainButton.className = 'problem-planner-button';
    mainButton.textContent = '题';
    mainButton.title = '打开做题计划(跨网站同步)';

    // 创建面板
    const panel = document.createElement('div');
    panel.className = 'problem-planner-panel';

    // 面板HTML结构(修改统计区)
    panel.innerHTML = `
        <div class="panel-header">
            做题计划
            <span class="sync-badge">跨站同步</span>
        </div>
        <div class="add-problem-form">
            <div class="form-group">
                <label for="problem-url">题目网址</label>
                <input type="text" id="problem-url" class="form-input" placeholder="https://example.com/problem/123">
                <div class="current-url-hint">
                    当前页面: ${window.location.href}
                    <button class="use-current-btn" id="use-current-url">使用当前网址</button>
                </div>
            </div>
            <div class="form-group">
                <label for="problem-name">题目名称</label>
                <input type="text" id="problem-name" class="form-input" placeholder="请输入题目名称">
            </div>
            <div class="form-group">
                <label>选择颜色</label>
                <div class="color-selection" id="color-selection"></div>
            </div>
            <button class="add-button" id="add-problem">添加题目到计划</button>
        </div>
        <div class="problems-list" id="problems-list"></div>
        <div class="backup-section">
            <div class="backup-title">数据备份</div>
            <div class="backup-buttons">
                <button class="export-btn" id="export-data">导出数据</button>
                <button class="import-btn" id="import-data">导入数据</button>
                <button class="clear-btn" id="clear-all">清空数据</button>
            </div>
            <div class="backup-notice">
                导出的数据包含所有题目列表和完成统计,可用于备份或迁移
            </div>
            <div class="data-preview" id="data-preview"></div>
        </div>
        <div class="panel-footer">
            <div class="stats">
                <div class="stat-item">
                    <span class="stat-label">未理解:</span>
                    <span class="stat-value" id="not-understood-count">0</span>
                </div>
                <div class="stat-item">
                    <span class="stat-label">已理解:</span>
                    <span class="stat-value" id="understood-count">0</span>
                </div>
                <div class="stat-item">
                    <span class="stat-label">已完成:</span>
                    <span class="stat-value completed-count" id="completed-count">0</span>
                </div>
            </div>
            <div class="action-buttons">
                <button class="export-btn" id="export-btn-small">导出</button>
                <button class="import-btn" id="import-btn-small">导入</button>
                <button class="clear-btn" id="clear-btn-small">清空</button>
            </div>
        </div>
        <input type="file" id="file-input" class="file-input" accept=".json,application/json">
        <a id="download-link" class="download-link"></a>
    `;

    container.appendChild(mainButton);
    container.appendChild(panel);
    document.body.appendChild(container);

    // 初始化颜色选择器
    const colorSelection = panel.querySelector('#color-selection');
    let selectedColor = colorOptions[0];

    colorOptions.forEach(color => {
        const colorDiv = document.createElement('div');
        colorDiv.className = 'color-option';
        colorDiv.style.backgroundColor = color;
        colorDiv.title = color;

        if (color === selectedColor) {
            colorDiv.classList.add('selected');
        }

        colorDiv.addEventListener('click', () => {
            panel.querySelectorAll('.color-option').forEach(opt => {
                opt.classList.remove('selected');
            });
            colorDiv.classList.add('selected');
            selectedColor = color;
        });

        colorSelection.appendChild(colorDiv);
    });

    // 更新计数器
    function updateCounters() {
        const notUnderstood = problems.filter(p => !p.understood).length;
        const understood = problems.filter(p => p.understood).length;

        panel.querySelector('#not-understood-count').textContent = notUnderstood;
        panel.querySelector('#understood-count').textContent = understood;
        panel.querySelector('#completed-count').textContent = completedCount;
    }

    // 渲染问题列表(未理解在上,已理解在下)
    function renderProblems() {
        const list = panel.querySelector('#problems-list');

        updateCounters();

        if (problems.length === 0) {
            list.innerHTML = '<div class="empty-message">暂无待做题目,请添加题目到计划中</div>';
            return;
        }

        // 分离未理解和已理解,各自保持原顺序
        const notUnderstood = problems.filter(p => !p.understood);
        const understood = problems.filter(p => p.understood);

        list.innerHTML = '';

        // 渲染未理解题目
        notUnderstood.forEach((problem, index) => {
            const item = createProblemItem(problem, index, false);
            list.appendChild(item);
        });

        // 渲染已理解题目
        understood.forEach((problem, index) => {
            const item = createProblemItem(problem, index, true);
            list.appendChild(item);
        });
    }

    // 创建单个题目DOM元素
    function createProblemItem(problem, index, isUnderstoodGroup) {
        const item = document.createElement('div');
        item.className = 'problem-item';
        if (problem.understood) {
            item.classList.add('understood');
        }

        const link = document.createElement('a');
        link.className = 'problem-link';
        link.href = problem.url;
        link.textContent = problem.name;
        link.style.color = problem.color;
        link.target = '_blank';
        link.rel = 'noopener noreferrer';

        // 如果已理解,添加一个标记
        if (problem.understood) {
            const badge = document.createElement('span');
            badge.className = 'understood-badge';
            badge.textContent = ' ✓';
            link.appendChild(badge);
        }

        const actions = document.createElement('div');
        actions.className = 'problem-actions';

        // 放弃按钮
        const giveupBtn = document.createElement('button');
        giveupBtn.className = 'giveup-btn';
        giveupBtn.textContent = '放弃';
        giveupBtn.title = '放弃此题(不计入完成)';
        giveupBtn.addEventListener('click', (e) => {
            e.preventDefault();
            if (confirm(`确定要放弃题目 "${problem.name}" 吗?`)) {
                const realIndex = problems.findIndex(p => p.url === problem.url); // 通过url找到实际索引
                if (realIndex !== -1) {
                    problems.splice(realIndex, 1);
                    saveData();
                    renderProblems();
                }
            }
        });

        // 理解按钮(仅当未理解时显示)
        if (!problem.understood) {
            const understandBtn = document.createElement('button');
            understandBtn.className = 'understand-btn';
            understandBtn.textContent = '理解';
            understandBtn.title = '标记为已理解(移至底部)';
            understandBtn.addEventListener('click', (e) => {
                e.preventDefault();
                const realIndex = problems.findIndex(p => p.url === problem.url);
                if (realIndex !== -1) {
                    problems[realIndex].understood = true;
                    saveData();
                    renderProblems();
                }
            });
            actions.appendChild(understandBtn);
        }

        // 完成按钮
        const completeBtn = document.createElement('button');
        completeBtn.className = 'complete-btn';
        completeBtn.textContent = '完成';
        completeBtn.title = '标记为已完成';
        completeBtn.addEventListener('click', (e) => {
            e.preventDefault();
            if (confirm(`确定要标记题目 "${problem.name}" 为已完成吗?`)) {
                const realIndex = problems.findIndex(p => p.url === problem.url);
                if (realIndex !== -1) {
                    problems.splice(realIndex, 1);
                    completedCount++;
                    saveData();
                    renderProblems();
                }
            }
        });

        actions.appendChild(giveupBtn);
        actions.appendChild(completeBtn);

        item.appendChild(link);
        item.appendChild(actions);
        return item;
    }

    // 导出数据
    function exportData() {
        const data = {
            problems: problems,
            completedCount: completedCount,
            exportDate: new Date().toISOString(),
            version: '1.0',
            totalProblems: problems.length
        };

        const dataStr = JSON.stringify(data, null, 2);
        const dataBlob = new Blob([dataStr], {type: 'application/json'});

        const downloadLink = panel.querySelector('#download-link');
        const url = URL.createObjectURL(dataBlob);
        downloadLink.href = url;

        const date = new Date();
        const dateStr = `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
        downloadLink.download = `做题计划备份_${dateStr}.json`;

        const dataPreview = panel.querySelector('#data-preview');
        dataPreview.textContent = dataStr;
        dataPreview.classList.add('show');

        setTimeout(() => {
            downloadLink.click();
            showNotification('数据已导出,正在下载文件...', '#52C41A');
            setTimeout(() => {
                dataPreview.classList.remove('show');
            }, 3000);
        }, 100);
    }

    // 导入数据
    function importData() {
        panel.querySelector('#file-input').click();
    }

    // 处理文件选择
    function handleFileSelect(event) {
        const file = event.target.files[0];
        if (!file) return;

        const reader = new FileReader();

        reader.onload = function(e) {
            try {
                const data = JSON.parse(e.target.result);

                if (!data.problems || !Array.isArray(data.problems)) {
                    throw new Error('数据格式不正确:缺少题目列表');
                }

                const importConfirm = confirm(
                    `准备导入 ${data.problems.length} 个题目\n` +
                    `完成数量:${data.completedCount || 0}\n\n` +
                    `导入会覆盖现有数据,确定继续吗?`
                );

                if (importConfirm) {
                    problems = data.problems.map(p => ({
                        ...p,
                        understood: p.understood || false
                    }));
                    completedCount = data.completedCount || 0;
                    saveData();
                    renderProblems();
                    showNotification('数据导入成功!', '#52C41A');
                }
            } catch (error) {
                alert(`导入失败:${error.message}\n\n请确保选择的是有效的备份文件。`);
                console.error('导入错误:', error);
            }

            event.target.value = '';
        };

        reader.onerror = function() {
            alert('读取文件失败,请重试');
            event.target.value = '';
        };

        reader.readAsText(file);
    }

    // 显示通知
    function showNotification(message, color = '#3498DB') {
        let notification = document.querySelector('.backup-notification');

        if (!notification) {
            notification = document.createElement('div');
            notification.className = 'backup-notification';
            notification.style.cssText = `
                position: fixed;
                top: 60px;
                right: 10px;
                color: white;
                padding: 10px 15px;
                border-radius: 5px;
                font-family: 'Segoe UI', sans-serif;
                font-size: 14px;
                z-index: 1000000;
                box-shadow: 0 2px 10px rgba(0,0,0,0.2);
                opacity: 0;
                transform: translateY(-20px);
                transition: opacity 0.3s, transform 0.3s;
            `;
            document.body.appendChild(notification);
        }

        notification.textContent = message;
        notification.style.background = color;
        notification.style.opacity = '1';
        notification.style.transform = 'translateY(0)';

        setTimeout(() => {
            notification.style.opacity = '0';
            notification.style.transform = 'translateY(-20px)';
        }, 3000);
    }

    // 使用当前网址
    panel.querySelector('#use-current-url').addEventListener('click', () => {
        const urlInput = panel.querySelector('#problem-url');
        urlInput.value = window.location.href;

        const useCurrentBtn = panel.querySelector('#use-current-url');
        const originalText = useCurrentBtn.textContent;
        useCurrentBtn.textContent = '已应用!';
        useCurrentBtn.style.background = '#52C41A';
        useCurrentBtn.style.color = 'white';
        useCurrentBtn.style.borderColor = '#52C41A';

        setTimeout(() => {
            useCurrentBtn.textContent = originalText;
            useCurrentBtn.style.background = '';
            useCurrentBtn.style.color = '';
            useCurrentBtn.style.borderColor = '';
        }, 1000);
    });

    // 添加问题
    panel.querySelector('#add-problem').addEventListener('click', () => {
        const urlInput = panel.querySelector('#problem-url');
        const nameInput = panel.querySelector('#problem-name');

        let url = urlInput.value.trim();
        const name = nameInput.value.trim();

        if (!url) {
            url = window.location.href;
            urlInput.value = url;
        }

        if (!name) {
            alert('请输入题目名称!');
            nameInput.focus();
            return;
        }

        try {
            new URL(url);
        } catch (e) {
            alert('请输入有效的网址!');
            urlInput.focus();
            return;
        }

        if (problems.some(problem => problem.url === url)) {
            alert('此题目已在计划中!');
            return;
        }

        problems.push({
            url: url,
            name: name,
            color: selectedColor,
            addedDate: new Date().toISOString(),
            understood: false
        });

        saveData();
        renderProblems();

        nameInput.value = '';
        urlInput.value = window.location.href;
        nameInput.focus();

        const addBtn = panel.querySelector('#add-problem');
        const originalText = addBtn.textContent;
        addBtn.textContent = '添加成功!';
        addBtn.style.background = 'linear-gradient(135deg, #52C41A, #2ecc71)';
        setTimeout(() => {
            addBtn.textContent = originalText;
            addBtn.style.background = 'linear-gradient(135deg, #52C41A, #3498DB)';
        }, 1000);
    });

    // 清空所有数据
    function clearAllData() {
        if (problems.length > 0 && confirm('确定要清空所有计划中的题目吗?此操作不可撤销。')) {
            problems = [];
            completedCount = 0;
            saveData();
            renderProblems();
            showNotification('数据已清空', '#FE4C61');
        }
    }

    // 绑定导入导出按钮
    panel.querySelector('#export-data').addEventListener('click', exportData);
    panel.querySelector('#export-btn-small').addEventListener('click', exportData);
    panel.querySelector('#import-data').addEventListener('click', importData);
    panel.querySelector('#import-btn-small').addEventListener('click', importData);
    panel.querySelector('#clear-all').addEventListener('click', clearAllData);
    panel.querySelector('#clear-btn-small').addEventListener('click', clearAllData);
    panel.querySelector('#file-input').addEventListener('change', handleFileSelect);

    // 按Enter添加
    panel.querySelector('#problem-name').addEventListener('keypress', (e) => {
        if (e.key === 'Enter') {
            panel.querySelector('#add-problem').click();
        }
    });

    // 切换面板显示
    mainButton.addEventListener('click', (e) => {
        e.stopPropagation();
        isPanelVisible = !isPanelVisible;
        panel.style.display = isPanelVisible ? 'block' : 'none';

        if (isPanelVisible) {
            loadData();
            renderProblems();

            const currentUrlHint = panel.querySelector('.current-url-hint');
            currentUrlHint.innerHTML = `当前页面: ${window.location.href} <button class="use-current-btn" id="use-current-url">使用当前网址</button>`;

            panel.querySelector('#use-current-url').addEventListener('click', () => {
                const urlInput = panel.querySelector('#problem-url');
                urlInput.value = window.location.href;

                const useCurrentBtn = panel.querySelector('#use-current-url');
                const originalText = useCurrentBtn.textContent;
                useCurrentBtn.textContent = '已应用!';
                useCurrentBtn.style.background = '#52C41A';
                useCurrentBtn.style.color = 'white';
                useCurrentBtn.style.borderColor = '#52C41A';

                setTimeout(() => {
                    useCurrentBtn.textContent = originalText;
                    useCurrentBtn.style.background = '';
                    useCurrentBtn.style.color = '';
                    useCurrentBtn.style.borderColor = '';
                }, 1000);
            });

            const urlInput = panel.querySelector('#problem-url');
            if (!urlInput.value.trim()) {
                urlInput.value = window.location.href;
            }

            setTimeout(() => {
                panel.querySelector('#problem-name').focus();
            }, 100);
        }
    });

    // 点击外部关闭面板
    document.addEventListener('click', (e) => {
        if (isPanelVisible && !panel.contains(e.target) && !mainButton.contains(e.target)) {
            isPanelVisible = false;
            panel.style.display = 'none';
        }
    });

    panel.addEventListener('click', (e) => {
        e.stopPropagation();
    });

    // 初始渲染
    renderProblems();

    // 页面可见性变化时检查更新
    document.addEventListener('visibilitychange', () => {
        if (!document.hidden && isPanelVisible) {
            loadData();
            renderProblems();
        }
    });

})();