news 2026/10/9 4:35:35

Spring Boot + Vue在线考试系统全栈项目实战:从架构设计到部署

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot + Vue在线考试系统全栈项目实战:从架构设计到部署

1. 项目概述与核心需求解析

这几年只要是做管理系统、业务后台、毕设项目的人,几乎都绕不开 Spring Boot + Vue 这对组合。市面上能看到的在线考试系统demo不少,但真正把“登录鉴权、考试状态流转、自动判分、成绩统计”这些核心环节都跑通,并且附带可直接导入的数据库脚本和说明文档的项目,其实没有想象中那么多。这个“基于 Spring Boot + Vue 在线考试系统(源码+数据库+文档)”的项目,就是一套典型的全栈分离结构:后端用 Spring Boot 提供 REST API,前端用 Vue 实现单页应用,数据库采用 MySQL 来承载用户、试题、试卷、考试记录和成绩等核心数据。

拆开来看,这个项目能解决的核心问题其实就一句话:把线下的“出题、发卷、答题、收卷、判分”完整流程搬到线上,并且让管理员、教师、考生三类角色都能在一套系统里各取所需。对开发者来说,它更大的价值在于提供了一个可复用的前后端分离脚手架:登录状态怎么保持、考试倒计时怎么处理、试卷随机组题怎么做、事务边界画在哪里,这些都能从源码里找到现成答案。

适合看这个项目的人大致分三类。第一类是刚学完 Spring Boot 和 Vue 基础、想找一个完整项目练手的学生,这套系统的代码量不大不小,刚好能把框架知识串起来。第二类是准备做毕业设计或课程项目的开发者,因为项目自带数据库脚本和文档,省去了从头设计表结构的痛苦。第三类是实际有在线考试需求的小团队,比如培训机构、企业内部测评,拿这套代码做二次开发,比从零造轮子快得多。

我在接触这个项目时,最看重的是它把“考试”这个业务场景里的特殊状态处理得怎么样,因为考试系统和普通 CRUD 系统有个本质区别:考试过程中不能随意刷新、不能重复提交、倒计时到了必须强制交卷。这些细节如果只在页面层做,后端不考虑并发和状态校验,线上跑起来一定会出问题。所以后面我会重点拆这几个环节,而不是只停留在“某某表怎么建、某个接口怎么调用”的层面。

2. 技术选型与整体架构设计

2.1 后端为什么锁死 Spring Boot 而不是 SSM 或 Spring Cloud

很多人会问,既然项目不大,用 SSM(Spring + Spring MVC + MyBatis)是不是更简单?实际上,Spring Boot 相比传统 SSM 最大的优势是“自动配置”和“起步依赖”机制。它把以前需要手动配的一大堆 XML 配置收敛成了几个注解和配置文件项,尤其适合快速搭建单体应用。这个在线考试系统本质上就是一个典型的单体 Web 应用:用户管理、考试管理、成绩管理都集中在同一个进程里,没有复杂的分布式诉求,所以引入 Spring Cloud 那套注册中心、网关、配置中心的组件反而是过度设计。

选 Spring Boot 的另一个现实原因,是生态里现成的组件足够顺手。持久层可以用 Spring Data JPA 快速完成实体映射,也可以用 MyBatis 写灵活 SQL,这套项目两种方式都有参考价值。安全方面可以直接引入 Spring Security 或结合 JWT / Token 机制来处理身份认证,不用自己从零写加密和会话管理。再加上内置 Tomcat,打包成一个可执行 jar 丢到服务器上就能跑,部署成本很低。

这里要注意一个高频问题:Spring Boot 版本太高导致兼容性翻车。我自己就踩过不少次,因为项目依赖的某些第三方库还没有适配最新版本,一升级就报各种诡异的 bean 加载失败或配置绑定错误。如果你拿到手的源码是基于 Spring Boot 2.x 开发的,就不要轻易升到 3.x,因为 3.x 不仅要求 JDK 17+,还把很多 API 做了模块化调整,默认的spring.factories加载机制也换成了AutoConfiguration.imports。除非你对源码结构非常熟,否则保持与项目原始版本一致是最稳的做法。

2.2 前端为什么选择 Vue 而不是 jQuery 或 React

