资讯动态

Vue + Spring Boot 大棚蔬菜管理系统开发实战

发布时间:2026/9/19 18:44:25 来源:尧图企业网站定制
简介这份资源是一篇基于SpringBoot与Vue.js的大棚蔬菜管理系统Java毕业论文文档主要面向计算机相关专业学生、毕业设计写作者以及需要了解全栈管理系统的开发者。文档从课题背景、需求分析、数据库设计到前后端开发与系统测试做了完整梳理核心围绕用户与管理员双角色权限体系展开覆盖蔬菜种类管理、蔬菜信息管理、智能调温、自动灌溉、在线客服、通知公告、订单管理等业务模块并给出了MVC模式、RESTful API、OAuth2.0以及前后端分离开发的实际应用说明。包体为单个docx文件容量5.32MB包含完整的论文正文、目录、中英文摘要与关键词排版结构清晰适合直接参考撰写或调整章节框架。目前已有158人浏览学习文中还对权限管理、数据安全和用户界面设计等做了专门讨论能帮助读者明确系统架构、数据库表结构及接口实现要点为完成类似毕业设计或课程项目提供一条可落地的参考路径。1. 用 Vue 和 Spring Boot 做大棚蔬菜管理系统毕业论文从哪开始毕业设计里最常见的题目模板就是“基于 Vue 和 Spring Boot 的 XX 管理系统”前端 Vue 管页面交互后端 Spring Boot 管业务数据和数据库读写Java 把这两层串在一起。大棚蔬菜管理系统要覆盖的功能通常包括蔬菜品种维护、大棚档案、环境监测、采收记录和库存统计看起来功能点不少但技术边界其实很清晰。给正在准备论文的人一个建议别急着找一套看不懂的源码再来编文档先把数据表定下来把接口返回格式定下来再把页面接上去整个项目就能立住。这套做法适合两类人一类是拿这个题目做毕业设计的学生另一类是刚接触前后端分离项目的 Java 工程师想用一个小而完整的业务练手。下面会按这个顺序走一遍先定数据表再写接口最后接页面。2. 数据库模型和后端骨架大棚蔬菜管理系统的数据怎么设计前后端分离项目最怕的不是功能多而是后端返回的字段每次都不一样。前端页面一多后端接口一套又一套最后数据对不上答辩时一演示就翻车。所以第一阶段要先把技术版本、数据库表、接口形态三件事定住后面写代码才会顺。2.1 为什么用 Spring Boot 而不是 SSMJava 后端的选型边界很多课程设计里用的是 SSM也就是 Spring MVC 加 Spring 加 MyBatis。SSM 不是不能用但配置文件多新建一个项目要先把 web.xml、spring-mvc.xml、mybatis-config.xml 排一遍还没写业务就花掉两天。Spring Boot 用 starter 把依赖和自动配置打包好一个 application.yml 就能启动。Spring Boot 本质还是 Spring核心仍然是 IOC 容器和 AOP这两个概念在答辩、面试里都躲不掉用 Spring Boot 做毕设既省配置又能把原理讲清楚。版本边界需要特别注意。Spring Boot 3.x 是当前的新版本但它要求 Java 17 起步。如果实验室电脑只装了 JDK 8别直接把 Spring Boot 3.2 拉进来创建完项目连编译都过不去报错信息大多是 UnsupportedClassVersionError 或者 Maven compiler 版本不匹配。更稳妥的选择是 Spring Boot 2.7.x它是最后一个完整支持 Java 8 的版本配合 JDK 8 和 MySQL 8 跑毕业设计完全够用。提示新建项目前先确认 JDK 版本。JDK 8 对应 Spring Boot 2.7.xJDK 17 及以上才建议用 Spring Boot 3.x。前端也类似Vue 3 配 Vite、vue-router 4、Element PlusVue 2 配 vue-cli、vue-router 3、Element UI混着安装依赖就会报版本兼容错误。2.2 数据表设计从蔬菜品种到环境监测记录大棚蔬菜系统最少要有五张表蔬菜、大棚、环境记录、种植批次、采收记录。拆表的原则是变化频率不同的数据别放一起。环境监测记录每天可能产生几十条而大棚档案一年可能只改两次如果塞在同一张表里查询温湿度趋势时要连带上不相关的大棚信息分页也会变慢。表名用途核心字段vegetable蔬菜品种与库存id, name, category, unit, price, stockgreenhouse大棚基础信息id, name, location, area, managerenvironment_record温湿度监测记录id, greenhouse_id, temp, humidity, soil_moisture, recorded_atplant_batch种植批次id, greenhouse_id, vegetable_id, planted_date, expected_date, quantityharvest_record采收记录id, batch_id, harvested_date, quantity, operator其中 plant_batch 和 harvest_record 是为了把一次种植和一次采收绑定起来。比如要算某批生菜的产量就从 plant_batch 查出这批种了多少再从 harvest_record 累加产了多少。如果只保留蔬菜表和采收表这个统计口径就说不清楚论文里也会少一个能讲的业务点。2.3 建表 SQL 和项目目录结构常见的做法是用 MySQL 8字符集用 utf8mb4。下面是 vegetable 表的最小建表语句其他表按同样的风格扩展。CREATE TABLE vegetable ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 主键, name VARCHAR(50) NOT NULL COMMENT 蔬菜名称, category VARCHAR(30) COMMENT 分类如叶菜、果菜, unit VARCHAR(10) DEFAULT 斤 COMMENT 销售单位, price DECIMAL(10,2) DEFAULT 0 COMMENT 单价, stock INT DEFAULT 0 COMMENT 当前库存, created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, KEY idx_category (category) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT蔬菜品种表;三个参数值得说明price 用 DECIMAL(10,2) 而不是 FLOAT是为了避免浮点数在小数计算上出现 0.1 加 0.2 不等于 0.3 的问题engine 固定为 InnoDB因为毕业论文里要涉及事务和关联查询MyISAM 不支持事务category 字段挂了一个普通索引因为按分类筛选是高频操作。外键不用建物理约束在 Java 代码里用 greenhouseId、vegetableId 维护逻辑关系删除和更新更灵活。created_at 和 updated_at 由数据库维护默认值实体类里可以只映射成 LocalDateTime不入库手填值。后端项目目录结构src/main/java/com/example/greenhouse ├── controller # 接收 HTTP 请求做参数校验 ├── service # 事务和业务规则如删除前检查 ├── repository # Spring Data JPA 数据访问接口 ├── entity # 与数据库表对应的实体类 └── common # 统一返回结果、异常处理这个结构在论文里可以直接画成系统架构图的后半部分。entity 一个类对应一张表repository 只写接口和方法签名service 里放库存判断这种业务规则controller 尽量薄别把 SQL 和 if 判断堆在 Controller 里。后面写接口时代码就按这个目录放。3. Spring Boot 写蔬菜管理接口JPA 实体、Service 规则和统一返回数据库表定好后后端要解决三件事实体怎么映射、业务规则放在哪、接口返回什么格式。这一章用蔬菜管理这个核心功能把 Spring Boot 的完整链路走一遍这块代码跑通了大棚和环境记录模块照同样的模式复制即可。3.1 Spring Boot 整合 JPAVegetable 实体和 Repository数据访问层我通常优先用 Spring Data JPA。对比 MyBatisJPA 的普通增删改查不需要写 SQLRepository 接口里定义方法名就能生成查询MyBatis 要维护 XML 或注解 SQL。对于蔬菜管理这种 CRUD 占大头的系统JPA 能省下大量重复代码。如果你更熟悉 MyBatis-Plus也可以用只是下面的实体注解要换成 TableName 那一套。pom.xml 里加三个核心依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency版本号不用写Spring Boot 父 POM 会统一管理。mysql-connector-j 是 MySQL 8 提供的新坐标老项目里的 com.mysql:mysql-connector-java 在 Spring Boot 2.7 里也能用新项目建议直接用新坐标避免依赖传递时出现两个驱动类冲突。实体类Entity Table(name vegetable) public class Vegetable { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; Column(nullable false, length 50) private String name; private String category; private String unit; private BigDecimal price; private Integer stock; }Column(nullable false, length 50) 表示 name 字段必填且最长 50 个字符对应建表语句里的 VARCHAR(50)。GeneratedValue(strategy GenerationType.IDENTITY) 使用数据库自增主键。实体类名用单数 Vegetable表名是 vegetableTable 已经指定了映射。如果字段命名规则是驼峰可以在 application.yml 里配置命名策略让 JPA 自动把 updatedAt 转成 updated_at。Repository 继承两个接口public interface VegetableRepository extends JpaRepositoryVegetable, Long, JpaSpecificationExecutorVegetable { PageVegetable findByNameContaining(String keyword, Pageable pageable); }JpaRepository 提供 findById、save、delete 这些基础方法JpaSpecificationExecutor 给后面的多条件动态查询留了扩展入口。findByNameContaining 是 Spring Data 的声明式查询方法名里的 NameContaining 会被解析成 LIKE %xxx%不用手写 JPQL。3.2 Service 层放业务规则分页查询和删除保护接口是给别人调的不能把数据库操作直接暴露在 Controller 里。分页、删除检查这些规则都要写在 Service 层这样 Controller 才能保持很薄。一个典型的 Service 是这样的Service RequiredArgsConstructor public class VegetableService { private final VegetableRepository vegetableRepository; private final PlantBatchRepository plantBatchRepository; public PageVegetable page(int page, int size, String keyword) { Pageable pageable PageRequest.of(page, size, Sort.by(Direction.DESC, id)); if (keyword null || keyword.isBlank()) { return vegetableRepository.findAll(pageable); } return vegetableRepository.findByNameContaining(keyword, pageable); } Transactional public void delete(Long id) { if (plantBatchRepository.existsByVegetableId(id)) { throw new BusinessException(该蔬菜已有种植批次不能删除); } vegetableRepository.deleteById(id); } }PageRequest.of 第一个参数是从 0 开始的下标不是页码。前端传 page1 表示第一页后端要 page-1。Sort.by(Direction.DESC, id) 是按主键倒序新加的品种排前面。delete 上加了 Transactional因为先查后删是两个操作中间如果抛异常要一起回滚。plantBatchRepository 是种植批次模块的 RepositoryexistsByVegetableId 判断蔬菜是否已经被种植批次引用业务上比直接删更合理。这里的 BusinessException 是自定义异常放在 common 包里配合 RestControllerAdvice 统一异常处理可以把 message 直接返回给前端不用让前端拿到 500 状态码弹一堆看不懂的英文。这种写法在论文里可以单独画一张异常处理流程图。3.3 Controller 层与统一返回结构API 路径怎么规划统一返回格式解决的是前端取值问题。后端接口都返回一个 Result 对象里面放 code、message、data 三个字段前端只需要判断 code 是否等于 200。public class ResultT { private int code; private String message; private T data; public static T ResultT ok(T data) { ResultT r new Result(); r.code 200; r.message success; r.data data; return r; } }Controller 把 service 的结果包一层RestController RequestMapping(/api/vegetable) RequiredArgsConstructor public class VegetableController { private final VegetableService vegetableService; GetMapping public ResultPageVegetable page(RequestParam(defaultValue 1) int page, RequestParam(defaultValue 10) int size, RequestParam(required false) String keyword) { return Result.ok(vegetableService.page(page - 1, size, keyword)); } PostMapping public ResultVegetable add(RequestBody Vegetable vegetable) { return Result.ok(vegetableService.save(vegetable)); } DeleteMapping(/{id}) public ResultVoid delete(PathVariable Long id) { vegetableService.delete(id); return Result.ok(null); } }PathVariable 用于绑定 URL 里的 {id}。RequestParam(required false) 让 keyword 可传可不传。所有接口都以 /api 开头方便后面统一配置前缀和权限拦截。add 方法接收 RequestBody前端 POST 的 JSON 会被 Spring Boot 反序列化成 Vegetable 对象属性名和前端字段必须一致。接口规划表方法路径功能说明GET/api/vegetable?page1size10keyword菜分页查询page 从 1 开始传POST/api/vegetable新增蔬菜JSON bodyPUT/api/vegetable/{id}修改信息JSON bodyDELETE/api/vegetable/{id}删除蔬菜有种植批次时返回业务错误GET/api/environment/trend?greenhouseId1days7温湿度趋势供前端图表调用前端只需要看 result.data分页信息在 data 里的 content、totalElements 等字段这一张表可以直接复制到论文的接口设计章节。4. Vue 前端对接 Spring Boot路由参数、axios 封装和图表页面写完后端接口前端的工作就只剩下一件事把 HTTP 请求对到页面组件上。Vue 3 的组件化写法足够应付这种规模的管理系统不需要引入 Pinia只有登录信息这类跨页面共享状态才值得用状态管理。Vue 和 React 在管理系统上的写法差别不大选 Vue 的原因主要是模板语法更直观论文截图也更适合给非前端背景的老师看。4.1 用 Vite 初始化 Vue 项目和依赖版本匹配创建前端项目的常见做法是用 Vite 脚手架npm create vitelatest greenhouse-web -- --template vue cd greenhouse-web npm install npm install axios vue-router4 element-plus echartsVite 5 要求 Node 18 以上安装依赖前先执行 node -v 确认版本低于 18 会直接报错。npm install 之后把 axios、vue-router、Element Plus、ECharts 一起装上。这里最容易踩坑的是版本搭配Vue 3 必须配 vue-router4Vue 2 必须配 vue-router3UI 组件库 Vue 3 用 Element PlusVue 2 用 Element UI。上面命令默认安装最新版匹配的就是 Vue 3。依赖Vue 3 搭配Vue 2 搭配vue-router4.x3.xUI 组件库element-pluselement-ui构建工具vitevue-cli路由配置import { createRouter, createWebHistory } from vue-router import VegetableList from ../views/VegetableList.vue const routes [ { path: /, redirect: /vegetable }, { path: /vegetable, component: VegetableList }, { path: /vegetable/:id, component: () import(../views/VegetableEdit.vue) } ] export default createRouter({ history: createWebHistory(), routes })createWebHistory 是 HTML5 history 模式URL 里没有 #但生产环境需要服务器把未知路径都转发到 index.html否则刷新页面会 404。如果不想折腾 Nginx把 createWebHistory 换成 createWebHashHistory刷新就不会出错。路由参数 /vegetable/:id 在编辑页里用 route.params.id 获取列表页跳转时用 $router.push 传到编辑组件。4.2 用 axios 封装请求和 Element Plus 的增删改查页面axios 不能挂在 Vue 原型上Vue 3 的全局属性不推荐放 http 客户端。我用一个独立文件封装实例import axios from axios const request axios.create({ timeout: 10000 }) export default requestbaseURL 不写让前端页面和接口同源。开发阶段启动 Vite 后浏览器请求 http://localhost:5173/api/vegetable由 Vite 的 devServer 代理转发到后端 8080这样就不会有跨域问题。在 vite.config.js 里加一段代理export default defineConfig({ server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })生产环境如果前端打包进 Spring Boot 的 static 目录或者用 Nginx 托管都需要让 /api 开头的请求落到后端这个代理配置就是开发环境里最省事的一步。蔬菜列表页组件script setup import { ref, onMounted } from vue import request from ../api/request const list ref([]) const keyword ref() async function fetchList() { const { data } await request.get(/api/vegetable, { params: { page: 1, size: 10, keyword: keyword.value } }) list.value data.data.content } async function removeItem(id) { await request.delete(/api/vegetable/ id) fetchList() } onMounted(fetchList) /script template el-input v-modelkeyword placeholder按名称搜索 changefetchList / el-table :datalist el-table-column propname label蔬菜名称 / el-table-column propprice label单价 / el-table-column label操作 template #default{ row } el-button click$router.push(/vegetable/ row.id)编辑/el-button el-button typedanger clickremoveItem(row.id)删除/el-button /template /el-table-column /el-table /templateonMounted 在组件挂载后执行适合放初始数据请求。ref 定义响应式数据list.value 赋值后页面自动更新。el-table 里 prop 对应后端字段名操作列用 #default{ row } 拿到当前行的对象row.id 传给路由或删除接口。一个常见错误是拿到 data 后直接取 data.data而不是 data.data.content因为 Spring Data JPA 的 Page 对象把列表放在 content 里total 之类的分页信息在其他字段。可以再写一个响应拦截器把 code 不为 200 的情况统一弹提示这样列表里的错误处理就只剩业务逻辑了。4.3 ECharts 展示环境监测趋势后端返回什么格式就给图什么格式环境监测是大棚蔬菜系统比普通 CRUD 更值得写进论文的功能。温度、湿度按时间记录前端把最近七天的数据画成折线图。后端 /api/environment/trend 接口返回 JSON 结构{ dates: [04-01, 04-02], temp: [21.5, 22.3], humidity: [60, 58] }前端组件script setup import * as echarts from echarts import { onMounted, ref } from vue import request from ../api/request const chartRef ref(null) onMounted(async () { const { data } await request.get(/api/environment/trend, { params: { greenhouseId: 1, days: 7 } }) const chart echarts.init(chartRef.value) chart.setOption({ tooltip: { trigger: axis }, xAxis: { type: category, data: data.data.dates }, yAxis: { type: value }, series: [ { name: 温度, type: line, data: data.data.temp }, { name: 湿度, type: line, data: data.data.humidity } ] }) }) /script template div refchartRef stylewidth: 100%; height: 400px/div /templateecharts.init 必须等 div 渲染出来再执行所以放在 onMounted 里放在 setup 顶层会拿到空节点。setOption 负责注入配置series 里的两个对象分别对应两条折线。greenhouseId 可以用 vue-router 的路由参数传进来也可以由父组件通过 props 传入图表组件只负责渲染数据不主动发上游请求这样图表组件可以被多个大棚页面复用接口的参数变化不会影响内部逻辑。5. 打包部署和毕业论文验证让大棚蔬菜系统能演示也要能讲5.1 前后端打包命令和静态资源合并系统要能交付先做后端打包mvn clean package -DskipTests java -jar target/greenhouse-0.0.1-SNAPSHOT.jar-DskipTests 跳过测试直接打包生成的 jar 包包含内嵌 Tomcatjava 命令直接运行。前端在 greenhouse-web 目录执行 npm run build产物在 dist 目录。如果想把前后端合并成一个部署单元把 dist 里的文件复制到 src/main/resources/static重新打包 jar 即可如果保持前后端分离用 Nginx 托管 dist并将 /api 请求反向代理到 8080 端口。数据库连接配置写在 application.ymlspring: datasource: url: jdbc:mysql://localhost:3306/greenhouse_veg?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 jpa: hibernate: ddl-auto: update show-sql: true开发阶段 ddl-auto 用 update 会自动建表省去手工执行建表脚本。答辩前如果不想让自动建表覆盖手工调整过的字段把它改成 validate 或 none。show-sql 打开后控制台可以直接看到 JPA 生成的 SQL演示时可以借此解释 JPA 的代理机制。5.2 答辩演示的一组测试用例和动态查询扩展演示流程不要临时想按下面这组场景走一遍场景操作预期结果新增记录新增“生菜”单价 2.5列表第一页出现生菜综合查询搜索框输入“菜”列表只剩名称含“菜”的品种删除保护删除有种植批次的蔬菜弹出“该蔬菜已有种植批次不能删除”图表联动选择 1 号大棚最近 7 天温湿度折线图正常渲染最后一个可以讲深的点是分页搜索的扩展。keyword 后续很可能变成多条件比如按分类加时间范围过滤。与其在 Repository 方法名里拼 findByNameContainingAndCategoryAndCreatedAtBetween不如直接用第 3 章预留的 JpaSpecificationExecutor用 Specification 把每个过滤条件作为独立 where 子句拼进查询条件组合用 List 维护代码可读性和可维护性都更好。把这个扩展思路写进论文的优化方向比起只写一个 CRUD 系统更有技术含量。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价