基于springboot+vue实现的微在线考试系统

基于springboot+vue实现的微在线考试系统 1,项目简介 1,1 开发目的 面向组织,公司,高校的一款通用在线考试系统,节约人力财力,轻松完成在线考核

本文包含相关资料包-----> 点击直达获取<-------

基于springboot+vue实现的微在线考试系统

1.项目简介

1.1 开发目的

面向组织、公司、高校的一款通用在线考试系统,节约人力财力,轻松完成在线考核。系统拥有自行添加题库,考试随机抽题,线上监考,自动阅卷评分,实现了无纸化考试,自动化阅卷等功能。

1.2 涉及技术

  • 基本平台 :JDK1.8+MySql8.0

  • 前端 :Vue2+Axios+ElementUI+echart

  • 后端 :SpringBoot+MyBatis+SpringSecurity+easyPoi+Jwt

  • 基于RBAC权限模型 (用户、角色、权限)

1.3 项目架构

  • bin :完整项目成品(一键部署)

  • image :项目运行截图

  • sql :数据库文件

  • voes-admin :Springboot核心模块

  • voes-common :公共模块

  • voes-examination :考试模块

  • voes-framework :框架模块

  • voes-system :系统模块

  • voes-ui :前端界面

1.4 部署步骤

源码安装

  • 将sql目录下的sql文件导入mysql数据库

  • 将项目导入IDEA开发工具,执行voes-admin模块目录下的main方法,启动SpringBoot服务

  • 进入voes-ui目录下执行 npm run dev 即可启动前端UI服务

一键安装

  • 将sql目录下的sql文件导入mysql数据库

  • 将bin目录下的文件移动到你的服务器上,点击start.bat即可启动服务

2.数据库设计

2.1 表结构

用户表

image-20211007144417346

角色表

image-20211007144434952

用户-角色表

image-20211007144502640

成绩表

image-20211007144515211

科目表

image-20211007144530451

权限表

image-20211007144547062

题目关键字表

image-20211007144615001

答案表

image-20211007144630090

题目解析表

image-20211007144649641

考试表

image-20211007144704512

字典表

image-20211007144723511

考场信息表

image-20211007144746251

2.2 E-R图

Diagram 1

3.系统设计

项目模块结构图

image-20211007150118211

  • 考生模块
  • 在线考试:考生在规定时间里选择考试科目,进行考试
  • 历史考试:考生可以查看历史考试的信息信息
  • 修改密码:考生可以修改自己的登录密码
  • 账户信息:考生可以修改的真实姓名和手机号码

  • 教师模块

  • 系统设置: 教师可以进行菜单管理添加、编辑、删除,角色管理添加、编辑、删除,修改密码
  • 用户管理:教师可以进行用户列表的添加、编辑、删除
  • 系统日志:教师可以进行日志列表的添加、删除
  • 学科管理: 教师可以进行学科列表的添加、编辑、删除
  • 考生管理:教师可以进行考生列表的添加、编辑、删除
  • 试题管理:教师可以进行试题列表的添加、编辑、删除,批量导入试题
  • 考试管理:教师可以进行考试列表的添加、编辑、删除
  • 试卷管理:教师可以进行试卷列表的编辑、删除
  • 答题管理:教师可以进行学生答题情况的查询
  • 成绩统计:教师可以进行查看考试的成绩统计图表

分布式架构

image-20211007150338770

项目结构图

image-20211007144956190

3.1 业务代码

功能模块

image-20211007150302199

