388 lines
12 KiB
JavaScript
388 lines
12 KiB
JavaScript
/**
|
||
* 班级操行分管理系统 - 管理端JS
|
||
*
|
||
* 开发者: Canglan
|
||
* 联系方式: admin@sea-studio.top
|
||
* 版权归属: Sea Network Technology Studio
|
||
* 许可证: MIT License
|
||
*
|
||
* 版权所有 © Sea Network Technology Studio
|
||
*/
|
||
|
||
// 全局变量(使用 var 避免与页面级 let 重复声明冲突)
|
||
var selectedStudentIds = [];
|
||
var currentPage = 1;
|
||
var totalPages = 1;
|
||
var currentHistoryPage = 1;
|
||
|
||
// 显示批量加减分模态框
|
||
function showBatchPointsModal() {
|
||
selectedStudentIds = [];
|
||
document.querySelectorAll('.student-checkbox:checked').forEach(cb => {
|
||
selectedStudentIds.push(parseInt(cb.dataset.id));
|
||
});
|
||
|
||
if (selectedStudentIds.length === 0) {
|
||
showToast('请先选择学生', 'warning');
|
||
return;
|
||
}
|
||
|
||
document.getElementById('selectedStudentsCount').innerHTML = `${selectedStudentIds.length} 人`;
|
||
document.getElementById('pointsChange').value = '';
|
||
document.getElementById('pointsReason').value = '';
|
||
document.getElementById('batchPointsModal').style.display = 'flex';
|
||
}
|
||
|
||
// 提交批量加减分
|
||
async function submitBatchPoints() {
|
||
const pointsChange = parseInt(document.getElementById('pointsChange').value);
|
||
const reason = document.getElementById('pointsReason').value;
|
||
|
||
if (isNaN(pointsChange) || pointsChange === 0) {
|
||
showToast('分值不能为0', 'error');
|
||
return;
|
||
}
|
||
|
||
if (!reason.trim()) {
|
||
showToast('请填写原因', 'error');
|
||
return;
|
||
}
|
||
|
||
const res = await apiPost('/api/admin/conduct/add', {
|
||
student_ids: selectedStudentIds,
|
||
points_change: pointsChange,
|
||
reason: reason
|
||
});
|
||
|
||
if (res && res.success) {
|
||
showToast(`操作成功: ${res.data.success_count} 人成功`);
|
||
closeModal('batchPointsModal');
|
||
loadStudents();
|
||
if (typeof loadConductStudents === 'function') loadConductStudents();
|
||
} else {
|
||
showToast(res?.message || '操作失败', 'error');
|
||
}
|
||
}
|
||
|
||
// 显示导入模态框
|
||
function showImportModal() {
|
||
document.getElementById('importModal').style.display = 'flex';
|
||
document.getElementById('importPreview').style.display = 'none';
|
||
document.getElementById('importPreview').innerHTML = '';
|
||
document.getElementById('importBtn').style.display = 'none';
|
||
document.getElementById('importFile').value = '';
|
||
}
|
||
|
||
// 预览导入文件
|
||
function previewImportFile() {
|
||
const file = document.getElementById('importFile').files[0];
|
||
if (!file) return;
|
||
|
||
const reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
try {
|
||
const data = JSON.parse(e.target.result);
|
||
const students = data.students || [];
|
||
|
||
let html = '<h4>预览数据</h4><div class="table-wrapper"><table><thead><tr>';
|
||
html += '<th>学号</th><th>姓名</th><th>家长手机号</th><th>初始密码</th>';
|
||
html += '</tr></thead><tbody>';
|
||
|
||
students.forEach(s => {
|
||
html += `<tr>
|
||
<td>${escapeHtml(s.student_no || '')}</td>
|
||
<td>${escapeHtml(s.name || '')}</td>
|
||
<td>${escapeHtml(s.parent_phone || '')}</td>
|
||
<td>${escapeHtml(s.password || '123456')}</td>
|
||
</tr>`;
|
||
});
|
||
|
||
html += `</tbody></table></div><p>共 ${students.length} 条记录,初始操行分默认为60分</p>`;
|
||
document.getElementById('importPreview').innerHTML = html;
|
||
document.getElementById('importPreview').style.display = 'block';
|
||
document.getElementById('importBtn').style.display = 'inline-block';
|
||
} catch (error) {
|
||
showToast('JSON格式错误', 'error');
|
||
}
|
||
};
|
||
reader.readAsText(file);
|
||
}
|
||
|
||
// 执行导入
|
||
async function doImport() {
|
||
const file = document.getElementById('importFile').files[0];
|
||
if (!file) {
|
||
showToast('请选择文件', 'warning');
|
||
return;
|
||
}
|
||
|
||
const formData = new FormData();
|
||
formData.append('file', file);
|
||
|
||
const token = getToken();
|
||
const response = await fetch(`${API_BASE_URL}/api/admin/students/import`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Authorization': `Bearer ${token}`
|
||
},
|
||
body: formData
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
showToast(result.message);
|
||
closeModal('importModal');
|
||
loadStudents();
|
||
|
||
// 显示详细导入结果
|
||
if (result.data && result.data.results) {
|
||
const failedList = result.data.results.filter(r => !r.success);
|
||
if (failedList.length > 0) {
|
||
let detail = '失败详情:\n';
|
||
failedList.forEach(r => {
|
||
detail += `${r.student_no || '未知'}: ${r.error}\n`;
|
||
});
|
||
alert(detail);
|
||
}
|
||
}
|
||
} else {
|
||
showToast(result.message, 'error');
|
||
}
|
||
}
|
||
|
||
// 显示新增学生模态框
|
||
function showAddStudentModal() {
|
||
document.getElementById('addStudentModal').style.display = 'flex';
|
||
document.getElementById('addStudentForm').reset();
|
||
}
|
||
|
||
// 提交新增学生
|
||
async function submitAddStudent() {
|
||
const studentNo = document.getElementById('studentNo').value.trim();
|
||
const name = document.getElementById('studentName').value.trim();
|
||
const parentPhone = document.getElementById('parentPhone').value.trim();
|
||
|
||
if (!studentNo || !name) {
|
||
showToast('请填写学号和姓名', 'warning');
|
||
return;
|
||
}
|
||
|
||
const res = await apiPost('/api/admin/students', {
|
||
student_no: studentNo,
|
||
name: name,
|
||
parent_phone: parentPhone
|
||
});
|
||
|
||
if (res && res.success) {
|
||
showToast('学生添加成功');
|
||
closeModal('addStudentModal');
|
||
loadStudents();
|
||
} else {
|
||
showToast(res?.message || '添加失败', 'error');
|
||
}
|
||
}
|
||
|
||
// 显示添加管理员模态框
|
||
function showAddAdminModal() {
|
||
document.getElementById('addAdminModal').style.display = 'flex';
|
||
document.getElementById('addAdminForm')?.reset();
|
||
}
|
||
|
||
// 提交添加管理员
|
||
async function submitAddAdmin() {
|
||
const username = document.getElementById('adminUsername').value.trim();
|
||
const realName = document.getElementById('adminRealName').value.trim();
|
||
const password = document.getElementById('adminPassword').value;
|
||
const roleType = document.getElementById('adminRole').value;
|
||
|
||
if (!username || !realName || !roleType) {
|
||
showToast('请填写完整信息', 'warning');
|
||
return;
|
||
}
|
||
|
||
const res = await apiPost('/api/admin/add', {
|
||
username: username,
|
||
real_name: realName,
|
||
password: password || undefined,
|
||
role_type: roleType
|
||
});
|
||
|
||
if (res && res.success) {
|
||
let msg = `管理员 ${res.data.username} 添加成功`;
|
||
if (res.data.password) msg += `,密码: ${res.data.password}`;
|
||
showToast(msg);
|
||
closeModal('addAdminModal');
|
||
loadAdmins();
|
||
} else {
|
||
showToast(res?.message || '添加失败', 'error');
|
||
}
|
||
}
|
||
|
||
// 显示添加科目模态框
|
||
function showAddSubjectModal() {
|
||
document.getElementById('addSubjectModal').style.display = 'flex';
|
||
document.getElementById('addSubjectForm').reset();
|
||
}
|
||
|
||
// 提交添加科目
|
||
async function submitAddSubject() {
|
||
const subjectName = document.getElementById('subjectName').value.trim();
|
||
const subjectCode = document.getElementById('subjectCode').value.trim();
|
||
|
||
if (!subjectName) {
|
||
showToast('请填写科目名称', 'warning');
|
||
return;
|
||
}
|
||
|
||
const res = await apiPost('/api/subject/create', {
|
||
subject_name: subjectName,
|
||
subject_code: subjectCode
|
||
});
|
||
|
||
if (res && res.success) {
|
||
showToast('科目添加成功');
|
||
closeModal('addSubjectModal');
|
||
loadSubjects();
|
||
} else {
|
||
showToast(res?.message || '添加失败', 'error');
|
||
}
|
||
}
|
||
|
||
// 撤销扣分记录
|
||
async function revokeRecord(recordId) {
|
||
if (!confirm('确定要撤销这条记录吗?撤销后学生分数将恢复。')) return;
|
||
|
||
const res = await apiPost('/api/admin/conduct/revoke', { record_id: recordId });
|
||
if (res && res.success) {
|
||
showToast('撤销成功');
|
||
loadHistory(currentHistoryPage);
|
||
} else {
|
||
showToast(res?.message || '撤销失败', 'error');
|
||
}
|
||
}
|
||
|
||
// 反撤销(恢复)记录
|
||
async function restoreRecord(recordId) {
|
||
if (!confirm('确定要反撤销这条记录吗?分数变动将重新生效。')) return;
|
||
|
||
const res = await apiPost('/api/admin/conduct/restore', { record_id: recordId });
|
||
if (res && res.success) {
|
||
showToast('反撤销成功');
|
||
loadHistory(currentHistoryPage);
|
||
} else {
|
||
showToast(res?.message || '反撤销失败', 'error');
|
||
}
|
||
}
|
||
|
||
// 关闭模态框
|
||
function closeModal(modalId) {
|
||
const modal = document.getElementById(modalId);
|
||
if (modal) {
|
||
modal.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
// HTML转义
|
||
function escapeHtml(str) {
|
||
if (!str) return '';
|
||
return str.replace(/[&<>]/g, function(m) {
|
||
if (m === '&') return '&';
|
||
if (m === '<') return '<';
|
||
if (m === '>') return '>';
|
||
return m;
|
||
});
|
||
}
|
||
|
||
// 全选功能
|
||
function toggleSelectAll() {
|
||
const selectAll = document.getElementById('selectAll');
|
||
if (selectAll) {
|
||
document.querySelectorAll('.student-checkbox').forEach(cb => {
|
||
cb.checked = selectAll.checked;
|
||
});
|
||
}
|
||
}
|
||
|
||
// 绑定文件选择事件
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
const fileInput = document.getElementById('importFile');
|
||
if (fileInput) {
|
||
fileInput.addEventListener('change', previewImportFile);
|
||
}
|
||
});
|
||
|
||
// ===== 学生编辑/删除/重置密码 =====
|
||
|
||
function showEditStudentModal(studentId, studentNo, name, phone) {
|
||
document.getElementById('editStudentId').value = studentId;
|
||
document.getElementById('editStudentNo').value = studentNo;
|
||
document.getElementById('editStudentName').value = name;
|
||
document.getElementById('editStudentPhone').value = phone || '';
|
||
document.getElementById('editStudentModal').style.display = 'flex';
|
||
}
|
||
|
||
async function submitEditStudent() {
|
||
const studentId = document.getElementById('editStudentId').value;
|
||
const name = document.getElementById('editStudentName').value.trim();
|
||
const phone = document.getElementById('editStudentPhone').value.trim();
|
||
|
||
if (!name) {
|
||
showToast('请输入姓名', 'warning');
|
||
return;
|
||
}
|
||
|
||
const res = await apiPut(`/api/admin/students/${studentId}`, {
|
||
name: name,
|
||
parent_phone: phone || null
|
||
});
|
||
|
||
if (res && res.success) {
|
||
showToast('学生信息更新成功');
|
||
closeModal('editStudentModal');
|
||
location.reload();
|
||
} else {
|
||
showToast(res?.message || '更新失败', 'error');
|
||
}
|
||
}
|
||
|
||
function showResetStudentPasswordModal(studentId, name) {
|
||
document.getElementById('resetStudentId').value = studentId;
|
||
document.getElementById('resetStudentInfo').textContent = `正在重置学生 "${name}" 的密码`;
|
||
document.getElementById('newStudentPassword').value = '';
|
||
document.getElementById('resetStudentPasswordModal').style.display = 'flex';
|
||
}
|
||
|
||
async function submitResetStudentPassword() {
|
||
const studentId = document.getElementById('resetStudentId').value;
|
||
const newPassword = document.getElementById('newStudentPassword').value;
|
||
|
||
if (!newPassword || newPassword.length < 6) {
|
||
showToast('密码至少6位', 'warning');
|
||
return;
|
||
}
|
||
|
||
const res = await apiPost(`/api/admin/students/reset-password/${studentId}`, {
|
||
new_password: newPassword
|
||
});
|
||
|
||
if (res && res.success) {
|
||
showToast('密码重置成功');
|
||
closeModal('resetStudentPasswordModal');
|
||
} else {
|
||
showToast(res?.message || '重置失败', 'error');
|
||
}
|
||
}
|
||
|
||
async function deleteStudent(studentId, name) {
|
||
if (!confirm(`确定要删除学生 "${name}" 吗?删除后学生账号将被禁用。`)) return;
|
||
|
||
const res = await apiDelete(`/api/admin/students/${studentId}`);
|
||
|
||
if (res && res.success) {
|
||
showToast('学生删除成功');
|
||
location.reload();
|
||
} else {
|
||
showToast(res?.message || '删除失败', 'error');
|
||
}
|
||
} |