web / index.html
uu531's picture
Add 1 files
aeeb754 verified
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>多功能搜索与任务管理</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.task-item {
animation: fadeIn 0.3s ease-out forwards;
}
.completed {
position: relative;
}
.completed::after {
content: '';
position: absolute;
left: 0;
top: 50%;
width: 100%;
height: 1px;
background: #000;
transform: scaleX(0);
transform-origin: left;
animation: strike 0.3s ease-out forwards;
}
@keyframes strike {
to { transform: scaleX(1); }
}
/* 自定义日期输入样式 */
input[type="date"]::-webkit-calendar-picker-indicator {
filter: invert(0.5);
}
input[type="date"]::-webkit-datetime-edit {
color: #4b5563;
}
/* 优先级通过待办框颜色表示 */
.priority-urgent {
background-color: #fee2e2; /* 红色背景 */
border-left: 4px solid #ef4444;
}
.priority-high {
background-color: #fef3c7; /* 黄色背景 */
border-left: 4px solid #f59e0b;
}
.priority-normal {
background-color: white; /* 白色背景 */
border-left: 4px solid #d1d5db;
}
/* 完成按钮颜色 */
.complete-btn-urgent {
border-color: #ef4444;
}
.complete-btn-high {
border-color: #f59e0b;
}
.complete-btn-normal {
border-color: #d1d5db;
}
/* 完成状态的按钮颜色 */
.complete-btn-done {
background-color: #10b981;
border-color: #10b981;
}
/* Logo样式 */
.todo-logo {
display: inline-flex;
align-items: center;
justify-content: center;
position: relative;
font-weight: 800;
font-size: 2.5rem;
color: #1f2937;
margin-bottom: 0.5rem;
}
.todo-logo span {
position: relative;
z-index: 2;
padding: 0 0.5rem;
}
.todo-logo::before {
content: '';
position: absolute;
width: 100%;
height: 1rem;
background: linear-gradient(90deg, rgba(239,68,68,0.3), rgba(245,158,11,0.3), rgba(16,185,129,0.3));
bottom: 0.5rem;
left: 0;
z-index: 1;
transform: skewY(-2deg);
}
.todo-logo .dot {
color: #ef4444;
font-size: 3rem;
line-height: 1;
margin-left: -0.5rem;
}
/* 暗夜模式 */
.dark-mode {
background-color: #111827;
color: #e5e7eb;
}
.dark-mode .todo-logo {
color: #e5e7eb;
}
.dark-mode .todo-logo::before {
background: linear-gradient(90deg, rgba(239,68,68,0.5), rgba(245,158,11,0.5), rgba(16,185,129,0.5));
}
.dark-mode input,
.dark-mode select,
.dark-mode .task-item {
background-color: #1f2937;
color: #e5e7eb;
border-color: #374151;
}
.dark-mode .priority-normal {
background-color: #1f2937;
}
.dark-mode .task-text {
color: #e5e7eb;
}
.dark-mode .completed .task-text {
color: #9ca3af;
}
.dark-mode .bg-white {
background-color: #1f2937;
border-color: #374151;
}
.dark-mode .text-gray-600 {
color: #9ca3af;
}
.dark-mode .border-gray-200 {
border-color: #374151;
}
.dark-mode .hover\:bg-gray-100:hover {
background-color: #374151;
}
.dark-mode .text-gray-700 {
color: #e5e7eb;
}
.dark-mode .text-gray-500 {
color: #9ca3af;
}
.dark-mode .text-gray-300 {
color: #6b7280;
}
/* 设置面板 */
.settings-panel {
position: fixed;
top: 0;
right: -400px;
width: 380px;
height: 100vh;
background-color: white;
box-shadow: -5px 0 15px rgba(0,0,0,0.1);
transition: right 0.3s ease;
z-index: 1000;
padding: 20px;
overflow-y: auto;
}
.dark-mode .settings-panel {
background-color: #1f2937;
color: #e5e7eb;
}
.settings-panel.open {
right: 0;
}
.settings-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0,0,0,0.5);
z-index: 999;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
}
.settings-overlay.active {
opacity: 1;
pointer-events: all;
}
/* 音乐播放器 */
.music-sidebar {
position: fixed;
top: 0;
right: -400px;
width: 380px;
height: 100vh;
background-color: white;
box-shadow: -5px 0 15px rgba(0,0,0,0.1);
transition: right 0.3s ease;
z-index: 998;
display: flex;
flex-direction: column;
}
.dark-mode .music-sidebar {
background-color: #1f2937;
color: #e5e7eb;
}
.music-sidebar.open {
right: 0;
}
.music-header {
padding: 20px;
border-bottom: 1px solid #e5e7eb;
display: flex;
justify-content: space-between;
align-items: center;
}
.dark-mode .music-header {
border-bottom-color: #374151;
}
.music-content {
flex: 1;
overflow-y: auto;
padding: 20px;
}
.music-player {
padding: 15px;
border-top: 1px solid #e5e7eb;
}
.dark-mode .music-player {
border-top-color: #374151;
}
.progress-container {
width: 100%;
height: 3px;
background-color: #eee;
margin-bottom: 10px;
}
.dark-mode .progress-container {
background-color: #374151;
}
.progress-bar {
height: 100%;
background-color: #ef4444;
width: 0%;
}
.player-controls {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.player-info {
margin-bottom: 15px;
}
.music-title {
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.music-artist {
font-size: 0.8rem;
color: #666;
}
.dark-mode .music-artist {
color: #9ca3af;
}
/* 主内容区域 */
.main-content {
transition: margin-right 0.3s ease;
}
.main-content.sidebar-open {
margin-right: 400px;
}
/* 音乐列表项 */
.music-item {
padding: 10px;
border-bottom: 1px solid #e5e7eb;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
}
.dark-mode .music-item {
border-bottom-color: #374151;
}
.music-item:hover {
background-color: #f3f4f6;
}
.dark-mode .music-item:hover {
background-color: #374151;
}
/* 登录按钮 */
.netease-login-btn {
display: inline-block;
padding: 8px 15px;
background-color: #e60026;
color: white;
border-radius: 4px;
font-size: 14px;
text-decoration: none;
transition: background-color 0.2s;
}
.netease-login-btn:hover {
background-color: #c50020;
}
/* 搜索引擎切换按钮 */
.search-engine-btn {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
cursor: pointer;
color: #6b7280;
}
.search-engine-btn:hover {
color: #4b5563;
}
.dark-mode .search-engine-btn {
color: #9ca3af;
}
.dark-mode .search-engine-btn:hover {
color: #e5e7eb;
}
/* 搜索引擎下拉菜单 */
.search-engine-dropdown {
position: absolute;
right: 0;
top: 100%;
background-color: white;
border: 1px solid #e5e7eb;
border-radius: 0.5rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
z-index: 10;
display: none;
min-width: 200px;
}
.dark-mode .search-engine-dropdown {
background-color: #1f2937;
border-color: #374151;
}
.search-engine-dropdown.show {
display: block;
}
.search-engine-item {
padding: 0.5rem 1rem;
cursor: pointer;
display: flex;
align-items: center;
}
.search-engine-item:hover {
background-color: #f3f4f6;
}
.dark-mode .search-engine-item:hover {
background-color: #374151;
}
.search-engine-icon {
margin-right: 0.5rem;
width: 16px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
}
/* 搜索卡片 */
.search-card {
background-color: white;
border-radius: 0.5rem;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.dark-mode .search-card {
background-color: #1f2937;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
/* 任务管理区域 */
.task-manager-section {
margin-top: 2rem;
background-color: white;
border-radius: 0.5rem;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
padding: 1.5rem;
}
.dark-mode .task-manager-section {
background-color: #1f2937;
}
/* 新添加的样式 */
.search-container {
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
}
@media (min-width: 768px) {
.search-container {
grid-template-columns: 1fr 1fr;
}
}
.search-box {
position: relative;
margin-bottom: 1rem;
}
.search-input {
width: 100%;
padding: 0.75rem 1rem;
padding-right: 3.5rem;
border-radius: 0.5rem;
border: 1px solid #e5e7eb;
font-size: 1rem;
transition: border-color 0.2s;
}
.search-input:focus {
outline: none;
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.search-button {
position: absolute;
right: 0.5rem;
top: 50%;
transform: translateY(-50%);
background-color: #3b82f6;
color: white;
border: none;
border-radius: 0.25rem;
padding: 0.5rem;
cursor: pointer;
transition: background-color 0.2s;
}
.search-button:hover {
background-color: #2563eb;
}
.search-engine-toggle {
position: absolute;
right: 3.5rem;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
color: #6b7280;
cursor: pointer;
}
.search-engine-toggle:hover {
color: #4b5563;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<!-- 设置按钮 -->
<div class="fixed top-4 right-4 z-50 flex space-x-2">
<button id="musicToggleBtn" class="p-3 rounded-full bg-gray-900 text-white hover:bg-gray-800 transition-colors shadow-lg">
<i class="fas fa-music"></i>
</button>
<button id="settingsBtn" class="p-3 rounded-full bg-gray-900 text-white hover:bg-gray-800 transition-colors shadow-lg">
<i class="fas fa-cog"></i>
</button>
</div>
<!-- 设置面板 -->
<div class="settings-overlay" id="settingsOverlay"></div>
<div class="settings-panel" id="settingsPanel">
<div class="flex justify-between items-center mb-6">
<h2 class="text-xl font-bold">设置</h2>
<button id="closeSettings" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div class="mb-6">
<h3 class="text-lg font-semibold mb-3">主题</h3>
<div class="flex items-center justify-between">
<span>暗夜模式</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="darkModeToggle" class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-gray-800"></div>
</label>
</div>
</div>
<div class="mb-6">
<h3 class="text-lg font-semibold mb-3">网易云音乐</h3>
<div class="mb-4">
<label class="block text-sm text-gray-600 mb-1">网易云音乐网址</label>
<div class="flex">
<input type="text" id="neteaseUrl" placeholder="https://music.163.com" value="https://music.163.com" class="flex-1 px-4 py-2 rounded-l-lg border border-gray-300 focus:outline-none focus:border-gray-500">
<a id="neteaseLoginBtn" href="https://music.163.com" target="_blank" class="netease-login-btn px-4 py-2 rounded-r-lg">
<i class="fas fa-sign-in-alt mr-1"></i> 登录
</a>
</div>
</div>
<div class="mb-4">
<label class="block text-sm text-gray-600 mb-1">搜索歌曲</label>
<div class="flex">
<input type="text" id="musicSearch" placeholder="输入歌曲名" class="flex-1 px-4 py-2 rounded-l-lg border border-gray-300 focus:outline-none focus:border-gray-500">
<button id="searchMusicBtn" class="px-4 py-2 bg-gray-900 text-white rounded-r-lg hover:bg-gray-800">
<i class="fas fa-search"></i>
</button>
</div>
</div>
<div id="musicResults" class="max-h-60 overflow-y-auto border border-gray-200 rounded-lg">
<!-- 音乐搜索结果将显示在这里 -->
<div class="text-center py-8 text-gray-500">
<i class="fas fa-music text-2xl mb-2 text-gray-300"></i>
<p>搜索你喜欢的音乐</p>
</div>
</div>
</div>
</div>
<!-- 音乐侧边栏 -->
<div class="music-sidebar" id="musicSidebar">
<div class="music-header">
<h2 class="text-xl font-bold">音乐播放器</h2>
<button id="closeMusicSidebar" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div class="music-content" id="musicContent">
<div class="text-center py-8 text-gray-500">
<i class="fas fa-music text-2xl mb-2 text-gray-300"></i>
<p>从设置中搜索音乐开始播放</p>
</div>
</div>
<div class="music-player">
<div class="progress-container">
<div class="progress-bar" id="progressBar"></div>
</div>
<div class="player-info">
<div class="music-title" id="musicTitle">未播放</div>
<div class="music-artist" id="musicArtist">-</div>
</div>
<div class="player-controls">
<button id="prevBtn" class="text-gray-900 hover:text-gray-700">
<i class="fas fa-step-backward"></i>
</button>
<button id="playPauseBtn" class="text-gray-900 hover:text-gray-700 text-xl">
<i class="fas fa-play"></i>
</button>
<button id="nextBtn" class="text-gray-900 hover:text-gray-700">
<i class="fas fa-step-forward"></i>
</button>
</div>
</div>
</div>
<div class="container mx-auto px-4 py-8 max-w-4xl main-content" id="mainContent">
<!-- 搜索区域 -->
<div class="search-card">
<h1 class="text-3xl font-bold text-center mb-8">多功能搜索</h1>
<div class="search-container">
<!-- 通用搜索引擎 -->
<div>
<h2 class="text-xl font-semibold mb-4">通用搜索</h2>
<div class="search-box">
<input
type="text"
id="generalSearchInput"
placeholder="在 Google 上搜索..."
class="search-input"
>
<button id="generalSearchEngineBtn" class="search-engine-toggle">
<i class="fas fa-chevron-down"></i>
</button>
<button id="generalSearchBtn" class="search-button">
<i class="fas fa-search"></i>
</button>
<div id="generalSearchEngineDropdown" class="search-engine-dropdown">
<div class="search-engine-item" data-engine="google">
<div class="search-engine-icon">
<i class="fab fa-google text-blue-500"></i>
</div>
<span>Google</span>
</div>
<div class="search-engine-item" data-engine="bing">
<div class="search-engine-icon">
<i class="fab fa-microsoft text-green-500"></i>
</div>
<span>Bing</span>
</div>
<div class="search-engine-item" data-engine="baidu">
<div class="search-engine-icon">
<i class="fas fa-search text-blue-600"></i>
</div>
<span>百度</span>
</div>
<div class="search-engine-item" data-engine="duckduckgo">
<div class="search-engine-icon">
<i class="fas fa-search text-yellow-500"></i>
</div>
<span>DuckDuckGo</span>
</div>
</div>
</div>
</div>
<!-- 学术搜索 -->
<div>
<h2 class="text-xl font-semibold mb-4">学术搜索</h2>
<div class="search-box">
<input
type="text"
id="academicSearchInput"
placeholder="在 Google 学术上搜索..."
class="search-input"
>
<button id="academicSearchEngineBtn" class="search-engine-toggle">
<i class="fas fa-chevron-down"></i>
</button>
<button id="academicSearchBtn" class="search-button">
<i class="fas fa-search"></i>
</button>
<div id="academicSearchEngineDropdown" class="search-engine-dropdown">
<div class="search-engine-item" data-engine="google_scholar">
<div class="search-engine-icon">
<i class="fas fa-graduation-cap text-blue-500"></i>
</div>
<span>Google 学术</span>
</div>
<div class="search-engine-item" data-engine="semantic_scholar">
<div class="search-engine-icon">
<i class="fas fa-book text-orange-500"></i>
</div>
<span>Semantic Scholar</span>
</div>
<div class="search-engine-item" data-engine="cnki">
<div class="search-engine-icon">
<i class="fas fa-university text-red-500"></i>
</div>
<span>CNKI</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 任务管理区域 -->
<div class="task-manager-section">
<div class="todo-logo">
<span>任务管理</span>
<span class="dot">.</span>
</div>
<p class="text-gray-600 mb-6">专注当下,高效生活</p>
<div class="relative mb-4">
<input
type="text"
id="newTaskInput"
placeholder="添加新任务..."
class="w-full px-6 py-3 rounded-lg border border-gray-300 focus:outline-none focus:border-gray-500 bg-white text-gray-900"
>
<button
id="addTaskBtn"
class="absolute right-2 top-1/2 transform -translate-y-1/2 bg-gray-900 text-white p-2 rounded-lg hover:bg-gray-800 transition-colors"
>
<i class="fas fa-plus"></i>
</button>
</div>
<div class="flex items-center justify-between mb-4">
<div class="flex-1 mr-2">
<label for="dueDate" class="block text-sm text-gray-600 mb-1">截止日期</label>
<input
type="date"
id="dueDate"
class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:border-gray-500 bg-white text-gray-900"
>
</div>
<div class="flex-1 ml-2">
<label for="priority" class="block text-sm text-gray-600 mb-1">优先级</label>
<select
id="priority"
class="w-full px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:border-gray-500 bg-white text-gray-900"
>
<option value="normal">普通</option>
<option value="high"></option>
<option value="urgent">紧急</option>
</select>
</div>
</div>
<!-- 任务控制 -->
<div class="flex justify-between items-center mb-6">
<div class="flex space-x-2">
<button id="filterAll" class="filter-btn active px-4 py-2 rounded-lg bg-gray-900 text-white font-medium">全部</button>
<button id="filterActive" class="filter-btn px-4 py-2 rounded-lg border border-gray-300 hover:bg-gray-100 text-gray-700">待办</button>
<button id="filterCompleted" class="filter-btn px-4 py-2 rounded-lg border border-gray-300 hover:bg-gray-100 text-gray-700">已完成</button>
<button id="filterDueToday" class="filter-btn px-4 py-2 rounded-lg border border-gray-300 hover:bg-gray-100 text-gray-700">今日到期</button>
</div>
<button id="clearCompleted" class="text-gray-700 hover:text-gray-900 text-sm">
<i class="fas fa-trash-alt mr-1"></i> 清除已完成
</button>
</div>
<!-- 任务统计 -->
<div class="bg-white rounded-lg p-4 mb-6 border border-gray-200 flex justify-between items-center">
<div>
<span class="text-gray-600">总任务:</span>
<span id="totalTasks" class="font-bold ml-1">0</span>
</div>
<div>
<span class="text-gray-600">已完成:</span>
<span id="completedTasks" class="font-bold ml-1">0</span>
</div>
<div>
<span class="text-gray-600">待完成:</span>
<span id="remainingTasks" class="font-bold ml-1">0</span>
</div>
<div>
<span class="text-gray-600">今日到期:</span>
<span id="dueTodayTasks" class="font-bold ml-1">0</span>
</div>
</div>
<!-- 任务列表 -->
<div id="taskList" class="space-y-3">
<!-- 任务将动态添加在这里 -->
<div class="text-center py-10 text-gray-500" id="emptyState">
<i class="fas fa-tasks text-4xl mb-3 text-gray-300"></i>
<p class="text-lg">暂无任务</p>
<p class="mt-1">添加你的第一个任务开始吧</p>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM元素
const newTaskInput = document.getElementById('newTaskInput');
const addTaskBtn = document.getElementById('addTaskBtn');
const dueDateInput = document.getElementById('dueDate');
const prioritySelect = document.getElementById('priority');
const taskList = document.getElementById('taskList');
const emptyState = document.getElementById('emptyState');
const filterAll = document.getElementById('filterAll');
const filterActive = document.getElementById('filterActive');
const filterCompleted = document.getElementById('filterCompleted');
const filterDueToday = document.getElementById('filterDueToday');
const clearCompleted = document.getElementById('clearCompleted');
const totalTasks = document.getElementById('totalTasks');
const completedTasks = document.getElementById('completedTasks');
const remainingTasks = document.getElementById('remainingTasks');
const dueTodayTasks = document.getElementById('dueTodayTasks');
// 设置相关元素
const settingsBtn = document.getElementById('settingsBtn');
const settingsPanel = document.getElementById('settingsPanel');
const settingsOverlay = document.getElementById('settingsOverlay');
const closeSettings = document.getElementById('closeSettings');
const darkModeToggle = document.getElementById('darkModeToggle');
const musicToggleBtn = document.getElementById('musicToggleBtn');
const musicSidebar = document.getElementById('musicSidebar');
const closeMusicSidebar = document.getElementById('closeMusicSidebar');
const musicContent = document.getElementById('musicContent');
const neteaseUrl = document.getElementById('neteaseUrl');
const neteaseLoginBtn = document.getElementById('neteaseLoginBtn');
// 音乐播放器相关元素
const musicSearch = document.getElementById('musicSearch');
const searchMusicBtn = document.getElementById('searchMusicBtn');
const musicResults = document.getElementById('musicResults');
const playPauseBtn = document.getElementById('playPauseBtn');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
const musicTitle = document.getElementById('musicTitle');
const musicArtist = document.getElementById('musicArtist');
const progressBar = document.getElementById('progressBar');
const mainContent = document.getElementById('mainContent');
// 搜索相关元素
const generalSearchInput = document.getElementById('generalSearchInput');
const generalSearchBtn = document.getElementById('generalSearchBtn');
const generalSearchEngineBtn = document.getElementById('generalSearchEngineBtn');
const generalSearchEngineDropdown = document.getElementById('generalSearchEngineDropdown');
const academicSearchInput = document.getElementById('academicSearchInput');
const academicSearchBtn = document.getElementById('academicSearchBtn');
const academicSearchEngineBtn = document.getElementById('academicSearchEngineBtn');
const academicSearchEngineDropdown = document.getElementById('academicSearchEngineDropdown');
// 状态
let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
let currentFilter = 'all';
let isDarkMode = localStorage.getItem('darkMode') === 'true';
let currentMusicIndex = -1;
let musicList = [];
let audio = new Audio();
let isPlaying = false;
let progressInterval;
let neteaseMusicUrl = localStorage.getItem('neteaseMusicUrl') || 'https://music.163.com';
// 搜索引擎配置
let currentGeneralEngine = 'google';
let currentAcademicEngine = 'google_scholar';
const searchEngines = {
// 通用搜索引擎
google: {
name: 'Google',
url: 'https://www.google.com/search?q=',
icon: 'fab fa-google text-blue-500'
},
bing: {
name: 'Bing',
url: 'https://www.bing.com/search?q=',
icon: 'fab fa-microsoft text-green-500'
},
baidu: {
name: '百度',
url: 'https://www.baidu.com/s?wd=',
icon: 'fas fa-search text-blue-600'
},
duckduckgo: {
name: 'DuckDuckGo',
url: 'https://duckduckgo.com/?q=',
icon: 'fas fa-search text-yellow-500'
},
// 学术搜索引擎
google_scholar: {
name: 'Google 学术',
url: 'https://scholar.google.com/scholar?q=',
icon: 'fas fa-graduation-cap text-blue-500'
},
semantic_scholar: {
name: 'Semantic Scholar',
url: 'https://www.semanticscholar.org/search?q=',
icon: 'fas fa-book text-orange-500'
},
cnki: {
name: 'CNKI',
url: 'https://search.cnki.net/search.aspx?q=',
icon: 'fas fa-university text-red-500'
}
};
// 初始化
initDarkMode();
renderTasks();
updateStats();
setupMusicPlayer();
neteaseUrl.value = neteaseMusicUrl;
updateNeteaseLoginBtn();
initSearchEngines();
// 事件监听
addTaskBtn.addEventListener('click', addTask);
newTaskInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') addTask();
});
filterAll.addEventListener('click', () => setFilter('all'));
filterActive.addEventListener('click', () => setFilter('active'));
filterCompleted.addEventListener('click', () => setFilter('completed'));
filterDueToday.addEventListener('click', () => setFilter('dueToday'));
clearCompleted.addEventListener('click', clearCompletedTasks);
// 设置面板事件
settingsBtn.addEventListener('click', openSettings);
closeSettings.addEventListener('click', closeSettingsPanel);
settingsOverlay.addEventListener('click', closeSettingsPanel);
darkModeToggle.addEventListener('change', toggleDarkMode);
musicToggleBtn.addEventListener('click', toggleMusicSidebar);
closeMusicSidebar.addEventListener('click', closeMusicSidebarPanel);
// 网易云音乐设置
neteaseUrl.addEventListener('change', updateNeteaseUrl);
// 音乐播放器事件
searchMusicBtn.addEventListener('click', searchMusic);
musicSearch.addEventListener('keypress', function(e) {
if (e.key === 'Enter') searchMusic();
});
playPauseBtn.addEventListener('click', togglePlayPause);
prevBtn.addEventListener('click', playPrevious);
nextBtn.addEventListener('click', playNext);
// 搜索相关事件
generalSearchBtn.addEventListener('click', () => performSearch('general'));
generalSearchInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') performSearch('general');
});
generalSearchEngineBtn.addEventListener('click', () => toggleSearchEngineDropdown('general'));
academicSearchBtn.addEventListener('click', () => performSearch('academic'));
academicSearchInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') performSearch('academic');
});
academicSearchEngineBtn.addEventListener('click', () => toggleSearchEngineDropdown('academic'));
// 关闭下拉菜单当点击其他地方时
document.addEventListener('click', function(e) {
if (!generalSearchEngineBtn.contains(e.target) && !generalSearchEngineDropdown.contains(e.target)) {
generalSearchEngineDropdown.classList.remove('show');
}
if (!academicSearchEngineBtn.contains(e.target) && !academicSearchEngineDropdown.contains(e.target)) {
academicSearchEngineDropdown.classList.remove('show');
}
});
// 功能函数
function addTask() {
const taskText = newTaskInput.value.trim();
if (taskText === '') return;
const dueDate = dueDateInput.value;
const priority = prioritySelect.value;
const task = {
id: Date.now().toString(),
text: taskText,
completed: false,
createdAt: new Date().toISOString(),
dueDate: dueDate,
priority: priority
};
tasks.unshift(task);
saveTasks();
renderTasks();
updateStats();
// 重置输入
newTaskInput.value = '';
dueDateInput.value = '';
prioritySelect.value = 'normal';
}
function renderTasks() {
// 根据当前筛选条件过滤任务
let filteredTasks = [];
switch(currentFilter) {
case 'active':
filteredTasks = tasks.filter(task => !task.completed);
break;
case 'completed':
filteredTasks = tasks.filter(task => task.completed);
break;
case 'dueToday':
filteredTasks = tasks.filter(task => isDueToday(task.dueDate) && !task.completed);
break;
default:
filteredTasks = [...tasks];
}
if (filteredTasks.length === 0) {
emptyState.classList.remove('hidden');
taskList.innerHTML = '';
taskList.appendChild(emptyState);
} else {
emptyState.classList.add('hidden');
taskList.innerHTML = '';
// 按优先级和截止日期排序
filteredTasks.sort((a, b) => {
// 先按完成状态排序(未完成的在前)
if (a.completed !== b.completed) {
return a.completed ? 1 : -1;
}
// 然后按优先级排序
const priorityOrder = { 'urgent': 0, 'high': 1, 'normal': 2 };
if (a.priority !== b.priority) {
return priorityOrder[a.priority] - priorityOrder[b.priority];
}
// 最后按截止日期排序(近期的在前)
if (a.dueDate && b.dueDate) {
return new Date(a.dueDate) - new Date(b.dueDate);
} else if (a.dueDate) {
return -1;
} else if (b.dueDate) {
return 1;
}
return 0;
});
filteredTasks.forEach(task => {
const taskElement = createTaskElement(task);
taskList.appendChild(taskElement);
});
}
}
function createTaskElement(task) {
const taskElement = document.createElement('div');
taskElement.className = `task-item rounded-lg p-4 border border-gray-200 flex items-center justify-between priority-${task.priority}`;
taskElement.dataset.id = task.id;
if (task.completed) {
taskElement.classList.add('completed');
}
// 格式化日期显示
let dueDateDisplay = '';
if (task.dueDate) {
const dueDate = new Date(task.dueDate);
const today = new Date();
today.setHours(0, 0, 0, 0);
const timeDiff = dueDate - today;
const daysDiff = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
if (daysDiff === 0) {
dueDateDisplay = '<span class="text-red-500">今天到期</span>';
} else if (daysDiff === 1) {
dueDateDisplay = '<span class="text-orange-500">明天到期</span>';
} else if (daysDiff < 0) {
dueDateDisplay = '<span class="text-red-500">已过期</span>';
} else {
dueDateDisplay = `${daysDiff}天后到期`;
}
dueDateDisplay = `<span class="text-xs ml-2">${dueDateDisplay}</span>`;
}
// 完成按钮类名
const completeBtnClass = task.completed
? 'complete-btn-done'
: `complete-btn-${task.priority}`;
taskElement.innerHTML = `
<div class="flex items-center space-x-3 flex-1">
<button class="complete-btn w-5 h-5 rounded border ${completeBtnClass} flex items-center justify-center">
${task.completed ? '<i class="fas fa-check text-xs text-white"></i>' : ''}
</button>
<div class="flex-1">
<div class="flex items-center">
<span class="task-text ${task.completed ? 'text-gray-400' : 'text-gray-800'}">${task.text}</span>
</div>
${task.dueDate ? `
<div class="text-xs text-gray-500 mt-1 ml-7">
<i class="far fa-calendar-alt mr-1"></i>
${formatDate(task.dueDate)}
${dueDateDisplay}
</div>
` : ''}
</div>
</div>
<div class="flex space-x-2">
<button class="edit-btn text-gray-500 hover:text-gray-700">
<i class="fas fa-pencil-alt text-sm"></i>
</button>
<button class="delete-btn text-gray-500 hover:text-gray-700">
<i class="fas fa-trash-alt text-sm"></i>
</button>
</div>
`;
// 为按钮添加事件监听
const completeBtn = taskElement.querySelector('.complete-btn');
const editBtn = taskElement.querySelector('.edit-btn');
const deleteBtn = taskElement.querySelector('.delete-btn');
completeBtn.addEventListener('click', () => toggleComplete(task.id));
editBtn.addEventListener('click', () => editTask(task.id));
deleteBtn.addEventListener('click', () => deleteTask(task.id));
return taskElement;
}
function toggleComplete(taskId) {
const taskIndex = tasks.findIndex(task => task.id === taskId);
if (taskIndex !== -1) {
tasks[taskIndex].completed = !tasks[taskIndex].completed;
saveTasks();
renderTasks();
updateStats();
}
}
function editTask(taskId) {
const task = tasks.find(task => task.id === taskId);
if (!task) return;
const taskElement = document.querySelector(`.task-item[data-id="${taskId}"]`);
const taskTextElement = taskElement.querySelector('.task-text');
const editForm = document.createElement('div');
editForm.className = 'w-full';
editForm.innerHTML = `
<div class="mb-2">
<input type="text" value="${task.text}" class="task-edit-input w-full px-3 py-2 border border-gray-300 rounded">
</div>
<div class="flex space-x-2">
<div class="flex-1">
<label class="block text-xs text-gray-600 mb-1">截止日期</label>
<input type="date" value="${task.dueDate || ''}" class="due-date-edit w-full px-3 py-1 border border-gray-300 rounded">
</div>
<div class="flex-1">
<label class="block text-xs text-gray-600 mb-1">优先级</label>
<select class="priority-edit w-full px-3 py-1 border border-gray-300 rounded">
<option value="normal" ${task.priority === 'normal' ? 'selected' : ''}>普通</option>
<option value="high" ${task.priority === 'high' ? 'selected' : ''}>高</option>
<option value="urgent" ${task.priority === 'urgent' ? 'selected' : ''}>紧急</option>
</select>
</div>
</div>
<div class="mt-2 flex justify-end space-x-2">
<button class="save-edit px-3 py-1 bg-gray-900 text-white text-sm rounded">保存</button>
<button class="cancel-edit px-3 py-1 border border-gray-300 text-gray-700 text-sm rounded">取消</button>
</div>
`;
taskTextElement.parentElement.parentElement.replaceWith(editForm);
const saveBtn = editForm.querySelector('.save-edit');
const cancelBtn = editForm.querySelector('.cancel-edit');
const editInput = editForm.querySelector('.task-edit-input');
const dueDateEdit = editForm.querySelector('.due-date-edit');
const priorityEdit = editForm.querySelector('.priority-edit');
editInput.focus();
const saveChanges = () => {
const newText = editInput.value.trim();
if (newText !== '' && (newText !== task.text || dueDateEdit.value !== task.dueDate || priorityEdit.value !== task.priority)) {
task.text = newText;
task.dueDate = dueDateEdit.value;
task.priority = priorityEdit.value;
saveTasks();
}
renderTasks();
};
saveBtn.addEventListener('click', saveChanges);
cancelBtn.addEventListener('click', () => {
renderTasks();
});
editInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') saveChanges();
});
}
function deleteTask(taskId) {
tasks = tasks.filter(task => task.id !== taskId);
saveTasks();
renderTasks();
updateStats();
}
function clearCompletedTasks() {
tasks = tasks.filter(task => !task.completed);
saveTasks();
renderTasks();
updateStats();
}
function setFilter(filter) {
currentFilter = filter;
// 更新筛选按钮的活动状态
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.classList.remove('active', 'bg-gray-900', 'text-white');
btn.classList.add('border', 'border-gray-300', 'hover:bg-gray-100', 'text-gray-700');
});
const activeBtn = document.getElementById(`filter${filter.charAt(0).toUpperCase() + filter.slice(1)}`);
if (activeBtn) {
activeBtn.classList.add('active', 'bg-gray-900', 'text-white');
activeBtn.classList.remove('border', 'hover:bg-gray-100', 'text-gray-700');
}
renderTasks();
}
function updateStats() {
const total = tasks.length;
const completed = tasks.filter(task => task.completed).length;
const remaining = total - completed;
const dueToday = tasks.filter(task => isDueToday(task.dueDate) && !task.completed).length;
totalTasks.textContent = total;
completedTasks.textContent = completed;
remainingTasks.textContent = remaining;
dueTodayTasks.textContent = dueToday;
}
function saveTasks() {
localStorage.setItem('tasks', JSON.stringify(tasks));
}
// 设置面板功能
function openSettings() {
settingsPanel.classList.add('open');
settingsOverlay.classList.add('active');
mainContent.classList.add('sidebar-open');
}
function closeSettingsPanel() {
settingsPanel.classList.remove('open');
settingsOverlay.classList.remove('active');
mainContent.classList.remove('sidebar-open');
}
function toggleMusicSidebar() {
if (musicSidebar.classList.contains('open')) {
closeMusicSidebarPanel();
} else {
openMusicSidebar();
}
}
function openMusicSidebar() {
musicSidebar.classList.add('open');
mainContent.classList.add('sidebar-open');
}
function closeMusicSidebarPanel() {
musicSidebar.classList.remove('open');
mainContent.classList.remove('sidebar-open');
}
function initDarkMode() {
if (isDarkMode) {
document.body.classList.add('dark-mode');
darkModeToggle.checked = true;
} else {
document.body.classList.remove('dark-mode');
darkModeToggle.checked = false;
}
}
function toggleDarkMode() {
isDarkMode = !isDarkMode;
localStorage.setItem('darkMode', isDarkMode);
initDarkMode();
}
// 网易云音乐设置
function updateNeteaseUrl() {
neteaseMusicUrl = neteaseUrl.value.trim();
if (neteaseMusicUrl === '') {
neteaseMusicUrl = 'https://music.163.com';
neteaseUrl.value = neteaseMusicUrl;
}
localStorage.setItem('neteaseMusicUrl', neteaseMusicUrl);
updateNeteaseLoginBtn();
}
function updateNeteaseLoginBtn() {
neteaseLoginBtn.href = neteaseMusicUrl;
}
// 音乐播放器功能
function setupMusicPlayer() {
audio.addEventListener('ended', playNext);
audio.addEventListener('timeupdate', updateProgress);
// 从本地存储加载播放列表
const savedPlaylist = localStorage.getItem('musicPlaylist');
if (savedPlaylist) {
musicList = JSON.parse(savedPlaylist);
renderMusicList();
}
// 从本地存储加载当前播放索引
const savedIndex = localStorage.getItem('currentMusicIndex');
if (savedIndex !== null) {
currentMusicIndex = parseInt(savedIndex);
if (currentMusicIndex >= 0 && currentMusicIndex < musicList.length) {
updateMusicInfo();
}
}
}
function renderMusicList() {
if (musicList.length === 0) {
musicContent.innerHTML = '<div class="text-center py-8 text-gray-500"><i class="fas fa-music text-2xl mb-2 text-gray-300"></i><p>从设置中搜索音乐开始播放</p></div>';
return;
}
musicContent.innerHTML = '';
musicList.forEach((song, index) => {
const songElement = document.createElement('div');
songElement.className = 'music-item';
songElement.innerHTML = `
<div>
<div class="font-medium">${song.title}</div>
<div class="text-sm text-gray-600">${song.artist}</div>
</div>
${currentMusicIndex === index ? '<i class="fas fa-volume-up text-blue-500"></i>' : '<i class="fas fa-play text-gray-500"></i>'}
`;
songElement.addEventListener('click', () => {
playSong(index);
});
musicContent.appendChild(songElement);
});
}
function searchMusic() {
const query = musicSearch.value.trim();
if (query === '') return;
// 使用网易云音乐API的代理服务
musicResults.innerHTML = '<div class="text-center py-4"><i class="fas fa-spinner fa-spin"></i> 搜索中...</div>';
// 使用网易云音乐API的代理服务
fetch(`https://api.injahow.cn/meting/?server=netease&type=search&id=${encodeURIComponent(query)}`)
.then(response => response.json())
.then(data => {
if (data && data.length > 0) {
musicResults.innerHTML = '';
// 处理返回的音乐数据
const songs = data.map(song => ({
id: song.id,
title: song.name,
artist: song.artist,
url: song.url,
cover: song.cover
}));
songs.forEach((song, index) => {
const songElement = document.createElement('div');
songElement.className = 'p-3 border-b border-gray-200 hover:bg-gray-100 cursor-pointer flex justify-between items-center';
songElement.innerHTML = `
<div class="flex items-center">
<img src="${song.cover}" alt="${song.title}" class="w-10 h-10 rounded mr-3">
<div>
<div class="font-medium">${song.title}</div>
<div class="text-sm text-gray-600">${song.artist}</div>
</div>
</div>
<i class="fas fa-play text-gray-500"></i>
`;
songElement.addEventListener('click', () => {
playSong(index, songs);
});
musicResults.appendChild(songElement);
});
// 更新播放列表
musicList = songs;
localStorage.setItem('musicPlaylist', JSON.stringify(musicList));
renderMusicList();
} else {
musicResults.innerHTML = '<div class="text-center py-8 text-gray-500">没有找到相关歌曲</div>';
}
})
.catch(error => {
console.error('搜索失败:', error);
musicResults.innerHTML = '<div class="text-center py-8 text-gray-500">搜索失败,请稍后再试</div>';
});
}
function playSong(index, playlist = musicList) {
if (index < 0 || index >= playlist.length) return;
currentMusicIndex = index;
const song = playlist[currentMusicIndex];
// 检查歌曲URL是否有效
if (!song.url) {
alert('无法获取歌曲播放地址');
return;
}
audio.src = song.url;
audio.play()
.then(() => {
isPlaying = true;
updatePlayPauseIcon();
updateMusicInfo();
startProgressUpdate();
renderMusicList();
// 保存当前播放索引
localStorage.setItem('currentMusicIndex', currentMusicIndex);
})
.catch(error => {
console.error('播放失败:', error);
alert('播放失败,请稍后再试');
});
}
function togglePlayPause() {
if (audio.src) {
if (isPlaying) {
audio.pause();
} else {
audio.play();
}
isPlaying = !isPlaying;
updatePlayPauseIcon();
if (isPlaying) {
startProgressUpdate();
} else {
stopProgressUpdate();
}
} else if (musicList.length > 0) {
// 如果没有正在播放的歌曲,但有播放列表,播放第一首
playSong(0);
}
}
function playPrevious() {
if (musicList.length === 0) return;
let newIndex = currentMusicIndex - 1;
if (newIndex < 0) {
newIndex = musicList.length - 1; // 循环到最后一首
}
playSong(newIndex);
}
function playNext() {
if (musicList.length === 0) return;
let newIndex = currentMusicIndex + 1;
if (newIndex >= musicList.length) {
newIndex = 0; // 循环到第一首
}
playSong(newIndex);
}
function updatePlayPauseIcon() {
playPauseBtn.innerHTML = isPlaying ? '<i class="fas fa-pause"></i>' : '<i class="fas fa-play"></i>';
}
function updateMusicInfo() {
if (currentMusicIndex >= 0 && currentMusicIndex < musicList.length) {
const song = musicList[currentMusicIndex];
musicTitle.textContent = song.title;
musicArtist.textContent = song.artist;
} else {
musicTitle.textContent = '未播放';
musicArtist.textContent = '-';
}
}
function updateProgress() {
if (audio.duration) {
const progress = (audio.currentTime / audio.duration) * 100;
progressBar.style.width = `${progress}%`;
}
}
function startProgressUpdate() {
stopProgressUpdate();
progressInterval = setInterval(updateProgress, 1000);
}
function stopProgressUpdate() {
if (progressInterval) {
clearInterval(progressInterval);
}
}
// 搜索功能
function initSearchEngines() {
// 设置默认搜索引擎
generalSearchInput.placeholder = `在 ${searchEngines[currentGeneralEngine].name} 上搜索...`;
academicSearchInput.placeholder = `在 ${searchEngines[currentAcademicEngine].name} 上搜索...`;
// 为下拉菜单项添加点击事件
document.querySelectorAll('#generalSearchEngineDropdown .search-engine-item').forEach(item => {
item.addEventListener('click', function() {
const engine = this.getAttribute('data-engine');
currentGeneralEngine = engine;
generalSearchInput.placeholder = `在 ${searchEngines[engine].name} 上搜索...`;
generalSearchEngineDropdown.classList.remove('show');
});
});
document.querySelectorAll('#academicSearchEngineDropdown .search-engine-item').forEach(item => {
item.addEventListener('click', function() {
const engine = this.getAttribute('data-engine');
currentAcademicEngine = engine;
academicSearchInput.placeholder = `在 ${searchEngines[engine].name} 上搜索...`;
academicSearchEngineDropdown.classList.remove('show');
});
});
}
function toggleSearchEngineDropdown(type) {
let dropdown;
switch(type) {
case 'general':
dropdown = generalSearchEngineDropdown;
break;
case 'academic':
dropdown = academicSearchEngineDropdown;
break;
}
// 关闭其他下拉菜单
if (type !== 'general') generalSearchEngineDropdown.classList.remove('show');
if (type !== 'academic') academicSearchEngineDropdown.classList.remove('show');
// 切换当前下拉菜单
dropdown.classList.toggle('show');
}
function performSearch(type) {
let input, engine;
switch(type) {
case 'general':
input = generalSearchInput;
engine = currentGeneralEngine;
break;
case 'academic':
input = academicSearchInput;
engine = currentAcademicEngine;
break;
}
const query = input.value.trim();
if (query === '') return;
const searchUrl = searchEngines[engine].url + encodeURIComponent(query);
window.open(searchUrl, '_blank');
}
// 辅助函数
function formatDate(dateString) {
if (!dateString) return '';
const date = new Date(dateString);
return `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}日`;
}
function isDueToday(dateString) {
if (!dateString) return false;
const today = new Date();
today.setHours(0, 0, 0, 0);
const dueDate = new Date(dateString);
dueDate.setHours(0, 0, 0, 0);
return dueDate.getTime() === today.getTime();
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=uu531/web" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>