```java /* * 通过ID查询单条数据 * * @param id 主键 * @return 实例对象 / @Override public QuestionBank queryById(Integer id, boolean answer) { Integer type = questionBankMapper.queryTypeById(id); //单选题 if (QuestionType.SINGLE_CHOICE.equals(type)) { log.info("单选题"); QuestionBank questionBank = questionBankMapper.queryByIdWithOption(id); QuestionOption questionOption = questionBank.getQuestionOption(); if (!answer) { questionOption.setRightKey(null); questionOption.setExplain(null); } return questionBank; } //多选题 if (QuestionType.MULTIPLE_CHOICE.equals(type)) { log.info("多选题"); QuestionBank questionBank = questionBankMapper.queryByIdWithOption(id); QuestionOption questionOption = questionBank.getQuestionOption(); if (!answer) { questionOption.setRightKey(null); questionOption.setExplain(null); } return questionBank;

    }
    //填空题
    if (QuestionType.FILL_IN_THE_BLANKS.equals(type)) {
        log.info("填空题");
        QuestionBank questionBank = questionBankMapper.queryByIdWithFill(id);

        QuestionFillBlanks questionFillBlank = questionBank.getQuestionFillBlank();
        if (!answer) {
            questionFillBlank.setAnswer(null);
        }
        return questionBank;
    }
    //判断题
    if (QuestionType.JUDGE.equals(type)) {
        log.info("判断题");
        QuestionBank questionBank = questionBankMapper.queryByIdWithJudgment(id);

        if (!answer) {
            QuestionJudgment questionJudgment = questionBank.getQuestionJudgment();
            questionJudgment.setRightKey(null);
            questionJudgment.setExplain(null);
        }

        return questionBank;
    }
    //主观题目
    //与填空题保持一致
    if (QuestionType.SUBJECTIVE.equals(type)) {
        log.info("主观题");
        QuestionBank questionBank = questionBankMapper.queryByIdWithFill(id);
        QuestionFillBlanks questionFillBlank = questionBank.getQuestionFillBlank();
        if (!answer) {
            questionFillBlank.setAnswer(null);
        }
        return questionBank;
    }

    return null;
}

@Override
public QuestionBank queryById(Integer id) {
    return queryById(id, true);
}


/**
 * 查询多条数据
 *
 * @param offset 查询起始位置
 * @param limit  查询条数
 * @return 对象列表
 */
@Override
public List<QuestionBank> queryAllByLimit(int offset, int limit) {
    return this.questionBankMapper.queryAllByLimit(offset, limit);
}

/**
 * 查询多条数据
 *
 * @return 对象列表
 */
@Override
public PageInfo<QuestionBank> queryAll(QuestionBank questionBank, int currentPage, int pageSize) {
    PageHelper.startPage(currentPage, pageSize);
    List<QuestionBank> questionBanks = questionBankMapper.queryAll(questionBank);
    return new PageInfo<QuestionBank>(questionBanks);
}

/**
 * 新增数据
 *
 * @param questionBank 实例对象
 * @return 实例对象
 */
@Override
public QuestionBank insert(QuestionBank questionBank) {
    Integer type = questionBank.getType();
    //插入题库
    questionBankMapper.insert(questionBank);
    Integer primary = questionBank.getId();

    //插入答案

    //单选题
    if (QuestionType.SINGLE_CHOICE.equals(type)) {
        QuestionOption questionOption = questionBank.getQuestionOption();
        questionOption.setQuestionBankId(primary);
        questionOptionMapper.insert(questionOption);
    }
    //多选题
    if (QuestionType.MULTIPLE_CHOICE.equals(type)) {
        QuestionOption questionOption = questionBank.getQuestionOption();
        questionOption.setQuestionBankId(primary);
        questionOptionMapper.insert(questionOption);
    }
    //填空题
    if (QuestionType.FILL_IN_THE_BLANKS.equals(type)) {
        QuestionFillBlanks questionFillBlank = questionBank.getQuestionFillBlank();
        questionFillBlank.setQuestionBankId(primary);
        questionFillBlanksMapper.insert(questionFillBlank);
    }
    //判断题
    if (QuestionType.JUDGE.equals(type)) {
        QuestionJudgment questionJudgment = questionBank.getQuestionJudgment();
        questionJudgment.setQuestionBankId(primary);
        questionJudgmentMapper.insert(questionJudgment);
    }
    //主观题
    //等于填空题
    if (QuestionType.SUBJECTIVE.equals(type)) {
        QuestionFillBlanks questionFillBlank = questionBank.getQuestionFillBlank();
        questionFillBlank.setQuestionBankId(primary);
        questionFillBlanksMapper.insert(questionFillBlank);
    }
    return questionBank;
}

/**
 * 修改数据
 *
 * @param questionBank 实例对象
 * @return 实例对象
 */
@Override
public QuestionBank update(QuestionBank questionBank) {
    //题编号
    Integer id = questionBank.getId();
    //题型
    Integer type = questionBank.getType();
    //单选题
    if (QuestionType.SINGLE_CHOICE.equals(type)) {
        QuestionOption questionOption = questionBank.getQuestionOption();
        questionOption.setQuestionBankId(id);
        questionOptionMapper.update(questionOption);
    }
    //多选题
    if (QuestionType.MULTIPLE_CHOICE.equals(type)) {
        QuestionOption questionOption = questionBank.getQuestionOption();
        questionOption.setQuestionBankId(id);
        questionOptionMapper.update(questionOption);
    }
    //填空题
    if (QuestionType.FILL_IN_THE_BLANKS.equals(type)) {
        QuestionFillBlanks questionFillBlank = questionBank.getQuestionFillBlank();
        questionFillBlank.setQuestionBankId(id);
        questionFillBlanksMapper.update(questionFillBlank);
    }
    //判断题
    if (QuestionType.JUDGE.equals(type)) {
        QuestionJudgment questionJudgment = questionBank.getQuestionJudgment();
        questionJudgment.setQuestionBankId(id);
        questionJudgmentMapper.update(questionJudgment);
    }
    //主观题
    //等于填空题
    if (QuestionType.SUBJECTIVE.equals(type)) {
        QuestionFillBlanks questionFillBlank = questionBank.getQuestionFillBlank();
        questionFillBlank.setQuestionBankId(id);
        questionFillBlanksMapper.update(questionFillBlank);
    }
    questionBankMapper.update(questionBank);
    return this.queryById(questionBank.getId());
}

/**
 * 通过主键删除数据
 *
 * @param id 主键
 * @return 是否成功
 */
@Override
public boolean deleteById(Integer id) {
    //删除答卷上面的题
    testPaperMapper.deleteByQuestionBankId(id);
    scoreMapper.deleteByQuestionBankId(id);
    //先删除答案
    //再删除题
    QuestionBank questionBank = questionBankMapper.queryById(id);
    Integer type = questionBank.getType();
    //单选题
    if (QuestionType.SINGLE_CHOICE.equals(type)) {
        questionOptionMapper.deleteByQuestionBankId(id);
    }
    //多选题
    if (QuestionType.MULTIPLE_CHOICE.equals(type)) {
        questionOptionMapper.deleteByQuestionBankId(id);
    }
    //填空题
    if (QuestionType.FILL_IN_THE_BLANKS.equals(type)) {
        questionFillBlanksMapper.deleteByQuestionBankId(id);
    }
    //判断题
    if (QuestionType.JUDGE.equals(type)) {
        questionJudgmentMapper.deleteByQuestionBankId(id);
    }
    //主观题
    if (QuestionType.SUBJECTIVE.equals(type)) {
        questionFillBlanksMapper.deleteByQuestionBankId(id);
    }
    return questionBankMapper.deleteById(id) > 0;
}

@Override
public boolean deleteByIds(List<Integer> ids) {
    if (ids != null && ids.size() > 0) {
        ids.forEach(id -> {
            boolean b = this.deleteById(id);
        });
    }
    return true;
}

```

