教材管理系统

文章目录

  • 教材管理系统
    • 一、系统演示
    • 二、项目介绍
    • 三、系统部分功能截图
    • 四、部分代码展示
    • 五、底部获取项目源码(9.9¥带走)

教材管理系统

一、系统演示

教材管理系统

二、项目介绍

语言:nodejs
框架:egg.js、Vue
数据库:MySQL

三个角色:管理员、教师、学生

用户管理、角色管理、菜单管理、教材数据、教材征订、出入库记录、领书操作、领书分析、班级管理、课程信息、教师信息

三、系统部分功能截图

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

四、部分代码展示

import { createRouter, createWebHashHistory } from 'vue-router'
import { getToken } from '../utils/cookie'
import { roleMenu } from '@/api/login'
import store from '@/store'

export const constantRoutes = [
  {
    path: '/login',
    name: 'Login',
    hidden: '1',
    component: () => import(/* webpackChunkName: "login" */ '../views/login/Login.vue')
  },
  {
    path: '/change/psw',
    component: () => import('../views/index/Home.vue'),
    redirect: '/change/psw',
    hidden: '1',
    children: [
      {
        path: '',
        component: () => import('@/views/index/ChangePsw'),
        name: 'ChangePsw',
        hidden: '0',
        meta: { title: '修改密码', icon: '' }
      }
    ]
  },
  {
    path: '/:w+',
    component: () => import('@/views/index/404.vue'),
    hidden: '1'
  }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes: constantRoutes
})

