[关闭]
@Bios 2018-12-10T08:38:59.000000Z 字数 16049 阅读 1085

在线考试系统(vue2 + elementui + express4 + MongoDB)

Vue express


这是我毕业项目,从0到1,前后台独立开发完成。功能不多,在此记录,温故而知新!项目github地址:https://github.com/FinGet/Exam ,博客地址:https://finget.github.io/


更新记录:2018-4-9,md5加密

安装mongodb

window下安装mongodb,需要参考的可以移步我的博客中:win10安装mongodb

项目初始化

本次项目使用的是express4 + vue2+ + elementUI1+ + mongodb3.4+

先看项目文件目录结构:

我页面用的vue所以server/viewsserver/public都没有用

  1. npm i -g express-generator
  2. // 在项目文件根目录下
  3. express server

由于前后台都是写在一个项目中的,我就将server下的package.jsonvue下的package.json合并了

安装一些插件

axios 请求数据

npm i axios --save
首先axios不支持vue.use()式声明

  1. // 在main.js中如下声明使用
  2. import axios from 'axios';
  3. Vue.prototype.$axios=axios;
  4. // 那么在其他vue组件中就可以this.$axios调用使用

elementUI

npm i element-ui --save

  1. import ElementUI from 'element-ui' // 加载ElementUI
  2. import 'element-ui/lib/theme-default/index.css'
  3. Vue.use(ElementUI) // 全局使用elementUI

vue-lazyload 图片懒加载

npm i vue-lazyload --save

  1. // main.js
  2. import VueLazyLoad from 'vue-lazyload'
  3. Vue.use(VueLazyLoad, { // 全局使用图片懒加载
  4. loading: 'static/loading-svg/loading-bars.svg', // 图片还没加载时的svg图片
  5. try: 1 // default 1
  6. })

使用懒加载:

  1. <img width="300" height="53" v-lazy="logoSrc" alt="">
  2. logoSrc:require('../common/img/logo.png')
  3. // 不能写成:<img width="300" height="53" v-lazy="../common/img/logo.png" alt="">

mongoose 操作mongodb的

npm i mongoose --save

就不一一列举所有的插件了(没有用vuex)

开发上的一些事

前台相关

sessionStorage

  1. // commonFun.js
  2. //获取sessionStorage
  3. function getSessionStorage(key, format) {
  4. var data;
  5. if (sessionStorage.getItem(key)) {
  6. if (format == 'json') {
  7. data = JSON.parse(sessionStorage.getItem(key));
  8. } else {
  9. data = sessionStorage.getItem(key);
  10. }
  11. } else {
  12. data = false
  13. }
  14. return data;
  15. }
  16. //写入sessionStorage
  17. function setSessionStorage(key, content, format) {
  18. var data;
  19. if (format == 'json') {
  20. data = JSON.stringify(content);
  21. } else {
  22. data = content;
  23. }
  24. sessionStorage.setItem(key, data);
  25. }
  26. export var mySessionStorage = {
  27. get: getSessionStorage,
  28. set: setSessionStorage
  29. }

全局挂载

  1. // main.js
  2. import * as commonFun from './common/js/commonFun.js'
  3. Vue.prototype.$mySessionStorage = commonFun.mySessionStorage;

在页面中使用

  1. this.$mySessionStorage.set(key,content,format);
  2. this.$mySessionStorage.get(key);

登录检测

  1. // main.js
  2. // 登录判断
  3. router.beforeEach((to, from, next) => {
  4. var userdata = getUserData();
  5. if (to.path != '/managelogin'&&to.name!='404'&&to.path != '/'&&to.path != "/frontregister"&&to.path!='/manageregister') { // 判断是否登录
  6. if(!userdata.userName){
  7. ElementUI.Message.error('抱歉,您还没有登录!');
  8. if(to.path.indexOf('front')>0){
  9. router.push({path:'/'});
  10. } else {
  11. router.push({path:'/managelogin'});
  12. }
  13. } else {
  14. next();
  15. }
  16. }
  17. else {
  18. next();
  19. }
  20. })

面包屑导航

