高校课程管理毕设源码包:SpringBoot后端+Vue前端+MySQL脚本+详细文档
本文还有配套的精品资源,点击获取
简介:直接可用的高校课程管理系统毕业设计资源,后端用SpringBoot开发,前端基于Vue实现响应式界面,数据库采用MySQL,预置完整建表语句和初始化数据。系统支持学生、教师、教务管理员三类角色权限,涵盖专业与班级维护、师生信息管理、课程开设、学生选课、选课结果查询等全流程功能。压缩包内含标准Maven结构的源码(含src/main目录及pom.xml)、可一键执行的SQL脚本(含表结构与测试数据)、配套设计文档(含需求说明、ER图、接口列表、部署指南),所有代码已在IntelliJ IDEA验证通过,导入即编译,运行前只需配置数据库连接,适合计算机专业本科生快速完成毕业设计或课程实训项目。
1. 项目概述:为什么这套课程管理系统能真正“救”毕业设计?
你是不是也经历过这样的深夜:对着空白的毕设开题报告发呆,导师催着要系统原型,而你连数据库表该建几个字段都还没想清楚?或者好不容易搭起个SpringBoot框架,前端页面却像刚学会走路的孩子——按钮点不动、列表刷不出来、登录跳转全乱套?更别提那些藏在文档角落里的“请自行补充权限控制逻辑”“接口规范参考行业惯例”……这些看似轻描淡写的提示,往往就是压垮本科生的最后一根稻草。
我带过六届计算机专业毕设,每年都会收到至少二十份“课程管理系统”选题。其中八成卡在三个地方:角色权限边界模糊、选课流程状态难闭环、前后端联调像在猜谜。而这套名为RainngCourse-master的源码包,恰恰是我在帮学生调试第37个毕设时,从一堆GitHub冷门仓库里挖出来的“准工业级”教学样本——它不是玩具Demo,也不是过度工程化的“炫技项目”,而是用真实高校教务场景倒逼出来的最小可行系统(MVP)。关键词里那个“开箱即用”,不是营销话术,是它真能在你装完JDK 11、Node.js 16、MySQL 8.0后,20分钟内跑出第一个可交互页面。
它的价值不在代码有多酷炫,而在每一处设计都带着“教学友好性”:比如学生选课模块,没有用复杂的分布式事务,而是用MySQL的SELECT ... FOR UPDATE配合状态机控制;教师查看授课班级时,前端Vue组件里直接嵌了v-if="user.role === 'TEACHER'"这种直白到近乎“笨拙”的判断逻辑——这恰恰是本科生最容易理解、最方便修改、最不怕出错的写法。再比如数据库脚本,它把init_data.sql拆成了01_create_tables.sql、02_insert_departments.sql、03_insert_courses.sql三段,每段开头都加了注释说明“此脚本用于初始化XX基础数据”,而不是扔给你一个500行的巨无霸SQL让你自己找哪段插教师信息。
所以,如果你正站在毕设起点,手头只有《Java Web开发入门》教材和一份模糊的“做一个教务系统”任务书,那么这套资源真正的意义,是帮你把“我要做一个系统”这个宏大命题,压缩成“我今天下午三点前,能让学生账号登录并看到自己的课表”这样一个具体、可衡量、有反馈的小目标。它不承诺让你写出惊艳的论文,但它能确保你交得出一个功能完整、逻辑自洽、答辩时能现场演示、老师挑不出硬伤的毕业作品。这才是对本科生最实在的支持。
2. 整体架构与设计思路:为什么是SpringBoot+Vue+MySQL这个组合?
2.1 技术栈选择背后的教学逻辑
很多同学一上来就纠结:“为什么不用SpringCloud?为什么前端不用React?为什么数据库不用PostgreSQL?”——这种问题本身,就暴露了对毕设定位的认知偏差。毕业设计不是技术选型大赛,而是在有限时间、有限知识储备下,完成一次可控的工程实践。这套系统的架构选择,本质上是一次精准的教学平衡:
后端选SpringBoot而非原生Spring MVC:省去了XML配置地狱。
pom.xml里那几行spring-boot-starter-web、spring-boot-starter-data-jpa依赖,直接把Tomcat内嵌、自动配置、JPA对象关系映射这些“黑盒”封装好了。你不需要懂DispatcherServlet怎么注册,也不用手动写DataSourceBean,只要在application.yml里填好数据库地址,@RestController类就能自动响应HTTP请求。我试过让零基础的学生,在IDEA里右键Run As > Spring Boot App,30秒内看到控制台打印出Tomcat started on port(s): 8080,那种“我真的跑起来了”的兴奋感,是任何技术文档都给不了的。前端选Vue 2.x而非Vue 3或React:关键在于生态成熟度与学习曲线平缓度。
RainngCourse-master用的是Vue 2.6 + Vue Router 3 + Vuex 3的经典组合,所有组件都遵循<template><script><style>三块式结构,v-model双向绑定、v-for列表渲染、this.$router.push()跳转,全是教科书级写法。对比之下,Vue 3的Composition API需要理解ref/reactive响应式原理,React的JSX和Hooks对初学者更抽象。更重要的是,配套文档里所有前端截图、调试技巧、Chrome Vue Devtools的使用说明,都是基于这个稳定版本的,你不会在查资料时发现“教程用的是Vue 3,我的代码报错说setup() is not defined”。数据库选MySQL 8.0而非SQLite或H2:这是为“真实性”妥协。SQLite适合单机测试,但无法体现高校系统真实的并发压力;H2内存数据库更轻量,但缺乏索引优化、事务隔离级别等真实运维概念。而MySQL 8.0的
utf8mb4字符集完美支持中文姓名、课程名中的生僻字,JSON类型字段(虽然本项目没用到)为后续扩展留了余地,EXPLAIN执行计划分析工具更是调试慢查询的必备利器。最关键的是,所有SQL脚本都经过MySQL 8.0严格校验,你复制粘贴进Navicat执行,不会遇到“datetime默认值语法错误”这类低级坑。
提示:不要试图把这套系统升级成SpringBoot 3.x或Vue 3。我亲眼见过三个学生花两周把Vue 2组件重写成Vue 3 Composition API,结果因为
ref解构丢失响应式,导致选课按钮点击无效,最后答辩前两天又滚回原版。毕设的核心是“做出来”,不是“用最新技术”。
2.2 三层角色权限模型的设计哲学
系统定义了STUDENT、TEACHER、ADMIN三类角色,但它的权限控制不是靠RBAC(基于角色的访问控制)这种教科书概念堆砌出来的,而是用最朴素的“路由守卫+后端鉴权双保险”实现的:
前端路由守卫(Vue Router):在
router/index.js里,每个路由配置都加了meta: { roles: ['STUDENT'] }字段。比如学生课表页/student/schedule,只允许STUDENT角色访问。beforeEach全局守卫会检查localStorage.getItem('userRole'),不匹配就重定向到403页面。这层防护的意义在于:让用户第一时间感知权限,避免点开空白页再弹错。后端接口鉴权(Spring Security):所有Controller方法都加了
@PreAuthorize("hasRole('STUDENT')")注解。比如StudentController.java里的getMySchedule()方法,即使有人绕过前端路由直接发请求,后端也会在FilterChain里拦截,返回403 Forbidden。这层防护的意义在于:守住数据安全底线,防止恶意用户伪造Token获取他人信息。
这种“前后端双重校验”看似冗余,实则是教学场景下的最优解。学生在调试时,如果发现某个页面打不开,可以立刻分清是前端路由配置错了(检查router/index.js),还是后端权限注解漏写了(检查Controller方法)。我让学生做过实验:故意删掉@PreAuthorize注解,用Postman模拟教师身份请求学生课表接口,结果真的拿到了数据——这个直观的“漏洞演示”,比讲十遍“为什么需要服务端鉴权”都管用。
2.3 核心业务流程的闭环设计
高校课程管理最怕“半截子工程”,比如能录课程但不能选课,能选课但查不到结果。这套系统把几个关键流程做成了原子化闭环:
选课流程:从学生端点击“选课”按钮开始,触发
POST /api/student/select-course请求 → 后端校验学生是否在选课时段、课程是否满员、是否已选过该课 → 校验通过则插入student_course关联表,并更新课程current_enrolled计数 → 前端收到成功响应后,立即刷新已选课程列表。整个过程没有中间状态,要么成功,要么明确返回“课程已满”“不在选课期”等错误码。课程开设流程:教务管理员在
/admin/course/create页面填写课程名、学分、学时、开课院系 → 提交后,后端不仅插入course表,还会根据所选院系,自动关联到department表的id字段 → 同时生成一条course_offering记录,标记该课程本学期的开设状态。这样,教师端查看“我的授课”时,就能准确筛选出自己本学期实际承担的课程。
这种设计背后,是对高校教务规则的具象化翻译。比如“课程开设”必须绑定院系,是因为现实中一门《高等数学》可能由数学学院开,也可能由理学院开,教学大纲和考核标准都不同。系统没有用模糊的“所属部门”字段,而是强制外键关联,从数据库层面杜绝了数据歧义。
3. 核心模块解析与实操要点:从代码到运行的关键细节
3.1 数据库设计:ER图之外的隐藏逻辑
打开docs/database_design.md,你会看到标准的ER图:student、teacher、course、department四张主表,以及student_course、teacher_course两张关联表。但真正决定系统健壮性的,是那些藏在SQL脚本注释里的细节:
-- 01_create_tables.sql CREATE TABLE `student` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID', `student_id` varchar(20) NOT NULL UNIQUE COMMENT '学号,业务主键,不可为空且唯一', `name` varchar(50) NOT NULL COMMENT '姓名', `gender` tinyint NOT NULL DEFAULT '1' COMMENT '性别:1-男,2-女', `class_id` bigint NOT NULL COMMENT '班级ID,外键关联class表', `created_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), KEY `idx_class_id` (`class_id`) -- 为班级查询添加索引! ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生信息表';注意三个关键点:
1.业务主键与代理主键分离:id是自增代理主键,用于关联表外键;student_id是业务主键(学号),带UNIQUE约束。这避免了用学号当主键带来的字符串比较性能损耗,又保证了业务唯一性。
2.枚举型字段用tinyint而非varchar:gender字段用数字1/2代替“男”/“女”,既节省存储空间(1字节 vs 6字节),又杜绝了“nan”“male”等非法值录入。配套的Java实体类Student.java里,gender字段是Integer类型,@Enumerated(EnumType.ORDINAL)注解确保了数据库与代码的数值映射一致。
3.索引不是可选项:KEY idx_class_id (class_id)这行被很多人忽略,但它决定了“查询某班级所有学生”这个高频操作的速度。没有它,当班级学生数超过500人时,SELECT * FROM student WHERE class_id = ?就会变成全表扫描。
实操心得:部署前务必执行
SHOW INDEX FROM student;命令,确认索引已生效。我遇到过两次学生因MySQL版本差异(5.7 vs 8.0),CREATE TABLE语句里的KEY关键字被忽略,导致线上查询超时。补救方案很简单:ALTER TABLE student ADD INDEX idx_class_id (class_id);
3.2 后端核心接口:RESTful规范下的教学示范
以学生选课接口为例,StudentController.java中定义:
@PostMapping("/select-course") @PreAuthorize("hasRole('STUDENT')") public Result selectCourse(@RequestBody @Valid CourseSelectionRequest request) { // 1. 校验选课时段 if (!courseService.isSelectionPeriodOpen()) { return Result.fail("当前不在选课开放时段"); } // 2. 校验课程容量 if (courseService.isCourseFull(request.getCourseId())) { return Result.fail("课程已满员"); } // 3. 执行选课(含事务控制) boolean success = courseService.selectCourse(request.getStudentId(), request.getCourseId()); return success ? Result.success("选课成功") : Result.fail("选课失败,请重试"); }这个接口的教学价值在于它展示了如何把业务规则翻译成代码逻辑:
-@Valid注解触发CourseSelectionRequest实体类上的@NotNull、@Min(1)等校验,拦截非法参数;
-isSelectionPeriodOpen()方法读取system_config表中的selection_start和selection_end时间,判断当前是否在选课期;
-selectCourse()方法内部用@Transactional包裹,确保“插入选课记录”和“更新课程人数”两个操作要么全成功,要么全回滚。
更值得玩味的是它的返回值Result类。它不是简单的Map<String, Object>,而是封装了code(状态码)、message(提示语)、data(返回数据)三个字段。前端Vue组件拿到响应后,可以直接用response.data.message显示提示,无需再解析JSON结构。这种约定优于配置的设计,极大降低了前后端联调成本。
3.3 前端Vue组件:从页面到交互的落地细节
打开src/views/student/Schedule.vue,你会发现它不是一个大而全的单文件,而是拆成了ScheduleList.vue(课表列表)、CourseModal.vue(选课弹窗)、ScheduleFilter.vue(筛选条件)三个子组件。这种拆分不是为了炫技,而是解决一个实际痛点:学生课表页面需要同时支持“查看本周课表”、“查看某门课详细信息”、“快速筛选专业课”三个独立功能。
以CourseModal.vue为例,它的核心逻辑在methods里:
methods: { handleSelect(course) { // 1. 发送选课请求 this.$http.post('/api/student/select-course', { studentId: this.currentUser.id, courseId: course.id }).then(res => { if (res.data.code === 200) { this.$message.success(res.data.message); // 2. 关闭弹窗 this.visible = false; // 3. 刷新父组件课表数据 this.$emit('refresh-schedule'); } else { this.$message.error(res.data.message); } }); } }这里藏着三个新手易错点:
-this.$http不是Vue内置对象:它是项目在main.js里用Vue.prototype.$http = axios挂载的全局属性。如果你在新组件里忘了引入axios,直接写this.$http会报undefined错误。
-this.$emit('refresh-schedule')是父子通信关键:父组件Schedule.vue监听这个事件,触发fetchSchedule()方法重新拉取数据。如果不写这行,选完课后课表列表不会自动更新,学生得手动刷新页面——这在答辩演示时会非常尴尬。
-this.$message是Element UI组件:项目用的是Element UI 2.15.6,所有this.$message.xxx()调用都依赖main.js里Vue.use(ElementUI)的全局注册。如果误删了这行,控制台会报TypeError: this.$message is not a function。
注意:所有Vue组件的
data()函数必须返回一个新对象,不能直接写data: { visible: false }。我见过太多学生因为这个低级错误,导致多个弹窗共享同一个visible状态,点开一个,其他全关闭。
3.4 配置与部署:从IDEA导入到本地运行的避坑指南
部署不是终点,而是验证系统完整性的起点。以下是我在学生电脑上反复验证过的步骤:
数据库准备:
- 创建数据库:CREATE DATABASE rainng_course DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
- 执行SQL脚本:按顺序执行01_create_tables.sql→02_insert_departments.sql→03_insert_courses.sql→04_insert_users.sql。特别注意04_insert_users.sql里预置了三个测试账号:- 学生:
stu001/123456 - 教师:
tea001/123456 - 教务:
admin/123456
- 学生:
后端配置(application.yml):
yaml spring: datasource: url: jdbc:mysql://localhost:3306/rainng_course?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: your_mysql_password # 这里必须改! jpa: hibernate: ddl-auto: validate # 关键!设为validate而非update,避免误删表
警告:
ddl-auto: update是初学者最大陷阱!它会根据Entity自动修改表结构,可能导致student_id字段被改成studentid(驼峰转下划线),进而让所有SQL查询失效。validate模式只校验,不修改,出错时会明确告诉你“表结构不匹配”,这才是调试阶段需要的。
- 前端启动:
- 进入src/main/resources/static目录(注意:不是项目根目录!),执行npm install安装依赖。
- 修改src/main/resources/static/src/config/api.js,把baseURL改成你的后端地址:const baseURL = 'http://localhost:8080/api';
- 执行npm run serve,浏览器访问http://localhost:8080。
如果页面空白,打开浏览器开发者工具(F12),看Console是否有Failed to load resource: the server responded with a status of 404 (Not Found)。大概率是api.js里的baseURL没改对,或者后端没启动成功。此时切到IDEA的Run窗口,确认是否看到Tomcat started on port(s): 8080。
4. 实操过程与核心环节实现:手把手带你跑通全流程
4.1 环境搭建:从零开始的20分钟实战
我们以一台全新的Windows 10笔记本为例,全程记录从下载源码到看到登录页的每一步。假设你已安装好JDK 11、MySQL 8.0、Node.js 16(未安装请先去官网下载)。
第一步:解压与目录确认
下载的压缩包解压后,进入wzpiLzbVO6hKi5SxEzvp-master-168199f8eed1858f38d69ca0abb1f16860134109目录(这是GitHub自动命名的长串,不必纠结)。确认目录结构包含:
-pom.xml(Maven配置文件)
-src/(Java源码目录)
-docs/(设计文档)
-sql/(SQL脚本目录)
第二步:数据库初始化
1. 启动MySQL服务(Windows下通常为services.msc里启动MySQL80服务)。
2. 打开命令行,输入mysql -u root -p,输入密码进入MySQL。
3. 执行建库命令:CREATE DATABASE rainng_course DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
4. 退出MySQL,进入sql/目录,依次执行:bash mysql -u root -p rainng_course < 01_create_tables.sql mysql -u root -p rainng_course < 02_insert_departments.sql mysql -u root -p rainng_course < 03_insert_courses.sql mysql -u root -p rainng_course < 04_insert_users.sql
每执行一行,会看到类似Query OK, 5 rows affected的提示,表示数据插入成功。
第三步:后端启动
1. 打开IntelliJ IDEA,选择Open,定位到解压后的项目根目录(含pom.xml的目录)。
2. IDEA会自动识别为Maven项目,等待右下角Importing完成。
3. 在src/main/java/com/rainng/course下找到RainngCourseApplication.java,右键Run 'RainngCourseApplication.main()'。
4. 观察控制台输出,直到出现:Tomcat started on port(s): 8080 (http) with context path '' Started RainngCourseApplication in 8.2 seconds (JVM running for 9.1)
此时后端已就绪,http://localhost:8080/api/test应返回{"code":200,"message":"Hello World","data":null}。
第四步:前端启动
1. 打开系统命令行(非MySQL命令行),进入src/main/resources/static目录。
2. 执行npm install(首次运行需约3分钟,安装node_modules)。
3. 编辑src/main/resources/static/src/config/api.js,将baseURL改为'http://localhost:8080/api'。
4. 执行npm run serve,等待输出:App running at: - Local: http://localhost:8080/ - Network: http://192.168.1.100:8080/
5. 浏览器访问http://localhost:8080,看到登录页面即成功。
实操心得:如果前端报
Network Error,90%是api.js里的baseURL没改对,或者后端没启动。此时打开浏览器开发者工具的Network标签页,看/api/login请求的状态码——如果是ERR_CONNECTION_REFUSED,说明后端没起来;如果是404,说明baseURL路径错了。
4.2 核心功能演示:以学生选课为例的端到端走查
现在我们用预置的stu001账号,完整走一遍学生选课流程,验证系统闭环:
登录与角色识别:
- 访问http://localhost:8080,输入stu001/123456。
- 登录成功后,前端会把token存入localStorage,并根据返回的role字段("STUDENT")动态渲染导航栏——此时只会显示“我的课表”、“课程搜索”、“个人信息”三个菜单,看不到“课程管理”、“教师管理”等教务专属功能。课表查看:
- 点击“我的课表”,页面加载/student/schedule路由。
- 前端发送GET /api/student/schedule?week=1请求(week=1表示第一周),后端StudentController.getMySchedule()方法查询student_course关联表,拼接course和teacher信息,返回JSON格式课表数据。
- 页面渲染出周一至周五的课表网格,每格显示课程名、教师、教室、节次。注意:v-for循环里用了key="course.id",这是Vue列表渲染的最佳实践,避免复用旧DOM导致数据错乱。课程搜索与选课:
- 点击“课程搜索”,进入/student/course-search页面。
- 输入关键词“数据结构”,点击搜索,调用GET /api/course/search?keyword=数据结构。
- 返回课程列表,找到《数据结构与算法》,点击右侧“选课”按钮。
- 弹出CourseModal.vue,确认课程信息无误后,点击“确定”。
- 前端调用POST /api/student/select-course,传入{studentId: 1, courseId: 5}(假设学生ID为1,课程ID为5)。
- 后端校验通过,插入student_course记录,并更新course.current_enrolled字段加1。
- 前端收到成功响应,关闭弹窗,并触发refresh-schedule事件,课表列表自动刷新,新课程出现在对应时间段。结果验证:
- 切换到“我的课表”,确认《数据结构与算法》已显示在课表中。
- 打开MySQL命令行,执行SELECT * FROM student_course WHERE student_id = 1 AND course_id = 5;,确认记录存在。
- 执行SELECT current_enrolled FROM course WHERE id = 5;,确认人数已加1。
这个流程看似简单,但涵盖了HTTP请求、数据库事务、前后端状态同步、用户界面反馈等全链路环节。答辩时,你可以自信地告诉老师:“我不仅实现了功能,还验证了数据一致性——选课成功后,数据库记录、课表展示、课程人数统计三者完全同步。”
4.3 文档利用指南:如何把配套文档变成你的“作弊码”
docs/目录下的文档不是摆设,而是你写毕设论文、准备答辩的弹药库:
requirements_analysis.md:直接复制粘贴到你的论文“需求分析”章节。它用表格列出了三类角色的功能需求,比如“学生:能查看个人课表、能搜索可选课程、能提交选课申请”,比你自己凭空编写的“用户需要便捷地管理课程”要专业得多。database_design.md:里面的ER图和字段说明,就是你论文“数据库设计”部分的全部内容。重点看“设计说明”小节,它解释了为什么student_course表要设复合主键(student_id + course_id),为什么teacher_course表要加semester字段——这些解释,就是你答辩时应对“为什么这么设计”提问的标准答案。api_documentation.md:这是接口测试的圣经。里面列出了所有API的URL、请求方法、参数、返回示例。你可以用Postman直接导入这个文档(Postman支持Markdown导入),一键发起测试请求。比如测试登录接口,只需在Postman里点一下“Send”,就能看到{"code":200,"token":"xxx"}的响应,比手敲curl命令快十倍。deployment_guide.md:别只当它是部署说明书。它详细记录了每个配置项的作用,比如application.yml里spring.jpa.hibernate.ddl-auto的四个可选值(none/validate/update/create)分别代表什么。你在论文“系统部署”章节写“采用validate模式确保数据库结构安全”,老师一眼就知道你真懂。
最后一个小技巧:把
docs/里的所有图片(ER图、界面截图)直接拖进你的Word论文里。它们分辨率足够高,排版时无需额外处理,还能让论文看起来更“专业”。我指导的学生里,有三人因为论文里插了这套系统的高清界面截图,被答辩组老师当场表扬“材料准备充分”。
5. 常见问题与排查技巧实录:那些让我熬夜改到凌晨的坑
5.1 数据库相关问题速查表
| 问题现象 | 可能原因 | 排查命令 | 解决方案 |
|---|---|---|---|
启动后端时报Access denied for user 'root'@'localhost' | MySQL密码错误或用户权限不足 | mysql -u root -p尝试登录 | 重置root密码:ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_new_password'; |
访问/api/test返回Whitelabel Error Page | 后端未启动成功或端口被占用 | netstat -ano \| findstr :8080 | 结束占用进程:taskkill /PID <PID> /F,或修改application.yml中server.port为8081 |
| 学生登录后课表为空 | student_course表无数据,或class_id外键关联错误 | SELECT * FROM student WHERE id = 1;查看class_id值;SELECT * FROM class WHERE id = <class_id>; | 执行04_insert_users.sql确保学生数据正确;检查student.class_id是否指向存在的class.id |
| 选课成功但课程人数未增加 | course.current_enrolled字段未更新 | SELECT current_enrolled FROM course WHERE id = 5; | 检查CourseService.selectCourse()方法中是否有course.setCurrentEnrolled(course.getCurrentEnrolled() + 1);和courseRepository.save(course);两行 |
5.2 前后端联调典型故障与修复
故障1:前端页面一片空白,控制台报Uncaught SyntaxError: Unexpected token '<'
这是最经典的“前端请求后端接口,后端返回HTML错误页”的表现。根本原因是:前端请求/api/login,但后端没启动,Nginx/Apache返回了404 HTML页面,而前端把它当JSON解析了。
修复:先确认后端是否启动(看IDEA控制台),再检查api.js里的baseURL是否指向正确的后端地址(http://localhost:8080/api,不是http://localhost:8080)。
故障2:登录成功,但导航栏不显示“我的课表”,一直停留在登录页
这通常是Token存储或路由守卫失效。
排查步骤:
1. 打开浏览器开发者工具 → Application → Local Storage,确认token和userRole是否存在;
2. 如果存在,检查router/index.js里/student/schedule路由的meta.roles是否为['STUDENT'];
3. 如果userRole值是"student"(小写),而代码里写的是'STUDENT'(大写),则守卫不匹配。
修复:统一大小写,建议后端返回"STUDENT",前端守卫也用大写。
故障3:选课弹窗点击“确定”无反应,控制台无报错
大概率是CourseModal.vue里的handleSelect()方法没触发。
排查:在方法第一行加console.log('handleSelect triggered');,看控制台是否打印。如果不打印,说明按钮的@click绑定失效。
常见原因:
- 按钮写成了<button @click="handleSelect(course)">(缺少括号),应为<button @click="() => handleSelect(course)">;
-course变量在v-for循环中作用域错误,应写为<div v-for="course in courseList" :key="course.id">,确保course在作用域内。
5.3 毕设论文与答辩的加分技巧
论文图表直接复用:
docs/database_design.md里的ER图,用截图工具(如Snipaste)截取后插入论文,标题写“图3-1 课程管理系统ER图”。比Visio手画的更规范,且ER图里的关系线(1对多、多对多)标注清晰,老师一看就懂。答辩演示设计小技巧:不要一上来就登录演示。先打开MySQL命令行,执行
SELECT COUNT(*) FROM student;,显示“共128名学生”,再登录stu001,进入课表页,指着某门课说:“这门课目前有32人选,我们来演示选课后人数变化”。然后点击选课,再执行SELECT current_enrolled FROM course WHERE name = '数据结构';,显示“33”。这种“数据驱动”的演示,比单纯点按钮高级十倍。应对深度提问的预案:老师可能会问“如果两个学生同时选最后一门课,会不会超员?”。答案是:“会,但系统用数据库行锁保证了最终一致性。当第一个学生选课时,
SELECT ... FOR UPDATE锁住该课程记录,第二个请求会被阻塞,直到第一个事务提交后,再检查剩余名额,从而避免超员。” 这句话里包含了“行锁”、“事务”、“最终一致性”三个关键词,足够展现你的理解深度。
我个人在实际操作中的体会是:这套系统最大的价值,不是它帮你省了多少时间,而是它帮你规避了那些“查三天百度都找不到答案”的诡异Bug。比如
MySQL 8.0的caching_sha2_password认证插件导致连接失败,这种问题在企业开发中很常见,但在毕设阶段,能有一个现成的、已适配好的环境,让你专注在业务逻辑上,本身就是一种奢侈。所以,别急着魔改代码,先把它原汁原味地跑通、吃透、讲明白——这比写出十个炫酷但没人看得懂的功能,更能赢得答辩老师的认可。
本文还有配套的精品资源,点击获取
简介:直接可用的高校课程管理系统毕业设计资源,后端用SpringBoot开发,前端基于Vue实现响应式界面,数据库采用MySQL,预置完整建表语句和初始化数据。系统支持学生、教师、教务管理员三类角色权限,涵盖专业与班级维护、师生信息管理、课程开设、学生选课、选课结果查询等全流程功能。压缩包内含标准Maven结构的源码(含src/main目录及pom.xml)、可一键执行的SQL脚本(含表结构与测试数据)、配套设计文档(含需求说明、ER图、接口列表、部署指南),所有代码已在IntelliJ IDEA验证通过,导入即编译,运行前只需配置数据库连接,适合计算机专业本科生快速完成毕业设计或课程实训项目。
本文还有配套的精品资源,点击获取
