From 59501e514faea673df08d1f99072bb56858cb3e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=A5=80=E6=A2=A6?= <3501646051@qq.com>
Date: Mon, 22 Dec 2025 19:48:38 +0800
Subject: [PATCH] =?UTF-8?q?feat(=E5=AD=A6=E7=94=9F):=20=E6=B7=BB=E5=8A=A0?=
=?UTF-8?q?=E5=AD=A6=E7=94=9F=E8=AF=BE=E7=A8=8B=E7=AE=A1=E7=90=86=E5=8A=9F?=
=?UTF-8?q?=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增我的课程页面及路由
- 实现课程列表和详情查看功能
- 更新导航链接指向新页面
- 添加成绩详情模态框
- 完善相关后端接口和服务
---
backend/controllers/studentController.js | 28 ++
backend/models/Course.js | 25 ++
backend/models/Score.js | 2 +-
backend/routes/student.js | 2 +
backend/server.js | 3 +
backend/services/studentService.js | 20 ++
database/score_management.sql | 2 +-
frontend/public/js/student.js | 114 +++++++-
frontend/views/student/dashboard.html | 74 ++++-
frontend/views/student/my_courses.html | 358 +++++++++++++++++++++++
frontend/views/student/profile.html | 2 +-
scripts/start.bat | 2 +-
12 files changed, 624 insertions(+), 8 deletions(-)
create mode 100644 frontend/views/student/my_courses.html
diff --git a/backend/controllers/studentController.js b/backend/controllers/studentController.js
index ed1975a..9b741b3 100644
--- a/backend/controllers/studentController.js
+++ b/backend/controllers/studentController.js
@@ -2,6 +2,34 @@ const StudentService = require('../services/studentService');
const { success, error } = require('../utils/response');
class StudentController {
+ static async getCourses(req, res) {
+ try {
+ const userId = req.session.user.id;
+ const data = await StudentService.getStudentCourses(userId);
+ success(res, data);
+ } catch (err) {
+ if (err.message === '学生信息不存在') {
+ return error(res, err.message, 404);
+ }
+ console.error('Get Courses Error:', err);
+ error(res, '服务器错误');
+ }
+ }
+
+ static async getCourseDetails(req, res) {
+ try {
+ const courseId = req.params.id;
+ const data = await StudentService.getCourseDetails(courseId);
+ success(res, data);
+ } catch (err) {
+ if (err.message === '课程不存在') {
+ return error(res, err.message, 404);
+ }
+ console.error('Get Course Details Error:', err);
+ error(res, '服务器错误');
+ }
+ }
+
static async getGrades(req, res) {
try {
const userId = req.session.user.id;
diff --git a/backend/models/Course.js b/backend/models/Course.js
index bff2e83..de374a6 100644
--- a/backend/models/Course.js
+++ b/backend/models/Course.js
@@ -12,6 +12,31 @@ class Course {
const rows = await db.query('SELECT * FROM courses WHERE id = ?', [id]);
return rows[0];
}
+
+ static async findDetailsById(id) {
+ const sql = `
+ SELECT c.*, u.name as teacher_name, cl.class_name
+ FROM courses c
+ JOIN users u ON c.teacher_id = u.id
+ JOIN classes cl ON c.class_id = cl.id
+ WHERE c.id = ?
+ `;
+ const rows = await db.query(sql, [id]);
+ return rows[0];
+ }
+
+ static async findByStudentId(studentId) {
+ const sql = `
+ SELECT c.*, u.name as teacher_name
+ FROM courses c
+ JOIN classes cl ON c.class_id = cl.id
+ JOIN students s ON cl.class_name = s.class
+ JOIN users u ON c.teacher_id = u.id
+ WHERE s.id = ?
+ ORDER BY c.course_code
+ `;
+ return await db.query(sql, [studentId]);
+ }
}
module.exports = Course;
\ No newline at end of file
diff --git a/backend/models/Score.js b/backend/models/Score.js
index 3f4fbd3..3b04309 100644
--- a/backend/models/Score.js
+++ b/backend/models/Score.js
@@ -22,7 +22,7 @@ class Score {
s.usual_score, s.midterm_score, s.final_score, s.total_score as score,
s.grade_point, s.grade_level, s.created_at, s.remark,
c.course_code, c.course_name, c.credit, c.semester,
- u.name as teacher_name, u.email as teacher_email,
+ u.name as teacher_name,
st.id as student_number, st.class as class_name
FROM grades s
JOIN courses c ON s.course_id = c.id
diff --git a/backend/routes/student.js b/backend/routes/student.js
index 478fa0e..ce1ba53 100644
--- a/backend/routes/student.js
+++ b/backend/routes/student.js
@@ -3,6 +3,8 @@ const router = express.Router();
const StudentController = require('../controllers/studentController');
const { requireAuth, requireRole } = require('../middleware/auth');
+router.get('/courses', requireAuth, requireRole(['student']), StudentController.getCourses);
+router.get('/courses/:id', requireAuth, requireRole(['student']), StudentController.getCourseDetails);
router.get('/grades', requireAuth, requireRole(['student']), StudentController.getGrades);
router.get('/grades/:id', requireAuth, requireRole(['student']), StudentController.getGradeDetails);
diff --git a/backend/server.js b/backend/server.js
index a8b9c89..cd3c47c 100644
--- a/backend/server.js
+++ b/backend/server.js
@@ -96,6 +96,9 @@ app.get('/dashboard', requirePageAuth, (req, res) => {
app.get('/student/dashboard', requirePageAuth, requirePageRole(['student']), (req, res) => {
res.sendFile(path.join(__dirname, '../frontend/views/student/dashboard.html'));
});
+app.get('/student/my-courses', requirePageAuth, requirePageRole(['student']), (req, res) => {
+ res.sendFile(path.join(__dirname, '../frontend/views/student/my_courses.html'));
+});
app.get('/student/profile', requirePageAuth, requirePageRole(['student']), (req, res) => {
res.sendFile(path.join(__dirname, '../frontend/views/student/profile.html'));
});
diff --git a/backend/services/studentService.js b/backend/services/studentService.js
index 1e8796b..62f7102 100644
--- a/backend/services/studentService.js
+++ b/backend/services/studentService.js
@@ -1,7 +1,27 @@
const Score = require('../models/Score');
const Student = require('../models/Student');
+const Course = require('../models/Course');
+
class StudentService {
+ static async getStudentCourses(userId) {
+ const student = await Student.findById(userId);
+ if (!student) {
+ throw new Error('学生信息不存在');
+ }
+
+ const courses = await Course.findByStudentId(userId);
+ return courses;
+ }
+
+ static async getCourseDetails(courseId) {
+ const course = await Course.findDetailsById(courseId);
+ if (!course) {
+ throw new Error('课程不存在');
+ }
+ return course;
+ }
+
static async getStudentGrades(userId) {
// 先通过 userId 获取 studentId
// 假设 users.id = students.id,或者通过 user_id 关联
diff --git a/database/score_management.sql b/database/score_management.sql
index 4a7f2b7..467b0a9 100644
--- a/database/score_management.sql
+++ b/database/score_management.sql
@@ -11,7 +11,7 @@
Target Server Version : 90500 (9.5.0)
File Encoding : 65001
- Date: 21/12/2025 22:41:10
+ Date: 21/12/2025 22:47:56
*/
SET NAMES utf8mb4;
diff --git a/frontend/public/js/student.js b/frontend/public/js/student.js
index cfd19d8..fabfae7 100644
--- a/frontend/public/js/student.js
+++ b/frontend/public/js/student.js
@@ -9,6 +9,7 @@ class StudentManager {
init() {
this.initDashboard();
+ this.initMyCourses();
this.updateCurrentTime();
setInterval(() => this.updateCurrentTime(), 1000);
}
@@ -44,6 +45,79 @@ class StudentManager {
console.error('Fetch dashboard data failed:', error);
}
}
+
+ async initMyCourses() {
+ // 检查是否在我的课程页面
+ const tbody = document.getElementById('coursesTableBody');
+ if (!tbody) return;
+
+ try {
+ const response = await fetch(`${this.apiBase}/courses`);
+ const result = await response.json();
+
+ if (result.success) {
+ this.renderCourses(result.data);
+ } else {
+ if (window.authManager) {
+ window.authManager.showNotification(result.message || '获取课程失败', 'error');
+ }
+ }
+ } catch (error) {
+ console.error('Fetch courses data failed:', error);
+ }
+ }
+
+ renderCourses(courses) {
+ const tbody = document.getElementById('coursesTableBody');
+ if (!tbody) return;
+
+ if (!courses || courses.length === 0) {
+ tbody.innerHTML = '
| 暂无课程记录 |
';
+ return;
+ }
+
+ tbody.innerHTML = courses.map(course => `
+
+ | ${course.course_code} |
+ ${course.course_name} |
+ ${course.credit} |
+ ${course.teacher_name} |
+ ${course.semester || '2023-2024 下学期'} |
+
+
+ |
+
+ `).join('');
+ }
+
+ async viewCourseDetails(courseId) {
+ try {
+ const response = await fetch(`${this.apiBase}/courses/${courseId}`);
+ const result = await response.json();
+
+ if (result.success) {
+ const course = result.data;
+ this.updateElement('modalCourseName', course.course_name);
+ this.updateElement('modalCourseCode', course.course_code);
+ this.updateElement('modalCourseCredit', course.credit);
+ this.updateElement('modalTeacherName', course.teacher_name);
+ this.updateElement('modalClassName', course.class_name);
+ this.updateElement('modalSemester', course.semester || '2023-2024 下学期');
+ this.updateElement('modalTeacherEmail', course.teacher_email || '暂无');
+
+ const modal = new bootstrap.Modal(document.getElementById('courseDetailsModal'));
+ modal.show();
+ } else {
+ if (window.authManager) {
+ window.authManager.showNotification(result.message || '获取课程详情失败', 'error');
+ }
+ }
+ } catch (error) {
+ console.error('Fetch course details failed:', error);
+ }
+ }
renderDashboard(data) {
const { grades, statistics } = data;
@@ -107,9 +181,43 @@ class StudentManager {
return '不及格';
}
- viewDetails(id) {
- // 实现查看详情逻辑,或者跳转到详情页
- console.log('View details for score:', id);
+ async viewDetails(id) {
+ try {
+ const response = await fetch(`${this.apiBase}/grades/${id}`);
+ const result = await response.json();
+
+ if (result.success) {
+ const grade = result.data.grade;
+ this.updateElement('modalGradeCourseName', grade.course_name);
+ this.updateElement('modalGradeCourseCode', grade.course_code);
+ this.updateElement('modalTotalScore', grade.score);
+ this.updateElement('modalUsualScore', grade.usual_score || '-');
+ this.updateElement('modalMidtermScore', grade.midterm_score || '-');
+ this.updateElement('modalFinalScore', grade.final_score || '-');
+ this.updateElement('modalGradeCredit', grade.credit);
+ this.updateElement('modalGradeLevel', grade.grade_level || '-');
+ this.updateElement('modalGradePoint', grade.grade_point || '-');
+ this.updateElement('modalGradeTeacher', grade.teacher_name);
+ this.updateElement('modalGradeTime', new Date(grade.created_at).toLocaleString());
+
+ const remarkContainer = document.getElementById('modalRemarkContainer');
+ if (grade.remark) {
+ remarkContainer.style.display = 'block';
+ this.updateElement('modalRemark', grade.remark);
+ } else {
+ remarkContainer.style.display = 'none';
+ }
+
+ const modal = new bootstrap.Modal(document.getElementById('gradeDetailsModal'));
+ modal.show();
+ } else {
+ if (window.authManager) {
+ window.authManager.showNotification(result.message || '获取成绩详情失败', 'error');
+ }
+ }
+ } catch (error) {
+ console.error('Fetch grade details failed:', error);
+ }
}
}
diff --git a/frontend/views/student/dashboard.html b/frontend/views/student/dashboard.html
index d10be6e..c28537a 100644
--- a/frontend/views/student/dashboard.html
+++ b/frontend/views/student/dashboard.html
@@ -236,7 +236,7 @@
-
+
我的课程
@@ -380,6 +380,78 @@
+
+
+
+
+
+
+
+
+
+
+
+ -
+ 学分
+ -
+
+ -
+ 等第
+ -
+
+ -
+ 绩点
+ -
+
+ -
+ 任课教师
+ -
+
+ -
+ 录入时间
+ -
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/views/student/my_courses.html b/frontend/views/student/my_courses.html
new file mode 100644
index 0000000..9aa6ab4
--- /dev/null
+++ b/frontend/views/student/my_courses.html
@@ -0,0 +1,358 @@
+
+
+
+
+
+ 我的课程 - 成绩管理系统
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | 课程代码 |
+ 课程名称 |
+ 学分 |
+ 任课教师 |
+ 学期 |
+ 操作 |
+
+
+
+
+
+ |
+
+ 数据加载中...
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/views/student/profile.html b/frontend/views/student/profile.html
index 0c840ed..c75275d 100644
--- a/frontend/views/student/profile.html
+++ b/frontend/views/student/profile.html
@@ -195,7 +195,7 @@
-
+
我的课程
diff --git a/scripts/start.bat b/scripts/start.bat
index eea088b..405f275 100644
--- a/scripts/start.bat
+++ b/scripts/start.bat
@@ -9,5 +9,5 @@ for /f "tokens=5" %%a in ('netstat -ano ^| findstr :3000 ^| findstr LISTENING')
)
echo Starting Backend Server...
-start http://localhost:3000
+:: start http://localhost:3000
npm start
\ No newline at end of file