绑定面包屑要根据实际情况来定,但是this.$router.currentRoute.matched是最主要的

  1. <template>
  2. <div class="bread">
  3. <el-breadcrumb separator="/">
  4. <el-breadcrumb-item v-for="(item, index) in breadData" :key="item.id" :to="{ name: item.meta.breadName=='管理系统'?'Index':item.name }">{{item.meta.breadName}}</el-breadcrumb-item>
  5. </el-breadcrumb>
  6. </div>
  7. </template>
  8. <script type="text/ecmascript-6">
  9. export default {
  10. data() {
  11. return {
  12. breadData:[]
  13. }
  14. },
  15. watch: {
  16. $route () {
  17. this.initBreadData();
  18. }
  19. },
  20. methods:{
  21. //面包屑
  22. initBreadData(){
  23. this.breadData=this.$router.currentRoute.matched;
  24. // console.log(this.breadData)
  25. }
  26. },
  27. created(){
  28. this.initBreadData();
  29. }
  30. }
  31. </script>

路由部分:

elementui面包屑导航与左侧导航相对应

根据实际情况来,不能套用,要看你的路由怎么写的 this.$router.currentRoute.path
:default-active="activeIndex"

  1. // conponents/sidebar.vue
  2. //初始化列表active状态
  3. ...
  4. methods:{
  5. initActiveIndex(){
  6. // var str =this.$router.currentRoute.path;
  7. this.activeIndex=this.$router.currentRoute.path;
  8. // console.log(str)
  9. }
  10. },
  11. watch:{
  12. '$route':'initActiveIndex'
  13. },
  14. created(){
  15. this.initActiveIndex();
  16. }
  17. ...

配置代理

