结对编程——简易在线考试系统
本次软件开发与创新课程设计,我与另一位同学(2452312)进行了在线考试系统的结对编程,在日常学习和教学场景中,一款能够随机出题、自动判分、具备防作弊功能的综合考试系统,能极大提升测试效率和公平性。基于此需求,我们进行了一个简易在线考试系统的开发:
讨论过程:首先我们对该系统的基本要求进行了讨论,即该系统需要满足什么要求(单选,多选的增删改查,题目做错后的解析,同时在答题结束后进行分数的评判,包含错题的报告),不仅于此,我们还考虑了其他的问题,如切出界面进行警告,超出一定次数后自动交卷,以及为了确保公平性,每个人所拿到的题目在难度相同的情况下,题目不一样,进行随机的抽取题目确保考试公平性,同时设计倒计时系统,时间结束自动交卷。为了便捷的能够使用,我们采取JAVA编程语言,结合HTML+CSS进行实现:包括:
(1)界面层(HTML+CSS):负责展示答题界面、进度条、倒计时、成绩报告,保证界面美观、简洁、易用;
(2)逻辑层(JavaScript):负责核心业务逻辑,包括随机抽题、选项选择、进度更新、倒计时、临时保存、切屏监测、自动判分、解析展示。
(3)数据库:因为题库的数据量较为庞大,使用纯前端显然不符合需求,因此我们使用了数据库建库,用于保存大量数据。



上图为出题界面展示
与此同时,我们还实现了以下目标,让用户的视觉体验效果更好
- 卡片式答题界面:添加阴影和圆角,提升视觉层次感;
- 选项选中高亮:选中的选项添加背景色和边框色,便于用户区分已选和未选;
- 进度条:实时展示答题进度,让用户清晰了解自己的答题情况;
- 错题解析:用不同颜色区分用户答案、正确答案和解析,重点突出错误点

上图为答题结束后的得分以及解析界面,设计为十道题百分制,用户答完题即可看到自己的得分情况以及错误点解析。

上图为重新答题的界面,用户点击重新答题后就会从题库中重新随机抽取十道题目进行重新出题,尽可能预防作弊


以上为数据库截图;

