// ==UserScript== // @name 跨网站同步待办事项管理器 // @namespace http://tampermonkey.net/ // @version 2.0 // @description 在任意网站中同步的待办事项管理工具,支持分类文件夹和导出功能 // @author 您的名称 // @match *://*/* // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_listValues // @grant GM_addStyle // @require https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js // @resource fontAwesomeCSS https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css // ==/UserScript== (function() { 'use strict'; // 添加Font Awesome样式 GM_addStyle(` @import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css'); `); // 添加主样式 GM_addStyle(` #sync-todo-app-container { position: fixed; top: 20px; right: 20px; width: 380px; background: #fff; border-radius: 12px; box-shadow: 0 6px 25px rgba(0,0,0,0.15); z-index: 10000; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-height: 80vh; display: flex; flex-direction: column; transition: all 0.3s ease; } #sync-todo-header { padding: 18px; background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); color: white; border-radius: 12px 12px 0 0; cursor: move; display: flex; justify-content: space-between; align-items: center; } #sync-todo-content { padding: 18px; overflow-y: auto; flex-grow: 1; } #sync-status { padding: 8px 12px; background: #f1f8ff; border-radius: 6px; margin-bottom: 15px; display: flex; align-items: center; gap: 8px; font-size: 14px; color: #0366d6; } .sync-todo-section { margin-bottom: 15px; } .sync-input-group { display: flex; gap: 8px; margin-bottom: 15px; } .sync-input-group input { flex: 1; padding: 10px; border: 1px solid #ddd; border-radius: 6px; outline: none; } .sync-input-group button { padding: 10px 16px; background: #6a11cb; color: white; border: none; border-radius: 6px; cursor: pointer; display: flex; align-items: center; gap: 5px; } #sync-todo-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 15px; max-height: 300px; overflow-y: auto; } .sync-todo-item { padding: 12px; background: #f8f9fa; border-radius: 8px; display: flex; align-items: center; gap: 12px; } .sync-todo-item input[type="checkbox"] { width: 18px; height: 18px; cursor: pointer; } .sync-todo-item span { flex-grow: 1; } .sync-todo-item.completed span { text-decoration: line-through; color: #888; } .sync-todo-item button { background: none; border: none; color: #dc3545; cursor: pointer; padding: 5px; } #sync-actions { display: flex; justify-content: space-between; margin-top: 15px; padding-top: 15px; border-top: 1px solid #eee; } #sync-actions button { padding: 10px 16px; color: white; border: none; border-radius: 6px; cursor: pointer; display: flex; align-items: center; gap: 5px; } #sync-export-btn { background: #00b894; } #sync-import-btn { background: #fd7e14; } #sync-todo-minimized { position: fixed; bottom: 20px; right: 20px; width: 50px; height: 50px; background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); color: white; border-radius: 50%; display: none; justify-content: center; align-items: center; cursor: pointer; box-shadow: 0 4px 15px rgba(0,0,0,0.2); z-index: 9999; } .sync-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; z-index: 10001; } .sync-modal-content { background: white; padding: 25px; border-radius: 12px; width: 320px; box-shadow: 0 5px 20px rgba(0,0,0,0.15); } .sync-modal-header { margin-bottom: 20px; display: flex; justify-content: space-between; align-items: center; } .sync-modal-title { font-size: 1.2rem; font-weight: 600; } .sync-modal-close { background: none; border: none; font-size: 20px; cursor: pointer; color: #999; } .sync-modal-body { margin-bottom: 20px; } .sync-modal-footer { display: flex; justify-content: flex-end; gap: 10px; } .sync-modal-btn { padding: 8px 16px; border: none; border-radius: 6px; cursor: pointer; } .sync-modal-btn.primary { background: #6a11cb; color: white; } .sync-modal-btn.secondary { background: #f8f9fa; color: #333; border: 1px solid #ddd; } #sync-category-select { width: 100%; padding: 10px; border-radius: 6px; border: 1px solid #ddd; background: white; margin-bottom: 15px; } `); // 创建UI容器 const container = document.createElement('div'); container.id = 'sync-todo-app-container'; container.innerHTML = `

同步待办事项

