疫情下的学生管理系统

代码:源码地址

采用前后端分离的开发模式
前端技术栈 Vue.js + Vue-router + axios + Element-plus
后端技术栈 Node.js + Express + Jwt + Mysql

之前我用的vue2+element-ui,现在我用vue3+element-plus重写,vue3和vue2的区别具体看官网

捋一下开发流程


前端:借助Vue.js,创建项目借助脚手架Vue CLI,用npm安装的话,要事先安装Node.js
前端项目创建:vue create client (也可使用图形化界面:vue ui)
初始化项目的同时安装了:Babel、Router、Linter/Formatter,路由使用history mode、ESLint + Standard config、Lint on save
安装Element-plus:npm install element-plus --save
安装less依赖(一种css的扩展语言):npm install less less-loader --save

②首先整一整根组件App.vue、入口文件main.js
引入了Element库,需要注册一下(全局或者按需注册),具体看官网
组件准备全放在components文件夹中,assets文件夹中放一些css文件、图片等资源
从jsconfig.json文件中得知@默认表示/src路径,所以引入文件的时候可用@符号简化路径
router文件夹下面的index.js中配置路径和组件的映射,组件先简单写点东西上去就行

③首先写登录界面LoginPage.vue,再写主页面HomePage.vue
主页面顶部样式确定,左侧边栏样式确定,但是会根据不同身份(管理员、学生、辅导员)渲染不同的菜单(不同身份的id不同,可以由此入手)
菜单右边的主要内容区域,会根据不同URL替换不同组件,比如有关个人信息的ProfilePage.vue、请假相关的LeaveForm.vue,体温上报相关的TemForm.vue等等
开发菜单过程中,用到了icon图标,根据官网要先安装element-plus/icons-vue,icon组件局部按需导入就行。不同身份的登录者,菜单不同,可利用component组件动态渲染

④初始化后端项目:npm init -y,新建入口文件app.js
安装express:npm i express
开发之前安装nodemon(npm i nodemon -g),它能自动检测文件更改并重启应用程序
数据库采用mysql,可用phpstudy一键开启,然后用Navicat建表,五张表:用户账号表、学生个人信息表,辅导员个人信息表、假条相关的表、体温表相关的表
安装mysql模块:npm i mysql
由于客户端端口和服务器运行端口不一致,为了实现跨域,在app.js中使用cors模块(npm i cors)
在app.js中用express.json和express.urlencoded解析JSON格式和URL-encoded格式的请求体数据

⑤后端项目新建model文件夹,里面存放一些模型,和数据库中的表对应,实际上就是一个类,里面封装了对该表增删改查的一些方法,这些模型都继承自一个父类,这个父类我们放在model.js文件中,父类里面封装了三个静态方法:连接数据库、执行一个sql语句、执行两个sql语句。

后端项目新建route文件夹,分几大模块,有相同路由前缀的放在一个模块中。统一在app.js中导入这些模块,当客户端发出请求请求时,针对不同的url前缀进行拦截,再转入到route文件夹下的对应模块中去处理

⑦先撰写登录界面的逻辑
和教务网类似,无需注册账号,账号即学号,管理员提前注册好,初始化密码都为11111
提交表单之前进行校验:学号、密码不能为空,密码3~15个字符,不符合这些要求就报错
重置功能(如果有默认值,那么重置之后为默认值,不为空)
开发过程中需要用到element-plus/icons-vue包中的icon图标,局部导入即可

用JWT实现跨域认证(npm i jsonwebtoken)
util文件夹下面的jwt.js中我们导出该模块的三个方法:sign、verify、decode,并用promisify方法对这些方法promisify化(使用该方法需安装util模块:npm i util)
当用户初次登录成功时,服务端生成token返回给客户端,token的生成需要密钥(可以用UUID),将该密钥放在config文件夹下面的config.default.js中
middleware文件夹下面创建一个用于身份验证的中间件auth.js,除了登录请求以外,其它请求都需要先经过这个中间件进行身份验证
客户端收到token以后,连同登录时输入的id和identity一并存入sessionStorage
前端项目安装axios(npm i axios),用axios在main.js中配置请求的根目录,拦截客户端发出的请求,为请求头对象添加token验证的Authorization字段,最后挂载axios使其在全局范围内可用

⑧开发其它模块,可借助Postman向服务端发请求,查看返回的数据,从而协助前端的开发,同时写接口文档
管理员 - 账号管理功能
学生 - 个人信息管理、请假条、提交体温表
辅导员 - 个人信息管理、审批假条、查看汇总的体温表

每个模块关键就是表格的开发,都差不多,这里以账号管理功能模块为例

可通过搜索框进行查询,可添加、删除账号
账号删除时让用户再确认一次
查询账号依据学号/工号,模糊查询(在sql中用like)
分页,要及时检测当前页码和每页显示数据条数,及时传给url的Params
添加用户,提交表单之前要进行校验,默认身份是学生
关闭添加用户的dialog,数据要重置,确保下次再点开是干净的
已经创建的账号如果再添加,就报错
修改账号,dialog要先获取数据进行渲染,只有密码pwd可以修改
在user表中创建新账号时,student或counselor表也需要初始化记录(例如都为‘空’),删除账号时同时也要删(通过外键约束中的级联)

⑨后端项目要安装silly-datetime,用于时间戳(假条和体温表的创建时间)的格式转换:npm i silly-datetime --save
假条状态:0-未审批 1-审批未通过 2-审批通过
获取假条数据为0条时,不去报错

