SpringBoot+Vue公寓报修管理系统:从数据库设计到前后端联调完整指南
发布时间:2026/9/10 0:31:48来源:尧图网络
毕业设计做管理系统选题基本绕不开“报修”这个方向。原因很简单业务场景清晰、角色划分明确、前后台交互完整用来展示技术栈非常合适。但真拿到“SpringBootVue公寓报修管理系统”这个题目时很多同学反而卡住了——不是不会写代码而是不知道怎么把整个项目拆成模块一步步落地。这篇博文就围绕这个完整项目把结构设计、数据库脚本、后端接口、前端页面和排坑经验一次性讲清楚。这套系统面向的场景很典型公寓内住户发现设施故障水、电、门锁、网络等需要报修维修工接单处理管理员负责派单和监管。核心价值在于把线下口头报修、电话催单的传统流程转变为线上提交、自动流转、全程可回溯的闭环管理。如果你是计算机相关专业的学生正在做 Java Web 方向的毕设或者想快速搭建一个前后端分离的管理系统作为项目经验这套内容可以直接作为参考底稿。我拿到这类项目时第一件事不是打开 IDE 写代码而是先把业务角色和数据流向在纸上画一遍。这个习惯帮我省掉了后面大量的返工。1. 项目整体设计与技术选型思路1.1 为什么是 SpringBoot Vue 这套组合先解决一个很多人纠结的问题Java Web 项目到底选什么架构。JSP Servlet 确实是传统方案教程多但前后端代码耦合在一起改个页面样式要重启整个服务多人协作时还容易冲突。SpringBoot Vue 是当前企业里用的最多的组合之一也符合大多数高校对“新技术应用”的评分标准。后端用 SpringBoot核心优势是“约定大于配置”。你不用像 SSM 时代那样写一堆 XML 配置Maven 引入依赖后一个带 main 方法的启动类就能把整个 Web 服务跑起来。内嵌的 Tomcat 也省去了单独部署的麻烦。Spring Data JPA 或者 MyBatis-Plus 二选一都能把数据库操作简化到“写接口、调方法”的程度。前端用 Vue配合 Element UI 组件库能在短时间内搭建出风格统一的后台管理界面。Vue 的响应式数据绑定和组件化开发让列表页、表单页、详情页这类重复度很高的页面写起来非常顺手。而且 Vue Router 做页面跳转、Vuex/Pinia 做全局状态管理都是面试常考点写在简历上是有分量的。1.2 报修业务的角色与流程拆解这套系统的业务不算复杂但涉及的角色和状态很典型。我按角色拆一下住户报修人注册登录后提交报修单描述故障类型、位置、问题详情查看自己提交的工单处理进度维修完成后进行确认和评价。维修工查看被分配到自己名下的工单更新处理状态接单、到场、维修中、完成填写维修结果说明。管理员维护公寓楼栋和房间信息审核住户注册管理维修工账号查看所有报修工单进行派单和回访。状态流转是这类系统的灵魂。我建议用状态机的方式去理解而不是简单加一个 status 字段就完事。报修单的生命周期大概是待分配 → 已派单 → 维修中 → 待验收/已完工 → 已完成 → 已评价。每一步操作都有对应的角色权限前端根据当前状态决定显示哪些按钮后端在接口里再次校验状态合法性这样双保险能挡掉绝大多数逻辑漏洞。1.3 技术栈选型与版本匹配经验技术选型这块有个实际坑SpringBoot 版本不能乱选。毕设场景下我推荐用 SpringBoot 2.7.x 配合 JDK 1.8 或 11这是一个非常成熟的组合网上资料也最全。不要一上来就追 SpringBoot 3.x那个版本要求 JDK 17而部分学校的教学环境还停留在 JDK 8而且很多第三方框架的兼容性文档更新不及时。整体技术栈清单如下后端SpringBoot 2.7.x、MyBatis-Plus、MySQL 8.0、JWT、Swagger/Knife4j 接口文档前端Vue 2.6.x Element UI、Vue Router、Axios、ECharts可选统计报表用工具Maven 3.6、Navicat数据库管理、Postman接口测试Vue 2 和 Element UI 是毕设最稳妥的搭配虽然 Vue 3 已经是主流但 Element UI 的组件文档、社区问答和现成模板数量仍是 Vue 3 生态比不了的。考虑到开发周期选成熟组合更实在。2. 数据库设计与 SQL 脚本的核心实现2.1 表结构设计与字段规划数据库是整套系统的基础表结构设计得好不好直接决定后续开发效率。我见过不少同学上来就建一张大表把所有信息堆在一起结果后面每一步都在为这个决定还债。这套公寓报修系统至少需要六张核心表。我需要特别说明几点用户表system_user不要直接叫 user 表因为 user 在 MySQL 里是保留字虽然加反引号能用但容易埋雷。字段包含 id、用户名、密码BCrypt 加密后存储、姓名、联系电话、角色标识1 住户/2 维修工/3 管理员、所属房间ID、头像地址、状态、创建时间。房间表apartment_room记录楼栋号、单元号、房间号和当前住户ID。这张表关联用户表帮助管理员快速定位报修位置。注意房间号要设计成唯一键否则会出现一房多主的数据混乱。报修单表repair_order这是核心业务表。字段包括报修单号可以用时间戳随机数生成、报修人ID、房间ID、报修类型水电/门窗/网络/家电等、故障描述、图片地址支持多图用逗号分隔、紧急程度普通/加急、状态字段、派单维修工ID、维修结果说明、住户评价、各状态的时间节点。状态字段我建议用 tinyint 类型存数字0到6分别代表不同状态前端根据数字映射成对应的文字标签和按钮状态比直接存中文更规范。维修工信息表repair_worker关联用户表记录维修工擅长的技能类型水电/木工/综合等、接单数量、评价得分。这张表主要是为了派单时筛选合适的人如果简化的话也可以直接靠用户表的角色字段区分。操作日志表repair_log记录每张工单从创建到完成的每次状态变更。包括工单ID、操作人ID、操作前状态、操作后状态、操作时间、备注说明。这张表一开始很容易忽略但毕设答辩时老师很爱问“如果住户和维修工对进度有争议怎么办”有日志表就是有力的证据支撑也体现了系统设计上的完整性。通知消息表notification站内信功能比如工单被分配后通知维修工、维修完成后通知住户。字段包含接收人ID、标题、内容、关联工单ID、是否已读、创建时间。2.2 SQL 脚本内容与关键写法完整的 SQL 脚本应该包含建库语句、建表语句、初始数据语句和索引设计。生产级别的脚本建议用 Navicat 执行前先备份开发阶段直接在本地 MySQL 执行即可。下面给出报修单表和操作日志表的建表脚本这是整套系统最核心的两张表-- 报修单表 CREATE TABLE repair_order ( id bigint(20) NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 报修单号, user_id bigint(20) NOT NULL COMMENT 报修人ID, room_id bigint(20) NOT NULL COMMENT 房间ID, repair_type varchar(20) NOT NULL COMMENT 报修类型, description varchar(500) DEFAULT NULL COMMENT 故障描述, images varchar(1000) DEFAULT NULL COMMENT 图片地址,多张逗号分隔, priority tinyint(1) DEFAULT 0 COMMENT 紧急程度 0普通 1加急, status tinyint(1) NOT NULL DEFAULT 0 COMMENT 状态 0待分配 1已派单 2维修中 3待验收 4已完成 5已评价 6已取消, worker_id bigint(20) DEFAULT NULL COMMENT 维修工ID, repair_result varchar(500) DEFAULT NULL COMMENT 维修结果说明, rating tinyint(1) DEFAULT NULL COMMENT 住户评分 1-5, create_time datetime NOT NULL COMMENT 创建时间, assign_time datetime DEFAULT NULL COMMENT 派单时间, finish_time datetime DEFAULT NULL COMMENT 完成时间, update_time datetime NOT NULL, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_user_id (user_id), KEY idx_worker_id (worker_id), KEY idx_status (status) ) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8mb4 COMMENT报修工单表;建表有几个细节值得关注所有表引擎用 InnoDB支持事务防止高并发下数据不一致字符集用 utf8mb4 而不是 utf8因为 utf8 在 MySQL 里最多存 3 字节遇到生僻字或特殊符号会报错时间字段用 datetime 而不是 timestamp后者有 2038 年问题且受时区影响逻辑外键加索引但不建物理外键约束——理由是物理外键会导致删除用户时报“有关联数据无法删除”给数据维护和测试带来大量麻烦。我在代码层面保证引用完整性比数据库层面的物理约束灵活得多。初始脚本也需要准备一个管理员账号密码用 BCrypt 加密后的字符串、几个测试房间、两个测试维修工账号和三个测试住户账号。这样系统跑起来就能直接演示完整流程不用从注册开始一步步走。2.3 状态字段设计与状态机约束状态字段我单独拿出来说因为这是最容易写崩的地方。很多同学的代码里状态就是一堆 if-else没有人能说清楚从 0 到 3 经过了哪些合法路径结果就是用户点了几下页面数据变成了垃圾。我建议在项目里用一个常量类定义状态和合法流转路径public class RepairStatus { public static final int PENDING 0; // 待分配 public static final int ASSIGNED 1; // 已派单 public static final int PROCESSING 2; // 维修中 public static final int PENDING_ACCEPT 3; // 待验收 public static final int COMPLETED 4; // 已完成 public static final int EVALUATED 5; // 已评价 public static final int CANCELED 6; // 已取消 private static final MapInteger, ListInteger TRANSITIONS new HashMap(); static { TRANSITIONS.put(PENDING, Arrays.asList(ASSIGNED, CANCELED)); TRANSITIONS.put(ASSIGNED, Arrays.asList(PROCESSING, CANCELED)); TRANSITIONS.put(PROCESSING, Arrays.asList(PENDING_ACCEPT)); TRANSITIONS.put(PENDING_ACCEPT, Arrays.asList(COMPLETED)); TRANSITIONS.put(COMPLETED, Arrays.asList(EVALUATED)); // 终态已评价、已取消 } public static boolean canTransition(int from, int to) { ListInteger targets TRANSITIONS.get(from); return targets ! null targets.contains(to); } }后端在更新状态前先调用 canTransition 校验非法流转直接抛业务异常前端通过全局异常处理器返回统一错误信息。这样一个机制能让整个系统的状态永远保持清晰也方便在答辩时讲解“状态机设计”这个亮点。3. 后端核心接口设计与实现3.1 接口文档与 Swagger 集成接口文档是标题里明确提到的交付物也是能体现专业度的地方。很多同学写完了系统才补文档结果要么忘记接口的入参含义要么文档和实际代码不一致。我建议用 Swagger 注解直接在代码里生成文档保持文档和代码同步更新还能通过页面直接调试接口。集成方式很简单引入 Knife4j 依赖Swagger 的增强版界面更友好然后在启动类或者配置类上加 EnableSwagger2 和 EnableKnife4j 注解。接口地址默认是 /doc.html。配置项里需要注意设置 api-info 的标题和描述比如“公寓报修管理系统接口文档”版本号填 1.0。后端每个接口都应该写清楚请求方式、请求路径、请求参数含义、返回数据结构、权限要求。比如报修单分页查询这个接口应该标注是管理员权限还是住户权限参数 pageNum 和 pageSize 的含义和默认值返回的数据里 status 字段的取值说明。这些信息在答辩时可以直接展示给老师看比口头描述有说服力得多。3.2 JWT 登录认证与接口安全控制管理系统必须有登录认证否则任何人都能调接口改数据。JWT 是目前前后端分离项目最主流的认证方案核心思路是用户登录成功后后端生成一个包含用户信息和过期时间的加密 Token 返回给前端前端把 Token 存在 localStorage 里每次请求在请求头加上 Authorization: Bearer Token后端通过拦截器解析 Token识别当前用户身份和权限。SpringBoot 里实现这个功能我习惯用 HandlerInterceptor 加 SpringBoot 自带的拦截器注册机制而不是引入 Spring Security——Spring Security 的学习曲线太高毕设阶段用它容易陷入配置地狱。自定义拦截器更轻量也方便理解背后的原理。核心代码示意Component public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录接口和 OPTIONS 预检请求 if (request.getRequestURI().contains(/login) || OPTIONS.equals(request.getMethod())) { return true; } String token request.getHeader(Authorization); if (StringUtils.isBlank(token)) { throw new BusinessException(未登录或登录已过期); } // 解析 Token把用户信息放入 request 上下文 Long userId JwtUtil.parseToken(token); request.setAttribute(userId, userId); return true; } }权限控制可以在 JWT 解析后结合注解或路径匹配来判断角色。比如路径 /admin/** 开头的接口只允许管理员角色访问。用拦截器在进入 Controller 前统一做校验Controller 里只需要写业务逻辑不需要每个方法重复做权限判断。3.3 报修全流程涉及的接口梳理整个报修流程涉及后端一整套接口我用列表梳理一下核心接口及其权限要求POST /api/auth/register住户注册参数包含用户名、密码、姓名、手机号、房间IDPOST /api/auth/login登录返回 Token 和用户基本信息POST /api/repair/create提交报修单住户权限GET /api/repair/page分页查询报修单管理员可查全部住户只能查自己的POST /api/repair/assign管理员派单参数包含工单ID和维修工IDPOST /api/repair/updateStatus更新工单状态不同角色传入不同目标状态POST /api/repair/evaluate住户评价参数包含工单ID、评分和评价内容GET /api/repair/statistics查询统计信息用于首页 ECharts 报表展示GET /api/log/list查询操作日志管理端每个接口的 Controller 层尽量保持轻量业务逻辑放在 Service 层。比如派单接口Service 层要做的不仅仅是更新工单的 worker_id 字段还要修改状态为已派单、往日志表插入一条操作记录、给维修工发一条通知消息。这些操作应该放在同一个事务方法里任何一个步骤失败都要回滚否则会出现工单显示已派单但通知没发出去的数据不一致。3.4 参数校验与全局异常处理很多同学的项目一跑起来用户随便输入个非法参数系统就报 500 错误。这个问题其实很好解决Controller 方法的入参加上 Validated 注解实体类字段加上 NotNull、NotBlank、Min、Max 这类校验注解。比如创建报修单时报修类型不能为空故障描述长度在 5 到 500 之间分页查询的 pageNum 不能小于 1。全局异常处理器也很有必要。我一般建议捕获三类异常业务异常BusinessException例如“工单状态不允许该操作”code 为 500返回明确提示参数校验异常MethodArgumentNotValidException收集所有校验失败的字段和提示信息一次性返回给前端兜底异常Exception记录日志返回“系统繁忙请稍后重试”避免把详细异常栈泄露给用户全局异常处理器用 RestControllerAdvice 注解实现配合统一返回结果类 Result 包含 code、message、data 三个字段前端 Axios 拦截器根据 code 判断请求是否成功统一弹出错误提示。这套机制做出来之后后端代码会清爽很多前端也不用在每一个请求回调里处理错误。4. 前端 Vue 核心功能实现4.1 Vue 项目初始化与环境配置前端项目初始化建议直接使用 Vue CLI 脚手架创建。创建后第一件事是安装 Element UI、Axios、Vue Router 和 Pinia/Vuex然后配置路径别名在 vue.config.js 里配置开发环境代理把 /api 前缀的请求转发到后端 8080 端口。跨域问题是前后端分离项目的经典坑。开发环境只要在 vue.config.js 里配置代理就能解决module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }生产环境部署时前端打包成静态文件后放入 NginxNginx 配置反向代理到后端端口。给个简化配置前端代码放在 /usr/share/nginx/html接口代理到 localhost:8080。这样部署完成后整个系统通过 80 端口就能访问不需要暴露后端端口。4.2 核心页面与组件拆分方案按照业务角色前端页面大致分为以下几块登录/注册页表单校验、密码加密传输实际项目走 HTTPS 更安全开发环境用 HTTP 就行。登录成功后保存 Token 到 localStorage同时根据角色路由跳转。住户端首页展示我的报修单列表。核心是状态标签的展示用 el-tag 组件不同状态显示不同颜色待分配灰色、已派单蓝色、维修中橙色、已完成绿色。列表数据用 el-pagination 分页组件配合后端分页接口。报修单提交页表单包含报修类型下拉选择、故障描述文本域、紧急程度单选、图片上传配置 Element UI 的 el-upload 组件上传完成后把返回的图片地址拼接到表单里。提交成功后跳转到列表页提示“报修单提交成功”。管理员工作台包括 ECharts 报修统计图表按类型、按状态的柱状图和饼图、工单管理表格支持按状态筛选和关键词搜索、派单对话框选择维修工后提交、住户和维修工账号管理页面。维修工端页面待接单列表和我的工单列表。看到新工单后可点击“接单”维修完成后填写处理结果点击“完工”。组件拆分上把报修单表格封装成 RepairOrderTable 组件props 传入角色和筛选条件emit 出去操作事件。这样住户端、管理员端、维修工端都能复用同一个表格组件只是操作按钮不同。4.3 状态展示与按钮显隐的联动方案状态字段在前端的映射是很容易出错的点。我建议用一个统一的映射文件来管理export const repairStatusMap { 0: { text: 待分配, tagType: info }, 1: { text: 已派单, tagType: primary }, 2: { text: 维修中, tagType: warning }, 3: { text: 待验收, tagType: warning }, 4: { text: 已完成, tagType: success }, 5: { text: 已评价, tagType: success }, 6: { text: 已取消, tagType: danger } } export const canOperate (status, role) { if (role admin) { return [0].includes(status) // 待分配状态可派单 } if (role worker) { return [1, 2].includes(status) // 已派单可接单维修中可完工 } if (role user) { return [4].includes(status) // 已完成可评价 } return false }页面模板里用 v-if 判断按钮显隐逻辑全部收敛到这个工具函数里。这样新增一个状态时只需要改一处映射文件不用满页面找按钮判断逻辑。4.4 Axios 请求封装与 Token 管理前端请求这块我建议封装一个统一的 request.js 模块配置基础 URL、请求拦截器和响应拦截器。请求拦截器从 localStorage 读取 Token 并加到请求头响应拦截器统一处理返回结果code 为 200 时直接返回 datacode 为 401 时跳转到登录页并清空本地凭证code 为 500 时用 Element UI 的 Message 组件弹出错误提示。这样一个封装之后业务代码里只需要写简单的调用逻辑// 页面中创建报修单 const res await createRepairOrder(formData) if (res) { this.$message.success(报修单提交成功) this.$router.push(/my-repairs) }不需要在每一个页面里写重复的请求头处理、错误弹窗逻辑。而且上线后如果接口地址变了只需要改 request.js 一个文件。5. 常见问题与排查技巧实录5.1 数据库连接与 SQL 脚本执行问题问题 1执行 SQL 脚本时提示“Unknown database”或者数据表创建失败。排查思路先确认是否已经创建了对应的数据库。脚本文件开头应该是 CREATE DATABASE IF NOT EXISTS repair_system如果没有这条语句需要先手动建库然后选中库再执行建表语句。另外检查字符集设置推荐在脚本开头加 SET NAMES utf8mb4。问题 2插入中文数据时报错“Incorrect string value”。原因大概率是表或者字段的字符集不是 utf8mb4。检查方式用 SHOW CREATE TABLE repair_order看 DEFAULT CHARSET。建表时统一指定 ENGINEInnoDB DEFAULT CHARSETutf8mb4 就能避免。问题 3修改表结构后旧数据乱了。开发迭代过程中改表结构非常频繁我建议给关键表维护一份变更脚本比如 V1.0__init.sql、V1.1__add_field.sql 这种带版本号的文件。MySQL 没有像 Flyway 那样的数据库版本管理除非自己集成所以手动维护变更记录是团队协作和答辩时展现专业度的加分项。5.2 前后端联调的经典报错问题 1前端请求接口报 404。先确认接口的访问路径在 Controller 里是否存在。很多人把 RequestMapping(/admin/repair) 和 PostMapping(/page) 拼起来的路径理解错了可以在浏览器直接访问 http://localhost:8080/swagger-ui/index.html 检查实际生成的接口地址对照前端配置基本就能定位问题。问题 2跨域报错“Access to XMLHttpRequest at ... from origin ... has been blocked by CORS policy”。开发环境没有配置 vue.config.js 代理或者配置了代理但请求路径里没有带 /api 前缀。最简单的方法是前端所有请求都发到 /api 开头的路径vue.config.js 里把 /api 代理到后端同时后端配置一个 CorsFilter 允许跨域双保险。问题 3传递 JSON 数据时后端接收不到字段值。确认请求头 Content-Type 是 application/json后端用 RequestBody 接收如果 Content-Type 变成 application/x-www-form-urlencoded后端就需要用 RequestParam 接收。Axios 默认对普通对象会转成 JSON 发送但如果用了 qs 库序列化可能会变成表单格式这个细节值得留意。问题 4日期字段返回给前端显示为时间戳。SpringBoot 默认的 Jackson 配置对 LocalDateTime 序列化格式不是“yyyy-MM-dd HH:mm:ss”需要加 JsonFormat(pattern yyyy-MM-dd HH:mm:ss, timezone GMT8) 注解或者在 application.yml 里配置 spring.jackson.date-format 和 local-date-time-format。5.3 权限与信息安全注意事项JWT 密钥不能硬编码在代码里。实际项目中应该放在配置中心或者环境变量里。毕设项目可以把 JWT 的 secret 放在 application.yml 中但至少不要明文出现在 github 上。密码存储必须用加密算法。很多项目直接明文存密码这是严重的安全隐患。实际验证时用户表被拖库明文密码就直接泄露了。我用 Spring Security Crypto 提供的 BCryptPasswordEncoder加密后的字符串包含盐值同一个密码每次加密结果都不同安全性比 MD5 高几个量级。这也是答辩时值得一说的地方。用户输入的数据尤其像报修描述、评价内容这类文本字段要给后端加个简单的长度校验防止恶意大对象攻击。图片上传功能也要限制文件类型和大小只接受 jpg/png 格式、单文件不超过 5MB否则有人直接传一个 2GB 的视频服务器磁盘会被瞬间塞满。5.4 打包部署与线上运行避坑SpringBoot 后端打包用 Maven 命令 mvn clean package -DskipTests生成 jar 包后直接 java -jar 启动。注意打包时如果用到外部 Tomcat 的同学SpringBoot 内嵌容器不需要额外配置。前端打包用 npm run build生成 dist 目录。部署时有两个细节容易踩坑一是 Vue Router 如果使用 history 模式Nginx 需要配置 try_files $uri $uri/ /index.html否则刷新页面会 404二是接口代理要确认代理路径和目标地址正确。Nginx 配置参考server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }如果部署在云服务器上记得在安全组放行 80 和 8080 端口。MySQL 尽量不要用 root 账号直连单独创建一个专属账号并授权只允许访问 repair_system 库这也是基本的运维习惯。5.5 毕设答辩时老师常问的难点最后聊聊答辩环节。这套系统老师大概率会问几个问题提前准备充分会从容很多Q报修单状态是怎么控制的A用状态机模型定义了 0 到 6 七个状态和合法的流转路径后端 Service 层更新状态前强制校验非法流转直接抛业务异常。同时前端按钮的显隐也和后端保持一致。Q如果住户提交报修后一直没人派单怎么办A可以在定时任务中扫描状态为待分配且创建时间超过设定时长的工单自动上报或告警。这个点可以作为系统的扩展功能代码里预留了定时任务的配置位置。Q并发情况下两个人同时操作同一张工单怎么办A比如管理员 A 和 B 同时给同一张工单派了不同的人工最后以谁为准这个问题的答案是在更新工单时使用乐观锁机制在表中加一个 version 字段更新时检查 version 是否匹配不匹配则提示“操作冲突请刷新后重试”。这正好体现对并发控制的理解。Q系统有什么扩展空间A可以扩展为公寓门禁报修联动、维修工绩效统计、报修备件库存管理或者加入消息推送邮件/SMS/微信模板消息。可以说设计初期已经预留了消息表和相关字段后续扩展不需要改动现有表结构。我在实际操作中的体会是这套系统最考验人的不是某一个技术点而是怎么把流程和状态梳理清楚让多个角色在一个系统里顺畅协作。很多项目做到一半推倒重来不是因为代码写不出来而是业务逻辑含糊页面按钮乱跳数据状态一团乱。如果你正在做类似的系统先花一天时间把角色、状态、流转和页面关系理清楚后面开发会顺畅很多。
网站建设高端定制企业官网