feat(exam): 优化答案解析弹窗和判断题/填空题样式
Some checks failed
continuous-integration/drone/push Build is failing

- 答案解析弹窗限制最大高度45vh,超出可滚动
- 判断题改为卡片式按钮,带图标和状态反馈
- 填空题增大输入框,添加图标和提示
This commit is contained in:
yuliang_guo
2026-01-28 14:16:43 +08:00
parent 12fd2fc0a1
commit f53b40c6c7

View File

@@ -67,20 +67,57 @@
<!-- 判断题 --> <!-- 判断题 -->
<div v-else-if="currentQuestion.type === 'judge'" class="judge-options"> <div v-else-if="currentQuestion.type === 'judge'" class="judge-options">
<el-radio-group v-model="userAnswer.judge" :disabled="showAnswer" @change="handleJudgeChange"> <div class="judge-cards">
<el-radio :label="true">正确</el-radio> <div
<el-radio :label="false">错误</el-radio> class="judge-card"
</el-radio-group> :class="{
active: userAnswer.judge === true,
disabled: showAnswer,
correct: showAnswer && currentQuestion.correctAnswer === true,
wrong: showAnswer && userAnswer.judge === true && currentQuestion.correctAnswer !== true
}"
@click="!showAnswer && selectJudge(true)"
>
<div class="judge-icon correct-icon">
<el-icon :size="32"><CircleCheck /></el-icon>
</div>
<span class="judge-text">正确</span>
</div>
<div
class="judge-card"
:class="{
active: userAnswer.judge === false,
disabled: showAnswer,
correct: showAnswer && currentQuestion.correctAnswer === false,
wrong: showAnswer && userAnswer.judge === false && currentQuestion.correctAnswer !== false
}"
@click="!showAnswer && selectJudge(false)"
>
<div class="judge-icon wrong-icon">
<el-icon :size="32"><CircleClose /></el-icon>
</div>
<span class="judge-text">错误</span>
</div>
</div>
</div> </div>
<!-- 填空题 --> <!-- 填空题 -->
<div v-else-if="currentQuestion.type === 'blank'" class="blank-answer"> <div v-else-if="currentQuestion.type === 'blank'" class="blank-answer">
<el-input <div class="blank-input-wrapper">
v-model="userAnswer.blank" <el-input
placeholder="请输入答案" v-model="userAnswer.blank"
:disabled="showAnswer" placeholder="请在此输入你的答案..."
@keyup.enter="handleBlankSubmit" :disabled="showAnswer"
/> @keyup.enter="handleBlankSubmit"
size="large"
clearable
>
<template #prefix>
<el-icon><Edit /></el-icon>
</template>
</el-input>
<p class="blank-hint"> Enter 键提交答案</p>
</div>
</div> </div>
<!-- 问答题 --> <!-- 问答题 -->
@@ -150,28 +187,31 @@
:close-on-click-modal="false" :close-on-click-modal="false"
:close-on-press-escape="false" :close-on-press-escape="false"
:show-close="false" :show-close="false"
class="answer-dialog"
> >
<div v-if="dialogQuestion" class="answer-dialog-content"> <div v-if="dialogQuestion" class="answer-dialog-content">
<div class="result-section"> <div class="result-section">
<div class="result-icon-large wrong"> <div class="result-icon-large wrong">
<el-icon :size="48"><CircleClose /></el-icon> <el-icon :size="40"><CircleClose /></el-icon>
</div> </div>
<h3 class="result-text">回答错误</h3> <h3 class="result-text">回答错误</h3>
</div> </div>
<div class="question-review"> <div class="dialog-scroll-area">
<h4>题目</h4> <div class="question-review">
<p>{{ dialogQuestion.title }}</p> <h4>题目</h4>
</div> <p>{{ dialogQuestion.title }}</p>
</div>
<div class="answer-review">
<h4>正确答案</h4> <div class="answer-review">
<div class="correct-answer-text markdown-content" v-html="renderMarkdown(formatCorrectAnswer(dialogQuestion))"></div> <h4>正确答案</h4>
</div> <div class="correct-answer-text markdown-content" v-html="renderMarkdown(formatCorrectAnswer(dialogQuestion))"></div>
</div>
<div class="explanation-review">
<h4>解析</h4> <div class="explanation-review">
<div class="explanation-text markdown-content" v-html="renderMarkdown(dialogQuestion.explanation)"></div> <h4>解析</h4>
<div class="explanation-text markdown-content" v-html="renderMarkdown(dialogQuestion.explanation)"></div>
</div>
</div> </div>
</div> </div>
@@ -190,7 +230,7 @@
import { ref, reactive, computed, onMounted, onUnmounted } from 'vue' import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox, ElDialog, ElLoading } from 'element-plus' import { ElMessage, ElMessageBox, ElDialog, ElLoading } from 'element-plus'
import { Loading, CircleCheck, CircleClose } from '@element-plus/icons-vue' import { Loading, CircleCheck, CircleClose, Edit } from '@element-plus/icons-vue'
import { courseApi } from '@/api/course' import { courseApi } from '@/api/course'
import { generateExam, judgeAnswer, recordMistake, getMistakes, updateRoundScore, type MistakeRecordItem } from '@/api/exam' import { generateExam, judgeAnswer, recordMistake, getMistakes, updateRoundScore, type MistakeRecordItem } from '@/api/exam'
import { marked } from 'marked' import { marked } from 'marked'
@@ -903,6 +943,14 @@ const handleJudgeChange = () => {
checkAndHandleAnswer() checkAndHandleAnswer()
} }
/**
* 判断题卡片点击
*/
const selectJudge = (value: boolean) => {
userAnswer.judge = value
checkAndHandleAnswer()
}
/** /**
* 填空题回车提交处理 * 填空题回车提交处理
*/ */
@@ -1355,21 +1403,118 @@ onUnmounted(() => {
} }
.judge-options { .judge-options {
.el-radio-group { .judge-cards {
.el-radio { display: flex;
margin-right: 32px; gap: 20px;
justify-content: center;
:deep(.el-radio__label) {
.judge-card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 140px;
height: 120px;
border: 2px solid #e4e7ed;
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
background: #fff;
&:hover:not(.disabled) {
border-color: #409eff;
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
}
&.active {
border-color: #409eff;
background: linear-gradient(135deg, #ecf5ff 0%, #f5f9ff 100%);
.judge-icon {
transform: scale(1.1);
}
}
&.disabled {
cursor: not-allowed;
opacity: 0.8;
}
&.correct {
border-color: #67c23a;
background: linear-gradient(135deg, #f0f9eb 0%, #f5faf2 100%);
.judge-text {
color: #67c23a;
}
}
&.wrong {
border-color: #f56c6c;
background: linear-gradient(135deg, #fef0f0 0%, #fdf5f5 100%);
.judge-text {
color: #f56c6c;
}
}
.judge-icon {
margin-bottom: 10px;
transition: transform 0.3s ease;
&.correct-icon {
color: #67c23a;
}
&.wrong-icon {
color: #f56c6c;
}
}
.judge-text {
font-size: 16px; font-size: 16px;
font-weight: 500;
color: #606266;
} }
} }
} }
} }
.blank-answer { .blank-answer {
.el-input { .blank-input-wrapper {
width: 100%; max-width: 500px;
max-width: 400px;
.el-input {
:deep(.el-input__wrapper) {
padding: 8px 15px;
box-shadow: 0 0 0 1px #dcdfe6 inset;
border-radius: 8px;
transition: all 0.3s ease;
&:hover {
box-shadow: 0 0 0 1px #c0c4cc inset;
}
&.is-focus {
box-shadow: 0 0 0 1px #409eff inset;
}
}
:deep(.el-input__inner) {
font-size: 16px;
}
:deep(.el-input__prefix) {
color: #909399;
}
}
.blank-hint {
margin-top: 8px;
font-size: 12px;
color: #909399;
text-align: right;
}
} }
} }
@@ -1470,15 +1615,22 @@ onUnmounted(() => {
} }
// 答案解析弹窗样式 // 答案解析弹窗样式
:deep(.answer-dialog) {
.el-dialog__body {
padding-top: 10px;
padding-bottom: 10px;
}
}
.answer-dialog-content { .answer-dialog-content {
.result-section { .result-section {
text-align: center; text-align: center;
margin-bottom: 24px; margin-bottom: 16px;
.result-icon-large { .result-icon-large {
width: 80px; width: 64px;
height: 80px; height: 64px;
margin: 0 auto 16px; margin: 0 auto 10px;
border-radius: 50%; border-radius: 50%;
display: flex; display: flex;
align-items: center; align-items: center;
@@ -1491,39 +1643,58 @@ onUnmounted(() => {
} }
.result-text { .result-text {
font-size: 20px; font-size: 18px;
font-weight: 600; font-weight: 600;
color: #f56c6c; color: #f56c6c;
margin: 0; margin: 0;
} }
} }
.dialog-scroll-area {
max-height: 45vh;
overflow-y: auto;
padding-right: 8px;
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background: #dcdfe6;
border-radius: 3px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
}
.question-review, .question-review,
.answer-review, .answer-review,
.explanation-review { .explanation-review {
margin-bottom: 24px; margin-bottom: 16px;
h4 { h4 {
font-size: 16px; font-size: 14px;
font-weight: 600; font-weight: 600;
color: #333; color: #333;
margin-bottom: 12px; margin-bottom: 8px;
} }
p { p {
font-size: 14px; font-size: 14px;
line-height: 1.8; line-height: 1.6;
margin: 0; margin: 0;
color: #666; color: #666;
} }
.correct-answer-text { .correct-answer-text {
font-size: 15px; font-size: 14px;
line-height: 1.8; line-height: 1.6;
color: #333; color: #333;
background: #f0f9ff; background: #f0f9ff;
border-left: 4px solid #67c23a; border-left: 4px solid #67c23a;
padding: 12px 16px; padding: 10px 14px;
border-radius: 6px; border-radius: 6px;
strong { strong {
@@ -1533,11 +1704,11 @@ onUnmounted(() => {
} }
.explanation-text { .explanation-text {
font-size: 14px; font-size: 13px;
line-height: 1.8; line-height: 1.6;
color: #666; color: #666;
background: #f5f7fa; background: #f5f7fa;
padding: 16px; padding: 12px;
border-radius: 8px; border-radius: 8px;
strong { strong {