已同步
`; // 创建最小化时的图标 const minimizedIcon = document.createElement('div'); minimizedIcon.id = 'sync-todo-minimized'; minimizedIcon.innerHTML = ``; // 添加到页面 document.body.appendChild(container); document.body.appendChild(minimizedIcon); // 初始化数据 let categories = GM_getValue('todoCategories', ['工作', '个人', '学习']); let todos = GM_getValue('todoItems', []); let currentCategory = categories.length > 0 ? categories[0] : ''; // 初始化拖拽功能 let isDragging = false; let dragOffsetX, dragOffsetY; document.getElementById('sync-todo-header').addEventListener('mousedown', function(e) { isDragging = true; dragOffsetX = e.clientX - container.getBoundingClientRect().left; dragOffsetY = e.clientY - container.getBoundingClientRect().top; container.style.cursor = 'grabbing'; }); document.addEventListener('mousemove', function(e) { if (isDragging) { container.style.left = (e.clientX - dragOffsetX) + 'px'; container.style.top = (e.clientY - dragOffsetY) + 'px'; container.style.right = 'unset'; } }); document.addEventListener('mouseup', function() { isDragging = false; container.style.cursor = 'default'; }); // 最小化和关闭功能 document.getElementById('sync-minimize-btn').addEventListener('click', function() { container.style.display = 'none'; minimizedIcon.style.display = 'flex'; }); document.getElementById('sync-close-btn').addEventListener('click', function() { if (confirm('确定要关闭待办事项管理器吗?')) { container.style.display = 'none'; minimizedIcon.style.display = 'flex'; } }); minimizedIcon.addEventListener('click', function() { container.style.display = 'flex'; minimizedIcon.style.display = 'none'; }); // 初始化UI renderCategorySection(); renderTodos(); // 添加分类功能 document.getElementById('sync-add-category-btn').addEventListener('click', showAddCategoryModal); // 添加待办事项功能 document.getElementById('sync-add-todo-btn').addEventListener('click', addNewTodo); document.getElementById('sync-new-todo-input').addEventListener('keypress', function(e) { if (e.key === 'Enter') { addNewTodo(); } }); // 分类选择事件 document.getElementById('sync-category-select').addEventListener('change', function() { currentCategory = this.value; renderTodos(); }); // 导出导入功能 document.getElementById('sync-export-btn').addEventListener('click', exportDataToFile); document.getElementById('sync-import-btn').addEventListener('click', triggerImport); // 立即同步按钮 document.getElementById('force-sync-btn').addEventListener('click', function() { updateStatus('数据已同步', 'success'); }); // 渲染分类部分 function renderCategorySection() { const select = document.getElementById('sync-category-select'); select.innerHTML = ''; if (categories.length === 0) { const option = document.createElement('option'); option.textContent = '暂无分类,请先创建'; option.value = ''; select.appendChild(option); return; } categories.forEach(category => { const option = document.createElement('option'); option.value = category; option.textContent = category; if (category === currentCategory) { option.selected = true; } select.appendChild(option); }); } // 渲染待办事项 function renderTodos() { const listSection = document.getElementById('sync-todo-list'); const filteredTodos = currentCategory ? todos.filter(todo => todo.category === currentCategory) : []; if (filteredTodos.length === 0) { listSection.innerHTML = '


该分类下暂无待办事项

'; return; } let html = ''; filteredTodos.forEach(todo => { html += `
${todo.text}
`; }); listSection.innerHTML = html; // 添加事件监听 document.querySelectorAll('input[type="checkbox"]').forEach(checkbox => { checkbox.addEventListener('change', toggleTodoStatus); }); document.querySelectorAll('.sync-delete-todo-btn').forEach(button => { button.addEventListener('click', deleteTodo); }); } // 显示添加分类模态框 function showAddCategoryModal() { const modal = document.createElement('div'); modal.className = 'sync-modal'; modal.innerHTML = `

添加新分类

`; document.body.appendChild(modal); document.getElementById('sync-cancel-category-btn').addEventListener('click', function() { document.body.removeChild(modal); }); document.querySelector('.sync-modal-close').addEventListener('click', function() { document.body.removeChild(modal); }); document.getElementById('sync-save-category-btn').addEventListener('click', function() { const name = document.getElementById('sync-new-category-name').value.trim(); if (name && !categories.includes(name)) { categories.push(name); GM_setValue('todoCategories', categories); currentCategory = name; renderCategorySection(); renderTodos(); document.body.removeChild(modal); updateStatus('分类已添加', 'success'); } }); } // 添加新待办事项 function addNewTodo() { const input = document.getElementById('sync-new-todo-input'); const text = input.value.trim(); if (!text) { alert('请输入待办事项内容'); return; } if (!currentCategory) { alert('请先创建或选择一个分类'); return; } const todo = { id: Date.now(), text: text, category: currentCategory, completed: false, createdAt: new Date().toISOString() }; todos.push(todo); GM_setValue('todoItems', todos); input.value = ''; renderTodos(); updateStatus('待办事项已添加', 'success'); } // 切换待办事项状态 function toggleTodoStatus(e) { const id = parseInt(e.target.getAttribute('data-id')); const todo = todos.find(t => t.id === id); if (todo) { todo.completed = e.target.checked; GM_setValue('todoItems', todos); renderTodos(); updateStatus('状态已更新', 'success'); } } // 删除待办事项 function deleteTodo(e) { const id = parseInt(e.target.closest('.sync-delete-todo-btn').getAttribute('data-id')); if (confirm('确定要删除这个待办事项吗?')) { todos = todos.filter(todo => todo.id !== id); GM_setValue('todoItems', todos); renderTodos(); updateStatus('待办事项已删除', 'success'); } } // 导出数据到文件 function exportDataToFile() { const data = { categories: categories, todos: todos, exportedAt: new Date().toISOString() }; const jsonString = JSON.stringify(data, null, 2); const blob = new Blob([jsonString], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `todo-data-${new Date().toISOString().slice(0, 10)}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); updateStatus('数据已导出', 'success'); } // 触发导入 function triggerImport() { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.onchange = function(e) { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function(e) { try { const data = JSON.parse(e.target.result); if (confirm('确定要导入数据吗?这将覆盖当前所有数据。')) { categories = data.categories || []; todos = data.todos || []; GM_setValue('todoCategories', categories); GM_setValue('todoItems', todos); if (categories.length > 0) { currentCategory = categories[0]; } renderCategorySection(); renderTodos(); updateStatus('数据已导入', 'success'); } } catch (error) { alert('导入失败:无效的数据格式'); } }; reader.readAsText(file); }; input.click(); } // 更新状态 function updateStatus(message, status) { const statusEl = document.getElementById('sync-status'); const icon = statusEl.querySelector('i'); statusEl.querySelector('span').textContent = message; // 根据状态更新颜色 if (status === 'success') { icon.style.color = '#28a745'; } else if (status === 'error') { icon.style.color = '#dc3545'; } else if (status === 'syncing') { icon.style.color = '#ffc107'; } } })();