分享一套锋哥原创的SpringBoot4+Vue3自习室预约管理系统
2026/7/9 10:25:01 网站建设 项目流程

大家好,我是Java1234_小锋老师,分享一套锋哥原创的SpringBoot4+Vue3自习室预约管理系统。

项目介绍

随着高校在校学生规模的不断扩大以及备考、自主学习需求的日益增长,图书馆和公共自习室的座位资源日趋紧张。传统的现场排队占座、纸质登记的管理方式存在信息不透明、占座浪费严重、管理效率低下等问题,已经难以满足师生对高效、公平使用自习资源的需求。为此,本文设计并实现了一套基于 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?pwd=1234
提取码: 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 R<PageResult<AnnouncementOut>> 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 R<AnnouncementOut> createAnnouncement(@RequestBody AnnouncementRequest request) { return R.ok(announcementService.createAnnouncement(request)); } /** * 更新公告(管理员) */ @PutMapping("/{annId}") public R<AnnouncementOut> updateAnnouncement(@PathVariable Integer annId, @RequestBody AnnouncementRequest request) { return R.ok(announcementService.updateAnnouncement(annId, request)); } /** * 删除公告(管理员) */ @DeleteMapping("/{annId}") public R<Void> deleteAnnouncement(@PathVariable Integer annId) { announcementService.deleteAnnouncement(annId); return R.okMessage("删除成功"); } }
<template> <div class="page-container"> <div class="search-bar"> <el-select v-model="query.status" placeholder="状态" clearable style="width: 120px" @change="loadData"> <el-option label="待回复" value="pending" /> <el-option label="已回复" value="replied" /> </el-select> <el-button type="primary" icon="Search" @click="loadData">搜索</el-button> <el-button type="success" icon="Plus" @click="openDialog()" v-if="!userStore.isAdmin">提交反馈</el-button> </div> <div class="table-card"> <el-table :data="tableData" stripe border style="width: 100%"> <el-table-column prop="id" label="ID" min-width="60" /> <el-table-column v-if="userStore.isAdmin" prop="real_name" label="反馈人" min-width="90" /> <el-table-column prop="content" label="反馈内容" min-width="240" show-overflow-tooltip /> <el-table-column prop="reply" label="回复内容" min-width="240" show-overflow-tooltip> <template #default="{ row }">{{ row.reply || '-' }}</template> </el-table-column> <el-table-column prop="status" label="状态" min-width="90"> <template #default="{ row }"> <el-tag :type="row.status === 'replied' ? 'success' : 'warning'" size="small"> {{ row.status === 'replied' ? '已回复' : '待回复' }} </el-tag> </template> </el-table-column> <el-table-column prop="create_time" label="提交时间" min-width="160"> <template #default="{ row }">{{ formatDateTime(row.create_time) }}</template> </el-table-column> <el-table-column prop="reply_time" label="回复时间" min-width="160"> <template #default="{ row }">{{ formatDateTime(row.reply_time) || '-' }}</template> </el-table-column> <el-table-column label="操作" min-width="120" fixed="right"> <template #default="{ row }"> <el-button v-if="userStore.isAdmin && row.status === 'pending'" type="primary" link size="small" @click="openReply(row)">回复</el-button> <el-popconfirm v-if="userStore.isAdmin" title="确定删除?" @confirm="handleDelete(row.id)"> <template #reference><el-button type="danger" link size="small">删除</el-button></template> </el-popconfirm> </template> </el-table-column> </el-table> <el-pagination class="pagination" v-model:current-page="query.page" v-model:page-size="query.page_size" :total="total" layout="total, prev, pager, next" @current-change="loadData" /> </div> <!-- 提交反馈 --> <el-dialog v-model="dialogVisible" title="提交意见反馈" width="500px" destroy-on-close> <el-form ref="formRef" :model="form" :rules="rules"> <el-form-item prop="content"> <el-input v-model="form.content" type="textarea" :rows="5" placeholder="请输入您的意见或建议..." /> </el-form-item> </el-form> <template #footer> <el-button @click="dialogVisible = false">取消</el-button> <el-button type="primary" @click="handleSubmit">提交</el-button> </template> </el-dialog> <!-- 回复反馈 --> <el-dialog v-model="replyVisible" title="回复反馈" width="500px" destroy-on-close> <p style="margin-bottom: 12px; color: #666">反馈内容: {{ replyRow?.content }}</p> <el-input v-model="replyContent" type="textarea" :rows="4" placeholder="请输入回复内容..." /> <template #footer> <el-button @click="replyVisible = false">取消</el-button> <el-button type="primary" @click="handleReply">确认回复</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>

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询