Files
ClassManager/frontend/assets/js/common.js
2026-04-14 14:19:19 +08:00

218 lines
6.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 班级操行分管理系统 - 公共JS
*
* 开发者: Canglan
* 联系方式: admin@sea-studio.top
* 版权归属: Sea Network Technology Studio
* 许可证: MIT License
*
* 版权所有 © Sea Network Technology Studio
*/
function getToken() {
return localStorage.getItem(window.JWT_STORAGE_KEY || 'class_system_token');
}
function getUserInfo() {
const userStr = localStorage.getItem(window.USER_STORAGE_KEY || 'class_system_user');
if (!userStr) return null;
try {
return JSON.parse(userStr);
} catch {
return null;
}
}
function setUserInfo(user) {
localStorage.setItem(window.USER_STORAGE_KEY || 'class_system_user', JSON.stringify(user));
}
function clearAuth() {
localStorage.removeItem(window.JWT_STORAGE_KEY || 'class_system_token');
localStorage.removeItem(window.USER_STORAGE_KEY || 'class_system_user');
}
async function apiRequest(url, options = {}) {
const token = getToken();
const headers = {
'Content-Type': 'application/json',
...options.headers
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const baseUrl = window.API_BASE_URL;
const fullUrl = `${baseUrl}${url}`;
try {
const response = await fetch(fullUrl, { ...options, headers });
const data = await response.json();
if (response.status === 401) {
clearAuth();
// 同步清除 PHP Session防止 index.php 302 重定向循环
try {
await fetch('/api/clear_session.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
} catch (e) {
console.warn('[Auth] 清除PHP Session失败:', e);
}
// 防循环机制:检查是否已在登录页
if (window.location.pathname === '/index.php' || window.location.pathname === '/') {
console.warn('[Auth] 已在登录页收到401停止重定向');
return null;
}
// 防循环机制5秒内重复401则停止重定向
const now = Date.now();
const lastRedirect = parseInt(sessionStorage.getItem('_last_401_redirect') || '0');
if (now - lastRedirect < 5000) {
console.warn('[Auth] 5秒内重复401停止重定向。请检查Token是否有效。');
return null;
}
sessionStorage.setItem('_last_401_redirect', now.toString());
window.location.href = '/index.php';
return null;
}
return data;
} catch (error) {
console.error('API请求错误:', error);
showToast('网络错误,请稍后重试', 'error');
return null;
}
}
function apiGet(url, params = {}) {
const queryString = new URLSearchParams(params).toString();
const fullUrl = queryString ? `${url}?${queryString}` : url;
return apiRequest(fullUrl, { method: 'GET' });
}
function apiPost(url, data = {}) {
return apiRequest(url, {
method: 'POST',
body: JSON.stringify(data)
});
}
function apiPut(url, data = {}) {
return apiRequest(url, {
method: 'PUT',
body: JSON.stringify(data)
});
}
function apiDelete(url) {
return apiRequest(url, { method: 'DELETE' });
}
function showToast(message, type = 'success') {
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 3000);
}
function formatDate(dateStr) {
if (!dateStr) return '-';
const date = new Date(dateStr);
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
}
function formatDateTime(dateStr) {
if (!dateStr) return '-';
const date = new Date(dateStr);
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
}
function getStatusBadge(status, type = 'homework') {
const statusMap = {
homework: {
'submitted': '已提交',
'not_submitted': '未提交',
'late': '迟交'
},
attendance: {
'present': '出勤',
'absent': '缺勤',
'late': '迟到',
'leave': '请假'
}
};
const texts = statusMap[type] || statusMap.homework;
const text = texts[status] || status;
let className = 'status-badge ';
switch (status) {
case 'submitted':
case 'present':
className += 'status-submitted';
break;
case 'not_submitted':
case 'absent':
className += 'status-not_submitted';
break;
case 'late':
className += 'status-late';
break;
case 'leave':
className += 'status-leave';
break;
default:
className += 'status-not_submitted';
}
return `<span class="${className}">${text}</span>`;
}
async function logout() {
// 清除 PHP Session
try {
await fetch('/api/clear_session.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
} catch (e) {
console.warn('清除Session失败', e);
}
// 清除后端 Token
try {
await apiPost('/api/auth/logout');
} catch (e) {
console.warn('后端登出失败', e);
}
// 清除 localStorage
clearAuth();
// 跳转回登录页
window.location.href = '/index.php';
}
function escapeHtml(str) {
if (!str) return '';
return str.replace(/[&<>]/g, function(m) {
if (m === '&') return '&amp;';
if (m === '<') return '&lt;';
if (m === '>') return '&gt;';
return m;
});
}
document.addEventListener('DOMContentLoaded', () => {
const user = getUserInfo();
const userNameSpan = document.getElementById('userName');
if (userNameSpan && user) {
userNameSpan.textContent = user.real_name || user.username;
}
const logoutBtn = document.getElementById('logoutBtn');
if (logoutBtn) {
logoutBtn.addEventListener('click', logout);
}
});