Download index.html from uu531/web: direct link, hf CLI and curl.
- Browser
- Download file 67.7 kB
-
https://huggingface.co/spaces/uu531/web/resolve/main/index.html
- Command line
-
hf download hf://spaces/uu531/web/index.html
-
curl -L -o index.html https://huggingface.co/spaces/uu531/web/resolve/main/index.html
67.7 kB
| <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> |