v0.6测试

This commit is contained in:
2026-04-14 20:42:18 +08:00
parent a60ba8352f
commit d17a63d4cd
13 changed files with 680 additions and 473 deletions

View File

@@ -97,7 +97,7 @@ function selectStatus(btn) {
// 加载学生列表
async function loadStudents() {
const res = await apiGet('/api/admin/students');
const res = await apiGet('/api/admin/students', {page_size: 1000});
if (res && res.success) {
studentsData = res.data.students;
renderStudentGrid();

View File

@@ -59,7 +59,7 @@ include __DIR__ . '/../includes/header.php';
var selectedStudentIds = [];
async function loadStudents() {
const res = await apiGet('/api/admin/students');
const res = await apiGet('/api/admin/students', {page_size: 1000});
if (res && res.success) {
let html = '';
res.data.students.forEach(student => {

View File

@@ -108,7 +108,7 @@ document.getElementById('pointsChange').setAttribute('min', -hwMaxPoints);
document.getElementById('pointsChange').setAttribute('max', hwMaxPoints);
async function loadStudents() {
const res = await apiGet('/api/admin/students');
const res = await apiGet('/api/admin/students', {page_size: 1000});
if (res && res.success) {
let html = '';
res.data.students.forEach(student => {

View File

@@ -51,7 +51,7 @@ if (isset($_SESSION['user_id']) && isset($_SESSION['user_type'])) {
</form>
<div class="login-footer">
<p>© Sea Network Technology Studio</p>
<p>&copy; <?php echo date('Y'); ?> Sea Network Technology Studio<?php if (defined('ICP_ENABLED') && ICP_ENABLED && defined('ICP_NUMBER') && ICP_NUMBER): ?> | <a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer"><?php echo htmlspecialchars(ICP_NUMBER); ?></a><?php endif; ?></p>
</div>
</div>

View File

@@ -18,500 +18,481 @@ if (!isset($_SESSION['user_id']) || $_SESSION['user_type'] !== 'student') {
exit();
}
$page_title = '学生端';
$student_id = $_SESSION['student_id'];
include __DIR__ . '/../includes/header.php';
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo SITE_NAME; ?> - 学生端</title>
<link rel="stylesheet" href="/assets/css/style.css">
<style>
.conduct-score {
text-align: center;
padding: 20px;
}
.score-number {
font-size: 64px;
font-weight: bold;
color: #667eea;
}
.score-label {
color: #666;
margin-top: 8px;
}
.record-item {
padding: 12px 0;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.record-points {
font-weight: bold;
}
.record-points.plus {
color: #38a169;
}
.record-points.minus {
color: #e53e3e;
}
.record-reason {
flex: 1;
margin: 0 15px;
color: #555;
}
.record-time {
font-size: 12px;
color: #999;
}
.view-more {
text-align: center;
margin-top: 15px;
}
.view-more a {
color: #667eea;
text-decoration: none;
}
</style>
</head>
<body>
<div class="header">
<h1><?php echo SITE_NAME; ?> - 学生端</h1>
<div class="header-info">
<span class="user-name" id="userName"></span>
<button class="btn-logout" id="logoutBtn">退出登录</button>
</div>
</div>
<div class="nav">
<button class="nav-item active" data-page="dashboard">首页</button>
<button class="nav-item" data-page="conduct">操行分详情</button>
<button class="nav-item" data-page="homework">作业情况</button>
<button class="nav-item" data-page="attendance">考勤记录</button>
<button class="nav-item" data-page="password">修改密码</button>
</div>
<style>
.conduct-score {
text-align: center;
padding: 20px;
}
.score-number {
font-size: 64px;
font-weight: bold;
color: #667eea;
}
.score-label {
color: #666;
margin-top: 8px;
}
.record-item {
padding: 12px 0;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.record-points {
font-weight: bold;
}
.record-points.plus {
color: #38a169;
}
.record-points.minus {
color: #e53e3e;
}
.record-reason {
flex: 1;
margin: 0 15px;
color: #555;
}
.record-time {
font-size: 12px;
color: #999;
}
.view-more {
text-align: center;
margin-top: 15px;
}
.view-more a {
color: #667eea;
text-decoration: none;
}
</style>
<div class="container" id="pageContainer">
<!-- 首页内容 -->
<div id="page-dashboard" class="page-content">
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">当前操行分</div>
<div class="stat-value" id="totalPoints">--</div>
</div>
<div class="stat-card">
<div class="stat-label">作业完成率</div>
<div class="stat-value" id="homeworkRate">--%</div>
</div>
<div class="stat-card">
<div class="stat-label">本月出勤率</div>
<div class="stat-value" id="attendanceRate">--%</div>
</div>
<div class="nav">
<button class="nav-item active" data-page="dashboard">首页</button>
<button class="nav-item" data-page="conduct">操行分详情</button>
<button class="nav-item" data-page="homework">作业情况</button>
<button class="nav-item" data-page="attendance">考勤记录</button>
<button class="nav-item" data-page="password">修改密码</button>
</div>
<div class="container" id="pageContainer">
<!-- 首页内容 -->
<div id="page-dashboard" class="page-content">
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">当前操行分</div>
<div class="stat-value" id="totalPoints">--</div>
</div>
<div class="card">
<div class="card-title">最新操行分记录</div>
<div id="recentRecords"></div>
<div class="view-more">
<a href="#" onclick="showPage('conduct'); return false;">查看更多 &gt;</a>
</div>
<div class="stat-card">
<div class="stat-label">作业完成率</div>
<div class="stat-value" id="homeworkRate">--%</div>
</div>
<div class="stat-card">
<div class="stat-label">本月出勤率</div>
<div class="stat-value" id="attendanceRate">--%</div>
</div>
</div>
<!-- 操行分详情页 -->
<div id="page-conduct" class="page-content" style="display: none;">
<div class="card">
<div class="conduct-score">
<div class="score-number" id="conductTotalPoints">--</div>
<div class="score-label">当前操行分</div>
</div>
</div>
<div class="card">
<div class="card-title">历史记录</div>
<div id="conductRecords"></div>
<div class="pagination" id="conductPagination"></div>
</div>
</div>
<!-- 作业情况页 -->
<div id="page-homework" class="page-content" style="display: none;">
<div class="card">
<div class="card-title">作业列表</div>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>科目</th>
<th>作业标题</th>
<th>截止日期</th>
<th>状态</th>
<th>备注</th>
</tr>
</thead>
<tbody id="homeworkList"></tbody>
</table>
</div>
</div>
</div>
<!-- 考勤记录页 -->
<div id="page-attendance" class="page-content" style="display: none;">
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">出勤</div>
<div class="stat-value" id="attPresent">0</div>
</div>
<div class="stat-card">
<div class="stat-label">缺勤</div>
<div class="stat-value" id="attAbsent">0</div>
</div>
<div class="stat-card">
<div class="stat-label">迟到</div>
<div class="stat-value" id="attLate">0</div>
</div>
<div class="stat-card">
<div class="stat-label">请假</div>
<div class="stat-value" id="attLeave">0</div>
</div>
</div>
<div class="card">
<div class="card-title">考勤记录明细</div>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>日期</th>
<th>状态</th>
<th>原因</th>
</tr>
</thead>
<tbody id="attendanceList"></tbody>
</table>
</div>
</div>
</div>
<!-- 修改密码页 -->
<div id="page-password" class="page-content" style="display: none;">
<div class="card">
<div class="card-title">修改密码</div>
<form id="passwordForm">
<div class="form-group">
<label>原密码</label>
<input type="password" id="oldPassword" required>
</div>
<div class="form-group">
<label>新密码</label>
<input type="password" id="newPassword" required>
<small>密码长度6-20位需包含字母和数字</small>
</div>
<div class="form-group">
<label>确认新密码</label>
<input type="password" id="confirmPassword" required>
</div>
<button type="submit" class="btn btn-primary">确认修改</button>
</form>
<div class="card">
<div class="card-title">最新操行分记录</div>
<div id="recentRecords"></div>
<div class="view-more">
<a href="#" onclick="showPage('conduct'); return false;">查看更多 ></a>
</div>
</div>
</div>
<!-- 修改密码模态框(首次登录强制) -->
<div id="forceChangePasswordModal" class="modal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h3>首次登录,请修改密码</h3>
<!-- 操行分详情页 -->
<div id="page-conduct" class="page-content" style="display: none;">
<div class="card">
<div class="conduct-score">
<div class="score-number" id="conductTotalPoints">--</div>
<div class="score-label">当前操行分</div>
</div>
<form id="forcePasswordForm">
</div>
<div class="card">
<div class="card-title">历史记录</div>
<div id="conductRecords"></div>
<div class="pagination" id="conductPagination"></div>
</div>
</div>
<!-- 作业情况页 -->
<div id="page-homework" class="page-content" style="display: none;">
<div class="card">
<div class="card-title">作业列表</div>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>科目</th>
<th>作业标题</th>
<th>截止日期</th>
<th>状态</th>
<th>备注</th>
</tr>
</thead>
<tbody id="homeworkList"></tbody>
</table>
</div>
</div>
</div>
<!-- 考勤记录页 -->
<div id="page-attendance" class="page-content" style="display: none;">
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">出勤</div>
<div class="stat-value" id="attPresent">0</div>
</div>
<div class="stat-card">
<div class="stat-label">缺勤</div>
<div class="stat-value" id="attAbsent">0</div>
</div>
<div class="stat-card">
<div class="stat-label">迟到</div>
<div class="stat-value" id="attLate">0</div>
</div>
<div class="stat-card">
<div class="stat-label">请假</div>
<div class="stat-value" id="attLeave">0</div>
</div>
</div>
<div class="card">
<div class="card-title">考勤记录明细</div>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>日期</th>
<th>状态</th>
<th>原因</th>
</tr>
</thead>
<tbody id="attendanceList"></tbody>
</table>
</div>
</div>
</div>
<!-- 修改密码页 -->
<div id="page-password" class="page-content" style="display: none;">
<div class="card">
<div class="card-title">修改密码</div>
<form id="passwordForm">
<div class="form-group">
<label>原密码</label>
<input type="password" id="oldPassword" required>
</div>
<div class="form-group">
<label>新密码</label>
<input type="password" id="forceNewPassword" required>
<input type="password" id="newPassword" required>
<small>密码长度6-20位需包含字母和数字</small>
</div>
<div class="form-group">
<label>确认新密码</label>
<input type="password" id="forceConfirmPassword" required>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">确认修改</button>
<input type="password" id="confirmPassword" required>
</div>
<button type="submit" class="btn btn-primary">确认修改</button>
</form>
</div>
</div>
</div>
<script>
window.API_BASE_URL = '<?php echo API_BASE_URL; ?>';
window.JWT_STORAGE_KEY = '<?php echo JWT_STORAGE_KEY; ?>';
window.USER_STORAGE_KEY = '<?php echo USER_STORAGE_KEY; ?>';
</script>
<script src="/assets/js/common.js"></script>
<script>
const STUDENT_ID = <?php echo $student_id; ?>;
<!-- 修改密码模态框(首次登录强制) -->
<div id="forceChangePasswordModal" class="modal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h3>首次登录,请修改密码</h3>
</div>
<form id="forcePasswordForm">
<div class="form-group">
<label>新密码</label>
<input type="password" id="forceNewPassword" required>
<small>密码长度6-20位需包含字母和数字</small>
</div>
<div class="form-group">
<label>确认新密码</label>
<input type="password" id="forceConfirmPassword" required>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">确认修改</button>
</div>
</form>
</div>
</div>
<script>
const STUDENT_ID = <?php echo $student_id; ?>;
let conductPage = 1;
let conductTotalPages = 1;
// 页面切换
function showPage(pageName) {
document.querySelectorAll('.page-content').forEach(page => {
page.style.display = 'none';
});
document.getElementById(`page-${pageName}`).style.display = 'block';
let conductPage = 1;
let conductTotalPages = 1;
// 页面切换
function showPage(pageName) {
document.querySelectorAll('.page-content').forEach(page => {
page.style.display = 'none';
});
document.getElementById(`page-${pageName}`).style.display = 'block';
document.querySelectorAll('.nav-item').forEach(item => {
item.classList.remove('active');
if (item.dataset.page === pageName) {
item.classList.add('active');
}
});
// 加载对应页面数据
switch(pageName) {
case 'dashboard':
loadDashboard();
break;
case 'conduct':
loadConductHistory();
break;
case 'homework':
loadHomework();
break;
case 'attendance':
loadAttendance();
break;
document.querySelectorAll('.nav-item').forEach(item => {
item.classList.remove('active');
if (item.dataset.page === pageName) {
item.classList.add('active');
}
}
});
// 加载首页
async function loadDashboard() {
try {
// 获取操行分
const conductRes = await apiGet(`/api/student/conduct/${STUDENT_ID}`);
if (conductRes && conductRes.success) {
document.getElementById('totalPoints').textContent = conductRes.data.total_points;
// 显示最近5条记录
const records = conductRes.data.records.slice(0, 5);
let html = '';
records.forEach(record => {
const pointsClass = record.points_change > 0 ? 'plus' : 'minus';
html += `
<div class="record-item">
<span class="record-points ${pointsClass}">${record.points_change > 0 ? '+' : ''}${record.points_change}</span>
<span class="record-reason">${record.reason}</span>
<span class="record-time">${formatDate(record.created_at)}</span>
</div>
`;
});
if (records.length === 0) {
html = '<div style="text-align:center;padding:20px;color:#999;">暂无记录</div>';
}
document.getElementById('recentRecords').innerHTML = html;
}
// 获取作业统计
const homeworkRes = await apiGet(`/api/student/homework/${STUDENT_ID}`);
if (homeworkRes && homeworkRes.success) {
const stats = homeworkRes.data.statistics;
const rate = stats.total > 0 ? Math.round(stats.submitted / stats.total * 100) : 0;
document.getElementById('homeworkRate').textContent = `${rate}%`;
}
// 获取考勤统计
const attendanceRes = await apiGet(`/api/student/attendance/${STUDENT_ID}`);
if (attendanceRes && attendanceRes.success) {
const stats = attendanceRes.data.statistics;
const total = stats.present + stats.absent + stats.late + stats.leave;
const rate = total > 0 ? Math.round(stats.present / total * 100) : 100;
document.getElementById('attendanceRate').textContent = `${rate}%`;
}
} catch (error) {
console.error('加载首页失败:', error);
}
// 加载对应页面数据
switch(pageName) {
case 'dashboard':
loadDashboard();
break;
case 'conduct':
loadConductHistory();
break;
case 'homework':
loadHomework();
break;
case 'attendance':
loadAttendance();
break;
}
// 加载操行分历史
async function loadConductHistory(page = 1) {
conductPage = page;
try {
const res = await apiGet(`/api/student/conduct/${STUDENT_ID}`, {
limit: 20,
offset: (page - 1) * 20
}
// 加载首页
async function loadDashboard() {
try {
// 获取操行分
const conductRes = await apiGet(`/api/student/conduct/${STUDENT_ID}`);
if (conductRes && conductRes.success) {
document.getElementById('totalPoints').textContent = conductRes.data.total_points;
// 显示最近5条记录
const records = conductRes.data.records.slice(0, 5);
let html = '';
records.forEach(record => {
const pointsClass = record.points_change > 0 ? 'plus' : 'minus';
html += `
<div class="record-item">
<span class="record-points ${pointsClass}">${record.points_change > 0 ? '+' : ''}${record.points_change}</span>
<span class="record-reason">${record.reason}</span>
<span class="record-time">${formatDate(record.created_at)}</span>
</div>
`;
});
if (res && res.success) {
document.getElementById('conductTotalPoints').textContent = res.data.total_points;
let html = '<div class="table-wrapper"><table><thead><tr><th>时间</th><th>分数变动</th><th>原因</th><th>操作人</th></tr></thead><tbody>';
res.data.records.forEach(record => {
const pointsClass = record.points_change > 0 ? 'plus' : 'minus';
html += `
<tr>
<td>${formatDateTime(record.created_at)}</td>
<td class="record-points ${pointsClass}">${record.points_change > 0 ? '+' : ''}${record.points_change}</td>
<td>${record.reason}</td>
<td>${record.recorder_name}</td>
</tr>
`;
});
if (res.data.records.length === 0) {
html += '<tr><td colspan="4" style="text-align:center;">暂无记录</td></tr>';
}
html += '</tbody></table></div>';
document.getElementById('conductRecords').innerHTML = html;
// 分页
const total = res.data.total || res.data.records.length;
conductTotalPages = Math.ceil(total / 20);
renderConductPagination();
if (records.length === 0) {
html = '<div style="text-align:center;padding:20px;color:#999;">暂无记录</div>';
}
} catch (error) {
console.error('加载操行分历史失败:', error);
}
}
function renderConductPagination() {
const container = document.getElementById('conductPagination');
if (!container) return;
if (conductTotalPages <= 1) {
container.innerHTML = '';
return;
}
let html = '';
for (let i = 1; i <= conductTotalPages; i++) {
if (i === conductPage) {
html += `<span class="active">${i}</span>`;
} else {
html += `<a href="#" onclick="loadConductHistory(${i}); return false;">${i}</a>`;
}
}
container.innerHTML = html;
}
// 加载作业
async function loadHomework() {
try {
const res = await apiGet(`/api/student/homework/${STUDENT_ID}`);
if (res && res.success) {
let html = '';
res.data.homework.forEach(hw => {
html += `
<tr>
<td>${hw.subject_name}</td>
<td>${hw.title}</td>
<td>${hw.deadline}</td>
<td>${getStatusBadge(hw.status, 'homework')}</td>
<td>${hw.comments || '-'}</td>
</tr>
`;
});
if (res.data.homework.length === 0) {
html = '<tr><td colspan="5" style="text-align:center;">暂无作业</td></tr>';
}
document.getElementById('homeworkList').innerHTML = html;
}
} catch (error) {
console.error('加载作业失败:', error);
}
}
// 加载考勤
async function loadAttendance() {
try {
const res = await apiGet(`/api/student/attendance/${STUDENT_ID}`);
if (res && res.success) {
const stats = res.data.statistics;
document.getElementById('attPresent').textContent = stats.present || 0;
document.getElementById('attAbsent').textContent = stats.absent || 0;
document.getElementById('attLate').textContent = stats.late || 0;
document.getElementById('attLeave').textContent = stats.leave || 0;
let html = '';
res.data.records.forEach(record => {
html += `
<tr>
<td>${record.date}</td>
<td>${getStatusBadge(record.status, 'attendance')}</td>
<td>${record.reason || '-'}</td>
</tr>
`;
});
if (res.data.records.length === 0) {
html = '<tr><td colspan="3" style="text-align:center;">暂无考勤记录</td></tr>';
}
document.getElementById('attendanceList').innerHTML = html;
}
} catch (error) {
console.error('加载考勤失败:', error);
}
}
// 修改密码
document.getElementById('passwordForm')?.addEventListener('submit', async (e) => {
e.preventDefault();
const oldPassword = document.getElementById('oldPassword').value;
const newPassword = document.getElementById('newPassword').value;
const confirmPassword = document.getElementById('confirmPassword').value;
if (newPassword !== confirmPassword) {
showToast('两次输入的新密码不一致', 'error');
return;
document.getElementById('recentRecords').innerHTML = html;
}
const res = await apiPost('/api/auth/change-password', {
old_password: oldPassword,
new_password: newPassword
// 获取作业统计
const homeworkRes = await apiGet(`/api/student/homework/${STUDENT_ID}`);
if (homeworkRes && homeworkRes.success) {
const stats = homeworkRes.data.statistics;
const rate = stats.total > 0 ? Math.round(stats.submitted / stats.total * 100) : 0;
document.getElementById('homeworkRate').textContent = `${rate}%`;
}
// 获取考勤统计
const attendanceRes = await apiGet(`/api/student/attendance/${STUDENT_ID}`);
if (attendanceRes && attendanceRes.success) {
const stats = attendanceRes.data.statistics;
const total = stats.present + stats.absent + stats.late + stats.leave;
const rate = total > 0 ? Math.round(stats.present / total * 100) : 100;
document.getElementById('attendanceRate').textContent = `${rate}%`;
}
} catch (error) {
console.error('加载首页失败:', error);
}
}
// 加载操行分历史
async function loadConductHistory(page = 1) {
conductPage = page;
try {
const res = await apiGet(`/api/student/conduct/${STUDENT_ID}`, {
limit: 20,
offset: (page - 1) * 20
});
if (res && res.success) {
showToast('密码修改成功,请重新登录');
setTimeout(() => logout(), 1500);
document.getElementById('conductTotalPoints').textContent = res.data.total_points;
let html = '<div class="table-wrapper"><table><thead><tr><th>时间</th><th>分数变动</th><th>原因</th><th>操作人</th></tr></thead><tbody>';
res.data.records.forEach(record => {
const pointsClass = record.points_change > 0 ? 'plus' : 'minus';
html += `
<tr>
<td>${formatDateTime(record.created_at)}</td>
<td class="record-points ${pointsClass}">${record.points_change > 0 ? '+' : ''}${record.points_change}</td>
<td>${record.reason}</td>
<td>${record.recorder_name}</td>
</tr>
`;
});
if (res.data.records.length === 0) {
html += '<tr><td colspan="4" style="text-align:center;">暂无记录</td></tr>';
}
html += '</tbody></table></div>';
document.getElementById('conductRecords').innerHTML = html;
// 分页
const total = res.data.total || res.data.records.length;
conductTotalPages = Math.ceil(total / 20);
renderConductPagination();
}
} catch (error) {
console.error('加载操行分历史失败:', error);
}
}
function renderConductPagination() {
const container = document.getElementById('conductPagination');
if (!container) return;
if (conductTotalPages <= 1) {
container.innerHTML = '';
return;
}
let html = '';
for (let i = 1; i <= conductTotalPages; i++) {
if (i === conductPage) {
html += `<span class="active">${i}</span>`;
} else {
showToast(res?.message || '密码修改失败', 'error');
}
});
// 强制修改密码
document.getElementById('forcePasswordForm')?.addEventListener('submit', async (e) => {
e.preventDefault();
const newPassword = document.getElementById('forceNewPassword').value;
const confirmPassword = document.getElementById('forceConfirmPassword').value;
if (newPassword !== confirmPassword) {
alert('两次输入的新密码不一致');
return;
}
const res = await apiPost('/api/auth/change-password', {
old_password: newPassword,
new_password: newPassword
});
if (res && res.success) {
showToast('密码修改成功,请重新登录');
setTimeout(() => logout(), 1500);
} else {
alert(res?.message || '密码修改失败');
}
});
// 检查是否需要强制修改密码
function checkForceChangePassword() {
const user = getUserInfo();
if (user && user.need_change_password) {
document.getElementById('forceChangePasswordModal').style.display = 'flex';
html += `<a href="#" onclick="loadConductHistory(${i}); return false;">${i}</a>`;
}
}
container.innerHTML = html;
}
// 加载作业
async function loadHomework() {
try {
const res = await apiGet(`/api/student/homework/${STUDENT_ID}`);
if (res && res.success) {
let html = '';
res.data.homework.forEach(hw => {
html += `
<tr>
<td>${hw.subject_name}</td>
<td>${hw.title}</td>
<td>${hw.deadline}</td>
<td>${getStatusBadge(hw.status, 'homework')}</td>
<td>${hw.comments || '-'}</td>
</tr>
`;
});
if (res.data.homework.length === 0) {
html = '<tr><td colspan="5" style="text-align:center;">暂无作业</td></tr>';
}
document.getElementById('homeworkList').innerHTML = html;
}
} catch (error) {
console.error('加载作业失败:', error);
}
}
// 加载考勤
async function loadAttendance() {
try {
const res = await apiGet(`/api/student/attendance/${STUDENT_ID}`);
if (res && res.success) {
const stats = res.data.statistics;
document.getElementById('attPresent').textContent = stats.present || 0;
document.getElementById('attAbsent').textContent = stats.absent || 0;
document.getElementById('attLate').textContent = stats.late || 0;
document.getElementById('attLeave').textContent = stats.leave || 0;
let html = '';
res.data.records.forEach(record => {
html += `
<tr>
<td>${record.date}</td>
<td>${getStatusBadge(record.status, 'attendance')}</td>
<td>${record.reason || '-'}</td>
</tr>
`;
});
if (res.data.records.length === 0) {
html = '<tr><td colspan="3" style="text-align:center;">暂无考勤记录</td></tr>';
}
document.getElementById('attendanceList').innerHTML = html;
}
} catch (error) {
console.error('加载考勤失败:', error);
}
}
// 修改密码
document.getElementById('passwordForm')?.addEventListener('submit', async (e) => {
e.preventDefault();
const oldPassword = document.getElementById('oldPassword').value;
const newPassword = document.getElementById('newPassword').value;
const confirmPassword = document.getElementById('confirmPassword').value;
// 初始化
document.querySelectorAll('.nav-item').forEach(btn => {
btn.addEventListener('click', () => {
showPage(btn.dataset.page);
});
if (newPassword !== confirmPassword) {
showToast('两次输入的新密码不一致', 'error');
return;
}
const res = await apiPost('/api/auth/change-password', {
old_password: oldPassword,
new_password: newPassword
});
loadDashboard();
checkForceChangePassword();
</script>
</body>
</html>
if (res && res.success) {
showToast('密码修改成功,请重新登录');
setTimeout(() => logout(), 1500);
} else {
showToast(res?.message || '密码修改失败', 'error');
}
});
// 强制修改密码
document.getElementById('forcePasswordForm')?.addEventListener('submit', async (e) => {
e.preventDefault();
const newPassword = document.getElementById('forceNewPassword').value;
const confirmPassword = document.getElementById('forceConfirmPassword').value;
if (newPassword !== confirmPassword) {
alert('两次输入的新密码不一致');
return;
}
const res = await apiPost('/api/auth/change-password', {
old_password: newPassword,
new_password: newPassword
});
if (res && res.success) {
showToast('密码修改成功,请重新登录');
setTimeout(() => logout(), 1500);
} else {
alert(res?.message || '密码修改失败');
}
});
// 检查是否需要强制修改密码
function checkForceChangePassword() {
const user = getUserInfo();
if (user && user.need_change_password) {
document.getElementById('forceChangePasswordModal').style.display = 'flex';
}
}
// 初始化
document.querySelectorAll('.nav-item').forEach(btn => {
btn.addEventListener('click', () => {
showPage(btn.dataset.page);
});
});
loadDashboard();
checkForceChangePassword();
</script>
<?php include __DIR__ . '/../includes/footer.php'; ?>