在线考试系统需要频繁操作 DOM、实时渲染倒计时、动态切换题目,用传统 jQuery 拼 HTML 字符串会非常痛苦。Vue 的响应式数据绑定天然适合这种场景:页面只是数据的状态映射,考生点击下一题、上一题,只需修改当前题目索引,视图自动跟着变。

Vue 2 和 Vue 3 的选择需要单独说。老项目很多是 Vue 2 + Element UI,因为当时 Vue 3 刚出来,生态还不成熟,Element Plus 也还没稳定。新项目则推荐直接上 Vue 3 + Vite + Element Plus,因为组合式 API 写业务逻辑更干净,Vite 的开发服务器启动速度也比 Webpack 时代快一个量级。不过如果你拿到的源码是 Vue 2 的,最好别强行升级到 Vue 3,项目里的路由写法、组件通信、UI 库引用方式都有差异,官方虽然有迁移工具,但迁移完通常还要手动改不少地方,工作量不小。

Vue 项目最容易卡住新人的其实是环境配置。装 Node、配 npm 镜像、创建项目这几个步骤,网上教程说法很多,版本一不对就会报错。我习惯的做法是直接用npm create vite@latest生成 Vue 3 项目,然后根据实际情况安装 Vue Router 和 Pinia。依赖安装完先跑一次npm run dev,把基础环境打通,再往项目里复制业务代码,这样能把环境问题和业务代码问题分开排查。

2.3 数据库选型与核心表结构设计

MySQL 是这个项目最合理的选择,因为考试系统的数据关系比较清晰,用关系型数据库管理事务和一致性优势明显。比如一场考试正式开始后,考生交卷时,后端需要同时更新考试记录状态、写入每道题的作答明细、累加成绩,这个动作必须在一个事务里完成,MySQL 的 ACID 特性可以保证不会出现“答题记录写了但成绩没算出来”的脏数据。

从表设计角度,一个在线考试系统至少需要这几张表:用户表(区分管理员、教师、学生角色)、试题表(存放题干、选项、正确答案、题型)、试卷表(定义试卷基本信息)、试卷题目关联表(或者直接用 JSON 字段存储选到的题目 ID)、考试记录表(记录某位考生某场考试的状态)、答题明细表(记录每个题目的作答内容和对错)。如果还要支持题库分类、知识点标签、成绩导出等功能,再额外扩展相关字段即可。

这里我想特别强调一下为什么要给“考试记录表”加状态字段而不是简单地用删除操作。因为考试场景里,“已开始”“进行中”“已交卷”“已判分”“已过期”这些状态不是用户随随便便就能改的,后端必须根据业务逻辑严格流转。比如考生从未进入考场时,状态可能是未开始;点了开始考试按钮,才生成一条进行中的记录;倒计时结束自动交卷后更新为已交卷;如果试卷里有主观题,还需要由教师手动评分,评分完成后状态再变为已判分。只有把状态建模清楚,前端页面和后端接口的行为才能保持一致。

2.4 前后端分离架构下,接口和权限到底怎么划

前端单独占用一个端口,后端单独占用一个端口,开发环境下通常让 Vite 代理后端接口,生产环境下则用 Nginx 反向代理。这个项目如果按标准方式搭,应该是前端只负责渲染和提交用户操作,后端只负责校验权限和业务逻辑,两边通过 JSON 格式交互。

