fix: 前端多选题不显示选项(multiple_choice 未渲染 options,补 checkbox 渲染、toggle 交互与判定)
Deploy Examination / deploy (push) Successful in 7s
Deploy Examination / deploy (push) Successful in 7s
This commit is contained in:
+54
-7
@@ -156,6 +156,10 @@ body { font-family: -apple-system, 'Segoe UI', sans-serif; background: var(--bg)
|
|||||||
.option.correct .radio::after { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--green); }
|
.option.correct .radio::after { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--green); }
|
||||||
.option.wrong .radio { border-color: var(--red); }
|
.option.wrong .radio { border-color: var(--red); }
|
||||||
.option.wrong .radio::after { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--red); }
|
.option.wrong .radio::after { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--red); }
|
||||||
|
.option .checkbox { width: 18px; height: 18px; border: 2px solid var(--surface2); border-radius: 4px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-size: 12px; color: white; transition: all 0.2s; }
|
||||||
|
.option.selected .checkbox { border-color: var(--accent); background: var(--accent); }
|
||||||
|
.option.correct .checkbox { border-color: var(--green); background: var(--green); }
|
||||||
|
.option.wrong .checkbox { border-color: var(--red); background: var(--red); }
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
.actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-top: 4px; }
|
.actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-top: 4px; }
|
||||||
@@ -508,19 +512,33 @@ function renderQuestionCard(q, i) {
|
|||||||
|
|
||||||
html += `</div>`;
|
html += `</div>`;
|
||||||
|
|
||||||
// Options for single_choice
|
// Options for single_choice and multiple_choice
|
||||||
if (q.type === 'single_choice' && q.options) {
|
if ((q.type === 'single_choice' || q.type === 'multiple_choice') && q.options) {
|
||||||
|
const isMC = q.type === 'multiple_choice';
|
||||||
|
const selectedSet = isMC ? (state?.selected || []) : [];
|
||||||
html += `<div class="options">`;
|
html += `<div class="options">`;
|
||||||
for (const [letter, text] of Object.entries(q.options)) {
|
for (const [letter, text] of Object.entries(q.options)) {
|
||||||
let optClass = '';
|
let optClass = '';
|
||||||
if (revealed) {
|
if (revealed) {
|
||||||
|
if (isMC) {
|
||||||
|
// multiple_choice: 'correct' = 答案里的正确项; 'wrong' = 用户误选的非正确项
|
||||||
|
const isCorrectAnswer = (q.answer || []).includes(letter);
|
||||||
|
const userSelected = selectedSet.includes(letter);
|
||||||
|
if (isCorrectAnswer) optClass = 'correct';
|
||||||
|
else if (userSelected) optClass = 'wrong';
|
||||||
|
} else {
|
||||||
if (letter === q.answer) optClass = 'correct';
|
if (letter === q.answer) optClass = 'correct';
|
||||||
else if (state?.value === letter) optClass = 'wrong';
|
else if (state?.value === letter) optClass = 'wrong';
|
||||||
} else if (state?.value === letter) {
|
}
|
||||||
|
} else if (isMC ? selectedSet.includes(letter) : state?.value === letter) {
|
||||||
optClass = 'selected';
|
optClass = 'selected';
|
||||||
}
|
}
|
||||||
html += `<div class="option ${optClass}" onclick="selectOption('${q.id}','${letter}')">
|
const clickFn = isMC ? `toggleOption('${q.id}','${letter}')` : `selectOption('${q.id}','${letter}')`;
|
||||||
<div class="radio"></div>
|
const marker = isMC
|
||||||
|
? `<div class="checkbox"><span>${selectedSet.includes(letter) ? '✓' : ''}</span></div>`
|
||||||
|
: `<div class="radio"></div>`;
|
||||||
|
html += `<div class="option ${optClass}" onclick="${clickFn}">
|
||||||
|
${marker}
|
||||||
<span><strong>${letter}.</strong> ${escapeHtml(text)}</span>
|
<span><strong>${letter}.</strong> ${escapeHtml(text)}</span>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
@@ -562,6 +580,19 @@ function selectOption(qId, letter) {
|
|||||||
renderQuiz();
|
renderQuiz();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Toggle an option for multiple_choice questions
|
||||||
|
function toggleOption(qId, letter) {
|
||||||
|
const q = currentQuestions.find(x => x.id === qId);
|
||||||
|
if (!q || q.type !== 'multiple_choice' || answers[qId]?.revealed) return;
|
||||||
|
const cur = answers[qId];
|
||||||
|
const selected = cur?.selected ? [...cur.selected] : [];
|
||||||
|
const idx = selected.indexOf(letter);
|
||||||
|
if (idx >= 0) selected.splice(idx, 1);
|
||||||
|
else selected.push(letter);
|
||||||
|
answers[qId] = { value: selected.join(','), selected, correct: false, revealed: false };
|
||||||
|
renderQuiz();
|
||||||
|
}
|
||||||
|
|
||||||
function saveFill(qId) {
|
function saveFill(qId) {
|
||||||
const input = document.getElementById(`input-${qId}`);
|
const input = document.getElementById(`input-${qId}`);
|
||||||
if (!input) return;
|
if (!input) return;
|
||||||
@@ -611,6 +642,12 @@ function revealAnswer(qId) {
|
|||||||
|
|
||||||
if (q.type === 'single_choice') {
|
if (q.type === 'single_choice') {
|
||||||
answers[qId].correct = answers[qId].value === q.answer;
|
answers[qId].correct = answers[qId].value === q.answer;
|
||||||
|
} else if (q.type === 'multiple_choice') {
|
||||||
|
const userSel = (answers[qId].selected || []).slice().sort();
|
||||||
|
const correctSel = (q.answer || []).slice().sort();
|
||||||
|
answers[qId].correct = userSel.length === correctSel.length &&
|
||||||
|
userSel.every((x, i) => x === correctSel[i]);
|
||||||
|
// 让多选选项未选中状态也能正确显示
|
||||||
}
|
}
|
||||||
answers[qId].revealed = true;
|
answers[qId].revealed = true;
|
||||||
renderQuiz();
|
renderQuiz();
|
||||||
@@ -619,9 +656,14 @@ function revealAnswer(qId) {
|
|||||||
function showAllAnswers() {
|
function showAllAnswers() {
|
||||||
currentQuestions.forEach(q => {
|
currentQuestions.forEach(q => {
|
||||||
if (!answers[q.id]?.revealed) {
|
if (!answers[q.id]?.revealed) {
|
||||||
if (!answers[q.id]) answers[q.id] = { value: '', correct: false };
|
if (!answers[q.id]) answers[q.id] = { value: '', selected: [], correct: false };
|
||||||
if (q.type === 'single_choice') {
|
if (q.type === 'single_choice') {
|
||||||
answers[q.id].correct = answers[q.id].value === q.answer;
|
answers[q.id].correct = answers[q.id].value === q.answer;
|
||||||
|
} else if (q.type === 'multiple_choice') {
|
||||||
|
const userSel = (answers[q.id].selected || []).slice().sort();
|
||||||
|
const correctSel = (q.answer || []).slice().sort();
|
||||||
|
answers[q.id].correct = userSel.length === correctSel.length &&
|
||||||
|
userSel.every((x, i) => x === correctSel[i]);
|
||||||
}
|
}
|
||||||
answers[q.id].revealed = true;
|
answers[q.id].revealed = true;
|
||||||
}
|
}
|
||||||
@@ -631,10 +673,15 @@ function showAllAnswers() {
|
|||||||
|
|
||||||
function submitAll() {
|
function submitAll() {
|
||||||
currentQuestions.forEach(q => {
|
currentQuestions.forEach(q => {
|
||||||
if (!answers[q.id]) answers[q.id] = { value: '', correct: false };
|
if (!answers[q.id]) answers[q.id] = { value: '', selected: [], correct: false };
|
||||||
if (!answers[q.id].revealed) {
|
if (!answers[q.id].revealed) {
|
||||||
if (q.type === 'single_choice') {
|
if (q.type === 'single_choice') {
|
||||||
answers[q.id].correct = answers[q.id].value === q.answer;
|
answers[q.id].correct = answers[q.id].value === q.answer;
|
||||||
|
} else if (q.type === 'multiple_choice') {
|
||||||
|
const userSel = (answers[q.id].selected || []).slice().sort();
|
||||||
|
const correctSel = (q.answer || []).slice().sort();
|
||||||
|
answers[q.id].correct = userSel.length === correctSel.length &&
|
||||||
|
userSel.every((x, i) => x === correctSel[i]);
|
||||||
} else if (q.type === 'fill_blank') {
|
} else if (q.type === 'fill_blank') {
|
||||||
answers[q.id].correct = matchFillAnswer(answers[q.id].value, q.answer, q.answer_rule || 'any');
|
answers[q.id].correct = matchFillAnswer(answers[q.id].value, q.answer, q.answer_rule || 'any');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user