class AdminDashboard { constructor() { // 动态设置API基础URL,支持file:///协议和localhost:3000访问 this.apiBase = window.location.protocol === 'file:' ? 'http://localhost:3000/api' : '/api'; this.currentUser = null; this.stats = {}; this.users = []; this.students = []; this.teachers = []; this.init(); } async init() { // 检查登录状? if (!await this.checkAuth()) { window.location.href = '/login'; return; } // 加载用户信息 await this.loadUserInfo(); // 加载统计数据 await this.loadStats(); // 加载用户数据 await this.loadUsers(); // 绑定事件 this.bindEvents(); // 更新界面 this.updateUI(); // 初始化图? this.initCharts(); } async checkAuth() { try { const response = await fetch(`${this.apiBase}/auth/me`, { credentials: 'include' }); if (!response.ok) { return false; } const data = await response.json(); return data.success && data.user.role === 'admin'; } catch (error) { console.error('认证检查失?', error); return false; } } async loadUserInfo() { try { const response = await fetch(`${this.apiBase}/auth/me`, { credentials: 'include' }); if (response.ok) { const data = await response.json(); if (data.success) { this.currentUser = data.user; } } } catch (error) { console.error('加载用户信息失败:', error); } } async loadStats() { try { const response = await fetch(`${this.apiBase}/admin/stats`, { credentials: 'include' }); if (response.ok) { const data = await response.json(); if (data.success) { this.stats = data.stats; this.updateStatsUI(); } } } catch (error) { console.error('加载统计数据失败:', error); this.showNotification('加载统计数据失败', 'error'); } } async loadUsers() { try { const response = await fetch(`${this.apiBase}/admin/users`, { credentials: 'include' }); if (response.ok) { const data = await response.json(); if (data.success) { this.users = data.users; this.renderUsersTable(); } } } catch (error) { console.error('加载用户数据失败:', error); this.showNotification('加载用户数据失败', 'error'); } } updateStatsUI() { // 更新统计卡片 const statElements = { 'totalUsers': 'totalUsers', 'totalStudents': 'totalStudents', 'totalTeachers': 'totalTeachers', 'totalCourses': 'totalCourses', 'totalGrades': 'totalGrades', 'avgScore': 'avgScore' }; Object.entries(statElements).forEach(([key, elementId]) => { const element = document.getElementById(elementId); if (element && this.stats[key] !== undefined) { element.textContent = this.stats[key]; } }); // 更新时间 const timeElement = document.getElementById('currentTime'); if (timeElement) { timeElement.textContent = new Date().toLocaleString(); } } renderUsersTable() { const tableBody = document.getElementById('usersTableBody'); if (!tableBody) return; if (this.users.length === 0) { tableBody.innerHTML = `

暂无用户数据

`; return; } tableBody.innerHTML = this.users.map(user => { const roleClass = this.getRoleClass(user.role); return ` ${user.user_id} ${user.full_name} ${user.role} ${user.class_name || 'N/A'} ${user.email || 'N/A'}
`; }).join(''); } getRoleClass(role) { switch (role) { case 'admin': return 'role-admin'; case 'teacher': return 'role-teacher'; case 'student': return 'role-student'; default: return 'role-default'; } } bindEvents() { // 导航菜单点击 document.querySelectorAll('.nav-link').forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); const page = link.dataset.page; this.loadPage(page); }); }); // 搜索按钮 document.getElementById('searchBtn')?.addEventListener('click', () => { this.handleSearch(); }); // 重置按钮 document.getElementById('resetBtn')?.addEventListener('click', () => { this.resetFilters(); }); // 添加用户按钮 document.getElementById('addUserBtn')?.addEventListener('click', () => { this.addUser(); }); // 导出按钮 document.getElementById('exportBtn')?.addEventListener('click', () => { this.exportUsers(); }); // 批量删除按钮 document.getElementById('batchDeleteBtn')?.addEventListener('click', () => { this.batchDeleteUsers(); }); // 表格操作按钮事件委托 document.addEventListener('click', (e) => { if (e.target.closest('.btn-edit')) { const userId = e.target.closest('.btn-edit').dataset.id; this.editUser(userId); } if (e.target.closest('.btn-delete')) { const userId = e.target.closest('.btn-delete').dataset.id; this.deleteUser(userId); } }); // 退出登? document.getElementById('logoutBtn')?.addEventListener('click', () => { this.handleLogout(); }); // 刷新按钮 document.getElementById('refreshBtn')?.addEventListener('click', () => { this.refreshData(); }); } async loadPage(page) { // 这里可以实现页面切换逻辑 // 暂时使用简单跳? switch (page) { case 'users': window.location.href = '/admin/user_management'; break; case 'students': window.location.href = '/admin/student_management'; break; case 'teachers': // 可以跳转到教师管理页? break; case 'grades': window.location.href = '/teacher/grade_management'; break; case 'settings': // 可以跳转到系统设置页? break; } } handleSearch() { const userId = document.getElementById('userIdFilter')?.value || ''; const name = document.getElementById('nameFilter')?.value || ''; const role = document.getElementById('roleFilter')?.value || ''; const className = document.getElementById('classFilter')?.value || ''; // 这里可以实现搜索逻辑 this.showNotification('搜索功能待实?, 'info'); } resetFilters() { document.getElementById('userIdFilter').value = ''; document.getElementById('nameFilter').value = ''; document.getElementById('roleFilter').value = ''; document.getElementById('classFilter').value = ''; // 重新加载数据 this.loadUsers(); } async addUser() { // 这里可以打开添加用户模态框 const userData = { user_id: prompt('请输入用户ID:'), full_name: prompt('请输入姓?'), role: prompt('请输入角?(admin/teacher/student):'), email: prompt('请输入邮?'), class_name: prompt('请输入班?(学生/教师可?:') }; if (!userData.user_id || !userData.full_name || !userData.role) { this.showNotification('用户ID、姓名和角色为必填项', 'error'); return; } try { const response = await fetch(`${this.apiBase}/admin/users`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify(userData) }); const data = await response.json(); if (data.success) { this.showNotification('用户添加成功', 'success'); await this.loadUsers(); } else { this.showNotification(data.message || '添加失败', 'error'); } } catch (error) { console.error('添加用户失败:', error); this.showNotification('添加用户失败', 'error'); } } async exportUsers() { try { const response = await fetch(`${this.apiBase}/admin/users/export`, { credentials: 'include' }); if (response.ok) { const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `用户列表_${new Date().toISOString().split('T')[0]}.xlsx`; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); } } catch (error) { console.error('导出失败:', error); this.showNotification('导出失败', 'error'); } } async batchDeleteUsers() { const checkboxes = document.querySelectorAll('.user-checkbox:checked'); if (checkboxes.length === 0) { this.showNotification('请选择要删除的用户', 'warning'); return; } if (!confirm(`确定要删除选中?${checkboxes.length} 个用户吗?`)) { return; } const userIds = Array.from(checkboxes).map(cb => cb.dataset.id); try { const response = await fetch(`${this.apiBase}/admin/users/batch`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ userIds }) }); const data = await response.json(); if (data.success) { this.showNotification(`成功删除 ${userIds.length} 个用户`, 'success'); await this.loadUsers(); } else { this.showNotification(data.message || '删除失败', 'error'); } } catch (error) { console.error('批量删除失败:', error); this.showNotification('批量删除失败', 'error'); } } async editUser(userId) { const user = this.users.find(u => u.id == userId); if (!user) return; // 这里可以打开编辑模态框 const newName = prompt('请输入新的姓?', user.full_name); if (newName === null) return; const newRole = prompt('请输入新的角?', user.role); if (newRole === null) return; try { const response = await fetch(`${this.apiBase}/admin/users/${userId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ full_name: newName, role: newRole, email: user.email, class_name: user.class_name }) }); const data = await response.json(); if (data.success) { this.showNotification('用户更新成功', 'success'); await this.loadUsers(); } else { this.showNotification(data.message || '更新失败', 'error'); } } catch (error) { console.error('更新用户失败:', error); this.showNotification('更新用户失败', 'error'); } } async deleteUser(userId) { if (!confirm('确定要删除这个用户吗?)) { return; } try { const response = await fetch(`${this.apiBase}/admin/users/${userId}`, { method: 'DELETE', credentials: 'include' }); const data = await response.json(); if (data.success) { this.showNotification('用户删除成功', 'success'); await this.loadUsers(); } else { this.showNotification(data.message || '删除失败', 'error'); } } catch (error) { console.error('删除用户失败:', error); this.showNotification('删除用户失败', 'error'); } } async handleLogout() { try { const response = await fetch(`${this.apiBase}/auth/logout`, { method: 'POST', credentials: 'include' }); if (response.ok) { window.location.href = '/login'; } } catch (error) { console.error('退出登录失?', error); } } async refreshData() { await this.loadStats(); await this.loadUsers(); this.showNotification('数据已刷?, 'success'); } updateUI() { // 更新用户信息 if (this.currentUser) { const userInfoElements = document.querySelectorAll('.user-info'); userInfoElements.forEach(el => { el.textContent = `${this.currentUser.full_name} (${this.currentUser.role})`; }); } } async initCharts() { // 加载Chart.js? await this.loadChartLibrary(); // 初始化用户分布饼? this.initUserDistributionChart(); // 初始化成绩分布柱状图 this.initGradeDistributionChart(); } showNotification(message, type = 'info') { // 创建通知元素 const notification = document.createElement('div'); notification.className = `notification notification-${type}`; notification.innerHTML = ` ${message} `; // 添加到页? document.body.appendChild(notification); // 添加关闭事件 notification.querySelector('.notification-close').addEventListener('click', () => { notification.remove(); }); // 自动移除 setTimeout(() => { if (notification.parentNode) { notification.remove(); } }, 5000); } async loadChartLibrary() { if (typeof Chart !== 'undefined') return; return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js'; script.onload = resolve; script.onerror = reject; document.head.appendChild(script); }); } initUserDistributionChart() { const ctx = document.getElementById('userDistributionChart'); if (!ctx) return; // 模拟数据 const data = { labels: ['学生', '教师', '管理?], datasets: [{ data: [this.stats.totalStudents || 100, this.stats.totalTeachers || 20, 1], backgroundColor: [ 'rgba(54, 162, 235, 0.8)', 'rgba(255, 206, 86, 0.8)', 'rgba(255, 99, 132, 0.8)' ] }] }; new Chart(ctx, { type: 'pie', data: data, options: { responsive: true, plugins: { legend: { position: 'bottom' } } } }); } initGradeDistributionChart() { const ctx = document.getElementById('gradeDistributionChart'); if (!ctx) return; // 模拟数据 const data = { labels: ['A', 'B', 'C', 'D', 'F'], datasets: [{ label: '成绩分布', data: [25, 35, 20, 15, 5], backgroundColor: [ 'rgba(75, 192, 192, 0.8)', 'rgba(54, 162, 235, 0.8)', 'rgba(255, 206, 86, 0.8)', 'rgba(255, 159, 64, 0.8)', 'rgba(255, 99, 132, 0.8)' ] }] }; new Chart(ctx, { type: 'bar', data: data, options: { responsive: true, scales: { y: { beginAtZero: true, ticks: { stepSize: 10 } } } } }); } }