const whiteList = ['/login']
router.beforeEach((to, from, next) => {

  document.title = to.meta.title || '系统'

  const token = getToken()
  if (token) {
    if (to.path === '/login') {
      next('/')
    } else {
      if (store.getters.addRouters.length === 0) {
        roleMenu().then(res => {
          store.dispatch('generateRoutes', res.data).then(() => {
            store.getters.addRouters.forEach(item => {
              router.addRoute(item)
            })
            next({ ...to, replace: true })
          })
        })
        store.dispatch('getCurUserInfo')
      } else {
        next()
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      next('/login')
    }
  }
})

router.afterEach((to, from) => {
  setKeepAlive(to)
})

function setKeepAlive(to) {
  if(to.meta.keep_alive === '1') {
    store.commit("setKeepAlive", to.name);
  }
}

export default router

<template>
  <div style="padding:20px;margin:20px;">

    <el-form :inline="true" :model="seacheForm" size="small">
      <el-form-item label="教材">
        <el-input v-model="seacheForm.book" clearable placeholder="请输入教材名称" size="small" />
      </el-form-item>
      <el-form-item label="书号">
        <el-input v-model="seacheForm.ISBN" clearable placeholder="请输入教材书号" size="small" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" size="small" @click="refresh">搜索</el-button>
      </el-form-item>
    </el-form>

    <el-table :data="bookTable" v-loading="tableLoading" size="small" style="width: 100%">
      <el-table-column type="index" label="序号" align="center" fixed="left" width="55"  />
      <el-table-column prop="book" align="center" label="教材名称" fixed="left" width="200" show-overflow-tooltip />
      <el-table-column prop="editor" align="center" label="主编" show-overflow-tooltip />
      <el-table-column prop="pubDate" align="center" label="出版时间" show-overflow-tooltip />
      <el-table-column prop="ISBN" align="center" label="书号"  show-overflow-tooltip />
      <el-table-column prop="publisher" align="center" label="出版社" show-overflow-tooltip />
      <el-table-column prop="book_type" align="center" label="教材类型" show-overflow-tooltip />
      <el-table-column prop="book_usage" align="center" label="使用情况" show-overflow-tooltip />
      <el-table-column prop="price" align="center" label="单价"  show-overflow-tooltip />
      <el-table-column prop="book_total" align="center" label="教材库存" fixed="right" show-overflow-tooltip />
      <el-table-column fixed="right" label="操作" align="center" width="120">
        <template #default="{row}">
          <el-button type="primary" size="small" @click="handleAddBook(row)">增订</el-button>
        </template>
      </el-table-column>
    </el-table>
    <div class="demo-pagination-block">
      <el-pagination
        v-model:currentPage="currentPage"
        v-model:page-size="pageSize"
        :page-sizes="[8, 20, 30, 50]"
        :background="false"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"/>
    </div> 

     <el-dialog v-model="addDialog" title="续订教材" width="55%">
       <el-form :model="form" :inline="true"  :rules="rules" ref="ruleFormRef"  label-width="120px">
        <el-form-item label="教材名称" prop="book">
          <el-input v-model="form.book" disabled placeholder="请输入教材名称" /> 
        </el-form-item>
        <el-form-item label="学生用书" prop="sdt_book_nums">
          <el-input v-model.number="form.sdt_book_nums" placeholder="请输入学生用书数量" /> 
        </el-form-item>
        <el-form-item label="学生备用" prop="standby_nums">
          <el-input v-model.number="form.standby_nums" placeholder="请输入学生备用书数量" /> 
        </el-form-item>
        <el-form-item label="教师用书" prop="tch_book_nums">
          <el-input v-model.number="form.tch_book_nums" placeholder="请输入教师用书数量" /> 
        </el-form-item>
        <el-form-item label="教材单价" prop="price">
          <el-input v-model.number="form.price" disabled placeholder="请输入教材单价(单位:元)" /> 
        </el-form-item>
        <el-form-item label="教材总数">
          <el-input v-model.number="bookTotal" disabled placeholder="教材总价" /> 
        </el-form-item>
        <el-form-item label="教材总价">
          <el-input v-model.number="bookPriceTotal" disabled placeholder="教材总价" /> 
        </el-form-item>
        <el-form-item label="教材使用情况" prop="book_usage">
          <el-select v-model="form.book_usage"  placeholder="请选择教材使用情况">
            <el-option label="选用" value="选用" />
            <el-option label="不选用" value="不选用" />
          </el-select>
        </el-form-item>
      </el-form>
        <template #footer>
          <span class="dialog-footer">
            <el-button @click="addDialog=false">取消</el-button>
            <el-button type="primary" @click="onSubmit">确定</el-button>
          </span>
        </template>
    </el-dialog>



  </div>
</template>
<script setup>
import { ElMessage,ElMessageBox } from 'element-plus';
import { computed, onMounted, reactive, ref} from 'vue';
import { operateRecord, operateStore } from '@/api/book/operate';
import { getRecordDetail } from '@/api/education/operate';
import dayjs from 'dayjs';
onMounted(()=>{
  refresh();
})
const tableLoading = ref(false);
const refresh = () =>{
  tableLoading.value = true;
  operateStore({
    limit:pageSize.value,
    page:currentPage.value,
    book:seacheForm.book,
    ISBN:seacheForm.ISBN,
  }).then(res=>{
    bookTable.value = res.data.rows;
    total.value = res.data.count;
    bookTable.value.forEach((item)=>{
      item.pubDate ? item.pubDate = dayjs(item.birth).format('YYYY-MM') : ''
    })
    tableLoading.value = false;
  })
}

// 搜索表单
const seacheForm = reactive({});

// 表格
const bookTable = ref([]);
const total = ref(0);
const bookTotal = computed(()=>{
  let total = form.value.sdt_book_nums + form.value.standby_nums + form.value.tch_book_nums;
  return total;
})
const bookPriceTotal = computed(()=>{
  let total = (form.value.sdt_book_nums + form.value.standby_nums + form.value.tch_book_nums) * Number(form.value.price);
  return total;
})
const addDialog = ref(false);
const form = ref({});
const handleAddBook = (row) =>{
  getRecordDetail({record_id:row.record_id}).then(res=>{
    form.value = res.data[0];
    form.value.sdt_book_nums=0
    form.value.standby_nums=0
    form.value.tch_book_nums=0
    addDialog.value = true;
  })
}

const onSubmit = () =>{
  if(bookTotal.value==0){
    ElMessage({type:'warning',message:'未填写征订数量!'});
    return;
  }
  delete form.value.id;
  form.value.total = bookPriceTotal.value;
  form.value.book_total = bookTotal.value;
  form.value.apply = 1;
  operateRecord({
    data:form.value,
    flag:1
  }).then(res=>{
    if(res.success){
      ElMessage({type:'success',message:'申请成功!'});
      addDialog.value = false;
    }else{
      ElMessage({type:'danger',message:'申请失败!'});
    }
  })
}



// 分页
const currentPage = ref(1);
const pageSize = ref(8);
const handleSizeChange = (val) => {
  pageSize.value = val;
  refresh();
}
const handleCurrentChange = (val) => {
  currentPage.value = val;
  refresh();
}

</script>

<style>
</style>

import { createRouter, createWebHashHistory } from 'vue-router'
import { getToken } from '../utils/cookie'
import { roleMenu } from '@/api/login'
import store from '@/store'

export const constantRoutes = [
  {
    path: '/login',
    name: 'Login',
    hidden: '1',
    component: () => import(/* webpackChunkName: "login" */ '../views/login/Login.vue')
  },
  {
    path: '/change/psw',
    component: () => import('../views/index/Home.vue'),
    redirect: '/change/psw',
    hidden: '1',
    children: [
      {
        path: '',
        component: () => import('@/views/index/ChangePsw'),
        name: 'ChangePsw',
        hidden: '0',
        meta: { title: '修改密码', icon: '' }
      }
    ]
  },
  {
    path: '/:w+',
    component: () => import('@/views/index/404.vue'),
    hidden: '1'
  }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes: constantRoutes
})

