
做工厂车间管理系统和我平时做商城、博客这类项目完全是两种心气儿。商城系统核心是流量和订单车间系统核心是别出错、能追溯、好操作——工人在车间里戴着劳保手套点按钮不可能容忍花里胡哨的交互但对数据准确性的要求极高。这套基于 SpringBoot2 Vue3 MyBatis-Plus MySQL8.0 的Java Web源码走的正是这个务实路子后端用老牌可靠的Java生态前端用现在社区活跃的Vue3全家桶数据库一步到位选MySQL8.0再配上完整的文档和SQL脚本非常适合做毕业设计、中小工厂数字化改造的起步项目也适合想从SSM框架跳到前后端分离架构的同学当范本拆解。这篇文章不吹项目多厉害只讲透它背后的设计逻辑、实操步骤以及那些文档里不会写的坑。在动手改代码之前你要先想清楚一件事一套车间管理系统本质上是在把工厂线下那套纸质工单口头沟通Excel表格的流程翻译成线上可追踪的数据流。翻译得好不好直接决定系统在车间里能不能真正被用起来。1. 项目整体定位与核心业务梳理1.1 车间管理系统解决什么问题车间管理的痛点其实很朴素计划员排了生产工单但工单下放到哪个班组、做到哪一步、用了多少料、产出多少合格品全靠Excel来回传或者车间主任的脑子记忆。一旦出质量问题要追溯翻纸质单据能翻到崩溃设备坏了什么时候保养、什么时候维修也没有明确记录库存里的物料看起来够实际上被几个工单同时领用谁也说不清。这套SpringBoot2Vue3项目做的就是把这堆线下流程搬到浏览器里。工单在系统里创建派工到具体班组生产完成后工人报工质检员录入检验结果物料库存实时扣减设备台账记录运行状态和保养计划。车间主任打开电脑就能看到今天哪些工单在进行、哪些设备在维修、哪批货质检出了问题。它的核心价值不是功能列表多好看而是让生产过程中的关键节点都有据可查、有迹可循。1.2 模块划分与业务边界这类管理系统最容易犯的错是模块划分过于抽象导致后面前后端接口写得不伦不类。我拆过这套项目的数据库脚本和后台菜单模块边界是相当清晰的大体分下面几块工单管理生产工单的创建、派工、进度跟踪、关闭是整个系统的主线数据。生产报工工人或班组对工单的实际完成数量、工时、不良数进行登记。设备管理设备台账、运行状态、保养提醒、维修记录。物料与库存管理物料档案、出入库记录、库存实时查询、安全库存预警。质量管理质检任务、检验结果、不良品记录、合格率统计。人员与权限管理用户账号、角色分配、菜单权限控制。统计分析工单按时完成率、设备利用率、质检合格率、个人产量汇总。每个模块边界很清楚工单管生产什么、做到哪了报工管实际干了多少物料管消耗了多少、还剩多少质量管合格了没有。业务边界一旦清楚数据库表怎么设计、接口路径怎么命名、前端页面怎么划分路由全部跟着清楚。1.3 角色权限与工作流设计权限这块项目用的是经典的 RBAC 模型用户属于角色角色绑定菜单和操作权限。工厂场景下角色划分建议至少四类管理员全部权限负责系统配置、用户管理。计划员创建工单、编辑物料档案、查看报表。车间主任派工、审核报工、查看生产进度。操作工只能看到被派给自己的工单提交报工和领料申请。这条权限链路上最关键的其实是操作工这个角色。车间现场人员对系统的容忍度很低你要是让他填十来个字段才能报完一个工他宁愿写在纸条上。所以实操中报工页面的字段一定要精简默认带出工单号、产品名称、计划数量工人只需要填实际完成数和工时甚至可以做成一键报工。工作流部分如果只用一个状态字段你会发现后面统计口径全乱。项目里的工单状态我建议这样设计新建 → 已派工 → 生产中 → 待质检 → 已完工 → 已关闭。其中待质检和已完工之间要允许质检不合格退回也就是状态回跳。数据库里通过状态值整数比如 0到5来表示前端下拉框和详情页展示对应的中文标签后端Service里再配一个状态流转校验方法防止跳步子。这一步做好了后面做看板统计会省非常多力气。2. 技术选型逻辑为什么是SpringBoot2 Vue3 MyBatis-Plus MySQL8.02.1 SpringBoot2追求可靠不追最新很多新手拿到代码第一反应是都2026年了怎么不用SpringBoot3答案很现实工厂内部系统对框架的新旧程度不敏感对可靠性和生态兼容性极其敏感。SpringBoot2.7.x搭配JDK8是目前存量Java项目最常见的组合之一任何第三方库的兼容问题少招人也好招遇到问题搜索引擎一搜一大堆答案。SpringBoot3强制JDK17起步虽然新特性吸引人但如果团队里还有人习惯写JDK8风格迁移成本其实不小。再者这个系统要跑在车间里很多时候部署的机器配置并不高甚至可能是一台老旧的Windows工控机。JDK8 SpringBoot2的内存占用和启动速度都比JDK17 SpringBoot3要友好。我个人的习惯是互联网高并发新项目可以追新框架企业内部管理系统永远选周围人最熟的组合。SpringBoot2在这套代码里承担的职责是自动装配数据源、内置Tomcat、统一异常处理、提供REST接口这些它都做得非常省心。2.2 Vue3 Element Plus后台管理页面的效率解Vue3这一层项目用的是组合式API script setup的写法配合Element Plus组件库。为什么这么选是因为后台管理系统90%的页面都是同一个套路左侧菜单、顶部栏、中间的表格 搜索条件 弹窗表单。Element Plus把表格、分页、下拉选择、日期选择、表单校验这些高频组件全都封装好了Vue3的ref和reactive又能让这些组件的状态管理写得非常简洁。和Vue2相比Vue3最大的优势不是运行速度变快了多少而是逻辑复用变干净了。以前在Vue2里做表格页得写一堆data、methods、computed混在一起的方法在Vue3里用组合式API把加载表格数据、处理分页、重置搜索条件各自封成一个函数一个页面的代码结构清晰很多。如果你是从Vue2转过来的看这套源码里的页面写法很容易上手因为Element Plus的组件用法和Element UI非常接近。2.3 MyBatis-Plus少写SQL但别丢掉SQLMyBatis-Plus在这套系统里的定位是数据库访问层的加速器。它提供的BaseMapper接口让你不需要为每个实体类手写一份基础的增删改查方法LambdaQueryWrapper又能用类型安全的Java代码构造带条件的SQL查询配合分页插件CRUD页面的后端代码量直接减半。但我想提醒一句MyBatis-Plus再强也只是CRUD层面的加速遇到复杂统计报表还是得回到XML手写SQL。比如车间大屏要统计近30天每天各班组的一次合格率这种带子查询、多表关联、按天分组的SQL用LambdaQueryWrapper硬拼只会把自己绕晕。所以这套项目中常见的设计是简单查询走BaseMapper自带方法复杂报表在Mapper层新增接口方法写XML里的自定义SQL。这样两种方式的优势都占到了代码也读得下去。2.4 MySQL8.0选它不是为了新是为了省事数据库选MySQL8.0而不是5.7最直接的原因是8.0的默认字符集就是utf8mb4不用你在建库时额外操心Emoji表情和生僻字其次是8.0对窗口函数、JSON字段、公共表表达式这些高级SQL特性的支持非常成熟后面做生产统计报表会非常爽。再一个原因是新装的MySQL8.0在安装和配置上确实比5.7多几个细节要注意但整体并不复杂具体的坑我在第五章里会详细说。对于车间管理系统这种数据量基本上是每天增加几千条记录级别的项目MySQL8.0单机部署完全够用不需要上什么中间件。要注意的核心其实是两件事建库时指定utf8mb4字符集连接串上正确配置时区和SSL参数后面你大概率会遇到的中文乱码、时间差8小时、应用启动报错全部能从这两件事里找到根源。3. 核心细节解析与实操要点3.1 数据库设计车间数据怎么建模拿到这套源码我建议你先别急着启动花半天时间把SQL脚本里的表结构关系画一遍。车间系统的核心表我理了一下大致是这样的逻辑链用户/角色表管账号权限工单表管生产指令报工表记录实际产出质检表记录质量结果物料/库存表支撑生产消耗设备表维护车间产能。工单表是绝对的主表它的字段设计决定了系统上半场能不能走得顺。我见过很多排序混乱的代码工单号居然没有唯一索引导致重复派工。项目里比较稳妥的设计是work_order表主键用自增ID单独设置order_no字段做业务唯一编号比如WO202404150001同时包含产品ID、计划数量、已报工数量、工单状态、计划开始时间、计划结束时间、创建人。为什么业务编号和主键要分开因为工单号要打印给车间看自增ID太容易被猜到也确实没有语义但数据库关联外键时用自增ID效率更高。其他表的关系基本都是围绕工单展开的比较典型的是报工表work_report一对多挂到工单下一个工单允许报多次累计完成数不能超过计划数量这个校验要么在Service层写Java逻辑要么在数据库层用触发器推荐前者。质检表quality_check和报工表一对一绑定质检不合格时更新工单状态回退到生产中同时记录不合格原因。物料进出表material_stock_log每次工单领料、退料都写一条流水库存表只保留实时总库存流水表用于追溯这批料发给了哪个工单。设备状态表equipment状态字段建议直接用字符串枚举跑比如NORMAL正常运行、MAINTENANCE保养中、FAULT故障比用0/1/2更直观。数据库层面还有一条建议不要用物理外键所有表关联都在业务层通过字段逻辑关联。车间系统每天有大量并发写入物理外键会在插入、更新时触发额外校验影响性能而且项目后期做数据归档、分表时物理外键会成为绊脚石。实际开发中大家都这么做放心用逻辑关联就好。3.2 后端核心实现从实体到接口的一整套套路SpringBoot2后端代码的基本结构是标准的三层架构Controller接收前端请求Service处理业务逻辑Mapper操作数据库。先把关键依赖加进pom.xml版本以SpringBoot2.7.18为例parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.7/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies注意 MyBatis-Plus 的3.5.x版本对 SpringBoot2 的兼容是最好的如果你拿的是最新的3.5.9记得看一眼是否引入了mybatis-plus-spring-boot3-starter那个是给SpringBoot3用的别搞混。application.yml里最核心的是数据源配置直接决定你能不能连上MySQL8.0spring: datasource: url: jdbc:mysql://localhost:3306/factory_mes?useUnicodetruecharacterEncodingutf8mb4useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl map-underscore-to-camel-case: true global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0这段配置里有三个关键点serverTimezoneAsia/Shanghai解决MySQL8.0和本地时区相差8小时的问题allowPublicKeyRetrievaltrue是为解决MySQL8.0的caching_sha2_password认证插件在部分客户端下报Public Key Retrieval is not allowed的问题logic-delete-field: deleted则是统一开启逻辑删除所有表都带一个deleted字段删除数据时自动变成更新防止误删后无法恢复这在车间数据里非常重要。实体类这块用 Lombok 的Data注解消除getter/setter样板代码配合 MyBatis-Plus 的TableName、TableId注解Data TableName(work_order) public class WorkOrder { TableId(type IdType.AUTO) private Long id; private String orderNo; private Long productId; private String productName; private Integer planQuantity; private Integer reportedQuantity; private Integer status; private LocalDateTime planStartTime; private LocalDateTime planEndTime; private Long createBy; private LocalDateTime createTime; TableLogic private Integer deleted; }Controller层的写法我强烈建议统一返回结构。给前端返回一个ResultT泛型封装包含code、msg、data三个字段成功是code200失败是code500前端 axios 拦截器统一处理。不要一会儿直接返回实体类一会儿又返回错误字符串后面联调你会被自己逼疯。典型的分页查询接口长这样RestController RequestMapping(/api/work-order) public class WorkOrderController { Resource private WorkOrderService workOrderService; PostMapping(/page) public ResultIPageWorkOrder page(RequestBody WorkOrderQuery query) { PageWorkOrder page new Page(query.getPageNum(), query.getPageSize()); LambdaQueryWrapperWorkOrder wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.hasText(query.getOrderNo()), WorkOrder::getOrderNo, query.getOrderNo()) .eq(null ! query.getStatus(), WorkOrder::getStatus, query.getStatus()) .orderByDesc(WorkOrder::getCreateTime); return Result.success(workOrderService.page(page, wrapper)); } }登录鉴权这块项目用的方案是JWT HandlerInterceptor 拦截器没有把 Spring Security 引入进来。为什么这么做因为车间系统功能本身不算复杂引入 Spring Security 的过滤器链、配置类、密码加密器学习成本和配置量都会变大而一个简单的拦截器配合JWT足够实现登录后放行、未登录返回401这个核心诉求。具体做法是登录成功生成一个带过期时间的token前端放到请求头Authorization里拦截器校验token有效后把用户ID塞到ThreadLocal里供后续业务代码获取当前登录人。JWT的密钥和过期时间放配置文件里别硬编码。3.3 前端核心实现Vue3后台页面的骨架前端的目录结构是典型的Vue3 Vite工程项目创建方式其实就是npm create vitelatest之后选vue模板。和Element Plus搭配有两条路全量引入和按需引入。对车间管理系统这种内部系统直接全量引入最省事项目能跑起来比什么都重要如果你追求首屏性能再用unplugin-vue-components做按需加载。路由用vue-router4因为后台管理系统有明显的登录前/登录后区别所以要配一个全局前置守卫没有token且访问的不是登录页统一重定向到/login。状态管理用 Pinia这是Vue3官方推荐的选择写法比Vuex简洁很多而且对TypeScript支持更好。需要存储在全局的状态其实不多用户信息、token、菜单权限、侧边栏折叠状态这几样就够了。axios封装是前端项目里最容易写乱的部分。建议单独建一个request.js做三件事设置baseURL通过环境变量区分开发/生产、请求拦截器里自动把token加到请求头、响应拦截器里统一处理code!200的错误弹出ElMessage和401状态跳转登录页。写完这个封装之后各个页面请求接口只需要写业务参数不用重复处理错误。工单列表页是典型的Element Plus表格页核心代码可以精简成这个套路script setup import { ref, onMounted } from vue import { getWorkOrderPage } from /api/workOrder import { ElMessage } from element-plus const query ref({ pageNum: 1, pageSize: 10, orderNo: , status: undefined }) const tableData ref([]) const total ref(0) async function loadData() { const res await getWorkOrderPage(query.value) tableData.value res.data.records total.value res.data.total } function handleSearch() { query.value.pageNum 1 loadData() } function handleReset() { query.value { pageNum: 1, pageSize: 10, orderNo: , status: undefined } loadData() } onMounted(loadData) /script页面上就是el-form搜索条件el-table数据列表el-pagination分页三件套。搜索表单里要注意el-input绑定的是字符串el-select绑定的是数字时要记得加:value绑定重置搜索条件时直接把query对象整体重新赋值比一个个字段清空更可靠但要注意如果用reactive定义整个对象重新赋值时要搞明白是替换了引用还是改了内部属性这就是Vue3新手最容易踩的坑之一。所以这个例子我用了ref定义对象重新赋值时就整个替换响应式不会丢。4. 实操过程与关键环节实现4.1 从零启动后端把项目从Git仓库拉下来后真正开始启动前环境准备顺序很重要。第一步装JDK8和MavenJDK版本建议就用8u202或者更高的小版本这里有一个隐形要求就是Maven的 setting.xml 里要配置阿里云镜像不然SpringBoot2.7依赖拉取会慢到让人怀疑人生。第二步安装MySQL8.0。如果你是在本地开发机上装安装msi包时选择Server only密码策略建议选Use Legacy Password Authentication也就是mysql_native_password模式后面Java连接时少一堆认证插件报错如果你已经装在Linux服务器上了可以在建用户时兼容处理CREATE DATABASE IF NOT EXISTS factory_mes DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 123456; FLUSH PRIVILEGES;注意utf8mb4_general_ci这个排序规则在MySQL8.0里依然可用对中文支持没问题。建完库后用source命令执行项目提供的SQL脚本一次性把表和初始数据导进去。第三步就是修改application.yml里的数据源用户名密码然后执行mvn spring-boot:run看到控制台输出Started Application in x.xxx seconds就说明后端起来了。可以用浏览器直接访问http://localhost:8080/api/work-order/page试一下如果返回401说明拦截器生效了正常的——还没登录。4.2 从零启动前端前端启动相对简单但对Node版本有要求。Vite 3及以上版本要求Node14.18如果你机器上还是老的Node 12直接npm run dev会报错。我建议直接装Node 18 LTS或Node 20 LTS兼容性最好。安装完Node后进入前端目录执行npm install这一步如果报各种网络错误那就换个npm镜像源再执行一次。依赖装完检查一下.env.development文件里VITE_API_BASE_URL是否指向后端的http://localhost:8080然后npm run dev浏览器打开Vite输出的地址看到登录页就说明前端环境没问题。用项目自带的管理员账号密码登录进去如果能进到首页并且菜单正常展示整个前后端链路已经通了。4.3 前后端联调与跨域处理前后端分离项目几乎必遇到跨域问题。原因是前端跑在http://localhost:5173Vite默认端口后端跑在http://localhost:8080浏览器同源策略直接拦下所有带X-Requested-With或自定义header的请求。解决跨域有两种常见做法做法一后端加CorsFilter。在SpringBoot里写一个配置类允许指定来源的跨域请求。这个方案适合前端部署地址固定的场景但如果你开Vite时随机换了端口来源就要跟着改。做法二前端Vite配置server.proxy把所有/api开头的请求转发到http://localhost:8080。这样做的好处是浏览器里看到的请求都是同源的跨域问题从源头消失也便于在本地联调时修改转发目标。我个人的习惯是开发环境用Vite proxy生产环境用Nginx做统一转发。既不要在开发时折磨自己也不要给生产环境埋隐患。后端呢照样配一个CorsFilter兜底双重保险反正代码不多Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(*) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }联调阶段最忌讳的是一个问题就不管了直接问对方。每次接口联调失败先看后端日志MyBatis-Plus开了控制台SQL日志能直接看到执行的SQL和参数再看浏览器Network里的请求payload和响应体基本能快速定位是参数格式不对、字段名不匹配还是后端逻辑报错。4.4 打包部署与运行验证本地开发跑通后部署到服务器上其实也就两步。前端先执行npm run build生成dist目录把它传到服务器的Nginx html目录下。然后Nginx配置一个Server块监听80端口将/api前缀的请求转发到后端服务的http://127.0.0.1:8080其他请求都走静态文件。后端打包执行mvn clean package -DskipTests生成target目录下的jar包传服务器后执行nohup java -jar factory-mes.jar --server.port8080 app.log 21 启动完成后按这个流程走一遍冒烟测试登录系统 → 创建一个生产工单 → 派工到某个班组 → 在报工页面提交实际数量 → 质检录入合格数 → 查看库存是否扣减、工单状态是否进入已完工。这一步能同时验证权限、工单状态流转、库存联动、前端表格刷新四条核心链路跑通后项目就可以交给业务人员试用了。5. 常见问题与排查技巧实录5.1 MySQL8.0连接坑问启动后端报Access denied for user rootlocalhost怎么办这个报错九成是数据库密码配错了但MySQL8.0还有一层坑就是认证插件。老项目里习惯用mysql_native_password8.0默认caching_sha2_password有些旧版JDBC驱动不认识新插件就会报错。解决方式有两个要么把驱动升级到mysql-connector-java8.0.x 并加上allowPublicKeyRetrievaltrue推荐因为8.0.x驱动原生支持新认证要么把用户的认证方式改回老模式。我在5.1节给的SQL就是后者的写法两条路都行但千万别再拿5.1.49之类的旧驱动去连8.0库。问数据库连接串里的serverTimezone不加会怎样会报一个The server time zone value йʱ is unrecognized或者更隐蔽的时间字段差了8小时。这是连接串上的Java时区和MySQL会话时区不一致导致的。解决方案就是我前面配置文件里写的serverTimezoneAsia/Shanghai这个配置要加在URL上不要写在项目代码里。问中文乱码怎么办乱码问题从数据库到应用一层层排查先确认建库字符集是utf8mb4再看连接串是否加了characterEncodingutf8mb4最后看前端页面本身的charset。我遇到过一种情况是数据库表是utf8mb4、连接串也加了但SQL脚本执行时终端本身编码是GBK导致导入的数据乱码。处理办法是用IDE以UTF-8编码执行SQL文件而不是在命令行直接source。5.2 MyBatis-Plus 分页和条件查询的坑问用Page做分页查询结果返回了所有数据分页没生效最高频的问题。MyBatis-Plus的分页功能不是默认开启的必须手动注入一个分页插件Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }漏掉这个配置IPage只能查出全量数据total还是0。写代码的时候要确认这个Bean被Spring容器扫描到别写在子模块里没被主启动类扫到。问LambdaQueryWrapper的like条件在某些场景下导致查询结果不对一个常见错误是like里的值本身没判空导致查出了所有记录。正确的写法是条件构造器的第一个参数写布尔表达式比如StringUtils.hasText(query.getOrderNo())为true时才拼接like否则不拼。不要先wrapper.like(...)之后再在SQL层面过滤那容易把条件拼重复。项目里还有很多拼接条件的地方你按eq、ge、le这几个方法配合判空表达式来写基本不会出问题。5.3 Vue3 前端问题问用reactive定义了一个表单对象提交后清空字段发现页面没有刷新这是Vue3响应式的一个经典陷阱。reactive的响应式原理是对象的属性变动才触发更新如果你直接Object.assign(form, {})会有效果但如果你form {}重新赋值等于把整个引用替换了原来的响应式代理对象就丢了内容自然也改不动。解决办法是定义对象时用ref或者清空时赋值Object.assign(form, {})或者把对象属性一个个重置。我这里建议在后台管理系统里能用ref定义的对象就用ref省心很多。问Vue3项目里用el-tabs想修改标签页的样式但一直不生效Element Plus的组件样式大部分都带scoped隔离你直接在当前页面style scoped里写.el-tabs__item { color: red }是无效的。需要额外写一个不加scoped的style块或者用:deep()穿透选择器比如:deep(.el-tabs__item) { color: red }。具体到车间这种内部系统样式不是核心功能看着顺眼能用就行别为了样式库的细节卡太久。问Vue3项目里用Element Plus动态添加删除表单行el-form表单行如何做校验比较常规的做法是定义form.items ref([])循环渲染表单字段绑定v-modelitem.name添加行时form.items.push({ name: , quantity: 1 })删除行时form.items.splice(index, 1)。注意el-form-item的prop路径用数组下标写法:propitems. index .name前面还要套:rules。一个比较隐蔽的坑是你用ref([{...}])初始化数组添加或删除后校验规则可能错位原因是表单的验证属性是渲染后才绑定的建议操作完数组结构后手动调用formRef.validateField(...)重新触发校验或者用clearValidate清理一下。问Vue3项目在Edge浏览器中有时候无法关闭浏览器右上角的最小化按钮这个问题不一定是项目代码的锅。优先检查是不是浏览器扩展拦截了快捷键或全局事件其次检查页面上有没有监听beforeunload事件、全局keydown事件挡住系统快捷键比如拦截了AltF4或CtrlW。我这个项目里没见过这个问题但如果用户反馈这个最稳妥的做法是升级Edge到最新版、禁用掉有冲突的扩展实在不行换个Chromium内核的浏览器实测大概率系统环境问题而不是项目本身。5.4 权限拦截与逻辑删除等杂项问拦截器把登录接口也拦了前端登录一直提示未认证配置拦截器的时候要在addPathPatterns(/**)的同时excludePathPatterns(/api/login, /api/captcha)把登录、验证码、静态资源这些路径放行。很多人漏了放行配置导致登录请求直接被拦排查半天也没找到原因。记住一个准则拦截器的作用是默认全拦按需放行不是按需拦截。问逻辑删除开启后删数据删不掉查询也查不到这是MyBatis-Plus开逻辑删除后的正常行为。删除操作实际变成UPDATE table SET deleted 1 WHERE id ?查询时自动带条件deleted 0。如果你还想查被逻辑删掉的历史数据需要自己写Mapper接口用Select指定自定义SQL绕过逻辑删除拦截器。这个功能对车间系统很有价值——历史工单不能真删只能标记作废所以逻辑删除是个保命设计。6. 文档使用、二次开发与后续扩展建议6.1 项目文档怎么读、怎么写标题里既然标注了【含文档】说明项目作者把环境说明、SQL脚本、接口列表、部署步骤都整理过了。我的建议是拿到文档后不要从头到尾泛读先把环境搭建和数据库初始化这两章看完把项目跑起来之后再看功能模块说明和接口列表。顺序反过来你会越看越困因为你对实际界面长什么样完全没有概念。如果是你自己要给这套项目补充文档建议至少包含四部分第一环境要求清单JDK版本、Node版本、MySQL版本、开发工具推荐第二快速启动指南数据库初始化SQL、后端启动命令、前端启动命令第三接口文档用Apifox或Postman导出标注好路径、请求参数、响应示例第四部署手册Nginx配置、jar包启动方式、日志查看命令。有这四样别人接手你的代码能省一半时间。很多开发者觉得写文档浪费时间但把这个项目当成简历项目投出去的时候面试官大概率更愿意看你能不能讲清楚业务和流程而文档正好是你思考过程的载体。6.2 后续可以扩展的功能方向项目跑通只是一个起点车间管理系统真正要落地还需要很多业务手感层面的补充。我从参观过的真实工厂经验和这个项目自身结构出发梳理几个高价值扩展方向扫码报工给每个工单生成二维码工人用PDA或者手机扫一下直接在一个页面完成报工和领料省去电脑端登录、找菜单、填单子的操作。这需要前端引入一个二维码识别组件后端加一个扫码入口接口。生产大屏看板车间里挂一块大屏实时显示今日工单完成率、设备状态、质量合格率。前端用ECharts或DataV做可视化后端开统计接口数据量小直接用定时统计或实时查询都行。消息提醒工单快要逾期、库存低于安全线、设备需要保养主动推送给相关角色。可以用WebSocket推送到前端也可以对接企业微信机器人的方式做消息通知。报表导入导出车间里Excel办公还是最普遍用EasyExcel把工单列表、报工明细、库存流水批量导出成Excel再把物料信息批量导入系统。这个几乎每个工厂都必需但很多源码没实现属于加分项。车间位置可视化如果工厂有多个车间、多台设备可以结合离线地图做设备定位和分布展示热词里提到的vue3 项目离线地图可打点其实就是这类场景。车间内部数据通常不允许外传所以要用离线地图方案显示效果更可控。这些扩展方向不是随便拍拍脑袋想出来的大部分来自工厂实际需求。如果你打算在这个项目上继续深入优先做扫码报工和报表导出这两个投入产出比最高。6.3 项目二次开发的一些个人体会最后分享一点我个人在车间项目里的体会。这类管理系统开发最忌讳的不是技术不行而是不懂业务流还硬套技术。一个工单从创建到关闭中间经历派工、领料、加工、质检、入库好几个环节每个环节谁负责、填哪些数据、出问题找谁全都有现实依据。代码只是把现实流程用数字方式重新表达了一遍所以你先要能跟车间主任聊天听懂他嘴里的工单派工报工到底指什么再动手设计表和接口。做二次开发的时候我习惯遵循三条原则。第一条先跑通主流程再动分支逻辑把工单创建到质检的链路跑顺再去做权限、统计、消息提醒这些外围功能第二条改别人的代码前先把状态流转画下来尤其是工单状态这种有分支有回退的字段不画清楚就动手改后面一定逻辑混乱第三条数据库字段要预留扩展位比如工单表加一个remark字段、设备表加一个ext_infoJSON字段未来接MES系统要补数据时不会弄得手忙脚乱。还有一个小工具层面的建议Vue3项目里调试定位代码效率不高的话可以装一个支持script setup的Vue DevTools插件配合浏览器的 source map点击页面元素就能快速跳转到对应的组件代码如果你在IDE里开发建议装一个Vue Language Features (Volar)扩展它对Vue3的支持比老旧的Vetur准确得多。这两个小配置不花十分钟但是每天帮你省下半小时找代码的时间很划算。我个人在实际操作中还有一个体会别把这类系统当算法项目做车间管理系统的代码CRUD是主体难点全在业务状态和人机交互细节上。与其纠结某个查询能不能优化到毫秒级不如把工单状态的每个流转分支都测试一遍把所有用户点错按钮会不会把数据搞乱的场景都填上校验这样的系统才真正能在工厂里活下来。把这几件事做到位这套源码在你手里就算物尽其用了。