-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
423 lines (356 loc) · 15.5 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reciter!</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
min-height: 100vh;
}
button {
margin: 5px;
}
#message {
display: none;
color: green;
}
.correct {
background-color: #d4edda;
color: #155724;
}
.incorrect {
background-color: #f8d7da;
color: #721c24;
}
#progress-container {
width: 100%;
background-color: #e0e0e0;
border-radius: 25px;
margin-top: 10px;
margin-bottom: 10px;
position: relative;
height: 20px;
}
#progress-bar-visited {
height: 20px;
width: 0%;
background-color: #76c7c0;
border-radius: 25px;
transition: width 0.5s;
position: absolute;
z-index: 0;
}
#progress-bar-completed {
height: 20px;
width: 0%;
background-color: #4caf50;
border-radius: 25px;
transition: width 0.5s;
position: absolute;
z-index: 1;
white-space: nowrap;
}
#content {
flex: 1;
}
footer {
text-align: center;
padding: 10px 0;
width: 100%;
margin-top: auto;
color: #e0e0e0;
}
</style>
</head>
<body>
<div id="content">
<h1>Reciter!</h1>
<div id="total_count_number"></div>
<div id="overall-stats"></div>
<div id="progress-container">
<div id="progress-bar-completed"></div>
<div id="progress-bar-visited"></div>
</div>
<button id="next-question">下一题</button>
<div id="quiz-container">
<div id="question-type"></div>
<div id="question-title"></div>
<div id="options-container"></div>
</div>
<button id="submit-answer">提交答案</button>
<div id="result"></div>
<button id="prev-question">上一题</button>
<div id="message">The question bank is not empty!</div>
<!--
<button id="clear-cache">清空缓存</button>
-->
</div>
<script>
let total_count = 0;
let questions = []; // 题库
let prev_question = null; // 上次题目
let current_question = null; // 当前题目
let question_wrong = []; // 做错的题
// let question_unfold = []; // 未做对的题
/* 清空缓存 */
function clearCache() {
if (confirm("是否清空所有做题记录?")) {
localStorage.removeItem('quiz_stats');
history_update();
updateProgressBar();
alert('缓存已清空');
}
}
//document.getElementById('clear-cache').addEventListener('click', clearCache);
async function question_init() {
try {
const response = await fetch('question.json');
if (!response.ok) throw new Error('Network response was not ok');
questions = await response.json();
} catch (error) {
console.error('Error loading questions:', error);
// 本地测试时的虚拟题库初始化
questions = [
{
"id": "77",
"type": "多选题(分值1.0 分 难度:易)",
"title": "推进中国式现代化需要正确处理的重大关系是( )。",
"options": [
"A.正确处理顶层设计与实践探索的关系",
"B.正确处理战略与策略的关系",
"C.正确处理守正与创新的关系",
"D.正确处理效率与公平的关系",
"E.正确处理独立自主与对外开放的关系"
],
"correct_answer": "ABCDE"
}, {
"id": "78",
"type": "填空题(分值1.0 分 难度:易)",
"title": "( )源远流长、博大精深,是中华民族独特的精神标识,是当代中国文化的根基,是维系全世界华人的精神纽带。",
"options": [],
"correct_answer": "参考答案中华文明"
}, {
"id": "84",
"type": "判断题(分值1.0 分 难度:易)",
"title": "共同富裕是中国特色社会主义的本质要求,我国很快就实现共同富裕。",
"options": [],
"correct_answer": "正确答案错"
}, {
"id": "90",
"type": "单选题(分值1.0 分 难度:易)",
"title": "维护党中央权威和集中统一领导,最关键的是( )",
"options": [
"A.实行民主集中制",
"B.坚决维护习近平同志党中央的核心、全党的核心地位",
"C.修改党章,加强党中央权力",
"D.严明政治纪律"
],
"correct_answer": "B"
}
];
}
const storedWrongQuestions = localStorage.getItem('question_wrong');
if (storedWrongQuestions)
question_wrong = JSON.parse(storedWrongQuestions);
total_count = parseInt(localStorage.getItem('total_exercise_count'));
question_next();
displayOverallStats();
}
/* 随机返回题目 */
const p_wrong = 4; // 1/4的概率,从错题里面抽题
function question_random() {
let randomNum = Math.random();
let stats = JSON.parse(localStorage.getItem('quiz_stats')) || {};
let questionsNeverCorrect = questions.filter(q => {
return !stats[q.id] || (stats[q.id] && stats[q.id].correct === 0);
});
if (randomNum < 1 / p_wrong && question_wrong.length > 0) {
// 1/4的概率从错题集中抽题
const index = Math.floor(Math.random() * question_wrong.length);
return question_wrong[index];
} else if (questionsNeverCorrect.length > 0) {
const index = Math.floor(Math.random() * questionsNeverCorrect.length);
return questionsNeverCorrect[index];
} else {
confirm('已答完全部!');
const index = Math.floor(Math.random() * questions.length);
return questions[index];
}
}
/* 返回上一题 */
function question_back() {
if (prev_question) {
current_question = prev_question;
question_update();
}
}
/* 随机下一题 */
function question_next() {
prev_question = current_question;
current_question = question_random();
question_update();
}
/* 更新当前题目并显示 */
function question_update() {
document.getElementById('question-type').innerText = current_question.type + "\n\n";
document.getElementById('question-title').innerText = current_question.title;
const optionsContainer = document.getElementById('options-container');
optionsContainer.innerHTML = '';
const questionType = current_question.type;
if (questionType.includes('单选')) {
current_question.options.forEach((option, index) => {
const optionElement = document.createElement('div');
optionElement.className = 'option';
optionElement.innerHTML = `
<input type="radio" name="option" value="${index}" id="option${index}">
<label for="option${index}">${option}</label>
`;
optionsContainer.appendChild(optionElement);
});
} else if (questionType.includes('多选')) {
current_question.options.forEach((option, index) => {
const optionElement = document.createElement('div');
optionElement.className = 'option';
optionElement.innerHTML = `
<input type="checkbox" name="option" value="${index}" id="option${index}">
<label for="option${index}">${option}</label>
`;
optionsContainer.appendChild(optionElement);
});
} else {
const inputElement = document.createElement('input');
inputElement.type = 'text';
inputElement.name = 'option';
inputElement.id = 'option0';
optionsContainer.appendChild(inputElement);
}
const resultContainer = document.getElementById('result');
resultContainer.innerHTML = '';
}
/* 判题并统计 */
function question_check() {
const resultContainer = document.getElementById('result');
resultContainer.innerHTML = '';
const questionType = current_question.type;
let userAnswer = '';
if (questionType.includes('单选')) {
const selectedOption = document.querySelector('input[name="option"]:checked');
if (selectedOption) {
userAnswer = selectedOption.nextElementSibling.textContent.charAt(0);
}
} else if (questionType.includes('多选')) {
const selectedOptions = document.querySelectorAll('input[name="option"]:checked');
selectedOptions.forEach(option => {
userAnswer += option.nextElementSibling.textContent.charAt(0);
});
userAnswer = userAnswer.split('').sort().join('');
} else {
const userInput = document.getElementById('option0').value.trim();
userAnswer = userInput;
}
const correctAnswer = current_question.correct_answer.replace(/[^A-Z]/g, '');
let isCorrect = false;
if (questionType.includes('单选') || questionType.includes('多选')) {
isCorrect = userAnswer === correctAnswer;
} else {
isCorrect = confirm(`正确答案是:${current_question.correct_answer}\n你的答案是:${userAnswer}\n是否标记为正确?`);
}
const resultMessage = document.createElement('div');
resultMessage.innerHTML = `
<p>参考答案:${current_question.correct_answer}</p>
<p>你的答案:${userAnswer}</p>
<p>结果:${isCorrect ? '正确' : '错误'}</p>
`;
resultMessage.classList.add(isCorrect ? 'correct' : 'incorrect');
resultContainer.appendChild(resultMessage);
history_update(current_question.id, isCorrect);
}
/* 计算并显示总体正确率 */
function displayOverallStats() {
document.getElementById('total_count_number').innerHTML = `<p>练习次数:${total_count ? total_count : 0}`;
let stats = localStorage.getItem('quiz_stats');
if (!stats) {
stats = {};
} else {
stats = JSON.parse(stats);
}
let totalAttempts = 0;
let totalCorrect = 0;
for (let key in stats) {
if (stats.hasOwnProperty(key)) {
totalAttempts += stats[key].attempts;
totalCorrect += stats[key].correct;
}
}
const overallStatsContainer = document.getElementById('overall-stats');
overallStatsContainer.innerHTML = `
<p>正确率:${totalAttempts ? ((totalCorrect / totalAttempts) * 100).toFixed(2) : 0}%</p>
`;
updateProgressBar();
}
/* 更新统计信息 */
function history_update(questionId, isCorrect) {
if (total_count)
total_count = total_count + 1;
else
total_count = 1;
let stats = localStorage.getItem('quiz_stats');
if (!stats) {
stats = {};
} else {
stats = JSON.parse(stats);
}
if (!stats[questionId]) {
stats[questionId] = {
attempts: 0,
correct: 0
};
}
stats[questionId].attempts += 1;
if (isCorrect) {
stats[questionId].correct += 1;
// 移除正确答案从 question_wrong 中
question_wrong = question_wrong.filter(question => question.id !== questionId);
}
else {
// 添加做错的题目
question_wrong.push(questions.find(question => question.id === questionId));
}
localStorage.setItem('quiz_stats', JSON.stringify(stats));
localStorage.setItem('question_wrong', JSON.stringify(question_wrong));
localStorage.setItem('total_exercise_count', total_count.toString());
displayOverallStats();
}
/* 更新进度条 */
function updateProgressBar() {
let stats = localStorage.getItem('quiz_stats');
if (!stats) {
stats = {};
} else {
stats = JSON.parse(stats);
}
let totalQuestions = questions.length;
let completedQuestions = Object.keys(stats).length - 1;
let correctQuestions = Object.values(stats).filter(stat => stat.correct > 0).length;
let progressVisitedPercentage = (completedQuestions / totalQuestions) * 100;
let progressCompletedPercentage = (correctQuestions / totalQuestions) * 100;
let progressBarVisited = document.getElementById('progress-bar-visited');
let progressBarCompleted = document.getElementById('progress-bar-completed');
progressBarVisited.style.width = progressVisitedPercentage + '%';
// progressBarVisited.innerText = `已浏览 ${completedQuestions} / ${totalQuestions} (${progressVisitedPercentage.toFixed(2)}%)`;
progressBarCompleted.style.width = progressCompletedPercentage + '%';
progressBarCompleted.innerText = `${correctQuestions} / ${totalQuestions} (${progressCompletedPercentage.toFixed(2)}%)`;
}
document.getElementById('next-question').addEventListener('click', question_next);
document.getElementById('submit-answer').addEventListener('click', question_check);
document.getElementById('prev-question').addEventListener('click', question_back);
question_init();
</script>
<footer>
<a href="https://github.com/wegret/Reciter/" target="_blank">GitHub</a>
</footer>
</body>
</html>