feat: 实现教师资料更新、操作日志和系统设置功能
新增教师资料更新功能,包括个人信息修改和密码更新 添加操作日志记录系统,记录用户关键操作 实现系统设置模块,支持动态配置系统参数 重构数据库模型,新增教师表和系统设置表 优化成绩录入逻辑,支持平时分、期中和期末成绩计算 添加数据导出功能,支持学生、教师和成绩数据导出 完善管理员后台,增加统计图表和操作日志查看
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -196,11 +196,14 @@ class AuthManager {
|
||||
});
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
alert('退出登录成功');
|
||||
window.location.href = '/login';
|
||||
} else {
|
||||
alert(result.message || '退出登录失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Logout error:', error);
|
||||
window.location.href = '/login';
|
||||
alert('退出登录出错');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,24 @@ class TeacherManager {
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.initDashboard();
|
||||
async init() {
|
||||
this.updateCurrentTime();
|
||||
setInterval(() => this.updateCurrentTime(), 1000);
|
||||
await this.loadUserInfo();
|
||||
|
||||
// 页面路由逻辑
|
||||
if (document.getElementById('courseList')) {
|
||||
this.initDashboard();
|
||||
}
|
||||
if (document.getElementById('studentTableBody') && document.getElementById('courseSelect')) {
|
||||
this.initGradeEntry();
|
||||
}
|
||||
if (document.getElementById('gradeTableBody')) {
|
||||
this.initGradeManagement();
|
||||
}
|
||||
if (document.getElementById('profileForm')) {
|
||||
this.initProfilePage();
|
||||
}
|
||||
}
|
||||
|
||||
updateCurrentTime() {
|
||||
@@ -24,27 +38,100 @@ class TeacherManager {
|
||||
timeElement.textContent = now.toLocaleString('zh-CN', options);
|
||||
}
|
||||
}
|
||||
|
||||
async loadUserInfo() {
|
||||
try {
|
||||
const res = await fetch('/api/auth/me');
|
||||
const result = await res.json();
|
||||
if (result.success && result.data.user) {
|
||||
const user = result.data.user;
|
||||
this.user = user;
|
||||
|
||||
// Update Sidebar
|
||||
const nameEls = document.querySelectorAll('#teacherName, #userName, #profileName');
|
||||
const idEls = document.querySelectorAll('#teacherId, #profileId, #inputTeacherId');
|
||||
|
||||
nameEls.forEach(el => el.textContent = user.name);
|
||||
idEls.forEach(el => {
|
||||
if (el.tagName === 'INPUT') el.value = user.id;
|
||||
else el.textContent = user.id;
|
||||
});
|
||||
|
||||
// Profile Page Specific
|
||||
const inputName = document.getElementById('inputName');
|
||||
const inputClass = document.getElementById('inputClass');
|
||||
if (inputName) inputName.value = user.name;
|
||||
if (inputClass) inputClass.value = user.class || '';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Load user info failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// ================= Dashboard =================
|
||||
async initDashboard() {
|
||||
// 检查是否在仪表板页面
|
||||
if (!document.getElementById('courseList')) return;
|
||||
await Promise.all([
|
||||
this.loadCourses(),
|
||||
this.loadManagedClasses()
|
||||
]);
|
||||
this.initAddCourse();
|
||||
this.initEditCourse();
|
||||
}
|
||||
|
||||
async loadManagedClasses() {
|
||||
const managedClassesSection = document.getElementById('managedClassesSection');
|
||||
const managedClassList = document.getElementById('managedClassList');
|
||||
const classCountEl = document.getElementById('classCount');
|
||||
|
||||
if (!managedClassList) return;
|
||||
|
||||
try {
|
||||
const res = await fetch(`${this.apiBase}/my-classes`);
|
||||
const result = await res.json();
|
||||
|
||||
if (result.success && result.data.classes.length > 0) {
|
||||
const classes = result.data.classes;
|
||||
classCountEl.textContent = classes.length;
|
||||
managedClassesSection.style.display = 'block';
|
||||
|
||||
managedClassList.innerHTML = classes.map(c => `
|
||||
<div class="col-md-6 col-xl-4 mb-3">
|
||||
<div class="card h-100 border-0 shadow-sm">
|
||||
<div class="card-body d-flex align-items-center">
|
||||
<div class="bg-info bg-opacity-10 text-info rounded p-3 me-3">
|
||||
<i class="fas fa-users fa-lg"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h6 class="mb-1 fw-bold">${c.class_name}</h6>
|
||||
<p class="text-muted small mb-0">${c.major || '专业未设置'} | ${c.grade}级</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
} else {
|
||||
classCountEl.textContent = '0';
|
||||
managedClassesSection.style.display = 'none';
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Load managed classes failed', e);
|
||||
}
|
||||
}
|
||||
|
||||
async loadCourses() {
|
||||
try {
|
||||
const response = await fetch(`${this.apiBase}/courses`);
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
this.renderDashboard(result.data.courses);
|
||||
} else {
|
||||
if (window.authManager) {
|
||||
window.authManager.showNotification(result.message || '获取课程失败', 'error');
|
||||
}
|
||||
this.courses = result.data.courses;
|
||||
this.renderDashboard(this.courses);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fetch teacher data failed:', error);
|
||||
console.error('Fetch courses failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
renderDashboard(courses) {
|
||||
const courseList = document.getElementById('courseList');
|
||||
if (!courseList) return;
|
||||
@@ -57,16 +144,22 @@ class TeacherManager {
|
||||
courseList.innerHTML = courses.map(course => `
|
||||
<div class="col-md-6 col-xl-4 mb-4">
|
||||
<div class="card h-100 border-0 shadow-sm course-card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<span class="badge bg-primary bg-opacity-10 text-primary px-3 py-2">
|
||||
<div class="card-body p-4">
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<span class="badge bg-primary bg-opacity-10 text-primary">
|
||||
<i class="fas fa-book me-1"></i> ${course.course_code || 'CODE'}
|
||||
</span>
|
||||
<span class="text-muted small">${course.credit} 学分</span>
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-link text-secondary p-0 btn-edit-course"
|
||||
data-id="${course.id}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<span class="text-muted small">${course.credit} 学分</span>
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="card-title fw-bold mb-2">${course.course_name}</h5>
|
||||
<p class="card-text text-secondary small mb-4">
|
||||
<i class="fas fa-users me-1"></i> 学生人数: ${course.student_count || 0}
|
||||
<i class="fas fa-users me-1"></i> ${course.class_name || '班级未指定'}
|
||||
</p>
|
||||
<div class="d-grid gap-2">
|
||||
<a href="/teacher/grade_entry?courseId=${course.id}" class="btn btn-outline-primary btn-sm">
|
||||
@@ -81,28 +174,439 @@ class TeacherManager {
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
// 更新统计数据
|
||||
document.getElementById('courseCount').textContent = courses.length;
|
||||
// Calculate total students
|
||||
const totalStudents = courses.reduce((sum, c) => sum + (c.student_count || 0), 0);
|
||||
document.getElementById('totalStudents').textContent = totalStudents;
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.teacherManager = new TeacherManager();
|
||||
|
||||
// 从 Session 获取用户信息并更新 UI
|
||||
fetch('/api/auth/me')
|
||||
.then(res => res.json())
|
||||
.then(result => {
|
||||
if (result.success && result.data.user) {
|
||||
const user = result.data.user;
|
||||
const nameEl = document.getElementById('userName');
|
||||
const teacherNameEl = document.getElementById('teacherName');
|
||||
initAddCourse() {
|
||||
const btn = document.getElementById('addCourseBtn');
|
||||
const modalEl = document.getElementById('addCourseModal');
|
||||
const saveBtn = document.getElementById('saveCourseBtn');
|
||||
|
||||
if (!btn || !modalEl) return;
|
||||
|
||||
const modal = new bootstrap.Modal(modalEl);
|
||||
|
||||
btn.addEventListener('click', async () => {
|
||||
// Load classes
|
||||
try {
|
||||
const res = await fetch(`${this.apiBase}/classes`);
|
||||
const result = await res.json();
|
||||
if (result.success) {
|
||||
const select = document.getElementById('classSelect');
|
||||
select.innerHTML = '<option value="">请选择班级...</option>' +
|
||||
result.data.classes.map(c => `<option value="${c.id}">${c.class_name}</option>`).join('');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Load classes failed', e);
|
||||
}
|
||||
modal.show();
|
||||
});
|
||||
|
||||
saveBtn.addEventListener('click', async () => {
|
||||
const form = document.getElementById('addCourseForm');
|
||||
const formData = new FormData(form);
|
||||
const data = Object.fromEntries(formData.entries());
|
||||
|
||||
try {
|
||||
const res = await fetch(`${this.apiBase}/courses`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
const result = await res.json();
|
||||
|
||||
if (nameEl) nameEl.textContent = user.name;
|
||||
if (teacherNameEl) teacherNameEl.textContent = user.name;
|
||||
if (result.success) {
|
||||
alert('课程创建成功');
|
||||
modal.hide();
|
||||
this.loadCourses();
|
||||
form.reset();
|
||||
} else {
|
||||
alert(result.message || '创建失败');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Create course failed', e);
|
||||
alert('系统错误');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
initEditCourse() {
|
||||
const modalEl = document.getElementById('editCourseModal');
|
||||
if (!modalEl) return;
|
||||
|
||||
const modal = new bootstrap.Modal(modalEl);
|
||||
const updateBtn = document.getElementById('updateCourseBtn');
|
||||
|
||||
// Use event delegation for dynamically created edit buttons
|
||||
document.addEventListener('click', async (e) => {
|
||||
const btn = e.target.closest('.btn-edit-course');
|
||||
if (!btn) return;
|
||||
|
||||
const courseId = btn.dataset.id;
|
||||
if (!this.courses) return;
|
||||
const courseData = this.courses.find(c => c.id == courseId);
|
||||
|
||||
if (!courseData) return;
|
||||
|
||||
// Fill form
|
||||
document.getElementById('editCourseId').value = courseData.id;
|
||||
document.getElementById('editCourseName').value = courseData.course_name;
|
||||
document.getElementById('editCourseCode').value = courseData.course_code;
|
||||
document.getElementById('editCourseCredit').value = courseData.credit;
|
||||
document.getElementById('editSemester').value = courseData.semester;
|
||||
|
||||
// Load classes and select current one
|
||||
try {
|
||||
const res = await fetch(`${this.apiBase}/classes`);
|
||||
const result = await res.json();
|
||||
if (result.success) {
|
||||
const select = document.getElementById('editClassSelect');
|
||||
select.innerHTML = '<option value="">请选择班级...</option>' +
|
||||
result.data.classes.map(c => `<option value="${c.id}" ${c.id == courseData.class_id ? 'selected' : ''}>${c.class_name}</option>`).join('');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Load classes failed', e);
|
||||
}
|
||||
|
||||
modal.show();
|
||||
});
|
||||
|
||||
updateBtn.addEventListener('click', async () => {
|
||||
const form = document.getElementById('editCourseForm');
|
||||
const formData = new FormData(form);
|
||||
const data = Object.fromEntries(formData.entries());
|
||||
const courseId = data.id;
|
||||
|
||||
try {
|
||||
const res = await fetch(`${this.apiBase}/courses/${courseId}`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
const result = await res.json();
|
||||
|
||||
if (result.success) {
|
||||
alert('课程更新成功');
|
||||
modal.hide();
|
||||
this.loadCourses();
|
||||
} else {
|
||||
alert(result.message || '更新失败');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Update course failed', e);
|
||||
alert('系统错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ================= Grade Entry =================
|
||||
async initGradeEntry() {
|
||||
const courseSelect = document.getElementById('courseSelect');
|
||||
|
||||
// Load Courses for Select
|
||||
try {
|
||||
const response = await fetch(`${this.apiBase}/courses`);
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
courseSelect.innerHTML = '<option value="">请选择课程...</option>' +
|
||||
result.data.courses.map(c => `<option value="${c.id}">${c.course_name} (${c.class_name || '未指定班级'})</option>`).join('');
|
||||
|
||||
// Check URL params
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const courseId = params.get('courseId');
|
||||
if (courseId) {
|
||||
courseSelect.value = courseId;
|
||||
this.loadStudentsForGradeEntry(courseId);
|
||||
}
|
||||
}
|
||||
} catch (e) { console.error(e); }
|
||||
|
||||
courseSelect.addEventListener('change', (e) => {
|
||||
if (e.target.value) {
|
||||
this.loadStudentsForGradeEntry(e.target.value);
|
||||
} else {
|
||||
document.getElementById('studentTableBody').innerHTML = '<tr><td colspan="7" class="text-center py-5 text-muted">请选择课程以加载学生列表</td></tr>';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async loadStudentsForGradeEntry(courseId) {
|
||||
const tbody = document.getElementById('studentTableBody');
|
||||
tbody.innerHTML = '<tr><td colspan="7" class="text-center py-5"><div class="spinner-border text-primary" role="status"></div><div class="mt-2">加载中...</div></td></tr>';
|
||||
|
||||
// Since we don't have a direct "get students by course" API that returns grades yet,
|
||||
// we might need to rely on what we have.
|
||||
// Actually, we need to fetch students enrolled in the class of this course.
|
||||
// AND fetch their existing grades for this course.
|
||||
// For simplicity, let's assume we have an endpoint or we modify `getGradeStatistics` or similar?
|
||||
// No, we should probably add `GET /api/teacher/course/:id/students`?
|
||||
// Or just use `GET /api/teacher/grades?courseId=X`.
|
||||
|
||||
// Let's assume `GET /api/teacher/grades?courseId=X` returns the list of students with their grades (or null if no grade).
|
||||
// I need to implement this backend logic if it's missing.
|
||||
// For now, I'll simulate or try to use what I have.
|
||||
// I'll add `getCourseGrades` to TeacherController.
|
||||
|
||||
// Wait, I can't modify backend endlessly.
|
||||
// Let's check `TeacherController.js` again.
|
||||
// It has `addScore`.
|
||||
// It does NOT have `getCourseGrades`.
|
||||
// I need to add it to support this view.
|
||||
|
||||
try {
|
||||
// Placeholder: I will add this endpoint in next step.
|
||||
const res = await fetch(`${this.apiBase}/grades?courseId=${courseId}`);
|
||||
const result = await res.json();
|
||||
|
||||
if (result.success) {
|
||||
this.renderGradeEntryTable(result.data.grades);
|
||||
} else {
|
||||
tbody.innerHTML = `<tr><td colspan="7" class="text-center text-danger">${result.message}</td></tr>`;
|
||||
}
|
||||
} catch (e) {
|
||||
tbody.innerHTML = `<tr><td colspan="7" class="text-center text-danger">加载失败: ${e.message}</td></tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
renderGradeEntryTable(grades) {
|
||||
const tbody = document.getElementById('studentTableBody');
|
||||
if (!grades || grades.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="7" class="text-center py-5 text-muted">该课程暂无学生</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
tbody.innerHTML = grades.map(g => `
|
||||
<tr>
|
||||
<td>${g.student_id}</td>
|
||||
<td>${g.student_name}</td>
|
||||
<td><input type="number" class="form-control form-control-sm" value="${g.usual_score || ''}" placeholder="平时"></td>
|
||||
<td><input type="number" class="form-control form-control-sm" value="${g.midterm_score || ''}" placeholder="期中"></td>
|
||||
<td><input type="number" class="form-control form-control-sm" value="${g.final_score || ''}" placeholder="期末"></td>
|
||||
<td>${g.total_score || '-'}</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary save-grade-btn" data-student-id="${g.student_id}">
|
||||
<i class="fas fa-save"></i> 保存
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
|
||||
// Bind save events
|
||||
document.querySelectorAll('.save-grade-btn').forEach(btn => {
|
||||
btn.addEventListener('click', (e) => this.saveGrade(e.target.closest('tr'), btn.dataset.studentId));
|
||||
});
|
||||
}
|
||||
|
||||
async saveGrade(row, studentId) {
|
||||
const inputs = row.querySelectorAll('input');
|
||||
const usual = inputs[0].value;
|
||||
const midterm = inputs[1].value;
|
||||
const final = inputs[2].value;
|
||||
const courseId = document.getElementById('courseSelect').value;
|
||||
|
||||
// Simple calculation for total score preview (Backend should do the real one)
|
||||
const total = (usual * 0.3 + midterm * 0.3 + final * 0.4).toFixed(1); // Example weights
|
||||
|
||||
try {
|
||||
const res = await fetch(`${this.apiBase}/grades`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
studentId,
|
||||
courseId,
|
||||
usual_score: usual,
|
||||
midterm_score: midterm,
|
||||
final_score: final,
|
||||
score: total // Passing total score as 'score' for compatibility with existing API
|
||||
})
|
||||
});
|
||||
|
||||
const result = await res.json();
|
||||
if (result.success) {
|
||||
// Update total cell
|
||||
row.cells[5].textContent = total;
|
||||
// Show success feedback
|
||||
const btn = row.querySelector('.save-grade-btn');
|
||||
const originalText = btn.innerHTML;
|
||||
btn.innerHTML = '<i class="fas fa-check"></i> 已保存';
|
||||
btn.classList.remove('btn-primary');
|
||||
btn.classList.add('btn-success');
|
||||
setTimeout(() => {
|
||||
btn.innerHTML = originalText;
|
||||
btn.classList.add('btn-primary');
|
||||
btn.classList.remove('btn-success');
|
||||
}, 2000);
|
||||
} else {
|
||||
alert(result.message || '保存失败');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
alert('保存失败');
|
||||
}
|
||||
}
|
||||
|
||||
// ================= Grade Management =================
|
||||
async initGradeManagement() {
|
||||
// Similar logic to Grade Entry but Read-Only or Filter focused
|
||||
// Fetch courses for filter
|
||||
try {
|
||||
const response = await fetch(`${this.apiBase}/courses`);
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
const select = document.getElementById('courseSelectFilter');
|
||||
select.innerHTML = '<option value="">全部课程</option>' +
|
||||
result.data.courses.map(c => `<option value="${c.id}">${c.course_name}</option>`).join('');
|
||||
}
|
||||
} catch (e) { console.error(e); }
|
||||
|
||||
document.getElementById('searchBtn').addEventListener('click', () => this.searchGrades());
|
||||
|
||||
// Check URL params
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
if (params.get('courseId')) {
|
||||
document.getElementById('courseSelectFilter').value = params.get('courseId');
|
||||
this.searchGrades();
|
||||
}
|
||||
}
|
||||
|
||||
async searchGrades() {
|
||||
const courseId = document.getElementById('courseSelectFilter').value;
|
||||
const studentName = document.getElementById('studentNameFilter').value;
|
||||
const tbody = document.getElementById('gradeTableBody');
|
||||
|
||||
tbody.innerHTML = '<tr><td colspan="7" class="text-center py-5"><div class="spinner-border text-primary"></div></td></tr>';
|
||||
|
||||
try {
|
||||
let url = `${this.apiBase}/grades?`;
|
||||
if (courseId) url += `courseId=${courseId}&`;
|
||||
if (studentName) url += `studentName=${studentName}`;
|
||||
|
||||
const res = await fetch(url);
|
||||
const result = await res.json();
|
||||
|
||||
if (result.success && result.data.grades) {
|
||||
if (result.data.grades.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="7" class="text-center py-5 text-muted">未找到相关成绩记录</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
tbody.innerHTML = result.data.grades.map(g => `
|
||||
<tr>
|
||||
<td>${g.course_name || '-'}</td>
|
||||
<td>${g.student_id || '-'}</td>
|
||||
<td>${g.student_name || '-'}</td>
|
||||
<td>${g.total_score || '-'}</td>
|
||||
<td>${g.grade_point || '-'}</td>
|
||||
<td><span class="badge bg-${this.getBadgeColor(g.grade_level)}">${g.grade_level || '-'}</span></td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-primary" onclick="window.location.href='/teacher/grade_entry?courseId=${g.course_id}'">
|
||||
<i class="fas fa-edit"></i> 修改
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
}
|
||||
} catch (e) {
|
||||
tbody.innerHTML = `<tr><td colspan="7" class="text-center text-danger">查询失败</td></tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
getBadgeColor(level) {
|
||||
if (!level) return 'secondary';
|
||||
if (level.startsWith('A')) return 'success';
|
||||
if (level.startsWith('B')) return 'info';
|
||||
if (level.startsWith('C')) return 'warning';
|
||||
if (level.startsWith('F')) return 'danger';
|
||||
return 'primary';
|
||||
}
|
||||
|
||||
// ================= Profile =================
|
||||
initProfilePage() {
|
||||
const saveProfileBtn = document.getElementById('saveProfileBtn');
|
||||
if (saveProfileBtn) {
|
||||
saveProfileBtn.addEventListener('click', async () => {
|
||||
const name = document.getElementById('inputName').value;
|
||||
const className = document.getElementById('inputClass').value;
|
||||
try {
|
||||
const res = await fetch('/api/auth/update-profile', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name, class: className })
|
||||
});
|
||||
const result = await res.json();
|
||||
if (result.success) {
|
||||
alert('资料更新成功');
|
||||
// 更新侧边栏和顶栏
|
||||
const nameEls = document.querySelectorAll('#teacherName, #userName, #profileName');
|
||||
nameEls.forEach(el => el.textContent = name);
|
||||
} else {
|
||||
alert(result.message || '更新失败');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Update profile failed', e);
|
||||
alert('系统错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const passwordForm = document.getElementById('passwordForm');
|
||||
if (passwordForm) {
|
||||
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;
|
||||
const errorEl = document.getElementById('passwordError');
|
||||
|
||||
// Hide previous error
|
||||
errorEl.style.display = 'none';
|
||||
|
||||
// Basic validation
|
||||
if (newPassword !== confirmPassword) {
|
||||
errorEl.textContent = '两次输入的新密码不一致';
|
||||
errorEl.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword.length < 6) {
|
||||
errorEl.textContent = '新密码长度至少为 6 位';
|
||||
errorEl.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/auth/update-password', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ oldPassword, newPassword })
|
||||
});
|
||||
|
||||
const result = await res.json();
|
||||
if (result.success) {
|
||||
alert('密码修改成功,请重新登录');
|
||||
// Logout and redirect
|
||||
await fetch('/api/auth/logout', { method: 'POST' });
|
||||
window.location.href = '/login';
|
||||
} else {
|
||||
errorEl.textContent = result.message || '修改失败';
|
||||
errorEl.style.display = 'block';
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
errorEl.textContent = '服务器错误,请稍后再试';
|
||||
errorEl.style.display = 'block';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.teacherManager = new TeacherManager();
|
||||
});
|
||||
|
||||
@@ -3,340 +3,334 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>学生成绩管理系统 - 管理员仪表板</title>
|
||||
<link rel="stylesheet" href="/public/css/style.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
</head>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 顶部导航<E5AFBC><E888AA>?-->
|
||||
<nav class="navbar">
|
||||
<div class="navbar-brand">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
<span>XX学校成绩管理系统</span>
|
||||
</div>
|
||||
<div class="navbar-menu">
|
||||
<a href="/" class="btn btn-secondary">
|
||||
<i class="fas fa-home"></i> 主页
|
||||
</a>
|
||||
<div class="navbar-user">
|
||||
<span class="user-name" id="userName">管理<EFBFBD><EFBFBD>?/span>
|
||||
<button id="logoutBtn" class="btn btn-primary">
|
||||
<i class="fas fa-sign-out-alt"></i> 退<><E98080>? </button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- 仪表板容<E69DBF><E5AEB9>?-->
|
||||
<div class="dashboard-container">
|
||||
<!-- 左侧侧边<E4BEA7><E8BEB9>?-->
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<div class="user-info">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
</div>
|
||||
<div class="user-details">
|
||||
<h3 id="adminName">管理<EFBFBD><EFBFBD>?/h3>
|
||||
<p>系统管理<EFBFBD><EFBFBD>?| 权限<E69D83><E99990>?span id="adminRole">超级管理<E7AEA1><E79086>?/span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="sidebar-menu">
|
||||
<li>
|
||||
<a href="#" class="active">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>仪表<EFBFBD><EFBFBD>?/span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/user_management">
|
||||
<i class="fas fa-users"></i>
|
||||
<span>用户管理</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/student_management">
|
||||
<i class="fas fa-user-graduate"></i>
|
||||
<span>学生管理</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="teacher_management.html">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
<span>教师管理</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="grade_statistics.html">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
<span>成绩统计</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="system_settings.html">
|
||||
<i class="fas fa-cog"></i>
|
||||
<span>系统设置</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="data_export.html">
|
||||
<i class="fas fa-download"></i>
|
||||
<span>数据导出</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="audit_log.html">
|
||||
<i class="fas fa-history"></i>
|
||||
<span>操作日志</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<main class="main-content">
|
||||
<div class="content-header">
|
||||
<div>
|
||||
<h1 class="page-title">管理员仪表板</h1>
|
||||
<div class="breadcrumb">
|
||||
<a href="/">主页</a>
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
<span>管理员仪表板</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="current-time" id="currentTime"></div>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon users">
|
||||
<i class="fas fa-users"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value" id="totalUsers">1,248</div>
|
||||
<div class="stat-label">总用户数</div>
|
||||
<div class="stat-change positive">
|
||||
<i class="fas fa-arrow-up"></i> 12 本周新增
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon students">
|
||||
<i class="fas fa-user-graduate"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value" id="totalStudents">3,567</div>
|
||||
<div class="stat-label">学生总数</div>
|
||||
<div class="stat-change positive">
|
||||
<i class="fas fa-arrow-up"></i> 45 本周新增
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon teachers">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value" id="totalTeachers">128</div>
|
||||
<div class="stat-label">教师总数</div>
|
||||
<div class="stat-change">
|
||||
<i class="fas fa-minus"></i> 无变<E697A0><E58F98>? </div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon courses">
|
||||
<i class="fas fa-book"></i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-value" id="totalCourses">89</div>
|
||||
<div class="stat-label">课程总数</div>
|
||||
<div class="stat-change positive">
|
||||
<i class="fas fa-arrow-up"></i> 3 本周新增
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 功能卡片 -->
|
||||
<div class="function-grid">
|
||||
<div class="function-card" onclick="window.location.href='user_management.html'">
|
||||
<div class="function-icon">
|
||||
<i class="fas fa-users"></i>
|
||||
</div>
|
||||
<h3 class="function-title">用户管理</h3>
|
||||
<p class="function-description">
|
||||
管理所有用户账户,包括添加、编辑、删除用户,设置用户角色和权限<E69D83><E99990>? </p>
|
||||
<button class="btn btn-primary">进入管理</button>
|
||||
</div>
|
||||
|
||||
<div class="function-card" onclick="window.location.href='student_management.html'">
|
||||
<div class="function-icon">
|
||||
<i class="fas fa-user-graduate"></i>
|
||||
</div>
|
||||
<h3 class="function-title">学生管理</h3>
|
||||
<p class="function-description">
|
||||
管理学生信息,包括学籍管理、班级分配、信息维护和批量导入导出<E5AFBC><E587BA>? </p>
|
||||
<button class="btn btn-primary">进入管理</button>
|
||||
</div>
|
||||
|
||||
<div class="function-card" onclick="window.location.href='teacher_management.html'">
|
||||
<div class="function-icon">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
</div>
|
||||
<h3 class="function-title">教师管理</h3>
|
||||
<p class="function-description">
|
||||
管理教师信息,包括教师分配、课程安排、权限设置和绩效考核<E88083><E6A0B8>? </p>
|
||||
<button class="btn btn-primary">进入管理</button>
|
||||
</div>
|
||||
|
||||
<div class="function-card" onclick="window.location.href='grade_statistics.html'">
|
||||
<div class="function-icon">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
</div>
|
||||
<h3 class="function-title">成绩统计</h3>
|
||||
<p class="function-description">
|
||||
查看全校成绩统计,生成分析报告,支持图表展示和数据导出<E5AFBC><E587BA>? </p>
|
||||
<button class="btn btn-primary">查看统计</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 系统状<E7BB9F><E78AB6>?-->
|
||||
<div class="system-status">
|
||||
<h2 class="section-title">
|
||||
<i class="fas fa-server"></i>
|
||||
系统状<E7BB9F><E78AB6>? </h2>
|
||||
<div class="status-list">
|
||||
<div class="status-item">
|
||||
<div class="status-indicator online"></div>
|
||||
<div>
|
||||
<div class="status-label">数据库服<EFBFBD><EFBFBD>?/div>
|
||||
<div class="status-value">运行正常 | 响应时间: 12ms</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<div class="status-indicator online"></div>
|
||||
<div>
|
||||
<div class="status-label">Web服务<EFBFBD><EFBFBD>?/div>
|
||||
<div class="status-value">运行正常 | 在线用户: 156</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<div class="status-indicator online"></div>
|
||||
<div>
|
||||
<div class="status-label">文件存储</div>
|
||||
<div class="status-value">使用<EFBFBD><EFBFBD>? 65% | 剩余: 35GB</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<div class="status-indicator warning"></div>
|
||||
<div>
|
||||
<div class="status-label">备份服务</div>
|
||||
<div class="status-value">上次备份: 2天前 | 建议立即备份</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最近活<E8BF91><E6B4BB>?-->
|
||||
<div class="recent-activities">
|
||||
<h2 class="section-title">
|
||||
<i class="fas fa-history"></i>
|
||||
最近活<E8BF91><E6B4BB>? </h2>
|
||||
<ul class="activity-list">
|
||||
<li class="activity-item">
|
||||
<div class="activity-icon">
|
||||
<i class="fas fa-user-plus"></i>
|
||||
</div>
|
||||
<div class="activity-content">
|
||||
<div class="activity-title">新增学生用户</div>
|
||||
<div class="activity-time">10分钟<EFBFBD><EFBFBD>?| 操作<E6938D><E4BD9C>? 管理<E7AEA1><E79086>?/div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="activity-item">
|
||||
<div class="activity-icon">
|
||||
<i class="fas fa-edit"></i>
|
||||
</div>
|
||||
<div class="activity-content">
|
||||
<div class="activity-title">修改教师信息</div>
|
||||
<div class="activity-time">1小时<EFBFBD><EFBFBD>?| 操作<E6938D><E4BD9C>? 管理<E7AEA1><E79086>?/div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="activity-item">
|
||||
<div class="activity-icon">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
</div>
|
||||
<div class="activity-content">
|
||||
<div class="activity-title">生成成绩统计报告</div>
|
||||
<div class="activity-time">3小时<EFBFBD><EFBFBD>?| 操作<E6938D><E4BD9C>? 系统</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="activity-item">
|
||||
<div class="activity-icon">
|
||||
<i class="fas fa-download"></i>
|
||||
</div>
|
||||
<div class="activity-content">
|
||||
<div class="activity-title">导出用户数据</div>
|
||||
<div class="activity-time">5小时<EFBFBD><EFBFBD>?| 操作<E6938D><E4BD9C>? 管理<E7AEA1><E79086>?/div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="activity-item">
|
||||
<div class="activity-icon">
|
||||
<i class="fas fa-cog"></i>
|
||||
</div>
|
||||
<div class="activity-content">
|
||||
<div class="activity-title">系统设置更新</div>
|
||||
<div class="activity-time">1天前 | 操作<E6938D><E4BD9C>? 管理<E7AEA1><E79086>?/div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 更新当前时间
|
||||
function updateCurrentTime() {
|
||||
const now = new Date();
|
||||
const options = {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
weekday: 'long',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
};
|
||||
document.getElementById('currentTime').textContent = now.toLocaleDateString('zh-CN', options);
|
||||
<title>管理员仪表板</title>
|
||||
<!-- Bootstrap 5 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--sidebar-width: 260px;
|
||||
--primary-color: #4e73df;
|
||||
--secondary-color: #858796;
|
||||
--light-bg: #f8f9fc;
|
||||
--admin-gradient: linear-gradient(135deg, #4e73df 0%, #224abe 100%);
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
updateCurrentTime();
|
||||
setInterval(updateCurrentTime, 1000);
|
||||
body {
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
background-color: var(--light-bg);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
// 退出登<E587BA><E799BB>? document.getElementById('logoutBtn').addEventListener('click', function() {
|
||||
if (confirm('确定要退出登录吗<E5BD95><E59097>?)) {
|
||||
// 清除登录状<E5BD95><E78AB6>? localStorage.removeItem('token');
|
||||
localStorage.removeItem('userRole');
|
||||
localStorage.removeItem('userInfo');
|
||||
|
||||
// 跳转到登录页<E5BD95><E9A1B5>? window.location.href = 'login.html';
|
||||
}
|
||||
});
|
||||
/* 侧边栏样式 */
|
||||
.sidebar {
|
||||
width: var(--sidebar-width);
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: var(--admin-gradient);
|
||||
color: white;
|
||||
z-index: 1000;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
// 加载用户信息
|
||||
const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}');
|
||||
if (userInfo.name) {
|
||||
document.getElementById('adminName').textContent = userInfo.name;
|
||||
document.getElementById('userName').textContent = userInfo.name;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
.sidebar-header {
|
||||
padding: 2rem 1.5rem;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
padding: 2rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.user-info h6 { margin-bottom: 0.25rem; font-weight: 600; }
|
||||
.user-info p { font-size: 0.85rem; opacity: 0.8; margin-bottom: 0; }
|
||||
|
||||
.nav-menu { padding: 1rem 0; }
|
||||
.nav-item { padding: 0.25rem 1rem; }
|
||||
|
||||
.nav-link {
|
||||
color: rgba(255,255,255,0.8);
|
||||
padding: 0.8rem 1.25rem;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link.active {
|
||||
color: white;
|
||||
background: rgba(255,255,255,0.15);
|
||||
}
|
||||
|
||||
.nav-link i { width: 20px; text-align: center; }
|
||||
|
||||
/* 主内容区 */
|
||||
.main-content {
|
||||
margin-left: var(--sidebar-width);
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.top-navbar {
|
||||
background: white;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.1);
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-heading h4 { margin-bottom: 0; font-weight: 700; color: #333; }
|
||||
|
||||
.stat-card {
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
.stat-card:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.sidebar { left: -var(--sidebar-width); }
|
||||
.main-content { margin-left: 0; }
|
||||
.sidebar.active { left: 0; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 侧边栏 -->
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<a href="#" class="sidebar-brand">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
<span>成绩管理系统</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="user-profile">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
</div>
|
||||
<div class="user-info text-white">
|
||||
<h6 id="adminName">加载中...</h6>
|
||||
<p>管理员</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-menu">
|
||||
<div class="nav-item">
|
||||
<a href="/admin/dashboard" class="nav-link active">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>仪表板</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/user_management" class="nav-link">
|
||||
<i class="fas fa-users"></i>
|
||||
<span>用户管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/student_management" class="nav-link">
|
||||
<i class="fas fa-user-graduate"></i>
|
||||
<span>学生管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/teacher_management" class="nav-link">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
<span>教师管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/grade_statistics" class="nav-link">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
<span>成绩统计</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/system_settings" class="nav-link">
|
||||
<i class="fas fa-cog"></i>
|
||||
<span>系统设置</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/data_export" class="nav-link">
|
||||
<i class="fas fa-download"></i>
|
||||
<span>数据导出</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/operation_logs" class="nav-link">
|
||||
<i class="fas fa-history"></i>
|
||||
<span>操作日志</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item mt-4">
|
||||
<a href="javascript:void(0)" id="logoutBtn" class="nav-link text-warning">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
<span>退出登录</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- 主内容 -->
|
||||
<div class="main-content">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="top-navbar">
|
||||
<div class="page-heading">
|
||||
<h4>管理员仪表板</h4>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="text-end d-none d-md-block">
|
||||
<div class="small text-muted" id="currentTime"></div>
|
||||
<div class="fw-bold" id="userName">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card border-0 shadow-sm h-100 stat-card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||
<div class="text-primary bg-primary bg-opacity-10 rounded p-3">
|
||||
<i class="fas fa-users fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="text-muted mb-1">总用户数</h5>
|
||||
<h2 class="fw-bold mb-0" id="totalUsers">...</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card border-0 shadow-sm h-100 stat-card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||
<div class="text-success bg-success bg-opacity-10 rounded p-3">
|
||||
<i class="fas fa-user-graduate fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="text-muted mb-1">学生总数</h5>
|
||||
<h2 class="fw-bold mb-0" id="totalStudents">...</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card border-0 shadow-sm h-100 stat-card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||
<div class="text-info bg-info bg-opacity-10 rounded p-3">
|
||||
<i class="fas fa-chalkboard-teacher fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="text-muted mb-1">教师总数</h5>
|
||||
<h2 class="fw-bold mb-0" id="totalTeachers">...</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card border-0 shadow-sm h-100 stat-card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||
<div class="text-warning bg-warning bg-opacity-10 rounded p-3">
|
||||
<i class="fas fa-book fa-2x"></i>
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="text-muted mb-1">课程总数</h5>
|
||||
<h2 class="fw-bold mb-0" id="totalCourses">...</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 快捷入口 -->
|
||||
<h5 class="fw-bold mb-3">快捷管理</h5>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<a href="/admin/user_management" class="text-decoration-none">
|
||||
<div class="card border-0 shadow-sm h-100 stat-card text-center p-4">
|
||||
<div class="text-primary mb-3">
|
||||
<i class="fas fa-users fa-3x"></i>
|
||||
</div>
|
||||
<h6 class="fw-bold text-dark">用户管理</h6>
|
||||
<p class="text-muted small mb-0">管理所有用户账户及权限</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<a href="/admin/student_management" class="text-decoration-none">
|
||||
<div class="card border-0 shadow-sm h-100 stat-card text-center p-4">
|
||||
<div class="text-success mb-3">
|
||||
<i class="fas fa-user-graduate fa-3x"></i>
|
||||
</div>
|
||||
<h6 class="fw-bold text-dark">学生管理</h6>
|
||||
<p class="text-muted small mb-0">管理学生信息及学籍</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<a href="/admin/teacher_management" class="text-decoration-none">
|
||||
<div class="card border-0 shadow-sm h-100 stat-card text-center p-4">
|
||||
<div class="text-info mb-3">
|
||||
<i class="fas fa-chalkboard-teacher fa-3x"></i>
|
||||
</div>
|
||||
<h6 class="fw-bold text-dark">教师管理</h6>
|
||||
<p class="text-muted small mb-0">管理教师信息及排课</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6 col-lg-3">
|
||||
<a href="/admin/grade_statistics" class="text-decoration-none">
|
||||
<div class="card border-0 shadow-sm h-100 stat-card text-center p-4">
|
||||
<div class="text-warning mb-3">
|
||||
<i class="fas fa-chart-bar fa-3x"></i>
|
||||
</div>
|
||||
<h6 class="fw-bold text-dark">成绩统计</h6>
|
||||
<p class="text-muted small mb-0">查看全校成绩统计分析</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap 5 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/public/js/admin.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
266
frontend/views/admin/data_export.html
Normal file
266
frontend/views/admin/data_export.html
Normal file
@@ -0,0 +1,266 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>数据导出 - 管理员端</title>
|
||||
<!-- Bootstrap 5 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--sidebar-width: 260px;
|
||||
--primary-color: #4e73df;
|
||||
--secondary-color: #858796;
|
||||
--light-bg: #f8f9fc;
|
||||
--admin-gradient: linear-gradient(135deg, #4e73df 0%, #224abe 100%);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
background-color: var(--light-bg);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 侧边栏样式 */
|
||||
.sidebar {
|
||||
width: var(--sidebar-width);
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: var(--admin-gradient);
|
||||
color: white;
|
||||
z-index: 1000;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 2rem 1.5rem;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
padding: 2rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.user-info h6 { margin-bottom: 0.25rem; font-weight: 600; }
|
||||
.user-info p { font-size: 0.85rem; opacity: 0.8; margin-bottom: 0; }
|
||||
|
||||
.nav-menu { padding: 1rem 0; }
|
||||
.nav-item { padding: 0.25rem 1rem; }
|
||||
|
||||
.nav-link {
|
||||
color: rgba(255,255,255,0.8);
|
||||
padding: 0.8rem 1.25rem;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link.active {
|
||||
color: white;
|
||||
background: rgba(255,255,255,0.15);
|
||||
}
|
||||
|
||||
.nav-link i { width: 20px; text-align: center; }
|
||||
|
||||
/* 主内容区 */
|
||||
.main-content {
|
||||
margin-left: var(--sidebar-width);
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.top-navbar {
|
||||
background: white;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.1);
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-heading h4 { margin-bottom: 0; font-weight: 700; color: #333; }
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.sidebar { left: -var(--sidebar-width); }
|
||||
.main-content { margin-left: 0; }
|
||||
.sidebar.active { left: 0; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 侧边栏 -->
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<a href="#" class="sidebar-brand">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
<span>成绩管理系统</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="user-profile">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
</div>
|
||||
<div class="user-info text-white">
|
||||
<h6 id="adminName">加载中...</h6>
|
||||
<p>管理员</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-menu">
|
||||
<div class="nav-item">
|
||||
<a href="/admin/dashboard" class="nav-link">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>仪表板</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/user_management" class="nav-link">
|
||||
<i class="fas fa-users"></i>
|
||||
<span>用户管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/student_management" class="nav-link">
|
||||
<i class="fas fa-user-graduate"></i>
|
||||
<span>学生管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/teacher_management" class="nav-link">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
<span>教师管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/grade_statistics" class="nav-link">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
<span>成绩统计</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/system_settings" class="nav-link">
|
||||
<i class="fas fa-cog"></i>
|
||||
<span>系统设置</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/data_export" class="nav-link active">
|
||||
<i class="fas fa-download"></i>
|
||||
<span>数据导出</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/operation_logs" class="nav-link">
|
||||
<i class="fas fa-history"></i>
|
||||
<span>操作日志</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item mt-4">
|
||||
<a href="javascript:void(0)" id="logoutBtn" class="nav-link text-warning">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
<span>退出登录</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- 主内容 -->
|
||||
<div class="main-content">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="top-navbar">
|
||||
<div class="page-heading">
|
||||
<h4>数据导出</h4>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="text-end d-none d-md-block">
|
||||
<div class="small text-muted" id="currentTime"></div>
|
||||
<div class="fw-bold" id="userName">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- 导出选项 -->
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card border-0 shadow-sm h-100 text-center p-4">
|
||||
<div class="text-primary mb-3">
|
||||
<i class="fas fa-user-graduate fa-4x"></i>
|
||||
</div>
|
||||
<h5 class="fw-bold">学生信息导出</h5>
|
||||
<p class="text-muted small">导出所有学生的基本信息、班级、专业等数据。</p>
|
||||
<button class="btn btn-outline-primary w-100" id="exportStudentsBtn">
|
||||
<i class="fas fa-download me-1"></i> 导出 Excel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card border-0 shadow-sm h-100 text-center p-4">
|
||||
<div class="text-info mb-3">
|
||||
<i class="fas fa-chalkboard-teacher fa-4x"></i>
|
||||
</div>
|
||||
<h5 class="fw-bold">教师信息导出</h5>
|
||||
<p class="text-muted small">导出所有教师的基本信息、职称、部门等数据。</p>
|
||||
<button class="btn btn-outline-info w-100" id="exportTeachersBtn">
|
||||
<i class="fas fa-download me-1"></i> 导出 Excel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card border-0 shadow-sm h-100 text-center p-4">
|
||||
<div class="text-warning mb-3">
|
||||
<i class="fas fa-table fa-4x"></i>
|
||||
</div>
|
||||
<h5 class="fw-bold">成绩数据导出</h5>
|
||||
<p class="text-muted small">导出所有课程的成绩记录,包括平时、期末、总评。</p>
|
||||
<button class="btn btn-outline-warning w-100" id="exportGradesBtn">
|
||||
<i class="fas fa-download me-1"></i> 导出 Excel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap 5 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/public/js/admin.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
279
frontend/views/admin/grade_statistics.html
Normal file
279
frontend/views/admin/grade_statistics.html
Normal file
@@ -0,0 +1,279 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>成绩统计 - 管理员端</title>
|
||||
<!-- Bootstrap 5 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--sidebar-width: 260px;
|
||||
--primary-color: #4e73df;
|
||||
--secondary-color: #858796;
|
||||
--light-bg: #f8f9fc;
|
||||
--admin-gradient: linear-gradient(135deg, #4e73df 0%, #224abe 100%);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
background-color: var(--light-bg);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 侧边栏样式 */
|
||||
.sidebar {
|
||||
width: var(--sidebar-width);
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: var(--admin-gradient);
|
||||
color: white;
|
||||
z-index: 1000;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 2rem 1.5rem;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
padding: 2rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.user-info h6 { margin-bottom: 0.25rem; font-weight: 600; }
|
||||
.user-info p { font-size: 0.85rem; opacity: 0.8; margin-bottom: 0; }
|
||||
|
||||
.nav-menu { padding: 1rem 0; }
|
||||
.nav-item { padding: 0.25rem 1rem; }
|
||||
|
||||
.nav-link {
|
||||
color: rgba(255,255,255,0.8);
|
||||
padding: 0.8rem 1.25rem;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link.active {
|
||||
color: white;
|
||||
background: rgba(255,255,255,0.15);
|
||||
}
|
||||
|
||||
.nav-link i { width: 20px; text-align: center; }
|
||||
|
||||
/* 主内容区 */
|
||||
.main-content {
|
||||
margin-left: var(--sidebar-width);
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.top-navbar {
|
||||
background: white;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.1);
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-heading h4 { margin-bottom: 0; font-weight: 700; color: #333; }
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.sidebar { left: -var(--sidebar-width); }
|
||||
.main-content { margin-left: 0; }
|
||||
.sidebar.active { left: 0; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 侧边栏 -->
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<a href="#" class="sidebar-brand">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
<span>成绩管理系统</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="user-profile">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
</div>
|
||||
<div class="user-info text-white">
|
||||
<h6 id="adminName">加载中...</h6>
|
||||
<p>管理员</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-menu">
|
||||
<div class="nav-item">
|
||||
<a href="/admin/dashboard" class="nav-link">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>仪表板</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/user_management" class="nav-link">
|
||||
<i class="fas fa-users"></i>
|
||||
<span>用户管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/student_management" class="nav-link">
|
||||
<i class="fas fa-user-graduate"></i>
|
||||
<span>学生管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/teacher_management" class="nav-link">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
<span>教师管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/grade_statistics" class="nav-link active">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
<span>成绩统计</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/system_settings" class="nav-link">
|
||||
<i class="fas fa-cog"></i>
|
||||
<span>系统设置</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/data_export" class="nav-link">
|
||||
<i class="fas fa-download"></i>
|
||||
<span>数据导出</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/operation_logs" class="nav-link">
|
||||
<i class="fas fa-history"></i>
|
||||
<span>操作日志</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item mt-4">
|
||||
<a href="javascript:void(0)" id="logoutBtn" class="nav-link text-warning">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
<span>退出登录</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- 主内容 -->
|
||||
<div class="main-content">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="top-navbar">
|
||||
<div class="page-heading">
|
||||
<h4>成绩统计</h4>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="text-end d-none d-md-block">
|
||||
<div class="small text-muted" id="currentTime"></div>
|
||||
<div class="fw-bold" id="userName">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计图表 -->
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-lg-8">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">各课程平均分对比</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<canvas id="courseAvgChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">全校及格率</h6>
|
||||
</div>
|
||||
<div class="card-body d-flex align-items-center justify-content-center">
|
||||
<div style="width: 100%; max-width: 300px;">
|
||||
<canvas id="passRateChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 详细数据表 -->
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">课程成绩详情</h6>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-4">课程代码</th>
|
||||
<th>课程名称</th>
|
||||
<th>任课教师</th>
|
||||
<th>选课人数</th>
|
||||
<th>平均分</th>
|
||||
<th>最高分</th>
|
||||
<th>最低分</th>
|
||||
<th class="text-end pe-4">及格率</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="gradeStatsBody">
|
||||
<tr><td colspan="8" class="text-center py-5">加载中...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chart.js & Bootstrap JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/public/js/admin.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
245
frontend/views/admin/operation_logs.html
Normal file
245
frontend/views/admin/operation_logs.html
Normal file
@@ -0,0 +1,245 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>操作日志 - 管理员端</title>
|
||||
<!-- Bootstrap 5 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--sidebar-width: 260px;
|
||||
--primary-color: #4e73df;
|
||||
--secondary-color: #858796;
|
||||
--light-bg: #f8f9fc;
|
||||
--admin-gradient: linear-gradient(135deg, #4e73df 0%, #224abe 100%);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
background-color: var(--light-bg);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 侧边栏样式 */
|
||||
.sidebar {
|
||||
width: var(--sidebar-width);
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: var(--admin-gradient);
|
||||
color: white;
|
||||
z-index: 1000;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 2rem 1.5rem;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
padding: 2rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.user-info h6 { margin-bottom: 0.25rem; font-weight: 600; }
|
||||
.user-info p { font-size: 0.85rem; opacity: 0.8; margin-bottom: 0; }
|
||||
|
||||
.nav-menu { padding: 1rem 0; }
|
||||
.nav-item { padding: 0.25rem 1rem; }
|
||||
|
||||
.nav-link {
|
||||
color: rgba(255,255,255,0.8);
|
||||
padding: 0.8rem 1.25rem;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link.active {
|
||||
color: white;
|
||||
background: rgba(255,255,255,0.15);
|
||||
}
|
||||
|
||||
.nav-link i { width: 20px; text-align: center; }
|
||||
|
||||
/* 主内容区 */
|
||||
.main-content {
|
||||
margin-left: var(--sidebar-width);
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.top-navbar {
|
||||
background: white;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.1);
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-heading h4 { margin-bottom: 0; font-weight: 700; color: #333; }
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.sidebar { left: -var(--sidebar-width); }
|
||||
.main-content { margin-left: 0; }
|
||||
.sidebar.active { left: 0; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 侧边栏 -->
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<a href="#" class="sidebar-brand">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
<span>成绩管理系统</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="user-profile">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
</div>
|
||||
<div class="user-info text-white">
|
||||
<h6 id="adminName">加载中...</h6>
|
||||
<p>管理员</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-menu">
|
||||
<div class="nav-item">
|
||||
<a href="/admin/dashboard" class="nav-link">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>仪表板</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/user_management" class="nav-link">
|
||||
<i class="fas fa-users"></i>
|
||||
<span>用户管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/student_management" class="nav-link">
|
||||
<i class="fas fa-user-graduate"></i>
|
||||
<span>学生管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/teacher_management" class="nav-link">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
<span>教师管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/grade_statistics" class="nav-link">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
<span>成绩统计</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/system_settings" class="nav-link">
|
||||
<i class="fas fa-cog"></i>
|
||||
<span>系统设置</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/data_export" class="nav-link">
|
||||
<i class="fas fa-download"></i>
|
||||
<span>数据导出</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/operation_logs" class="nav-link active">
|
||||
<i class="fas fa-history"></i>
|
||||
<span>操作日志</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item mt-4">
|
||||
<a href="javascript:void(0)" id="logoutBtn" class="nav-link text-warning">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
<span>退出登录</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- 主内容 -->
|
||||
<div class="main-content">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="top-navbar">
|
||||
<div class="page-heading">
|
||||
<h4>操作日志</h4>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="text-end d-none d-md-block">
|
||||
<div class="small text-muted" id="currentTime"></div>
|
||||
<div class="fw-bold" id="userName">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-4">操作时间</th>
|
||||
<th>操作人</th>
|
||||
<th>操作类型</th>
|
||||
<th>详情</th>
|
||||
<th>IP地址</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="logsTableBody">
|
||||
<tr><td colspan="5" class="text-center py-5">暂无日志数据</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap 5 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/public/js/admin.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,439 +3,327 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>学生管理 - XX学校成绩管理系统</title>
|
||||
<link rel="stylesheet" href="/public/css/style.css">
|
||||
<title>学生管理 - 管理员端</title>
|
||||
<!-- Bootstrap 5 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--sidebar-width: 260px;
|
||||
--primary-color: #4e73df;
|
||||
--secondary-color: #858796;
|
||||
--light-bg: #f8f9fc;
|
||||
--admin-gradient: linear-gradient(135deg, #4e73df 0%, #224abe 100%);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
background-color: var(--light-bg);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 侧边栏样式 */
|
||||
.sidebar {
|
||||
width: var(--sidebar-width);
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: var(--admin-gradient);
|
||||
color: white;
|
||||
z-index: 1000;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 2rem 1.5rem;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
padding: 2rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.user-info h6 { margin-bottom: 0.25rem; font-weight: 600; }
|
||||
.user-info p { font-size: 0.85rem; opacity: 0.8; margin-bottom: 0; }
|
||||
|
||||
.nav-menu { padding: 1rem 0; }
|
||||
.nav-item { padding: 0.25rem 1rem; }
|
||||
|
||||
.nav-link {
|
||||
color: rgba(255,255,255,0.8);
|
||||
padding: 0.8rem 1.25rem;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link.active {
|
||||
color: white;
|
||||
background: rgba(255,255,255,0.15);
|
||||
}
|
||||
|
||||
.nav-link i { width: 20px; text-align: center; }
|
||||
|
||||
/* 主内容区 */
|
||||
.main-content {
|
||||
margin-left: var(--sidebar-width);
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.top-navbar {
|
||||
background: white;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.1);
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-heading h4 { margin-bottom: 0; font-weight: 700; color: #333; }
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.sidebar { left: -var(--sidebar-width); }
|
||||
.main-content { margin-left: 0; }
|
||||
.sidebar.active { left: 0; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 导航<EFBFBD><EFBFBD>?-->
|
||||
<nav class="navbar">
|
||||
<div class="container">
|
||||
<div class="navbar-brand">
|
||||
<a href="/">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
<span>XX学校成绩管理系统</span>
|
||||
<!-- 侧边栏 -->
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<a href="#" class="sidebar-brand">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
<span>成绩管理系统</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="user-profile">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
</div>
|
||||
<div class="user-info text-white">
|
||||
<h6 id="adminName">加载中...</h6>
|
||||
<p>管理员</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-menu">
|
||||
<div class="nav-item">
|
||||
<a href="/admin/dashboard" class="nav-link">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>仪表板</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-menu">
|
||||
<a href="/" class="navbar-item">
|
||||
<i class="fas fa-home"></i>
|
||||
<span>主页</span>
|
||||
</a>
|
||||
<a href="/admin/dashboard" class="navbar-item">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>控制面板</span>
|
||||
</a>
|
||||
<a href="/admin/student_management" class="navbar-item active">
|
||||
<div class="nav-item">
|
||||
<a href="/admin/user_management" class="nav-link">
|
||||
<i class="fas fa-users"></i>
|
||||
<span>学生管理</span>
|
||||
</a>
|
||||
<a href="/teacher/grade_management" class="navbar-item">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
<span>成绩管理</span>
|
||||
</a>
|
||||
<a href="/admin/user_management" class="navbar-item">
|
||||
<i class="fas fa-user-cog"></i>
|
||||
<span>用户管理</span>
|
||||
</a>
|
||||
<div class="navbar-user">
|
||||
<i class="fas fa-user-circle"></i>
|
||||
<span>管理<EFBFBD><EFBFBD>?/span>
|
||||
<div class="user-dropdown">
|
||||
<a href="#"><i class="fas fa-user"></i> 个人资料</a>
|
||||
<a href="#"><i class="fas fa-cog"></i> 设置</a>
|
||||
<a href="/login"><i class="fas fa-sign-out-alt"></i> 退出登<E587BA><E799BB>?/a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/student_management" class="nav-link active">
|
||||
<i class="fas fa-user-graduate"></i>
|
||||
<span>学生管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/teacher_management" class="nav-link">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
<span>教师管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/grade_statistics" class="nav-link">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
<span>成绩统计</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/system_settings" class="nav-link">
|
||||
<i class="fas fa-cog"></i>
|
||||
<span>系统设置</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/data_export" class="nav-link">
|
||||
<i class="fas fa-download"></i>
|
||||
<span>数据导出</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/operation_logs" class="nav-link">
|
||||
<i class="fas fa-history"></i>
|
||||
<span>操作日志</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item mt-4">
|
||||
<a href="javascript:void(0)" id="logoutBtn" class="nav-link text-warning">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
<span>退出登录</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- 主内容 -->
|
||||
<div class="main-content">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="top-navbar">
|
||||
<div class="page-heading">
|
||||
<h4>学生管理</h4>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="text-end d-none d-md-block">
|
||||
<div class="small text-muted" id="currentTime"></div>
|
||||
<div class="fw-bold" id="userName">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- 主要内容<EFBFBD><EFBFBD>?-->
|
||||
<main class="main-content">
|
||||
<div class="container">
|
||||
<div class="student-management">
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-header">
|
||||
<h1><i class="fas fa-users"></i> 学生管理</h1>
|
||||
<p>管理学生基本信息,支持添加、编辑、删除和查询学生信息</p>
|
||||
</div>
|
||||
|
||||
<!-- 筛选区<E98089><E58CBA>?-->
|
||||
<div class="filter-section">
|
||||
<div class="filter-row">
|
||||
<div class="filter-group">
|
||||
<label for="student-id"><i class="fas fa-id-card"></i> 学号</label>
|
||||
<input type="text" id="student-id" placeholder="请输入学<E585A5><E5ADA6>?>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label for="student-name"><i class="fas fa-user"></i> 姓名</label>
|
||||
<input type="text" id="student-name" placeholder="请输入姓<E585A5><E5A793>?>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label for="class-select"><i class="fas fa-school"></i> 班级</label>
|
||||
<select id="class-select">
|
||||
<option value="">全部班级</option>
|
||||
<option value="计算机科学与技<E4B88E><E68A80>?<3F><>?>计算机科学与技<E4B88E><E68A80>?<3F><>?/option>
|
||||
<option value="计算机科学与技<EFBFBD><EFBFBD>?<EFBFBD><EFBFBD>?>计算机科学与技<EFBFBD><EFBFBD>?<3F><>?/option>
|
||||
<option value="软件工程1<E7A88B><31>?>软件工程1<E7A88B><31>?/option>
|
||||
<option value="软件工程2<EFBFBD><EFBFBD>?>软件工程2<EFBFBD><EFBFBD>?/option>
|
||||
<option value="网络工程1<E7A88B><31>?>网络工程1<E7A88B><31>?/option>
|
||||
<option value="网络工程2<EFBFBD><EFBFBD>?>网络工程2<EFBFBD><EFBFBD>?/option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label for="gender-select"><i class="fas fa-venus-mars"></i> 性别</label>
|
||||
<select id="gender-select">
|
||||
<option value="">全部性别</option>
|
||||
<option value="<22><>?><3E><>?/option>
|
||||
<option value="<EFBFBD><EFBFBD>?><EFBFBD><EFBFBD>?/option>
|
||||
</select>
|
||||
<!-- 筛选栏 -->
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-body">
|
||||
<form id="studentFilterForm" class="row g-3 align-items-end">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">搜索学生</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
<input type="text" class="form-control" id="studentSearch" placeholder="学号、姓名或班级">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button id="add-btn" class="btn-add">
|
||||
<i class="fas fa-plus"></i>
|
||||
添加学生
|
||||
</button>
|
||||
<button id="search-btn" class="btn-search">
|
||||
<i class="fas fa-search"></i>
|
||||
查询学生
|
||||
</button>
|
||||
<button id="reset-btn" class="btn-reset">
|
||||
<i class="fas fa-redo"></i>
|
||||
重置条件
|
||||
</button>
|
||||
<button id="export-btn" class="btn-export">
|
||||
<i class="fas fa-file-export"></i>
|
||||
导出数据
|
||||
<!--
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">班级筛选</label>
|
||||
<select class="form-select" id="classFilter">
|
||||
<option value="">全部班级</option>
|
||||
</select>
|
||||
</div>
|
||||
-->
|
||||
<div class="col-md-9 text-md-end">
|
||||
<button type="button" class="btn btn-primary" id="addStudentBtn">
|
||||
<i class="fas fa-plus me-1"></i> 新增学生
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 学生表格 -->
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 学生列表 -->
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="student-table">
|
||||
<thead>
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th>学号</th>
|
||||
<th class="ps-4">学号</th>
|
||||
<th>姓名</th>
|
||||
<th>性别</th>
|
||||
<th>班级</th>
|
||||
<th>联系电话</th>
|
||||
<th>邮箱</th>
|
||||
<th>入学时间</th>
|
||||
<th>操作</th>
|
||||
<th>专业</th>
|
||||
<th>年级</th>
|
||||
<th>联系方式</th>
|
||||
<th class="text-end pe-4">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="student-table-body">
|
||||
<!-- 数据将通过JavaScript动态加<E68081><E58AA0>?-->
|
||||
<tr>
|
||||
<td colspan="8" class="loading">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
<p>正在加载学生数据...</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody id="studentTableBody">
|
||||
<tr><td colspan="7" class="text-center py-5">加载中...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 分页控件 -->
|
||||
<div class="pagination" id="pagination">
|
||||
<button id="prev-btn" disabled>
|
||||
<i class="fas fa-chevron-left"></i>
|
||||
上一<E4B88A><E4B880>? </button>
|
||||
<button class="active">1</button>
|
||||
<button>2</button>
|
||||
<button>3</button>
|
||||
<button id="next-btn">
|
||||
下一<E4B88B><E4B880>? <i class="fas fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-footer bg-white border-0 py-3">
|
||||
<nav>
|
||||
<ul class="pagination justify-content-center mb-0" id="studentPagination">
|
||||
<!-- Pagination rendered by JS -->
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add/Edit Student Modal -->
|
||||
<div class="modal fade" id="studentModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="studentModalTitle">新增学生</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="studentForm">
|
||||
<input type="hidden" name="isEdit" id="studentIsEdit" value="false">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">学号</label>
|
||||
<input type="text" class="form-control" name="id" id="studentId" required>
|
||||
<div class="form-text">学号将作为登录账号,默认密码同账号</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">姓名</label>
|
||||
<input type="text" class="form-control" name="name" id="studentNameInput" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">班级</label>
|
||||
<input type="text" class="form-control" name="class" id="studentClass" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">专业</label>
|
||||
<input type="text" class="form-control" name="major" id="studentMajor">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">年级</label>
|
||||
<input type="number" class="form-control" name="grade" id="studentGrade" placeholder="例如:2023">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">联系方式</label>
|
||||
<input type="text" class="form-control" name="contact_info" id="studentContact">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" id="saveStudentBtn">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<p>© 2023 XX学校成绩管理系统. 版权所<E69D83><E68980>?</p>
|
||||
<p>技术支<EFBFBD><EFBFBD>? 计算机科学与技术学<E69CAF><E5ADA6>?/p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// 模拟学生数据
|
||||
const mockStudents = [
|
||||
{
|
||||
id: '20230001',
|
||||
name: '张三',
|
||||
gender: '<27><>?,
|
||||
class: '计算机科学与技<EFBFBD><EFBFBD>?<EFBFBD><EFBFBD>?,
|
||||
phone: '13800138001',
|
||||
email: 'zhangsan@example.com',
|
||||
enrollmentDate: '2023-09-01'
|
||||
},
|
||||
{
|
||||
id: '20230002',
|
||||
name: '李四',
|
||||
gender: '<27><>?,
|
||||
class: '计算机科学与技<EFBFBD><EFBFBD>?<EFBFBD><EFBFBD>?,
|
||||
phone: '13800138002',
|
||||
email: 'lisi@example.com',
|
||||
enrollmentDate: '2023-09-01'
|
||||
},
|
||||
{
|
||||
id: '20230003',
|
||||
name: '王五',
|
||||
gender: '<27><>?,
|
||||
class: '计算机科学与技<EFBFBD><EFBFBD>?<EFBFBD><EFBFBD>?,
|
||||
phone: '13800138003',
|
||||
email: 'wangwu@example.com',
|
||||
enrollmentDate: '2023-09-01'
|
||||
},
|
||||
{
|
||||
id: '20230004',
|
||||
name: '赵六',
|
||||
gender: '<27><>?,
|
||||
class: '软件工程1<EFBFBD><EFBFBD>?,
|
||||
phone: '13800138004',
|
||||
email: 'zhaoliu@example.com',
|
||||
enrollmentDate: '2023-09-01'
|
||||
},
|
||||
{
|
||||
id: '20230005',
|
||||
name: '钱七',
|
||||
gender: '<27><>?,
|
||||
class: '软件工程2<EFBFBD><EFBFBD>?,
|
||||
phone: '13800138005',
|
||||
email: 'qianqi@example.com',
|
||||
enrollmentDate: '2023-09-01'
|
||||
},
|
||||
{
|
||||
id: '20230006',
|
||||
name: '孙八',
|
||||
gender: '<27><>?,
|
||||
class: '网络工程1<EFBFBD><EFBFBD>?,
|
||||
phone: '13800138006',
|
||||
email: 'sunba@example.com',
|
||||
enrollmentDate: '2023-09-01'
|
||||
},
|
||||
{
|
||||
id: '20230007',
|
||||
name: '周九',
|
||||
gender: '<27><>?,
|
||||
class: '网络工程2<EFBFBD><EFBFBD>?,
|
||||
phone: '13800138007',
|
||||
email: 'zhoujiu@example.com',
|
||||
enrollmentDate: '2023-09-01'
|
||||
},
|
||||
{
|
||||
id: '20230008',
|
||||
name: '吴十',
|
||||
gender: '<27><>?,
|
||||
class: '计算机科学与技<EFBFBD><EFBFBD>?<EFBFBD><EFBFBD>?,
|
||||
phone: '13800138008',
|
||||
email: 'wushi@example.com',
|
||||
enrollmentDate: '2023-09-01'
|
||||
}
|
||||
];
|
||||
|
||||
// 当前页数<E9A1B5><E695B0>? let currentPage = 1;
|
||||
const pageSize = 5;
|
||||
let filteredStudents = [...mockStudents];
|
||||
|
||||
// DOM元素
|
||||
const studentTableBody = document.getElementById('student-table-body');
|
||||
const pagination = document.getElementById('pagination');
|
||||
const prevBtn = document.getElementById('prev-btn');
|
||||
const nextBtn = document.getElementById('next-btn');
|
||||
const searchBtn = document.getElementById('search-btn');
|
||||
const resetBtn = document.getElementById('reset-btn');
|
||||
const addBtn = document.getElementById('add-btn');
|
||||
const exportBtn = document.getElementById('export-btn');
|
||||
|
||||
// 渲染学生表格
|
||||
function renderStudentTable() {
|
||||
const startIndex = (currentPage - 1) * pageSize;
|
||||
const endIndex = startIndex + pageSize;
|
||||
const pageStudents = filteredStudents.slice(startIndex, endIndex);
|
||||
|
||||
if (pageStudents.length === 0) {
|
||||
studentTableBody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="8" class="no-results">
|
||||
<i class="fas fa-user-slash"></i>
|
||||
<h3>没有找到学生信息</h3>
|
||||
<p>请尝试调整筛选条件或添加新学<E696B0><E5ADA6>?/p>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
let tableHTML = '';
|
||||
pageStudents.forEach(student => {
|
||||
tableHTML += `
|
||||
<tr>
|
||||
<td>${student.id}</td>
|
||||
<td>${student.name}</td>
|
||||
<td>${student.gender}</td>
|
||||
<td>${student.class}</td>
|
||||
<td>${student.phone}</td>
|
||||
<td>${student.email}</td>
|
||||
<td>${student.enrollmentDate}</td>
|
||||
<td>
|
||||
<div class="action-buttons-cell">
|
||||
<button class="btn-edit" onclick="editStudent('${student.id}')">
|
||||
<i class="fas fa-edit"></i>
|
||||
编辑
|
||||
</button>
|
||||
<button class="btn-delete" onclick="deleteStudent('${student.id}')">
|
||||
<i class="fas fa-trash"></i>
|
||||
删除
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
studentTableBody.innerHTML = tableHTML;
|
||||
}
|
||||
|
||||
// 更新分页控件
|
||||
function updatePagination() {
|
||||
const totalPages = Math.ceil(filteredStudents.length / pageSize);
|
||||
const paginationButtons = pagination.querySelectorAll('button:not(#prev-btn):not(#next-btn)');
|
||||
|
||||
// 更新页码按钮
|
||||
paginationButtons.forEach((btn, index) => {
|
||||
if (index < totalPages) {
|
||||
btn.textContent = index + 1;
|
||||
btn.style.display = 'inline-block';
|
||||
btn.classList.toggle('active', index + 1 === currentPage);
|
||||
} else {
|
||||
btn.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// 更新上一<E4B88A><E4B880>?下一页按钮状<E992AE><E78AB6>? prevBtn.disabled = currentPage === 1;
|
||||
nextBtn.disabled = currentPage === totalPages || totalPages === 0;
|
||||
}
|
||||
|
||||
// 筛选学<E98089><E5ADA6>? function filterStudents() {
|
||||
const studentId = document.getElementById('student-id').value.trim();
|
||||
const studentName = document.getElementById('student-name').value.trim();
|
||||
const selectedClass = document.getElementById('class-select').value;
|
||||
const selectedGender = document.getElementById('gender-select').value;
|
||||
|
||||
filteredStudents = mockStudents.filter(student => {
|
||||
const matchesId = !studentId || student.id.includes(studentId);
|
||||
const matchesName = !studentName || student.name.includes(studentName);
|
||||
const matchesClass = !selectedClass || student.class === selectedClass;
|
||||
const matchesGender = !selectedGender || student.gender === selectedGender;
|
||||
|
||||
return matchesId && matchesName && matchesClass && matchesGender;
|
||||
});
|
||||
|
||||
currentPage = 1;
|
||||
renderStudentTable();
|
||||
updatePagination();
|
||||
}
|
||||
|
||||
// 重置筛选条<E98089><E69DA1>? function resetFilters() {
|
||||
document.getElementById('student-id').value = '';
|
||||
document.getElementById('student-name').value = '';
|
||||
document.getElementById('class-select').value = '';
|
||||
document.getElementById('gender-select').value = '';
|
||||
|
||||
filteredStudents = [...mockStudents];
|
||||
currentPage = 1;
|
||||
renderStudentTable();
|
||||
updatePagination();
|
||||
}
|
||||
|
||||
// 添加学生
|
||||
function addStudent() {
|
||||
alert('添加学生功能将在后端API完成后实<E5908E><E5AE9E>?);
|
||||
// 这里可以打开一个模态框来添加学生信<E7949F><E4BFA1>? }
|
||||
|
||||
// 编辑学生
|
||||
function editStudent(studentId) {
|
||||
alert(`编辑学生 ${studentId} 功能将在后端API完成后实现`);
|
||||
// 这里可以打开一个模态框来编辑学生信<E7949F><E4BFA1>? }
|
||||
|
||||
// 删除学生
|
||||
function deleteStudent(studentId) {
|
||||
if (confirm(`确定要删除学号为 ${studentId} 的学生吗?`)) {
|
||||
alert(`删除学生 ${studentId} 功能将在后端API完成后实现`);
|
||||
// 这里可以调用API删除学生
|
||||
}
|
||||
}
|
||||
|
||||
// 导出数据
|
||||
function exportData() {
|
||||
alert('导出数据功能将在后端API完成后实<EFBFBD><EFBFBD>?);
|
||||
// 这里可以调用API导出Excel或CSV文件
|
||||
}
|
||||
|
||||
// 页面切换
|
||||
function goToPage(page) {
|
||||
currentPage = page;
|
||||
renderStudentTable();
|
||||
updatePagination();
|
||||
}
|
||||
|
||||
// 初始化事件监<E4BBB6><E79B91>? function initEventListeners() {
|
||||
searchBtn.addEventListener('click', filterStudents);
|
||||
resetBtn.addEventListener('click', resetFilters);
|
||||
addBtn.addEventListener('click', addStudent);
|
||||
exportBtn.addEventListener('click', exportData);
|
||||
|
||||
prevBtn.addEventListener('click', () => {
|
||||
if (currentPage > 1) {
|
||||
goToPage(currentPage - 1);
|
||||
}
|
||||
});
|
||||
|
||||
nextBtn.addEventListener('click', () => {
|
||||
const totalPages = Math.ceil(filteredStudents.length / pageSize);
|
||||
if (currentPage < totalPages) {
|
||||
goToPage(currentPage + 1);
|
||||
}
|
||||
});
|
||||
|
||||
// 页码按钮点击事件
|
||||
pagination.addEventListener('click', (e) => {
|
||||
if (e.target.tagName === 'BUTTON' &&
|
||||
!e.target.id &&
|
||||
!e.target.classList.contains('active')) {
|
||||
const page = parseInt(e.target.textContent);
|
||||
goToPage(page);
|
||||
}
|
||||
});
|
||||
|
||||
// 输入框回车搜<E8BDA6><E6909C>? document.getElementById('student-id').addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') filterStudents();
|
||||
});
|
||||
|
||||
document.getElementById('student-name').addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') filterStudents();
|
||||
});
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// 模拟API延迟加载
|
||||
setTimeout(() => {
|
||||
renderStudentTable();
|
||||
updatePagination();
|
||||
initEventListeners();
|
||||
}, 500);
|
||||
});
|
||||
</script>
|
||||
<!-- Bootstrap 5 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/public/js/admin.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
290
frontend/views/admin/system_settings.html
Normal file
290
frontend/views/admin/system_settings.html
Normal file
@@ -0,0 +1,290 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>系统设置 - 管理员端</title>
|
||||
<!-- Bootstrap 5 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--sidebar-width: 260px;
|
||||
--primary-color: #4e73df;
|
||||
--secondary-color: #858796;
|
||||
--light-bg: #f8f9fc;
|
||||
--admin-gradient: linear-gradient(135deg, #4e73df 0%, #224abe 100%);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
background-color: var(--light-bg);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 侧边栏样式 */
|
||||
.sidebar {
|
||||
width: var(--sidebar-width);
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: var(--admin-gradient);
|
||||
color: white;
|
||||
z-index: 1000;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 2rem 1.5rem;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
padding: 2rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.user-info h6 { margin-bottom: 0.25rem; font-weight: 600; }
|
||||
.user-info p { font-size: 0.85rem; opacity: 0.8; margin-bottom: 0; }
|
||||
|
||||
.nav-menu { padding: 1rem 0; }
|
||||
.nav-item { padding: 0.25rem 1rem; }
|
||||
|
||||
.nav-link {
|
||||
color: rgba(255,255,255,0.8);
|
||||
padding: 0.8rem 1.25rem;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link.active {
|
||||
color: white;
|
||||
background: rgba(255,255,255,0.15);
|
||||
}
|
||||
|
||||
.nav-link i { width: 20px; text-align: center; }
|
||||
|
||||
/* 主内容区 */
|
||||
.main-content {
|
||||
margin-left: var(--sidebar-width);
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.top-navbar {
|
||||
background: white;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.1);
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-heading h4 { margin-bottom: 0; font-weight: 700; color: #333; }
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.sidebar { left: -var(--sidebar-width); }
|
||||
.main-content { margin-left: 0; }
|
||||
.sidebar.active { left: 0; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 侧边栏 -->
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<a href="#" class="sidebar-brand">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
<span>成绩管理系统</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="user-profile">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
</div>
|
||||
<div class="user-info text-white">
|
||||
<h6 id="adminName">加载中...</h6>
|
||||
<p>管理员</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-menu">
|
||||
<div class="nav-item">
|
||||
<a href="/admin/dashboard" class="nav-link">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>仪表板</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/user_management" class="nav-link">
|
||||
<i class="fas fa-users"></i>
|
||||
<span>用户管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/student_management" class="nav-link">
|
||||
<i class="fas fa-user-graduate"></i>
|
||||
<span>学生管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/teacher_management" class="nav-link">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
<span>教师管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/grade_statistics" class="nav-link">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
<span>成绩统计</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/system_settings" class="nav-link active">
|
||||
<i class="fas fa-cog"></i>
|
||||
<span>系统设置</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/data_export" class="nav-link">
|
||||
<i class="fas fa-download"></i>
|
||||
<span>数据导出</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/operation_logs" class="nav-link">
|
||||
<i class="fas fa-history"></i>
|
||||
<span>操作日志</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item mt-4">
|
||||
<a href="javascript:void(0)" id="logoutBtn" class="nav-link text-warning">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
<span>退出登录</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- 主内容 -->
|
||||
<div class="main-content">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="top-navbar">
|
||||
<div class="page-heading">
|
||||
<h4>系统设置</h4>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="text-end d-none d-md-block">
|
||||
<div class="small text-muted" id="currentTime"></div>
|
||||
<div class="fw-bold" id="userName">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- 基础设置 -->
|
||||
<div class="col-md-6">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">基础设置</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="basicSettingsForm">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">系统名称</label>
|
||||
<input type="text" class="form-control" value="学校成绩管理系统">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">当前学期</label>
|
||||
<select class="form-select">
|
||||
<option value="2023-2024-1">2023-2024 第一学期</option>
|
||||
<option value="2023-2024-2" selected>2023-2024 第二学期</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">开放选课</label>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="courseSelectionSwitch" checked>
|
||||
<label class="form-check-label" for="courseSelectionSwitch">允许学生选课</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">开放查分</label>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="gradeCheckSwitch" checked>
|
||||
<label class="form-check-label" for="gradeCheckSwitch">允许学生查询成绩</label>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">保存设置</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 数据备份 -->
|
||||
<div class="col-md-6">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">数据维护</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle me-1"></i> 定期备份数据可以防止意外丢失。
|
||||
</div>
|
||||
<div class="d-grid gap-3">
|
||||
<button class="btn btn-outline-primary text-start">
|
||||
<i class="fas fa-database me-2"></i> 立即备份数据库
|
||||
</button>
|
||||
<button class="btn btn-outline-warning text-start">
|
||||
<i class="fas fa-trash-alt me-2"></i> 清理系统缓存
|
||||
</button>
|
||||
<button class="btn btn-outline-danger text-start">
|
||||
<i class="fas fa-history me-2"></i> 重置所有学生密码
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap 5 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/public/js/admin.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
316
frontend/views/admin/teacher_management.html
Normal file
316
frontend/views/admin/teacher_management.html
Normal file
@@ -0,0 +1,316 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>教师管理 - 管理员端</title>
|
||||
<!-- Bootstrap 5 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--sidebar-width: 260px;
|
||||
--primary-color: #4e73df;
|
||||
--secondary-color: #858796;
|
||||
--light-bg: #f8f9fc;
|
||||
--admin-gradient: linear-gradient(135deg, #4e73df 0%, #224abe 100%);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
background-color: var(--light-bg);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 侧边栏样式 */
|
||||
.sidebar {
|
||||
width: var(--sidebar-width);
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: var(--admin-gradient);
|
||||
color: white;
|
||||
z-index: 1000;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 2rem 1.5rem;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
padding: 2rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.user-info h6 { margin-bottom: 0.25rem; font-weight: 600; }
|
||||
.user-info p { font-size: 0.85rem; opacity: 0.8; margin-bottom: 0; }
|
||||
|
||||
.nav-menu { padding: 1rem 0; }
|
||||
.nav-item { padding: 0.25rem 1rem; }
|
||||
|
||||
.nav-link {
|
||||
color: rgba(255,255,255,0.8);
|
||||
padding: 0.8rem 1.25rem;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link.active {
|
||||
color: white;
|
||||
background: rgba(255,255,255,0.15);
|
||||
}
|
||||
|
||||
.nav-link i { width: 20px; text-align: center; }
|
||||
|
||||
/* 主内容区 */
|
||||
.main-content {
|
||||
margin-left: var(--sidebar-width);
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.top-navbar {
|
||||
background: white;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.1);
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-heading h4 { margin-bottom: 0; font-weight: 700; color: #333; }
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.sidebar { left: -var(--sidebar-width); }
|
||||
.main-content { margin-left: 0; }
|
||||
.sidebar.active { left: 0; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 侧边栏 -->
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<a href="#" class="sidebar-brand">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
<span>成绩管理系统</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="user-profile">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
</div>
|
||||
<div class="user-info text-white">
|
||||
<h6 id="adminName">加载中...</h6>
|
||||
<p>管理员</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-menu">
|
||||
<div class="nav-item">
|
||||
<a href="/admin/dashboard" class="nav-link">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>仪表板</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/user_management" class="nav-link">
|
||||
<i class="fas fa-users"></i>
|
||||
<span>用户管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/student_management" class="nav-link">
|
||||
<i class="fas fa-user-graduate"></i>
|
||||
<span>学生管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/teacher_management" class="nav-link active">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
<span>教师管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/grade_statistics" class="nav-link">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
<span>成绩统计</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/system_settings" class="nav-link">
|
||||
<i class="fas fa-cog"></i>
|
||||
<span>系统设置</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/data_export" class="nav-link">
|
||||
<i class="fas fa-download"></i>
|
||||
<span>数据导出</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/operation_logs" class="nav-link">
|
||||
<i class="fas fa-history"></i>
|
||||
<span>操作日志</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item mt-4">
|
||||
<a href="javascript:void(0)" id="logoutBtn" class="nav-link text-warning">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
<span>退出登录</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- 主内容 -->
|
||||
<div class="main-content">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="top-navbar">
|
||||
<div class="page-heading">
|
||||
<h4>教师管理</h4>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="text-end d-none d-md-block">
|
||||
<div class="small text-muted" id="currentTime"></div>
|
||||
<div class="fw-bold" id="userName">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选栏 -->
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-body">
|
||||
<form id="teacherFilterForm" class="row g-3 align-items-end">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">搜索教师</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
<input type="text" class="form-control" id="teacherSearch" placeholder="工号或姓名">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-9 text-md-end">
|
||||
<button type="button" class="btn btn-primary" id="addTeacherBtn">
|
||||
<i class="fas fa-plus me-1"></i> 新增教师
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 教师列表 -->
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th class="ps-4">工号</th>
|
||||
<th>姓名</th>
|
||||
<th>职称</th>
|
||||
<th>学院/部门</th>
|
||||
<th>联系方式</th>
|
||||
<th class="text-end pe-4">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="teacherTableBody">
|
||||
<tr><td colspan="6" class="text-center py-5">加载中...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-white border-0 py-3">
|
||||
<nav>
|
||||
<ul class="pagination justify-content-center mb-0" id="teacherPagination">
|
||||
<!-- Pagination rendered by JS -->
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add/Edit Teacher Modal -->
|
||||
<div class="modal fade" id="teacherModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="teacherModalTitle">新增教师</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="teacherForm">
|
||||
<input type="hidden" name="isEdit" id="teacherIsEdit" value="false">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">工号</label>
|
||||
<input type="text" class="form-control" name="id" id="teacherId" required>
|
||||
<div class="form-text">工号将作为登录账号,默认密码同账号</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">姓名</label>
|
||||
<input type="text" class="form-control" name="name" id="teacherNameInput" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">学院/部门</label>
|
||||
<input type="text" class="form-control" name="department" id="teacherDepartment">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">职称</label>
|
||||
<input type="text" class="form-control" name="title" id="teacherTitle">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">联系方式</label>
|
||||
<input type="text" class="form-control" name="contact_info" id="teacherContact">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" id="saveTeacherBtn">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap 5 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/public/js/admin.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,492 +3,327 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>用户管理 - XX学校成绩管理系统</title>
|
||||
<link rel="stylesheet" href="/public/css/style.css">
|
||||
<title>用户管理 - 管理员端</title>
|
||||
<!-- Bootstrap 5 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--sidebar-width: 260px;
|
||||
--primary-color: #4e73df;
|
||||
--secondary-color: #858796;
|
||||
--light-bg: #f8f9fc;
|
||||
--admin-gradient: linear-gradient(135deg, #4e73df 0%, #224abe 100%);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
background-color: var(--light-bg);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 侧边栏样式 */
|
||||
.sidebar {
|
||||
width: var(--sidebar-width);
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: var(--admin-gradient);
|
||||
color: white;
|
||||
z-index: 1000;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 2rem 1.5rem;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
padding: 2rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.user-info h6 { margin-bottom: 0.25rem; font-weight: 600; }
|
||||
.user-info p { font-size: 0.85rem; opacity: 0.8; margin-bottom: 0; }
|
||||
|
||||
.nav-menu { padding: 1rem 0; }
|
||||
.nav-item { padding: 0.25rem 1rem; }
|
||||
|
||||
.nav-link {
|
||||
color: rgba(255,255,255,0.8);
|
||||
padding: 0.8rem 1.25rem;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link.active {
|
||||
color: white;
|
||||
background: rgba(255,255,255,0.15);
|
||||
}
|
||||
|
||||
.nav-link i { width: 20px; text-align: center; }
|
||||
|
||||
/* 主内容区 */
|
||||
.main-content {
|
||||
margin-left: var(--sidebar-width);
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.top-navbar {
|
||||
background: white;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.1);
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-heading h4 { margin-bottom: 0; font-weight: 700; color: #333; }
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.sidebar { left: -var(--sidebar-width); }
|
||||
.main-content { margin-left: 0; }
|
||||
.sidebar.active { left: 0; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 导航<EFBFBD><EFBFBD>?-->
|
||||
<nav class="navbar">
|
||||
<div class="container">
|
||||
<div class="navbar-brand">
|
||||
<a href="/">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
<span>XX学校成绩管理系统</span>
|
||||
<!-- 侧边栏 -->
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<a href="#" class="sidebar-brand">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
<span>成绩管理系统</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="user-profile">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
</div>
|
||||
<div class="user-info text-white">
|
||||
<h6 id="adminName">加载中...</h6>
|
||||
<p>管理员</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-menu">
|
||||
<div class="nav-item">
|
||||
<a href="/admin/dashboard" class="nav-link">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>仪表板</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-menu">
|
||||
<div class="navbar-start">
|
||||
<a href="/admin/dashboard" class="navbar-item">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>仪表<EFBFBD><EFBFBD>?/span>
|
||||
</a>
|
||||
<a href="/admin/user_management" class="navbar-item active">
|
||||
<i class="fas fa-users"></i>
|
||||
<span>用户管理</span>
|
||||
</a>
|
||||
<a href="/admin/student_management" class="navbar-item">
|
||||
<i class="fas fa-user-graduate"></i>
|
||||
<span>学生管理</span>
|
||||
</a>
|
||||
<a href="/teacher/grade_management" class="navbar-item">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
<span>成绩统计</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item user-info">
|
||||
<i class="fas fa-user-circle"></i>
|
||||
<span>管理<EFBFBD><EFBFBD>?/span>
|
||||
<div class="dropdown">
|
||||
<a href="#" class="dropdown-toggle">
|
||||
<i class="fas fa-caret-down"></i>
|
||||
</a>
|
||||
<div class="dropdown-menu">
|
||||
<a href="/admin/dashboard" class="dropdown-item">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
仪表<E4BBAA><E8A1A8>? </a>
|
||||
<a href="#" class="dropdown-item">
|
||||
<i class="fas fa-cog"></i>
|
||||
系统设置
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="/" class="dropdown-item">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
退出登<E587BA><E799BB>? </a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/user_management" class="nav-link active">
|
||||
<i class="fas fa-users"></i>
|
||||
<span>用户管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/student_management" class="nav-link">
|
||||
<i class="fas fa-user-graduate"></i>
|
||||
<span>学生管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/teacher_management" class="nav-link">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
<span>教师管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/grade_statistics" class="nav-link">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
<span>成绩统计</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/system_settings" class="nav-link">
|
||||
<i class="fas fa-cog"></i>
|
||||
<span>系统设置</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/data_export" class="nav-link">
|
||||
<i class="fas fa-download"></i>
|
||||
<span>数据导出</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/admin/operation_logs" class="nav-link">
|
||||
<i class="fas fa-history"></i>
|
||||
<span>操作日志</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item mt-4">
|
||||
<a href="javascript:void(0)" id="logoutBtn" class="nav-link text-warning">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
<span>退出登录</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- 主内容 -->
|
||||
<div class="main-content">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="top-navbar">
|
||||
<div class="page-heading">
|
||||
<h4>用户管理</h4>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="text-end d-none d-md-block">
|
||||
<div class="small text-muted" id="currentTime"></div>
|
||||
<div class="fw-bold" id="userName">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<main class="main-content">
|
||||
<div class="container">
|
||||
<div class="user-management">
|
||||
<!-- 页面标题和面包屑导航 -->
|
||||
<div class="page-header">
|
||||
<h1>用户管理</h1>
|
||||
<div class="breadcrumb">
|
||||
<a href="/">主页</a> >
|
||||
<a href="/admin/dashboard">管理员仪表板</a> >
|
||||
<span>用户管理</span>
|
||||
<!-- 筛选栏 -->
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-body">
|
||||
<form id="filterForm" class="row g-3 align-items-end">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">搜索用户</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
||||
<input type="text" class="form-control" id="search" placeholder="ID、姓名或班级">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">角色筛选</label>
|
||||
<select class="form-select" id="roleFilter">
|
||||
<option value="">全部角色</option>
|
||||
<option value="student">学生</option>
|
||||
<option value="teacher">教师</option>
|
||||
<option value="admin">管理员</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 text-md-end">
|
||||
<button type="button" class="btn btn-primary" id="addUserBtn">
|
||||
<i class="fas fa-plus me-1"></i> 新增用户
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选区<EFBFBD><EFBFBD>?-->
|
||||
<div class="filter-section">
|
||||
<form class="filter-form" id="filter-form">
|
||||
<div class="filter-group">
|
||||
<label for="user-id">用户ID</label>
|
||||
<input type="text" id="user-id" placeholder="请输入用户ID">
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label for="user-name">姓名</label>
|
||||
<input type="text" id="user-name" placeholder="请输入姓<E585A5><E5A793>?>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label for="role-select">角色</label>
|
||||
<select id="role-select">
|
||||
<option value="">全部角色</option>
|
||||
<option value="admin">管理<EFBFBD><EFBFBD>?/option>
|
||||
<option value="teacher">教师</option>
|
||||
<option value="student">学生</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label for="class-select">班级</label>
|
||||
<select id="class-select">
|
||||
<option value="">全部班级</option>
|
||||
<option value="计算机科学与技<E4B88E><E68A80>?<3F><>?>计算机科学与技<E4B88E><E68A80>?<3F><>?/option>
|
||||
<option value="计算机科学与技<EFBFBD><EFBFBD>?<EFBFBD><EFBFBD>?>计算机科学与技<EFBFBD><EFBFBD>?<3F><>?/option>
|
||||
<option value="软件工程1<E7A88B><31>?>软件工程1<E7A88B><31>?/option>
|
||||
<option value="软件工程2<EFBFBD><EFBFBD>?>软件工程2<EFBFBD><EFBFBD>?/option>
|
||||
<option value="网络工程1<E7A88B><31>?>网络工程1<E7A88B><31>?/option>
|
||||
<option value="网络工程2<EFBFBD><EFBFBD>?>网络工程2<EFBFBD><EFBFBD>?/option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<button type="button" class="btn-add" id="add-btn">
|
||||
<i class="fas fa-plus"></i>
|
||||
添加用户
|
||||
</button>
|
||||
<button type="button" class="btn-search" id="search-btn">
|
||||
<i class="fas fa-search"></i>
|
||||
查询
|
||||
</button>
|
||||
<button type="button" class="btn-reset" id="reset-btn">
|
||||
<i class="fas fa-redo"></i>
|
||||
重置
|
||||
</button>
|
||||
<button type="button" class="btn-export" id="export-btn">
|
||||
<i class="fas fa-file-export"></i>
|
||||
导出
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- 结果区域 -->
|
||||
<div class="table-container">
|
||||
<table class="user-table">
|
||||
<thead>
|
||||
<!-- 用户列表 -->
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="bg-light">
|
||||
<tr>
|
||||
<th>用户ID</th>
|
||||
<th class="ps-4">ID</th>
|
||||
<th>姓名</th>
|
||||
<th>角色</th>
|
||||
<th>班级</th>
|
||||
<th>联系电话</th>
|
||||
<th>邮箱</th>
|
||||
<th>班级/部门</th>
|
||||
<th>注册时间</th>
|
||||
<th>操作</th>
|
||||
<th class="text-end pe-4">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="user-table-body">
|
||||
<!-- 数据将通过JavaScript动态加<E68081><E58AA0>?-->
|
||||
<tr>
|
||||
<td colspan="8" class="loading">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
<p>正在加载用户数据...</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody id="userTableBody">
|
||||
<tr><td colspan="6" class="text-center py-5">加载中...</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 分页控件 -->
|
||||
<div class="pagination" id="pagination">
|
||||
<button id="prev-btn" disabled>
|
||||
<i class="fas fa-chevron-left"></i>
|
||||
上一<E4B88A><E4B880>? </button>
|
||||
<div id="page-numbers">
|
||||
<button class="active">1</button>
|
||||
<button>2</button>
|
||||
<button>3</button>
|
||||
</div>
|
||||
<button id="next-btn">
|
||||
下一<E4B88B><E4B880>? <i class="fas fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-footer bg-white border-0 py-3">
|
||||
<nav>
|
||||
<ul class="pagination justify-content-center mb-0" id="pagination">
|
||||
<!-- Pagination rendered by JS -->
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add/Edit User Modal -->
|
||||
<div class="modal fade" id="userModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="userModalTitle">新增用户</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="userForm">
|
||||
<input type="hidden" name="isEdit" id="isEdit" value="false">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">用户ID</label>
|
||||
<input type="text" class="form-control" name="id" id="userId" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">姓名</label>
|
||||
<input type="text" class="form-control" name="name" id="userNameInput" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">角色</label>
|
||||
<select class="form-select" name="role" id="userRole" required>
|
||||
<option value="student">学生</option>
|
||||
<option value="teacher">教师</option>
|
||||
<option value="admin">管理员</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">密码</label>
|
||||
<input type="password" class="form-control" name="password" id="userPassword" placeholder="默认密码同ID">
|
||||
<div class="form-text">新增时默认密码与ID相同,编辑时留空不修改</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">班级/部门</label>
|
||||
<input type="text" class="form-control" name="class" id="userClass" placeholder="学生必填班级">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" id="saveUserBtn">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<p>© 2023 XX学校成绩管理系统. 版权所<E69D83><E68980>?</p>
|
||||
<p>技术支<EFBFBD><EFBFBD>? 信息技术中<E69CAF><E4B8AD>?| 联系电话: 010-12345678</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- JavaScript -->
|
||||
<script>
|
||||
// 模拟用户数据
|
||||
const mockUsers = [
|
||||
{
|
||||
id: 'admin001',
|
||||
name: '张管理员',
|
||||
role: 'admin',
|
||||
className: '',
|
||||
phone: '13800138001',
|
||||
email: 'admin@xxschool.edu.cn',
|
||||
registerTime: '2023-01-15'
|
||||
},
|
||||
{
|
||||
id: 'teacher001',
|
||||
name: '李老师',
|
||||
role: 'teacher',
|
||||
className: '计算机科学与技<E4B88E><E68A80>?<3F><>?,
|
||||
phone: '13800138002',
|
||||
email: 'li.teacher@xxschool.edu.cn',
|
||||
registerTime: '2023-02-10'
|
||||
},
|
||||
{
|
||||
id: 'teacher002',
|
||||
name: '王老师',
|
||||
role: 'teacher',
|
||||
className: '软件工程1<EFBFBD><EFBFBD>?,
|
||||
phone: '13800138003',
|
||||
email: 'wang.teacher@xxschool.edu.cn',
|
||||
registerTime: '2023-02-12'
|
||||
},
|
||||
{
|
||||
id: 'student001',
|
||||
name: '张三',
|
||||
role: 'student',
|
||||
className: '计算机科学与技<E4B88E><E68A80>?<3F><>?,
|
||||
phone: '13800138004',
|
||||
email: 'zhangsan@xxschool.edu.cn',
|
||||
registerTime: '2023-03-01'
|
||||
},
|
||||
{
|
||||
id: 'student002',
|
||||
name: '李四',
|
||||
role: 'student',
|
||||
className: '计算机科学与技<EFBFBD><EFBFBD>?<EFBFBD><EFBFBD>?,
|
||||
phone: '13800138005',
|
||||
email: 'lisi@xxschool.edu.cn',
|
||||
registerTime: '2023-03-01'
|
||||
},
|
||||
{
|
||||
id: 'student003',
|
||||
name: '王五',
|
||||
role: 'student',
|
||||
className: '软件工程1<E7A88B><31>?,
|
||||
phone: '13800138006',
|
||||
email: 'wangwu@xxschool.edu.cn',
|
||||
registerTime: '2023-03-02'
|
||||
},
|
||||
{
|
||||
id: 'student004',
|
||||
name: '赵六',
|
||||
role: 'student',
|
||||
className: '软件工程1<EFBFBD><EFBFBD>?,
|
||||
phone: '13800138007',
|
||||
email: 'zhaoliu@xxschool.edu.cn',
|
||||
registerTime: '2023-03-02'
|
||||
},
|
||||
{
|
||||
id: 'student005',
|
||||
name: '钱七',
|
||||
role: 'student',
|
||||
className: '网络工程1<E7A88B><31>?,
|
||||
phone: '13800138008',
|
||||
email: 'qianqi@xxschool.edu.cn',
|
||||
registerTime: '2023-03-03'
|
||||
},
|
||||
{
|
||||
id: 'student006',
|
||||
name: '孙八',
|
||||
role: 'student',
|
||||
className: '网络工程2<EFBFBD><EFBFBD>?,
|
||||
phone: '13800138009',
|
||||
email: 'sunba@xxschool.edu.cn',
|
||||
registerTime: '2023-03-03'
|
||||
},
|
||||
{
|
||||
id: 'student007',
|
||||
name: '周九',
|
||||
role: 'student',
|
||||
className: '计算机科学与技<E4B88E><E68A80>?<3F><>?,
|
||||
phone: '13800138010',
|
||||
email: 'zhoujiu@xxschool.edu.cn',
|
||||
registerTime: '2023-03-04'
|
||||
}
|
||||
];
|
||||
|
||||
// 当前显示的用户数<E688B7><E695B0>? let currentUsers = [...mockUsers];
|
||||
let currentPage = 1;
|
||||
const usersPerPage = 5;
|
||||
|
||||
// DOM元素
|
||||
const userTableBody = document.getElementById('user-table-body');
|
||||
const pagination = document.getElementById('pagination');
|
||||
const prevBtn = document.getElementById('prev-btn');
|
||||
const nextBtn = document.getElementById('next-btn');
|
||||
const pageNumbers = document.getElementById('page-numbers');
|
||||
const searchBtn = document.getElementById('search-btn');
|
||||
const resetBtn = document.getElementById('reset-btn');
|
||||
const addBtn = document.getElementById('add-btn');
|
||||
const exportBtn = document.getElementById('export-btn');
|
||||
const userIdInput = document.getElementById('user-id');
|
||||
const userNameInput = document.getElementById('user-name');
|
||||
const roleSelect = document.getElementById('role-select');
|
||||
const classSelect = document.getElementById('class-select');
|
||||
|
||||
// 初始<E5889D><E5A78B>? document.addEventListener('DOMContentLoaded', function() {
|
||||
renderUserTable();
|
||||
setupEventListeners();
|
||||
updatePagination();
|
||||
});
|
||||
|
||||
// 设置事件监听<E79B91><E590AC>? function setupEventListeners() {
|
||||
searchBtn.addEventListener('click', handleSearch);
|
||||
resetBtn.addEventListener('click', handleReset);
|
||||
addBtn.addEventListener('click', handleAddUser);
|
||||
exportBtn.addEventListener('click', handleExport);
|
||||
prevBtn.addEventListener('click', goToPrevPage);
|
||||
nextBtn.addEventListener('click', goToNextPage);
|
||||
}
|
||||
|
||||
// 渲染用户表格
|
||||
function renderUserTable() {
|
||||
if (currentUsers.length === 0) {
|
||||
userTableBody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="8" class="no-results">
|
||||
<i class="fas fa-user-slash"></i>
|
||||
<h3>没有找到用户</h3>
|
||||
<p>请尝试其他筛选条件或添加新用<E696B0><E794A8>?/p>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算当前页的用户
|
||||
const startIndex = (currentPage - 1) * usersPerPage;
|
||||
const endIndex = startIndex + usersPerPage;
|
||||
const pageUsers = currentUsers.slice(startIndex, endIndex);
|
||||
|
||||
let tableHTML = '';
|
||||
pageUsers.forEach(user => {
|
||||
// 角色徽章
|
||||
let roleBadge = '';
|
||||
if (user.role === 'admin') {
|
||||
roleBadge = '<span class="role-badge role-admin">管理<EFBFBD><EFBFBD>?/span>';
|
||||
} else if (user.role === 'teacher') {
|
||||
roleBadge = '<span class="role-badge role-teacher">教师</span>';
|
||||
} else {
|
||||
roleBadge = '<span class="role-badge role-student">学生</span>';
|
||||
}
|
||||
|
||||
tableHTML += `
|
||||
<tr>
|
||||
<td>${user.id}</td>
|
||||
<td>${user.name}</td>
|
||||
<td>${roleBadge}</td>
|
||||
<td>${user.className || '<27><>?}</td>
|
||||
<td>${user.phone}</td>
|
||||
<td>${user.email}</td>
|
||||
<td>${user.registerTime}</td>
|
||||
<td>
|
||||
<div class="action-buttons-cell">
|
||||
<button class="btn-edit" onclick="editUser('${user.id}')">
|
||||
<i class="fas fa-edit"></i>
|
||||
编辑
|
||||
</button>
|
||||
<button class="btn-delete" onclick="deleteUser('${user.id}')">
|
||||
<i class="fas fa-trash"></i>
|
||||
删除
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
userTableBody.innerHTML = tableHTML;
|
||||
}
|
||||
|
||||
// 更新分页
|
||||
function updatePagination() {
|
||||
const totalPages = Math.ceil(currentUsers.length / usersPerPage);
|
||||
|
||||
// 更新按钮状<E992AE><E78AB6>? prevBtn.disabled = currentPage === 1;
|
||||
nextBtn.disabled = currentPage === totalPages || totalPages === 0;
|
||||
|
||||
// 更新页码按钮
|
||||
let pageButtonsHTML = '';
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
if (i <= 5) { // 最多显<E5A49A><E698BE>?个页<E4B8AA><E9A1B5>? pageButtonsHTML += `<button class="${i === currentPage ? 'active' : ''}" onclick="goToPage(${i})">${i}</button>`;
|
||||
}
|
||||
}
|
||||
pageNumbers.innerHTML = pageButtonsHTML;
|
||||
}
|
||||
|
||||
// 处理搜索
|
||||
function handleSearch() {
|
||||
const userId = userIdInput.value.trim();
|
||||
const userName = userNameInput.value.trim();
|
||||
const role = roleSelect.value;
|
||||
const className = classSelect.value;
|
||||
|
||||
currentUsers = mockUsers.filter(user => {
|
||||
// 用户ID筛<44><E7AD9B>? if (userId && !user.id.toLowerCase().includes(userId.toLowerCase())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 姓名筛<E5908D><E7AD9B>? if (userName && !user.name.toLowerCase().includes(userName.toLowerCase())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 角色筛<E889B2><E7AD9B>? if (role && user.role !== role) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 班级筛<E7BAA7><E7AD9B>? if (className && user.className !== className) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
currentPage = 1;
|
||||
renderUserTable();
|
||||
updatePagination();
|
||||
}
|
||||
|
||||
// 处理重置
|
||||
function handleReset() {
|
||||
userIdInput.value = '';
|
||||
userNameInput.value = '';
|
||||
roleSelect.value = '';
|
||||
classSelect.value = '';
|
||||
|
||||
currentUsers = [...mockUsers];
|
||||
currentPage = 1;
|
||||
renderUserTable();
|
||||
updatePagination();
|
||||
}
|
||||
|
||||
// 处理添加用户
|
||||
function handleAddUser() {
|
||||
alert('添加用户功能将在后端API完成后实<E5908E><E5AE9E>?);
|
||||
// 这里可以打开一个模态框来添加新用户
|
||||
}
|
||||
|
||||
// 处理导出
|
||||
function handleExport() {
|
||||
alert('导出功能将在后端API完成后实<EFBFBD><EFBFBD>?);
|
||||
// 这里可以导出为Excel或CSV格式
|
||||
}
|
||||
|
||||
// 编辑用户
|
||||
function editUser(userId) {
|
||||
alert(`编辑用户 ${userId} - 功能将在后端API完成后实现`);
|
||||
// 这里可以打开一个模态框来编辑用户信<E688B7><E4BFA1>? }
|
||||
|
||||
// 删除用户
|
||||
function deleteUser(userId) {
|
||||
if (confirm(`确定要删除用<EFBFBD><EFBFBD>?${userId} 吗?此操作不可撤销。`)) {
|
||||
// 从模拟数据中删除
|
||||
const index = mockUsers.findIndex(user => user.id === userId);
|
||||
if (index !== -1) {
|
||||
mockUsers.splice(index, 1);
|
||||
// 更新当前显示的数<E79A84><E695B0>? handleSearch();
|
||||
alert('用户删除成功');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 分页函数
|
||||
function goToPage(page) {
|
||||
currentPage = page;
|
||||
renderUserTable();
|
||||
updatePagination();
|
||||
}
|
||||
|
||||
function goToPrevPage() {
|
||||
if (currentPage > 1) {
|
||||
currentPage--;
|
||||
renderUserTable();
|
||||
updatePagination();
|
||||
}
|
||||
}
|
||||
|
||||
function goToNextPage() {
|
||||
const totalPages = Math.ceil(currentUsers.length / usersPerPage);
|
||||
if (currentPage < totalPages) {
|
||||
currentPage++;
|
||||
renderUserTable();
|
||||
updatePagination();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!-- Bootstrap 5 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/public/js/admin.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -253,54 +253,65 @@
|
||||
</div>
|
||||
|
||||
<div class="col-md-8 mb-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-user-edit me-2 text-primary"></i> 基本信息
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0 fw-bold"><i class="fas fa-user-edit me-2 text-primary"></i>基本信息</h5>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<form>
|
||||
<form id="profileForm">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">学号</label>
|
||||
<input type="text" class="form-control" id="profileId" readonly disabled>
|
||||
<input type="text" class="form-control bg-light" id="profileId" readonly disabled>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">姓名</label>
|
||||
<input type="text" class="form-control" id="profileNameInput" readonly>
|
||||
<input type="text" class="form-control" id="profileNameInput" placeholder="请输入姓名">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">班级</label>
|
||||
<input type="text" class="form-control" id="profileClass" readonly disabled>
|
||||
<input type="text" class="form-control" id="profileClass" placeholder="请输入班级">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">角色</label>
|
||||
<input type="text" class="form-control" value="学生" readonly disabled>
|
||||
<input type="text" class="form-control bg-light" value="学生" readonly disabled>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
<h6 class="mb-3 fw-bold text-secondary">安全设置</h6>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">原密码</label>
|
||||
<input type="password" class="form-control" id="oldPassword" placeholder="请输入原密码">
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">新密码</label>
|
||||
<input type="password" class="form-control" id="newPassword" placeholder="请输入新密码">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">确认新密码</label>
|
||||
<input type="password" class="form-control" id="confirmPassword" placeholder="再次输入新密码">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-end">
|
||||
<button type="button" class="btn btn-primary px-4" id="savePasswordBtn">
|
||||
<i class="fas fa-save me-1"></i> 保存更改
|
||||
<button type="button" id="saveProfileBtn" class="btn btn-outline-primary">
|
||||
<i class="fas fa-save me-1"></i> 保存基本信息
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0 fw-bold"><i class="fas fa-key me-2 text-primary"></i>修改密码</h5>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<form id="passwordForm">
|
||||
<div class="mb-3">
|
||||
<label for="oldPassword" class="form-label">原密码</label>
|
||||
<input type="password" class="form-control" id="oldPassword" required placeholder="请输入原密码">
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<label for="newPassword" class="form-label">新密码</label>
|
||||
<input type="password" class="form-control" id="newPassword" required placeholder="请输入新密码">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="confirmPassword" class="form-label">确认新密码</label>
|
||||
<input type="password" class="form-control" id="confirmPassword" required placeholder="请再次输入新密码">
|
||||
</div>
|
||||
</div>
|
||||
<div id="passwordError" class="text-danger mb-3" style="display: none;"></div>
|
||||
<div class="text-end">
|
||||
<button type="submit" class="btn btn-primary px-4">
|
||||
<i class="fas fa-save me-1"></i> 修改密码
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -342,26 +353,58 @@
|
||||
updateTime();
|
||||
setInterval(updateTime, 1000);
|
||||
|
||||
// 保存基本信息逻辑
|
||||
const saveProfileBtn = document.getElementById('saveProfileBtn');
|
||||
if (saveProfileBtn) {
|
||||
saveProfileBtn.addEventListener('click', async () => {
|
||||
const name = document.getElementById('profileNameInput').value;
|
||||
const className = document.getElementById('profileClass').value;
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/auth/update-profile', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name, class: className })
|
||||
});
|
||||
const result = await res.json();
|
||||
if (result.success) {
|
||||
alert('资料更新成功');
|
||||
// 更新侧边栏和顶栏
|
||||
document.getElementById('userName').textContent = name;
|
||||
document.getElementById('studentName').textContent = name;
|
||||
document.getElementById('profileName').textContent = name;
|
||||
} else {
|
||||
alert(result.message || '更新失败');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Update profile failed', e);
|
||||
alert('系统错误');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 修改密码逻辑
|
||||
const savePasswordBtn = document.getElementById('savePasswordBtn');
|
||||
if (savePasswordBtn) {
|
||||
savePasswordBtn.addEventListener('click', async () => {
|
||||
const passwordForm = document.getElementById('passwordForm');
|
||||
if (passwordForm) {
|
||||
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 (!oldPassword || !newPassword || !confirmPassword) {
|
||||
alert('请填写所有密码字段');
|
||||
return;
|
||||
}
|
||||
const errorEl = document.getElementById('passwordError');
|
||||
|
||||
errorEl.style.display = 'none';
|
||||
|
||||
if (newPassword !== confirmPassword) {
|
||||
alert('两次输入的新密码不一致');
|
||||
errorEl.textContent = '两次输入的新密码不一致';
|
||||
errorEl.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword.length < 6) {
|
||||
alert('新密码长度至少为 6 位');
|
||||
errorEl.textContent = '新密码长度至少为 6 位';
|
||||
errorEl.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -377,17 +420,16 @@
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
alert('密码修改成功,请重新登录');
|
||||
// 登出并跳转到登录页
|
||||
fetch('/api/auth/logout', { method: 'POST' })
|
||||
.then(() => {
|
||||
window.location.href = '/login';
|
||||
});
|
||||
await fetch('/api/auth/logout', { method: 'POST' });
|
||||
window.location.href = '/login';
|
||||
} else {
|
||||
alert(result.message || '修改失败');
|
||||
errorEl.textContent = result.message || '修改失败';
|
||||
errorEl.style.display = 'block';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Update password error:', error);
|
||||
alert('网络错误,请稍后再试');
|
||||
errorEl.textContent = '网络错误,请稍后再试';
|
||||
errorEl.style.display = 'block';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -201,19 +201,25 @@
|
||||
|
||||
<nav class="nav-menu">
|
||||
<div class="nav-item">
|
||||
<a href="#" class="nav-link active">
|
||||
<a href="/teacher/dashboard" class="nav-link active">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>教师仪表板</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="#" class="nav-link">
|
||||
<i class="fas fa-list-alt"></i>
|
||||
<span>课程列表</span>
|
||||
<a href="/teacher/grade_entry" class="nav-link">
|
||||
<i class="fas fa-edit"></i>
|
||||
<span>成绩录入</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="#" class="nav-link">
|
||||
<a href="/teacher/grade_management" class="nav-link">
|
||||
<i class="fas fa-tasks"></i>
|
||||
<span>成绩管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/teacher/profile" class="nav-link">
|
||||
<i class="fas fa-user-edit"></i>
|
||||
<span>个人资料</span>
|
||||
</a>
|
||||
@@ -248,7 +254,7 @@
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-xl-4 col-md-6">
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card stat-card h-100 p-3">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
@@ -261,7 +267,20 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-4 col-md-6">
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card stat-card h-100 p-3">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<p class="text-secondary small mb-1 fw-bold">负责班级</p>
|
||||
<h3 class="mb-0 fw-bold" id="classCount">0</h3>
|
||||
</div>
|
||||
<div class="stat-icon bg-info bg-opacity-10 text-info">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card stat-card h-100 p-3">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
@@ -274,7 +293,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-4 col-md-6">
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card stat-card h-100 p-3">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
@@ -289,10 +308,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 负责班级 -->
|
||||
<div class="mb-4" id="managedClassesSection" style="display: none;">
|
||||
<h5 class="mb-3 fw-bold"><i class="fas fa-users-cog me-2 text-info"></i>我负责的班级</h5>
|
||||
<div class="row" id="managedClassList">
|
||||
<!-- 班级卡片 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 课程列表 -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h5 class="mb-0 fw-bold"><i class="fas fa-chalkboard me-2 text-primary"></i>我的负责课程</h5>
|
||||
<button class="btn btn-sm btn-primary px-3">
|
||||
<button id="addCourseBtn" class="btn btn-sm btn-primary px-3">
|
||||
<i class="fas fa-plus me-1"></i> 新增课程
|
||||
</button>
|
||||
</div>
|
||||
@@ -306,6 +333,91 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Course Modal -->
|
||||
<div class="modal fade" id="addCourseModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">新增课程</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="addCourseForm">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">课程名称</label>
|
||||
<input type="text" class="form-control" name="course_name" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">课程代码</label>
|
||||
<input type="text" class="form-control" name="course_code" required placeholder="如: CS101">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">学分</label>
|
||||
<input type="number" step="0.5" class="form-control" name="credit" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">授课班级</label>
|
||||
<select class="form-select" name="class_id" id="classSelect" required>
|
||||
<option value="">加载中...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">学期</label>
|
||||
<input type="text" class="form-control" name="semester" placeholder="如: 2023-2024 秋季" required>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" id="saveCourseBtn">创建课程</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Course Modal -->
|
||||
<div class="modal fade" id="editCourseModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">编辑课程</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="editCourseForm">
|
||||
<input type="hidden" name="id" id="editCourseId">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">课程名称</label>
|
||||
<input type="text" class="form-control" name="course_name" id="editCourseName" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">课程代码</label>
|
||||
<input type="text" class="form-control" name="course_code" id="editCourseCode" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">学分</label>
|
||||
<input type="number" step="0.5" class="form-control" name="credit" id="editCourseCredit" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">授课班级</label>
|
||||
<select class="form-select" name="class_id" id="editClassSelect" required>
|
||||
<option value="">加载中...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">学期</label>
|
||||
<input type="text" class="form-control" name="semester" id="editSemester" required>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="button" class="btn btn-primary" id="updateCourseBtn">保存修改</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap 5 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/public/js/auth.js"></script>
|
||||
|
||||
@@ -3,445 +3,242 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>成绩录入 - XX学校成绩管理系统</title>
|
||||
<link rel="stylesheet" href="/public/css/style.css">
|
||||
<title>成绩录入 - 教师端</title>
|
||||
<!-- Bootstrap 5 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--sidebar-width: 260px;
|
||||
--primary-color: #1cc88a;
|
||||
--secondary-color: #858796;
|
||||
--light-bg: #f8f9fc;
|
||||
--teacher-gradient: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
background-color: var(--light-bg);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 侧边栏样式 */
|
||||
.sidebar {
|
||||
width: var(--sidebar-width);
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: var(--teacher-gradient);
|
||||
color: white;
|
||||
z-index: 1000;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 2rem 1.5rem;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
padding: 2rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.user-info h6 { margin-bottom: 0.25rem; font-weight: 600; }
|
||||
.user-info p { font-size: 0.85rem; opacity: 0.8; margin-bottom: 0; }
|
||||
|
||||
.nav-menu { padding: 1rem 0; }
|
||||
.nav-item { padding: 0.25rem 1rem; }
|
||||
|
||||
.nav-link {
|
||||
color: rgba(255,255,255,0.8);
|
||||
padding: 0.8rem 1.25rem;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link.active {
|
||||
color: white;
|
||||
background: rgba(255,255,255,0.15);
|
||||
}
|
||||
|
||||
.nav-link i { width: 20px; text-align: center; }
|
||||
|
||||
/* 主内容区 */
|
||||
.main-content {
|
||||
margin-left: var(--sidebar-width);
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.top-navbar {
|
||||
background: white;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.1);
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-heading h4 { margin-bottom: 0; font-weight: 700; color: #333; }
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.sidebar { left: -var(--sidebar-width); }
|
||||
.main-content { margin-left: 0; }
|
||||
.sidebar.active { left: 0; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 导航<EFBFBD><EFBFBD>?-->
|
||||
<nav class="navbar">
|
||||
<div class="navbar-brand">
|
||||
<a href="/"><i class="fas fa-graduation-cap"></i> XX学校成绩管理系统</a>
|
||||
<!-- 侧边栏 -->
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<a href="#" class="sidebar-brand">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
<span>成绩管理系统</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="navbar-menu">
|
||||
<a href="/teacher/dashboard" class="navbar-item"><i class="fas fa-tachometer-alt"></i> 教师仪表<E4BBAA><E8A1A8>?/a>
|
||||
<a href="/teacher/grade_entry" class="navbar-item active"><i class="fas fa-edit"></i> 成绩录入</a>
|
||||
<a href="/teacher/grade_management" class="navbar-item"><i class="fas fa-list"></i> 成绩管理</a>
|
||||
<div class="user-info">
|
||||
<span class="user-name">李老师</span>
|
||||
<span class="user-role">教师</span>
|
||||
<a href="/login" class="btn btn-outline btn-small"><i class="fas fa-sign-out-alt"></i> 退<><E98080>?/a>
|
||||
|
||||
<div class="user-profile">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user-tie"></i>
|
||||
</div>
|
||||
<div class="user-info text-white">
|
||||
<h6 id="teacherName">加载中...</h6>
|
||||
<p>教师 | <span id="teacherId">...</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- 面包屑导<E5B191><E5AFBC>?-->
|
||||
<div class="container">
|
||||
<div class="breadcrumb">
|
||||
<a href="/">主页</a>
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
<a href="/teacher/dashboard">教师仪表<EFBFBD><EFBFBD>?/a>
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
<span>成绩录入</span>
|
||||
<nav class="nav-menu">
|
||||
<div class="nav-item">
|
||||
<a href="/teacher/dashboard" class="nav-link">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>教师仪表板</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/teacher/grade_entry" class="nav-link active">
|
||||
<i class="fas fa-edit"></i>
|
||||
<span>成绩录入</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/teacher/grade_management" class="nav-link">
|
||||
<i class="fas fa-tasks"></i>
|
||||
<span>成绩管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/teacher/profile" class="nav-link">
|
||||
<i class="fas fa-user-edit"></i>
|
||||
<span>个人资料</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item mt-4">
|
||||
<a href="javascript:void(0)" id="logoutBtn" class="nav-link text-warning">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
<span>退出登录</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- 主内容 -->
|
||||
<div class="main-content">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="top-navbar">
|
||||
<div class="page-heading">
|
||||
<h4>成绩录入</h4>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="text-end d-none d-md-block">
|
||||
<div class="small text-muted" id="currentTime"></div>
|
||||
<div class="fw-bold" id="userName">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 录入卡片 -->
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body p-4">
|
||||
<div class="row mb-4 align-items-end">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-muted small fw-bold">选择课程</label>
|
||||
<select id="courseSelect" class="form-select">
|
||||
<option value="">加载课程中...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-8 text-end">
|
||||
<div class="alert alert-info d-inline-block mb-0 py-2 px-3 small">
|
||||
<i class="fas fa-info-circle me-1"></i> 请先选择课程,系统将自动加载该课程的学生名单
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>学号</th>
|
||||
<th>姓名</th>
|
||||
<th style="width: 120px;">平时成绩</th>
|
||||
<th style="width: 120px;">期中成绩</th>
|
||||
<th style="width: 120px;">期末成绩</th>
|
||||
<th>总评</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="studentTableBody">
|
||||
<tr>
|
||||
<td colspan="7" class="text-center py-5 text-muted">
|
||||
<i class="fas fa-arrow-up mb-2"></i><br>
|
||||
请选择课程以加载学生列表
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<div class="grade-entry-container">
|
||||
<div class="entry-header">
|
||||
<h1><i class="fas fa-edit"></i> 成绩录入</h1>
|
||||
<p>请选择班级和课程,然后为每个学生录入成<EFBFBD><EFBFBD>?/p>
|
||||
</div>
|
||||
|
||||
<div class="entry-form">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="class-select"><i class="fas fa-users"></i> 选择班级</label>
|
||||
<select id="class-select" class="form-control">
|
||||
<option value="">请选择班级</option>
|
||||
<option value="class1">计算机科学与技<EFBFBD><EFBFBD>?021<32><31>?<3F><>?/option>
|
||||
<option value="class2">计算机科学与技<EFBFBD><EFBFBD>?021<32><31>?<3F><>?/option>
|
||||
<option value="class3">软件工程2021<EFBFBD><EFBFBD>?<3F><>?/option>
|
||||
<option value="class4">软件工程2021<EFBFBD><EFBFBD>?<3F><>?/option>
|
||||
<option value="class5">网络工程2021<EFBFBD><EFBFBD>?<3F><>?/option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="course-name"><i class="fas fa-book"></i> 课程名称</label>
|
||||
<input type="text" id="course-name" placeholder="请输入课程名称,如:数据结构、操作系统等">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="exam-date"><i class="fas fa-calendar-alt"></i> 考试日期</label>
|
||||
<input type="date" id="exam-date">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label><i class="fas fa-info-circle"></i> 备注说明(可选)</label>
|
||||
<textarea id="remarks" rows="3" placeholder="可输入本次考试的说明信息,如:期中考试、期末考试<E88083><E8AF95>?></textarea>
|
||||
</div>
|
||||
|
||||
<div class="students-table-container">
|
||||
<h3><i class="fas fa-user-graduate"></i> 学生成绩录入</h3>
|
||||
<p class="text-muted">选择班级后,系统将自动加载该班级的学生列<EFBFBD><EFBFBD>?/p>
|
||||
|
||||
<div id="students-loading" class="loading" style="display: none;">
|
||||
<i class="fas fa-spinner"></i>
|
||||
<p>正在加载学生列表...</p>
|
||||
</div>
|
||||
|
||||
<div id="no-students" class="no-students" style="display: none;">
|
||||
<i class="fas fa-user-slash"></i>
|
||||
<p>该班级暂无学生数据,请先添加学生信息</p>
|
||||
</div>
|
||||
|
||||
<table id="students-table" class="students-table" style="display: none;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>学号</th>
|
||||
<th>姓名</th>
|
||||
<th>平时成绩</th>
|
||||
<th>期中成绩</th>
|
||||
<th>期末成绩</th>
|
||||
<th>总评成绩</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="students-tbody">
|
||||
<!-- 学生数据将通过JavaScript动态加<E68081><E58AA0>?-->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button id="cancel-btn" class="btn btn-secondary btn-large">
|
||||
<i class="fas fa-times"></i> 取消
|
||||
</button>
|
||||
<button id="submit-btn" class="btn btn-primary btn-large" disabled>
|
||||
<i class="fas fa-check"></i> 提交成绩
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3><i class="fas fa-lightbulb"></i> 使用说明</h3>
|
||||
<ul class="text-muted">
|
||||
<li>首先选择要录入成绩的班级和课<EFBFBD><EFBFBD>?/li>
|
||||
<li>系统会自动加载该班级的学生列<EFBFBD><EFBFBD>?/li>
|
||||
<li>为每个学生输入平时成绩、期中成绩和期末成绩</li>
|
||||
<li>总评成绩将根据权重自动计算(默认权重:平<EFBFBD><EFBFBD>?0%,期<EFBC8C><E69C9F>?0%,期<EFBC8C><E69C9F>?0%<25><>?/li>
|
||||
<li>确认无误后点击“提交成绩”按钮保存数<EFBFBD><EFBFBD>?/li>
|
||||
<li>提交后可以在“成绩管理”页面查看和修改已录入的成绩</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- JavaScript -->
|
||||
<script>
|
||||
// 模拟学生数据
|
||||
const mockStudents = [
|
||||
{ id: '2021001', name: '张三', usual: '', midterm: '', final: '', total: '' },
|
||||
{ id: '2021002', name: '李四', usual: '', midterm: '', final: '', total: '' },
|
||||
{ id: '2021003', name: '王五', usual: '', midterm: '', final: '', total: '' },
|
||||
{ id: '2021004', name: '赵六', usual: '', midterm: '', final: '', total: '' },
|
||||
{ id: '2021005', name: '钱七', usual: '', midterm: '', final: '', total: '' },
|
||||
{ id: '2021006', name: '孙八', usual: '', midterm: '', final: '', total: '' },
|
||||
{ id: '2021007', name: '周九', usual: '', midterm: '', final: '', total: '' },
|
||||
{ id: '2021008', name: '吴十', usual: '', midterm: '', final: '', total: '' }
|
||||
];
|
||||
|
||||
// DOM元素
|
||||
const classSelect = document.getElementById('class-select');
|
||||
const courseNameInput = document.getElementById('course-name');
|
||||
const examDateInput = document.getElementById('exam-date');
|
||||
const remarksInput = document.getElementById('remarks');
|
||||
const studentsLoading = document.getElementById('students-loading');
|
||||
const noStudents = document.getElementById('no-students');
|
||||
const studentsTable = document.getElementById('students-table');
|
||||
const studentsTbody = document.getElementById('students-tbody');
|
||||
const cancelBtn = document.getElementById('cancel-btn');
|
||||
const submitBtn = document.getElementById('submit-btn');
|
||||
|
||||
// 设置默认考试日期为今<E4B8BA><E4BB8A>? const today = new Date().toISOString().split('T')[0];
|
||||
examDateInput.value = today;
|
||||
|
||||
// 班级选择变化事件
|
||||
classSelect.addEventListener('change', function() {
|
||||
const selectedClass = this.value;
|
||||
|
||||
if (!selectedClass) {
|
||||
hideStudentsTable();
|
||||
submitBtn.disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示加载状<E8BDBD><E78AB6>? showLoading();
|
||||
|
||||
// 模拟API调用延迟
|
||||
setTimeout(() => {
|
||||
loadStudentsForClass(selectedClass);
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
// 加载学生数据
|
||||
function loadStudentsForClass(className) {
|
||||
// 模拟API响应
|
||||
if (mockStudents.length > 0) {
|
||||
renderStudentsTable(mockStudents);
|
||||
} else {
|
||||
showNoStudents();
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染学生表格
|
||||
function renderStudentsTable(students) {
|
||||
hideLoading();
|
||||
|
||||
// 清空表格
|
||||
studentsTbody.innerHTML = '';
|
||||
|
||||
// 添加学生<E5ADA6><E7949F>? students.forEach((student, index) => {
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>${student.id}</td>
|
||||
<td>${student.name}</td>
|
||||
<td>
|
||||
<input type="number" class="grade-input usual-grade"
|
||||
data-index="${index}" min="0" max="100"
|
||||
placeholder="0-100" value="${student.usual}">
|
||||
</td>
|
||||
<td>
|
||||
<input type="number" class="grade-input midterm-grade"
|
||||
data-index="${index}" min="0" max="100"
|
||||
placeholder="0-100" value="${student.midterm}">
|
||||
</td>
|
||||
<td>
|
||||
<input type="number" class="grade-input final-grade"
|
||||
data-index="${index}" min="0" max="100"
|
||||
placeholder="0-100" value="${student.final}">
|
||||
</td>
|
||||
<td>
|
||||
<span class="total-grade" data-index="${index}">${student.total || ''}</span>
|
||||
</td>
|
||||
`;
|
||||
|
||||
studentsTbody.appendChild(row);
|
||||
});
|
||||
|
||||
// 显示表格
|
||||
studentsTable.style.display = 'table';
|
||||
noStudents.style.display = 'none';
|
||||
|
||||
// 启用提交按钮
|
||||
submitBtn.disabled = false;
|
||||
|
||||
// 添加成绩输入事件监听
|
||||
addGradeInputListeners();
|
||||
}
|
||||
|
||||
// 显示加载状<E8BDBD><E78AB6>? function showLoading() {
|
||||
studentsLoading.style.display = 'block';
|
||||
studentsTable.style.display = 'none';
|
||||
noStudents.style.display = 'none';
|
||||
}
|
||||
|
||||
// 隐藏加载状<E8BDBD><E78AB6>? function hideLoading() {
|
||||
studentsLoading.style.display = 'none';
|
||||
}
|
||||
|
||||
// 显示无学生数<E7949F><E695B0>? function showNoStudents() {
|
||||
hideLoading();
|
||||
studentsTable.style.display = 'none';
|
||||
noStudents.style.display = 'block';
|
||||
}
|
||||
|
||||
// 隐藏学生表格
|
||||
function hideStudentsTable() {
|
||||
studentsTable.style.display = 'none';
|
||||
noStudents.style.display = 'none';
|
||||
}
|
||||
|
||||
// 添加成绩输入事件监听
|
||||
function addGradeInputListeners() {
|
||||
const usualGrades = document.querySelectorAll('.usual-grade');
|
||||
const midtermGrades = document.querySelectorAll('.midterm-grade');
|
||||
const finalGrades = document.querySelectorAll('.final-grade');
|
||||
|
||||
usualGrades.forEach(input => {
|
||||
input.addEventListener('input', calculateTotalGrade);
|
||||
});
|
||||
|
||||
midtermGrades.forEach(input => {
|
||||
input.addEventListener('input', calculateTotalGrade);
|
||||
});
|
||||
|
||||
finalGrades.forEach(input => {
|
||||
input.addEventListener('input', calculateTotalGrade);
|
||||
});
|
||||
}
|
||||
|
||||
// 计算总评成绩
|
||||
function calculateTotalGrade(event) {
|
||||
const input = event.target;
|
||||
const index = input.getAttribute('data-index');
|
||||
|
||||
// 获取三个成绩
|
||||
const usualInput = document.querySelector(`.usual-grade[data-index="${index}"]`);
|
||||
const midtermInput = document.querySelector(`.midterm-grade[data-index="${index}"]`);
|
||||
const finalInput = document.querySelector(`.final-grade[data-index="${index}"]`);
|
||||
const totalSpan = document.querySelector(`.total-grade[data-index="${index}"]`);
|
||||
|
||||
const usual = parseFloat(usualInput.value) || 0;
|
||||
const midterm = parseFloat(midtermInput.value) || 0;
|
||||
const final = parseFloat(finalInput.value) || 0;
|
||||
|
||||
// 计算总评成绩(权重:平时20%,期<EFBC8C><E69C9F>?0%,期<EFBC8C><E69C9F>?0%<25><>? const total = (usual * 0.2) + (midterm * 0.3) + (final * 0.5);
|
||||
|
||||
// 显示总评成绩(四舍五入到整数<E695B4><E695B0>? totalSpan.textContent = Math.round(total);
|
||||
|
||||
// 根据成绩范围设置颜色
|
||||
if (total >= 90) {
|
||||
totalSpan.style.color = '#38a169';
|
||||
totalSpan.style.fontWeight = 'bold';
|
||||
} else if (total >= 60) {
|
||||
totalSpan.style.color = '#d69e2e';
|
||||
totalSpan.style.fontWeight = 'normal';
|
||||
} else {
|
||||
totalSpan.style.color = '#e53e3e';
|
||||
totalSpan.style.fontWeight = 'bold';
|
||||
}
|
||||
}
|
||||
|
||||
// 表单验证
|
||||
function validateForm() {
|
||||
const className = classSelect.value;
|
||||
const courseName = courseNameInput.value.trim();
|
||||
|
||||
if (!className) {
|
||||
alert('请选择班级');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!courseName) {
|
||||
alert('请输入课程名<E7A88B><E5908D>?);
|
||||
courseNameInput.focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否有学生成绩未填<E69CAA><E5A1AB>? const usualGrades = document.querySelectorAll('.usual-grade');
|
||||
const midtermGrades = document.querySelectorAll('.midterm-grade');
|
||||
const finalGrades = document.querySelectorAll('.final-grade');
|
||||
|
||||
for (let i = 0; i < usualGrades.length; i++) {
|
||||
const usual = usualGrades[i].value.trim();
|
||||
const midterm = midtermGrades[i].value.trim();
|
||||
const final = finalGrades[i].value.trim();
|
||||
|
||||
if (!usual || !midterm || !final) {
|
||||
alert(`请填写第${i + 1}行学生的所有成绩`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const usualNum = parseFloat(usual);
|
||||
const midtermNum = parseFloat(midterm);
|
||||
const finalNum = parseFloat(final);
|
||||
|
||||
if (usualNum < 0 || usualNum > 100 ||
|
||||
midtermNum < 0 || midtermNum > 100 ||
|
||||
finalNum < 0 || finalNum > 100) {
|
||||
alert(`<60><>?{i + 1}行学生的成绩必须<E5BF85><E9A1BB>?-100之间`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 收集表单数据
|
||||
function collectFormData() {
|
||||
const className = classSelect.options[classSelect.selectedIndex].text;
|
||||
const courseName = courseNameInput.value.trim();
|
||||
const examDate = examDateInput.value;
|
||||
const remarks = remarksInput.value.trim();
|
||||
|
||||
const grades = [];
|
||||
const rows = studentsTbody.querySelectorAll('tr');
|
||||
|
||||
rows.forEach((row, index) => {
|
||||
const studentId = row.cells[0].textContent;
|
||||
const studentName = row.cells[1].textContent;
|
||||
const usual = row.cells[2].querySelector('input').value;
|
||||
const midterm = row.cells[3].querySelector('input').value;
|
||||
const final = row.cells[4].querySelector('input').value;
|
||||
const total = row.cells[5].querySelector('span').textContent;
|
||||
|
||||
grades.push({
|
||||
studentId,
|
||||
studentName,
|
||||
usual: parseFloat(usual),
|
||||
midterm: parseFloat(midterm),
|
||||
final: parseFloat(final),
|
||||
total: parseFloat(total)
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
className,
|
||||
courseName,
|
||||
examDate,
|
||||
remarks,
|
||||
grades
|
||||
};
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
function submitForm() {
|
||||
if (!validateForm()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = collectFormData();
|
||||
|
||||
// 显示提交中状<E4B8AD><E78AB6>? submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 提交<EFBFBD><EFBFBD>?..';
|
||||
submitBtn.disabled = true;
|
||||
|
||||
// 模拟API调用
|
||||
setTimeout(() => {
|
||||
// 模拟成功响应
|
||||
alert(`成绩提交成功!\n班级<EFBFBD><EFBFBD>?{formData.className}\n课程<EFBFBD><EFBFBD>?{formData.courseName}\n共录<EFBFBD><EFBFBD>?{formData.grades.length}名学生成绩`);
|
||||
|
||||
// 重置按钮状<E992AE><E78AB6>? submitBtn.innerHTML = '<i class="fas fa-check"></i> 提交成绩';
|
||||
submitBtn.disabled = false;
|
||||
|
||||
// 重置表单
|
||||
resetForm();
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
function resetForm() {
|
||||
classSelect.value = '';
|
||||
courseNameInput.value = '';
|
||||
examDateInput.value = today;
|
||||
remarksInput.value = '';
|
||||
|
||||
// 清空学生表格
|
||||
studentsTbody.innerHTML = '';
|
||||
studentsTable.style.display = 'none';
|
||||
noStudents.style.display = 'none';
|
||||
|
||||
// 禁用提交按钮
|
||||
submitBtn.disabled = true;
|
||||
}
|
||||
|
||||
// 取消按钮事件
|
||||
cancelBtn.addEventListener('click', function() {
|
||||
if (confirm('确定要取消吗?所有未保存的更改将会丢失<E4B8A2><E5A4B1>?)) {
|
||||
resetForm();
|
||||
}
|
||||
});
|
||||
|
||||
// 提交按钮事件
|
||||
submitBtn.addEventListener('click', submitForm);
|
||||
|
||||
// 页面加载时初始化
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 检查是否有必要的数<E79A84><E695B0>? if (!classSelect.value) {
|
||||
submitBtn.disabled = true;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<!-- Bootstrap 5 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/public/js/auth.js"></script>
|
||||
<script src="/public/js/teacher.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -3,572 +3,246 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>成绩查询/管理 - 学生成绩管理系统</title>
|
||||
<link rel="stylesheet" href="/public/css/style.css">
|
||||
<title>成绩管理 - 教师端</title>
|
||||
<!-- Bootstrap 5 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--sidebar-width: 260px;
|
||||
--primary-color: #1cc88a;
|
||||
--secondary-color: #858796;
|
||||
--light-bg: #f8f9fc;
|
||||
--teacher-gradient: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
background-color: var(--light-bg);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 侧边栏样式 */
|
||||
.sidebar {
|
||||
width: var(--sidebar-width);
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: var(--teacher-gradient);
|
||||
color: white;
|
||||
z-index: 1000;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 2rem 1.5rem;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
padding: 2rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.user-info h6 { margin-bottom: 0.25rem; font-weight: 600; }
|
||||
.user-info p { font-size: 0.85rem; opacity: 0.8; margin-bottom: 0; }
|
||||
|
||||
.nav-menu { padding: 1rem 0; }
|
||||
.nav-item { padding: 0.25rem 1rem; }
|
||||
|
||||
.nav-link {
|
||||
color: rgba(255,255,255,0.8);
|
||||
padding: 0.8rem 1.25rem;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link.active {
|
||||
color: white;
|
||||
background: rgba(255,255,255,0.15);
|
||||
}
|
||||
|
||||
.nav-link i { width: 20px; text-align: center; }
|
||||
|
||||
/* 主内容区 */
|
||||
.main-content {
|
||||
margin-left: var(--sidebar-width);
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.top-navbar {
|
||||
background: white;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.1);
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-heading h4 { margin-bottom: 0; font-weight: 700; color: #333; }
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.sidebar { left: -var(--sidebar-width); }
|
||||
.main-content { margin-left: 0; }
|
||||
.sidebar.active { left: 0; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 导航<EFBFBD><EFBFBD>?-->
|
||||
<nav class="navbar">
|
||||
<div class="nav-container">
|
||||
<div class="nav-brand">
|
||||
<a href="/">
|
||||
<i class="fas fa-graduation-cap"></i>
|
||||
<span>XX学校成绩管理系统</span>
|
||||
</a>
|
||||
<!-- 侧边栏 -->
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<a href="#" class="sidebar-brand">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
<span>成绩管理系统</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="user-profile">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user-tie"></i>
|
||||
</div>
|
||||
<div class="nav-menu">
|
||||
<div class="user-info text-white">
|
||||
<h6 id="teacherName">加载中...</h6>
|
||||
<p>教师 | <span id="teacherId">...</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-menu">
|
||||
<div class="nav-item">
|
||||
<a href="/teacher/dashboard" class="nav-link">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>教师仪表<EFBFBD><EFBFBD>?/span>
|
||||
<span>教师仪表板</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/teacher/grade_entry" class="nav-link">
|
||||
<i class="fas fa-edit"></i>
|
||||
<span>成绩录入</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/teacher/grade_management" class="nav-link active">
|
||||
<i class="fas fa-search"></i>
|
||||
<i class="fas fa-tasks"></i>
|
||||
<span>成绩管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-user">
|
||||
<div class="user-info">
|
||||
<i class="fas fa-user-circle"></i>
|
||||
<span>张老师</span>
|
||||
</div>
|
||||
<a href="/login" class="btn-logout">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
<span>退<EFBFBD><EFBFBD>?/span>
|
||||
<div class="nav-item">
|
||||
<a href="/teacher/profile" class="nav-link">
|
||||
<i class="fas fa-user-edit"></i>
|
||||
<span>个人资料</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="nav-item mt-4">
|
||||
<a href="javascript:void(0)" id="logoutBtn" class="nav-link text-warning">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
<span>退出登录</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- 主内容区 -->
|
||||
<main class="main-content">
|
||||
<div class="container">
|
||||
<!-- 面包屑导<E5B191><E5AFBC>?-->
|
||||
<div class="breadcrumb">
|
||||
<a href="/">主页</a>
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
<a href="/teacher/dashboard">教师仪表<EFBFBD><EFBFBD>?/a>
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
<span>成绩查询/管理</span>
|
||||
<!-- 主内容 -->
|
||||
<div class="main-content">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="top-navbar">
|
||||
<div class="page-heading">
|
||||
<h4>成绩管理</h4>
|
||||
</div>
|
||||
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">成绩查询/管理</h1>
|
||||
<p class="page-description">查询、修改和删除学生成绩记录</p>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="text-end d-none d-md-block">
|
||||
<div class="small text-muted" id="currentTime"></div>
|
||||
<div class="fw-bold" id="userName">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选区<E98089><E58CBA>?-->
|
||||
<div class="filter-section">
|
||||
<h2 class="filter-title">
|
||||
<i class="fas fa-filter"></i>
|
||||
筛选条<E98089><E69DA1>? </h2>
|
||||
<div class="filter-form">
|
||||
<div class="form-group">
|
||||
<label for="class-select">班级</label>
|
||||
<select id="class-select" class="form-control">
|
||||
<option value="">全部班级</option>
|
||||
<option value="class1">计算机科学与技<EFBFBD><EFBFBD>?<3F><>?/option>
|
||||
<option value="class2">计算机科学与技<EFBFBD><EFBFBD>?<3F><>?/option>
|
||||
<option value="class3">软件工程1<EFBFBD><EFBFBD>?/option>
|
||||
<option value="class4">软件工程2<EFBFBD><EFBFBD>?/option>
|
||||
<option value="class5">网络工程1<EFBFBD><EFBFBD>?/option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="course-select">课程</label>
|
||||
<select id="course-select" class="form-control">
|
||||
</div>
|
||||
|
||||
<!-- 筛选卡片 -->
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-body p-4">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-muted small fw-bold">课程筛选</label>
|
||||
<select id="courseSelectFilter" class="form-select">
|
||||
<option value="">全部课程</option>
|
||||
<option value="course1">数据结构</option>
|
||||
<option value="course2">操作系统</option>
|
||||
<option value="course3">计算机网<EFBFBD><EFBFBD>?/option>
|
||||
<option value="course4">数据库系<EFBFBD><EFBFBD>?/option>
|
||||
<option value="course5">软件工程</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="student-id">学生学号</label>
|
||||
<input type="text" id="student-id" class="form-control" placeholder="输入学生学号">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label text-muted small fw-bold">学生查找</label>
|
||||
<input type="text" id="studentNameFilter" class="form-control" placeholder="输入姓名或学号">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="student-name">学生姓名</label>
|
||||
<input type="text" id="student-name" class="form-control" placeholder="输入学生姓名">
|
||||
</div>
|
||||
<div class="filter-actions">
|
||||
<button id="search-btn" class="btn-search">
|
||||
<i class="fas fa-search"></i>
|
||||
查询
|
||||
</button>
|
||||
<button id="reset-btn" class="btn-reset">
|
||||
<i class="fas fa-redo"></i>
|
||||
重置
|
||||
<div class="col-md-4 d-flex align-items-end">
|
||||
<button id="searchBtn" class="btn btn-primary w-100">
|
||||
<i class="fas fa-search me-2"></i> 查询成绩
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 结果区域 -->
|
||||
<div class="results-section">
|
||||
<div class="results-header">
|
||||
<h2 class="results-title">查询结果</h2>
|
||||
<div class="results-actions">
|
||||
<button id="export-btn" class="btn-export">
|
||||
<i class="fas fa-file-export"></i>
|
||||
导出成绩
|
||||
</button>
|
||||
<button id="batch-delete-btn" class="btn-batch-delete">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
批量删除
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 结果表格 -->
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body p-4">
|
||||
<div class="table-responsive">
|
||||
<table class="grade-table">
|
||||
<thead>
|
||||
<table class="table table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th><input type="checkbox" id="select-all"></th>
|
||||
<th>课程</th>
|
||||
<th>学号</th>
|
||||
<th>姓名</th>
|
||||
<th>班级</th>
|
||||
<th>课程</th>
|
||||
<th>平时成绩</th>
|
||||
<th>期中成绩</th>
|
||||
<th>期末成绩</th>
|
||||
<th>总评成绩</th>
|
||||
<th>绩点</th>
|
||||
<th>等级</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="grade-table-body">
|
||||
<!-- 数据将通过JavaScript动态加<E68081><E58AA0>?-->
|
||||
<tr>
|
||||
<td colspan="11" class="loading">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
<p>正在加载成绩数据...</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody id="gradeTableBody">
|
||||
<tr><td colspan="7" class="text-center py-5 text-muted">请选择条件进行查询</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination">
|
||||
<button id="prev-page" disabled>上一<EFBFBD><EFBFBD>?/button>
|
||||
<button class="active">1</button>
|
||||
<button>2</button>
|
||||
<button>3</button>
|
||||
<button id="next-page">下一<EFBFBD><EFBFBD>?/button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<p>© 2023 XX学校成绩管理系统. 版权所<E69D83><E68980>?</p>
|
||||
<p>技术支<EFBFBD><EFBFBD>? 计算机科学与技术学<E69CAF><E5ADA6>?/p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// 模拟成绩数据
|
||||
const mockGrades = [
|
||||
{
|
||||
id: 1,
|
||||
studentId: "20230001",
|
||||
studentName: "张三",
|
||||
className: "计算机科学与技<E4B88E><E68A80>?<3F><>?,
|
||||
courseName: "数据结构",
|
||||
regularScore: 85,
|
||||
midtermScore: 88,
|
||||
finalScore: 90,
|
||||
totalScore: 88.5,
|
||||
grade: "优秀"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
studentId: "20230002",
|
||||
studentName: "李四",
|
||||
className: "计算机科学与技<EFBFBD><EFBFBD>?<EFBFBD><EFBFBD>?,
|
||||
courseName: "数据结构",
|
||||
regularScore: 78,
|
||||
midtermScore: 82,
|
||||
finalScore: 85,
|
||||
totalScore: 82.3,
|
||||
grade: "良好"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
studentId: "20230003",
|
||||
studentName: "王五",
|
||||
className: "计算机科学与技<E4B88E><E68A80>?<3F><>?,
|
||||
courseName: "操作系统",
|
||||
regularScore: 92,
|
||||
midtermScore: 88,
|
||||
finalScore: 95,
|
||||
totalScore: 91.8,
|
||||
grade: "优秀"
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
studentId: "20230004",
|
||||
studentName: "赵六",
|
||||
className: "计算机科学与技<EFBFBD><EFBFBD>?<EFBFBD><EFBFBD>?,
|
||||
courseName: "操作系统",
|
||||
regularScore: 65,
|
||||
midtermScore: 70,
|
||||
finalScore: 68,
|
||||
totalScore: 67.9,
|
||||
grade: "及格"
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
studentId: "20230005",
|
||||
studentName: "钱七",
|
||||
className: "软件工程1<E7A88B><31>?,
|
||||
courseName: "数据库系<EFBFBD><EFBFBD>?,
|
||||
regularScore: 88,
|
||||
midtermScore: 85,
|
||||
finalScore: 90,
|
||||
totalScore: 87.9,
|
||||
grade: "良好"
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
studentId: "20230006",
|
||||
studentName: "孙八",
|
||||
className: "软件工程1<E7A88B><31>?,
|
||||
courseName: "数据库系<EFBFBD><EFBFBD>?,
|
||||
regularScore: 75,
|
||||
midtermScore: 78,
|
||||
finalScore: 80,
|
||||
totalScore: 78.1,
|
||||
grade: "中等"
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
studentId: "20230007",
|
||||
studentName: "周九",
|
||||
className: "软件工程2<E7A88B><32>?,
|
||||
courseName: "软件工程",
|
||||
regularScore: 90,
|
||||
midtermScore: 92,
|
||||
finalScore: 88,
|
||||
totalScore: 89.6,
|
||||
grade: "优秀"
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
studentId: "20230008",
|
||||
studentName: "吴十",
|
||||
className: "软件工程2<EFBFBD><EFBFBD>?,
|
||||
courseName: "软件工程",
|
||||
regularScore: 82,
|
||||
midtermScore: 85,
|
||||
finalScore: 87,
|
||||
totalScore: 85.1,
|
||||
grade: "良好"
|
||||
}
|
||||
];
|
||||
|
||||
// DOM元素
|
||||
const searchBtn = document.getElementById('search-btn');
|
||||
const resetBtn = document.getElementById('reset-btn');
|
||||
const exportBtn = document.getElementById('export-btn');
|
||||
const batchDeleteBtn = document.getElementById('batch-delete-btn');
|
||||
const selectAllCheckbox = document.getElementById('select-all');
|
||||
const gradeTableBody = document.getElementById('grade-table-body');
|
||||
const classSelect = document.getElementById('class-select');
|
||||
const courseSelect = document.getElementById('course-select');
|
||||
const studentIdInput = document.getElementById('student-id');
|
||||
const studentNameInput = document.getElementById('student-name');
|
||||
|
||||
// 当前选中的成绩ID
|
||||
let selectedGradeIds = new Set();
|
||||
|
||||
// 初始化页<E58C96><E9A1B5>? function initPage() {
|
||||
renderGradeTable(mockGrades);
|
||||
setupEventListeners();
|
||||
updateSelectedCount();
|
||||
}
|
||||
|
||||
// 渲染成绩表格
|
||||
function renderGradeTable(grades) {
|
||||
if (grades.length === 0) {
|
||||
gradeTableBody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="11" class="no-results">
|
||||
<i class="fas fa-search"></i>
|
||||
<h3>未找到相关成绩记<E7BBA9><E8AEB0>?/h3>
|
||||
<p>请尝试调整筛选条件或添加新的成绩记录</p>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
let tableHTML = '';
|
||||
|
||||
grades.forEach(grade => {
|
||||
// 根据成绩确定CSS<53><53>? let gradeClass = 'grade-cell';
|
||||
if (grade.totalScore >= 90) {
|
||||
gradeClass += ' grade-excellent';
|
||||
} else if (grade.totalScore >= 80) {
|
||||
gradeClass += ' grade-good';
|
||||
} else if (grade.totalScore < 60) {
|
||||
gradeClass += ' grade-poor';
|
||||
}
|
||||
|
||||
// 根据总评成绩确定等级
|
||||
let gradeLevel = grade.grade;
|
||||
|
||||
tableHTML += `
|
||||
<tr data-grade-id="${grade.id}">
|
||||
<td>
|
||||
<input type="checkbox" class="grade-checkbox" data-id="${grade.id}">
|
||||
</td>
|
||||
<td>${grade.studentId}</td>
|
||||
<td>${grade.studentName}</td>
|
||||
<td>${grade.className}</td>
|
||||
<td>${grade.courseName}</td>
|
||||
<td>${grade.regularScore}</td>
|
||||
<td>${grade.midtermScore}</td>
|
||||
<td>${grade.finalScore}</td>
|
||||
<td class="${gradeClass}">${grade.totalScore.toFixed(1)}</td>
|
||||
<td>${gradeLevel}</td>
|
||||
<td>
|
||||
<div class="action-buttons">
|
||||
<button class="btn-edit" onclick="editGrade(${grade.id})">
|
||||
<i class="fas fa-edit"></i> 修改
|
||||
</button>
|
||||
<button class="btn-delete" onclick="deleteGrade(${grade.id})">
|
||||
<i class="fas fa-trash"></i> 删除
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
gradeTableBody.innerHTML = tableHTML;
|
||||
|
||||
// 重新绑定复选框事件
|
||||
document.querySelectorAll('.grade-checkbox').forEach(checkbox => {
|
||||
checkbox.addEventListener('change', function() {
|
||||
const gradeId = parseInt(this.getAttribute('data-id'));
|
||||
if (this.checked) {
|
||||
selectedGradeIds.add(gradeId);
|
||||
} else {
|
||||
selectedGradeIds.delete(gradeId);
|
||||
selectAllCheckbox.checked = false;
|
||||
}
|
||||
updateSelectedCount();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 设置事件监听<E79B91><E590AC>? function setupEventListeners() {
|
||||
// 查询按钮
|
||||
searchBtn.addEventListener('click', function() {
|
||||
performSearch();
|
||||
});
|
||||
|
||||
// 重置按钮
|
||||
resetBtn.addEventListener('click', function() {
|
||||
classSelect.value = '';
|
||||
courseSelect.value = '';
|
||||
studentIdInput.value = '';
|
||||
studentNameInput.value = '';
|
||||
renderGradeTable(mockGrades);
|
||||
selectedGradeIds.clear();
|
||||
selectAllCheckbox.checked = false;
|
||||
updateSelectedCount();
|
||||
});
|
||||
|
||||
// 导出按钮
|
||||
exportBtn.addEventListener('click', function() {
|
||||
exportGrades();
|
||||
});
|
||||
|
||||
// 批量删除按钮
|
||||
batchDeleteBtn.addEventListener('click', function() {
|
||||
if (selectedGradeIds.size === 0) {
|
||||
alert('请先选择要删除的成绩记录<E8AEB0><E5BD95>?);
|
||||
return;
|
||||
}
|
||||
|
||||
if (confirm(`确定要删除选中<E98089><E4B8AD>?${selectedGradeIds.size} 条成绩记录吗?此操作不可撤销。`)) {
|
||||
batchDeleteGrades();
|
||||
}
|
||||
});
|
||||
|
||||
// 全选复选框
|
||||
selectAllCheckbox.addEventListener('change', function() {
|
||||
const checkboxes = document.querySelectorAll('.grade-checkbox');
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.checked = this.checked;
|
||||
const gradeId = parseInt(checkbox.getAttribute('data-id'));
|
||||
if (this.checked) {
|
||||
selectedGradeIds.add(gradeId);
|
||||
} else {
|
||||
selectedGradeIds.delete(gradeId);
|
||||
}
|
||||
});
|
||||
updateSelectedCount();
|
||||
});
|
||||
|
||||
// 输入框回车键搜索
|
||||
[studentIdInput, studentNameInput].forEach(input => {
|
||||
input.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
performSearch();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 执行搜索
|
||||
function performSearch() {
|
||||
const selectedClass = classSelect.value;
|
||||
const selectedCourse = courseSelect.value;
|
||||
const studentId = studentIdInput.value.trim();
|
||||
const studentName = studentNameInput.value.trim();
|
||||
|
||||
// 显示加载状<E8BDBD><E78AB6>? gradeTableBody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="11" class="loading">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
<p>正在搜索成绩数据...</p>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
|
||||
// 模拟API延迟
|
||||
setTimeout(() => {
|
||||
let filteredGrades = mockGrades.filter(grade => {
|
||||
// 班级筛<E7BAA7><E7AD9B>? if (selectedClass && grade.className !== selectedClass) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 课程筛<E7A88B><E7AD9B>? if (selectedCourse && grade.courseName !== selectedCourse) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 学号筛<E58FB7><E7AD9B>? if (studentId && !grade.studentId.includes(studentId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 姓名筛<E5908D><E7AD9B>? if (studentName && !grade.studentName.includes(studentName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
renderGradeTable(filteredGrades);
|
||||
selectedGradeIds.clear();
|
||||
selectAllCheckbox.checked = false;
|
||||
updateSelectedCount();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
// 导出成绩
|
||||
function exportGrades() {
|
||||
// 这里应该调用后端API导出成绩
|
||||
// 暂时使用模拟导出
|
||||
alert('成绩导出功能正在开发中,将支持Excel和CSV格式导出<EFBFBD><EFBFBD>?);
|
||||
|
||||
// 模拟导出过程
|
||||
exportBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 导出<E5AFBC><E587BA>?..';
|
||||
exportBtn.disabled = true;
|
||||
|
||||
setTimeout(() => {
|
||||
exportBtn.innerHTML = '<i class="fas fa-file-export"></i> 导出成绩';
|
||||
exportBtn.disabled = false;
|
||||
|
||||
// 在实际应用中,这里会触发文件下载
|
||||
// 暂时显示成功消息
|
||||
showNotification('成绩导出成功!文件已开始下载<E4B88B><E8BDBD>?, 'success');
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
// 批量删除成绩
|
||||
function batchDeleteGrades() {
|
||||
// 这里应该调用后端API删除成绩
|
||||
// 暂时使用模拟删除
|
||||
batchDeleteBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 删除<EFBFBD><EFBFBD>?..';
|
||||
batchDeleteBtn.disabled = true;
|
||||
|
||||
setTimeout(() => {
|
||||
// 从模拟数据中删除选中的成<E79A84><E68890>? selectedGradeIds.forEach(id => {
|
||||
const index = mockGrades.findIndex(grade => grade.id === id);
|
||||
if (index !== -1) {
|
||||
mockGrades.splice(index, 1);
|
||||
}
|
||||
});
|
||||
|
||||
// 重新渲染表格
|
||||
renderGradeTable(mockGrades);
|
||||
selectedGradeIds.clear();
|
||||
selectAllCheckbox.checked = false;
|
||||
|
||||
batchDeleteBtn.innerHTML = '<i class="fas fa-trash-alt"></i> 批量删除';
|
||||
batchDeleteBtn.disabled = false;
|
||||
|
||||
showNotification(`成功删除 ${selectedGradeIds.size} 条成绩记录!`, 'success');
|
||||
updateSelectedCount();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 编辑成绩
|
||||
function editGrade(gradeId) {
|
||||
// 这里应该跳转到编辑页面或打开编辑模态框
|
||||
// 暂时显示提示
|
||||
alert(`编辑成绩 ID: ${gradeId}\n在实际应用中,这里会打开编辑表单。`);
|
||||
}
|
||||
|
||||
// 删除单个成绩
|
||||
function deleteGrade(gradeId) {
|
||||
if (confirm('确定要删除这条成绩记录吗?此操作不可撤销<E692A4><E99480>?)) {
|
||||
// 这里应该调用后端API删除成绩
|
||||
// 暂时使用模拟删除
|
||||
const index = mockGrades.findIndex(grade => grade.id === gradeId);
|
||||
if (index !== -1) {
|
||||
mockGrades.splice(index, 1);
|
||||
renderGradeTable(mockGrades);
|
||||
selectedGradeIds.delete(gradeId);
|
||||
updateSelectedCount();
|
||||
showNotification('成绩记录已删除!', 'success');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新选中计数
|
||||
function updateSelectedCount() {
|
||||
const count = selectedGradeIds.size;
|
||||
if (count > 0) {
|
||||
batchDeleteBtn.innerHTML = `<i class="fas fa-trash-alt"></i> 批量删除 (${count})`;
|
||||
} else {
|
||||
batchDeleteBtn.innerHTML = '<i class="fas fa-trash-alt"></i> 批量删除';
|
||||
}
|
||||
}
|
||||
|
||||
// 显示通知
|
||||
function showNotification(message, type = 'info') {
|
||||
// 在实际应用中,这里会显示一个美观的通知组件
|
||||
// 暂时使用alert
|
||||
alert(message);
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化
|
||||
document.addEventListener('DOMContentLoaded', initPage);
|
||||
</script>
|
||||
<!-- Bootstrap 5 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/public/js/auth.js"></script>
|
||||
<script src="/public/js/teacher.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
283
frontend/views/teacher/profile.html
Normal file
283
frontend/views/teacher/profile.html
Normal file
@@ -0,0 +1,283 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>个人资料 - 教师端</title>
|
||||
<!-- Bootstrap 5 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--sidebar-width: 260px;
|
||||
--primary-color: #1cc88a;
|
||||
--secondary-color: #858796;
|
||||
--light-bg: #f8f9fc;
|
||||
--teacher-gradient: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
background-color: var(--light-bg);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 侧边栏样式 */
|
||||
.sidebar {
|
||||
width: var(--sidebar-width);
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: var(--teacher-gradient);
|
||||
color: white;
|
||||
z-index: 1000;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 2rem 1.5rem;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.sidebar-brand {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
padding: 2rem 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.user-info h6 { margin-bottom: 0.25rem; font-weight: 600; }
|
||||
.user-info p { font-size: 0.85rem; opacity: 0.8; margin-bottom: 0; }
|
||||
|
||||
.nav-menu { padding: 1rem 0; }
|
||||
.nav-item { padding: 0.25rem 1rem; }
|
||||
|
||||
.nav-link {
|
||||
color: rgba(255,255,255,0.8);
|
||||
padding: 0.8rem 1.25rem;
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link.active {
|
||||
color: white;
|
||||
background: rgba(255,255,255,0.15);
|
||||
}
|
||||
|
||||
.nav-link i { width: 20px; text-align: center; }
|
||||
|
||||
/* 主内容区 */
|
||||
.main-content {
|
||||
margin-left: var(--sidebar-width);
|
||||
padding: 2rem;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.top-navbar {
|
||||
background: white;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.1);
|
||||
margin-bottom: 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.page-heading h4 { margin-bottom: 0; font-weight: 700; color: #333; }
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.sidebar { left: -var(--sidebar-width); }
|
||||
.main-content { margin-left: 0; }
|
||||
.sidebar.active { left: 0; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 侧边栏 -->
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<a href="#" class="sidebar-brand">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
<span>成绩管理系统</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="user-profile">
|
||||
<div class="user-avatar">
|
||||
<i class="fas fa-user-tie"></i>
|
||||
</div>
|
||||
<div class="user-info text-white">
|
||||
<h6 id="teacherName">加载中...</h6>
|
||||
<p>教师 | <span id="teacherId">...</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-menu">
|
||||
<div class="nav-item">
|
||||
<a href="/teacher/dashboard" class="nav-link">
|
||||
<i class="fas fa-tachometer-alt"></i>
|
||||
<span>教师仪表板</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/teacher/grade_entry" class="nav-link">
|
||||
<i class="fas fa-edit"></i>
|
||||
<span>成绩录入</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/teacher/grade_management" class="nav-link">
|
||||
<i class="fas fa-tasks"></i>
|
||||
<span>成绩管理</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<a href="/teacher/profile" class="nav-link active">
|
||||
<i class="fas fa-user-edit"></i>
|
||||
<span>个人资料</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item mt-4">
|
||||
<a href="javascript:void(0)" id="logoutBtn" class="nav-link text-warning">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
<span>退出登录</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- 主内容 -->
|
||||
<div class="main-content">
|
||||
<!-- 顶部导航 -->
|
||||
<div class="top-navbar">
|
||||
<div class="page-heading">
|
||||
<h4>个人资料</h4>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="text-end d-none d-md-block">
|
||||
<div class="small text-muted" id="currentTime"></div>
|
||||
<div class="fw-bold" id="userName">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-4 mb-4">
|
||||
<div class="card border-0 shadow-sm text-center p-4 h-100">
|
||||
<div class="mb-3">
|
||||
<div class="bg-light rounded-circle d-inline-flex align-items-center justify-content-center" style="width: 120px; height: 120px;">
|
||||
<i class="fas fa-user-tie fa-4x text-secondary"></i>
|
||||
</div>
|
||||
</div>
|
||||
<h4 id="profileName">加载中...</h4>
|
||||
<p class="text-muted" id="profileId">...</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-8 mb-4">
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0 fw-bold">基本信息</h5>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<form id="profileForm">
|
||||
<div class="mb-3 row">
|
||||
<label class="col-sm-3 col-form-label">工号</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" readonly class="form-control-plaintext" id="inputTeacherId" value="...">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label class="col-sm-3 col-form-label">姓名</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="inputName" placeholder="请输入姓名">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label class="col-sm-3 col-form-label">角色</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" readonly class="form-control-plaintext" value="教师">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label class="col-sm-3 col-form-label">所属学院/班级</label>
|
||||
<div class="col-sm-9">
|
||||
<input type="text" class="form-control" id="inputClass" placeholder="请输入学院或班级名称">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-9 offset-sm-3">
|
||||
<button type="button" id="saveProfileBtn" class="btn btn-outline-primary btn-sm">
|
||||
<i class="fas fa-save me-1"></i> 保存基本信息
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0 fw-bold">修改密码</h5>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<form id="passwordForm">
|
||||
<div class="mb-3">
|
||||
<label for="oldPassword" class="form-label">原密码</label>
|
||||
<input type="password" class="form-control" id="oldPassword" required placeholder="请输入原密码">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="newPassword" class="form-label">新密码</label>
|
||||
<input type="password" class="form-control" id="newPassword" required placeholder="请输入新密码">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="confirmPassword" class="form-label">确认新密码</label>
|
||||
<input type="password" class="form-control" id="confirmPassword" required placeholder="请再次输入新密码">
|
||||
</div>
|
||||
<div id="passwordError" class="text-danger mb-3" style="display: none;"></div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save me-1"></i> 修改密码
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap 5 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/public/js/auth.js"></script>
|
||||
<script src="/public/js/teacher.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user