⑩在前端项目router/index.js文件中加入beforeEach(to, from, next) 全局前置守卫(路由跳转前触发),如果你没有登录,也就拿不到token,那你就没有权限访问其它页面。

开发过程中的问题

1.什么是回调函数?

回调函数就是传递一个参数化的函数,就是将这个函数作为一个参数传到另一个主函数里面,当那一个主函数执行完之后,再执行这个函数。

2.异步编程

同步函数:当一个同步函数被调用时,该函数不会立即返回,直到该函数里面的代码全部执行完了才返回
异步函数:当一个异步函数被调用时,该函数会立即返回,尽管这个函数规定的操作任务还没有完成
所以,异步函数不阻塞代码
但是,如果我想要异步函数执行的结果,我怎么获取?
可以用回调函数来接收嘛。而Promise能够以一种更优雅的方式执行异步操作。

 new Promise((resolve, reject) => {
    setTimeout(() => {
      //成功的时候调用resolve
      // resolve('Free Loop');
      //失败的时候调用reject
      reject('Error Message');
    }, 1000)
  }).then((data) => {
    console.log(data);
  }).catch((err) => {
    console.log(err);
  })

而在js中,我们可以用async/await来更简洁地实现基于Promise的异步行为
使用async关键字声明一个异步函数。
await  操作符用于等待一个Promise 对象,只能在异步函数 async function 中使用。
await 会暂停当前 异步函数 的执行,等待 Promise 处理完成。
若 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值,继续执行 异步函数下面的代码
若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出
另外,如果 await 操作符后的表达式的值不是一个 Promise,则返回该值本身

function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

async function f1() {
  var x = await resolveAfter2Seconds(10);
  console.log(x); // 10
}
f1();

3.http请求报文

请求行+请求头+请求体

举例:

1 GET/sample.jspHTTP/1.1
2 Accept:image/gif.image/jpeg,*/*
3 Accept-Language:zh-cn
4 Connection:Keep-Alive
5 Host:localhost
6 User-Agent:Mozila/4.0(compatible;MSIE5.01;Window NT5.0)
7 Accept-Encoding:gzip,deflate
8
9 username=jinqiao&password=1234

第一行为http请求行,包含方法,URI 和http版本
2-7为请求头,包含浏览器,主机,接受的编码方式和压缩方式
第8行表示一个空行 表示请求头结束 这个空行是必须的
第9行是数据体,比如是需要查询的信息

4.跨域问题

从一个域名去请求另一个域名的资源,如果跨域,将无法访问
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

http://www.test.com/http://www.test.com/index.html同源(协议、域名、端口号相同)
http://www.test.com/https://www.test.com/index.html跨域协议不同(http/https)
http://www.test.com/百度一下,你就知道跨域主域名不同(test/baidu)
http://www.test.com/http://blog.test.com/跨域子域名不同(www/blog)
http://www.test.com:8080/http://www.test.com:7001/跨域端口号不同(8080/7001)

本项目客户端端口为8080,后台项目监听的是5000端口,存在跨域

解决方法①

app.all("*", function (req, res, next) {
  // 设置允许跨域的域名,*代表允许任意域名跨域
  res.header("Access-Control-Allow-Origin", "*")
  // 允许的header类型
  res.header("Access-Control-Allow-Headers", "*")
  // 跨域允许的请求方式
  res.header("Access-Control-Allow-Methods", "*")
  if (req.method.toLowerCase() === 'options')
    res.sendStatus(200)
  else
    next()
})

解决方法②

安装cors模块        npm install cors --save

const cors = require('cors')    //导入

app.use(cors())                 //使用,要放在所有API前面

5.sessionStorage、localStorage、cookie

都用于浏览器本地存储数据

cookie是当你浏览某个网站的时候,由web服务器存储在你的机器硬盘上的一个小的文本文件。它记录了你的用户名、密码、浏览的网页、停留的时间等等信息。当你再次来到这个网站时,web服务器会先看看有没有它上次留下来的cookie。如果有的话,会读取cookie中的内容,来判断使用者,并送出相应的网页内容,比如在页面显示欢迎你的标语,或者让你不用输入ID、密码就直接登录等等。

sessionStorage用于本地存储一个会话中的数据,这些数据只有在同一个会话中的页面才能访问,并且当会话结束后,数据也随之销毁。所以sessionStorage仅仅是会话级别的存储,而不是一种持久化的本地存储。

localStorage是持久化的本地存储,除非是通过js删除,或者清除浏览器缓存,否则数据是永远不会过期的。

客户端收到服务器返回的JWT,可以存储在Cookie里面,也可以存储在localStorage

此后,客户端每次与服务器通信,都要带上这个JWT。你可以把它放在Cookie里面自动发送,但是这样不能跨域,所以更好的做法是放在HTTP请求的头信息Authorization字段里面 

6.其余

如何消除地址栏中url的/#/?

路由有两种模式 hash 和 history,默认为hash,所以路径会有 # 号,因此我们只需要修改成 history 即可

const router = new VueRouter({
  mode: 'history',
  routes: [
    ...
  ]
})

什么作为假条/体温表的主键? 

可以用序号,不过要注意每次创建假条的序号 = 当前数据库中所有假条序号最大值+1

获取请求的方式

req.params       用于获取url中的参数
req.query         用于获取get请求中的参数(params)中的数据
req.body           用于获取post请求中的数据

vue3中新增了v-model:xxx 的写法,但是vscode报错,可能是Vetur插件的问题

解决方法

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

漂流の少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值