diff --git a/web/static/css/style.css b/web/static/css/style.css index c9156be6..3de42179 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -548,6 +548,74 @@ header { background: var(--bg-primary); } +/* 用户菜单样式 */ +.user-menu-container { + position: relative; +} + +.user-avatar-btn { + display: inline-flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border-radius: 50%; + border: 1px solid var(--border-color); + background: var(--bg-primary); + color: var(--text-secondary); + cursor: pointer; + transition: all 0.2s ease; + padding: 0; +} + +.user-avatar-btn:hover { + background: var(--bg-tertiary); + border-color: var(--accent-color); + color: var(--accent-color); +} + +.user-avatar-btn svg { + stroke: currentColor; +} + +.user-menu-dropdown { + position: absolute; + top: calc(100% + 8px); + right: 0; + min-width: 160px; + background: var(--bg-primary); + border: 1px solid var(--border-color); + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + z-index: 1000; + overflow: hidden; +} + +.user-menu-item { + display: flex; + align-items: center; + gap: 8px; + padding: 12px 16px; + cursor: pointer; + color: var(--text-primary); + font-size: 0.875rem; + transition: all 0.2s ease; + border: none; + background: none; + width: 100%; + text-align: left; +} + +.user-menu-item:hover { + background: var(--bg-tertiary); + color: var(--accent-color); +} + +.user-menu-item svg { + stroke: currentColor; + flex-shrink: 0; +} + .attack-chain-btn:not(:disabled):hover { background: var(--bg-tertiary); border-color: var(--accent-color); diff --git a/web/static/js/auth.js b/web/static/js/auth.js index 4610a7a4..ab0495f4 100644 --- a/web/static/js/auth.js +++ b/web/static/js/auth.js @@ -328,4 +328,59 @@ async function initializeApp() { showLoginOverlay(); } +// 用户菜单控制 +function toggleUserMenu() { + const dropdown = document.getElementById('user-menu-dropdown'); + if (!dropdown) return; + + const isVisible = dropdown.style.display !== 'none'; + dropdown.style.display = isVisible ? 'none' : 'block'; +} + +// 点击页面其他地方时关闭下拉菜单 +document.addEventListener('click', function(event) { + const dropdown = document.getElementById('user-menu-dropdown'); + const avatarBtn = document.querySelector('.user-avatar-btn'); + + if (dropdown && avatarBtn && + !dropdown.contains(event.target) && + !avatarBtn.contains(event.target)) { + dropdown.style.display = 'none'; + } +}); + +// 退出登录 +async function logout() { + // 关闭下拉菜单 + const dropdown = document.getElementById('user-menu-dropdown'); + if (dropdown) { + dropdown.style.display = 'none'; + } + + try { + // 先尝试调用退出API(如果token有效) + if (authToken) { + const headers = new Headers(); + headers.set('Authorization', `Bearer ${authToken}`); + await fetch('/api/auth/logout', { + method: 'POST', + headers: headers, + }).catch(() => { + // 忽略错误,继续清除本地认证信息 + }); + } + } catch (error) { + console.error('退出登录API调用失败:', error); + } finally { + // 无论如何都清除本地认证信息 + clearAuthStorage(); + hideLoginOverlay(); + showLoginOverlay('已退出登录'); + } +} + +// 导出函数供HTML使用 +window.toggleUserMenu = toggleUserMenu; +window.logout = logout; + document.addEventListener('DOMContentLoaded', initializeApp); diff --git a/web/templates/index.html b/web/templates/index.html index 4d6741e2..1c76556e 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -42,6 +42,24 @@ 攻击链 +