ARTICLE DETAIL

资讯详情

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

基于Spring Boot和Vue的高校运动会管理系统设计与实现

基于Spring Boot和Vue的高校运动会管理系统设计与实现 1. 项目定位与整体设计思路1.1 高校运动会管理到底在管什么每年春秋两季各高校都要办运动会。但真正经历过的人都知道一场覆盖几十个学院、上千名运动员、近百个比赛项目的校级运动会背后的事务性工作有多繁琐运动员报名信息要反复核对、田赛径赛的时间场地要人工排布、检录表要打印几百份、成绩出来后要手工统计团体总分、破纪录要翻查历史数据、证书要一个个手写填写。我做过几个类似的信息化项目最大的感受是运动会的业务链路其实非常适合系统化管理。它的数据边界清晰——运动员、项目、成绩、团体分就这四类核心实体但它的规则细节又足够多——同分如何判定名次、一个运动员最多报几个单项、接力项目算不算双倍积分、田赛和径赛的名次录取规则还完全不同。这些规则如果靠Excel维护每届运动会都要重做一遍而且极易出错。这个项目就是做一个前后端分离的高校体育运动会管理系统后端用Spring Boot提供RESTful接口前端用Vue 3 Element Plus搭建管理界面支持从运动员报名、赛事编排、成绩录入到团体总分自动排名、证书打印的全流程管理。它解决的痛点很明确把运动会的组织工作从Excel 微信群 手写纸质表的模式迁移到一套可配置、可追溯、可复用的在线系统里。适合谁来参考这个项目如果你正在做类似的校园管理类系统考勤、选课、社团活动报名都适用或者你的毕业设计/课程设计正好落在这个方向这篇文章里的数据建模思路、成绩排名算法、文件存储方案和前后端部署细节都可以直接抄作业。1.2 为什么选Spring Boot Vue这套组合选型这事我向来不追求花哨关键是稳定和生态成熟。Spring Boot Vue这套组合在校园管理类系统里基本是标准答案级别理由有三第一Spring Boot的自动装配机制极大降低了后端搭建成本。项目里用到Spring Security做登录认证、MyBatis-Plus操作数据库、MinIO做文件存储、POI做Excel导入导出这些都是Spring Boot生态里非常成熟的组件。你不需要为每个功能单独写一堆配置类加依赖、配application.yml、写业务接口三步走。第二Vue 3 Element Plus的前端方案对管理后台这类以表单和表格为核心的场景非常友好。运动会系统的前端页面本质上就是报名表、编排表、成绩单的电子化呈现Element Plus的表格组件自带排序、筛选、分页表单组件自带校验规则配合Vue Router做页面跳转、Pinia做全局状态管理开发效率比原生jQuery时代高出一个量级。第三前后端分离带来的部署灵活性。开发阶段前端用Vite dev server跑在5173端口后端跑在8080端口通过代理转发解决跨域部署阶段既可以把前端打包成静态文件扔到Nginx里也可以把dist目录直接塞进Spring Boot的resources/static打成单jar包运行。两种方案我都实际跑过后面第5节会详细讲。2. 核心功能拆解与数据建模2.1 六个核心业务模块整个系统按运动会业务流程拆成六个模块每个模块的边界要清晰才能避免后期改代码改到头秃系统管理模块用户登录、角色权限超级管理员、学院领队、裁判员、普通学生、学院班级数据维护。这里我用的是Spring Security JWT的无状态认证前端登录后拿到token存在localStorage里每次请求带在Authorization头里。运动员管理模块运动员信息录入支持Excel批量导入这个很重要几百号人一个个手输会崩溃、运动员报名项目管理要校验每人限报两个单项加一个接力这类规则。赛事编排模块比赛项目维护田赛/径赛分类、分组、预决赛轮次、赛程时间表生成、检录表打印。这块的核心是一个场地时间冲突检测算法后面3.1节细说。成绩管理模块裁判员录入成绩、成绩复核、自动排名、破纪录标记。径赛要记录秒数并精确到百分位田赛记录米数并精确到厘米排名逻辑完全不同。团体总分模块按单项、接力等不同权重自动累加学院总分支持实时刷新排行榜。数据统计与证书模块各学院金牌榜/总分榜、运动员个人成绩单、获奖证书PDF批量生成。2.2 数据库表设计的关键决策数据库设计是这个项目的骨架我踩过不少坑重点说三个决策第一比赛项目表的分类字段不能只存田赛/径赛两个值。我一开始只用一个event_type字段区分田赛和径赛后来发现很多规则判断都需要更细的分类径赛里100米是全程分道800米是部分分道田赛里的跳高和铅球的录取规则也不同。最后改成两级分类event_type田赛/径赛/全能加event_sub_type短跑/长跑/跳跃/投掷代码里用枚举统一管理避免魔法值满天飞。第二成绩表和排名表分离。新手容易犯的错是把排名直接算好存进成绩表成绩一修改排名就乱套。我的做法是成绩表result只存原始成绩数据排名ranking由排名算法运行时生成并缓存。这样裁判录入的成绩能回溯、能复核排名出错了重跑一遍算法就行数据永远干净。第三别用一张报名表硬扛所有业务。运动员报名一个项目涉及到运动员表、项目表、报名关系表三张表。报名关系表里要加status字段已报名/已检录/已参赛/弃权这个状态流转是业务流程的核心后面3.3节讲。核心库表清单如下表名用途关键字段sys_user用户账号username, password, rolecollege学院name, codeathlete运动员name, gender, college_id, student_noevent比赛项目name, type, sub_type, gender, round, score_unitregistration报名关系athlete_id, event_id, status, group_noresult成绩记录registration_id, score, rank, is_broken_recordschedule赛程安排event_id, venue, start_time, group_no3. 后端Spring Boot关键实现实录3.1 赛事编排与场地时间冲突检测赛程编排是个典型的约束满足问题。一个标准田径场有跑道、跳远沙坑、铅球区等场地同一时间段内不同场地可以并行比赛但同一场地不能同时安排两场比赛。人工排赛程的痛点在于几十个项目、几百组比赛交叉安排很容易出现一个场地同时被占用、或者一个运动员同时被两个项目检录的冲突。我的实现思路是做一个时间槽 场地锁的分配算法。先把运动会时间切成固定时长的时段比如上午8:00-12:00按每30分钟一个时隙每个场地在每个时隙只有一个可占用的槽位。编排时遍历所有比赛组对每个组做三件事根据项目预估时长计算需要占用的时隙数量找一个所有所需时隙都空闲的场地检查该组的运动员在所选时段内是否有其他比赛如果有则换时段。这个算法本质是一个贪心策略实际使用中90%的场景都能一次分配成功剩余的冲突交给人工作调整。关键代码逻辑// 场地时隙占用检查 public boolean isVenueAvailable(Long venueId, LocalDateTime startTime, int durationMinutes, ListSchedule schedules) { LocalDateTime endTime startTime.plusMinutes(durationMinutes); return schedules.stream() .filter(s - s.getVenueId().equals(venueId)) .noneMatch(s - isTimeOverlap(s.getStartTime(), s.getEndTime(), startTime, endTime)); } // 运动员同一时段冲突检查 public boolean isAthleteAvailable(Long athleteId, LocalDateTime startTime, int durationMinutes, ListSchedule schedules) { // 查该运动员所有已编排项目的比赛时间做区间重叠判断 }注意贪心算法解决不了全部约束问题所以我给管理员留了手动调度的入口。界面上用日历组件展示各场地的占用情况管理员可以直接拖拽调整系统实时做冲突提示。编排这个功能自动化程度再高人工兜底永远是必须的。3.2 成绩录入、复核与自动排名算法成绩模块是整个系统的业务核心也是最容易出逻辑bug的地方。径赛成绩录入径赛按秒记成绩精确到百分位。裁判录入的是原始成绩比如11.25秒系统按小组排名然后所有组按成绩统一排名取前八名进入决赛或直接录取。这里有个细节不同分组的运动员成绩要在不同组内排序预赛小组排名决定晋级决赛排名则全体统一比较。田赛成绩录入田赛每人试跳/试投若干次通常是三次取最好成绩作为最终成绩。这个三次取最优的逻辑要用独立表记录每次试跳成绩最终成绩字段单独冗余一份方便排名时直接比较。排名算法的同分处理是重灾区。径赛同秒数极少见但田赛里成绩完全相同比如都跳过1.75米的情况很常见。国际田联规则是如果成绩相同比较次优成绩再相同就比较第三次成绩也相同则名次并列。我在ranking服务里实现了完整的成绩降序、次优成绩降序、第三次成绩降序三级比较器public class FieldRankComparator implements ComparatorResult { Override public int compare(Result r1, Result r2) { // 第一级最好成绩 int cmp Double.compare(r2.getBestScore(), r1.getBestScore()); if (cmp ! 0) return cmp; // 第二级次优成绩 cmp Double.compare(r2.getSecondBestScore(), r1.getSecondBestScore()); if (cmp ! 0) return cmp; // 第三级第三次成绩 return Double.compare(r2.getThirdBestScore(), r1.getThirdBestScore()); } }破纪录判断我在event表里加了record_score字段存当前校纪录。成绩录入时自动比对超过就弹窗提示破纪录并标记is_broken_record字段。历史纪录数据我放在了单独的record_history表里保留每次破纪录的运动员、成绩、时间和原纪录对比既方便做破纪录排行榜展示也让数据可追溯。3.3 报名状态机与规则校验报名这个环节看似简单实际上是最容易让用户骂娘的地方。一场运动会几百上千个报名数据必须做好两件事规则校验和状态流转。规则校验我在后端做了三层数据格式层学号格式、性别匹配、手机号格式用Spring Boot自带的Validated注解搞定业务规则层每人限报两个单项加一个接力、同一项目不能重复报名、女生不能报男子项目这些写在Service层里用自定义异常抛出数据完整性层学院、班级ID必须存在且匹配避免前端传了脏数据。报名状态我用一个状态机管理字段status取值范围PENDING已提交待审核、APPROVED审核通过、CHECKED_IN已检录、COMPETED已完赛、CANCELLED已取消。状态流转图如下PENDING - APPROVED - CHECKED_IN - COMPETED PENDING - CANCELLED APPROVED - CANCELLED每次状态变更都会写入registration_log表记录操作人、操作时间、从哪个状态到哪个状态。这个设计在运动会当天特别有用——领队说我的运动员检录了没裁判说这个运动员到场了没一查日志就清清楚楚。3.4 基于MinIO的文件存储与M3U8视频回放这个项目的文件存储部分我一开始图省事直接存服务器本地磁盘后来发现三个问题一是比赛视频文件大一个项目几百MB本地磁盘扛不住二是服务器磁盘扩容麻烦三是视频在线回放需要流媒体支持。最后改成了MinIO这东西和Spring Boot的整合非常顺滑。MinIO是什么你可以把它理解成一个跑在自己的服务器上的迷你S3对象存储。数据以桶bucket为单位组织每个文件是一个对象用HTTP接口访问。和FastDFS相比MinIO的部署和客户端SDK要简单太多和阿里云OSS相比它不依赖外部云服务数据不出校门。Spring Boot整合MinIO的步骤很简单加依赖minioMaven坐标是io.minio:minio:8.5.x在application.yml里配置endpoint、accessKey、secretKey、bucket名称写一个MinioService封装上传、下载、生成临时访问链接的方法。Service public class MinioService { Value(${minio.endpoint}) private String endpoint; Value(${minio.access-key}) private String accessKey; Value(${minio.secret-key}) private String secretKey; Value(${minio.bucket}) private String bucket; private MinioClient minioClient; PostConstruct public void init() { minioClient MinioClient.builder() .endpoint(endpoint) .credentials(accessKey, secretKey) .build(); // 检查bucket是否存在不存在则创建 } public String upload(MultipartFile file, String objectName) throws Exception { minioClient.putObject( PutObjectArgs.builder() .bucket(bucket) .object(objectName) .stream(file.getInputStream(), file.getSize(), -1) .contentType(file.getContentType()) .build()); return endpoint / bucket / objectName; } }M3U8视频回放这个需求很多人忽略但实际做的时候全是坑。运动会现场录的视频格式五花八门手机录的是MP4摄像机录的是MOV让浏览器直接播放需要格式兼容。我的方案是后端接一个FFmpeg转码服务把上传的原始视频统一转成HLSM3U8流媒体格式再配合前端hls.js播放器实现在线回放。vue播放m3u8免安装这个需求在运动会场景下就是典型应用——评委和观众不需要装任何播放器浏览器打开就能看。转码我用的是FFmpeg命令行调用写一个异步任务执行ffmpeg -i input.mp4 -codec copy -start_number 0 -hls_time 10 -hls_list_size 0 -f hls output.m3u8前端Vue里用hls.js加载M3U8流import Hls from hls.js; function playVideo(videoUrl) { const video document.getElementById(videoPlayer); if (Hls.isSupported()) { const hls new Hls(); hls.loadSource(videoUrl); hls.attachMedia(video); } else if (video.canPlayType(application/vnd.apple.mpegurl)) { // Safari原生支持直接设置src video.src videoUrl; } }注意M3U8是分片文件所有.ts分片要和.m3u8索引文件放在同一个目录下如果目录结构变了播放器会报错。我踩坑的经历是把视频文件按每年运动会/每个项目分目录存到MinIO里前端拼访问URL时路径一定要和上传时的objectName完全一致。除了视频MinIO还负责存运动会横幅图、获奖证书模板、运动员头像等静态资源。这么做有两个直接好处一是Spring Boot应用不再处理静态文件上传的状态无状态服务更容易横向扩展二是MinIO自带的预签名URL功能可以控制文件访问权限让过期链接自动失效小规模内部系统用着刚刚好。4. 前端Vue实操记录4.1 路由设计与权限控制前端用Vue Router做页面路由模式选的是createWebHistoryHTML5 History模式原因有两点一是URL干净不带#号方便分享二是部署到Spring Boot里可以做转发配置让刷新页面时不白屏。路由分两部分登录页和管理后台。管理后台是一个嵌套路由父组件Layout.vue包含侧边栏菜单和顶部导航条子路由对应各个业务模块const routes [ { path: /login, component: Login }, { path: /, component: Layout, redirect: /dashboard, children: [ { path: dashboard, name: Dashboard, component: Dashboard }, { path: athletes, name: AthleteList, component: AthleteList }, { path: events, name: EventList, component: EventList }, { path: schedules, name: ScheduleList, component: ScheduleList }, { path: results, name: ResultEntry, component: ResultEntry }, { path: rankings, name: RankingBoard, component: RankingBoard } ] } ];权限控制核心是路由守卫。我在router.beforeEach里检查两件事一是token是否存在不存在就跳登录页二是用户角色是否有权限访问当前路由。角色的权限清单存在后端登录时随用户信息一起返回前端用Pinia存一份。这里有个实际开发中经常踩的坑动态路由。一开始我把所有菜单都写死在前端后来不同角色看到的菜单不一样只好在路由注册时做动态过滤。Vue Router 4提供了addRoute方法可以根据当前用户角色动态挂载路由。但如果处理不当退出登录时动态路由不会自动清除换个账号登录还会看到上一个账号的菜单。解决方案是登出时重置整个路由实例或者在注册时保存一份原始路由表每次登录根据角色从零开始重建。4.2 成绩录入表单与实时榜单成绩录入页面是前端做起来最顺手也最需要细致考虑的部分。裁判员在田径场的烈日下录成绩手上可能还夹着纸质检录表所以页面的交互必须做到最少点击、最大容错。我的做法是选择比赛项目后页面自动加载该项目的所有参赛运动员列表按组别和道次排好序。成绩录入区用表格形式每行一个运动员径赛录入秒数带两位小数的输入框田赛录入三次试跳成绩三个输入框。输入框的校验规则是前端先校验格式必须是数字、范围在合理区间后端再校验业务规则。表单里藏着两个提高效率的细节键盘回车自动跳转下一个输入框。给输入框加keyup.enter事件判断当前行/列位置后把焦点focus到下一个框。裁判录完一个运动员的成绩回车就到下一格手都不用离开键盘。这个交互细节对大批量录入体验的提升比任何UI美化都管用。防误触的保存并继续模式。录完一组成绩后点击保存本组就把当前组所有成绩提交如果所有成绩都合法就直接展示下一组不弹成功提示。这样零打断的操作流一场接力赛只需要不到两分钟就能录完全部成绩。实时榜单页是Vue的强项用setInterval每5秒向后端拉一次团体总分排行前端计算属性驱动排行榜渲染。分数变化的时候加一个简单的CSS过渡效果数字滚动观众席大屏投影出来效果非常好。4.3 文件预览与视频播放的坑这个项目的文件资源包括上传的比赛视频、生成的获奖证书PDF、运动会的横幅图、运动员照片。前端展示这些文件时有三组问题是我反复踩坑后沉淀下来的PDF预览。直接用iframe加载PDF地址能预览但EasyExcel生成的PDF文件名带中文时部分浏览器会乱码或打不开。解决方法是encodeURI编码文件名或者在生成时就统一用英文文件名展示时再映射中文名。图片不显示。MinIO返回的图片URL如果是预签名URL链接里带X-Amz-*参数直接放进img src是能显示的。但我遇到过一个问题有些图片URL里的号会被浏览器解析成空格导致链接失效。排查了半天最后发现是URLEncoder编码空格符号时用了而不是%20统一成%20后问题解决。视频播放延迟。M3U8格式的视频首次加载需要先请求索引文件、再按序加载各个.ts分片如果MinIO和浏览器之间网络状况一般首帧会出现3-5秒的缓冲。优化手段是转码时把hls_time参数调大比如10秒一个分片减少分片数量另外可以在页面加载时预连接MinIO端点用link relpreconnect提前建立连接。实测下来首帧时间能缩短到1秒左右体验已经可接受。5. 项目打包部署实战5.1 方案一前后端分开部署推荐生产环境推荐前后端分开部署前端build后的静态文件交给Nginx后端Spring Boot打包成jar独立运行。这样做的最大好处是动静分离——静态资源由Nginx直接返回Tomcat只处理API请求压力小很多另外前端更新时不需要重启后端服务发版更快。前端部署步骤# 1. 构建前端 npm run build # 2. 把dist目录下的文件上传到服务器 /var/www/sports-meet/ # 3. 配置Nginx反向代理Nginx配置的关键片段server { listen 80; server_name sports.example.edu.cn; # 前端静态资源 location / { root /var/www/sports-meet; index index.html; try_files $uri $uri/ /index.html; # 解决前端路由刷新404 } # 后端API反向代理 location /api/ { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }这里特别提一下try_files这一行。Vue Router开了HTML5 History模式后前端路由像/athletes、/rankings在刷新时Nginx会去找对应的物理文件找不到就返回404。加上try_files $uri $uri/ /index.html后所有找不到的路径都回退到index.html由前端路由接管问题解决。后端部署就简单了Spring Boot项目用Maven打包mvn clean package -DskipTests java -jar target/sports-meet-0.0.1.jar --server.port80805.2 方案二Vue打包放进Spring Boot单jar运行有些场景下没有独立的Nginx环境或者就一台小服务器不想多维护一个服务——这时可以把Vue打包后的dist目录直接拷贝到Spring Boot项目的src/main/resources/static下重新打包成jar后端启动后前端页面和应用接口都在同一个端口提供访问。这个方案我在一个小型内部系统上实测过跑通的前提是注意三个地方前后端路由的联动。前端Vue Router用createWebHistory模式时访问/athletes刷新会出现404因为Spring Boot默认只映射了静态资源没有把未知路径转发到index.html。解决办法是写一个WebMvcConfigurer把非API路径全部转发到/index.htmlConfiguration public class WebConfig implements WebMvcConfigurer { Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController(/{spring:\\w}) .setViewName(forward:/index.html); registry.addViewController(/**/{spring:\\w}) .setViewName(forward:/index.html); } }静态资源路径要用相对路径。Vite默认base是/打成jar后资源路径如果带绝对路径在某些嵌入场景下会找不到。实测最稳的方式是构建时设置base: ./让Vue打包出来的资源全部走相对路径这样不管jar部署在哪个端口都没问题。上传文件的存储目录要和jar包分离。用java -jar运行时当前工作目录是jar所在目录如果文件上传路径写成相对路径每次升级jar包时很容易把上传的老数据覆盖掉。我是把上传路径配置成绝对路径/data/sports-meet/upload在jar外面独立管理这样jar升级完全不影响已有文件。5.3 MySQL初始化与数据迁移数据库部分我用MySQL 8.0项目里配了Flyway做数据库版本管理。每次表结构变更写一个新的V{n}__{description}.sql迁移脚本spring.flyway.enabledtrue应用启动时自动执行增量迁移。这个习惯帮我避免了好几次测试环境加了字段、生产环境忘了加的惨剧。初始化数据用data.sqlFlyway的R__前缀文件可以重复执行把学院名单、默认管理员账号、比赛项目模板100米、200米、跳远、铅球等写进去。新一届运动会开赛前管理员不需要从头配置基础数据只要把报名数据导入就行。6. 常见问题与排查实录6.1 开发环境的高频坑这个项目开发过程中我记录了不少典型问题挑几个最有代表性的整理成表问题现象根本原因解决方案前端请求后端接口报跨域错误Vite开发服务器5173端口和后端8080端口不同源开发环境用Vite代理server.proxy转发/api到localhost:8080生产环境靠Nginx天然同源上传大视频文件时Netty报内存溢出MinIO客户端写入时未分片处理后端用FileInputStream分段流传给MinIO不用MultipartFile一次性读入内存同时调大server.tomcat.max-swallow-size和spring.servlet.multipart.max-file-sizeVue打包后运行时白屏路由模式用了history但服务器未配fallback按方案一配Nginx的try_files或按方案二走WebMvcConfigurer转发刷新页面返回404同上同上表格导出中文乱码POI生成Excel时未设置XSSFWorkbook的字符集设置wb.setCharset或统一用UTF-8 BOM格式输出CSV/ExcelSpring Boot版本和JDK版本不匹配导致启动失败Spring Boot 3.x强制JDK17而项目用了JDK8要么升JDK要么降级Spring Boot 2.7.x基于javax而非jakarta命名空间6.2 数据与权限类问题的独家排查技巧并发报名冲突。运动会报名开放当天几百个学生同时在线报名出现过同一运动员重复报名同一项目的脏数据。排查后发现是前后端校验只做了一端两个请求同时进来都通过了校验。解决方式是在数据库层面加唯一约束UNIQUE(athlete_id, event_id)从根上防止重复再用后端的select ... for update行锁保护报名流程的并发更新。操作日志排查。系统上线后出现过一个成绩被修改但查不到是谁改的问题。当时所有成绩更新操作只记录更新后的值没有记录旧值。后来我在成绩更新接口里强制附加了旧值快照日志格式是旧成绩-新成绩操作人操作时间。排查问题时一眼就能看到数据变化的前因后果。建议所有涉及关键数据的更新操作都记录完整审计日志这个习惯关键时刻能救你命。JWT过期引发的会话混乱。JWT token设有2小时有效期裁判录成绩录到一半token过期前端请求返回401但页面没有任何提示裁判以为系统卡了白录的成绩全丢了。解决方案两个一是在Axios拦截器里统一处理401响应弹出提示并跳转登录页二是给token加静默续期机制——每次API请求返回时带上新的token前端取下更新本地存储让长时间在线的裁判不会被迫中间重新登录。成绩录入精度问题。径赛成绩手动输入时出现过11.5被自动纠正为11.50但排名时发现11.5和11.50被当成了不同数值。排查后才知道问题出在JSON序列化前端传字符串11.5后端BigDecimal正常存储但传给Double字段做比较时丢失了精度。最终方案是所有成绩字段统一用BigDecimal存储比较用compareTo而不是equals问题根治。6.3 性能优化实测运动会当天是系统压力最大的时刻检录查询、成绩录入、榜单刷新三线并发高峰期QPS可能到几百。我做的性能优化有三个实测有效的点数据库层面查询最频繁的排名查询和报名状态查询涉及的字段全部加了联合索引比如(event_id, status)、(athlete_id, event_id)慢查询日志里原先300ms的查询降到20ms以内。Redis缓存团体总分排行和项目报名人数这类读多写少的数据用Redis做缓存设置30秒过期。排名算法计算完成后写入缓存前端定时轮询的接口直接读缓存数据库压力大幅下降。异步化PDF证书批量生成是典型耗时操作我改成MQ异步任务前端点生成全员证书后返回生成中提示后端线程池异步处理完成后前端能看到生成结果列表。运动会现场没有人愿意为生成1000张证书等半分钟的页面转圈。最后再分享一个我做这类校园管理系统的心得功能做成什么样取决于谁在用。运动会系统的管理员是体育部的老师裁判是学生志愿者我一开始把界面做得功能丰富、选项很多实际使用后收到最多的反馈是太复杂了我就想录入成绩。后来我专门开了简化模式裁判登录后默认直达成绩录入页所有无关菜单都隐藏能一键完成的绝不两步。系统最终好不好用不是看功能全不全而是看最核心的那条操作链路顺不顺。这个项目的所有设计都该围绕这个原则来取舍。
返回列表