const BASE = '/api'; let token = localStorage.getItem('wk5_token') || ''; let role = localStorage.getItem('wk5_role') || ''; document.addEventListener('DOMContentLoaded', () => { if (token) { showMain(); loadStudents(); checkRoleUI(); } }); // ==================== 认证 ==================== async function doLogin() { const username = document.getElementById('login-username').value.trim(); const password = document.getElementById('login-password').value.trim(); if (!username || !password) { showLoginError('请输入用户名和密码'); return; } try { const resp = await fetch(BASE + '/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); const result = await resp.json(); if (resp.ok && result.code === 200) { token = result.data.token; role = result.data.role; localStorage.setItem('wk5_token', token); localStorage.setItem('wk5_role', role); showMain(); loadStudents(); checkRoleUI(); } else { showLoginError(result.message || '登录失败'); } } catch (e) { showLoginError('连接失败: ' + e.message); } } async function doRegister() { const username = document.getElementById('reg-username').value.trim(); const password = document.getElementById('reg-password').value.trim(); if (!username || !password) { document.getElementById('reg-error').textContent = '请输入用户名和密码'; return; } try { const resp = await fetch(BASE + '/auth/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); const result = await resp.json(); if (resp.ok && result.code === 200) { token = result.data.token; role = result.data.role; localStorage.setItem('wk5_token', token); localStorage.setItem('wk5_role', role); showMain(); loadStudents(); checkRoleUI(); } else { document.getElementById('reg-error').textContent = result.message || '注册失败'; } } catch (e) { document.getElementById('reg-error').textContent = '连接失败'; } } function showLoginError(msg) { document.getElementById('login-error').textContent = msg; } function showLogin() { document.getElementById('login-panel').style.display = 'block'; document.getElementById('register-panel').style.display = 'none'; document.getElementById('main-panel').style.display = 'none'; } function showRegister() { document.getElementById('login-panel').style.display = 'none'; document.getElementById('register-panel').style.display = 'block'; } function showMain() { document.getElementById('login-panel').style.display = 'none'; document.getElementById('register-panel').style.display = 'none'; document.getElementById('main-panel').style.display = 'block'; } function logout() { localStorage.removeItem('wk5_token'); localStorage.removeItem('wk5_role'); token = ''; role = ''; showLogin(); } /** 根据角色显示/隐藏操作按钮 */ function checkRoleUI() { document.getElementById('user-info').textContent = '当前: ' + (role || '-') + ' | '; document.getElementById('btn-add').style.display = (role === 'ADMIN') ? '' : 'none'; } // ==================== 请求封装 ==================== async function api(url, options) { options = options || {}; options.headers = options.headers || {}; options.headers['Authorization'] = 'Bearer ' + token; const resp = await fetch(url, options); const result = await resp.json(); if (resp.status === 403) { showError('权限不足:需要 ADMIN 角色'); return null; } if ((resp.status === 401 || resp.status === 403) && result.code !== 200) { logout(); showError('认证失败,请重新登录'); return null; } if (result.extra && result.extra.orm) { document.getElementById('stat-orm').textContent = result.extra.orm; } return result; } function showError(msg) { const bar = document.getElementById('error-bar'); bar.textContent = '⚠ ' + msg; bar.style.display = 'block'; setTimeout(() => bar.style.display = 'none', 4000); } // ==================== 数据 ==================== async function loadStudents(keyword) { const tbody = document.getElementById('table-body'); tbody.innerHTML = '加载中...'; let url = BASE + '/students'; if (keyword) url += '?keyword=' + encodeURIComponent(keyword); const result = await api(url); if (!result) { tbody.innerHTML = '加载失败'; return; } const students = result.data; if (!students || students.length === 0) { tbody.innerHTML = '暂无数据'; } else { tbody.innerHTML = students.map(s => { let cls = 'badge-poor'; if (s.score >= 90) cls = 'badge-excellent'; else if (s.score >= 80) cls = 'badge-good'; else if (s.score >= 60) cls = 'badge-normal'; const actions = role === 'ADMIN' ? ` ` : '--'; return ` ${s.id}${escapeHtml(s.name)} ${s.age}${escapeHtml(s.email)} ${s.score} ${actions}`; }).join(''); } document.getElementById('stat-total').textContent = result.extra?.total ?? students.length; const statResp = await api(BASE + '/students/stats/excellent?min=85'); if (statResp) document.getElementById('stat-excellent').textContent = statResp.data; } function search() { loadStudents(document.getElementById('keyword').value.trim() || undefined); } // ==================== 表单 ==================== async function showForm(id) { document.getElementById('form-id').value = ''; document.getElementById('student-form').reset(); if (id) { document.getElementById('modal-title').textContent = '编辑学生'; const result = await api(BASE + '/students/' + id); if (!result) return; const s = result.data; document.getElementById('form-id').value = s.id; document.getElementById('form-name').value = s.name; document.getElementById('form-age').value = s.age; document.getElementById('form-email').value = s.email; document.getElementById('form-score').value = s.score; } else { document.getElementById('modal-title').textContent = '新增学生'; } document.getElementById('modal-overlay').classList.add('show'); } async function submitForm(e) { e.preventDefault(); const id = document.getElementById('form-id').value; const body = { name: document.getElementById('form-name').value.trim(), age: parseInt(document.getElementById('form-age').value), email: document.getElementById('form-email').value.trim(), score: parseInt(document.getElementById('form-score').value) }; let result; if (id) { result = await api(BASE + '/students/' + id, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); } else { result = await api(BASE + '/students', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); } if (result) { closeModal(); loadStudents(); } } async function deleteStudent(id) { if (!confirm('确认删除?(MP 模式下为逻辑删除)')) return; const result = await api(BASE + '/students/' + id, { method: 'DELETE' }); if (result) loadStudents(); } function closeModal() { document.getElementById('modal-overlay').classList.remove('show'); } document.addEventListener('click', e => { if (e.target.id === 'modal-overlay') closeModal(); }); document.addEventListener('keydown', e => { if (e.key === 'Enter' && document.getElementById('login-panel').style.display !== 'none') doLogin(); }); function escapeHtml(t) { const d = document.createElement('div'); d.textContent = t; return d.innerHTML; }