分享一套锋哥原创的基于Python的图书馆(图书借阅)管理系统(FastAPI+Vue3)

大家好,我是Java1234_小锋老师,分享一套锋哥原创的基于Python的图书馆(图书借阅)管理系统(FastAPI+Vue3)

项目介绍

随着信息技术的高速发展和高校、公共图书馆藏书规模的不断扩大,传统依靠人工登记、纸质卡片管理图书借阅的方式已经难以满足现代图书馆高效、准确管理的需求。人工管理方式不仅工作量大、容易出错,而且查询统计困难、借阅信息难以及时更新,已经成为制约图书馆服务质量提升的重要因素。因此,设计并实现一套功能完善、操作便捷、运行稳定的图书馆(图书借阅)管理系统具有重要的现实意义。

本文基于 Python 语言,采用 FastAPI 作为后端 Web 框架、Vue3 作为前端框架、MySQL 作为后台数据库,设计并实现了一套前后端分离的图书馆图书借阅管理系统。系统后端使用 FastAPI 构建 RESTful API 接口,借助 SQLAlchemy 进行对象关系映射、Pydantic 完成数据校验、JWT 实现登录鉴权;前端使用 Vue3 + Vite + Element Plus + Pinia + Axios 技术栈构建单页应用,实现了美观友好的用户交互界面。

系统主要包括系统管理、图书管理、读者管理、借阅管理以及统计查询等功能模块,实现了管理员登录、图书与图书分类的增删改查、读者信息维护、图书借阅与归还、逾期罚款计算以及借阅数据统计等核心业务功能。论文按照软件工程的思想,依次完成了系统的需求分析、总体设计、数据库设计、详细实现与系统测试。测试结果表明,本系统功能完整、界面友好、运行稳定,能够有效提高图书馆的管理效率,具有较好的实用价值。

源码下载  

链接:https://pan.baidu.com/s/1DGDj55EpfL-_y4hxIDmpHA?pwd=1234
提取码:1234

系统展示

核心代码

"""
图书分类路由
提供分类的增删改查接口
"""
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session

from app.database import get_db
from app.models.category import Category
from app.schemas.category import CategoryCreate, CategoryUpdate
from app.core.security import require_admin, get_current_user
from app.core.response import success, fail, page_result
from app.core.exceptions import BusinessException

router = APIRouter(prefix="/category", tags=["图书分类"])


@router.get("/list", summary="分类列表")
def list_categories(
    page: int = 1,
    page_size: int = 10,
    keyword: str = "",
    db: Session = Depends(get_db),
    current_user: dict = Depends(get_current_user),
):
    """分页查询图书分类列表,支持关键字搜索"""
    query = db.query(Category)
    if keyword:
        query = query.filter(Category.name.like(f"%{keyword}%"))
    total = query.count()
    items = query.order_by(Category.sort.asc(), Category.id.asc()).offset((page - 1) * page_size).limit(page_size).all()
    result = [{"id": c.id, "name": c.name, "sort": c.sort, "remark": c.remark} for c in items]
    return page_result(result, total, page, page_size)


@router.get("/all", summary="全部分类(下拉用)")
def all_categories(
    db: Session = Depends(get_db),
    current_user: dict = Depends(get_current_user),
):
    """获取全部分类,用于下拉选择"""
    items = db.query(Category).order_by(Category.sort.asc()).all()
    return success([{"id": c.id, "name": c.name} for c in items])


@router.post("/add", summary="新增分类")
def add_category(
    req: CategoryCreate,
    db: Session = Depends(get_db),
    current_user: dict = Depends(require_admin),
):
    """新增图书分类(管理员)"""
    exists = db.query(Category).filter(Category.name == req.name).first()
    if exists:
        return fail("分类名称已存在")
    category = Category(name=req.name, sort=req.sort, remark=req.remark)
    db.add(category)
    db.commit()
    return success(msg="添加成功")


@router.put("/update/{category_id}", summary="修改分类")
def update_category(
    category_id: int,
    req: CategoryUpdate,
    db: Session = Depends(get_db),
    current_user: dict = Depends(require_admin),
):
    """修改图书分类(管理员)"""
    category = db.query(Category).filter(Category.id == category_id).first()
    if not category:
        raise BusinessException("分类不存在")
    if req.name is not None:
        category.name = req.name
    if req.sort is not None:
        category.sort = req.sort
    if req.remark is not None:
        category.remark = req.remark
    db.commit()
    return success(msg="修改成功")