要想请求到后台数据,这一步是必须的
配置代理之后,localhost:8088/api/* -> localhost:3000/api/*

  1. config/index.js
  2. proxyTable: {
  3. // proxy all requests starting with /api to jsonplaceholder
  4. '/api': {
  5. target: 'http://127.0.0.1:3000/api', // 端口号根据后台设置来,默认是3000
  6. changeOrigin: true,
  7. pathRewrite: {
  8. '^/api': '' // 若target中没有/api、这里又为空,则404;
  9. }
  10. }
  11. },

ElementUi动态增加表单的表单验证 大坑

  1. <div v-if="dialogForm.type!='judgement'&&dialogForm.type!='Q&A'">
  2. <el-form-item v-for="(item,index) in dialogForm.surveyQuestionOptionList"
  3. :key="item.key"
  4. :label="'选项'+(index+1) +':'"
  5. :prop="'surveyQuestionOptionList.' + index + '.optionContent'"
  6. :rules="{
  7. required:true, message:'选项不能为空', trigger:'blur'
  8. }"
  9. >
  10. // 最重要的是prop 一定要带上`.optionContent`,也就是你绑定值的key
  11. <el-input placeholder="请输入选项" class="dialog_input" v-model="item.optionContent"></el-input>
  12. <i class="el-icon-delete delete-icon" @click="deleteDlalogOption(index)"></i>
  13. </el-form-item>
  14. <el-button type="primary" size="small" class="marginB10" @click="addDialogOption">添加选项</el-button>
  15. </div>

query要用path来引入,params要用name来引入

  1. goToExam(id){
  2. // params传参只能用name引入
  3. this.$router.push({name:'ForntExam',params:{id:id}});
  4. }

Elementui 单选框对上单选题

  1. <div class="single">
  2. <h4>单选题(只有一个正确答案)</h4>
  3. <ul>
  4. <li class="marginB10" v-for="(item,index) in singleQuestions" :key="item.id">
  5. <p class="question-title">{{index+1}} 、{{item.name}}()</p>
  6. <span class="option"
  7. v-if="item.type!='judgement'&&item.type!='Q&A'"item
  8. v-for="(item1,index1) in item.selection" :key="item1.id">
  9. <el-radio v-model="item.sanswer" :label="options[index1]" :key="index1">{{options[index1]}}、{{item1}}</el-radio>
  10. </span>
  11. </li>
  12. </ul>
  13. </div>
  1. init(){
  2. if(this.id == '' || !this.id ){
  3. this.$router.push({path:'forntexamindex'});
  4. return
  5. } else {
  6. this.$axios.get('/api/getExamInfo',{
  7. params:{
  8. id: this.id
  9. }
  10. }).then(response => {
  11. let res = response.data;
  12. if(res.status == '0') {
  13. for(let key in this.paperData) {
  14. this.paperData[key] = res.result[key];
  15. }
  16. res.result._questions.forEach(item => {
  17. if(item.type=='single'){
  18. item.sanswer = ''; // 重要的在这 给他新增一个属性,用来存答案
  19. this.singleQuestions.push(item);
  20. } else if(item.type == 'multi'){
  21. item.sanswer = []; // 多选题
  22. this.multiQuestions.push(item);
  23. } else if(item.type == 'Q&A') {
  24. item.sanswer = '';
  25. this.QAQuestions.push(item);
  26. } else if(item.type == 'judgement'){
  27. item.sanswer = '';
  28. this.judgeQuestions.push(item);
  29. }
  30. })
  31. }
  32. }).catch(err => {
  33. this.$message.error(err);
  34. })
  35. }
  36. }

后台相关

连接数据库

在server根目录下新建db.js

  1. // db.js
  2. var mongoose = require('mongoose');
  3. var dbUrl = 'mongodb://127.0.0.1:27017/examSystem';
  4. var db = mongoose.connect(dbUrl);
  5. db.connection.on('error',function(error) {
  6. console.log('数据库链接失败:'+ error);
  7. });
  8. db.connection.on('connected',function() {
  9. console.log('数据库链接成功!');
  10. });
  11. db.connection.on('disconnected',function() {
  12. console.log('Mongoose connection disconnected');
  13. });
  14. module.exports = db;
  1. // server/app.js
  2. // 链接数据库
  3. require('./db');

配置seesion

需要express-sessioncookie-parser插件

  1. // app.js
  2. // 加载解析session的中间件
  3. // session 的 store 有四个常用选项:1)内存 2)cookie 3)缓存 4)数据库
  4. // 数据库 session。除非你很熟悉这一块,知道自己要什么,否则还是老老实实用缓存吧 需要用到(connect-mongo插件 line 7)
  5. // app.use(sessionParser({ 会在数据库中新建一个session集合存储session
  6. // secret: 'express',
  7. // store: new mongoStore({
  8. // url:'mongodb://127.0.0.1:27017/examSystem',
  9. // collection:'session'
  10. // })
  11. // }));
  12. // 默认使用内存来存 session,对于开发调试来说很方便
  13. app.use(sessionParser({
  14. secret: '12345', // 建议使用 128 个字符的随机字符串
  15. name: 'userInfo',
  16. cookie: { maxAge: 1800000 }, // 时间可以长点
  17. resave:true,
  18. rolling:true,
  19. saveUninitialized:false
  20. }));

配置后台路由

默认的使用方式:

  1. // appi.js
  2. var index = require('./routes/index');
  3. app.use('/', index);
  1. // routes/index
  2. var express = require('express');
  3. var router = express.Router();
  4. /* GET home page. */
  5. router.get('/', function(req, res, next) {
  6. res.render('index', { title: 'Express' });
  7. });
  8. module.exports = router;

我之前做的一个电子商城采用的这种方式:github地址

我的项目中:

  1. // app.js
  2. var indexs = require('./routes/index');
  3. var routes = require('./routes/routes');
  4. indexs(app);
  5. routes(app);
  1. // routes/index.js
  2. module.exports = function(app) {
  3. app.get('/api', (req, res) => {
  4. res.render('index', {title: 'Express'});
  5. })
  6. }

两种方式有什么不同:
- 如果你有多个路由文件 (例如goods.js,index.js,users.js……),你都需要去app.js中引入

  1. // app.js
  2. var index = require('./routes/index');
  3. var users = require('./routes/users');
  4. var goods = require('./routes/goods');
  5. app.use('/', index);
  6. app.use('/users', users);
  7. app.use('/goods', goods);

