- 后端从 Python FastAPI 重写为 Go Gin(端口 56789) - 多班级完全隔离 - 超级管理员独立登录 - 课代表作业管理、排行榜分项排行 - 角色加减分上下限可配置 - 家长改密功能(可开关) - 周度/月度重置功能 - MySQL 5.7 兼容 - 43轮代码审查+全部修复 - Apache 2.0 许可证
215 lines
7.8 KiB
PHP
215 lines
7.8 KiB
PHP
<?php
|
|
/**
|
|
* 共享班级管理系统 - 班级管理页面
|
|
*
|
|
* 开发者: Canglan
|
|
* 联系方式: admin@sea-studio.top
|
|
* 版权归属: Sea Network Technology Studio
|
|
* 许可证: Apache License 2.0
|
|
*
|
|
* 版权所有 © Sea Network Technology Studio
|
|
*/
|
|
|
|
$page_title = '班级管理';
|
|
require_once __DIR__ . '/../config.php';
|
|
|
|
// 权限检查
|
|
if (!isset($_SESSION['user_type']) || $_SESSION['user_type'] !== 'super_admin') {
|
|
header('Location: /admin/dashboard.php');
|
|
exit();
|
|
}
|
|
|
|
require_once __DIR__ . '/../includes/header.php';
|
|
require_once __DIR__ . '/../includes/nav.php';
|
|
?>
|
|
|
|
<div class="container">
|
|
<div class="page-header">
|
|
<h2>班级管理</h2>
|
|
<button class="btn btn-primary" onclick="showCreateModal()">新增班级</button>
|
|
</div>
|
|
|
|
<div id="classList" class="card-grid">
|
|
<div class="loading">加载中...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 创建/编辑班级弹窗 -->
|
|
<div id="classModal" class="modal" style="display:none;">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h3 id="modalTitle">新增班级</h3>
|
|
<button class="btn-close" onclick="closeModal()">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<input type="hidden" id="editClassId">
|
|
<div class="form-group">
|
|
<label>班级名称 <span class="required">*</span></label>
|
|
<input type="text" id="className" placeholder="如:高一(1)班" maxlength="100">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>年级</label>
|
|
<input type="text" id="classGrade" placeholder="如:高一" maxlength="50">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>描述</label>
|
|
<textarea id="classDesc" placeholder="班级描述(选填)" maxlength="255"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-secondary" onclick="closeModal()">取消</button>
|
|
<button class="btn btn-primary" onclick="saveClass()">保存</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
async function loadClasses() {
|
|
const result = await apiGet('/api/class/list', { include_disabled: true });
|
|
if (result && result.success) {
|
|
renderClasses(result.data.classes || []);
|
|
} else {
|
|
document.getElementById('classList').innerHTML = '<div class="empty-state">暂无班级数据</div>';
|
|
}
|
|
}
|
|
|
|
function renderClasses(classes) {
|
|
if (classes.length === 0) {
|
|
document.getElementById('classList').innerHTML = '<div class="empty-state">暂无班级数据,点击右上角"新增班级"创建</div>';
|
|
return;
|
|
}
|
|
|
|
let html = '';
|
|
classes.forEach(cls => {
|
|
const statusBadge = cls.status === 1
|
|
? '<span class="status-badge status-submitted">启用</span>'
|
|
: '<span class="status-badge status-not_submitted">禁用</span>';
|
|
html += `
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3>${escapeHtml(cls.class_name)}</h3>
|
|
${statusBadge}
|
|
</div>
|
|
<div class="card-body">
|
|
<p>年级: ${escapeHtml(cls.grade || '-')}</p>
|
|
<p>学生人数: ${cls.student_count || 0}</p>
|
|
<p>描述: ${escapeHtml(cls.description || '-')}</p>
|
|
</div>
|
|
<div class="card-footer">
|
|
<button class="btn btn-sm btn-primary" onclick="switchClass(${cls.class_id}, '${escapeHtml(cls.class_name)}')">进入班级</button>
|
|
<button class="btn btn-sm btn-secondary" onclick="editClass(${cls.class_id}, '${escapeHtml(cls.class_name)}', '${escapeHtml(cls.grade || '')}', '${escapeHtml(cls.description || '')}')">编辑</button>
|
|
<button class="btn btn-sm btn-danger" onclick="deleteClass(${cls.class_id}, '${escapeHtml(cls.class_name)}')">删除</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
});
|
|
document.getElementById('classList').innerHTML = html;
|
|
}
|
|
|
|
function showCreateModal() {
|
|
document.getElementById('modalTitle').textContent = '新增班级';
|
|
document.getElementById('editClassId').value = '';
|
|
document.getElementById('className').value = '';
|
|
document.getElementById('classGrade').value = '';
|
|
document.getElementById('classDesc').value = '';
|
|
document.getElementById('classModal').style.display = 'flex';
|
|
}
|
|
|
|
function editClass(classId, name, grade, desc) {
|
|
document.getElementById('modalTitle').textContent = '编辑班级';
|
|
document.getElementById('editClassId').value = classId;
|
|
document.getElementById('className').value = name;
|
|
document.getElementById('classGrade').value = grade;
|
|
document.getElementById('classDesc').value = desc;
|
|
document.getElementById('classModal').style.display = 'flex';
|
|
}
|
|
|
|
function closeModal() {
|
|
document.getElementById('classModal').style.display = 'none';
|
|
}
|
|
|
|
async function saveClass() {
|
|
const classId = document.getElementById('editClassId').value;
|
|
const data = {
|
|
class_name: document.getElementById('className').value.trim(),
|
|
grade: document.getElementById('classGrade').value.trim() || null,
|
|
description: document.getElementById('classDesc').value.trim() || null
|
|
};
|
|
|
|
if (!data.class_name) {
|
|
showToast('请输入班级名称', 'error');
|
|
return;
|
|
}
|
|
|
|
let result;
|
|
if (classId) {
|
|
result = await apiPut(`/api/class/update/${classId}`, data);
|
|
} else {
|
|
result = await apiPost('/api/class/create', data);
|
|
}
|
|
|
|
if (result && result.success) {
|
|
showToast(classId ? '班级更新成功' : '班级创建成功');
|
|
closeModal();
|
|
loadClasses();
|
|
} else {
|
|
showToast(result ? result.message : '操作失败', 'error');
|
|
}
|
|
}
|
|
|
|
async function deleteClass(classId, className) {
|
|
if (!confirm(`确定要删除班级 "${className}" 吗?此操作不可撤销。`)) return;
|
|
|
|
const result = await apiDelete(`/api/class/delete/${classId}`);
|
|
if (result && result.success) {
|
|
showToast('班级已删除');
|
|
loadClasses();
|
|
} else {
|
|
showToast(result ? result.message : '删除失败', 'error');
|
|
}
|
|
}
|
|
|
|
async function switchClass(classId, className) {
|
|
const result = await apiPost('/api/class/switch', { class_id: classId });
|
|
if (result && result.success) {
|
|
// 更新本地存储的用户信息
|
|
const userInfo = getUserInfo();
|
|
if (userInfo) {
|
|
userInfo.class_id = classId;
|
|
userInfo.class_name = className;
|
|
// 更新token
|
|
if (result.data && result.data.token) {
|
|
localStorage.setItem(window.JWT_STORAGE_KEY || 'class_system_token', result.data.token);
|
|
}
|
|
setUserInfo(userInfo);
|
|
}
|
|
// 同步到 PHP Session
|
|
try {
|
|
await fetch('/api/save_session.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + getToken() },
|
|
body: JSON.stringify({
|
|
user_id: userInfo.user_id,
|
|
user_type: userInfo.user_type,
|
|
username: userInfo.username,
|
|
real_name: userInfo.real_name,
|
|
role: userInfo.role,
|
|
class_id: classId,
|
|
class_name: className
|
|
})
|
|
});
|
|
} catch (e) {
|
|
console.warn('同步Session失败', e);
|
|
}
|
|
showToast(`已切换到: ${className}`);
|
|
window.location.href = '/admin/dashboard.php';
|
|
} else {
|
|
showToast(result ? result.message : '切换失败', 'error');
|
|
}
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', loadClasses);
|
|
</script>
|
|
|
|
<?php require_once __DIR__ . '/../includes/footer.php'; ?>
|