资讯动态

Java学生考勤管理系统课设:Spring Boot + Thymeleaf完整实现与答辩指南

发布时间:2026/10/15 14:34:37 来源:尧图企业网站定制
简介一个面向Java初学者的学生考勤管理系统课程设计项目用于记录出勤、迟到、旷课等课堂考勤信息并支持教师日常考勤管理与数据维护。资源打包为zip压缩包大小约6.21MB适合作为课程设计参考、实验作业模板或二次开发起点。项目内容覆盖Java基础语法与控制结构、面向对象建模包含Student与Attendance等核心类、ArrayList/HashMap集合框架、文件读写与对象序列化以及可选的Swing/JavaFX图形界面和JDBC数据库连接方案同时可扩展多线程并发处理考勤录入场景。通过完成该系统初学者能理解程序从数据输入到持久化存储的完整流程掌握异常处理与模块化拆分技巧获得一份结构清晰、便于调试的实践范本。资源已有1431人学习下载适合作为课堂知识向工程能力过渡的进阶练习。1. Java课设学生考勤管理别把它做成一堆增删改查的代码堆Java课设里「学生考勤管理」这个题目的出现频率极高但真正交上来的成品里十个能有一个做成完整的考勤业务闭环就算不错。多数版本就是一张学生表的增删改查外加一个课程字段统计用手写的 if 判断报表导出干脆不做——这样的课设在答辩时基本是送分题变送命题。这个标题背后的完整诉求其实很明确要有学生档案、课程信息、签到与补录、按周按月的出勤统计、报表导出还要有不同角色的操作边界。它适合正在做 8 到 12 周 Java 课设的人也适合第一次用 Java 写管理系统的初学者。按业务闭环来做这个题目就是一个能演示、能答辩、能写进简历的完整项目。2. 技术选型与数据建模先把表设计对了后面九成代码都是顺水推舟2.1 选型取舍为什么课设优先走 Spring Boot Thymeleaf而不是 Swing 或 JSP学生考勤管理这类系统选型上常见有三条路桌面端的 Swing/JavaFX、老课件的 JSP Servlet JDBC、以及现在主流的 Spring Boot Thymeleaf MyBatis。不少同学一开始选 Swing理由是界面不用部署、双击就能跑但做到统计报表和角色权限时才发现桌面端展示弱图表和导入导出都要额外拼组件临近答辩再转 Web 就成了血泪教训。我的建议是除非课程明确要求桌面端否则优先 Spring Boot 做 Web 管理端。技术形态适合场景课设友好度主要问题Swing / JavaFX 桌面端单机工具、本地演示上手快界面定制繁琐统计报表展示弱简历含金量一般JSP Servlet JDBC老教材配套方案代码量可控视图与服务混在一起改页面要重启前端资源组织落后Spring Boot Thymeleaf MyBatis主流 Web 管理系统生态成熟需要理解 Maven 和依赖管理选 Spring Boot 还有一个实际理由机房和笔记本环境差异大Swing 程序换一台机器可能字体、JDK 版本、数据库驱动全出问题而 Web 项目只要浏览器能开就能演示。依赖方面课设阶段不需要引入太多东西spring-boot-starter-web、spring-boot-starter-thymeleaf、mybatis-spring-boot-starter、mysql-connector-j再加上导出用的poi-ooxml基本就是全部家当。版本上要注意一个匹配问题JDK 1.8 环境用 Spring Boot 2.x 系列JDK 17 环境用 Spring Boot 3.x 系列不要混搭。机房如果还是 JDK 1.8硬上 Boot 3.x 会在编译期就报一堆依赖错误这属于可避免的翻车。2.2 四张核心表学生、课程、考勤记录、用户账号的关系与约束学生考勤管理的表结构很多同学一上来就建一张「考勤总表」把学号、姓名、班级、课程、签到时间、状态全塞进去结果一个学生选多门课、一天多次签到时就彻底失控。正确做法是拆成四张核心表账号表、学生表、课程表、考勤记录表。账号表存登录用的 username、password、role学生表存学号和身份信息课程表存上课时间和节次考勤记录表用 student_id、course_id、attendance_date 三个字段定位「谁在哪天上了哪门课」。四张表的关系是典型的「一对多」一个学生对应多条考勤记录一门课程也对应多条考勤记录。考勤记录表里最重要的约束是唯一索引(student_id, course_id, attendance_date)。意思是一个学生同一门课在同一天只能有一条考勤记录。这个约束是后面防重复、做统计的地基——没有它快速双击提交、多窗口重复操作都会产生脏数据统计人数直接翻倍。外键可以按课设要求加上但物理外键在删除学生时容易锁表我的习惯是逻辑关联 普通索引报告里用 E-R 图说明关系即可。2.3 建表 SQL一条 SQL 把课设数据库初始化跑起来这一步可以直接抄作业把下面 SQL 在 MySQL 里执行四张表加初始数据就齐了。字符集统一用 utf8mb4避免中文乱码排序规则用utf8mb4_unicode_ci。CREATE DATABASE IF NOT EXISTS attendance_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE attendance_db; CREATE TABLE user_account ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL COMMENT 课设阶段可用MD5工程建议BCrypt, real_name VARCHAR(50) NOT NULL, role VARCHAR(20) NOT NULL COMMENT ADMIN/TEACHER/STUDENT, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB COMMENT 账号表; CREATE TABLE student ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL UNIQUE COMMENT 学号, name VARCHAR(50) NOT NULL, gender VARCHAR(10) DEFAULT 男, class_name VARCHAR(50) NOT NULL COMMENT 班级, phone VARCHAR(20), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB COMMENT 学生档案表; CREATE TABLE course ( id BIGINT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(50) NOT NULL, teacher_name VARCHAR(50) NOT NULL, week_day TINYINT NOT NULL COMMENT 1周一, 7周日, start_time TIME NOT NULL, end_time TIME NOT NULL ) ENGINEInnoDB COMMENT 课程表; CREATE TABLE attendance_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL, course_id BIGINT NOT NULL, attendance_date DATE NOT NULL COMMENT 上课日期, check_in_time TIME NULL COMMENT 实际签到时间补录时可为空, attendance_status VARCHAR(12) NOT NULL DEFAULT PENDING COMMENT PRESENT/LATE/ABSENT/LEAVE/PENDING, remark VARCHAR(255), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_student_course_date (student_id, course_id, attendance_date) ) ENGINEInnoDB COMMENT 考勤记录表; INSERT INTO user_account (username, password, real_name, role) VALUES (admin, admin123, 系统管理员, ADMIN), (t001, 123456, 测试教师, TEACHER), (s001, 123456, 测试学生, STUDENT);几个设计点说明状态字段用 VARCHAR 存语义值而不是 0/1/2答辩时一眼能看懂程序里也不用维护一套数字映射日期用 DATE签到时间用 TIME分开存比存一个 DATETIME 更灵活因为补录场景下可能有日期没签到时间。PENDING状态表示「这条记录只是初始化占位老师还没确认」统计时会自动排除掉。如果学校要求必须有物理外键可以在表后追加外键约束但日常开发建议保留唯一索引就够了。3. 考勤核心业务的代码落地从登录鉴权到一次签到请求的完整链路工程结构整齐一点后面写代码会顺手很多。单模块 Spring Boot 工程按分层就够了controller、service、mapper、entity 各自归位模板放 resources/templates启动入口在顶层。这个结构也是面试时最容易讲清楚的分层。src/main/java/com/example/attendance/ ├── AttendanceApplication.java ├── config/WebConfig.java ├── interceptor/AuthInterceptor.java ├── controller/LoginController.java ├── controller/AttendanceController.java ├── controller/studentController.java ├── service/AttendanceService.java ├── entity/Student.java ├── entity/AttendanceRecord.java └── mapper/AttendanceMapper.java src/main/resources/ ├── mapper/AttendanceMapper.xml ├── templates/ │ ├── login.html │ ├── attendance.html │ └── statistics.html └── application.yml3.1 登录与会话权限用拦截器做最简单的角色控制不把课设变成 Spring Security 大杂烩很多同学听说权限控制就用 Spring Security结果引入依赖后要配 UserDetailsService、过滤链、密码加密器课设三分之一时间耗在配置上。实际上考勤管理就三种角色用 Session 加一个拦截器就能讲清楚代码量还不到 Spring Security 配置的一半。我一般会写一个AuthInterceptor在请求进入 Controller 前统一校验登录态。// AuthInterceptor.java public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object role request.getSession().getAttribute(role); if (role null) { // 未登录一律回到登录页 response.sendRedirect(/login); return false; } String uri request.getRequestURI(); if (uri.startsWith(/student/) !ADMIN.equals(role) !STUDENT.equals(role)) { // 学生相关接口只允许管理员和学生本人访问 response.sendError(403); return false; } return true; } }注册拦截器时要注意放行静态资源和登录接口否则 CSS 都加载不出来页面会变成「纯文本灾难」// WebConfig.java Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AuthInterceptor()) .addPathPatterns(/**) .excludePathPatterns(/login, /css/**, /js/**, /images/**); } }登录 Controller 里做一件事校验用户名密码后把 role 和用户 id 放进 Session。后端渲染菜单时根据 role 决定显示哪个操作入口。角色权限映射建议做成一张表答辩时放在 PPT 里非常有说服力。角色可访问页面核心操作ADMIN学生管理、课程管理、考勤管理、统计导出全部TEACHER考勤管理、统计导出录入和修改考勤STUDENT考勤查询查看个人记录3.2 新增考勤记录参数校验与唯一约束双保险让「补录」不产生脏数据教师端考勤页面的常见交互是选课程、选日期、点初始化系统自动把该课程当天所有学生生成一条PENDING记录然后老师逐个人改成出勤、迟到、缺勤或请假。这样做的好处是统计时不会漏人不用去猜「没有记录的学生是不是缺勤」。// AttendanceService.java Override Transactional public void initCourseAttendance(Long courseId, LocalDate date) { ListStudent students studentMapper.findByCourseId(courseId); for (Student s : students) { // 依赖数据库唯一索引已存在的记录不会被重复插入 attendanceMapper.insertIgnore(s.getId(), courseId, date); } }后端接收入参时一定要做校验不能信前端。状态字段必须限制在白名单里日期要能正常解析课程 id 和学生 id 要存在。这里最容易被钻空子的是状态参数如果直接UPDATE ... SET status #{status}传一个DROP之类的非法值虽然不一定会注入但脏数据一定会进库。// AttendanceController.java PostMapping(/attendance/update) ResponseBody public Result updateAttendance(Long recordId, String status, String checkInTime) { if (!Arrays.asList(PRESENT, LATE, ABSENT, LEAVE).contains(status)) { return Result.fail(非法的考勤状态); } attendanceService.updateRecord(recordId, status, checkInTime); return Result.success(考勤已更新); }insertIgnore是这条链路的保险丝它依赖 2.3 建的那条唯一索引如果同一学生同一天同一课程已经有记录这次插入会被 MySQL 静默忽略而不是抛异常。这样即使老师快速点了两次初始化数据也不会翻倍。逻辑说明写在这里insertIgnore只负责防重业务上需要知道「这次到底插没插进去」的时候可以改用ON DUPLICATE KEY UPDATE返回影响行数影响行数为 0 说明是重复记录。3.3 页面请求与时间处理上课时间、签到时间和补录时间如何分清楚考勤系统里最容易让学生懵的是时间字段一张表里至少有三个时间概念课程表里的start_time和end_time表示这堂课什么时候上考勤表里的check_in_time表示学生实际几点签到create_time表示这条考勤记录是什么时候录入的。补录场景下check_in_time可能为空但create_time一定有值因为老师操作的时间是确定的。判断迟到只需要比较check_in_time和课程start_time晚于开始时间就算迟到。但如果学生是提前签到的要允许不能判定为异常。public boolean isLate(LocalTime courseStart, LocalTime checkIn) { if (checkIn null) { return false; // 补录无签到时间不自动判迟到 } return checkIn.isAfter(courseStart); }前端用input typedate和input typetime提交的字符串是 ISO 格式后端直接LocalDate.parse()和LocalTime.parse()就能转不需要手写格式化。Thymeleaf 渲染日期时要注意用#temporals工具类不能在模板里直接toString()否则可能输出一串2024-05-20T00:00之类的长格式。时间处理统一用LocalDate、LocalTime、LocalDateTime不要用Date原因放到第 5 章「日期翻车」那条详细说。4. 从考勤记录到老师要的表格统计口径与 Excel 导出4.1 统计口径出勤、迟到、缺勤、请假怎么算才不会被答辩老师追问统计口径是整个课设最容易被追问的点因为不同老师对「出勤率」的定义不一致。有的认为迟到也算出勤有的认为迟到和缺勤一样严重。我的建议是不要只给一个总数而是展示「出勤率」和「准时率」两个指标让算不算迟到这个问题在数据层面自然化解。状态是否算出勤是否算应到说明PRESENT 出勤是是正常出勤LATE 迟到是是算作出勤但不算准时ABSENT 缺勤否是应到未到LEAVE 请假否是有假条不扣出勤但在应到基数里出勤率公式出勤率 (出勤 迟到) / 应到总数准时率公式准时率 出勤 / 应到总数。PENDING 状态不计入任何一方如果某天还有 PENDING 记录统计页要给出提示让老师回去补录。否则一个班三十人二十条 PENDING统计结果看上去全员迟到这种错误会在答辩时被一眼抓出来。4.2 月度统计与导出一条聚合 SQL 拿到总表再用 POI 生成 xlsx统计 SQL 建议写在 Mapper XML 里因为动态条件、CASE WHEN 聚合在注解里写会乱成一团。下面的 SQL 统计某个时间范围内每个学生的各类考勤次数支持按课程过滤。!-- AttendanceMapper.xml -- select idcountMonthly resultTypemap SELECT s.student_no AS studentNo, s.name AS studentName, s.class_name AS className, SUM(CASE WHEN a.attendance_status PRESENT THEN 1 ELSE 0 END) AS presentCount, SUM(CASE WHEN a.attendance_status LATE THEN 1 ELSE 0 END) AS lateCount, SUM(CASE WHEN a.attendance_status ABSENT THEN 1 ELSE 0 END) AS absentCount, SUM(CASE WHEN a.attendance_status LEAVE THEN 1 ELSE 0 END) AS leaveCount, COUNT(a.id) AS totalCount FROM student s JOIN attendance_record a ON a.student_id s.id JOIN course c ON c.id a.course_id WHERE a.attendance_date BETWEEN #{startDate} AND #{endDate} AND a.attendance_status ! PENDING if testcourseId ! nullAND c.id #{courseId}/if GROUP BY s.id ORDER BY s.student_no /select导出 Excel 用 POI 的 XSSFWorkbook 足够课设数据量一般几百条不需要上 SXSSFWorkbook 的流式写。导出接口要设置Content-Disposition响应头文件名带中文时必须URLEncoder.encode否则浏览器下载下来的文件名是乱码。GetMapping(/attendance/export) public void export(HttpServletResponse response, RequestParam String startDate, RequestParam String endDate, RequestParam(required false) Long courseId) throws Exception { ListMapString, Object rows attendanceMapper.countMonthly(startDate, endDate, courseId); try (XSSFWorkbook workbook new XSSFWorkbook()) { Sheet sheet workbook.createSheet(月考勤统计); String[] headers {学号, 姓名, 班级, 出勤, 迟到, 缺勤, 请假, 总记录}; Row headerRow sheet.createRow(0); for (int i 0; i headers.length; i) { headerRow.createCell(i).setCellValue(headers[i]); } int rowIndex 1; for (MapString, Object row : rows) { Row dataRow sheet.createRow(rowIndex); dataRow.createCell(0).setCellValue((String) row.get(studentNo)); dataRow.createCell(1).setCellValue((String) row.get(studentName)); dataRow.createCell(2).setCellValue((String) row.get(className)); dataRow.createCell(3).setCellValue(((Number) row.get(presentCount)).intValue()); dataRow.createCell(4).setCellValue(((Number) row.get(lateCount)).intValue()); dataRow.createCell(5).setCellValue(((Number) row.get(absentCount)).intValue()); dataRow.createCell(6).setCellValue(((Number) row.get(leaveCount)).intValue()); dataRow.createCell(7).setCellValue(((Number) row.get(totalCount)).intValue()); } for (int i 0; i headers.length; i) { sheet.autoSizeColumn(i); } String fileName URLEncoder.encode(考勤统计_ startDate _ endDate .xlsx, UTF-8); response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setHeader(Content-Disposition, attachment; filename fileName); workbook.write(response.getOutputStream()); } }参数说明autoSizeColumn对中文表头偶尔会估算不准比如「出勤」列宽偏窄可以在导出后手动setColumnWidth兜底。导出接口返回 void 是因为响应内容直接写到 HTTP 响应流不需要再返回 JSON如果同时还要保证导出失败时能提示可以在导出前判断rows.isEmpty()为空时直接返回错误页面避免生成一个只有表头的空 Excel 文件。4.3 报表辅助小功能导出前先预览避免导出「查不到数据」的空白表格导出功能做完了还差一个体验细节统计页面必须先有一个预览表格展示当前筛选条件下查出的数据确认无误后再点导出。很多同学直接做导出按钮下载下来才发现日期范围选反了或者课程没选对Excel 里一片空白。预览表格复用同一个countMonthly查询接口前端模板循环输出导出按钮传同样的参数给后端。这样逻辑只有一套不会出现预览和导出口径不一致的诡异问题。统计页还需要考虑性能边界。课设阶段几十个学生、一学期几十次课数据量很小聚合查询秒回。但如果你在记录表里完全没有索引attendance_date BETWEEN这种范围查询会走全表扫描数据一多页面就开始转圈。所以 2.3 建表时除了唯一索引再给attendance_date单独建一个普通索引是值得的SQL 里就是一行KEY idx_date (attendance_date)但多这行就多一层保障。5. 启动、乱码、日期、重复、404课设最常踩的 5 个坑与排查记录这一段不是我凭空吓唬人而是课设期间最高频的翻车现场。每一条我都按「现象 → 原因 → 解决」的排查顺序写照着走能省下大量 debug 时间。5.1 启动项目就白屏端口占用、MySQL 时区、JDK 版本不匹配现象Spring Boot 项目一启动就报错常见日志是Web server failed to start. Port 8080 was already in use或者The server time zone value ... is unrecognized更隐蔽的是 JDK 17 跑 Spring Boot 2.x 直接编译失败。原因拆开看8080 端口被上一个没关干净的进程占用MySQL 8 的 JDBC 连接串没带serverTimezoneJDK 和 Boot 版本不匹配。解决端口占用用netstat -ano | findstr 8080查到进程号再杀掉或者改server.port换一个端口。连接串统一写成下面的格式spring.datasource.urljdbc:mysql://localhost:3306/attendance_db?serverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf8useSSLfalse spring.datasource.usernameroot spring.datasource.password你的密码JDK 版本问题最省事的办法是保持统一JDK 1.8 用 Spring Boot 2.xJDK 17 用 Spring Boot 3.x不要混搭。这个原则往下走能避开大量玄学报错。5.2 页面上全是问号和乱码characterEncoding 只配了一半现象数据库里的中文正常页面上也正常但导出 Excel 后中文变成了问号或者从数据库读出来中文在控制台打印变成乱码。原因JDBC 连接串里的characterEncodingutf8没加或者页面响应的Content-Type没指定 UTF-8又或者数据库表本身不是 utf8mb4。解决连接串补上characterEncodingutf8HTML 的head里写meta charsetUTF-8application.yml里把模板编码也定死spring: thymeleaf: encoding: UTF-8 cache: false如果是 MySQL 命令行里看中文正常但网页乱码那多半是 JDBC 连接串的问题优先补连接串参数。还有个隐蔽点response.setHeader(Content-Disposition, attachment; filename fileName)时要确保 fileName 已经URLEncoder.encode过否则下载文件名那串中文会变成问号。5.3 日期处理翻车new Date() 存库少了 8 小时统计月份对不上现象数据库里的create_time比本地时间早 8 个小时按月份统计月考勤时月底最后一天的记录跑到了下个月。原因JDBC 连接串没带serverTimezoneAsia/Shanghai时MySQL 用的时区和本机不一致new Date()存入 TIMESTAMP 时发生时区偏移。解决连接串补时区参数实体里的时间字段全部用LocalDate、LocalTime、LocalDateTime不要用java.util.Date。用LocalDate还有一个额外好处前端input[typedate]提交的字符串能直接parse省去SimpleDateFormat的解析异常。SimpleDateFormat 本身线程不安全在并发请求下会出现解析出错误日期的黑匣子问题这也是我坚持用java.time包的原因。5.4 重复点击生成两条考勤唯一索引没建还在代码里手动查一遍现象老师在考勤页面快速双击「初始化」按钮同一天对同一个学生生成了两条考勤记录月度统计人数直接翻倍。原因前端虽然做了按钮禁用但双击请求可能已经发出两次后端代码如果写的是「先SELECT COUNT(*)判断是否存在再INSERT」两个请求同时跑到判断时都认为不存在于是都插入成功。解决数据库唯一索引 INSERT IGNORE双保险。先查后插在并发下必然有漏洞这也是为什么我在第 2 章强调唯一索引是地基。如果以后做真实项目可以用INSERT ... ON DUPLICATE KEY UPDATE一行解决防重和更新两个需求。5.5 Thymeleaf 模板飘 404 或改完没生效模板缓存和资源路径现象Controllerreturn attendance后浏览器 404或者改完 HTML 刷新页面还是旧内容。原因控制器类上写的是RestController返回字符串就被当成 JSON 而不是视图名应该用Controller另一个常见原因是开发期没有关掉 Thymeleaf 缓存改模板不生效。解决返回视图的控制器用Controller模板文件放在src/main/resources/templates/attendance.html返回的字符串是去掉后缀的模板文件名。开发期在application.yml里关缓存spring: thymeleaf: cache: false prefix: classpath:/templates/ suffix: .html注意prefix默认就是classpath:/templates/如果你把模板放到了src/main/resources/static/下面那 Controller 是渲染不到文件的。static 目录只放 CSS、JS 和图片模板必须放 templates。6. 答辩演示与交付让这学期 Java 课设真正落地收尾项目做完距离答辩还有最后一步把演示路径理顺。我的习惯是准备一条 3 分钟的演示脚本按「管理员登录 → 初始化考勤 → 批量修改状态 → 查看月度统计 → 导出 Excel → 学生端查看」的顺序走一遍每一步提前想清楚要点哪里、预期看到什么。演示前把项目打好包避免现场在 IDEA 里等编译。命令行启动方式见下面mvn clean package java -jar target/attendance-0.0.1-SNAPSHOT.jar如果你没有写maven-war-plugin打包成 war 的需求就按 Spring Boot 默认的 jar 包来一个包跑起来最省事。启动后浏览器打开http://localhost:8080用管理员账号登录按脚本演示一遍。这里有一个我踩过很多次的坑演示机如果没启动 MySQL项目启动会直接失败所以脚本第一步永远是「确认 MySQL 服务已启动、数据库已导入」。把这个检查写进 README比现场现查日志靠谱得多。除了演示课设还少不了一份简短的报告。报告里核心放三块四张表的 E-R 图和关系说明、统计口径的定义出勤率和准时率、以及你在开发中实际遇到并解决的问题。第三块别抄网上的常见问题写自己真实踩过的坑比如第 5 章里那几条老师反而会觉得你有工程意识。每次带课设我都会和学生强调一个习惯答辩前至少完整走一遍演示脚本。某届 A 同学就是答辩前十分钟才发现导出按钮报空指针原因是他把临时目录写死成了自己电脑上的路径换一台演示机就崩了。后来我养成习惯把导出目录改成相对路径或直接输出到浏览器下载并且把所有启动依赖写成一份环境清单放在项目根目录。这个习惯也留给你希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