权限划分是这类系统最容易做模糊的地方。比如学生只能看到自己参与或可参加的考试,教师只能出题和改卷,管理员能管理用户和所有考试。后端的 Controller 层必须做角色校验,不能依赖前端把按钮隐藏了就算安全。我以前见过不少项目,前端把管理入口隐藏了,但后端的接口只要知道路径,随便一个人就能访问,这是非常危险的做法。所以这套系统里你如果看到@PreAuthorize或自定义拦截器对/admin/**、/teacher/**路径做角色限制,属于正常且必须的设计。

3. 核心模块设计与实现要点

3.1 登录鉴权:从 Session 到 Token 的落地方案

在线考试系统涉及三类角色,后端必须能够识别出当前请求到底是谁、属于什么角色。传统做法是 Session + Cookie,后端把登录状态存在内存,前端带 Cookie 访问。但在前后端分离架构下,尤其是接口还可能被小程序或移动端复用时,基于 Token 的方式会更通用。

这套项目里常见的做法是登录成功后返回一个自定义 Token,前端存到本地存储或内存里,每次请求带上这个 Token,后端通过拦截器解析出用户 ID 和角色信息。与 JWT 结合的方案也很常见,JWT 的好处是服务器无状态,Token 本身携带过期时间和用户信息,但坏处是一旦签发,在过期前很难主动吊销。如果要实现“管理员可以把某个人踢下线”这种功能,还得引入 Redis 来维护一个黑名单或在线状态。

实操里给新手的建议:先别把鉴权想得太复杂,把登录接口调通、Token 能带在请求头里、后端拦截器能放行已登录用户,这三件事搞定,系统的权限骨架就立住了。至于刷新 Token、并发登录限制、密码加密策略这些,属于后续安全加固阶段的事,可以放在第二版再做。

3.2 试卷生成:手动组卷还是随机抽题

做在线考试系统,绕不开的问题是怎么把题目装进一张试卷。最简单的做法是教师手工从题库里一道道选题,固定下来,考生所有人都考同一张卷子。这个方案开发最简单,维护也直观,但有个明显缺陷:相邻考生之间可能互相传答案,不同批次考试的人也会用同一套题。

更好的做法是支持“固定题 + 随机题”混合组卷。比如一套试卷配置为单选题从指定题库中随机抽 20 道、多选题随机抽 10 道、判断题随机抽 10 道、主观题由教师固定指定 2 道。这套逻辑在后端实现时,通常就是按题型分组随机抽取,拼装成一份试卷存到考试记录里。需要注意,一定要在考试开始那一刻把本次考试的题目快照保存下来,而不是每次进入考试都重新随机加载一次。否则考生刷新页面后看到的是另一套题,成绩就完全没法算了。

从性能角度考虑,随机抽题的 SQL 不能写得太大。低于几千道题的题库,用ORDER BY RAND()勉强可以,但对于几万条数据,这种方式会全表扫描排序,非常不推荐。更好的做法是先算出只包含当前题型和难度条件的 ID 列表,然后用程序生成一个不重复的随机下标集合,再去按主键查询完整题目信息。

3.3 考试状态与倒计时:一刷新就回到上一步才算合格

考生开始考试后,会遇到“网络断了”“浏览器崩溃”“不小心关掉页面”这些突发情况。如果一刷新,作答记录全部丢失,需要重新开始,这种体验肯定不会有人接受。所以后端接口在设计上必须支持断点续考:考试记录里保存好已经开始的时间、截止时间、已经答过的题目明细,考生重新进入时,后端根据当前时间和截止时间算出剩余时长,把已答内容回显给前端。

倒计时的实现也要分两层。前端用setInterval每秒更新一次剩余时间,到 0 时自动触发交卷接口。但真正靠得住的还是后端的时间校验:后端在保存答案或提交试卷时,会校验当前时间是否已经超过考试截止时间,如果超了就拒绝继续作答,或者直接标记为超时交卷。这样做的好处是防止有人故意改本地电脑时间延长考试,或者绕过前端逻辑直接调用后端接口修改作答记录。把状态机和倒计时的校验逻辑放在后端,是我在这个项目里最看重的一点。

3.4 自动判分与手动评分如何和谐共存

客观题(单选题、多选题、判断题)判分逻辑不复杂,后端拿考生提交的答案和标准答案做比较即可。但这里有两个容易踩坑的细节:第一,多选题的判分要区分“完全一致才算对”还是“漏选、错选按比例得分”;第二,答案的存储格式必须统一,不能有的存AB,有的存A,B,有的又存成["A","B"],否则解析逻辑会越写越乱。

一套比较稳妥的存储方案是:选项用A|B|C|D这种带分隔符的字符串存储,提交答案也用相同格式,判分时先转成 Set 再比较,顺序不一致也不影响结果。主观题则需要教师手动评分,所以试卷表里要加一个字段表示总分和及格分,判分时只把客观题分数算出来,主观题分数先空着,等教师后台评分后再汇总,计算最终成绩。

成绩统计在数据库中应该尽量用一次查询汇总,而不是在 Java 里循环几十个学生算成绩。比如按考试 ID 分组查询每个考生的客观题得分,再关联主观题评分表,用一条 SQL 完成大部分统计,比逐个调接口快得多。

4. 实操:从零搭建并跑通这个项目

4.1 环境准备:先弄清楚你要面对的版本组合

跑这种“源码+数据库+文档”的项目,目录里通常都会有README.md或者部署文档,第一步一定不要跳过去直接启动代码。先把文档里写的 JDK 版本、Maven 版本、Node 版本、MySQL 版本记下来。

我推荐的基础组合是:

  • JDK 8 或 JDK 11(对应 Spring Boot 2.x)
  • Maven 3.6+,配好阿里云镜像仓库,避免首次下载依赖慢到怀疑人生
  • MySQL 5.7 或 8.0,字符集用utf8mb4
  • Node.js 16 或 18,npm 源切换到国内镜像
  • 如果是 Vue 3 + Vite,Node 版本不要低于 16

如果项目文档只写了“JDK8+”,但代码里其实引用了 Spring Boot 2.7,那你本地装了 JDK 17 也大概率能跑,因为 2.7 支持到 JDK 17 了。真正麻烦的是 Spring Boot 3.x 必须 JDK 17+,你把源码按 2.x 的思路改成 3.x,很多依赖会集体报错,所以尽量别在版本上搞创新。

4.2 导入数据库:看 SQL 脚本的顺序比执行本身更重要

数据库脚本一般会拆成两类文件:一类是school_student.sql这种建库建表和基础数据脚本,另一类是bill.sql这种带模拟数据的脚本。有的项目还会把初始化数据单独放在data.sql里。

实际操作时,我先在 MySQL 里手动创建一个空数据库:CREATE DATABASE exam_system DEFAULT CHARACTER SET utf8mb4;,然后执行源码包里的 SQL 文件。执行顺序如果乱了,可能出现“某张表不存在”的报错,因为后执行的脚本里可能有外键关联或依赖基础数据。建议用 Navicat 或命令行按文件名顺序逐个执行,看到每个文件执行成功的提示后再进行下一步。

导入之后,一定要看一眼后端配置文件里的数据源连接信息。默认配置往往写的是localhost:3306,用户名密码是root/root,如果你的本地环境不是这个设置,不改配置就直接启动,跑起来以后报的错都是“数据库连接失败”或者“Access denied for user”,非常浪费时间。

4.3 启动后端:Maven 命令的先后顺序别搞反

后端代码拿到手之后,我并不建议直接在 IDE 里点运行,而是先在命令行执行一次打包,这样做能提前暴露很多问题。在项目根目录运行:

mvn clean package -DskipTests

如果项目是分模块的,比如有exam-common、exam-system、exam-web这样的多模块结构,就需要先确认你所在的目录是根模块。打包成功后会生成target/*.jar,直接用java -jar启动也能跑,但日常开发调试还是在 IDEA 里找到启动类直接运行更方便。

启动日志里重点看三个信息:Tomcat 监听端口(默认 8080)、数据库连接是否成功、Mapper 有没有扫描到。如果启动一闪而过或者日志里出现红色异常,多半是配置文件里的 IP、端口、账号密码不对,或者某些依赖库版本冲突。

启动成功后在浏览器访问http://localhost:8080/,如果能出现 Spring Boot 默认的欢迎页或项目自定义的接口文档页,说明后端基本没大问题了。

4.4 配置并启动 Vue 前端:端口不同才是正常的

前端项目一般在前端目录或vue-exam下面。先把package.json里的依赖安装好:

# 进入前端目录 npm install

这个命令的执行时间取决于网络状况和依赖包数量,有的项目依赖了几百个包,装起来很慢。如果装了很长时间还卡住,建议把 npm 镜像换成国内源,或者删掉node_modules重新装一次。

安装完成之后启动开发服务器:

npm run dev

Vite 默认端口通常是 5173,Vue CLI 项目默认是 8080。后端的 8080 和前端开发服务器的 8080 如果冲突了,就不是小问题了。我遇到过一次,后端和前端都用 8080,结果后端先启动,前端再怎么编译都访问不到,页面白屏。解决办法很简单:给 Vue CLI 项目改端口,在vue.config.js里设置devServer.port = 8081;Vite 项目则在vite.config.js里配置server.port。

前端能打开后,先验证一下登录页,用管理员账号登录,如果能跳转到首页且菜单能点开,说明前后端联调基本通了。

4.5 联调与演示:别卡在最后的接口代理上

开发环境下,前端页面请求/api/login,但后端接口实际是http://localhost:8080/api/login,这个映射关系必须在 Vite 或 webpack 的代理配置里写好。如果代理没配,浏览器控制台会报跨域错误,接口请求发不出去,页面看着像是登录功能坏了。

常见的跨域处理有两种:后端配置 CORS(@CrossOrigin或全局 CorsFilter),或者前端开发服务器配置代理。我个人的习惯是开发阶段用前端代理,生产阶段用 Nginx 代理,后端一般不全局放开跨域,因为那样等于关闭了浏览器同源策略的保护,安全性会打折扣。

联调完成后,可以走一条完整演示路径:管理员创建一场考试,指定教师出题,给学生分配考试,学生登录进入考试页面,答完题交卷,教师登录后台对主观题评分,最后管理员或学生查看成绩报表。如果你拿到手的项目功能没那么全,也可以只走“管理员发布考试 -> 学生考试 -> 系统自动判分并展示成绩”这一段,核心链路跑通,项目的价值就已经体现出来了。

5. 常见问题与排查技巧实录

5.1 前端请求接口报跨域,到底先查哪个方向

跨域报错是前后端分离项目的头号敌人。浏览器控制台出现CORS policy或No 'Access-Control-Allow-Origin' header时,我的排查顺序是:先看请求到底发到了哪个地址。打开开发者工具 Network 面板,看请求的 URL 是不是localhost:8080,如果发现请求还指向 5173 前端端口,那十有八九是代理没生效,或者 vite 配置改了没重启。

第二步看后端是否真的收到了请求。如果后端收到了但响应头里没有跨域信息,那就是后端没配置 CORS,或者配置了但被拦截器提前截住了。有些项目的安全拦截器会在进入 Controller 之前直接返回 401,导致前端连跨域响应头都看不到,这时候需要先放行 OPTIONS 预检请求。

5.2 考试倒计时和交卷时间对不上

前端倒计时归零自动调交卷接口,结果后端记录的时间比前端显示的时间多了几秒,这其实是正常现象,因为网络请求本身有延迟。但如果差异太大,比如前端显示还有 20 分钟,后端却提示考试已截止,那就要检查系统时间是不是不一致了。

在后端起服务的那台机器上,用date命令查看系统时间,如果服务器时区设的不是Asia/Shanghai,Jackson 序列化时间的格式就会出问题,考试时间可能被转成 UTC 导致差 8 小时。处理办法是在后端配置文件里加spring.jackson.time-zone=GMT+8,数据库连接串里也可以加上serverTimezone=Asia/Shanghai来避免 MySQL 驱动解析时间异常。

5.3 数据库连接失败但账号密码明明没问题

这种问题通常出在 MySQL 驱动版本或者连接参数上。比如 MySQL 8.0 需要使用com.mysql.cj.jdbc.Driver,而老项目里写的是com.mysql.jdbc.Driver,就会启动直接报驱动类找不到。另外还要确认 MySQL 服务是不是真的启动了,如果是 Docker 跑的数据库,还要看端口有没有映射到宿主机。

一个容易被忽略的坑是数据库字符集。如果建表时用了错误的字符集或者排序规则,插入中文数据可能报Incorrect string value错误,所以创建数据库时我都会显式指定utf8mb4,而不是依赖默认配置。

5.4 Spring Boot 版本太高、依赖冲突怎么处理

这类问题在源码项目里太常见了。拿到项目第一件事,看pom.xml里用的 Spring Boot 版本是多少,再看本机 Maven 仓库里下载的依赖是什么。如果本地之前装过别的项目依赖,Maven 仓库可能缓存了版本信息,有时候删掉~/.m2/repository里对应目录重新下载才能解决奇怪的冲突。

遇到ClassNotFoundException或NoSuchMethodError,别急着怀疑代码逻辑,先查是不是某个依赖的版本被 Maven 仲裁成了不兼容的版本。在 IDEA 里按Ctrl + Shift + F10运行项目时,控制台如果出现APPLICATION FAILED TO START,通常会明确提示是哪个 bean 创建失败或哪个配置属性找不到,照着提示去排查基本上都能解决。

5.5 Vue 项目启动报 tsconfig 找不到或者依赖缺失

如果你拿到的前端源码是用 TypeScript 写的,启动时可能会报Failed to load tsconfig '@vue/tsconfig/tsconfig.web.json'这种错误,原因是项目里引用的 tsconfig 依赖包没装全。解决方式是在前端目录执行:

npm install

或者单独安装:

npm install -D @vue/tsconfig

Node 版本和依赖不匹配也可能导致启动直接崩,尤其是sass、node-sass这类原生模块。优先使用sass,因为它支持最新的 Node 版本;如果项目锁死了node-sass,大概率需要按照项目文档里指定的 Node 版本重新安装。

6. 对二次开发的一些实际想法

项目跑通只是第一步,真正把它用到实际考试场景里,还有不少值得完善的地方。如果让我在这个源码基础上继续做,我第一优先会加 Redis 缓存和分布式锁,尤其是在“同一考生同时提交试卷”这种极端并发下,后端必须保证同一场考试同一考生只生成一条有效的交卷记录,否则成绩数据会重复或错乱。用 Redis 的SETNX做简单防重提交,再配合数据库唯一索引兜底,是比较稳妥的思路。

第二优先是试卷的安全设计。现在的系统如果直接按固定试卷考,题库泄漏风险很大。改进方向包括按考生维度打乱选项顺序、同一套题每个人看到的选项顺序不同,以及限制考生的切屏次数和复制粘贴行为。这些功能看起来很小,但对实际使用的安全感提升明显。

第三,把成绩分析做厚一点。考试系统如果只有总分展示,价值比较有限。可以基于答题明细,按题型、知识点维度统计正确率,为教师调整教学重点提供数据依据。这部分功能并不复杂,后端多几个统计接口,前端用 ECharts 画图表即可。

对于网上提到的那些“源码怎么发给别人”的问题,我的建议是把后端打成 jar、前端构建出 dist 目录,连同 SQL 脚本和部署说明一起打包,对方只需要准备环境和数据库就能跑起来。千万记得把配置文件里的数据库密码和 Token 密钥在生产环境重新生成,别把测试环境的敏感信息直接带到线上。

我个人在实际操作中的体会是,这种“源码+数据库+文档”的项目,价值不在于代码多高深,而在于它把一整套完整业务闭环串了起来。先别急着改功能,按原样跑通一遍,了解每条数据是怎么流转的,再去调整、优化、二次开发,会比直接上手改代码高效得多。最后再分享一个小技巧:拿到任何新项目,先在本地建一个专门的环境变量文件或配置 profile,把所有本地环境和项目的默认配置区分开,这样无论怎么折腾,都不会弄脏原始代码,排查问题也能下手更快。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/9 4:35:29

测试员转型AI测试开发:从手工点点点到年薪60万的技术红利之路

我先说结论:这个问题的答案不只是“测试员能不能学AI”,而是“测试员凭什么能吃下这波AI红利”。如果你现在还在做纯手工功能测试,每天忙着点按钮、写用例、提bug,那这篇文章就是给你看的。我是山东菏泽的一名测试员,工…

作者头像 李华
网站建设 2026/10/9 4:35:21

叛乱:沙漠风暴Linux开服全指南:Ubuntu 22.04部署、systemd服务与崩溃排查

1. 为什么在Linux上开《叛乱:沙漠风暴》服务器不是“装个服务端就完事”? “叛乱2 linux服务器”“叛乱沙漠风暴怎么开服”——这两个搜索词背后,站着一群刚摸到Linux命令行、手握一台廉价VPS、满心期待拉上三五好友打一场硬核战术对抗的玩家…

作者头像 李华
网站建设 2026/10/9 4:33:50

国产工业IDE适配鸿蒙:嵌入式测试工具的技术突破与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/9 4:31:08

电梯电瓶车闯入报警:基于YOLOv8的实时目标检测实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/9 4:30:03

ThinkPHP与Laravel混搭架构的Vue敬老院管理系统开发实战

接手一个标题为“Thinkphp和Laravel基于VUE敬老院管理系统养老院”的项目时,很多人第一反应是:一个系统里怎么同时出现两个PHP框架?这其实一点不稀奇,很多实际业务系统就是历史代码加新模块迭代堆出来的,也可能是有两个…

作者头像 李华