@router.delete("/delete/{category_id}", summary="删除分类")
def delete_category(
    category_id: int,
    db: Session = Depends(get_db),
    current_user: dict = Depends(require_admin),
):
    """删除图书分类(管理员)"""
    category = db.query(Category).filter(Category.id == category_id).first()
    if not category:
        raise BusinessException("分类不存在")
    db.delete(category)
    db.commit()
    return success(msg="删除成功")
<template>
  <div class="page-container">
    <div class="search-bar">
      <el-select v-model="form.reader_id" placeholder="选择读者" filterable style="width:220px">
        <el-option v-for="r in readers" :key="r.id" :label="`${r.real_name} (${r.card_no})`" :value="r.id" />
      </el-select>
      <el-select v-model="form.book_id" placeholder="选择图书" filterable style="width:280px">
        <el-option v-for="b in books" :key="b.id" :label="`${b.title} (库存:${b.stock_count})`" :value="b.id" :disabled="b.stock_count <= 0" />
      </el-select>
      <el-button type="primary" @click="handleLend"><el-icon><Switch /></el-icon>办理借书</el-button>
    </div>

    <div class="table-card">
      <h3 style="margin-bottom:16px;color:#303133">当前在借图书</h3>
      <el-table :data="borrowingList" stripe border style="width:100%">
        <el-table-column prop="reader_name" label="读者" min-width="100" />
        <el-table-column prop="card_no" label="借书证号" min-width="130" />
        <el-table-column prop="book_title" label="图书" min-width="180" show-overflow-tooltip />
        <el-table-column prop="borrow_time" label="借阅时间" min-width="170" />
        <el-table-column prop="due_time" label="应还时间" min-width="170" />
        <el-table-column prop="renew_count" label="续借次数" min-width="90" />
        <el-table-column prop="status_text" label="状态" min-width="80">
          <template #default="{ row }">
            <el-tag :type="row.status === 3 ? 'danger' : 'warning'" size="small">{{ row.status_text }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="fine" label="罚款(元)" min-width="90" />
        <el-table-column label="操作" min-width="180" fixed="right">
          <template #default="{ row }">
            <el-button v-if="row.status !== 2" type="success" link @click="handleReturn(row)">还书</el-button>
            <el-button v-if="row.status === 1" type="primary" link @click="handleRenew(row)">续借</el-button>
          </template>
        </el-table-column>
      </el-table>
      <div class="pagination-wrap">
        <el-pagination v-model:current-page="page" v-model:page-size="pageSize" :total="total"
          layout="total, sizes, prev, pager, next" @change="loadBorrowing" />
      </div>
    </div>
  </div>
</template>

<script setup>
/** 借阅管理页面 - 管理员办理借书/还书/续借 */
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getAllReaders, getBookList, getBorrowList, lendBook, returnBook, renewBook } from '@/api'

const readers = ref([])
const books = ref([])
const borrowingList = ref([])
const page = ref(1)
const pageSize = ref(10)
const total = ref(0)
const form = reactive({ reader_id: null, book_id: null })

async function loadReaders() {
  const res = await getAllReaders()
  readers.value = res.data
}

async function loadBooks() {
  const res = await getBookList({ page: 1, page_size: 100 })
  books.value = res.data.list
}

async function loadBorrowing() {
  const res = await getBorrowList({ page: page.value, page_size: pageSize.value, active_only: 1 })
  borrowingList.value = res.data.list
  total.value = res.data.total
}

async function handleLend() {
  if (!form.reader_id || !form.book_id) {
    ElMessage.warning('请选择读者和图书')
    return
  }
  await lendBook(form)
  ElMessage.success('借书成功')
  form.book_id = null
  loadBooks()
  loadBorrowing()
}

async function handleReturn(row) {
  await ElMessageBox.confirm(`确定为「${row.reader_name}」办理还书吗?`, '还书确认', { type: 'info' })
  const res = await returnBook(row.id)
  ElMessage.success(res.msg)
  loadBooks()
  loadBorrowing()
}

async function handleRenew(row) {
  await ElMessageBox.confirm(`确定为「${row.book_title}」办理续借吗?`, '续借确认', { type: 'info' })
  await renewBook(row.id)
  ElMessage.success('续借成功')
  loadBorrowing()
}

onMounted(() => { loadReaders(); loadBooks(); loadBorrowing() })
</script>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值