- 后端从 Python FastAPI 重写为 Go Gin(端口 56789) - 多班级完全隔离 - 超级管理员独立登录 - 课代表作业管理、排行榜分项排行 - 角色加减分上下限可配置 - 家长改密功能(可开关) - 周度/月度重置功能 - MySQL 5.7 兼容 - 43轮代码审查+全部修复 - Apache 2.0 许可证
440 lines
18 KiB
PHP
440 lines
18 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_id'])) {
|
||
header('Location: /index.php');
|
||
exit();
|
||
}
|
||
|
||
$role = $_SESSION['role'] ?? '';
|
||
if (!in_array($role, ['班主任', '系统管理员'])) {
|
||
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>
|
||
<p class="text-muted">修改当前班级的扣分规则、加减分限制和功能开关(仅班主任可修改)</p>
|
||
</div>
|
||
|
||
<!-- 扣分规则 -->
|
||
<div class="card">
|
||
<h3>扣分规则</h3>
|
||
<div id="deductionRules">
|
||
<div class="form-group">
|
||
<label>学生初始操行分</label>
|
||
<input type="number" id="setting_student_initial_points" value="60" min="0" max="200">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>作业未提交扣分</label>
|
||
<input type="number" id="setting_deduction_homework_not_submit" value="2" min="0" max="20">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>作业迟交扣分</label>
|
||
<input type="number" id="setting_deduction_homework_late" value="1" min="0" max="20">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>缺勤扣分</label>
|
||
<input type="number" id="setting_deduction_attendance_absent" value="3" min="0" max="20">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>迟到扣分</label>
|
||
<input type="number" id="setting_deduction_attendance_late" value="1" min="0" max="20">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>请假扣分(0=不扣分)</label>
|
||
<input type="number" id="setting_deduction_attendance_leave" value="0" min="0" max="20">
|
||
</div>
|
||
<button class="btn btn-primary" onclick="saveSettings()">保存设置</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 角色加减分上下限 -->
|
||
<div class="card">
|
||
<h3>角色加减分限制</h3>
|
||
<p class="text-muted" style="margin-bottom:15px;">配置各角色单次加减分的上下限</p>
|
||
<div id="pointLimits">
|
||
<div class="settings-grid">
|
||
<div class="form-group">
|
||
<label>班长单次加分上限</label>
|
||
<input type="number" id="limit_monitor_max_add" value="5" min="0" max="100">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>班长单次扣分下限</label>
|
||
<input type="number" id="limit_monitor_max_subtract" value="-5" min="-100" max="0">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>学习委员加分上限</label>
|
||
<input type="number" id="limit_study_comm_max_points" value="5" min="0" max="100">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>学习委员扣分下限</label>
|
||
<input type="number" id="limit_study_comm_min_points" value="-5" min="-100" max="0">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>考勤委员扣分上限</label>
|
||
<input type="number" id="limit_attendance_rep_max_points" value="8" min="0" max="100">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>考勤委员扣分下限</label>
|
||
<input type="number" id="limit_attendance_rep_min_points" value="-8" min="-100" max="0">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>劳动委员加分上限</label>
|
||
<input type="number" id="limit_labor_rep_max_points" value="1" min="0" max="100">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>劳动委员扣分下限</label>
|
||
<input type="number" id="limit_labor_rep_min_points" value="-1" min="-100" max="0">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>志愿委员加分上限</label>
|
||
<input type="number" id="limit_volunteer_rep_max_points" value="5" min="0" max="100">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>志愿委员扣分下限</label>
|
||
<input type="number" id="limit_volunteer_rep_min_points" value="-5" min="-100" max="0">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>科任老师加分上限</label>
|
||
<input type="number" id="limit_subject_teacher_max_points" value="5" min="0" max="100">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>科任老师扣分下限</label>
|
||
<input type="number" id="limit_subject_teacher_min_points" value="-5" min="-100" max="0">
|
||
</div>
|
||
<button class="btn btn-primary" onclick="savePointLimits()">保存加减分限制</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 周期重置 -->
|
||
<div class="card">
|
||
<h3>周期重置</h3>
|
||
<p class="text-muted" style="margin-bottom:15px;">按周或按月自动重置学生操行分(需配合定时任务或手动触发)</p>
|
||
<div id="periodResetSettings">
|
||
<div class="form-group">
|
||
<label>重置频率</label>
|
||
<select id="setting_reset_frequency" onchange="toggleResetDay()">
|
||
<option value="none">不重置(仅学期结算)</option>
|
||
<option value="weekly">每周重置</option>
|
||
<option value="monthly">每月重置</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group" id="reset_day_of_week_group" style="display:none;">
|
||
<label>每周重置日</label>
|
||
<select id="setting_reset_day_of_week">
|
||
<option value="1">周一</option>
|
||
<option value="2">周二</option>
|
||
<option value="3">周三</option>
|
||
<option value="4">周四</option>
|
||
<option value="5">周五</option>
|
||
<option value="6">周六</option>
|
||
<option value="7">周日</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group" id="reset_day_of_month_group" style="display:none;">
|
||
<label>每月重置日</label>
|
||
<input type="number" id="setting_reset_day_of_month" value="1" min="1" max="28">
|
||
<small style="color:#999;">1~28日,建议避免月末最后几天</small>
|
||
</div>
|
||
<button class="btn btn-primary" onclick="savePeriodResetSettings()">保存周期重置设置</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 角色开关 -->
|
||
<div class="card">
|
||
<h3>功能开关</h3>
|
||
<p class="text-muted" style="margin-bottom:15px;">控制各角色的功能启用状态</p>
|
||
<div id="roleToggles">
|
||
<div class="toggle-group">
|
||
<label class="toggle-label">
|
||
<input type="checkbox" id="toggle_parent_account_enabled">
|
||
<span>家长账号启用</span>
|
||
</label>
|
||
<label class="toggle-label">
|
||
<input type="checkbox" id="toggle_parent_password_change_enabled">
|
||
<span>家长改密启用</span>
|
||
</label>
|
||
<label class="toggle-label">
|
||
<input type="checkbox" id="toggle_parent_view_attendance">
|
||
<span>家长查看考勤</span>
|
||
</label>
|
||
<label class="toggle-label">
|
||
<input type="checkbox" id="toggle_parent_view_ranking">
|
||
<span>家长查看排名</span>
|
||
</label>
|
||
<label class="toggle-label">
|
||
<input type="checkbox" id="toggle_student_view_ranking">
|
||
<span>学生查看排行榜</span>
|
||
</label>
|
||
<label class="toggle-label">
|
||
<input type="checkbox" id="toggle_homework_management">
|
||
<span>作业管理模块</span>
|
||
</label>
|
||
<label class="toggle-label">
|
||
<input type="checkbox" id="toggle_attendance_management">
|
||
<span>考勤管理模块</span>
|
||
</label>
|
||
<label class="toggle-label">
|
||
<input type="checkbox" id="toggle_cadre_homework">
|
||
<span>课代表作业管理</span>
|
||
</label>
|
||
</div>
|
||
<button class="btn btn-primary" onclick="saveRoleToggles()">保存功能开关</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<style>
|
||
.settings-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||
gap: 15px;
|
||
margin-bottom: 15px;
|
||
}
|
||
.toggle-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
margin-bottom: 15px;
|
||
}
|
||
.toggle-label {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
}
|
||
.toggle-label input[type="checkbox"] {
|
||
width: 18px;
|
||
height: 18px;
|
||
cursor: pointer;
|
||
}
|
||
select {
|
||
padding: 8px 12px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
background: #fff;
|
||
cursor: pointer;
|
||
}
|
||
</style>
|
||
|
||
<script>
|
||
// limitFieldMap: 前端 input ID 后缀 → 后端 class_settings 表 key
|
||
var limitFieldMap = {
|
||
'monitor_max_add': 'point_limit_班长_max',
|
||
'monitor_max_subtract': 'point_limit_班长_min',
|
||
'study_comm_max_points': 'point_limit_学习委员_max',
|
||
'study_comm_min_points': 'point_limit_学习委员_min',
|
||
'attendance_rep_max_points': 'point_limit_考勤委员_max',
|
||
'attendance_rep_min_points': 'point_limit_考勤委员_min',
|
||
'labor_rep_max_points': 'point_limit_劳动委员_max',
|
||
'labor_rep_min_points': 'point_limit_劳动委员_min',
|
||
'volunteer_rep_max_points': 'point_limit_志愿委员_max',
|
||
'volunteer_rep_min_points': 'point_limit_志愿委员_min',
|
||
'subject_teacher_max_points': 'point_limit_科任老师_max',
|
||
'subject_teacher_min_points': 'point_limit_科任老师_min'
|
||
};
|
||
var limitFields = Object.keys(limitFieldMap);
|
||
|
||
var toggleFields = [
|
||
'parent_account_enabled', 'parent_password_change_enabled', 'parent_view_attendance',
|
||
'parent_view_ranking', 'student_view_ranking', 'homework_management',
|
||
'attendance_management', 'cadre_homework'
|
||
];
|
||
|
||
async function loadSettings() {
|
||
var params = {};
|
||
if (window.CLASS_ID) {
|
||
params.class_id = window.CLASS_ID;
|
||
}
|
||
var result = await apiGet('/api/config/deduction-rules', params);
|
||
if (result && result.success && result.data) {
|
||
var d = result.data;
|
||
document.getElementById('setting_student_initial_points').value = d.STUDENT_INITIAL_POINTS || 60;
|
||
document.getElementById('setting_deduction_homework_not_submit').value = d.DEDUCTION_HOMEWORK_NOT_SUBMIT || 2;
|
||
document.getElementById('setting_deduction_homework_late').value = d.DEDUCTION_HOMEWORK_LATE || 1;
|
||
document.getElementById('setting_deduction_attendance_absent').value = d.DEDUCTION_ATTENDANCE_ABSENT || 3;
|
||
document.getElementById('setting_deduction_attendance_late').value = d.DEDUCTION_ATTENDANCE_LATE || 1;
|
||
document.getElementById('setting_deduction_attendance_leave').value = d.DEDUCTION_ATTENDANCE_LEAVE || 0;
|
||
}
|
||
}
|
||
|
||
async function loadPointLimits() {
|
||
var result = await apiGet('/api/class/point-limits');
|
||
if (result && result.success && result.data) {
|
||
var d = result.data.settings || result.data;
|
||
limitFields.forEach(function(key) {
|
||
var el = document.getElementById('limit_' + key);
|
||
var backendKey = limitFieldMap[key];
|
||
if (el) {
|
||
// 优先读取后端 point_limit_* key,兼容旧 key
|
||
if (backendKey && d[backendKey] !== undefined) {
|
||
el.value = d[backendKey];
|
||
} else if (d[key] !== undefined) {
|
||
el.value = d[key];
|
||
}
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
async function loadRoleToggles() {
|
||
var result = await apiGet('/api/class/features');
|
||
if (result && result.success && result.data) {
|
||
var d = result.data.features || result.data;
|
||
toggleFields.forEach(function(key) {
|
||
var el = document.getElementById('toggle_' + key);
|
||
if (el) {
|
||
el.checked = d[key] === true || d[key] === 1 || d[key] === '1';
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
async function saveSettings() {
|
||
var settings = [
|
||
{ key: 'initial_points', value: document.getElementById('setting_student_initial_points').value },
|
||
{ key: 'deduction_homework_not_submit', value: document.getElementById('setting_deduction_homework_not_submit').value },
|
||
{ key: 'deduction_homework_late', value: document.getElementById('setting_deduction_homework_late').value },
|
||
{ key: 'deduction_attendance_absent', value: document.getElementById('setting_deduction_attendance_absent').value },
|
||
{ key: 'deduction_attendance_late', value: document.getElementById('setting_deduction_attendance_late').value },
|
||
{ key: 'deduction_attendance_leave', value: document.getElementById('setting_deduction_attendance_leave').value },
|
||
];
|
||
|
||
var success = true;
|
||
for (var i = 0; i < settings.length; i++) {
|
||
var s = settings[i];
|
||
var result = await apiPost('/api/class/settings', { setting_key: s.key, setting_value: s.value });
|
||
if (!result || !result.success) {
|
||
success = false;
|
||
}
|
||
}
|
||
|
||
if (success) {
|
||
showToast('班级设置已保存');
|
||
} else {
|
||
showToast('部分设置保存失败', 'error');
|
||
}
|
||
}
|
||
|
||
async function savePointLimits() {
|
||
var data = {};
|
||
limitFields.forEach(function(key) {
|
||
var el = document.getElementById('limit_' + key);
|
||
if (el) {
|
||
// 使用后端 point_limit_* key 保存,确保 conduct_service 能正确读取
|
||
var backendKey = limitFieldMap[key] || key;
|
||
data[backendKey] = el.value;
|
||
}
|
||
});
|
||
|
||
var result = await apiPost('/api/class/point-limits', data);
|
||
if (result && result.success) {
|
||
showToast('加减分限制已保存');
|
||
} else {
|
||
showToast(result && result.message ? result.message : '保存失败', 'error');
|
||
}
|
||
}
|
||
|
||
async function saveRoleToggles() {
|
||
var success = true;
|
||
for (var i = 0; i < toggleFields.length; i++) {
|
||
var key = toggleFields[i];
|
||
var el = document.getElementById('toggle_' + key);
|
||
if (el) {
|
||
var result = await apiPost('/api/class/features', {
|
||
feature_key: key,
|
||
enabled: el.checked ? 1 : 0
|
||
});
|
||
if (!result || !result.success) {
|
||
success = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (success) {
|
||
showToast('功能开关已保存');
|
||
} else {
|
||
showToast('部分开关保存失败', 'error');
|
||
}
|
||
}
|
||
|
||
// ========== 周期重置设置 ==========
|
||
|
||
async function loadPeriodResetSettings() {
|
||
var result = await apiGet('/api/class/settings');
|
||
if (result && result.success && result.data && result.data.settings) {
|
||
var s = result.data.settings;
|
||
var freqSelect = document.getElementById('setting_reset_frequency');
|
||
freqSelect.value = s['reset_frequency'] || 'none';
|
||
toggleResetDay();
|
||
|
||
if (s['reset_day_of_week']) {
|
||
document.getElementById('setting_reset_day_of_week').value = s['reset_day_of_week'];
|
||
}
|
||
if (s['reset_day_of_month']) {
|
||
document.getElementById('setting_reset_day_of_month').value = s['reset_day_of_month'];
|
||
}
|
||
}
|
||
}
|
||
|
||
function toggleResetDay() {
|
||
var freq = document.getElementById('setting_reset_frequency').value;
|
||
document.getElementById('reset_day_of_week_group').style.display = (freq === 'weekly') ? 'block' : 'none';
|
||
document.getElementById('reset_day_of_month_group').style.display = (freq === 'monthly') ? 'block' : 'none';
|
||
}
|
||
|
||
async function savePeriodResetSettings() {
|
||
var freq = document.getElementById('setting_reset_frequency').value;
|
||
var settings = [
|
||
{ key: 'reset_frequency', value: freq }
|
||
];
|
||
if (freq === 'weekly') {
|
||
settings.push({ key: 'reset_day_of_week', value: document.getElementById('setting_reset_day_of_week').value });
|
||
} else if (freq === 'monthly') {
|
||
settings.push({ key: 'reset_day_of_month', value: document.getElementById('setting_reset_day_of_month').value });
|
||
}
|
||
|
||
var success = true;
|
||
for (var i = 0; i < settings.length; i++) {
|
||
var result = await apiPost('/api/class/settings', { setting_key: settings[i].key, setting_value: settings[i].value });
|
||
if (!result || !result.success) {
|
||
success = false;
|
||
}
|
||
}
|
||
|
||
if (success) {
|
||
showToast('周期重置设置已保存');
|
||
} else {
|
||
showToast('部分设置保存失败', 'error');
|
||
}
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
loadSettings();
|
||
loadPointLimits();
|
||
loadRoleToggles();
|
||
loadPeriodResetSettings();
|
||
});
|
||
</script>
|
||
|
||
<?php require_once __DIR__ . '/../includes/footer.php'; ?>
|