// UI相关函数 function toggleSettings(e) { // 密码保护校验 if (window.isPasswordProtected && window.isPasswordVerified) { if (window.isPasswordProtected() && !window.isPasswordVerified()) { showPasswordModal && showPasswordModal(); return; } } // 阻止事件冒泡,防止触发document的点击事件 e && e.stopPropagation(); const panel = document.getElementById('settingsPanel'); panel.classList.toggle('show'); } // 改进的Toast显示函数 - 支持队列显示多个Toast const toastQueue = []; let isShowingToast = false; function showToast(message, type = 'error') { // 将新的toast添加到队列 toastQueue.push({ message, type }); // 如果当前没有显示中的toast,则开始显示 if (!isShowingToast) { showNextToast(); } } function showNextToast() { if (toastQueue.length === 0) { isShowingToast = false; return; } isShowingToast = true; const { message, type } = toastQueue.shift(); const toast = document.getElementById('toast'); const toastMessage = document.getElementById('toastMessage'); const bgColors = { 'error': 'bg-red-500', 'success': 'bg-green-500', 'info': 'bg-blue-500', 'warning': 'bg-yellow-500' }; const bgColor = bgColors[type] || bgColors.error; toast.className = `fixed top-4 left-1/2 -translate-x-1/2 px-6 py-3 rounded-lg shadow-lg transform transition-all duration-300 ${bgColor} text-white`; toastMessage.textContent = message; // 显示提示 toast.style.opacity = '1'; toast.style.transform = 'translateX(-50%) translateY(0)'; // 3秒后自动隐藏 setTimeout(() => { toast.style.opacity = '0'; toast.style.transform = 'translateX(-50%) translateY(-100%)'; // 等待动画完成后显示下一个toast setTimeout(() => { showNextToast(); }, 300); }, 3000); } // 添加显示/隐藏 loading 的函数 let loadingTimeoutId = null; function showLoading(message = '加载中...') { // 清除任何现有的超时 if (loadingTimeoutId) { clearTimeout(loadingTimeoutId); } const loading = document.getElementById('loading'); const messageEl = loading.querySelector('p'); messageEl.textContent = message; loading.style.display = 'flex'; // 设置30秒后自动关闭loading,防止无限loading loadingTimeoutId = setTimeout(() => { hideLoading(); showToast('操作超时,请稍后重试', 'warning'); }, 30000); } function hideLoading() { // 清除超时 if (loadingTimeoutId) { clearTimeout(loadingTimeoutId); loadingTimeoutId = null; } const loading = document.getElementById('loading'); loading.style.display = 'none'; } function updateSiteStatus(isAvailable) { const statusEl = document.getElementById('siteStatus'); if (isAvailable) { statusEl.innerHTML = '● 可用'; } else { statusEl.innerHTML = '● 不可用'; } } function closeModal() { document.getElementById('modal').classList.add('hidden'); // 清除 iframe 内容 document.getElementById('modalContent').innerHTML = ''; } // 获取搜索历史的增强版本 - 支持新旧格式 function getSearchHistory() { try { const data = localStorage.getItem(SEARCH_HISTORY_KEY); if (!data) return []; const parsed = JSON.parse(data); // 检查是否是数组 if (!Array.isArray(parsed)) return []; // 支持旧格式(字符串数组)和新格式(对象数组) return parsed.map(item => { if (typeof item === 'string') { return { text: item, timestamp: 0 }; } return item; }).filter(item => item && item.text); } catch (e) { console.error('获取搜索历史出错:', e); return []; } } // 保存搜索历史的增强版本 - 添加时间戳和最大数量限制,现在缓存2个月 function saveSearchHistory(query) { if (!query || !query.trim()) return; // 清理输入,防止XSS query = query.trim().substring(0, 50).replace(//g, '>'); let history = getSearchHistory(); // 获取当前时间 const now = Date.now(); // 过滤掉超过2个月的记录(约60天,60*24*60*60*1000 = 5184000000毫秒) history = history.filter(item => typeof item === 'object' && item.timestamp && (now - item.timestamp < 5184000000) ); // 删除已存在的相同项 history = history.filter(item => typeof item === 'object' ? item.text !== query : item !== query ); // 新项添加到开头,包含时间戳 history.unshift({ text: query, timestamp: now }); // 限制历史记录数量 if (history.length > MAX_HISTORY_ITEMS) { history = history.slice(0, MAX_HISTORY_ITEMS); } try { localStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(history)); } catch (e) { console.error('保存搜索历史失败:', e); // 如果存储失败(可能是localStorage已满),尝试清理旧数据 try { localStorage.removeItem(SEARCH_HISTORY_KEY); localStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(history.slice(0, 3))); } catch (e2) { console.error('再次保存搜索历史失败:', e2); } } renderSearchHistory(); } // 渲染最近搜索历史的增强版本 function renderSearchHistory() { const historyContainer = document.getElementById('recentSearches'); if (!historyContainer) return; const history = getSearchHistory(); if (history.length === 0) { historyContainer.innerHTML = ''; return; } // 创建一个包含标题和清除按钮的行 historyContainer.innerHTML = `