分享一套锋哥原创的SpringBoot4+Vue3自习室预约管理系统

📅 2026/7/9 10:25:07
分享一套锋哥原创的SpringBoot4+Vue3自习室预约管理系统
大家好我是Java1234_小锋老师分享一套锋哥原创的SpringBoot4Vue3自习室预约管理系统。项目介绍随着高校在校学生规模的不断扩大以及备考、自主学习需求的日益增长图书馆和公共自习室的座位资源日趋紧张。传统的现场排队占座、纸质登记的管理方式存在信息不透明、占座浪费严重、管理效率低下等问题已经难以满足师生对高效、公平使用自习资源的需求。为此本文设计并实现了一套基于 SpringBoot 与 Vue3 的自习室预约管理系统旨在实现自习室座位资源的在线化、可视化与规范化管理。系统采用前后端分离的架构进行开发。后端以 SpringBoot 作为基础框架搭建 RESTful 接口使用 MyBatis-Plus 作为持久层框架完成对 MySQL 数据库的高效访问并借助其条件构造器、分页插件与通用 Mapper 大幅减少了样板代码同时结合 Spring Security 与 JWT 实现无状态的登录认证与权限控制。前端基于 Vue3 的组合式 API 构建单页应用配合 Vue Router、Pinia、Element Plus 与 Axios 完成路由管理、状态管理、界面渲染与接口通信并使用 ECharts 实现数据的可视化统计。系统按照管理员和学生两种角色划分功能主要包括登录认证、用户管理、自习室管理、座位管理、在线预约与审核、可视化选座、签到与取消、公告通知、意见反馈、数据统计以及个人中心等模块。经过功能测试系统运行稳定界面友好能够有效解决座位预约与管理中的实际问题具有较好的实用价值。源码下载链接: https://pan.baidu.com/s/1NOT7BJvjwFe7KqZeQWphxQ?pwd1234提取码: 1234系统展示核心代码package com.studyroom.controller; import com.studyroom.common.PageResult; import com.studyroom.common.R; import com.studyroom.dto.AnnouncementOut; import com.studyroom.dto.AnnouncementRequest; import com.studyroom.service.AnnouncementService; import org.springframework.web.bind.annotation.*; /** * 公告管理控制器 */ RestController RequestMapping(/api/announcements) public class AnnouncementController { private final AnnouncementService announcementService; /** * 构造公告控制器 */ public AnnouncementController(AnnouncementService announcementService) { this.announcementService announcementService; } /** * 分页查询公告列表 */ GetMapping public RPageResultAnnouncementOut listAnnouncements( RequestParam(defaultValue 1) int page, RequestParam(name page_size, defaultValue 10) int pageSize, RequestParam(required false) String keyword, RequestParam(required false) Integer status) { return R.ok(announcementService.listAnnouncements(page, pageSize, keyword, status)); } /** * 创建公告管理员 */ PostMapping public RAnnouncementOut createAnnouncement(RequestBody AnnouncementRequest request) { return R.ok(announcementService.createAnnouncement(request)); } /** * 更新公告管理员 */ PutMapping(/{annId}) public RAnnouncementOut updateAnnouncement(PathVariable Integer annId, RequestBody AnnouncementRequest request) { return R.ok(announcementService.updateAnnouncement(annId, request)); } /** * 删除公告管理员 */ DeleteMapping(/{annId}) public RVoid deleteAnnouncement(PathVariable Integer annId) { announcementService.deleteAnnouncement(annId); return R.okMessage(删除成功); } }template div classpage-container div classsearch-bar el-select v-modelquery.status placeholder状态 clearable stylewidth: 120px changeloadData el-option label待回复 valuepending / el-option label已回复 valuereplied / /el-select el-button typeprimary iconSearch clickloadData搜索/el-button el-button typesuccess iconPlus clickopenDialog() v-if!userStore.isAdmin提交反馈/el-button /div div classtable-card el-table :datatableData stripe border stylewidth: 100% el-table-column propid labelID min-width60 / el-table-column v-ifuserStore.isAdmin propreal_name label反馈人 min-width90 / el-table-column propcontent label反馈内容 min-width240 show-overflow-tooltip / el-table-column propreply label回复内容 min-width240 show-overflow-tooltip template #default{ row }{{ row.reply || - }}/template /el-table-column el-table-column propstatus label状态 min-width90 template #default{ row } el-tag :typerow.status replied ? success : warning sizesmall {{ row.status replied ? 已回复 : 待回复 }} /el-tag /template /el-table-column el-table-column propcreate_time label提交时间 min-width160 template #default{ row }{{ formatDateTime(row.create_time) }}/template /el-table-column el-table-column propreply_time label回复时间 min-width160 template #default{ row }{{ formatDateTime(row.reply_time) || - }}/template /el-table-column el-table-column label操作 min-width120 fixedright template #default{ row } el-button v-ifuserStore.isAdmin row.status pending typeprimary link sizesmall clickopenReply(row)回复/el-button el-popconfirm v-ifuserStore.isAdmin title确定删除? confirmhandleDelete(row.id) template #referenceel-button typedanger link sizesmall删除/el-button/template /el-popconfirm /template /el-table-column /el-table el-pagination classpagination v-model:current-pagequery.page v-model:page-sizequery.page_size :totaltotal layouttotal, prev, pager, next current-changeloadData / /div !-- 提交反馈 -- el-dialog v-modeldialogVisible title提交意见反馈 width500px destroy-on-close el-form refformRef :modelform :rulesrules el-form-item propcontent el-input v-modelform.content typetextarea :rows5 placeholder请输入您的意见或建议... / /el-form-item /el-form template #footer el-button clickdialogVisible false取消/el-button el-button typeprimary clickhandleSubmit提交/el-button /template /el-dialog !-- 回复反馈 -- el-dialog v-modelreplyVisible title回复反馈 width500px destroy-on-close p stylemargin-bottom: 12px; color: #666反馈内容: {{ replyRow?.content }}/p el-input v-modelreplyContent typetextarea :rows4 placeholder请输入回复内容... / template #footer el-button clickreplyVisible false取消/el-button el-button typeprimary clickhandleReply确认回复/el-button /template /el-dialog /div /template script setup /** 意见反馈页面 */ import { ref, reactive, onMounted } from vue import { ElMessage } from element-plus import request from /utils/request import { formatDateTime } from /utils/date import { useUserStore } from /store/user const userStore useUserStore() const tableData ref([]) const total ref(0) const dialogVisible ref(false) const replyVisible ref(false) const replyRow ref(null) const replyContent ref() const formRef ref(null) const query reactive({ page: 1, page_size: 10, status: }) const form reactive({ content: }) const rules { content: [{ required: true, message: 请输入反馈内容, trigger: blur }] } async function loadData() { const res await request.get(/feedbacks, { params: query }) tableData.value res.data.items total.value res.data.total } function openDialog() { form.content dialogVisible.value true } function openReply(row) { replyRow.value row replyContent.value replyVisible.value true } async function handleSubmit() { await formRef.value.validate() await request.post(/feedbacks, form) ElMessage.success(提交成功) dialogVisible.value false loadData() } async function handleReply() { if (!replyContent.value.trim()) return ElMessage.warning(请输入回复内容) await request.put(/feedbacks/${replyRow.value.id}/reply, { reply: replyContent.value }) ElMessage.success(回复成功) replyVisible.value false loadData() } async function handleDelete(id) { await request.delete(/feedbacks/${id}) ElMessage.success(删除成功) loadData() } onMounted(loadData) /script style scoped .pagination { margin-top: 16px; justify-content: flex-end; } /style