序言
一个在线教育平台,习题上传功能是不可缺少的。这里基于Nodejs的Express框架,接入MongoDB数据库,实现习题的创建,作答和统计,并使用Echart展示数据。
前期设计
流程设计
一个简单的习题功能,包括了教师的习题创建、学生的习题作答和教师的习题统计数据查看的三部分功能。
页面设计
教师课程页面的习题创建和查看入口
教师创建新习题页面,包括选择题和填空题
教师习题数据统计页面,使用柱状图进行数据的展示,并且可以查看具体的学生名单
学生的习题作答界面
前端脚本
习题上传脚本
主要需要动态修改题目数量、填入题目内容与答案、将填入好的习题数据发送至服务端的功能
$(document).ready(() => { // 添加选择题 $("#addChoice").click(function() { // 点击增加选择题 $("#choice").append('\ // 选择题的具体HTML代码 '); // jQ动态创建的DOM元素,需要通过父元素指定来绑定事件 $("#choice").on("click", ".delete", function() { $(this).parent().remove(); }); }); }); $(document).ready(() => { $("#addCompletion").click(function() { // 点击增加填空题 $("#completion").append('\ // 填空题的具体HTML码 ') }) // 同样绑定移除填空题的事件 $("#completion").on("click", ".delete", function() { $(this).parent().remove(); }); }) $(document).ready(() => { var choice = []; var completion = []; $("#submit").click(() => { $("#choice >li").each(function() { // 利用jQ选择题,获得填入的选择题信息 }); $("#completion >li").each(function() { // 利用jQ选择题,获得填入的填空题信息 }); // 将习题数据Post发出 $.post("process_upload", {test: JSON.stringify(test)}, (data) => { // ... }, "json"); }) })
习题作答脚本
只需要利用jQ Ajax功能获得题目并展示,以及将作答数据Post回服务端
$(document).ready(() => { $.get("/tests?id=" + id, (data) => { // 获得JSON格式的试题数据 data.choice.forEach((item, index, array) => { $("#choice").append('\ // 选择题的具体HTML代码 ') }); data.completion.forEach((item, index, array) => { $("#completion").append('\ // 填空题的具体HTML代码 ') }); }, "json"); }); $(document).ready(() => { $("#submit").click(() => { // 利用jQ获得作答的数据,并且Post给服务端 }); });
习题数据查看脚本
$(document).ready(() => { $.get("/tests?id=" + id, (data) => { // 同样获得习题数据并展示 }, "json"); }); // 绘图 function plant(choiceArr, completionArr) { choiceArr.forEach((element, index, array) => { // 对每道选择题数据进行绘图 }); completionArr.forEach((element, index, array) => { // 对每道填空题数据进行绘图 }); }
后端处理
习题数据
每一份习题在数据库中存储的形式:
_id | classname | name | choice | completion |
习题的唯一id(自动生成) | 习题发布的班级 | 习题名 | 所有选择题(数列) | 所有填空题(数列) |
每一道选择题的存储形式:
stem | A | B | C | D | answer | answers |
题干 | A选项内容 | B选项内容 | C选项内容 | D选项内容 | 标准答案 | 选择四个选项的学生名单 |
每一道填空题的存储形式:
stem | answer | answers |
题干 | 标准答案 | 回答正确学生名单、回答错误学生名单以及错误答案 |
路由配置
添加了一个tests.js路由文件,用来处理与试题相关的请求。
由于试题数据需要登录才能查看,并且需要分身份发送不同的内容,
几乎所有的响应都包含对是否已在session中登录和对身份是否符合的判断(在以下代码示例中略),
如果不符合,跳转至登录界面或者403界面。
// 直接返回试题原数据 router.get('/', function(req, res) { db.find("test", {"_id": ObjectId(req.query.id)}, (data) => { // 调用自己编写的调用数据库的函数,获得试题信息并返回 // 如果身份是学生,只截取 }); }) // 试题查看页面 router.get('/paper', function(req, res) { db.find("test", {"_id": ObjectId(req.query.id)}, (data) => { // 判断习题是否存在 // 习题若存在,如果session的identity是教师,发送教师的页面;如果是学生发送学生的页面 }); }) // 试题页面 router.get('/add', function(req, res) { // 如果session的identity是教师,发送创建试题页面 }) // 上传新试题 router.post('/process_upload', function(req, res) { // 将获得的新习题数据存入数据库 }) // 提交完成的试题 router.post('/process_submit', function(req, res) { // 从数据库中取出相应的习题数据,将学生的作答插入数据中,再更新数据库 }) // 读取习题列表 router.get('/list', function(req, res) { // 获得所有习题的列表 })
结语
这样便完成了一个基础的习题上传功能,在接下来还需要做功能的添加和优化,例如
- 增加题型
- 设置试题允许作答的起止时间、得分分布等
- 增加教师对学生作答内容的反馈功能
- 增加教师对习题的删除、锁定、修改等等动能
- 增加学生对习题的结果回查、排名查看
- 在从数据库取原作答情况,和更新新作答情况之间给数据库加Pessimistic Lock,以免学生同时提交作答导致数据丢失
- 模块化从试题数据库取试题所有数据、仅取题目、仅取学生作答情况和得分、仅取某一个学生的所有作答等功能
- 以及其他功能和优化