feat: 多班级版班级管理系统 v2.0
技术栈:Go (Gin + GORM) + PHP + MySQL 5.7 + Redis 主要功能: - 多班级完全隔离(class_id 贯穿全系统) - 后端 Go Gin(端口 56789),Nginx 反代 - 超级管理员独立登录(env 配置,默认账密 admin/Admin123) - bcrypt 密码加密(无 PASSWORD_SALT) - 科任老师/课代表新角色 - 课代表作业管理页面 - 排行榜分项排行(操行分/考勤/作业) - 角色加减分上下限由班主任配置 - 家长改密功能(可开关) - 班级角色按需开关 - 宿舍号格式:南0-000 - 周度/月度重置功能 - MySQL 5.7 兼容 - 43 轮代码审查 + 全部修复 开发者: Canglan 版权归属: Sea Network Technology Studio 许可证: Apache License 2.0
This commit is contained in:
219
frontend/admin/dashboard.php
Normal file
219
frontend/admin/dashboard.php
Normal file
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
/**
|
||||
* 多班级版班级管理系统 - 管理端首页
|
||||
*
|
||||
* 开发者: Canglan
|
||||
* 联系方式: admin@sea-studio.top
|
||||
* 版权归属: Sea Network Technology Studio
|
||||
* 许可证: Apache License 2.0
|
||||
*
|
||||
* 版权所有 © Sea Network Technology Studio
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../config.php';
|
||||
|
||||
if (!isset($_SESSION['user_id']) || $_SESSION['user_type'] !== 'admin') {
|
||||
header('Location: /index.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
$page_title = '首页';
|
||||
$role = $_SESSION['role'] ?? '';
|
||||
include __DIR__ . '/../includes/header.php';
|
||||
?>
|
||||
|
||||
<?php include __DIR__ . '/../includes/nav.php'; ?>
|
||||
|
||||
<div class="container">
|
||||
<div class="stats-grid" id="dashboardStats"></div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">快捷操作</div>
|
||||
<div class="action-buttons" id="quickActions"></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">操行分排行榜</div>
|
||||
<div class="table-wrapper">
|
||||
<div style="display: flex; align-items: center; margin-bottom: 12px; gap: 8px;">
|
||||
<span style="font-size: 14px; color: #666;">显示前</span>
|
||||
<input type="number" id="percentileFilter" style="width: 70px; padding: 4px 8px; border: 1px solid #ddd; border-radius: 4px;" min="1" max="100" value="100" placeholder="1-100">
|
||||
<span style="font-size: 14px; color: #666;">% 的学生</span>
|
||||
<button class="btn btn-sm btn-primary" onclick="applyPercentileFilter()">筛选</button>
|
||||
<button class="btn btn-sm btn-ghost" onclick="resetPercentileFilter()">显示全部</button>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr><th>排名</th><th>学号</th><th>姓名</th><th>操行分</th></tr>
|
||||
</thead>
|
||||
<tbody id="rankingList"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($role === '班主任'): ?>
|
||||
<!-- 升级提示模态框 -->
|
||||
<div id="upgradeModal" class="modal" style="display:none;">
|
||||
<div class="modal-content" style="max-width:520px;">
|
||||
<div class="modal-header">
|
||||
<h3>🔄 系统升级</h3>
|
||||
<button class="modal-close" onclick="closeModal('upgradeModal')" id="upgradeCloseBtn">×</button>
|
||||
</div>
|
||||
<div style="padding: 16px 0;">
|
||||
<div style="text-align: center; margin-bottom: 12px;">
|
||||
<p style="font-size: 15px; color: var(--color-text);">检测到数据库有新版本可用</p>
|
||||
<p style="font-size: 13px; color: var(--color-text-muted); margin-top: 8px;">
|
||||
当前版本: <span id="currentDbVersion">--</span> → 目标版本: <span id="targetDbVersion">--</span>
|
||||
</p>
|
||||
</div>
|
||||
<div id="upgradeStepsList" style="margin: 12px 0;"></div>
|
||||
<div id="upgradeResult" style="display:none; margin-top: 12px;"></div>
|
||||
<p id="upgradeWarning" class="text-danger" style="font-size: 12px; text-align: center; margin-top: 8px;">⚠️ 升级前请确保已备份数据库</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" onclick="closeModal('upgradeModal')" id="upgradeLaterBtn">稍后再说</button>
|
||||
<button class="btn btn-primary" onclick="startUpgrade()" id="startUpgradeBtn">开始升级</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<script>window.PAGE_CONFIG = { role: '<?php echo $role; ?>' };</script>
|
||||
<script src="/assets/js/dashboard.js"></script>
|
||||
|
||||
<?php if ($role === '班主任'): ?>
|
||||
<script>
|
||||
(function() {
|
||||
var upgradeSteps = [];
|
||||
var currentStepIndex = 0;
|
||||
|
||||
function escapeHtml(str) {
|
||||
if (typeof str !== 'string') return '';
|
||||
var div = document.createElement('div');
|
||||
div.appendChild(document.createTextNode(str));
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
fetch('/api/check_upgrade.php')
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
// 检查是否返回了错误
|
||||
if (data.error) {
|
||||
console.warn('升级检查失败:', data.error);
|
||||
return;
|
||||
}
|
||||
if (data.needs_upgrade) {
|
||||
document.getElementById('currentDbVersion').textContent = data.current;
|
||||
document.getElementById('targetDbVersion').textContent = data.target;
|
||||
upgradeSteps = data.steps || [];
|
||||
|
||||
// 渲染步骤列表
|
||||
var listHtml = '';
|
||||
for (var i = 0; i < upgradeSteps.length; i++) {
|
||||
listHtml += '<div style="display:flex;align-items:center;padding:8px 12px;margin:4px 0;border-radius:6px;font-size:13px;background:var(--color-hover);border-left:3px solid var(--color-border);" id="ustep-' + i + '">' +
|
||||
'<span style="margin-right:8px;" id="ustep-icon-' + i + '">○</span>' +
|
||||
'<span>升级至 v' + escapeHtml(upgradeSteps[i].version) + '</span>' +
|
||||
'</div>';
|
||||
}
|
||||
document.getElementById('upgradeStepsList').innerHTML = listHtml;
|
||||
document.getElementById('upgradeModal').style.display = 'flex';
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.warn('升级检查请求失败:', err);
|
||||
});
|
||||
|
||||
window.startUpgrade = function() {
|
||||
var btn = document.getElementById('startUpgradeBtn');
|
||||
var closeBtn = document.getElementById('upgradeCloseBtn');
|
||||
var laterBtn = document.getElementById('upgradeLaterBtn');
|
||||
|
||||
btn.disabled = true;
|
||||
btn.textContent = '升级中...';
|
||||
btn.style.opacity = '0.7';
|
||||
closeBtn.style.display = 'none';
|
||||
laterBtn.style.display = 'none';
|
||||
document.getElementById('upgradeWarning').style.display = 'none';
|
||||
|
||||
currentStepIndex = 0;
|
||||
executeNextUpgradeStep();
|
||||
};
|
||||
|
||||
function executeNextUpgradeStep() {
|
||||
if (currentStepIndex >= upgradeSteps.length) {
|
||||
// 所有步骤完成
|
||||
var btn = document.getElementById('startUpgradeBtn');
|
||||
btn.textContent = '升级完成 ✓';
|
||||
btn.style.background = '#52c41a';
|
||||
|
||||
var laterBtn = document.getElementById('upgradeLaterBtn');
|
||||
laterBtn.style.display = '';
|
||||
laterBtn.textContent = '关闭';
|
||||
laterBtn.onclick = function() { location.reload(); };
|
||||
|
||||
document.getElementById('upgradeResult').style.display = 'block';
|
||||
document.getElementById('upgradeResult').innerHTML = '<div style="background:#f6ffed;border:1px solid #b7eb8f;border-radius:6px;padding:12px;text-align:center;color:var(--color-success);font-size:14px;">✓ 数据库升级成功!</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
var step = upgradeSteps[currentStepIndex];
|
||||
var stepEl = document.getElementById('ustep-' + currentStepIndex);
|
||||
var iconEl = document.getElementById('ustep-icon-' + currentStepIndex);
|
||||
|
||||
// 标记为执行中
|
||||
if (stepEl) {
|
||||
stepEl.style.borderLeftColor = 'var(--color-primary)';
|
||||
stepEl.style.background = 'var(--color-primary-light)';
|
||||
}
|
||||
if (iconEl) iconEl.textContent = '⟳';
|
||||
|
||||
fetch('/api/execute_upgrade.php?action=step&version=' + encodeURIComponent(step.version), { method: 'POST' })
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(data) {
|
||||
if (data.success) {
|
||||
if (stepEl) {
|
||||
stepEl.style.borderLeftColor = 'var(--color-success)';
|
||||
stepEl.style.background = '#f6ffed';
|
||||
}
|
||||
if (iconEl) iconEl.textContent = '✓';
|
||||
currentStepIndex++;
|
||||
executeNextUpgradeStep();
|
||||
} else {
|
||||
if (stepEl) {
|
||||
stepEl.style.borderLeftColor = 'var(--color-danger)';
|
||||
stepEl.style.background = 'var(--color-danger-light)';
|
||||
}
|
||||
if (iconEl) iconEl.textContent = '✗';
|
||||
showUpgradeError(data.error || '未知错误');
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
if (stepEl) {
|
||||
stepEl.style.borderLeftColor = 'var(--color-danger)';
|
||||
stepEl.style.background = 'var(--color-danger-light)';
|
||||
}
|
||||
if (iconEl) iconEl.textContent = '✗';
|
||||
showUpgradeError(err.message);
|
||||
});
|
||||
}
|
||||
|
||||
function showUpgradeError(msg) {
|
||||
var btn = document.getElementById('startUpgradeBtn');
|
||||
btn.textContent = '升级失败';
|
||||
btn.style.background = 'var(--color-danger)';
|
||||
btn.disabled = false;
|
||||
btn.style.opacity = '';
|
||||
|
||||
var laterBtn = document.getElementById('upgradeLaterBtn');
|
||||
laterBtn.style.display = '';
|
||||
laterBtn.textContent = '关闭';
|
||||
|
||||
document.getElementById('upgradeResult').style.display = 'block';
|
||||
document.getElementById('upgradeResult').innerHTML = '<div style="background:var(--color-danger-light);border:1px solid #ffccc7;border-radius:6px;padding:12px;color:var(--color-danger-dark);font-size:13px;"><strong>升级失败:</strong>' + escapeHtml(msg) + '</div>';
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php include __DIR__ . '/../includes/footer.php'; ?>
|
||||
Reference in New Issue
Block a user