3.2 控制层代码

```java @Autowired QuestionBankService questionBankService;

@RequestMapping("/questionBanks") public AjaxResult list( QuestionBank questionBank, @RequestParam(defaultValue = "1") int currentPage, @RequestParam(defaultValue = "10") int pageSize) { PageInfo questionBankPageInfo = questionBankService.queryAll(questionBank, currentPage, pageSize); return AjaxResult.success(questionBankPageInfo); }

@RequestMapping("/get/{id}") public AjaxResult list(@PathVariable int id) { QuestionBank questionBank = questionBankService.queryById(id); return AjaxResult.success(questionBank); }

// 下面的记得添加权限 @RequestMapping("/add") public AjaxResult add(@Valid @RequestBody QuestionBank questionBank) { QuestionBank insert = questionBankService.insert(questionBank); return AjaxResult.success(insert); }

@RequestMapping("/update") public AjaxResult update(@Valid @RequestBody QuestionBank questionBank) { QuestionBank insert = questionBankService.update(questionBank); return AjaxResult.success(insert); }

@RequestMapping("/delete") public AjaxResult delete(Integer id) { boolean flag = questionBankService.deleteById(id); return AjaxResult.success(flag); }

@RequestMapping("/deletes") public AjaxResult deletes(IdsVo idsVo) { List ids = idsVo.getIds(); questionBankService.deleteByIds(ids); return AjaxResult.success(); }

@RequestMapping("/searchCount") public AjaxResult searchCount(String keyWord) { Map map = questionBankService.searchCount(keyWord); return AjaxResult.success(map); } ```

