/** * 认证模块管理器 * 处理登录、注册、注销及权限检查 * 适配 Bootstrap 5 样式 */ class AuthManager { constructor() { this.apiBase = '/api'; this.init(); } init() { this.createNotificationContainer(); this.initEventListeners(); this.checkAuthStatus(); } /** * 创建通知容器 (适配 Bootstrap Toast) */ createNotificationContainer() { if (!document.getElementById('notification-container')) { const container = document.createElement('div'); container.id = 'notification-container'; container.className = 'toast-container position-fixed top-0 end-0 p-3'; container.style.zIndex = '9999'; document.body.appendChild(container); } } /** * 检查用户认证状态 */ async checkAuthStatus() { const currentPath = window.location.pathname; const isAuthPage = currentPath.includes('/login') || currentPath.includes('/register'); try { const response = await fetch(`${this.apiBase}/auth/me`); const data = await response.json(); if (data.success && data.data && data.data.user) { const redirectUrl = this.getDashboardUrl(data.data.user.role); if (isAuthPage || currentPath === '/') { window.location.href = redirectUrl; } } } catch (error) { console.error('Auth check failed:', error); } } getDashboardUrl(role) { switch(role) { case 'student': return '/student/dashboard'; case 'teacher': return '/teacher/dashboard'; case 'admin': return '/admin/dashboard'; default: return '/dashboard'; } } initEventListeners() { // 登录表单 const loginForm = document.getElementById('loginForm'); if (loginForm) { loginForm.addEventListener('submit', (e) => this.handleLogin(e)); } // 注册表单 const registerForm = document.getElementById('registerForm'); if (registerForm) { registerForm.addEventListener('submit', (e) => this.handleRegister(e)); // 角色选择联动 const roleSelect = document.getElementById('role'); if (roleSelect) { roleSelect.addEventListener('change', (e) => this.handleRoleChange(e)); } } // 注销按钮 document.querySelectorAll('.btn-logout, #logoutBtn').forEach(btn => { btn.addEventListener('click', (e) => this.handleLogout(e)); }); } handleRoleChange(e) { const role = e.target.value; const classField = document.getElementById('classField'); const classInput = document.getElementById('class'); if (classField && classInput) { if (role === 'student' || role === 'teacher') { classField.style.display = 'flex'; // 配合 Bootstrap input-group classInput.required = true; } else { classField.style.display = 'none'; classInput.required = false; classInput.value = ''; } } } async handleLogin(e) { e.preventDefault(); const form = e.target; const submitBtn = document.getElementById('loginBtn') || form.querySelector('button[type="submit"]'); if (this.setLoading(submitBtn, true)) { try { const formData = new FormData(form); const data = Object.fromEntries(formData.entries()); const response = await fetch(`${this.apiBase}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await response.json(); if (result.success) { this.showNotification('登录成功,正在跳转...', 'success'); // 获取用户信息,优先从 result.data.user 获取,兼容旧格式 result.data 或 result.user const user = (result.data && result.data.user) || result.data || result.user; const role = user ? user.role : null; if (role) { setTimeout(() => { window.location.href = this.getDashboardUrl(role); }, 1000); } else { console.error('Login success but role not found:', result); this.showNotification('登录状态异常,请重试', 'error'); this.setLoading(submitBtn, false); } } else { this.showNotification(result.message || '登录失败', 'error'); this.setLoading(submitBtn, false); } } catch (error) { console.error('Login error:', error); this.showNotification('服务器连接失败,请稍后重试', 'error'); this.setLoading(submitBtn, false); } } } async handleRegister(e) { e.preventDefault(); const form = e.target; const submitBtn = document.getElementById('registerBtn') || form.querySelector('button[type="submit"]'); const formData = new FormData(form); const data = Object.fromEntries(formData.entries()); if (data.password !== data.confirmPassword) { this.showNotification('两次输入的密码不一致', 'error'); return; } if (this.setLoading(submitBtn, true)) { try { const response = await fetch(`${this.apiBase}/auth/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await response.json(); if (result.success) { this.showNotification('注册成功,请登录', 'success'); setTimeout(() => { window.location.href = '/login'; }, 1500); } else { this.showNotification(result.message || '注册失败', 'error'); this.setLoading(submitBtn, false); } } catch (error) { console.error('Register error:', error); this.showNotification('服务器连接失败,请稍后重试', 'error'); this.setLoading(submitBtn, false); } } } async handleLogout(e) { e.preventDefault(); if (confirm('确定要退出登录吗?')) { try { const response = await fetch(`${this.apiBase}/auth/logout`, { method: 'POST' }); const result = await response.json(); if (result.success) { alert('退出登录成功'); window.location.href = '/login'; } else { alert(result.message || '退出登录失败'); } } catch (error) { console.error('Logout error:', error); alert('退出登录出错'); } } } /** * 设置按钮加载状态 */ setLoading(btn, isLoading) { if (!btn) return false; const spinner = btn.querySelector('.spinner-border'); if (isLoading) { if (btn.disabled) return false; btn.disabled = true; if (spinner) spinner.classList.remove('d-none'); } else { btn.disabled = false; if (spinner) spinner.classList.add('d-none'); } return true; } /** * 显示通知 (Bootstrap 5 Toast) */ showNotification(message, type = 'info') { const container = document.getElementById('notification-container'); if (!container) return; const iconMap = { success: 'fa-check-circle', error: 'fa-exclamation-circle', warning: 'fa-exclamation-triangle', info: 'fa-info-circle' }; const bgMap = { success: 'bg-success', error: 'bg-danger', warning: 'bg-warning', info: 'bg-info' }; const toastId = 'toast-' + Date.now(); const toastHtml = `