/** * PerToolBox Front - 公共 JavaScript */ // ========== 全局变量 ========== let currentUser = null; // ========== 工具函数 ========== function getToken() { return localStorage.getItem('token'); } function setToken(token) { if (token) { localStorage.setItem('token', token); } else { localStorage.removeItem('token'); } } function isLoggedIn() { return !!getToken(); } // ========== API 请求封装 ========== async function apiRequest(endpoint, options = {}) { const baseUrl = window.API_BASE || '/api/v1'; const url = endpoint.startsWith('http') ? endpoint : `${baseUrl}${endpoint}`; // 默认 headers const headers = { ...options.headers }; // 如果没有设置 Content-Type 且 body 是 URLSearchParams,不自动添加 if (!options.headers || !options.headers['Content-Type']) { if (options.body && options.body instanceof URLSearchParams) { // 表单数据,不添加 JSON Content-Type headers['Content-Type'] = 'application/x-www-form-urlencoded'; } else if (options.body && typeof options.body === 'string') { // 已经是字符串,不添加 } else if (options.body && typeof options.body === 'object') { // JSON 对象 headers['Content-Type'] = 'application/json'; options.body = JSON.stringify(options.body); } } const token = getToken(); if (token) { headers['Authorization'] = `Bearer ${token}`; } try { const response = await fetch(url, { ...options, headers }); if (response.status === 401) { setToken(null); if (!window.location.pathname.includes('/login.php')) { window.location.href = '/login.php'; } return null; } // 获取响应文本 const text = await response.text(); if (!response.ok) { let errorMsg; try { const errorData = JSON.parse(text); errorMsg = errorData.detail || errorData.message || JSON.stringify(errorData); } catch (e) { errorMsg = text || '请求失败'; } throw new Error(errorMsg); } try { return JSON.parse(text); } catch (e) { return text; } } catch (error) { console.error('API 请求错误:', error); throw error; } } // ========== 热度上报 ========== async function recordUsage(toolName) { try { await apiRequest(`/tool/usage?tool_name=${toolName}`, { method: 'POST' }); console.log(`✅ 热度上报: ${toolName}`); } catch (error) { console.warn('热度上报失败:', error); } } // ========== 获取用户信息 ========== async function loadUserInfo() { if (!isLoggedIn()) { updateUserUI(null); return null; } try { const user = await apiRequest('/user/profile'); currentUser = user; updateUserUI(user); return user; } catch (error) { console.error('获取用户信息失败:', error); setToken(null); updateUserUI(null); return null; } } // ========== 更新侧边栏 UI ========== function updateUserUI(user) { const userInfoDiv = document.getElementById('userInfo'); const profileLink = document.getElementById('profileLink'); const logoutBtn = document.getElementById('logoutBtn'); const loginLink = document.getElementById('loginLink'); if (user) { const displayName = user.username || user.phone || user.email || '用户'; if (userInfoDiv) { userInfoDiv.innerHTML = `
欢迎,
${escapeHtml(displayName)}
`; } if (profileLink) profileLink.style.display = 'flex'; if (logoutBtn) logoutBtn.style.display = 'flex'; if (loginLink) loginLink.style.display = 'none'; } else { if (userInfoDiv) userInfoDiv.innerHTML = ''; if (profileLink) profileLink.style.display = 'none'; if (logoutBtn) logoutBtn.style.display = 'none'; if (loginLink) loginLink.style.display = 'flex'; } } // ========== 退出登录 ========== function logout() { setToken(null); currentUser = null; updateUserUI(null); window.location.href = '/'; } // ========== 侧边栏控制 ========== function initSidebar() { const menuBtn = document.getElementById('menuBtn'); const closeBtn = document.getElementById('closeSidebar'); const sidebar = document.getElementById('sidebar'); const overlay = document.getElementById('overlay'); function openSidebar() { if (sidebar) sidebar.classList.add('open'); if (overlay) overlay.classList.add('active'); document.body.style.overflow = 'hidden'; } function closeSidebar() { if (sidebar) sidebar.classList.remove('open'); if (overlay) overlay.classList.remove('active'); document.body.style.overflow = ''; } if (menuBtn) menuBtn.addEventListener('click', openSidebar); if (closeBtn) closeBtn.addEventListener('click', closeSidebar); if (overlay) overlay.addEventListener('click', closeSidebar); } // ========== HTML 转义 ========== function escapeHtml(text) { if (!text) return ''; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // ========== 显示 Toast 消息 ========== function showToast(message, type = 'success') { const toast = document.createElement('div'); toast.className = `fixed bottom-4 right-4 z-50 px-4 py-2 rounded-lg shadow-lg text-white ${ type === 'success' ? 'bg-green-500' : 'bg-red-500' }`; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => toast.remove(), 3000); } // ========== 页面初始化 ========== document.addEventListener('DOMContentLoaded', () => { initSidebar(); loadUserInfo(); const logoutBtn = document.getElementById('logoutBtn'); if (logoutBtn) { logoutBtn.addEventListener('click', (e) => { e.preventDefault(); logout(); }); } });