v2.5更新

This commit is contained in:
2026-05-29 17:35:29 +08:00
parent 6c0d8f0e94
commit fe58ee1d23
12 changed files with 258 additions and 24 deletions

View File

@@ -33,6 +33,7 @@ async function loadHistory(page = 1) {
const studentId = document.getElementById('historyStudentId').value;
const reasonFilter = document.getElementById('historyReasonFilter').value;
const isGrouped = document.getElementById('historyGrouped').checked;
const statusFilter = document.getElementById('historyStatusFilter')?.value;
const params = {
page, page_size: 20,
@@ -42,6 +43,7 @@ async function loadHistory(page = 1) {
if (studentId) params.student_id = studentId;
if (reasonFilter) params.reason_prefix = reasonFilter;
if (isGrouped) params.grouped = true;
if (statusFilter !== undefined && statusFilter !== '') params.is_revoked = parseInt(statusFilter);
const res = await apiGet('/api/admin/conduct/history', params);
@@ -50,6 +52,9 @@ async function loadHistory(page = 1) {
let headHtml = '';
if (isGrouped) {
headHtml = '<th>时间</th><th>原因</th><th>分值</th><th' + nowrapStyle + '>操作人</th><th>涉及学生</th>';
if (role === '班主任' || role === '班长') {
headHtml += '<th>操作</th>';
}
} else {
headHtml = '<th>时间</th><th>学生</th><th>分数变动</th><th>原因</th><th' + nowrapStyle + '>操作人</th>';
if (role === '班主任' || role === '班长' || role === '考勤委员') {
@@ -63,46 +68,56 @@ async function loadHistory(page = 1) {
res.data.records.forEach(record => {
const pointsClass = record.points_change > 0 ? 'plus' : 'minus';
const names = record.student_names || '';
html += `<tr>
<td>${formatDateTime(record.created_at)}</td>
<td class="preserve-newlines">${escapeHtml(record.reason)}</td>
const allRevoked = record.all_revoked;
const revokedStyle = allRevoked ? ' style="opacity:0.5; text-decoration:line-through;"' : '';
html += `<tr${revokedStyle}>
<td class="history-time">${formatDateTime(record.created_at)}</td>
<td class="preserve-newlines history-reason">${escapeHtml(record.reason)}</td>
<td class="${pointsClass}">${record.points_change > 0 ? '+' : ''}${record.points_change}×${record.student_count}</td>
<td>${escapeHtml(record.recorder_name || '')}</td>
<td style="white-space: nowrap;">${escapeHtml(names)}</td>
</tr>`;
<td class="history-students">${escapeHtml(names)}</td>`;
if (role === '班主任' || role === '班长') {
if (allRevoked) {
html += `<td><span class="text-muted">已撤销</span></td>`;
} else {
html += `<td><button class="btn btn-sm btn-outline-danger" onclick="batchRevokeGrouped('${escapeHtml(record.reason)}', ${record.points_change}, '${escapeHtml(record.recorder_name || '')}', '${formatDateTime(record.created_at)}')">批量撤销</button></td>`;
}
}
html += `</tr>`;
});
if (res.data.records.length === 0) {
html = '<tr><td colspan="5" style="text-align:center;">暂无记录</td></tr>';
const colSpan = (role === '班主任' || role === '班长') ? 6 : 5;
html = '<tr><td colspan="' + colSpan + '" style="text-align:center;">暂无记录</td></tr>';
}
} else {
res.data.records.forEach(record => {
const pointsClass = record.points_change > 0 ? 'plus' : 'minus';
const revokedStyle = record.is_revoked == 1 ? ' style="opacity:0.5; text-decoration:line-through;"' : '';
html += `<tr${revokedStyle}>
<td>${formatDateTime(record.created_at)}</td>
<td>${escapeHtml(record.student_name)}</td>
<td class="history-time">${formatDateTime(record.created_at)}</td>
<td class="history-students">${escapeHtml(record.student_name)}</td>
<td class="${pointsClass}">${record.points_change > 0 ? '+' : ''}${record.points_change}</td>
<td class="preserve-newlines">${escapeHtml(record.reason)}</td>
<td class="preserve-newlines history-reason">${escapeHtml(record.reason)}</td>
<td>${escapeHtml(record.recorder_name)}</td>`;
if (role === '班主任') {
if (record.is_revoked == 1) {
const revokerInfo = record.revoker_name ? `${escapeHtml(record.revoker_name)} 撤销` : '已撤销';
html += `<td><span class="text-muted" style="margin-right:4px;">${revokerInfo}</span><button class="btn btn-sm btn-secondary" onclick="restoreRecord(${record.record_id})">反撤销</button></td>`;
html += `<td><span class="text-muted" style="margin-right:4px;">${revokerInfo}</span><button class="btn btn-sm btn-outline" onclick="restoreRecord(${record.record_id})">反撤销</button></td>`;
} else {
html += `<td><button class="btn btn-sm btn-danger" onclick="revokeRecord(${record.record_id})">撤销</button></td>`;
html += `<td><button class="btn btn-sm btn-outline" onclick="revokeRecord(${record.record_id})">撤销</button></td>`;
}
} else if (role === '班长') {
if (record.is_revoked == 1) {
const revokerInfo = record.revoker_name ? `${escapeHtml(record.revoker_name)} 撤销` : '已撤销';
html += `<td><span class="text-muted">${revokerInfo}</span></td>`;
} else {
html += `<td><button class="btn btn-sm btn-danger" onclick="revokeRecord(${record.record_id})">撤销</button></td>`;
html += `<td><button class="btn btn-sm btn-outline" onclick="revokeRecord(${record.record_id})">撤销</button></td>`;
}
} else if (role === '考勤委员') {
if (record.is_revoked == 1) {
html += `<td><span class="text-muted">已撤销</span></td>`;
} else if (record.recorder_id == currentUserId) {
html += `<td><button class="btn btn-sm btn-danger" onclick="revokeRecord(${record.record_id})">撤销</button></td>`;
html += `<td><button class="btn btn-sm btn-outline" onclick="revokeRecord(${record.record_id})">撤销</button></td>`;
} else {
html += `<td><span class="text-muted">-</span></td>`;
}
@@ -177,6 +192,53 @@ async function exportHistoryRecords() {
}
}
// 批量撤销合并记录(按条件查找并撤销)
async function batchRevokeGrouped(reason, pointsChange, recorderName, createdAt) {
if (!confirm(`确定要撤销所有"${reason}"(${pointsChange > 0 ? '+' : ''}${pointsChange}分)的记录吗?`)) return;
showToast('正在批量撤销...', 'info');
try {
// 先查询匹配的记录
const params = {
page: 1, page_size: 1000,
start_date: document.getElementById('historyStartDate').value,
end_date: document.getElementById('historyEndDate').value,
reason_prefix: reason.substring(0, 4),
grouped: false
};
const res = await apiGet('/api/admin/conduct/history', params);
if (!res || !res.success || !res.data.records) {
showToast('查询记录失败', 'error');
return;
}
// 精确匹配
const matchedIds = [];
res.data.records.forEach(r => {
if (r.reason === reason && r.points_change === pointsChange && r.is_revoked == 0) {
matchedIds.push(r.record_id);
}
});
if (matchedIds.length === 0) {
showToast('没有找到可撤销的记录', 'warning');
return;
}
const revokeRes = await apiPost('/api/admin/conduct/batch-revoke', { record_ids: matchedIds });
if (revokeRes && revokeRes.success) {
showToast(`批量撤销完成: ${revokeRes.data.success_count}条成功`);
loadHistory(currentHistoryPage);
} else {
showToast(revokeRes?.message || '批量撤销失败', 'error');
}
} catch (err) {
showToast('批量撤销失败: ' + err.message, 'error');
}
}
loadStudentsForSelect().then(() => {
const urlParams = new URLSearchParams(window.location.search);
const preStudentId = urlParams.get('student_id');
@@ -191,5 +253,6 @@ loadStudentsForSelect().then(() => {
window.loadHistory = loadHistory;
window.loadStudentsForSelect = loadStudentsForSelect;
window.exportHistoryRecords = exportHistoryRecords;
window.batchRevokeGrouped = batchRevokeGrouped;
})();