以上为数据库日志
项目优点:
(1) 界面美观,交互友好:采用卡片式设计,配色统一,按钮、进度条、高亮效果等细节处理到位,用户操作直观,答题体验流畅;
(2) 实现切屏监测,累计3次切屏自动交卷,同时支持倒计时自动交卷,保证测试的公平性。
(3)随机出题、切屏限制、临时保存、自动判分、题目解析等所有核心功能,能够满足综合测试、查漏补缺的需求
项目不足:
(1)虽然支持移动端显示,但在小屏设备上,题目选项可能出现排版错乱,交互体验有待优化
(2)用户提交试卷后,仅能查看本次成绩,无法记录历史成绩,不便于用户跟踪自己的学习进度
(3)相较于实际测试需求,题库规模仍可进一步扩大,增加题目多样性;
感想:经过本次双人协作编程,我意识到每个人的编程习惯是不同的,在双人协作编程时,即使已经提前制定了编程路线。实操下来的感觉还是不同,这让我对别人的编程思路有了进一步的了解,能够进行合作编程,对系统的 开发有了进一步的认识
2452312感想:通过本次作业,我深入学习了 Spring Boot 架构,了解了同伴在前端设计中的思路与实现方式,也反思并意识到了自身在编码规范、逻辑结构及代码习惯方面存在的一些不足。
具体相关代码展示如下:
语数英综合随机考试(大学难度)无注释完整版
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>语数英综合随机考试(大学难度)</title>
<style>
*{margin:0;padding:0;box-sizing:border-box;font-family:"Microsoft YaHei",sans-serif}
:root{--main:#409eff;--ok:#67c23a;--err:#f56c6c;--bg:#f5f7fa;--card:#fff;--text:#303133;--border:#e4e7ed}
body{background:var(--bg);color:var(--text);padding:20px}
.container{max-width:800px;margin:0 auto}
.header{text-align:center;margin:20px 0}
.header h2{background:linear-gradient(90deg,#409eff,#67c23a);-webkit-background-clip:text;color:transparent}
.timer{position:fixed;top:15px;right:15px;background:var(--err);color:#fff;padding:8px 14px;border-radius:20px;font-weight:bold}
.question{background:var(--card);padding:22px;border-radius:12px;margin-bottom:16px;box-shadow:0 2px 10px rgba(0,0,0,.07);border-left:4px solid var(--main)}
.question h3{margin-bottom:14px;font-size:16px}
.option{padding:10px 14px;border:1px solid var(--border);border-radius:8px;margin:8px 0;cursor:pointer}
.option:hover{border-color:var(--main)}
.option.selected{background:#e6f7ff;border-color:var(--main)}
.progress{background:var(--card);padding:15px;border-radius:10px;margin-bottom:20px}
.bar-box{height:7px;background:var(--border);border-radius:4px;margin-top:8px}
.bar{height:100%;background:var(--main);width:0%}
.btn{padding:10px 24px;background:var(--main);color:#fff;border:none;border-radius:8px;cursor:pointer;margin:5px}
.btn-group{text-align:center;margin:20px 0}
.report{background:var(--card);padding:25px;border-radius:12px;display:none}
.score{text-align:center;font-size:28px;font-weight:bold;margin:20px 0}
.wrong{border-left:4px solid var(--err);padding:16px;margin:16px 0;background:#fef0f0;border-radius:8px}
.analysis{margin-top:8px;padding:8px;background:#f6ffed;border-radius:6px;color:#389e0d}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h2>语数英综合随机考试(10题 · 满分100分)</h2>
</div>
<div class="progress">
答题进度:<span id="now">0</span>/10
<div class="bar-box"><div class="bar" id="bar"></div></div>
</div>
<div id="exam"></div>
<div class="btn-group">
<button class="btn" onclick="saveTemp()"> 临时保存</button>
<button class="btn" onclick="submitExam()">提交试卷</button>
</div>
<div class="report" id="report">
<h3 align="center">考试成绩报告</h3>
<div class="score">得分:<span id="score">0</span> / 100</div>
<div id="wrongList"></div>
<div class="btn-group">
<button class="btn" onclick="location.reload()">重新考试(新题)</button>
</div>
</div>
</div>
<div class="timer" id="timer">15:00</div>
<script>
const questionBank = [
{title:"“落霞与孤鹜齐飞,秋水共长天一色”出自?",options:["王勃《滕王阁序》","范仲淹《岳阳楼记》","王羲之《兰亭集序》","苏轼《赤壁赋》"],ans:"A",analysis:"出自王勃《滕王阁序》,千古写景名句"},
{title:"《诗经》的三大表现手法是?",options:["赋、比、兴","风、雅、颂","诗、词、曲","礼、义、廉、耻"],ans:"A",analysis:"赋(直陈)、比(比喻)、兴(起兴)"},
{title:"“出师未捷身先死,长使英雄泪满襟”咏的是?",options:["诸葛亮","刘备","曹操","周瑜"],ans:"A",analysis:"杜甫《蜀相》,怀念诸葛亮"},
{title:"“先天下之忧而忧,后天下之乐而乐”出自?",options:["《岳阳楼记》","《醉翁亭记》","《桃花源记》","《小石潭记》"],ans:"A",analysis:"范仲淹《岳阳楼记》名句"},
{title:"《红楼梦》作者是?",options:["曹雪芹","吴承恩","罗贯中","施耐庵"],ans:"A",analysis:"清代曹雪芹著《红楼梦》"},
{title:"“长风破浪会有时,直挂云帆济沧海”出自?",options:["李白","杜甫","白居易","王维"],ans:"A",analysis:"李白《行路难》"},
{title:"下列不属于“唐宋八大家”的是?",options:["李白","韩愈","柳宗元","欧阳修"],ans:"A",analysis:"唐宋八大家:韩柳欧王曾三苏"},
{title:"“己所不欲,勿施于人”出自?",options:["《论语》","《孟子》","《大学》","《中庸》"],ans:"A",analysis:"儒家经典《论语》"},
{title:"lim(x→0) sinx / x =?",options:["1","0","∞","-1"],ans:"A",analysis:"第一重要极限,结果为1"},
{title:"lim(x→∞)(1+1/x)^x =?",options:["e","1","0","∞"],ans:"A",analysis:"自然常数e的定义极限"},
{title:"f(x)=x³ 的导数 f’(x)=?",options:["3x²","x²","3x","2x²"],ans:"A",analysis:"幂函数求导:(xⁿ)’=nxⁿ⁻¹"},
{title:"f(x)=lnx 的导数 f’(x)=?",options:["1/x","x","-1/x","eˣ"],ans:"A",analysis:"(lnx)’=1/x"},
{title:"∫cosx dx =?",options:["sinx+C","-sinx+C","cosx+C","-cosx+C"],ans:"A",analysis:"cosx积分是sinx"},
{title:"∫1/x dx =?",options:["ln|x|+C","x+C","1+C","eˣ+C"],ans:"A",analysis:"基本积分公式"},
{title:"行列式 |1 0; 0 1| =?",options:["1","0","-1","2"],ans:"A",analysis:"单位行列式值为1"},
{title:"行列式 |2 3; 1 4| =?",options:["5","11","8","3"],ans:"A",analysis:"2×4 - 3×1 = 8-3=5"},
{title:"函数 y=eˣ 的导数是?",options:["eˣ","1","0","xeˣ⁻¹"],ans:"A",analysis:"eˣ导数不变"},
{title:"∫eˣ dx =?",options:["eˣ+C","xeˣ+C","1+C","eˣ/x+C"],ans:"A",analysis:"eˣ积分不变"},
{title:"lim(x→0) (1-cosx)/x² =?",options:["1/2","1","0","∞"],ans:"A",analysis:"等价无穷小:1-cosx ~ x²/2"},
{title:"若 f’(x)=0,则x是f(x)的?",options:["驻点","极值点","最大值点","最小值点"],ans:"A",analysis:"导数为0的点称为驻点"},
{title:"The synonym of “substantial” is?",options:["large","fake","illegal","tiny"],ans:"A",analysis:"substantial 大量的,重大的"},
{title:"The synonym of “persistent” is?",options:["continuous","weak","temporary","passive"],ans:"A",analysis:"persistent 持续的、坚持不懈的"},
{title:"Neither he nor I ______ to go.",options:["am","is","are","be"],ans:"A",analysis:"就近一致原则,靠近I用am"},
{title:"It is important that she ______ on time.",options:["come","comes","came","will come"],ans:"A",analysis:"虚拟语气 (should) + 动词原形"},
{title:"Only by hard work ______ your goal.",options:["can you achieve","you can achieve","you achieve","you will achieve"],ans:"A",analysis:"Only开头状语,句子部分倒装"},
{title:"I regret ______ him the truth.",options:["telling","to tell","tell","told"],ans:"A",analysis:"regret doing 后悔做过某事"},
{title:"The new policy will ______ development.",options:["accelerate","assess","arise","await"],ans:"A",analysis:"accelerate 加速"},
{title:"He was denied ______ to the building.",options:["access","approach","avenue","angle"],ans:"A",analysis:"access to 使用权、进入权"},
{title:"______, he would have passed the exam.",options:["Had he worked","If he worked","He worked","If he works"],ans:"A",analysis:"虚拟语气倒装,Had he worked = If he had worked"},
{title:"She is accustomed ______ up early.",options:["to getting","to get","get","getting"],ans:"A",analysis:"be accustomed to doing 习惯于"},
{title:"The opposite of “optimistic” is?",options:["pessimistic","energetic","calm","generous"],ans:"A",analysis:"optimistic 乐观的;pessimistic 悲观的"},
{title:"I have no option ______ to refuse.",options:["but","except","besides","than"],ans:"A",analysis:"have no option but to do 别无选择"}
];
function randPick(arr, n) {
let t = [...arr].sort(() => Math.random() - 0.5);
return t.slice(0, n);
}
const questions = randPick(questionBank, 10);
let userAns = JSON.parse(localStorage.getItem("examAns")) || Array(10).fill("");
const exam = document.getElementById("exam");
let leaveCount = 0;
const MAX_LEAVE = 3;
questions.forEach((q, i) => {
let h = `<div class="question"><h3>${i+1}. ${q.title}</h3>`;
q.options.forEach((opt, j) => {
const c = String.fromCharCode(65 + j);
h += `<div class="option" onclick="sel(${i},'${c}',this)" ${userAns[i]==c ? 'class="option selected"' : ''}>${c}. ${opt}</div>`;
});
h += `</div>`;
exam.innerHTML += h;
});
function sel(idx, c, el) {
const opts = el.parentElement.querySelectorAll(".option");
opts.forEach(o => o.classList.remove("selected"));
el.classList.add("selected");
userAns[idx] = c;
updateProgress();
}
function updateProgress() {
let cnt = userAns.filter(x => x).length;
document.getElementById("now").innerText = cnt;
document.getElementById("bar").style.width = cnt * 10 + "%";
}
function saveTemp() {
localStorage.setItem("examAns", JSON.stringify(userAns));
alert("答案已临时保存!刷新/关闭再打开可恢复");
}
let sec = 15 * 60;
let timer = setInterval(() => {
sec--;
if(sec <= 0) {
alert("时间到,系统自动交卷!");
submitExam();
}
const m = String(Math.floor(sec/60)).padStart(2,'0');
const s = String(sec%60).padStart(2,'0');
document.getElementById("timer").innerText = `${m}:${s}`;
}, 1000);
document.addEventListener("visibilitychange", () => {
if(document.hidden) {
leaveCount++;
alert(`你已离开考试页面 ${leaveCount}/${MAX_LEAVE} 次!`);
if(leaveCount >= MAX_LEAVE) {
alert("离开次数超限,系统强制自动交卷!");
submitExam();
}
}
});
function submitExam() {
clearInterval(timer);
localStorage.removeItem("examAns");
let score = 0;
let wrong = "";
questions.forEach((q, i) => {
const ua = userAns[i] || "";
if(ua === q.ans) score += 10;
else {
wrong += `<div class="wrong"><p><strong>题目:</strong>${q.title}</p><p>你的答案:<span style="color:red">${ua||"未答"}</span></p><p>正确答案:<span style="color:green">${q.ans}</span></p><p class="analysis"><strong>解析:</strong>${q.analysis}</p></div>`;
}
});
document.getElementById("exam").style.display = "none";
document.getElementById("report").style.display = "block";
document.getElementById("score").innerText = score;
document.getElementById("wrongList").innerHTML = wrong || "<p align='center'>全部正确!太棒了!</p>";
}
updateProgress();
</script>
</body>
</html>
以上为源代码展示

浙公网安备 33010602011771号