写毕设项目这几年,我前后帮人看过不少Java方向的课题,其中幼儿园管理系统算是出镜率非常高的一类。今天想借这个机会,把基于SpringBoot+Vue前后端分离的幼儿园儿童信息管理系统的完整实现思路、核心模块、数据库设计、部署细节和踩坑记录做一个系统梳理。如果你正在准备Java毕设,或者刚接触前后端分离项目,这篇文章可以帮你少走很多弯路。
整个项目从业务上看并不复杂,无非就是幼儿信息、班级管理、家长绑定、考勤、健康档案、收费、请假审批这些事。但真正把项目做到“能演示、能答辩、能部署”,还是有一堆细节要注意的:前后端如何联调、权限怎么控制、文件上传怎么处理、打包后怎么部署,每一环都可能卡住人。下面我按照实际开发的顺序,把整个系统的技术原理和操作过程完整拆开来讲。
1. 项目定位与技术选型
1.1 这类毕设项目的核心价值
幼儿园管理系统属于典型的“管理信息系统”,核心是围绕“幼儿”这个主体,把入园、在园、离园全流程的数据管起来。很多同学选这个题目,看中的是业务清楚、功能容易展示、演示效果好。但我个人觉得,这类项目的真正价值在于:它能覆盖一套完整的前后端分离开发流程,从需求拆分、数据库建模、接口设计、权限控制到部署上线,每个环节都是真实项目会用到的。
用Java+SpringBoot做后端,Vue做前端,是目前最主流的技术组合。SpringBoot负责提供RESTful API,处理业务逻辑、数据持久化、权限校验;Vue负责页面渲染、交互操作、状态管理。你把这个项目完整写一遍,等于把Java后端开发的核心知识串了一遍:Spring MVC、MyBatis Plus、Spring Security或JWT、文件上传、定时任务、事务管理,这些全都能用上。
另外我要提醒一点:毕设答辩时,老师问你“为什么前后端分离”,不要只回答“前端拿JSON数据渲染”。你要能说出来分离带来什么好处——比如前端和后端可以独立开发、独立部署,前端只需要调用接口,不需要关心服务端模板;后端只负责提供数据,不掺杂页面逻辑。这样的回答才是有分量的。
1.2 前后端分离到底带来什么
前后端分离在实际开发中,意味着后端工程和前端工程是两套独立的代码库,各自拥有独立的开发、构建、部署流程。
后端通常跑在8080端口,提供形如/api/child/info这样的接口。前端通过Axios发起HTTP请求,拿到JSON数据后在浏览器端渲染页面。开发阶段,前端工程由Node提供服务,默认跑在8080或别的端口,通过代理把接口请求转发到后端;生产阶段,前端代码被打包成静态文件,可以放在Nginx里,也可以直接把dist目录塞进SpringBoot的resources/static目录下,一个端口启动整个系统。
我在这个项目中采用的就是标准的分离结构,项目目录大致如下:
├── backend │ ├── src/main/java │ │ ├── controller │ │ ├── service │ │ ├── mapper │ │ ├── entity │ │ └── config │ └── src/main/resources │ ├── application.yml │ └── mapper └── frontend ├── src │ ├── api │ ├── views │ ├── components │ ├── router │ └── store └── package.json这种结构的好处很直接:后端写接口,前端写页面,你可以在IDEA里开一个后端服务,在VS Code里写Vue代码,两边互不干扰。调试的时候,浏览器F12看Network面板,请求和响应一目了然,比传统JSP开发图像清爽得多。
2. 业务功能分解与模块实现
2.1 幼儿园业务的特点
幼儿园和中小学的管理系统有很大区别。中小学的核心是“班级、课程、成绩”,幼儿园的核心是“幼儿、家长、健康、安全”。这里的业务主体是一个几岁的孩子,系统里要记录的信息必须围绕孩子的生活和成长展开。
从角色上看,系统至少要有三类用户:园长或管理员、教师、家长。园长关心全园数据统计和收费情况;教师关心班级幼儿信息、考勤和接送管理;家长最关心孩子在园表现、健康状况和缴费记录。不同角色看到的功能完全不同,这也是为什么权限设计在这个项目里特别重要。
2.2 核心模块划分
我设计的功能模块一共八个,覆盖幼儿园日常管理的方方面面:
- 系统管理模块:用户管理、角色管理、菜单管理,用的是一套标准的RBAC权限模型。
- 幼儿信息管理:幼儿的入园登记、基本信息维护、离园记录,支持批量导入。
- 班级管理:年级、班级的维护,幼儿分班和调班。
- 家长管理:家长信息和幼儿的绑定关系,支持一个幼儿绑定多个家长。
- 考勤管理:幼儿每日入园、离园打卡记录,教师可代登记。
- 健康管理:体检记录、疫苗接种记录、每日晨检情况登记。
- 收费管理:按月生成缴费单,记录缴费状态。
- 请假审批:家长提交请假申请,教师审批,请假记录关联考勤。
这套模块看起来多,但实现起来并没有想象中困难,因为大多数模块都是标准的增删改查加一个关联查询。真正有看点的只有两个:一个是幼儿分班和调班的数据联动,另一个是收费模块的按月自动生成账单。这两块逻辑写好了,能明显拉开和其他同学的差距。
2.3 典型业务闭环
我举一个具体的业务场景来说明这些模块是如何协作的。
家长用账号登录系统,在“请假申请”页面选择孩子、填写请假时间和事由,提交后数据存入leave表,状态为待审批。教师登录后,在待办列表里看到审批任务,点击通过后,请假状态变为已批准。此时考勤模块里,该孩子对应日期的考勤记录会被标记为“请假”,不再算作缺勤。
再比如收费场景:管理员在收费管理模块配置每个月每个班级的收费标准,系统通过定时任务,在每个月1号自动为所有在园幼儿生成当月缴费账单。家长登录后在缴费记录里看到账单并确认,管理员在后台看到缴费状态统计。整个过程不需要人工一个个去建账单,这就是定时任务的价值所在。
3. 数据库设计与权限模型
3.1 表结构设计思路
数据库是这类项目的灵魂,表设计得好不好,直接决定后期代码好不好写。我用的数据库是MySQL 8.0,核心表一共15张左右。下面挑几张重点表说几个关键设计点。
幼儿信息表child_info是最核心的一张表,存储姓名、性别、出生日期、入园日期、班级ID、家长ID等核心字段。这里有一个设计要点:不要把家长信息直接冗余到幼儿表里,而是建立单独的家长表和绑定关系表,因为一个孩子可能同时绑定爸爸、妈妈、爷爷奶奶多个联系人。
班级表classes和幼儿表是典型的“一对多”关系。幼儿表持有class_id外键。分班操作本质就是更新幼儿表的class_id字段,调班也是同一个逻辑。但要注意:如果幼儿已经有考勤记录或缴费记录,调班后历史数据的归属就变得复杂了。稳妥的做法是,幼儿表记录当前班级,考勤表记录幼儿当时所在班级的快照字段,这样历史统计不会乱。
考勤表attendance记录了幼儿每天的入园离园时间和状态。设计上建议用(attendance_date, child_id)做唯一索引,保证一个孩子一天只能生成一条考勤记录,避免重复数据。
我列出主要表的字段结构供参考:
| 表名 | 核心字段 | 说明 |
|---|---|---|
| sys_user | id, username, password, role_id, status | 用户表,保存登录账号 |
| sys_role | id, role_name, role_code | 角色表,区分管理员/教师/家长 |
| child_info | id, name, gender, birthday, class_id, photo | 幼儿信息表 |
| parents | id, name, phone, relation, child_id | 家长表,relation存爸爸/妈妈等 |
| classes | id, grade, class_name, head_teacher | 班级表 |
| attendance | id, child_id, date, status, check_in_time | 考勤记录表 |
| health_record | id, child_id, record_date, height, weight, note | 健康档案表 |
| fee_bill | id, child_id, month, amount, status | 缴费账单表 |
| leave_request | id, child_id, start_date, end_date, reason, status | 请假申请表 |
3.2 角色权限设计
权限模型我采用经典的RBAC设计:用户-角色-菜单三层关系。用户表里存role_id,角色表里存角色编码,菜单表里存前端路由地址和按钮权限标识。
前端路由的动态注册依赖菜单表:用户登录后,后端根据用户角色返回对应的菜单列表,前端拿到菜单列表后,通过Vue Router的addRoute方法动态添加路由。这样做的好处是,家长登录后根本看不到系统管理菜单,也不会因为路由缺失跳到404页面,体验和安全性都好很多。
后端接口层用Spring Security加JWT做认证。登录成功后签发JWT令牌,后续请求在请求头里携带token,后端通过拦截器校验。接口的权限控制用注解方式实现,比如只有管理员能调用收费管理接口时,在Controller方法上加@PreAuthorize("hasRole('ADMIN')"),没有权限就直接返回403。
这里有一个我实践下来的心得:毕设项目不要为了追求复杂而引入太多框架。JWT+拦截器完全够用,加上Spring Security完全可以,但不要再去搞OAuth2、网关之类的。项目规模就那么大,做多了反而显得主次不分。
4. 后端SpringBoot核心实现
4.1 项目结构与安全认证
我在后端用的是SpringBoot 2.7版本,为什么不用3.x?这里有个很现实的坑:SpringBoot 3.x要求JDK 17,很多同学本机还是JDK 8,换过来以后会引发一堆兼容性问题。另外3.x里不少依赖的命名空间和配置方式都变了,增删改查的代码倒是影响不大,但安全框架的写法差异很大。毕设稳定压倒一切,用2.7版本配JDK 8最稳妥。
项目包结构按职责划分:
com.example.kindergarten ├── config ├── controller ├── service ├── mapper ├── entity ├── common └── utils统一返回结果类Result是必须的,里面包含code、message、data三个字段。所有接口都返回这个结构,前端Axios根据code字段判断请求是否成功,这样处理错误逻辑只需要写一次。查询时的分页参数统一用pageNum和pageSize,返回分页对象PageResult。
登录认证这块,我用的方案是JWT+拦截器。用户登录成功后在服务端生成token,token里携带用户ID和角色。拦截器里解析token并放入ThreadLocal,业务代码中可以随时取出当前登录用户。这个方案实现简单,逻辑也容易讲清楚。如果你使用Spring Security,核心是重写UserDetailsService和JwtAuthenticationFilter,思路是一样的。
4.2 接口设计与统一返回
接口设计遵循Restful风格,接口路径统一以/api开头。前后端分离项目里,接口路径加/api前缀是约定俗成的做法,好处是一方面方便Nginx做路径转发,另一方面在开发环境也容易区分静态资源和接口请求。
幼儿信息相关的接口设计如下:
| 接口 | 方法 | 说明 |
|---|---|---|
| /api/child/list | GET | 分页查询幼儿列表 |
| /api/child/{id} | GET | 查询幼儿详情 |
| /api/child | POST | 新增幼儿 |
| /api/child/{id} | PUT | 修改幼儿信息 |
| /api/child/{id} | DELETE | 删除幼儿 |
| /api/child/import | POST | Excel批量导入 |
| /api/child/export | GET | 导出幼儿名单 |
写接口时大家容易忽略参数校验。用注解方式给实体类的关键字段加上校验规则,比如@NotBlank、@NotNull,在Controller参数上加上@Validated注解,非法参数会被统一拦截并返回友好提示。这属于加分项,代码里体现出来,答辩时就能说“我考虑了接口的健壮性”。
4.3 文件上传与MinIO接入
幼儿园系统里有一类功能必不可少,就是图片上传。幼儿照片、体检报告、活动图片都属于文件上传场景。很多毕设项目把文件放在本地磁盘,然后在代码里配置一个虚拟路径映射。这个方案够用,但不够专业。
我的做法是引入MinIO做对象存储。MinIO是开源的兼容S3协议的对象存储服务,部署非常轻量,一个二进制文件就能跑起来。SpringBoot集成MinIO的流程很固定:先在pom.xml引入minio依赖,配置文件里加入endpoint、accessKey、secretKey、bucketName,然后封装一个FileStorageUtil工具类,提供上传、下载、删除三个方法即可。
MinIO接入SpringBoot的关键代码如下:
import io.minio.MinioClient; import io.minio.PutObjectArgs; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class MinioUtil { @Value("${minio.endpoint}") private String endpoint; @Value("${minio.access-key}") private String accessKey; @Value("${minio.secret-key}") private String secretKey; private MinioClient client; @PostConstruct public void init() { client = MinioClient.builder() .endpoint(endpoint) .credentials(accessKey, secretKey) .build(); } public String upload(MultipartFile file, String bucketName) { String fileName = System.currentTimeMillis() + "_" + file.getOriginalFilename(); // 具体上传逻辑... return endpoint + "/" + bucketName + "/" + fileName; } }文件上传后,返回给前端的是一串可访问的URL。前端拿到URL,直接放到img标签里即可显示。用MinIO的好处是,文件不占用应用服务器磁盘,而且后期如果要扩展分布式部署,存储层天然独立,不用改业务代码。
这里有一个很多人不知道的点:MinIO也可以用来存视频。如果项目里涉及活动视频回放,前端用Vue播放mp4视频,直接使用video标签就可以;如果涉及m3u8切片流,那就需要引入hls.js这类播放器来做适配。这个可以根据自己项目的演示需求决定要不要加。
4.4 定时任务与提醒
收费模块的按月自动生成账单功能,依赖的是SpringBoot的定时任务。在启动类上加@EnableScheduling注解,然后在方法上加@Scheduled(cron = "0 0 0 1 * ?"),意思是每月1号零点执行。
自动生成账单的实现逻辑:查当月所有在园幼儿,根据班级的收费标准生成月度账单,调用批量插入方法写入fee_bill表。这里要注意事务控制,生成过程中如果有一条数据插入失败,整个批量插入都应该回滚,否则会出现部分幼儿有账单、部分没有的情况。在方法上加Spring的@Transactional注解即可。
定时任务在实际系统中是很常见的需求,比如每天生成考勤汇总、每月自动生成收费账单、定期清理过期日志。毕设里出现定时任务,属于典型的“功能亮点”,建议至少做一个。
5. 前端Vue核心实现
5.1 环境准备与工程搭建
前端我使用的是Vue2版本搭配Element-UI组件库。Vue2虽然已经不是最新的选择,但对于毕设项目,生态成熟、资料多、遇到问题容易搜到答案,这是我推荐它的核心理由。如果你对Vue3的Composition API比较熟悉,用Vue3加Element-Plus也可以,核心思路一样。
前端开发环境的搭建要注意几个关键点:
- Node.js版本不宜太高,Vue2项目建议使用Node 14或16,版本太高可能安装依赖时报错。
- 安装依赖用
npm install,如果遇到网络问题,配置淘宝镜像源。 - 工程创建推荐用Vue CLI,命令是
vue create frontend,选择Router和Vuex基础模板即可。
项目创建完成后,第一步先装Axios,然后在src目录下新建api文件夹,封装一个统一的request.js。我在request.js里做了三件事:设置请求超时时间、在请求头里自动加token、统一处理返回结果。整个过程是:
import axios from 'axios' 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( response => { const res = response.data if (res.code !== 200) { return Promise.reject(new Error(res.message)) } return res }, error => { // 统一处理401等错误 return Promise.reject(error) } ) export default request这样封装之后,每个接口文件只需要几行代码。比如child.js文件的写法:
import request from '@/utils/request' export function getChildList(params) { return request({ url: '/child/list', method: 'get', params }) }5.2 动态路由与权限控制
前端权限控制的核心是动态路由。登录成功时,后端除了返回token,还会返回当前用户的角色编码和菜单列表。前端拿到菜单列表后,在主布局的beforeEach路由守卫里判断:如果已经登录但路由未注册,就调用addRoute方法动态添加路由。
动态路由的写法在Vue2里稍微有点绕,但很值得掌握。核心步骤是:
- 定义一份静态路由表,包含登录页、404页、主布局。
- 在路由守卫中判断当前用户是否有权限访问目标路由。
- 如果没有动态注册过任何路由,就根据菜单列表生成动态路由数组并注册。
按钮级别的权限用自定义指令实现。比如删除幼儿按钮,只有管理员能看到,在按钮上写v-permission="'sys:child:delete'",指令内部判断当前用户是否有这个权限标识,没有就从DOM中移除该按钮。这个设计比单纯隐藏菜单更进一步,答辩时可以讲一讲。
5.3 页面与组件封装
Element-UI里最常用的组件是表格、表单、弹窗、日期选择器、树形控件。这里我有一个比较实用的建议:把列表页的翻页逻辑统一封装成混入对象,能减少大量重复代码。
幼儿园系统的页面结构比较固定:一个搜索区、一个操作按钮区、一个表格区、一个分页区。把这段逻辑抽出来,每个列表页只需要维护查询条件和列配置。我带的学生项目中,用这种方式之后,新增一个管理页面的工作量从半天压缩到了半小时。
表单校验是前端的一个重点。添加幼儿时,姓名、出生日期、性别是必填项,手机号要符合格式。Element-UI的form表单自带校验规则,配置rules属性即可。提交前调用validate方法,校验通过才允许提交。这一步不能省,如果前端不拦截非法数据,后端就算有参数校验,也会返回一堆不明所以的错误提示,体验非常差。
5.4 视频播放等扩展能力
如果项目里要展示幼儿活动视频,前端播放mp4格式比较简单,直接用HTML5的video标签。但有的演示场景要求播放m3u8视频流,这就涉及HLS流的客户端播放问题。
Vue播放m3u8的主流方案有两个:一个是vue-video-player组件,另一个是hls.js库。我一般用hls.js,因为体积小、配置灵活。实现方式很简单:下载m3u8地址后,通过Hls实例加载,然后挂载到video元素上。
这段代码可以作为一个扩展功能点到系统里,比如在“班级风采”模块展示活动视频。这类功能对毕设来说属于“加分项”,做好了对技术面的帮助不小。不过要注意,如果只是演示视频,需要先把视频文件传到MinIO或服务器上,拿到可访问的URL后再配置播放地址。
6. 环境配置与部署全流程
6.1 本机开发环境
在开始编码前,先把环境准备妥当。我的建议配置清单如下:
| 工具 | 版本建议 | 说明 |
|---|---|---|
| JDK | 1.8 | 与SpringBoot 2.7配套 |
| Maven | 3.6+ | 后端依赖管理 |
| Node.js | 14或16 | 前端开发环境 |
| MySQL | 5.7或8.0 | 数据库 |
| Redis | 5.x+ | 按需使用 |
| MinIO | 最新稳定版 | 对象存储 |
| IDEA | 2022+ | 后端开发IDE |
| VS Code | 最新 | 前端开发IDE |
6.2 后端运行步骤
后端工程的运行其实就三步:导入项目、改配置、启动。
第一步,用IDEA打开backend目录,等待Maven下载依赖。第一次加载比较慢,建议配置阿里云Maven镜像,否则下载依赖能等上半小时。在maven的settings.xml里加上镜像配置,速度会有质的提升。
第二步,修改application.yml里的数据库连接、Redis、MinIO配置。数据库连接要注意时区参数,serverTimezone=Asia/Shanghai不能漏掉。数据库需要先手动创建,执行sql脚本生成表结构和初始化数据。
第三步,启动主启动类。如果端口被占用,在application.yml里修改server.port。启动成功后在浏览器访问登录接口,返回正常JSON说明后端已经跑起来了。
6.3 前端运行与打包
前端开发模式下运行命令是npm run serve。这里有一个必须配置的点:开发环境的代理。在vue.config.js里配置:
module.exports = { devServer: { port: 8080, proxy: { '/api': { target: 'http://localhost:8081', changeOrigin: true } } } }配置代理后,前端请求/api/user/login,开发服务器会把请求转发到后端的http://localhost:8081/api/user/login,这样就不会有跨域问题。有的同学不加代理,在Axios里写死完整地址,虽然能用,但一旦换环境就要改代码,代理的方式明显更优雅。
打包命令:npm run build。打包完成后,dist目录里是纯静态文件。验证打包是否成功,只需要在dist目录下启动一个本地静态服务器,访问index.html即可。
6.4 生产部署方案
部署方式我推荐两种方案。
方式一是前端Nginx部署、后端独立运行。将dist目录拷贝到Nginx的html目录,Nginx配置里把/api开头的请求反向代理到后端接口地址。后端的SpringBoot工程打成jar包,用nohup java -jar xxx.jar命令后台运行。这种方式完全符合前后端分离的生产实践,而且Nginx可以配置负载均衡和HTTPS。
方式二是前端打进SpringBoot,统一端口访问。把dist目录复制到后端工程的resources/static目录,重新打包。启动后直接访问8081端口,即能看到前端页面。这种方式的优点是不需要额外装Nginx,演示环境里非常省事。缺点是前后端耦合在一起,失去了独立扩展的能力。
如果是毕设答辩,我建议学会方式一,因为被问到部署问题时,Nginx反向代理是一个很成熟的回答方向,说清楚以后老师会觉得你对生产环境是有概念的。同时把方式二作为备用方案,现场演示更稳定可靠。
7. 常见问题与排查实录
7.1 高频问题速查表
我在调试这个项目时,遇到和解决的问题不少,整理成一张速查表,方便你在卡住时对照排查:
| 问题现象 | 根本原因 | 解决办法 |
|---|---|---|
| 前端请求接口跨域 | 前端端口和后端端口不一致 | 配置devServer代理,或后端添加跨域过滤器 |
| 登录成功后页面不跳转 | token未写入localStorage或路由跳转逻辑有误 | 检查登录后的代码是否保存token并跳转 |
| 前端访问后端404 | 后端接口路径和前端请求路径不一致 | 统一接口前缀,用Postman先测通后端接口 |
| 时间字段显示为UTC | 后端时区配置错误 | 在配置文件中设置timezone |
| 文件上传报错文件不存在 | MinIO的bucket未创建 | 写一个初始化方法,启动时自动创建bucket |
| Vue打包后图片不显示 | 静态资源路径配置错误 | vue.config.js中配置publicPath为./ |
| Node启动报错Digital Envelope | Node版本过高导致 | 降低Node版本到16,或设置环境变量 |
| Maven下载依赖太慢 | 默认中央仓库网络不稳 | 配置阿里云镜像 |
这些坑看着不大,但每个都能卡住人好几个小时。尤其是跨域问题和Node版本问题,几乎每个项目都会遇到。排查的时候要沉着一点,按“网络请求是否发出、后端是否收到、返回数据是什么”的顺序逐步排除,不要瞎猜。
7.2 我踩过的三个最深的坑
第一个坑是SpringBoot版本选择。一开始有人图新用了SpringBoot 3.0,结果JDK从8升到17,MyBatis Plus的兼容版本也得跟着换,Spring Security的写法更是全部重来。最后我全部迁移回2.7才消停。所以给毕设项目定技术栈时,不要盲目追新,稳定、资料多、你熟悉,这三点比版本新更重要。
第二个坑是前端动态路由死循环。Vue路由守卫里注册路由时,没有加一个标志位判断“是否已经注册过”,结果每次路由跳转都重新调addRoute,导致跳转死循环。算是Vue动态路由的经典陷阱,建议动态路由只注册一次,用一个布尔变量或者判断当前路由是否存在于路由表里来控制。
第三个坑是文件上传的虚拟路径。最初我把幼儿照片传到本地磁盘的upload目录,然后通过WebMvcConfigurer配置映射,本地测试一切正常。但部署到服务器后,同样的代码路径却访问不到图片了,因为生产环境的工作目录和开发环境不一致。这个问题的根源在于路径写死了。引入MinIO之后,彻底摆脱了对本地文件系统的依赖,问题才真正解决。做项目时,凡是涉及路径的配置,都要养成“可配置、不写死”的习惯。
7.3 一个实用的规范习惯
这个项目写到最后,我建议大家建立一个习惯:每一张数据表都要有create_time和update_time字段,每一次删除都尽量用逻辑删除(加一个deleted标志位)。这个习惯来源很简单:毕业设计不是一次性代码,导师可能会要求你加功能、改逻辑。如果表里没有时间字段,日志审计和数据追溯都做不了;如果删数据是物理删除,出问题想恢复数据都没机会。
具体到代码层面,MyBatis Plus的MetaObjectHandler可以实现字段自动填充:新增时自动填create_time,更新时自动填update_time。逻辑删除更简单,在实体类deleted字段上加@TableLogic注解即可。这两件事写起来不难,但带来的维护体验完全不同,属于花小钱办大事的典型操作。
回到这个幼儿园管理系统本身。我自己的体会是,这类项目能不能做出彩,技术栈只是基础,真正拉开差距的是你有没有把业务细节想清楚。比如幼儿调班时历史考勤归属的问题,收费账单自动生成的边界条件,家长端和教师端各自的操作习惯差异,这些东西才是答辩时最有话聊的内容。
最后再分享一个小技巧:前端表格里展示幼儿照片时,如果还没有传头像,不要显示裂图。可以在封装的Image组件里监听error事件,图片加载失败时自动替换成本地默认头像。这个细节很多项目都忽略了,你做了,演示效果就会明显比别人的精致。
如果这个项目你已经做完了,想更进一步,可以试试把考勤数据做成每日统计图表,用Echarts在首页展示各班级的出勤率趋势。技术上并不复杂,但能让整个系统的数据价值感提升一个台阶。