大家好,我是Java1234_小锋老师,分享一套锋哥原创的基于Python的自习室预约管理系统(FastAPI+Vue3)

项目介绍
随着高校在校学生人数的持续增长,图书馆及公共自习室座位资源日益紧张,传统“占座”式的线下管理方式存在座位利用率低、信息不透明、管理成本高等突出问题。为提升自习室座位资源的使用效率、规范学生的预约使用行为,本文设计并实现了一套基于Python的自习室预约管理系统。
系统采用前后端分离的架构思想进行设计与开发。后端基于Python语言的高性能Web框架FastAPI构建RESTful API,使用SQLAlchemy作为对象关系映射(ORM)框架操作MySQL数据库,并通过JWT(JSON Web Token)实现无状态的用户身份认证与权限控制;前端基于Vue3渐进式框架,结合Vite构建工具、Element Plus组件库、Pinia状态管理以及ECharts可视化图表库,构建了响应式、可视化的用户操作界面。系统按照管理员与学生两类角色划分功能,主要包括用户管理、自习室管理、座位管理、预约管理、公告管理、意见反馈以及数据统计等模块。其中,预约管理模块实现了座位级别的可视化选座与时段冲突自动校验,有效避免了重复预约问题。
本文详细阐述了系统的需求分析、总体设计、数据库设计与功能实现过程,并对系统进行了功能测试。测试结果表明,系统功能完整、运行稳定、界面友好,能够较好地满足高校自习室座位在线预约与管理的实际需求,具有一定的实用价值。
源码下载
链接:https://pan.baidu.com/s/14RQYu2GeLBMmG24H8kXMPg?pwd=1234 提取码:1234
系统展示 






核心代码
"""
FastAPI 应用入口
配置 CORS、静态文件、路由注册
"""
import os
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from app.api import auth, user, room, seat, reservation, announcement, feedback, stats, upload
from app.core.config import settings
# 确保上传目录存在
os.makedirs(settings.UPLOAD_DIR, exist_ok=True)
# 创建 FastAPI 应用实例
app = FastAPI(
title=settings.PROJECT_NAME,
description="自习室预约管理系统后端 API",
version="1.0.0",
)
# 配置跨域
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 挂载静态文件目录 (上传文件访问)
app.mount("/uploads", StaticFiles(directory=settings.UPLOAD_DIR), name="uploads")
# 注册 API 路由
app.include_router(auth.router, prefix=settings.API_PREFIX, tags=["认证"])
app.include_router(user.router, prefix=settings.API_PREFIX, tags=["用户管理"])
app.include_router(room.router, prefix=settings.API_PREFIX, tags=["自习室管理"])
app.include_router(seat.router, prefix=settings.API_PREFIX, tags=["座位管理"])
app.include_router(reservation.router, prefix=settings.API_PREFIX, tags=["预约管理"])
app.include_router(announcement.router, prefix=settings.API_PREFIX, tags=["公告管理"])
app.include_router(feedback.router, prefix=settings.API_PREFIX, tags=["意见反馈"])
app.include_router(stats.router, prefix=settings.API_PREFIX, tags=["数据统计"])
app.include_router(upload.router, prefix=settings.API_PREFIX, tags=["文件上传"])
@app.get("/")
def root():
"""根路径 – 系统信息"""
return {"message": settings.PROJECT_NAME, "version": "1.0.0", "docs": "/docs"}
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
<template>
<div class="page-container">
<div class="profile-card">
<div class="profile-header">
<el-avatar :size="80" :style="{ background: 'linear-gradient(135deg, #667eea, #764ba2)', fontSize: '32px' }">
{{ userStore.user?.real_name?.[0] || 'U' }}
</el-avatar>
<div class="profile-info">
<h2>{{ userStore.user?.real_name || userStore.user?.username }}</h2>
<el-tag :type="userStore.isAdmin ? 'danger' : 'primary'" size="small">
{{ userStore.isAdmin ? '管理员' : '学生' }}
</el-tag>
</div>
</div>
<el-divider />
<el-form ref="formRef" :model="form" label-width="80px" style="max-width: 500px">
<el-form-item label="用户名"><el-input v-model="form.username" disabled /></el-form-item>
<el-form-item label="姓名"><el-input v-model="form.real_name" /></el-form-item>
<el-form-item label="性别">
<el-select v-model="form.gender" style="width: 100%">
<el-option label="男" value="男" /><el-option label="女" value="女" />
</el-select>
</el-form-item>
<el-form-item label="手机号"><el-input v-model="form.phone" /></el-form-item>
<el-form-item label="邮箱"><el-input v-model="form.email" /></el-form-item>
<el-form-item label="学号" v-if="!userStore.isAdmin"><el-input v-model="form.student_no" /></el-form-item>
<el-form-item label="信用分" v-if="!userStore.isAdmin">
<el-progress :percentage="form.credit_score" :color="creditColor" style="width: 200px" />
<span style="margin-left: 12px">{{ form.credit_score }} 分</span>
</el-form-item>
<el-form-item label="新密码"><el-input v-model="form.password" type="password" placeholder="不修改请留空" show-password /></el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSave">保存修改</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script setup>
/** 个人中心页面 */
import { reactive, computed, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import request from '@/utils/request'
import { useUserStore } from '@/store/user'
const userStore = useUserStore()
const form = reactive({
username: '', real_name: '', gender: '', phone: '', email: '',
student_no: '', credit_score: 100, password: ''
})
const creditColor = computed(() => {
if (form.credit_score >= 90) return '#67c23a'
if (form.credit_score >= 70) return '#e6a23c'
return '#f56c6c'
})
onMounted(() => {
if (userStore.user) Object.assign(form, { …userStore.user, password: '' })
})
async function handleSave() {
const data = { …form }
if (!data.password) delete data.password
delete data.username
await request.put(`/users/${userStore.user.id}`, data)
ElMessage.success('保存成功')
await userStore.fetchUser()
}
</script>
<style scoped>
.profile-card {
background: #fff;
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
padding: 40px;
max-width: 700px;
}
.profile-header {
display: flex;
align-items: center;
gap: 24px;
}
.profile-info h2 {
font-size: 22px;
color: #1a1a2e;
margin-bottom: 8px;
}
</style>