const whiteList = ['/login']
router.beforeEach((to, from, next) => {

  document.title = to.meta.title || '系统'

  const token = getToken()
  if (token) {
    if (to.path === '/login') {
      next('/')
    } else {
      if (store.getters.addRouters.length === 0) {
        roleMenu().then(res => {
          store.dispatch('generateRoutes', res.data).then(() => {
            store.getters.addRouters.forEach(item => {
              router.addRoute(item)
            })
            next({ ...to, replace: true })
          })
        })
        store.dispatch('getCurUserInfo')
      } else {
        next()
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      next('/login')
    }
  }
})

router.afterEach((to, from) => {
  setKeepAlive(to)
})

function setKeepAlive(to) {
  if(to.meta.keep_alive === '1') {
    store.commit("setKeepAlive", to.name);
  }
}

export default router

五、底部获取项目源码(9.9¥带走)

有问题,或者需要协助调试运行项目的也可以

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/383766.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python入门篇:程序的3种控制结构

Python程序中有三种主要的控制结构&#xff1a;顺序结构、选择结构和循环结构。这些结构允许程序以不同的方式执行代码块&#xff0c;从而实现各种功能。 1.顺序结构&#xff1a;这是最简单的控制结构&#xff0c;程序按照代码从上到下的顺序逐行执行。如果没有特定的控制流语句…

中科大计网学习记录笔记(八):FTP | EMail

前言&#xff1a; 学习视频&#xff1a;中科大郑烇、杨坚全套《计算机网络&#xff08;自顶向下方法 第7版&#xff0c;James F.Kurose&#xff0c;Keith W.Ross&#xff09;》课程 该视频是B站非常著名的计网学习视频&#xff0c;但相信很多朋友和我一样在听完前面的部分发现信…

时域和离散域的重要转换器

自然界的模拟信号都是连续信号&#xff0c;也就是我们常说的时域信号&#xff0c;而我们的计算机只能处理离线的数字量信号&#xff0c;但是我们的闭环控制系统都是由离散域和时域所组成的&#xff0c;这里的离散域包括我们的计算机微控制器&#xff0c;时域包括我们的被控对象…

错误的集合(力扣刷题)

个人主页&#xff08;找往期文章包括但不限于本期文章中不懂的知识点&#xff09;&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 由于作者比较菜&#xff0c;还没学malloc这个函数&#xff0c;因此这个题目只写一些与原题大致的思路。 题目链接&#xff1a;645. 错误的集合 - 力扣…

小周带你正确理解Prompt-engineering,RAG,fine-tuning工程化的地位和意义

有人会说&#xff1a;"小周&#xff0c;几天不见这么拉了&#xff0c;现在别说算法了&#xff0c;连code都不讲了&#xff0c;整上方法论了。" 我并没有拉&#xff01;而且方法论很重要&#xff0c;尤其工程化的时候&#xff0c;你总得知道每种技术到底适合干啥&…

创建你的第一个Vue项目(小白专享版本)

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

TestNG基础教程

TestNG基础教程 一、常用断言二、执行顺序三、依赖测试四、参数化测试1、通过dataProvider实现2、通过xml配置&#xff08;这里是直接跑xml&#xff09; 五、testng.xml常用配置方式1、分组维度控制2、类维度配置3、包维度配置 六、TestNG并发测试1、通过注解来实现2、通过xml来…

05.坐标系

1. 坐标系原点 坐标系原点就是屏幕/窗口的左上角&#xff0c;X向右增长&#xff0c;Y向下增长。 2.设置控件位置 设置控件位置&#xff0c;就相当于是需要指定控件的坐标&#xff0c;对于该控件来说&#xff0c;其坐标原点是其父窗口/父控件的左上角。 设置方法就是通过控件的…

腾讯云4核8G服务器够用吗?容纳多少人同时访问?

腾讯云4核8G服务器支持多少人在线访问&#xff1f;支持25人同时访问。实际上程序效率不同支持人数在线人数不同&#xff0c;公网带宽也是影响4核8G服务器并发数的一大因素&#xff0c;假设公网带宽太小&#xff0c;流量直接卡在入口&#xff0c;4核8G配置的CPU内存也会造成计算…

《CSS 简易速速上手小册》第9章:CSS 最佳实践(2024 最新版)

文章目录 9.1 维护大型项目的 CSS9.1.1 基础知识9.1.2 重点案例&#xff1a;构建一个可复用的 UI 组件库9.1.3 拓展案例 1&#xff1a;优化现有项目的 CSS 结构9.1.4 拓展案例 2&#xff1a;实现主题切换功能 9.2 BEM、OOCSS 和 SMACSS 方法论9.2.1 基础知识9.2.2 重点案例&…

C/C++模板初阶

目录 1. 泛型编程 2. 函数模板 2.1 函数模板概念 2.1 函数模板格式 2.3 函数模板的原理 2.4 函数模板的实例化 2.5 模板参数的匹配原则 3. 类模板 3.1 类模板的定义格式 3.2 类模板的实例化 1. 泛型编程 如何实现一个通用的交换函数呢&#xff1f; void Swap(int&…

【Jenkins】Jenkins关闭Jenkins关闭、重启

目录 一、Jenkins关闭、重启 二、Jenkins服务的启动、停止方法。 一、Jenkins关闭、重启 1.关闭Jenkins 只需要在访问jenkins服务器的网址url地址后加上exit&#xff0c;关闭Jenkins服务。 例如&#xff1a;http://localhost:8081/exit 2.重启Jenkies 只有在Jenkins服务启动…

亚马逊认证考试系列 - 知识点 - LightSail介绍

一、引言 在当今云计算的时代&#xff0c;亚马逊网络服务&#xff08;AWS&#xff09;已成为业界领先的云服务提供商。其中&#xff0c;LightSail服务是AWS为简化云计算的入门和使用而推出的一项服务。它特别适合那些想要快速搭建网站、开发环境或小型应用的用户。通过LightSa…

[office] Excel表格中自动添加的超连接怎么取消? #媒体#其他#知识分享

Excel表格中自动添加的超连接怎么取消&#xff1f; Excel表格中自动添加的连接怎么取消&#xff1f;有时候在Excel2013中输入网址或邮箱时会自动添加超连接&#xff0c;本质上这是很人性化的功能&#xff0c;可是对很多人来说可能用不到&#xff0c;而且很繁琐&#xff0c;下面…

寒假作业:2024/2/11

作业1&#xff1a;使用递归实现n! 代码&#xff1a; #include <stdio.h> #include <string.h> #include <stdlib.h> int fun(int n) {if(0n){return 1;}else{return n*fun(n-1);} } int main(int argc, const char *argv[]) {int n;printf("please en…

寒假思维训练day20

更新一道1600的反向贪心 题意&#xff1a; 有n场比赛&#xff0c;且小明的智商是m&#xff0c;每场比赛需要的智商是,当时, 可以直接看题&#xff0c;当时&#xff0c;需要智商m减1才能看这道题&#xff0c;当智商为0不能继续往下看题&#xff0c;问最多能看多少题 题解&#x…

【洛谷题解】P1029[普及组]最大公约数和最小公倍数问题

题目链接&#xff1a;[NOIP2001 普及组] 最大公约数和最小公倍数问题 - 洛谷 题目难度&#xff1a;普及- 涉及知识点&#xff1a;stl函数&#xff0c;最大公因数&#xff0c;最小公倍数 题意&#xff1a; 输入输出样例&#xff1a; 分析&#xff1a;直接套用公式优化累加即…

解决Typora导出HTML不显示图片

解决Typora导出HTML不显示图片 产生原因 Typora导出HTML不显示图片&#xff0c;可能时图片存放在我们的硬盘中。 我们可以将markdown中的图片转化为base64格式&#xff0c;嵌入到html中。 解决步骤 首先&#xff0c;下载 TyporaToBase64.jar 密码:45jq 其次&#xff0c;将…

DS:单链表实现队列

创作不易&#xff0c;友友们来个三连支持吧&#xff01; 一、队列的概念 队列&#xff1a;是只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出FIFO(First In First Out)的特点。 入队列&#xff1a;进行插入操作…

VTK 三维场景的基本要素(相机) vtkCamera 相机的运动

相机的运动 当物体在处于静止位置时&#xff0c;相机可以在物体周围移动&#xff0c;摄取不同角度的图像 移动 移动分为相机的移动&#xff0c;和相机焦点的移动&#xff1b;移动改变了相机相对焦点的位置&#xff0c;离焦点更近或者更远&#xff1b;这样就会改变被渲染的物体…