3.3 前端代码

```html

          <el-form-item label="考场">
            <el-input
                placeholder="请输入考试位置"
                v-model="searchData.examLocation"
                size="small"
                clearable>
            </el-input>
          </el-form-item>
          <el-form-item label="考试状态">
            <el-select v-model="searchData.examState"
                  placeholder="请选择考试状态"
                  clearable
                  size="small"
            >
              <el-option
                  v-for="item in examState"
                  :key="item.valueId"
                  :label="item.valueName"
                  :value="item.valueId">
              </el-option>
            </el-select>
          </el-form-item>
          <el-form-item>
            <el-button icon="el-icon-search" size="mini" type="primary" @click="onSearch">搜索</el-button>
            <el-button icon="el-icon-refresh" size="mini" @click="onReset">重置</el-button>
          </el-form-item>
        </el-form>
      </div>
    </el-col>
  </el-row>
  <!--工具栏-->
  <el-row>
    <el-col :span="24">
      <div >
        <el-button @click="handleAddBtn" size="mini" icon="el-icon-plus" type="primary">新增</el-button>
        <el-button :disabled="editBtnDisable" size="mini" icon="el-icon-edit"
              @click="handleEditBtn(null,{id:selected[0]})"
              type="success">修改
        </el-button>
        <el-button :disabled="deleteBtnDisable" size="mini" icon="el-icon-delete"
              @click="handleDeleteByIds(selected)"
              type="danger">删除
        </el-button>
        <el-button :loading="exportBtnLoading" :disabled="exportBtnDisable" size="mini"
              icon="el-icon-download" type="warning" @click="exportRole">导出
        </el-button>
        <el-button :disabled="false" size="mini" icon="el-icon-refresh" @click="refresh">刷新
        </el-button>
      </div>
    </el-col>
  </el-row>
  <!--表格-->
  <el-row>
    <el-col :span="24">
      <div >
        <el-table
            :data="tableData"
            row-key="id"
            :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
            @selection-change="selectionChange"
            stripe
            border
            >
          <el-table-column
              type="selection"
              align="center"
              width="50">
          </el-table-column>

          <el-table-column
              label="展开"
              align="center"
              type="expand"
              width="50">
            <template #default="props">
              <el-form label-position="left" label-width="100px" inline >
                <el-form-item label="考试编号:">
                  <span>{{ props.row.id }}</span>
                </el-form-item>
                <el-form-item label="考试名称:">
                  <span>{{ props.row.examName }}</span>
                </el-form-item>
                <el-form-item label="考试科目:">
              <span
                  v-for="item in examSubject"
                  :key="item.id"
                  :label="item.subject"
                  :value="item.id"
                  v-if="props.row.examSubject===item.id">
                {{item.subject}}
              </span>
                </el-form-item>
                <el-form-item label="出卷人:">
                  <span>{{ props.row.examProviderUser.name }}</span>
                </el-form-item>
                <el-form-item label="考试时间:">
                  <span>
                    {{ props.row.examBeginTime }}
                    至
                    {{ props.row.examOverTime }}
                  </span>
                </el-form-item>
                <el-form-item label="考试时长:">
                  <span>
                    {{(new Date(props.row.examOverTime).getTime()-new Date(props.row.examBeginTime).getTime())/60000+'分钟' }}
                  </span>
                </el-form-item>
                <el-form-item label="考试地点:">
                  <span>{{ props.row.examLocation }}</span>
                </el-form-item>
                <el-form-item label="考试状态:">
                  <span
                      v-for="item in examState"
                      :key="item.valueId"
                      :label="item.valueName"
                      :value="item.valueId"
                      v-if="props.row.examState===item.valueId">
                {{item.valueName}}
                </span>
                </el-form-item>
                <el-form-item label="考生数:">
                  <span>{{ props.row.examJoinNum }}</span>
                </el-form-item>
              </el-form>
            </template>
          </el-table-column>

          <el-table-column
              prop="examName"
              align="left"
              label="考试名称">
          </el-table-column>
          <el-table-column
              align="left"
              label="考试科目">
            <template slot-scope="scope">
              <span
                  v-for="item in examSubject"
                  :key="item.id"
                  :label="item.subject"
                  :value="item.id"
                  v-if="scope.row.examSubject===item.id">
                {{item.subject}}
              </span>
            </template>
          </el-table-column>
          <el-table-column
              prop="examProviderUser.name"
              align="left"
              label="出卷人">
          </el-table-column>
          <el-table-column
              prop="examBeginTime"
              align="left"
              label="考试时间">
          </el-table-column>
          <el-table-column label="考试状态" width="100" align="center">
            <template slot-scope="scope">
               <span
                   v-for="item in examState"
                   :key="item.valueId"
                   :label="item.valueName"
                   :value="item.valueId"
                   v-if="scope.row.examState===item.valueId">
                  <el-tag v-if="item.valueId===1">{{item.valueName}}</el-tag>
                  <el-tag v-if="item.valueId===2" type="info">{{item.valueName}}</el-tag>
                  <el-tag v-if="item.valueId===3" type="danger">{{item.valueName}}</el-tag>
                  <el-tag v-if="item.valueId===4" type="warning">{{item.valueName}}</el-tag>
                  <el-tag v-if="item.valueId===5" type="success">{{item.valueName}}</el-tag>
                </span>
            </template>
          </el-table-column>

          <el-table-column
              prop="examJoinNum"
              width="100"
              align="center"
              label="考生数">
          </el-table-column>
          <el-table-column label="操作" align="center">
            <template slot-scope="scope">
              <el-button
                  size="mini"
                  type="text"
                  icon="el-icon-edit"
                  @click="handleEditBtn(scope.$index, scope.row)">编辑
              </el-button>
              <el-button
                  size="mini"
                  type="text"
                  icon="el-icon-delete"
                  @click="handleDeleteBtn(scope.$index, scope.row)">删除
              </el-button>
              &nbsp;
              <el-dropdown size="mini">
                <el-button
                    size="mini"
                    type="text"
                >更多<i ></i>
                </el-button>
                <el-dropdown-menu size="medium" slot="dropdown">
                  <el-dropdown-item @click.native="randomQuestions(scope.$index, scope.row)">
                    <i ></i>随机抽题
                  </el-dropdown-item>
                  <el-dropdown-item @click.native="handleGetTestPaper(scope.$index, scope.row)">
                    <i ></i>查看试卷
                  </el-dropdown-item>
                  <el-dropdown-item @click.native="handleInvigilate(scope.$index, scope.row)">
                    <i ></i>智慧监考
                  </el-dropdown-item>
                  <el-dropdown-item @click.native="markingExamPapers(scope.$index, scope.row)">
                    <i ></i>考试阅卷
                  </el-dropdown-item>
                  <el-dropdown-item @click.native="announceResults(scope.$index, scope.row)">
                    <i ></i>公布成绩
                  </el-dropdown-item>
                </el-dropdown-menu>
              </el-dropdown>
            </template>
          </el-table-column>
        </el-table>
        <div >
          <el-pagination
              background
              @size-change="handleSizeChange"
              @current-change="handleCurrentChange"
              :current-page="currentPage"
              :page-sizes="[10,20,50,100, 200, 300, 400]"
              :page-size="pageSize"
              layout="total, sizes, prev, pager, next, jumper"
              :total="total">
          </el-pagination>
        </div>
      </div>
    </el-col>
  </el-row>

  <el-dialog
      :close-on-click-modal="false"
      title="新增"
      width="600px"
      :visible.sync="addDialogVisible">
    <el-form :inline="false" :model="add"
         label-position="right"
         label-width="90px">
      <el-form-item label="考试名称">
        <el-input
            placeholder="请输入考试名称"
            v-model="add.examName"
            size="small"
            clearable>
        </el-input>
      </el-form-item>
      <el-form-item label="考试科目">
        <el-select v-model="add.examSubject"
              placeholder="请选择考试科目"
              size="small"
        >
          <el-option
              v-for="item in examSubject"
              :key="item.id"
              :label="item.subject"
              :value="item.id">
          </el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="考试时间">
        <el-date-picker
            @change="timeChange"
            v-model="time"
            size="small"
            type="datetimerange"
            range-separator="至"
            start-placeholder="开始时间"
            end-placeholder="结束时间">
        </el-date-picker>
      </el-form-item>
      <el-form-item label="考试地点">
        <el-input
            placeholder="请输入考试地点"
            v-model="add.examLocation"
            size="small"
            clearable>
        </el-input>
      </el-form-item>

    </el-form>

    <span slot="footer" >
<el-button @click="addDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="handleAdd">确 定</el-button>

<el-date-picker @change="timeChange" v-model="time" size="small" type="datetimerange" range-separator="至" start-pl

参考文献

  • 基于分布式爬虫的在线考试系统设计与实现(厦门大学·李捷)
  • 基于Web的在线考试系统的设计与实现(电子科技大学·廖欧)
  • 基于微服务的在线教育系统的设计与实现(华中科技大学·毛颖志)
  • 面向远程实验的在线智能考试系统研究和开发(北京邮电大学·龚潘晶)
  • 基于微服务的在线教育系统的设计与实现(华中科技大学·毛颖志)
  • 面向远程实验的在线智能考试系统研究和开发(北京邮电大学·龚潘晶)
  • 基于微服务的在线教育系统的设计与实现(华中科技大学·毛颖志)
  • 基于Java EE架构的在线考试系统设计与实现(西安电子科技大学·龚尚映)
  • 基于J2EE的高职院校在线考试系统(内蒙古大学·赵源)
  • 基于ASP.NET的在线考试系统设计与实现(吉林大学·吴树德)
  • 基于微服务的在线教育系统的设计与实现(华中科技大学·毛颖志)
  • 基于微服务的在线教育系统的设计与实现(华中科技大学·毛颖志)
  • 基于微服务的在线教育系统的设计与实现(华中科技大学·毛颖志)
  • 基于Web的在线考试系统的设计与实现(南昌大学·胡显春)
  • 基于J2EE的高职院校在线考试系统(内蒙古大学·赵源)

本文内容包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主题。发布者:毕设工坊 ,原文地址:https://m.bishedaima.com/yuanma/35574.html

相关推荐

发表回复

登录后才能评论