在前台请求的时候:

  1. // goods.js
  2. ....
  3. router.get("/list", function (req, res, next) {
  4. ...
  5. }
  1. // xxx.vue
  2. ...
  3. this.$axios.get('/goods/list').then()... // 不能忘了加上goods,也就是你在app.js中定义的一级路由
  4. ...

如果没看懂,可以去GitHub上看一下实际代码,有助于理解

  1. // route.js
  2. var Teacher = require('../controllers/teacher'),
  3. Student = require('../controllers/student');
  4. module.exports = function(app) {
  5. /*----------------------教师用户----------------------*/
  6. app.post('/api/register',Teacher.register);
  7. // 用户登录
  8. app.post('/api/login', Teacher.signup);
  9. // 登出
  10. app.post("/api/logout", Teacher.signout);
  11. // 获取用户信息
  12. app.post('/api/getUserInfo',Teacher.getUserInfo);
  13. // 修改用户信息
  14. app.post('/api/updateUser', Teacher.updateUser);
  15. // 获取试卷(分页、模糊查询)
  16. app.get('/api/mypapers', Teacher.getPapers);
  17. // 保存试卷
  18. app.post('/api/savePaper', Teacher.savePaper);
  19. // 发布试卷
  20. app.post('/api/publishPaper', Teacher.publishPaper);
  21. // 删除试卷
  22. app.post('/api/deletePaper', Teacher.deletePaper);
  23. // 查找试卷
  24. app.post('/api/findPaper', Teacher.findPaper);
  25. // 修改试题
  26. app.post('/api/updateQuestion', Teacher.updateQuestion);
  27. // 修改试卷
  28. app.post('/api/updatePaper', Teacher.updatePaper);
  29. // 获取所有的考试
  30. app.get('/api/getAllExams',Teacher.getAllExams);
  31. // 获取已考试的试卷
  32. app.get('/api/getExams',Teacher.getExams);
  33. // 获取学生考试成绩
  34. app.get('/api/getScores', Teacher.getScores);
  35. // 批阅试卷
  36. app.get('/api/getCheckPapers', Teacher.getCheckPapers);
  37. // 打分提交
  38. app.get('/api/submitScore', Teacher.submitScore);
  39. /*----------------------学生用户----------------------*/
  40. // 学生注册
  41. app.post('/api/studentregister',Student.register);
  42. // 学生登录
  43. app.post('/api/studentlogin', Student.signup);
  44. // 学生登出
  45. app.post('/api/studentlogout', Student.signout);
  46. // 修改信息
  47. app.post('/api/updateStudent', Student.updateStudent);
  48. // 获取考试记录
  49. app.get('/api/getexamlogs', Student.getExamLogs);
  50. // 获取个人信息
  51. app.get('/api/studentinfo', Student.getInfo);
  52. // 获取考试信息
  53. app.get('/api/getExamsPaper',Student.getExams);
  54. // 获取试卷信息
  55. app.get('/api/getExamInfo',Student.getExamInfo);
  56. // 提交考试信息
  57. app.post('/api/submitExam',Student.submitExam);
  58. }

可以看到,我将每个路由的方法都是提取出去的,这样可以避免这个文件不会有太多的代码,可读性降低,将代码分离开来,也有助于维护

在使用的时候:

  1. // xxx.vue
  2. ...
  3. this.$axios.get('/api/getexamlogs').then()...
  4. ...

数据库的相关操作

我这次用mongodb,主要是因为可以用js来操作,对我来说比较简单,mysql我不会用。在实际开发过程中发现,考试系统各个表(集合)都是需要关联,mongodb这种非关系型数据库,做起来反而麻烦了不少。在此将一些数据库增删改查的方法回顾一下。

初始化一条数据

如果对mongodb,mongoose没有基础的了解,建议看一看mongoose深入浅出mongoose基础操作

  1. // controllers/student.js
  2. const Student = require('../model/student');
  3. var mongoose = require('mongoose');
  4. var Schema = mongoose.Schema;
  5. var student = new Student({
  6. userId: 12001, // 学号
  7. userName: '张三', // 用户名
  8. passWord: '123321', // 密码
  9. grade: 3, // 年级 1~6 分别代表一年级到六年级
  10. class: 3, // 班级
  11. exams:[{ // 参加的考试
  12. _paper:Schema.Types.ObjectId("5a40a4ef485a584d44764ff1"), // 这个是_id,在mongodb自动生成的,从数据库复制过来,初始化一个学生,应该是没有参加考试的
  13. score:100,
  14. date: new Date(),
  15. answers: []
  16. }]
  17. })
  18. // 保存
  19. student.save((err,doc) => {
  20. console.log(err);
  21. });
用户注册,其实就是创建一条数据
  1. exports.register = function (req,res) {
  2. let userInfo = req.body.userInfo; // req.body 获取post方式传递的参数
  3. Student.findOne(userInfo,(err,doc) => {
  4. if(err) {
  5. ...
  6. } else {
  7. if(doc) {
  8. res.json({
  9. status:'2',
  10. msg: '用户已存在'
  11. })
  12. } else {
  13. userInfo.exams = [];
  14. // userInfo 是个对象,包含了用户相关的信息
  15. Student.create(userInfo,(err1,doc1) => {
  16. if(err1) {
  17. ...
  18. }else {
  19. if(doc1) {
  20. ...
  21. } else {
  22. ...
  23. }
  24. }
  25. })
  26. }
  27. }
  28. })
  29. };
获取考试记录,子文档数组分页模糊查询

如下图是我的student集合:

在该集合中,学生参加过的考试记录,存在exams数组中,当想实现分页查询几条数据的时候,需要用到$slice

$slice:[start,size] 第一个参数表示,数组开始的下标,第二个表示截取的数量
在后台接收到前台传递的pageSizepageNumber时,需要计算出当前需要截取的下标,即let skip = (pageNumber-1)*pageSize

  1. exports.getExamLogs = function (req, res){
  2. let userName =req.session.userName;
  3. let name = req.param('name');
  4. // 通过req.param()取到的值都是字符串,而limit()需要一个数字作为参数
  5. let pageSize = parseInt(req.param('pageSize'));
  6. let pageNumber = parseInt(req.param('pageNumber'));
  7. let skip = (pageNumber-1)*pageSize; // 跳过几条
  8. let reg = new RegExp(name,'i'); // 在nodejs中,必须要使用RegExp,来构建正则表达式对象。
  9. Student.findOne({"userName":userName},{"exams":{$slice:[skip,pageSize]}}).populate({path:'exams._paper',match:{name: reg}})
  10. .exec((err,doc) => {
  11. if (err) {
  12. ...
  13. } else {
  14. if (doc) {
  15. res.json({
  16. status: '0',
  17. msg:'success',
  18. result:doc,
  19. count: doc.exams.length?doc.exams.length:0
  20. })
  21. } else {
  22. ...
  23. }
  24. }
  25. })
  26. };
另一种分页模糊查询--在文档之间(document)

每个试卷都是独立的文档,通过他们的名称name实现模糊查询

  1. // 获取考试信息
  2. exports.getExams = function (req,res) {
  3. let userName =req.session.userName;
  4. let name = req.param('name');
  5. // 通过req.param()取到的值都是字符串,而limit()需要一个数字作为参数
  6. let pageSize = parseInt(req.param('pageSize'));
  7. let pageNumber = parseInt(req.param('pageNumber'));
  8. let skip = (pageNumber-1)*pageSize; // 跳过几条
  9. let reg = new RegExp(name,'i'); // 在nodejs中,必须要使用RegExp,来构建正则表达式对象。
  10. Student.findOne({"userName":userName},(err,doc)=>{
  11. if(err) {
  12. res.json({
  13. status: '1',
  14. msg: err.message
  15. })
  16. } else {
  17. if(doc) {
  18. // 关键在这里
  19. Paper.find({startTime:{$exists:true},name:reg}).skip(skip).limit(pageSize).populate({path:'_questions'}).exec((err1,doc1)=>{
  20. ....
  21. })
  22. };
还有一种模糊分页查询--查询关联文档再模糊分页查询

先通过populate查询除关联文档,在模糊分页查询

  1. exports.getPapers = function (req, res) {
  2. // console.log(req.session.userName);
  3. let name = req.param('name'),
  4. // 通过req.param()取到的值都是字符串,而limit()需要一个数字作为参数
  5. pageSize = parseInt(req.param('pageSize')),
  6. pageNumber = parseInt(req.param('pageNumber')),
  7. userName = req.session.userName;
  8. let skip = (pageNumber-1)*pageSize; // 跳过几条
  9. let reg = new RegExp(name,'i'); // 在nodejs中,必须要使用RegExp,来构建正则表达式对象。
  10. let params = {
  11. name: reg
  12. };
  13. Teacher.findOne({'userName':userName}).populate({path:'_papers',match:{name: reg},options:{skip:skip,limit:pageSize}})
  14. .exec((err, doc) => {
  15. ....
  16. })
  17. };
populate

mongodb本来就是非关系型的数据库,但是有很多时候不同的集合直接是需要关联的,这是就用到了mongoose提供的populate

直接看图,不同集合直接的关联,用的就是_id,比如下图中,学生参加的考试,关联了试卷,试卷里面又关联了题目

怎么查询呢:

  1. Student.findOne({}).populate({path:'exams._paper'}).exec(....)

更多的可以看看我项目中的实际代码都在server/controllers下面

关联集合的新增

在系统中,教师可以增加试卷,这个时候我就不知道该怎么保存前台传过来的数据。数据中既有试卷的信息,也有很多题目。题目都属于该试卷,改试卷又属于当前登录系统的老师(即创建试卷的老师)。
怎么才能让试卷、教师、问题关联起来啊,ref存的是_id,然而这些新增的数据,是保存之后才有_id的。

  1. exports.savePaper = function (req, res) {
  2. let paperForm = req.body.paperForm;
  3. let userName = req.session.userName;
  4. if(paperForm == {}){
  5. res.json({
  6. status:'5',
  7. msg: '数据不能为空'
  8. })
  9. }
  10. // 第一步查找当前登录的教师
  11. Teacher.findOne({"userName": userName}, (err,doc)=>{
  12. if (err) {
  13. ...
  14. } else {
  15. if (doc) {
  16. let paperData = {
  17. name:paperForm.name,
  18. totalPoints:paperForm.totalPoints,
  19. time:paperForm.time,
  20. _teacher: doc._id, // 这里就可以拿到教师的_id
  21. _questions: [],
  22. examnum:0
  23. }
  24. // 第二步创建试卷
  25. Paper.create(paperData,function (err1,doc1) {
  26. if (err1) {
  27. ...
  28. } else {
  29. if (doc1) {
  30. doc._papers.push(doc1._id); // 教师中添加该试卷的_id
  31. doc.save(); // 很重要 不save则没有数据
  32. // 第三步 创建问题
  33. paperForm._questions.forEach(item => {
  34. item._papers = [];
  35. item._papers.push(doc1._id); // 试卷中存入试卷的_id,因为此时已经创建了试卷,所以可以拿到_id
  36. item._teacher = doc._id; // 试卷中存入教师的_id
  37. })
  38. Question.create(paperForm._questions,function (err2,doc2) {
  39. if (err2) {
  40. ...
  41. } else {
  42. if (doc2) {
  43. doc2.forEach(item => {
  44. doc1._questions.push(item._id); // 当问题创建成功,则在试卷中存入问题的_id
  45. })
  46. doc1.save();
  47. res.json({
  48. status:'0',
  49. msg: 'success'
  50. })
  51. } else {
  52. ...
  53. }
  54. }
  55. })
  56. } else {
  57. ...
  58. }
  59. }
  60. })
  61. }
  62. else {
  63. ...
  64. }
  65. }
  66. })
  67. };
关联集合的删除---删除试卷

删除某一个试卷,既要删除教师中对应的试卷_id,也要删除问题中对应的试卷_id

  1. // 删除试卷
  2. exports.deletePaper = function (req, res) {
  3. let id = req.body.id;
  4. let userName = req.session.userName;
  5. // 第一步 删除教师中的_id _papers是一个数组,所以用到了`$pull`
  6. Teacher.update({"userName":userName},{'$pull':{'_papers':{$in:id}}}, (err,doc)=>{
  7. if (err) {
  8. res.json({
  9. status:'1',
  10. msg: err.message
  11. })
  12. } else {
  13. if (doc) {
  14. // 第二步 删除试卷 即 移除一个文档
  15. Paper.remove({"_id":{$in:id}},function (err1,doc1){
  16. if(err1) {
  17. res.json({
  18. status:'1',
  19. msg: err1.message
  20. })
  21. } else {
  22. if (doc1) {
  23. // 第三步 updateMany删除多个问题中的_id 这里并没有删除试卷中包含的问题,是为了以后题库做准备
  24. Question.updateMany({'_papers':{$in:id}},{'$pull':{'_papers':{$in:id}}},function (err2,doc2) {
  25. if(err2){
  26. ...
  27. } else {
  28. if (doc2){
  29. ...
  30. }
  31. }
  32. })
  33. } else {
  34. ...
  35. }
  36. }
  37. })
  38. } else {
  39. ...
  40. }
  41. }
  42. })
  43. };
关联集合多条数据的更新--修改试卷
  1. // 修改试卷-修改试卷
  2. exports.updatePaper = function (req,res) {
  3. let userName = req.session.userName;
  4. let params = req.body.params;
  5. let paperParams = { // 试卷需要更新的字段
  6. name: params.name,
  7. totalPoints: params.totalPoints,
  8. time: params.time
  9. }
  10. let updateQuestion = []; // 需要更新的题目
  11. let addQuestion = []; // 需要新增的题目
  12. params._questions.forEach(item => {
  13. if(item._id) { // 通过判断是否有_id区分已有的或者是新增的
  14. updateQuestion.push(item);
  15. } else {
  16. addQuestion.push(item);
  17. }
  18. })
  19. Teacher.findOne({'userName':userName},(err,doc)=>{
  20. if (err) {
  21. ...
  22. } else {
  23. if (doc) {
  24. Paper.findOneAndUpdate({"_id":params._id},paperParams,(err1,doc1) => {
  25. if(err1) {
  26. ...
  27. }else {
  28. if(doc1){
  29. updateQuestion.forEach((item,index)=>{ // 循环更新题目,好像很傻的方法,可能有更好的办法
  30. Question.update({"_id":item._id},item,(err2,doc2)=>{
  31. if(err2){
  32. res.json({
  33. status:'1',
  34. msg: err2.message
  35. })
  36. }else {
  37. if(doc2){
  38. if(index == (updateQuestion.length-1)){
  39. if (addQuestion.length>0){
  40. addQuestion.forEach(item => {
  41. item._papers = [];
  42. item._papers.push(doc1._id);
  43. item._teacher = doc._id;
  44. })
  45. // 创建新增题目
  46. Question.create(addQuestion,(err3,doc3) => {
  47. if(err3) {
  48. ...
  49. } else {
  50. if(doc3) {
  51. doc3.forEach(item => {
  52. doc1._questions.push(item._id); // 还要将新增的题目关联到试卷当中
  53. })
  54. doc1.save(); // 很重要 不save则没有数据
  55. res.json({
  56. status:'0',
  57. msg: 'success'
  58. })
  59. // .......................判断太长省略........................
  60. })
  61. };
更新子文档数组--阅卷打分
  1. // 打分提交
  2. exports.submitScore = function (req, res) {
  3. let name = req.param('userName'),
  4. date = req.param('date'),
  5. score = req.param('score') - 0,
  6. userName = req.session.userName;
  7. Teacher.findOne({'userName':userName},(err,doc) => {
  8. if(err) {
  9. ...
  10. } else {
  11. if(doc) {
  12. Student.update({"userName":name,"exams.date":date},{$set:{"exams.$.score":score,"exams.$.isSure":true}},(err1, doc1) => {
  13. if(err1) {
  14. ...
  15. } else {
  16. if(doc1) {
  17. ...
  18. } else {
  19. ...
  20. }
  21. }
  22. })
  23. } else {
  24. ...
  25. }
  26. }
  27. })
  28. };

md5加密

  1. //student.js
  2. const crypto = require('crypto');
  3. let mdHash = function(data){
  4. // hash 的定义要写在这个方法内,不然会报错Digest already called ****
  5. const hash = crypto.createHash('md5');
  6. return hash.update(data).digest('hex');
  7. }
  8. // 使用
  9. //注册
  10. exports.register = function (req,res) {
  11. let userInfo = req.body.userInfo;
  12. 获取到前台传过来的密码,先加密再存储
  13. userInfo.passWord = mdHash(userInfo.passWord);
  14. ...
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注