',
// ---- 共用筛选区 ----
'
',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
' ',
'
',
'
',
// ---- 导出 ----
'
',
'
',
' ',
' ',
' ',
'
',
'
',
' ',
' ',
' 首次导出会自动按当前来源与筛选载入题目',
'
',
'
',
' ',
' ',
' ',
'
',
'
',
// ---- 重练 ----
'
',
'
',
' ',
' ',
' ',
' ',
'
',
'
',
' ',
' ',
' ',
'
',
'
',
'
最近组卷
',
'
',
'
',
// ---- 笔记 ----
'
',
'
',
' ' + icon('search') + '',
' ',
'
',
'
',
' ',
' ',
' ',
' ',
'
',
'
',
'
',
'
',
'
',
' ',
' ',
' ',
' ',
'
',
'
数据保存在本浏览器(localStorage)。清理浏览器数据或换设备前请先备份。
',
'
',
'
'
].join('');
document.body.appendChild(viewEl);
// 批注轨道:脱离题目滚动容器,靠 JS 镜像其位置并跟随 scrollTop
var railEl = document.createElement('div');
railEl.id = 'gth-rail';
railEl.className = 'gth-rail';
railEl.hidden = true;
railEl.innerHTML = '' +
'
' +
'
' + icon('hash') + '题目ID ' + esc(q.id) + ' ' + esc(quiz.desc) + '
' +
(q.material ? '
' + wakeImgs(q.material) + '
' : '') +
'
' + wakeImgs(q.content || '') + '
' +
'
' + (opts || '
该题型无选项
') + '
' +
(multi ? '
多选题,共 ' + ansKey(q.correct_answer).length + ' 个正确选项
' : '') +
'
' +
'
' + cells + '
' +
'
' +
'' +
'' +
'
' +
'
';
}
function reportHtml() {
var right = 0;
var items = quiz.list.map(function (q, i) {
var ans = quiz.answers[q.id] || [];
var answered = ans.length > 0;
var mine = ansKey(ans);
var ok = answered && mine === ansKey(q.correct_answer);
if (ok) right++;
var opts = (q.opt || []).map(function (o) {
var isRight = ansKey(q.correct_answer).indexOf(o.label) >= 0;
var isMine = (quiz.answers[q.id] || []).indexOf(o.label) >= 0;
var mark = isRight ? '(正确)' : (isMine ? '(你选的)' : '');
var style = isRight ? ' style="color:#16a34a;font-weight:600"' : (isMine ? ' style="color:var(--gth-destructive)"' : '');
return '' + esc(o.label) + '. ' + wakeImgs(o.content || '') + mark + '
';
}).join('');
return '' +
'' +
'
' +
'' +
(ok ? icon('checkCircle') : (answered ? icon('xCircle') : icon('warn'))) +
(ok ? '正确' : (answered ? '错误' : '未作答')) + '' +
'第 ' + (i + 1) + ' 题' +
'·' + esc(SUBJECT_NAME[q.content_type] || '') + '' +
'·#' + esc(q.id) + '' +
(function () { var t = errTag(errCountOf(q));
return '' + t.html + ''; })() +
'
' +
(q.material ? '
' + wakeImgs(q.material) + '
' : '') +
'
' + wakeImgs(q.content || '') + '
' +
'
' + opts + '
' +
'
你的答案:' + (mine || '未作答') + '
' +
'
正确答案:' + ansKey(q.correct_answer) + '
' +
(analysisOf(q) ? '
解析' + wakeImgs(analysisOf(q)) + '
' : '') +
'
' +
'
' +
'' +
'
' +
'
' +
'
' +
'
';
}).join('');
var pct = quiz.list.length ? Math.round(right / quiz.list.length * 100) : 0;
// 错题数只统计「答过且答错」的,未作答不计入错题
var wrongCount = quiz.list.filter(function (q) {
var a = quiz.answers[q.id] || [];
return a.length > 0 && ansKey(a) !== ansKey(q.correct_answer);
}).length;
return '' +
'' +
(wrongCount ? '
' + icon('warn') + '错题 ' + wrongCount + ' 道
' : '') +
'
' + items + '
' +
'
';
}
function bindQuiz() {
var q = quiz.list[quiz.idx];
$$('.gthq-opt', quizEl).forEach(function (el) {
el.addEventListener('click', function () {
toggleAnswer(q.id, el.dataset.label, ansKey(q.correct_answer).length > 1);
});
});
$$('.gthq-cell', quizEl).forEach(function (el) {
el.addEventListener('click', function () { quiz.idx = Number(el.dataset.i); renderQuiz(); });
});
var prev = $('#gth-prev', quizEl);
if (prev) prev.addEventListener('click', function () {
if (quiz.idx > 0) { quiz.idx--; renderQuiz(); }
});
var next = $('#gth-next', quizEl);
if (next) next.addEventListener('click', function () {
if (quiz.idx < quiz.list.length - 1) { quiz.idx++; renderQuiz(); }
});
var submit = $('#gth-submit', quizEl);
if (submit) submit.addEventListener('click', function () {
var un = quiz.list.length - answeredCount();
if (un > 0 && !confirm('还有 ' + un + ' 题未作答,确定交卷吗?')) return;
clearInterval(quiz.timer);
quiz.timer = null;
applyMastery();
quiz.submitted = true;
// 交卷:未作答的不算「做过」。全部作答 -> 整套完成(下次从头开始);
// 否则下次从「第一道未作答」继续,而不是径直跳到这套之后
if (quiz.resumeKey) {
var fu = 0;
for (; fu < quiz.list.length; fu++) {
if (!quiz.answeredIds[String(quiz.list[fu].id)]) break;
}
if (fu >= quiz.list.length) saveResume(quiz.resumeKey, quiz.resumeBase + quiz.list.length, '', {});
else saveResume(quiz.resumeKey, quiz.resumeBase + fu, quiz.list[fu].id, quiz.answeredIds);
}
renderQuiz();
quizEl.querySelector('.gthq-main').scrollTop = 0;
});
var ow = $('#gth-onlywrong', quizEl);
if (ow) ow.addEventListener('click', function () {
$$('#gth-report .gthq-r-item').forEach(function (el) {
el.hidden = !el.classList.contains('wrong');
});
});
var al = $('#gth-all', quizEl);
if (al) al.addEventListener('click', function () {
$$('#gth-report .gthq-r-item').forEach(function (el) { el.hidden = false; });
});
var exit = $('#gth-exit', quizEl);
if (exit) exit.addEventListener('click', closeQuiz);
$$('textarea[data-note]', quizEl).forEach(function (ta) {
ta.addEventListener('input', debounce(function () {
var q2 = null;
for (var i = 0; i < quiz.list.length; i++) if (String(quiz.list[i].id) === ta.dataset.note) { q2 = quiz.list[i]; break; }
setNote(ta.dataset.note, ta.value.trim(), {
snapshot: q2 ? stripHtml(q2.content || '').slice(0, 240) : '',
subject: q2 ? q2.content_type : null
});
}, 400));
});
// 报告页:划线清单(点文字写批注 / 点 ✕ 取消)由 document 上的委托统一处理
var cb = $('#gth-report');
if (cb) refreshQuizHl();
$$('.gthq-r-item[data-gth-qid] [data-act="collect"]', quizEl).forEach(function (btn) {
var item = btn.closest('.gthq-r-item');
btn.addEventListener('click', function () {
openCollect([String(item.getAttribute('data-gth-qid'))]);
});
});
}
// 交卷时统计对错:未作答的题目既不计入对错,也不累计答错次数、不改变掌握度
function applyMastery() {
quiz.list.forEach(function (q) {
var ans = quiz.answers[q.id];
if (!ans || !ans.length) return; // 未作答:直接跳过,当作「没做过」处理
var ok = ansKey(ans) === ansKey(q.correct_answer);
var prev = (store.mastered[q.id] && store.mastered[q.id].streak) || 0;
store.mastered[q.id] = { streak: ok ? prev + 1 : 0, updated: Date.now() };
bumpWrongCount(q, ok);
});
saveStore();
}
document.addEventListener('keydown', function (e) {
if (quizEl.hidden) return;
if (e.key === 'Escape') { closeQuiz(); return; }
if (quiz && !quiz.submitted) {
if (e.key === 'ArrowLeft' && quiz.idx > 0) { quiz.idx--; renderQuiz(); }
if (e.key === 'ArrowRight' && quiz.idx < quiz.list.length - 1) { quiz.idx++; renderQuiz(); }
}
});
// 批注栏在错题页与收藏页都生效,用 body 上的标记类限定样式作用域
// (类名沿用 gth-error,改名会牵动一批 CSS 选择器,没必要)
function syncRouteClass() {
document.body.classList.toggle('gth-error', isListRoute());
syncSourceDefault();
}
window.addEventListener('hashchange', function () {
syncRouteClass();
invalidateLoaded(); // 换了路由,缓存的题目列表跟着作废
// 离开错题页 / 收藏页时收起助手视图,避免它跟着显示到别的界面上
if (!isListRoute() && viewOn) setView(false);
refreshPageUI();
});
if (!getToken()) {
setStatus('未检测到登录 token,请先登录站点后再使用。', 'err');
}
syncRouteClass();
syncFilterUI();
renderHistory();
updateExportHint();
})();