ARTICLE DETAIL

资讯详情

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

SpringBoot+MyBatis-Plus+Vue3全栈疾病防控系统实战解析

SpringBoot+MyBatis-Plus+Vue3全栈疾病防控系统实战解析 最近在帮人调试一个疾病防控综合系统前端Vue3Element Plus后端SpringBoot2MyBatis-Plus数据库MySQL8.0前后端分离。这套组合几乎是目前毕业设计和中小型项目里的标准答案SpringBoot2负责业务接口MyBatis-Plus把繁琐的SQL简化到令人舒适Vue3的组合式API让页面状态管理比Vue2时代清爽得多MySQL8.0则在JSON支持、窗口函数和稳定性上都比5.7有明显进步。这个系统本质上是给疾病预防控制中心、社区卫生服务站这类场景用的信息管理平台。核心围绕传染病登记、病例随访、密接管理、疫苗接种记录、健康申报统计这几条业务线展开。你拿到源码后会发现它不复杂但涉及的技术面很全非常合适用来串一遍Java全栈知识也能直接作为毕业设计的底子改造。这篇就按我实际调试和二次开发的经验把整个系统的架构、表设计、核心代码思路、部署过程和踩过的坑一次讲清楚。1. 项目拆解疾控业务如何落到功能模块上1.1 需求拆解从业务场景到功能清单我拿到这个项目第一件事不是看代码是先对照需求理清业务边界。疾病防控系统听起来大落到实际场景里就是这几件事第一基础信息管理。系统得有用户体系但用户的角色不是单一的。疾控中心管理员、医院填报医生、社区卫生站的随访人员、普通居民这些角色看到的页面和可操作的范围完全不同。所以权限模型不能只做一个用户表加密码必须带角色和菜单权限。第二传染病登记与上报。医生发现疑似或确诊传染病病例后需要在系统里建卡登记内容包括患者基本信息、发病日期、诊断类型、报告单位、报告时间。这个动作在实际疾控工作中叫报卡是整个系统的核心操作必须支持新增、修改、审核、查询列表。第三病例随访与密接管理。登记不是终点确诊后需要持续跟进患者健康状况每天记录体温、症状、用药情况。同时要登记密切接触者做隔离提醒和健康监测。这对应到功能上就是随访记录表和密接管理表两张表都跟病例表关联。第四疫苗接种记录管理。疫苗接种是防控中最前置、最有效的手段系统里要维护接种点信息、疫苗批次、接种人记录、接种剂次。这块业务方便后面做覆盖率统计。第五统计与可视化。管理员最关心的不是单条数据而是整体趋势比如每日新增病例数、各区域感染分布、疫苗接种覆盖率。系统需要提供统计接口前端配合可视化图表展示。把业务梳理完功能模块就自然出来了系统管理用户、角色、菜单、病例信息管理、案例随访管理、密接管理、疫苗管理、统计仪表盘。这也是项目里数据表和前后端页面的划分依据。1.2 整体架构设计前后端分离是怎么落地的这个项目采用的是标准的Vue3 SpringBoot2前后端分离架构。前端项目独立运行在Nginx或者Node开发的Dev Server上后端跑在Tomcat内嵌容器里双方通过HTTP协议下的RESTful API通信。前后端分离带来的好处在开发阶段和运维阶段都能感受到。开发阶段前后端可以并行开发前端用Mock数据先调页面后端用Postman测接口两边互不阻塞。部署阶段前端打包成静态文件扔到Nginx里后端单独跑一个Java进程哪个挂了单独重启不至于整站不可用。数据流向是这样的Vue3页面里用户点击按钮触发API请求Axios把请求发到后端的Controller层Controller做参数校验和权限校验然后调用Service层处理业务逻辑Service通过MyBatis-Plus提供的Mapper接口操作MySQL8.0里的数据表结果再逐层返回。说句实在话这套链路本身没什么新奇的地方但这个项目好就好在链路完整从登录鉴权到增删改查再到统计报表该有的环节一个没少很适合用来建立全局观。1.3 技术选型为什么是这四个组件先说SpringBoot2。选它不选SpringBoot3的原因很现实社区生态和资料数量差太多。SpringBoot2.7.x是目前最稳定的版本网上遇到问题基本都有现成答案同时它也完全够用腾讯云、阿里云的Java应用里大量还在跑2.x。这个项目的依赖管理走的是MavenSpringBoot2.7父级POM进来自动管理版本不用自己操心依赖冲突。Vue3相比Vue2最大的变化是组合式API。以前Vue2是data、methods、computed分开写一个页面逻辑复杂后代码会散得到处都是。Vue3的setup语法把数据、方法、生命周期钩子聚在一起同一个业务的一块逻辑可以封装成自定义hook复用起来非常舒服。加上Teleport、Fragment、Suspense这些新特性写复杂表单和弹窗组件时会感觉明显顺手。MyBatis-Plus真的是把MyBatis的缺点补上了。传统MyBatis几乎每张表都要手写XML和SQL单表增删改查的机械劳动量很大。MyBatis-Plus内置了BaseMapper提供了selectById、insert、updateById、selectPage这些通用方法单表操作基本不用写SQL。它的条件构造器LambdaQueryWrapper也很有意思查询条件用Java链式调用比如lambdaQuery().eq(User::getStatus, 1).list()不用拼接字符串从根本上避免了一大票SQL注入风险。MySQL8.0在5.7基础上增强了JSON类型和窗口函数等能力而且默认的utf8mb4字符集让我这种对中文乱码有心理阴影的人很安心。8.0的安装和账户权限管理也比5.7完善本地开发用Docker起一个8.0实例几分钟就搞定。2. 数据库设计与表结构这些坑一定要避开2.1 核心表设计从用户到健康申报的全链路这个项目的数据库设计是典型的业务驱动型。我把它拆成四条线来看用户权限线、病例业务线、疫苗业务线、申报统计线。用户权限线包含三张表sys_user、sys_role、sys_user_role。sys_user里除了username和password还存了real_name、phone、status字段。password存储不是明文而是用了BCrypt加密的哈希串后端登录时用BCrypt.matches进行校验这也是SpringSecurity生态里默认推荐的密码加密方案。sys_role表只有几个预设角色用户和角色通过中间表关联方便后面做RBAC权限控制。病例业务线是系统的核心主要有disease_case、case_follow_up、close_contact三张表。disease_case是最重要的一张表字段包括patient_name、id_card、gender、age、phone、address、onset_date、diagnosis_date、disease_type、report_unit、report_user、case_status。case_status这个字段承载了业务状态流转比如待审核、已确诊、已排除、已治愈不同状态对应不同列表页签和操作按钮。case_follow_up表记录每次随访的内容字段有case_id、follow_date、body_temp、symptom_desc、health_status、follow_up_doctor。close_contact表记录密接人员字段包括case_id、contact_name、contact_phone、contact_address、contact_type、isolation_status、health_status。疫苗业务线包含vaccine_info、vaccination_record两张表。vaccine_info存疫苗批次、生产厂家、有效期vaccination_record存居民接种明细字段有person_name、id_card、vaccine_id、dose_number、vaccinate_date、vaccinate_site。健康申报表health_report是给居民日常上报体温和健康状况用的字段有report_date、body_temp、symptom_desc、contact_history、report_user_id。这张表和统计接口配合可以直接查出每天的申报人数和异常人数。2.2 表关系与设计要点表关系其实不复杂。disease_case是一切的源头case_follow_up通过case_id关联它close_contact也通过case_id关联它形成一对多关系。vaccination_record通过vaccine_id关联疫苗信息表。sys_user_role是用户和角色的多对多中间表。health_report通过report_user_id关联sys_user记录是谁汇报的。几点设计经验分享下。第一所有业务表都要有id主键、create_time、update_time这些公共字段。这个项目统一了字段命名然后配合MyBatis-Plus的MetaObjectHandler做字段自动填充插入和更新时不用手动给这几个字段赋值省不少事。第二状态字段一定要用数字或者短字符串不要用中文。比如case_status用0代表待审核、1代表已确诊前端用枚举映射成中文标签。存中文在数据库里后续改状态逻辑和统计都会很难受。第三身份证号、手机号这类查询频繁的字段要建索引否则数据量上来后按身份证查一个人的接种记录会全表扫描性能会很糟糕。这个项目数据库还有一个值得一提的点初始化脚本里预置了完整的菜单表数据和几个测试账号。我拿到源码后直接导入SQL启动后端连上数据库就有数据可看不用自己手动构造。对于做毕设的同学来说这种细节真的能节省大量时间。2.3 MySQL8.0的连接配置与常见参数用MySQL8.0的时候JDBC连接串跟5.7时代有点不一样。首先是驱动类变成了com.mysql.cj.jdbc.Driver其次连接串需要带serverTimezoneAsia/Shanghai参数否则会报Server returns invalid timezone的错误。8.0默认的认证插件是caching_sha2_password老版本的驱动连不上所以pom里的mysql-connector-java版本不能太低至少8.0.x。我实际使用的连接串长这样spring.datasource.urljdbc:mysql://localhost:3306/disease_control?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue spring.datasource.usernameroot spring.datasource.password你的密码 spring.datasource.driver-class-namecom.mysql.cj.jdbc.DriveruseSSLfalse是本地开发必备不然每次连接都会弹出SSL握手警告。allowPublicKeyRetrievaltrue是为了解决8.0的RSA公钥明文传输问题否则第一次连接可能报Public Key Retrieval is not allowed。MySQL8.0安装方面我推荐用Docker容器跑避免本机卸载不干净导致的后患docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD123456 \ -e MYSQL_DATABASEdisease_control \ -e TZAsia/Shanghai \ -v /data/mysql:/var/lib/mysql \ mysql:8.0.36启动后用docker exec -it mysql8 mysql -uroot -p进入容器把项目里的sql/disease_control.sql导入进去数据库这步就算完成了。注意Docker容器里的MySQL默认字符集可能不是utf8mb4如果发现导入后中文乱码可以在启动参数里加--character-set-serverutf8mb4 --collation-serverutf8mb4_unicode_ci。3. 后端实现SpringBoot2 MyBatis-Plus 的核心代码3.1 项目骨架与Maven依赖配置后端项目结构是标准的Maven单模块布局controller、service、mapper、entity、config、common几个包。启动类就一个DiseaseControlApplication标了SpringBootApplication没做多余的花活。pom.xml里有几个关键依赖值得看下。SpringBoot父POM用的是2.7.18版本MyBatis-Plus用的是3.5.3.x代码生成器用的是mybatis-plus-generator。此外还有fastjson2或Jackson做JSON序列化lombok简化实体类jwt做登录令牌。parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency配置文件的命名我也建议延续SpringBoot的风格用application.yml。端口、数据源、MyBatis-Plus逻辑删除配置、日志级别都放在里面。MyBatis-Plus的配置要注意map-underscore-to-camel-case默认就是true所以数据库的create_time能自动映射到实体的createTime字段不用在XML里费劲写resultMap。3.2 MyBatis-Plus快速开发实体、Mapper到条件构造器实体类用lombok写非常简洁。以disease_case表为例Data TableName(disease_case) public class DiseaseCase { TableId(type IdType.AUTO) private Long id; private String patientName; private String idCard; private Integer gender; private Integer age; private String phone; private String address; private LocalDate onsetDate; private LocalDate diagnosisDate; private String diseaseType; private String reportUnit; private String reportUser; private String caseStatus; TableField(fill FieldFill.INSERT) private LocalDateTime createTime; TableField(fill FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; }TableName指定表名TableId标记主键并指定自增TableField用来描述字段填充策略。实体写好后Mapper接口就三行public interface DiseaseCaseMapper extends BaseMapperDiseaseCase { }ServiceImpl继承ServiceImplDiseaseCaseMapper, DiseaseCase再顺手实现一个自定义的接口。这样一来单表的增删改查、分页查询全部白送。比如列表页要按病种和状态筛选用LambdaQueryWrapper写起来非常直观LambdaQueryWrapperDiseaseCase wrapper Wrappers.lambdaQuery(); wrapper.like(StringUtils.hasText(keyword), DiseaseCase::getPatientName, keyword) .eq(StringUtils.hasText(status), DiseaseCase::getCaseStatus, status) .orderByDesc(DiseaseCase::getCreateTime); PageDiseaseCase page diseaseCaseService.page(new Page(pageNum, pageSize), wrapper);注意page方法会触发分页插件分页插件需要在配置类里注册一下否则分页不会生效Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }3.3 业务核心病例上报与统计接口的实现逻辑病例上报接口不是简单的save还包含了一套状态流转控制。新增病例时默认状态是0待审核只有管理员可以改成1已确诊或者2排除。这个权限规则我用自定义注解加拦截器实现在Controller方法上标RequireRole(admin)拦截器里从JWT解析出当前用户角色不合符直接返回403。统计接口是另一个重点。比如按天统计新增病例数SQL层面用到了MySQL8.0的DATE_FORMAT函数Override public ListMapString, Object countCaseByDate(LocalDate startDate, LocalDate endDate) { QueryWrapperDiseaseCase wrapper new QueryWrapper(); wrapper.select(DATE_FORMAT(create_time, %Y-%m-%d) as report_date, count(*) as total) .between(create_time, startDate.atStartOfDay(), endDate.plusDays(1).atStartOfDay()) .groupBy(report_date) .orderByAsc(report_date); return diseaseCaseMapper.selectMaps(wrapper); }selectMaps返回ListMap其中key就是report_date和total前端拿这个数组直接渲染柱状图和折线图字段名都不用改。这里有个细节统计涨跌趋势时最好用report_date这种语义明确的别名别用mysql关键字当列名不然前端很容易踩坑。业务层和Controller层还有个约定接口统一返回Result对象它包含code、message、data三个字段。code为200表示成功401表示未登录403表示无权限500表示服务端异常。所有Controller方法都返回Result.success(data)或Result.error(提示信息)前端Axios响应拦截器里判断code统一做Message提示避免了每个页面各自写错误处理的重复劳动。3.4 认证与权限JWT方案的一次完整梳理登录流程是前端把用户名密码提交到/api/auth/login后端拿用户名查出用户用BCrypt校验密码然后生成JWT返回给前端。JWT的载荷里放了userId、username、role三个关键信息后端用io.jsonwebtoken库签发生成密钥在application.yml里配置。前端拿到token后存到localStorage每次请求在Axios请求拦截器里塞进Authorization: Bearer token头。后端写了一个拦截器统一从请求头里取token并解析解析成功就把用户信息放到ThreadLocal里供Controller使用解析失败直接返回401。除了登录接口和几个公开的查询接口其他所有接口都受保护。这个方案省掉了SpringSecurity那一大套配置对毕设和中型项目来说是够用的。如果后续想引入权限菜单可以根据用户角色动态返回菜单列表前端用v-if或路由守卫来控制按钮和页面的显示比在后端代码里写死权限判断要优雅很多。项目里我建议直接用自定义注解的方式去搞这样后端权限点清晰前端也方便配合。4. 前端实现Vue3组合式API的工程化落地4.1 Vue3项目初始化与基础配置前端用的是Vite作为构建工具创建命令很简单npm create vitelatest disease-control-web -- --template vue创建完成后安装依赖注意要装的包有这么几个vue-router路由、pinia状态管理、element-plusUI组件库、axios请求库、echarts图表。Vite相比Webpack最直观的优势就是启动快Dev Server秒开。但Vite也有一些配置上的讲究比如需要手动配置别名指向src目录在vite.config.js里加import { defineConfig } from vite import vue from vitejs/plugin-vue import path from path export default defineConfig({ plugins: [vue()], resolve: { alias: { : path.resolve(__dirname, src) } }, server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })Dev模式下通过proxy解决跨域打包后通过Nginx的location配置也可以解决跨域。这里要说明一下本地开发时不要用axios.defaults.baseURL http://localhost:8080这种硬编码因为打包后部署到服务器就要换地址很麻烦。建议配置多个环境文件比如.env.development和.env.production这样切换环境时只需要改环境变量代码不用动。4.2 登录页、路由守卫和用户状态管理登录页是典型的Element Plus表单验证码可以不要但基本的用户名、密码、回车提交、表单校验最好都有不然答辩时会被问连个前端校验都没有就尴尬了。登录成功后路由跳转用router.push(/dashboard)同时把用户信息和token存入Pinia和localStorage。路由守卫是前端权限的第一道关卡。在router/index.js里给每个路由标meta: { requiresAuth: true }然后全局前置守卫里判断router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) } else { next() } })这一步能挡住未登录用户直接URL访问页面但别指望它做真正的安全控制后端接口鉴权才是关键。前端路由守卫只是提升体验让人觉得这是套正经系统。Pinia在这套系统里的角色很简单就是保存当前登录用户信息和动态菜单列表。Vue3里用Pinia比Vuex清爽的地方在于不需要写那么多Mutation和Action模板直接改state就行很适合中小型项目。4.3 数据统计大屏与列表页的写法要点统计页面是整项目的门面答辩时先展示这个页面就很加分。echarts在Vue3里一般封装成一个公共组件接收option作为props内部负责初始化和监听变化。效果图展示每日新增病例的折线图、各病种占比的饼图、各区县病例分布的柱状图。列表页的写法有几个习惯建议保持一致。第一所有列表接口都用分页参数页面用el-pagination组件切页时重新请求接口。第二表单组件统一用el-dialog包裹提交成功后关闭弹窗并刷新当前页数据。第三删除操作必须弹MessageBox确认否则误删数据无法恢复。这几点看着不起眼但直接决定了一个前端项目的成熟度。细节上再补充一句Vue3的ref和reactive一定要分清楚。列表数据用ref([])来声明表单对象用ref({})来声明别随便用reactive嵌套数组。reactive在修改深层属性时可能不会触发视图更新ref在取值时要写.value虽然刚开始觉得烦但这是Vue3响应式机制的底层逻辑多写两遍就习惯了。5. 项目运行与部署从本地到服务器的完整流程5.1 本地运行环境准备本地跑这个项目最少需要三个软件JDK1.8或JDK11、Node.js16、MySQL8.0。很多同学的坑不是代码而是环境版本对不上。SpringBoot2.7用JDK17也能跑但没必要用JDK8最稳跟绝大多数教程保持一致。后端启动前先改application.yml里的数据库连接信息。启动方式可以直接在IDEA里运行启动类也可以用maven打包后跑jar包。第一种方式适合开发和调试第二种方式适合验证打包产物。IDEA里运行前需要先安装Lombok插件不然实体类的getter和setter编译不过这个坑新人经常踩。前端启动前先npm install装依赖。国内网络建议把npm源切到淘宝镜像npm config set registry https://registry.npmmirror.com然后npm run dev启动开发服务器浏览器访问http://localhost:5173。登录时用预置的admin账号密码是admin123之类的具体以源码里的SQL初始数据为准。5.2 打包构建与环境分离前端打包命令是npm run build产物在dist目录。dist里全是静态文件部署到Nginx只需要两步把dist目录的文件拷贝到Nginx的html目录里然后在nginx.conf里加一个location配置server { listen 80; server_name your-domain.com; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }try_files那行是关键Vue路由用了history模式后刷新页面时如果Nginx没配置这个会直接404。/api的代理转发让前端页面和后端接口挂在同一个域名下解决了生产环境的跨域问题。后端打包用mvn clean package -DskipTests打完包在target目录下有一个disease-control-0.0.1-SNAPSHOT.jar上传到服务器后执行nohup java -jar disease-control-0.0.1-SNAPSHOT.jar app.log 21 查看日志用tail -f app.log确认出了Started DiseaseControlApplication字样就说明启动成功了。5.3 Docker Compose一键部署的补充方案如果说本地跑是第一步那线上部署还有更省心的方式。很多人喜欢用Docker Compose把前端、后端、数据库三个服务编排在一起。大致结构是mysql一个容器、后端一个容器、前端Nginx一个容器前端容器里打包好的dist文件直接挂载进Nginx镜像。三个容器通过docker网络内网互通端口只暴露80和3306。这种做法的好处是迁移部署时一条docker compose up -d就能拉起来整套环境对于后面要部署到云服务器、参加演示的场景挺方便的。但前提是服务器上得装好Docker和Compose插件同时数据库数据要挂载volume持久化不然容器重启数据全丢这个教训我印象深刻。6. 常见问题与排查技巧实录6.1 MyBatis-Plus分页失效版本迁移的坑项目里Controller层写的是return Result.success(diseaseCaseService.page(new Page(pageNum, pageSize), queryWrapper));如果查询返回的分页数据里total永远是0records却是全量数据八成是MyBatis-Plus分页插件没注册。3.4版本之后PaginationInnerInterceptor必须要手动注册Bean不像老版本配置好依赖就自动生效。我调试时踩过这个坑加上配置类后问题迎刃而解。另外如果代码生成器生成的分页语句里出现了LIMIT 0,10这种奇怪参数检查一下分页插件版本和MyBatis-Plus core版本是否一致。版本不一致导致的SQL拼接错误是MyBatis-Plus的经典问题统一版本后基本不会再出现。6.2 Vue3响应式丢更新ref和reactive用错了列表页里我一开始用reactive({ list: [] })存接口数据然后res.data.list.forEach(item list.push(item))结果列表渲染出来了但勾选状态无法同步更新。排查半天发现是reactive数组的深层响应式在嵌套对象上有时候不触发视图更新。我的建议是列表数据一律用ref用list.value res.data.records整体赋值。表单对象也一样用ref({})整体替换。Vue3的响应式确实比Vue2强但不是完全等价遇到视图不更新的情况先检查是不是用了错误的声明方式。6.3 MySQL8.0的时区和认证问题启动后端时如果报错The server time zone value CST is unrecognized就是数据库时区没配。解决方式连接串后面加serverTimezoneAsia/Shanghai或者启动Docker容器时加-e TZAsia/Shanghai。如果项目里大量用了LocalDateTime建议数据库连接串里同时加useLegacyDatetimeCodefalse避免时区被转换出错。如果连接报Public Key Retrieval is not allowed则是在MySQL8.0的caching_sha2_password认证机制下客户端第一次连接需要加密传输公钥加allowPublicKeyRetrievaltrue解决。这个报错在本地第一次连接时出现的概率很高但是配置里写死这个参数对生产环境其实不太安全生产环境建议用SSL连接替代。6.4 跨域问题本地和线上分别怎么处理本地开发时前端在5173端口、后端在8080端口直接发起请求会触发跨域。我前面提过用Vite proxy解决这个方案最优雅浏览器里根本没有跨域这回事因为请求是Vite Dev Server转发出去的。线上部署时前后端如果不在同一域名就需要在后端写CORS配置或者用Nginx反向代理。更推荐规划好域名让前端页面和后端API共用域名这样在Nginx层就解决了后端不用暴露给公网安全性和调试便利性都好很多。如果后端接口本身没做认证就把CORS开得很大那大概率会被扫描工具盯上这点在公网部署时一定要留意。6.5 文档与答辩准备给你的最后一条建议这个项目带了完整文档包含需求分析、数据库设计、接口文档、部署说明。你拿到手不要直接交重要的是自己走一遍流程把每一步记在笔记里。答辩时老师会随机追问比如这个字段为什么这么设计某个接口怎么实现权限控制如果并发量大了怎么优化这些都需要你真正跑过代码后才能答得上来。文档里的SQL初始数据也建议自己重新核对一遍特别是表名和字段名因为有些生成的SQL脚本跟实体类不完全对应。我的习惯是删库重建一次通过前端页面完整操作一遍功能流程确认所有页面都有数据展示再去写答辩PPT。这个习惯能帮你避开现场演示页面空白这种大型尴尬现场。我个人在实际操作中的体会是这类全栈项目最忌讳源码到手就算完成。真正把它跑通、改一个自己感兴趣的功能点、亲手踩几次坑再爬出来收获比读十篇教程都大。如果时间充裕试着把统计大屏改成ECharts的Map图、把Excel导出做成异步任务或者把登录换成验证码登录任何一个方向的二次开发都能让你在做项目陈述时更有底气。
返回列表