ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot+Vue卫生健康管理系统前后端分离开发实战指南

SpringBoot+Vue卫生健康管理系统前后端分离开发实战指南 简介一套基于Vue与SpringBoot的卫生健康系统设计完整项目包面向毕业设计、课程设计及期末大作业场景也适合医疗健康信息化方向的学习者与初级开发者参考。项目采用前后端分离架构前端使用Vue技术栈实现用户界面与交互后端基于SpringBoot构建RESTful API覆盖用户认证、健康档案管理、在线问诊、药品信息查询等典型业务功能整体设计兼顾界面友好、数据安全与业务可扩展性。压缩包共850个文件大小约16.77MB包含java、vue、js、html、css等前后端核心源码svg、gif、jpg、png等界面素材以及sql数据库脚本、功能文档、开题报告、任务书和README等说明材料目录按back与front明确划分后端与前端代码结构清晰便于定位模块、二次开发与学习研究。目前已有38人浏览学习。借助配套文档与脚本读者可以快速复现项目运行环境理解从需求分析、数据库设计到前后端联调和安全合规落地的完整流程是一份能直接用于答辩展示和项目提升的完整案例。1. 先把这套卫生健康系统拆开看它替你省掉的不只是写代码这部分如果你正在做基于Vue和SpringBoot的毕业设计、课程设计或期末大作业卫生健康管理系统是个绕不开的高频选题难点通常不在「功能有多复杂」而在「前后端怎么完整串起来」。这套资源默认你只掌握了Java基础和JavaScript基础把SpringBoot后端、Vue前端、数据库脚本、接口调用顺序按可运行的方式排好了。你拿到手不要急着双击运行先对照下文把工程结构和配置项看一遍确认依赖版本对得上再启动。它能解决从「空工程」到「能演示」的整条链路问题适合三类人需要交期末大作业的在校生、想快速搭一套前后端分离项目的初级开发者以及没有时间从零写起的答辩备考生。2. 后端SpringBoot怎么落地三层分包、JWT鉴权与统一返回结构拿到资源后先把后端工程单独拎出来跑一遍不要一上来就打开两个终端。SpringBoot端的代码量通常占整个项目的一半以上先搞清楚它怎么组织后面改接口时才不会到处翻。2.1 项目结构与依赖选择先看懂三层分包再动代码很多毕设的Controller里堆了全部业务逻辑一个方法写完删除用户再写发送通知看起来能跑答辩时老师追问「事务怎么控制」「异常怎么办」就直接卡住。这套资源采用常见的三层结构Controller接收请求、Service处理业务、Mapper访问数据库Entity对应表结构Config放拦截器和跨域配置。拿到压缩包后先看后端的包结构是不是下面这个样子src/main/java/com/example/health ├── config # 拦截器、跨域、静态资源映射 ├── controller # 接收HTTP请求只做参数校验和返回 ├── service # 业务逻辑事务注解放在这一层 ├── mapper # 继承BaseMapper不需要手写SQL ├── entity # 和数据库表字段一一对应 └── common # Result统一返回、JWT工具、全局异常处理这个分包顺序是按依赖方向排的Controller依赖ServiceService依赖MapperEntity贯穿三层。改需求时遵循这个方向往下找就行不用整个工程翻一遍。一个比较重要的经验是SpringBoot版本别追太高很多老代码用的还是javax下的一系列包SpringBoot 3.0之后改成Jakarta命名空间如果资源本身不是按新版本写的你强行换到新版本会让编译直接失败光是import语句就要改几十处。2.2 核心依赖就这几样pom文件里不要堆料后端pom.xml里真正影响功能的依赖通常不超过五个。SpringBoot的Parent已经锁定了大多数版本子模块里不需要再写version所以看到pom里写一堆自己指定的版本号反而要警惕那通常是网上复制来的版本冲突概率很大。常见保留的是下面这些!-- pom.xml 关键依赖片段 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency说下我的习惯SpringBoot选2.7.x这个系列配合MyBatis-Plus 3.5.x兼容性最稳。MyBatis-Plus的作用是让单表增删改查不用写XMLMapper接口直接继承BaseMapper就有现成的selectPage、selectById这些方法对课设来说省掉大量SQL编写时间。Lombok提供Data注解Entity里只用写字段名getter/setter自动生成。如果项目里还引入了其他中间件依赖多半用不到删掉能减少启动报错。JWT功能记得在pom里补上jjwt-api、jjwt-impl、jjwt-jackson三件套impl和jackson的作用域设为runtime它们只在运行期需要。参数说明mysql-connector-java的runtime作用域表示只在运行阶段需要打包时不会强制校验Lombok需要IDE装插件否则编译报错找不到getter方法这一点在换电脑跑项目时最容易翻车。提示Lombok如果出现编译报错先检查IDE是否装了插件再检查pom里是不是本地仓库有残留旧版本这两个问题占了换环境后报错的大半。2.3 登录鉴权JWT生成、拦截器与放行路径卫生健康系统里有患者信息、体检记录登录鉴权基本不会缺席这类毕设资源一般用JWT配合登录拦截器。前端登录成功后拿到token后续请求在请求头里带上Authorization字段后端通过拦截器验证。核心工具类长这样// JwtUtil.java 简化版 public class JwtUtil { // 实际项目里密钥应该放application.yml不要硬编码 private static final String SECRET health-system-secret; private static final long EXPIRE 7 * 24 * 60 * 60 * 1000L; public static String createToken(Integer userId, String username) { return Jwts.builder() .setSubject(username) .claim(userId, userId) .setExpiration(new Date(System.currentTimeMillis() EXPIRE)) .signWith(SignatureAlgorithm.HS256, SECRET) .compact(); } public static boolean verify(String token) { try { Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token); return true; } catch (Exception e) { return false; } } }这里有一个比较容易忽略的点token有效期设置成7天适合日常演示但答辩前一天你用演示账号登录过第二天数据过期后接口会全部401现场重新登录又显得手忙脚乱。所以我一般会把EXPIRE调成一个更长的值或者干脆在答辩演示前重新登录一次刷新token。拦截器负责验证请求头注册时要把登录和注册接口放行// WebConfig.java Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns(/api/**) .excludePathPatterns(/api/user/login, /api/user/register); } }放行路径是必查项我见过很多项目启动后接口全部401最后发现是拦截器把登录接口也拦了前端拿不到token死循环跳回登录页。LoginInterceptor里的preHandle方法逻辑很简单取请求头Authorization校验token校验失败返回401。public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { return true; } String token request.getHeader(Authorization); if (token null || !JwtUtil.verify(token)) { response.setStatus(401); return false; } request.setAttribute(userId, JwtUtil.getUserId(token)); return true; } }需要说明的是这里校验的是token能否解析、是否过期并不代表用户一定有权限访问某个接口。如果要做角色权限区分可以在token里再加一个role字段在拦截器里判断角色或者结合注解控制。对于课程设计来说拦截器做到登录拦截这一层基本够用。2.4 统一返回结构让前后端对得上话后端接口返回格式如果不统一前端每个页面都要写一遍判断逻辑。这类项目的common包下一般有一个Result类结构是code、msg、data三件套成功code为200失败code为401或500。改造或新增接口时所有Controller都返回Result而不是裸数据前端axios拦截器只认这个结构写起来省事很多。// Result.java 核心逻辑 Data public class ResultT { private int code; private String msg; private T data; public static T ResultT ok(T data) { ResultT r new Result(); r.code 200; r.msg success; r.data data; return r; } public static T ResultT error(int code, String msg) { ResultT r new Result(); r.code code; r.msg msg; return r; } }健康档案的分页接口作为参考RequestParam控制页码和每页条数RequestBody接收JSON对象RestController RequestMapping(/api/health) public class HealthRecordController { Autowired private HealthRecordService healthRecordService; GetMapping(/page) public ResultIPageHealthRecord page( RequestParam(defaultValue 1) long current, RequestParam(defaultValue 10) long size, RequestParam(required false) String keyword) { return Result.ok(healthRecordService.queryPage(current, size, keyword)); } PostMapping public ResultBoolean add(RequestBody HealthRecord record) { return Result.ok(healthRecordService.save(record)); } }分页参数里defaultValue的意思是前端不传时默认取第1页、每页10条required false表示keyword可以省略。Service里的queryPage用MyBatis-Plus的LambdaQueryWrapper做模糊查询即可不用手写limit语句。注意IPage这个泛型类型分页查询返回的是记录列表和总数两样东西前端在分页组件里要拿到的是records和total两个字段。3. 前端Vue侧怎么搭路由守卫、状态缓存与axios请求封装后端能独立跑通后再启动前端。Vue这边的内容看起来比后端多但实际只有一个核心逻辑页面跳转时怎么带token、请求接口时怎么带token。搞清楚这两条线前端项目就能改得动。3.1 工程初始化与目录规划脚手架和依赖装到哪一步算完前端部分常见的是Vue3 Vite Element Plus的组合如果你拿到的源码已经是这个组合node版本建议用16以上npm install之前先删掉旧的node_modules和package-lock.json避免本机环境拉出新冲突。初始化命令以Vite为例npm create vitelatest frontend -- --template vue cd frontend npm install npm install vue-router pinia axios element-plus这几条命令分别做三件事创建Vue3项目骨架、安装基础依赖、安装路由/状态管理/请求库/UI组件库。Vite创建的项目默认没有vue-router和pinia需要手动加。装完后把src下的目录整理成下面这种结构方便按模块找人frontend/src ├── api # 每个后端模块的请求函数 ├── router # 路由配置与守卫 ├── store # pinia状态存用户信息 ├── views # 页面按业务模块分文件夹 ├── components # 公共组件 ├── App.vue └── main.jsapi目录单独拆出来是我一直坚持的习惯。很多人直接把axios请求写在.vue文件里页面一多就会看到十处重复的request配置改一次baseURL要全局搜索。3.2 路由与动态侧边栏从登录态到菜单渲染路由配置里登录页和主页是两套布局主页下面挂仪表盘、健康档案、用户管理等子路由。懒加载用箭头函数返回import这样首屏不会一次性加载全部页面。路由守卫是这套系统的关键点没有token就跳登录页。// router/index.js const routes [ { path: /login, component: () import(../views/Login.vue) }, { path: /, component: () import(../layout/Index.vue), redirect: /dashboard, children: [ { path: dashboard, component: () import(../views/Dashboard.vue), meta: { title: 数据看板 } }, { path: health-records, component: () import(../views/HealthRecords.vue), meta: { title: 健康档案 } }, { path: users, component: () import(../views/UserManage.vue), meta: { title: 用户管理 } } ] } ] router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.path ! /login !token) { next(/login) } else { next() } })meta里的title字段在侧边栏渲染时直接取来当菜单名也可以配合动态路由实现不同角色显示不同菜单。毕设场景里我建议先把固定路由跑通动态路由作为加分项因为动态路由涉及的权限过滤逻辑会让调试时间翻倍。如果后端返回的菜单数据和前端路由字段对不上最常见的就是字段名不一致比如后端给的是menuName前端取的是title这种问题定位时先对JSON字段不要急着改代码。对于用Pinia还是用localStorage存用户信息我的建议是token放localStorage因为刷新页面不能丢用户详情放Pinia因为刷新后可以重新拉取。不要把所有用户信息都塞进localStorage演示时评委可能会点开开发者工具看存储密码字段明文暴露在那里观感很差。3.3 axios请求封装统一处理token、错误码和401跳转前端与后端对接只有一个入口就是src/api/request.js这个文件。统一在这里做三件事给每个请求带上token、处理业务错误码、遇到401跳回登录页。// api/request.js import axios from axios import { ElMessage } from element-plus import router from ../router const request axios.create({ baseURL: /api, timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }) request.interceptors.response.use( (res) { if (res.data.code 200) { return res.data } ElMessage.error(res.data.msg || 请求失败) return Promise.reject(new Error(res.data.msg)) }, (err) { if (err.response err.response.status 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(err) } ) export default request这里重点说明每部分的取舍。baseURL写成/api开发环境下由Vite代理转发到后端生产环境下后端接口本身就是/api开头这份配置不用改。拦截器里只用状态码判断有个隐患部分后端会把业务失败也返回200状态码仅仅在body里把code改成500所以response拦截器里先判断业务code再抛错。401处理放在错误回调里因为HTTP层面的401不会被成功回调捕获。页面里调用时按模块写api文件例如健康档案的接口// api/health.js import request from ./request export const getHealthPage (params) request.get(/health/page, { params }) export const addHealthRecord (data) request.post(/health, data)params和data的区别要注意params会拼到URL查询字符串里适合分页关键字data是JSON请求体适合新增和更新。传错位置会导致后端收不到参数接口报400这类问题在浏览器Network面板里一眼就能看出来。3.4 页面里的表格与表单分页组件和校验规则怎么绑健康档案列表页一般是el-table配合el-pagination分页组件触发change事件时重新调接口。参考写法el-pagination v-model:current-pagequery.current v-model:page-sizequery.size layouttotal, sizes, prev, pager, next :totaltotal size-changeloadData current-changeloadData /const query reactive({ current: 1, size: 10, keyword: }) async function loadData() { const res await getHealthPage(query) records.value res.data.records total.value res.data.total }v-model:current-page和v-model:page-size是Element Plus 2.x的双向绑定写法改页码后query里的值自动更新然后手动调用loadData。新手容易漏掉size-change事件导致切换每页条数后数据不刷新。表单提交时用el-form的rules校验血压字段可以用正则限制格式要求填成120/80这种写法避免录入时格式混乱。4. 前后端联调与打包部署代理转发、跨域与把Vue塞进SpringBoot很多项目本地启动没问题一到打包部署就崩问题基本都出在「前端请求地址」和「后端静态资源」这两件事上。下面按部署顺序把三道坎一次过完。4.1 开发环境的跨域与代理vite.config里配好就不用法后端CORS前后端分离开发时前端的5173端口和后端的8080端口不同源浏览器直接请求会被CORS策略拦掉。处理方式有两种后端加CORS跨域配置或者让Vite开发服务器做代理转发。我建议用代理这样生产环境前端打包后走同源跨域配置完全不需要改动最少。// vite.config.js import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })proxy的作用是当浏览器访问http://localhost:5173/api/health/page时Vite会把请求转发到http://localhost:8080/api/health/page。changeOrigin设置为true后端拿到的Host就是8080避免部分后端框架基于Host做校验时误判。如果后端改了端口只改这里的target就行前端代码里的baseURL不用动。有些系统在后端已经配置了CorsFilter开发环境两边都生效不会冲突但要多注意OPTIONS请求。后端如果没放行OPTIONS浏览器预检会失败表现为接口在Postman里正常、浏览器里报跨域。排查时先看Network面板里有没有一条灰色的OPTIONS请求有就要在后端放行。4.2 生产环境打包dist目录和SpringBoot的static怎么结合毕设答辩最常见的部署方式不是把前端单独放在Nginx上而是把Vue打包成静态文件塞进SpringBoot的resources/static目录打成一个jar包直接运行。先构建前端npm run buildVite默认把产物输出到dist目录里面有index.html和assets文件夹。把dist里的内容整体拷贝到backend/src/main/resources/static下然后正常打包后端cp -r dist/* ../backend/src/main/resources/static/ cd ../backend mvn clean package -DskipTests java -jar target/*.jarWindows下没有cp命令直接打开两个资源管理器窗口拖过去就行效果一样。注意拷贝的是dist里面的内容不是把dist文件夹本身放进去否则访问路径会变成/static/dist/index.html白白多一层目录。这里有一个关键配置vite.config.js里的base。默认值是/如果你的jar包在根路径部署没问题如果放到服务器的子路径下比如/sys/静态资源路径全部会404。我现在习惯在构建时把base设置成相对路径export default defineConfig({ base: ./, })设置成./之后index.html里引用的js和css路径会变成./assets/xxx.js无论放在哪个子目录都能正确找到。代价是vue-router如果用了history模式直接刷新会有路由问题这个冲突在第5章避坑部分展开。打包完成后前端请求的/api路径和后端接口同源没有跨域问题axios的baseURL不用改。整个jar包双击或者java -jar启动后打开http://localhost:8080就能看到前端页面。4.3 数据库初始化SQL脚本和字段设计的三个坑后端跑起来之前数据库要先准备好。资源包里如果带.sql文件优先用这个文件建库不要手工在Navicat里重建表字段类型和注释很容易对不上。健康类系统里至少有两张核心表用户表和健康档案表。用户表除了账号密码通常还有角色字段区分管理员和普通用户健康档案表存血压、心率、身高体重、检查日期。CREATE TABLE health_user ( id bigint NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL, password varchar(100) NOT NULL, real_name varchar(50) DEFAULT NULL, role tinyint DEFAULT 1 COMMENT 1-普通用户 2-管理员, created_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE health_record ( id bigint NOT NULL AUTO_INCREMENT, user_id bigint NOT NULL, blood_pressure varchar(32) DEFAULT NULL COMMENT 收缩压/舒张压, heart_rate int DEFAULT NULL, height decimal(5,2) DEFAULT NULL, weight decimal(5,2) DEFAULT NULL, check_date date DEFAULT NULL, deleted tinyint DEFAULT 0, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;三个容易踩的坑。第一密码字段长度至少要100因为登录里的密码通常不是明文存储会经过BCrypt加密加密结果长度在60位以上varchar(50)直接存不下。第二字符集一定要用utf8mb4否则录入生僻字或者某些特殊符号时数据库会报错utf8mb4是utf8的超集兼容性更好。第三逻辑删除字段deleted配成tinyint默认0就行MyBatis-Plus的TableLogic注解配合这个字段删除操作自动变成update保留数据便于答辩时演示「数据还在」。数据库连接串里的时区参数也要注意完整写法是jdbc:mysql://localhost:3306/health?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai缺了serverTimezone在最新版驱动下会报时区错误。提示application.yml修改后如果始终不生效优先检查启动类所在的包和配置文件位置SpringBoot默认只读classpath根目录的配置配置文件放错目录时会静默加载不到。5. 避坑与常见问题答辩前最容易翻车的五个场景这部分是我拆过多个类似项目后整理的高频故障按出现概率排序。每一条都是「现象——原因——解决」的结构你只要按顺序排查大多数问题能在十分钟内定位。5.1 登录接口报401拦截器把登录请求也拦了现象前端输入正确账号密码点击登录后端日志打印访问/api/user/login响应状态码401页面一直停留在登录页。原因WebConfig里的addInterceptors配置了addPathPatterns(/api/**)但excludePathPatterns里没有把登录接口写进去或者写的是/user/login而实际请求路径是/api/user/login匹配不上。解决检查excludePathPatterns里的路径必须和Controller里的完整路径一致写成/api/user/login。改完重启后端再试如果还有401在LoginInterceptor的preHandle第一行临时输出handler.toString()看看拦截的到底是哪个方法。顺带检查前端是否在登录请求里带了过期的Authorization头登录接口本身应该是不带token的。5.2 前端项目发给同学跑不起来node_modules和本地配置文件的问题现象把源码压缩包发给别人对方npm install之后npm run dev报错缺包或者报语法错误你本机却一切正常。原因第一node_modules目录没删干净就压缩传到别人电脑上后某些平台相关的二进制包失效第二项目里的.env.development文件被.gitignore忽略掉了文件根本没进压缩包前端拿不到后端地址。解决重新压缩前先删掉node_modules、dist这两个目录让接收方自己执行npm install。打包时手动勾选包含.env开头的文件或者把后端地址直接写在vite.config.js的proxy target里不依赖环境变量。代码里如果出现import路径对不上多半是文件被误删让接收方把报错信息里的路径和src目录结构对一下。5.3 数据库连接失败Access denied或者connect refused现象后端启动时控制台打印Access denied for user rootlocalhost或者Communications link failure有时是在启动几分钟后才报异常。原因前者是账号密码错误常见于SQL脚本里创建的密码和application.yml里配置的密码不一致后者是MySQL没启动或连接串里的端口不是3306。解决先确认MySQL服务已启动再核对application.yml里的url、username、password三项。注意application.yml里如果有多个profile比如application-dev.yml和application-prod.yml启动参数里没激活对应profile时SpringBoot读的是默认配置你改的文件可能根本没生效。在启动命令或IDE里加--spring.profiles.activedev或者直接改主配置。检查驱动依赖的groupId新版本驱动类名是com.mysql.cj.jdbc.Driver老配置里写com.mysql.jdbc.Driver在新版本下会报错。5.4 后端端口被占用Address already in use现象后端启动直接抛BindException提示端口8080被占用但你觉得没开过其他程序。原因上一次运行的后端jar没有关闭或者IDEA残留了旧进程也可能是电脑上其他开发工具占用了8080。解决Windows下用netstat -ano | findstr 8080拿到PID再在任务管理器结束对应进程Mac和Linux下用lsof -i :8080。如果端口经常冲突干脆把server.port改成8081前端vite代理的target同步改掉就行不到一分钟的事。这里顺带说一句如果改的是application-dev.yml但没激活dev环境端口始终不会变这和第5.3条是一个原因。5.5 打包部署后刷新页面404history路由和SpringBoot路由冲突现象前端打包放进static后打开首页正常进入子页面再按F5刷新直接404而前端单独跑dev模式时刷新正常。原因vue-router用了createWebHistory模式URL是真实的/path路径刷新时浏览器向SpringBoot请求这个路径SpringBoot找不到对应的Controller就返回404。在Vite开发环境里dev服务器会把这些路径自动回退到index.html所以开发时发现不了。解决两个方向。一是在SpringBoot里加一个转发兜底把非/api开头的路径全部转发到index.html二是vue-router改用createWebHashHistoryURL变成/#/health-records形式。毕设场景我推荐用hash模式改动最小不用动后端代价是URL里带个#部分评委不太喜欢但功能上完全没问题。如果一定要保留history模式后端加一个配置类// WebConfig.java 追加 Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController(/{path:[^\\.]*}) .setViewName(forward:/index.html); }这个配置的作用是所有不含点号的路径请求都转发到index.htmlapi开头的请求因为有Controller处理不会走到这里静态资源如css/js带点号也不会误转发。注意正则里的.两面都要写少一个转义符号整个匹配都会失效。6. 进阶与验证把系统从「能跑」升级到「经得起追问」基础功能调通后还有两件事决定这个项目在答辩里的上限数据可视化做得够不够直观以及你对自己系统的边界是否清楚。下面给一个加分项和一套验收清单。6.1 数据可视化用ECharts画近30天血压趋势卫生健康系统如果只有表格增删改查功能会显得单薄。在后端加一个统计接口返回近30天每天的血压平均值前端用ECharts折线图展示是成本最低的加分项。前端部分可以这样接// Dashboard.vue 折线图核心代码 import * as echarts from echarts import { getBloodPressureTrend } from ../api/dashboard const chart echarts.init(document.getElementById(bpChart)) const res await getBloodPressureTrend() chart.setOption({ xAxis: { type: category, data: res.data.dates }, yAxis: { type: value, name: mmHg }, series: [ { name: 收缩压, type: line, data: res.data.systolic }, { name: 舒张压, type: line, data: res.data.diastolic } ] })图表的容器div要显式设置宽高因为ECharts初始化时拿不到零尺寸的容器这是新手最容易出现的白屏问题。折线图配上tooltip和legend演示时鼠标悬停能看到每天的具体数值讲数据时的说服力比干列表强很多。这个模块适合放首页打开系统第一眼就是可视化效果。6.2 答辩前走一遍验收清单我拆过不少课程设计项目发现真正让评审扣分的往往不是功能缺失而是演示顺序混乱。按下面这个顺序过一遍基本能覆盖所有提问点登录模块先演示错误密码的提示再演示正确登录说明token怎么存、怎么过期。 用户管理演示新增用户、给用户分配角色说明删除用的逻辑删除数据没真删。 健康档案演示分页查询、关键字搜索、新增一条记录后列表回到第一页讲清分页的current和size参数。 数据看板演示图表随数据变化说明统计接口的查询口径。 打包部署演示改一个前端标题重新build后放进staticjar包启动后访问验证。6.3 一次演示事故教会我的事最后分享一个我自己的教训。毕业季帮学弟排错时他演示前十分钟改了数据库里一条记录演示时数据看板上的统计数字全部大变样老师追问「为什么和论文里写的对不上」他当场愣住。从那以后我碰任何给演示用的项目都会先把数据库导出成初始化SQL演示前重新执行一遍保证数据和论文截图完全一致整个流程强制走三遍清库复原、按验收清单点一遍、再打包一次。这套流程看着笨但确实救过我多次。希望帮到你。本文还有配套的精品资源点击获取
返回列表