3368 lines
85 KiB
Vue
3368 lines
85 KiB
Vue
<template>
|
||
<div class="exam-page">
|
||
<!-- 练习中心标题 -->
|
||
<div class="banner-section">
|
||
<div class="banner-container">
|
||
<img src="/banners/考前须知.png" alt="考前须知" class="banner-image" />
|
||
</div>
|
||
<div class="banner-text">
|
||
<h2>考试中心</h2>
|
||
<p>涵盖多种题型,全方位考核,AI智能阅卷</p>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
<!-- 课程信息导航 -->
|
||
<div class="course-info-nav">
|
||
<div class="container">
|
||
<div class="nav-content">
|
||
<div class="nav-left">
|
||
<span class="nav-title">{{ courseName }}</span>
|
||
<span class="nav-separator">></span>
|
||
<span class="nav-item">{{ examName }}</span>
|
||
</div>
|
||
<div class="nav-right">
|
||
<span class="question-type-item" :class="{ 'active': currentQuestionType === '单选题' }">单选 ({{
|
||
questionsByType['单选题'].length }})</span>
|
||
<span class="type-separator">|</span>
|
||
<span class="question-type-item" :class="{ 'active': currentQuestionType === '多选题' }">多选 ({{
|
||
questionsByType['多选题'].length }})</span>
|
||
<span class="type-separator">|</span>
|
||
<span class="question-type-item" :class="{ 'active': currentQuestionType === '判断题' }">判断 ({{
|
||
questionsByType['判断题'].length }})</span>
|
||
<span class="type-separator">|</span>
|
||
<span class="question-type-item" :class="{ 'active': currentQuestionType === '填空题' }">填空 ({{
|
||
questionsByType['填空题'].length }})</span>
|
||
<span class="type-separator">|</span>
|
||
<span class="question-type-item" :class="{ 'active': currentQuestionType === '简答题' }">简答 ({{
|
||
questionsByType['简答题'].length }})</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 练习主体内容 -->
|
||
<div class="exam-main" v-if="!examFinished">
|
||
<div class="container">
|
||
<div class="exam-layout">
|
||
<!-- 左侧导航 -->
|
||
<div class="exam-sidebar">
|
||
<!-- 练习题目 -->
|
||
<div class="answer-sheet" :class="{ 'disabled': examSubmitted }">
|
||
<div class="answer-sheet-header">
|
||
<h4>练习题目</h4>
|
||
<span class="progress-text">{{ String(currentQuestionIndex + 1).padStart(2, '0') }}<span>/{{
|
||
String(questions.length).padStart(2, '0') }}</span></span>
|
||
</div>
|
||
<div class="question-grid">
|
||
<!-- 单选题 -->
|
||
<div class="question-section">
|
||
<div class="section-header">
|
||
<h5>单选 ({{ questionsByType['单选题'].length }})</h5>
|
||
</div>
|
||
<div class="question-numbers">
|
||
<div v-for="question in questionsByType['单选题']" :key="question.id" class="answer-card-number"
|
||
:class="[getQuestionStatus(question.id - 1), { 'disabled': examSubmitted }]"
|
||
@click="handleQuestionClick(question.id - 1)">
|
||
{{ String(question.id).padStart(2, '0') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 多选题 -->
|
||
<div class="question-section">
|
||
<div class="section-header">
|
||
<h5>多选 ({{ questionsByType['多选题'].length }})</h5>
|
||
</div>
|
||
<div class="question-numbers">
|
||
<div v-for="question in questionsByType['多选题']" :key="question.id" class="answer-card-number"
|
||
:class="[getQuestionStatus(question.id - 1), { 'disabled': examSubmitted }]"
|
||
@click="handleQuestionClick(question.id - 1)">
|
||
{{ String(question.id).padStart(2, '0') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 判断题 -->
|
||
<div class="question-section">
|
||
<div class="section-header">
|
||
<h5>判断 ({{ questionsByType['判断题'].length }})</h5>
|
||
</div>
|
||
<div class="question-numbers">
|
||
<div v-for="question in questionsByType['判断题']" :key="question.id" class="answer-card-number"
|
||
:class="[getQuestionStatus(question.id - 1), { 'disabled': examSubmitted }]"
|
||
@click="handleQuestionClick(question.id - 1)">
|
||
{{ String(question.id).padStart(2, '0') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 填空题 -->
|
||
<div class="question-section">
|
||
<div class="section-header">
|
||
<h5>填空 ({{ questionsByType['填空题'].length }})</h5>
|
||
</div>
|
||
<div class="question-numbers">
|
||
<div v-for="question in questionsByType['填空题']" :key="question.id" class="answer-card-number"
|
||
:class="[getQuestionStatus(question.id - 1), { 'disabled': examSubmitted }]"
|
||
@click="handleQuestionClick(question.id - 1)">
|
||
{{ String(question.id).padStart(2, '0') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 简答题 -->
|
||
<div class="question-section">
|
||
<div class="section-header">
|
||
<h5>简答 ({{ questionsByType['简答题'].length }})</h5>
|
||
</div>
|
||
<div class="question-numbers">
|
||
<div v-for="question in questionsByType['简答题']" :key="question.id" class="answer-card-number"
|
||
:class="[getQuestionStatus(question.id - 1), { 'disabled': examSubmitted }]"
|
||
@click="handleQuestionClick(question.id - 1)">
|
||
{{ String(question.id).padStart(2, '0') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 答题状态说明 -->
|
||
<div class="answer-legend">
|
||
<div class="legend-item">
|
||
<div class="legend-icon unanswered"></div>
|
||
<span class="legend-text">只看错题</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 练习控制按钮 -->
|
||
<div class="submit-section">
|
||
<button class="btn-pause-practice" :class="{ 'paused': isPaused }" @click="pausePractice">
|
||
{{ isPaused ? '恢复练习' : '暂停练习' }}
|
||
</button>
|
||
<button class="btn-end-practice" @click="endPractice">
|
||
结束练习
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右侧题目内容 -->
|
||
<div class="exam-content">
|
||
<div class="question-card" v-if="currentQuestion && !examFinished">
|
||
<div class="question-header">
|
||
<span class="question-title-info">{{ String(currentQuestionIndex + 1).padStart(2, '0') }}【{{
|
||
getQuestionTypeShort(currentQuestion.type) }}】<span>{{ currentQuestion.score }}分</span></span>
|
||
</div>
|
||
|
||
<div class="question-content">
|
||
<div class="question-title">
|
||
{{ currentQuestion.title }}
|
||
</div>
|
||
|
||
<!-- 选择题选项 -->
|
||
<div
|
||
v-if="currentQuestion.type === '单选题' || currentQuestion.type === '多选题' || currentQuestion.type === '判断题'"
|
||
class="question-options">
|
||
<div v-for="(option, index) in currentQuestion.options" :key="index" class="option-item"
|
||
:class="{ 'selected': isOptionSelected(index) }" @click="selectOption(index)">
|
||
<div class="option-checkbox">
|
||
<input type="checkbox" :name="`question-${currentQuestionIndex}`"
|
||
:checked="isOptionSelected(index)" @change="handleCheckboxClick(index, $event)"
|
||
@click="handleCheckboxClick(index, $event)">
|
||
</div>
|
||
<span class="option-label">{{ String.fromCharCode(65 + index) }}.</span>
|
||
<span class="option-text">{{ option }}</span>
|
||
</div>
|
||
|
||
<!-- 答案解析 -->
|
||
<div v-if="showAnswerExplanation[currentQuestionIndex]" class="answer-explanation">
|
||
<div class="explanation-header">
|
||
<div class="result-indicator"
|
||
:class="{ 'correct': questionResults[currentQuestionIndex], 'incorrect': !questionResults[currentQuestionIndex] }">
|
||
{{ questionResults[currentQuestionIndex] ? '回答正确' : '回答错误' }}
|
||
</div>
|
||
<div class="answer-details">
|
||
<span class="correct-answer">{{ getCorrectAnswerText() }}</span>
|
||
<span class="user-answer">{{ getUserAnswerText() }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="explanation-content">
|
||
<h4>答案解析:</h4>
|
||
<p>{{ currentQuestion.explanation }}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 填空题输入框 -->
|
||
<div v-else-if="currentQuestion.type === '填空题'" class="fill-blank">
|
||
<div class="fill-item" v-for="(_, index) in currentQuestion.blanks || [1]" :key="index">
|
||
<span class="fill-number">{{ index + 1 }}.</span>
|
||
<input type="text" :value="getFillAnswer(currentQuestionIndex, index)"
|
||
@input="setFillAnswer(currentQuestionIndex, index, $event.target.value)" placeholder=""
|
||
class="fill-input" />
|
||
</div>
|
||
<div class="fill-hint">
|
||
*请在上方输入框内输入填空内容
|
||
</div>
|
||
|
||
<!-- 填空题答案解析 -->
|
||
<div v-if="showAnswerExplanation[currentQuestionIndex]" class="answer-explanation">
|
||
<div class="explanation-header">
|
||
<div class="result-indicator"
|
||
:class="{ 'correct': questionResults[currentQuestionIndex], 'incorrect': !questionResults[currentQuestionIndex] }">
|
||
{{ questionResults[currentQuestionIndex] ? '回答正确' : '回答错误' }}
|
||
</div>
|
||
<div class="answer-details">
|
||
<span class="correct-answer">{{ getCorrectAnswerText() }}</span>
|
||
<span class="user-answer">{{ getUserAnswerText() }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="explanation-content">
|
||
<h4>答案解析:</h4>
|
||
<p>{{ currentQuestion.explanation }}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 简答题文本域 -->
|
||
<div v-else-if="currentQuestion.type === '简答题'" class="essay-answer">
|
||
<div class="essay-container">
|
||
<textarea :value="essayAnswers[currentQuestionIndex]"
|
||
@input="handleEssayAnswerChange(currentQuestionIndex, $event.target.value)" placeholder=""
|
||
class="essay-textarea" rows="8" maxlength="500"></textarea>
|
||
</div>
|
||
<div class="essay-footer">
|
||
<div class="essay-hint">
|
||
*请在上方输入框内输入填空内容
|
||
</div>
|
||
<div class="essay-counter">
|
||
{{ getEssayLength(currentQuestionIndex) }}/500
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 简答题答案解析 -->
|
||
<div v-if="showAnswerExplanation[currentQuestionIndex]" class="answer-explanation">
|
||
<div class="explanation-header">
|
||
<div class="result-indicator"
|
||
:class="{ 'correct': questionResults[currentQuestionIndex], 'incorrect': !questionResults[currentQuestionIndex] }">
|
||
{{ questionResults[currentQuestionIndex] ? '回答正确' : '回答错误' }}
|
||
</div>
|
||
<div class="answer-details">
|
||
<span class="correct-answer">{{ getCorrectAnswerText() }}</span>
|
||
<span class="user-answer">{{ getUserAnswerText() }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="explanation-content">
|
||
<h4>答案解析:</h4>
|
||
<p>{{ currentQuestion.explanation }}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="question-navigation">
|
||
<button class="btn-nav btn-prev" @click="previousQuestion" :disabled="currentQuestionIndex === 0">
|
||
上一题
|
||
</button>
|
||
<button class="btn-nav btn-next" @click="nextQuestion">
|
||
下一题
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 确认交卷对话框 -->
|
||
<div class="confirm-dialog-overlay" v-if="showConfirmDialog" @click="cancelSubmit">
|
||
<div class="confirm-dialog" @click.stop>
|
||
<div class="dialog-content">
|
||
<p class="dialog-message">{{ confirmDialogMessage }}</p>
|
||
<div class="dialog-buttons">
|
||
<button class="btn-cancel" @click="cancelSubmit">否</button>
|
||
<button class="btn-cancel" @click="confirmSubmit">是</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 时间到提示对话框 -->
|
||
<div class="time-up-overlay" v-if="showTimeUpDialog">
|
||
<div class="time-up-dialog">
|
||
<div class="time-up-content">
|
||
<div class="time-up-image">
|
||
<img src="/images/examination/time-up.png" alt="时间到" class="time-up-img" />
|
||
</div>
|
||
<p class="time-up-message">时间到,已自动为您提交答题卡!</p>
|
||
<!-- <button class="btn-confirm" @click="goToResult">确定</button> -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 练习结果 -->
|
||
<div class="exam-result" v-if="examFinished">
|
||
<div class="container">
|
||
<div class="exam-layout">
|
||
<!-- 左侧答题卡保持不变 -->
|
||
<div class="exam-sidebar">
|
||
|
||
<!-- 答题卡 -->
|
||
<div class="answer-sheet" :class="{ 'disabled': examSubmitted }">
|
||
<div class="answer-sheet-header">
|
||
<h4>答题卡</h4>
|
||
<span class="progress-text">
|
||
<span class="progress-current">{{ String(questions.length).padStart(2, '0') }}</span>
|
||
<span class="progress-total">/{{ String(questions.length).padStart(2, '0') }}</span>
|
||
</span>
|
||
</div>
|
||
<div class="question-grid">
|
||
<!-- 单选题 -->
|
||
<div class="question-section">
|
||
<div class="section-header">
|
||
<h5>单选 ({{ questionsByType['单选题'].length }})</h5>
|
||
</div>
|
||
<div class="question-numbers">
|
||
<div v-for="question in questionsByType['单选题']" :key="question.id"
|
||
class="answer-card-number answered" :class="{ 'disabled': examSubmitted }">
|
||
{{ String(question.id).padStart(2, '0') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 多选题 -->
|
||
<div class="question-section">
|
||
<div class="section-header">
|
||
<h5>多选 ({{ questionsByType['多选题'].length }})</h5>
|
||
</div>
|
||
<div class="question-numbers">
|
||
<div v-for="question in questionsByType['多选题']" :key="question.id"
|
||
class="answer-card-number answered" :class="{ 'disabled': examSubmitted }">
|
||
{{ String(question.id).padStart(2, '0') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 判断题 -->
|
||
<div class="question-section">
|
||
<div class="section-header">
|
||
<h5>判断 ({{ questionsByType['判断题'].length }})</h5>
|
||
</div>
|
||
<div class="question-numbers">
|
||
<div v-for="question in questionsByType['判断题']" :key="question.id"
|
||
class="answer-card-number answered" :class="{ 'disabled': examSubmitted }">
|
||
{{ String(question.id).padStart(2, '0') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 填空题 -->
|
||
<div class="question-section">
|
||
<div class="section-header">
|
||
<h5>填空 ({{ questionsByType['填空题'].length }})</h5>
|
||
</div>
|
||
<div class="question-numbers">
|
||
<div v-for="question in questionsByType['填空题']" :key="question.id"
|
||
class="answer-card-number answered" :class="{ 'disabled': examSubmitted }">
|
||
{{ String(question.id).padStart(2, '0') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 简答题 -->
|
||
<div class="question-section">
|
||
<div class="section-header">
|
||
<h5>简答 ({{ questionsByType['简答题'].length }})</h5>
|
||
</div>
|
||
<div class="question-numbers">
|
||
<div v-for="question in questionsByType['简答题']" :key="question.id"
|
||
class="answer-card-number answered" :class="{ 'disabled': examSubmitted }">
|
||
{{ String(question.id).padStart(2, '0') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 答题状态说明 -->
|
||
<div class="answer-legend">
|
||
<div class="legend-item">
|
||
<div class="legend-color legend-unanswered"></div>
|
||
<span>未答</span>
|
||
</div>
|
||
<div class="legend-item">
|
||
<div class="legend-color legend-answered"></div>
|
||
<span>已答</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 提交按钮区域 -->
|
||
<div class="submit-section">
|
||
<button class="btn-submit-exam" :class="{ 'disabled': examSubmitted }" disabled>
|
||
交卷
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右侧提交成功信息 -->
|
||
<div class="exam-content">
|
||
<div class="submit-success">
|
||
<div class="success-image">
|
||
<img src="/images/examination/submit-the-paper.png" alt="提交成功" class="success-img">
|
||
</div>
|
||
<div class="success-message">
|
||
<h3>答题卡已成功提交!预计1-3个工作日完成批阅请您耐心等候查询成绩</h3>
|
||
</div>
|
||
<div class="success-actions">
|
||
<button class="btn-return-home" @click="goHome">返回课程</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 练习结果弹窗 -->
|
||
<div class="practice-result-modal" v-if="showPracticeResultModal">
|
||
<div class="modal-overlay" @click="closePracticeResult"></div>
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h3>答题报告</h3>
|
||
<!-- <button class="close-btn" @click="closePracticeResult">×</button> -->
|
||
</div>
|
||
<div class="modal-body">
|
||
<!-- 得分展示区域 -->
|
||
<div class="score-display">
|
||
<div class="difficulty-tag">难度:4.8</div>
|
||
<div class="score-gauge">
|
||
<div class="gauge-circle">
|
||
<div class="gauge-fill" :style="{ transform: `rotate(${Math.min((finalScore / 120) * 180, 180)}deg)` }">
|
||
</div>
|
||
<div class="score-content">
|
||
<div class="score-label">我的得分</div>
|
||
<div class="score-number">{{ finalScore }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 答题详情 -->
|
||
<div class="quiz-details">
|
||
<div class="detail-item">
|
||
<span class="detail-label">答题时间:</span>
|
||
<span class="detail-value">{{ new Date().toLocaleDateString('zh-CN') }} {{ new
|
||
Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }) }}</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">答题时长:</span>
|
||
<span class="detail-value">56分23秒</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">答题总数:</span>
|
||
<span class="detail-value">{{ correctCount }}/{{ questions.length }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 得分情况 -->
|
||
<div class="score-breakdown">
|
||
<h4>得分情况</h4>
|
||
<div class="breakdown-items">
|
||
<div class="breakdown-item">
|
||
<span class="item-label">单选</span>
|
||
<div class="progress-bar">
|
||
<div class="progress-fill" style="width: 80%"></div>
|
||
</div>
|
||
<span class="item-score"><span class="item-score-number">8</span>/10</span>
|
||
</div>
|
||
<div class="breakdown-item">
|
||
<span class="item-label">多选</span>
|
||
<div class="progress-bar">
|
||
<div class="progress-fill" style="width: 60%"></div>
|
||
</div>
|
||
<span class="item-score"><span class="item-score-number">6</span>/10</span>
|
||
</div>
|
||
<div class="breakdown-item">
|
||
<span class="item-label">判断</span>
|
||
<div class="progress-bar">
|
||
<div class="progress-fill" style="width: 90%"></div>
|
||
</div>
|
||
<span class="item-score"><span class="item-score-number">9</span>/10</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 统计数据 -->
|
||
<div class="statistics">
|
||
<div class="stat-item">
|
||
<div class="stat-number orange">120</div>
|
||
<div class="stat-label">最高分</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-number red">第 <span class="stat-number-rank">10</span> 名</div>
|
||
<div class="stat-label">练习人数3892人</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-number blue">90</div>
|
||
<div class="stat-label">答对最多</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||
import { useRoute, useRouter } from 'vue-router'
|
||
|
||
const route = useRoute()
|
||
const router = useRouter()
|
||
|
||
// 路由参数
|
||
const courseId = ref(Number(route.params.courseId))
|
||
const sectionId = ref(Number(route.params.sectionId))
|
||
const courseName = ref(route.query.courseName as string || '课程名称')
|
||
const examName = ref(route.query.examName as string || '练习')
|
||
|
||
// 练习状态
|
||
const examStarted = ref(false)
|
||
const examFinished = ref(false)
|
||
const examSubmitted = ref(false) // 新增:练习已提交状态
|
||
const isPaused = ref(false) // 新增:练习是否暂停状态
|
||
const showPracticeResultModal = ref(false) // 新增:控制练习结果弹窗显示
|
||
|
||
// 确认对话框状态
|
||
const showConfirmDialog = ref(false)
|
||
const confirmDialogMessage = ref('')
|
||
const confirmDialogType = ref('') // 'complete' 或 'incomplete'
|
||
const examDuration = ref(120) // 练习时长(分钟)- 2小时
|
||
const remainingTime = ref(0) // 剩余时间(秒)
|
||
const timer = ref<NodeJS.Timeout | null>(null)
|
||
const showTimeUpDialog = ref(false) // 时间到提示对话框状态
|
||
|
||
// 题目相关
|
||
const currentQuestionIndex = ref(0)
|
||
const selectedAnswers = ref<number[]>([])
|
||
const multipleAnswers = ref<number[][]>([]) // 多选题答案
|
||
const fillAnswers = ref<string[][]>([]) // 填空题答案
|
||
const essayAnswers = ref<string[]>([]) // 简答题答案
|
||
const isMarked = ref<boolean[]>([])
|
||
const finalScore = ref(0)
|
||
const correctCount = ref(0)
|
||
|
||
// 练习相关状态
|
||
const showAnswerExplanation = ref<boolean[]>([]) // 是否显示答案解析
|
||
const questionResults = ref<boolean[]>([]) // 题目答题结果(正确/错误)
|
||
|
||
// 初始化答案数组
|
||
const initializeAnswers = () => {
|
||
selectedAnswers.value = new Array(questions.value.length).fill(undefined)
|
||
multipleAnswers.value = Array.from({ length: questions.value.length }, () => [])
|
||
fillAnswers.value = new Array(questions.value.length).fill('')
|
||
essayAnswers.value = new Array(questions.value.length).fill('')
|
||
isMarked.value = new Array(questions.value.length).fill(false)
|
||
|
||
// 初始化练习相关状态
|
||
showAnswerExplanation.value = new Array(questions.value.length).fill(false)
|
||
questionResults.value = new Array(questions.value.length).fill(false)
|
||
}
|
||
|
||
// 模拟练习题目
|
||
const questions = ref([
|
||
// 单选题 (1-10)
|
||
{
|
||
id: 1,
|
||
type: '单选题',
|
||
score: 2,
|
||
title: '信息学奥赛中常用的编程语言是什么?',
|
||
options: ['Java', 'Python', 'C++', 'JavaScript'],
|
||
correctAnswer: 2,
|
||
explanation: 'C++是信息学奥赛中最常用的编程语言,因为它具有高效的执行速度和丰富的算法库支持。'
|
||
},
|
||
{
|
||
id: 2,
|
||
type: '单选题',
|
||
score: 2,
|
||
title: '以下哪个是正确的C++变量声明?',
|
||
options: ['int 2num;', 'int num_2;', 'int num-2;', 'int num 2;'],
|
||
correctAnswer: 1,
|
||
explanation: 'C++变量名不能以数字开头,不能包含连字符,不能包含空格。int num_2; 是正确的变量声明。'
|
||
},
|
||
{
|
||
id: 3,
|
||
type: '单选题',
|
||
score: 2,
|
||
title: '在C++中,以下哪个运算符用于取地址?',
|
||
options: ['*', '&', '%', '^'],
|
||
correctAnswer: 1,
|
||
explanation: '&运算符用于取变量的地址,*运算符用于解引用指针。'
|
||
},
|
||
{
|
||
id: 4,
|
||
type: '单选题',
|
||
score: 2,
|
||
title: '以下哪个循环结构在执行前不检查条件?',
|
||
options: ['for循环', 'while循环', 'do-while循环', '以上都不是'],
|
||
correctAnswer: 2,
|
||
explanation: 'do-while循环先执行循环体,然后再检查条件,所以至少会执行一次。'
|
||
},
|
||
{
|
||
id: 5,
|
||
type: '单选题',
|
||
score: 2,
|
||
title: '在C++中,数组下标从几开始?',
|
||
options: ['1', '0', '-1', '任意数'],
|
||
correctAnswer: 1,
|
||
explanation: 'C++中数组下标从0开始,这是C++语言的标准规定。'
|
||
},
|
||
{
|
||
id: 6,
|
||
type: '单选题',
|
||
score: 2,
|
||
title: '以下哪个是C++的标准输入流?',
|
||
options: ['cout', 'cin', 'printf', 'scanf'],
|
||
correctAnswer: 1,
|
||
explanation: 'cin是C++的标准输入流,cout是标准输出流。printf和scanf是C语言的函数。'
|
||
},
|
||
{
|
||
id: 7,
|
||
type: '单选题',
|
||
score: 2,
|
||
title: '在C++中,以下哪个关键字用于定义常量?',
|
||
options: ['var', 'let', 'const', 'final'],
|
||
correctAnswer: 2,
|
||
explanation: 'const是C++中用于定义常量的关键字,var和let是其他编程语言的关键字。'
|
||
},
|
||
{
|
||
id: 8,
|
||
type: '单选题',
|
||
score: 2,
|
||
title: '以下哪个是正确的函数定义语法?',
|
||
options: ['function int add()', 'int add()', 'def add()', 'add() int'],
|
||
correctAnswer: 1,
|
||
explanation: 'C++中函数定义语法是:返回类型 函数名(参数列表)。int add()是正确的语法。'
|
||
},
|
||
{
|
||
id: 9,
|
||
type: '单选题',
|
||
score: 2,
|
||
title: '在C++中,以下哪个符号用于单行注释?',
|
||
options: ['//', '/* */', '#', '--'],
|
||
correctAnswer: 0,
|
||
explanation: '//用于单行注释,/* */用于多行注释。#是预处理指令符号。'
|
||
},
|
||
{
|
||
id: 10,
|
||
type: '单选题',
|
||
score: 2,
|
||
title: '以下哪个是C++中的逻辑与运算符?',
|
||
options: ['&', '&&', 'and', '|'],
|
||
correctAnswer: 1,
|
||
explanation: '&&是逻辑与运算符,&是位与运算符,|是位或运算符。'
|
||
},
|
||
// 多选题 (11-20)
|
||
{
|
||
id: 11,
|
||
type: '多选题',
|
||
score: 3,
|
||
title: '以下哪些是C++的基本数据类型?',
|
||
options: ['int', 'float', 'string', 'char'],
|
||
correctAnswer: [0, 1, 3],
|
||
explanation: 'int(整型)、float(浮点型)、char(字符型)是C++的基本数据类型,string是标准库类型。'
|
||
},
|
||
{
|
||
id: 12,
|
||
type: '多选题',
|
||
score: 3,
|
||
title: '以下哪些是面向对象编程的特征?',
|
||
options: ['封装', '继承', '多态', '抽象'],
|
||
correctAnswer: [0, 1, 2, 3],
|
||
explanation: '面向对象编程的四大特征是:封装(隐藏实现细节)、继承(代码重用)、多态(同一接口不同实现)、抽象(提取共同特征)。'
|
||
},
|
||
{
|
||
id: 13,
|
||
type: '多选题',
|
||
score: 3,
|
||
title: '以下哪些是C++中的循环语句?',
|
||
options: ['for', 'while', 'do-while', 'foreach'],
|
||
correctAnswer: [0, 1, 2],
|
||
explanation: 'C++中的循环语句包括for、while、do-while。foreach不是C++标准语法,但可以用范围for循环实现类似功能。'
|
||
},
|
||
{
|
||
id: 14,
|
||
type: '多选题',
|
||
score: 3,
|
||
title: '以下哪些是C++中的条件语句?',
|
||
options: ['if', 'switch', 'case', 'else'],
|
||
correctAnswer: [0, 1],
|
||
explanation: 'if和switch是C++中的条件语句,case是switch语句的一部分,else是if语句的配套关键字。'
|
||
},
|
||
{
|
||
id: 15,
|
||
type: '多选题',
|
||
score: 3,
|
||
title: '以下哪些是C++中的运算符?',
|
||
options: ['++', '--', '+=', '=='],
|
||
correctAnswer: [0, 1, 2, 3],
|
||
explanation: '这些都是C++中的运算符:++(自增)、--(自减)、+=(复合赋值)、==(相等比较)。'
|
||
},
|
||
{
|
||
id: 16,
|
||
type: '多选题',
|
||
score: 3,
|
||
title: '以下哪些是C++中的存储类?',
|
||
options: ['auto', 'static', 'extern', 'register'],
|
||
correctAnswer: [0, 1, 2, 3],
|
||
explanation: '这些都是C++中的存储类:auto(自动)、static(静态)、extern(外部)、register(寄存器)。'
|
||
},
|
||
{
|
||
id: 17,
|
||
type: '多选题',
|
||
score: 3,
|
||
title: '以下哪些是C++中的访问修饰符?',
|
||
options: ['public', 'private', 'protected', 'internal'],
|
||
correctAnswer: [0, 1, 2],
|
||
explanation: 'C++中的访问修饰符包括public(公有)、private(私有)、protected(保护)。internal是C#中的修饰符。'
|
||
},
|
||
{
|
||
id: 18,
|
||
type: '多选题',
|
||
score: 3,
|
||
title: '以下哪些是C++中的标准库?',
|
||
options: ['iostream', 'vector', 'string', 'algorithm'],
|
||
correctAnswer: [0, 1, 2, 3],
|
||
explanation: '这些都是C++标准库:iostream(输入输出)、vector(动态数组)、string(字符串)、algorithm(算法)。'
|
||
},
|
||
{
|
||
id: 19,
|
||
type: '多选题',
|
||
score: 3,
|
||
title: '以下哪些是C++中的容器?',
|
||
options: ['vector', 'list', 'map', 'set'],
|
||
correctAnswer: [0, 1, 2, 3],
|
||
explanation: '这些都是C++标准库中的容器:vector(向量)、list(链表)、map(映射)、set(集合)。'
|
||
},
|
||
{
|
||
id: 20,
|
||
type: '多选题',
|
||
score: 3,
|
||
title: '以下哪些是C++中的智能指针?',
|
||
options: ['unique_ptr', 'shared_ptr', 'weak_ptr', 'auto_ptr'],
|
||
correctAnswer: [0, 1, 2],
|
||
explanation: 'C++11引入的智能指针包括unique_ptr、shared_ptr、weak_ptr。auto_ptr已被废弃,不推荐使用。'
|
||
},
|
||
// 判断题 (21-30)
|
||
{
|
||
id: 21,
|
||
type: '判断题',
|
||
score: 1,
|
||
title: 'C++是一种面向对象的编程语言。',
|
||
options: ['正确', '错误'],
|
||
correctAnswer: 0,
|
||
explanation: 'C++支持面向对象编程,具有类、继承、多态等面向对象特性,同时也支持过程式编程。'
|
||
},
|
||
{
|
||
id: 22,
|
||
type: '判断题',
|
||
score: 1,
|
||
title: 'C++中的数组下标从1开始。',
|
||
options: ['正确', '错误'],
|
||
correctAnswer: 1,
|
||
explanation: 'C++中数组下标从0开始,这是C++语言的标准规定。'
|
||
},
|
||
{
|
||
id: 23,
|
||
type: '判断题',
|
||
score: 1,
|
||
title: 'C++中可以使用//进行单行注释。',
|
||
options: ['正确', '错误'],
|
||
correctAnswer: 0,
|
||
explanation: 'C++支持//单行注释和/* */多行注释两种注释方式。'
|
||
},
|
||
{
|
||
id: 24,
|
||
type: '判断题',
|
||
score: 1,
|
||
title: 'C++中的变量必须先声明后使用。',
|
||
options: ['正确', '错误'],
|
||
correctAnswer: 0,
|
||
explanation: 'C++中变量必须先声明后使用,这是语言的基本规则。'
|
||
},
|
||
{
|
||
id: 25,
|
||
type: '判断题',
|
||
score: 1,
|
||
title: 'C++中的函数可以重载。',
|
||
options: ['正确', '错误'],
|
||
correctAnswer: 0,
|
||
explanation: 'C++支持函数重载,允许同名函数有不同的参数列表。'
|
||
},
|
||
{
|
||
id: 26,
|
||
type: '判断题',
|
||
score: 1,
|
||
title: 'C++中的类可以继承多个父类。',
|
||
options: ['正确', '错误'],
|
||
correctAnswer: 0,
|
||
explanation: 'C++支持多重继承,一个类可以继承多个父类。'
|
||
},
|
||
{
|
||
id: 27,
|
||
type: '判断题',
|
||
score: 1,
|
||
title: 'C++中的指针可以进行算术运算。',
|
||
options: ['正确', '错误'],
|
||
correctAnswer: 0,
|
||
explanation: 'C++中指针可以进行加减运算,用于数组遍历和内存操作。'
|
||
},
|
||
{
|
||
id: 28,
|
||
type: '判断题',
|
||
score: 1,
|
||
title: 'C++中的const关键字用于定义常量。',
|
||
options: ['正确', '错误'],
|
||
correctAnswer: 0,
|
||
explanation: 'const关键字用于定义常量,防止变量被修改。'
|
||
},
|
||
{
|
||
id: 29,
|
||
type: '判断题',
|
||
score: 1,
|
||
title: 'C++中的虚函数可以实现多态。',
|
||
options: ['正确', '错误'],
|
||
correctAnswer: 0,
|
||
explanation: '虚函数是实现运行时多态的关键机制,允许在运行时确定调用哪个函数。'
|
||
},
|
||
{
|
||
id: 30,
|
||
type: '判断题',
|
||
score: 1,
|
||
title: 'C++中的析构函数可以有参数。',
|
||
options: ['正确', '错误'],
|
||
correctAnswer: 1,
|
||
explanation: '析构函数不能有参数,这是C++语言的规定。析构函数用于清理对象资源。'
|
||
},
|
||
// 填空题 (31-40)
|
||
{
|
||
id: 31,
|
||
type: '填空题',
|
||
score: 1,
|
||
title: '危险化学品生产企业应当提供危险化学品安全技术说明书,并在包装______上标贴或者悬挂与包装内危险化学品相符的化学品______。',
|
||
options: [],
|
||
blanks: [1, 2],
|
||
correctAnswer: ['包装', '标签'],
|
||
explanation: '根据危险化学品安全管理规定,企业应在包装上标贴化学品标签,确保安全信息清晰可见。'
|
||
},
|
||
{
|
||
id: 32,
|
||
type: '填空题',
|
||
score: 4,
|
||
title: 'C++中定义整型变量的关键字是______。',
|
||
options: [],
|
||
correctAnswer: 'int',
|
||
explanation: 'int是C++中用于定义整型变量的关键字,可以存储整数类型的值。'
|
||
},
|
||
{
|
||
id: 33,
|
||
type: '填空题',
|
||
score: 4,
|
||
title: 'C++中用于包含头文件的预处理指令是______。',
|
||
options: [],
|
||
correctAnswer: '#include',
|
||
explanation: '#include是C++的预处理指令,用于包含头文件,将其他文件的内容插入到当前文件中。'
|
||
},
|
||
{
|
||
id: 34,
|
||
type: '填空题',
|
||
score: 4,
|
||
title: 'C++中主函数的名称是______。',
|
||
options: [],
|
||
correctAnswer: 'main',
|
||
explanation: 'main是C++程序的入口函数,程序执行时首先调用main函数。'
|
||
},
|
||
{
|
||
id: 35,
|
||
type: '填空题',
|
||
score: 4,
|
||
title: 'C++中用于定义类的关键字是______。',
|
||
options: [],
|
||
correctAnswer: 'class',
|
||
explanation: 'class是C++中用于定义类的关键字,类是面向对象编程的基本单位。'
|
||
},
|
||
{
|
||
id: 36,
|
||
type: '填空题',
|
||
score: 4,
|
||
title: 'C++中用于动态分配内存的运算符是______。',
|
||
options: [],
|
||
correctAnswer: 'new',
|
||
explanation: 'new运算符用于动态分配内存,返回指向新分配内存的指针。'
|
||
},
|
||
{
|
||
id: 37,
|
||
type: '填空题',
|
||
score: 4,
|
||
title: 'C++中用于释放动态分配内存的运算符是______。',
|
||
options: [],
|
||
correctAnswer: 'delete',
|
||
explanation: 'delete运算符用于释放通过new分配的内存,防止内存泄漏。'
|
||
},
|
||
{
|
||
id: 38,
|
||
type: '填空题',
|
||
score: 4,
|
||
title: 'C++中用于定义命名空间的关键字是______。',
|
||
options: [],
|
||
correctAnswer: 'namespace',
|
||
explanation: 'namespace用于定义命名空间,避免名称冲突,组织代码结构。'
|
||
},
|
||
{
|
||
id: 39,
|
||
type: '填空题',
|
||
score: 4,
|
||
title: 'C++中用于继承的符号是______。',
|
||
options: [],
|
||
correctAnswer: ':',
|
||
explanation: '冒号(:)用于表示类的继承关系,如class Derived : public Base。'
|
||
},
|
||
{
|
||
id: 40,
|
||
type: '填空题',
|
||
score: 4,
|
||
title: 'C++中用于访问类成员的运算符是______。',
|
||
options: [],
|
||
correctAnswer: '.',
|
||
explanation: '点运算符(.)用于访问对象的成员,如object.member。'
|
||
},
|
||
// 简答题 (41-44)
|
||
{
|
||
id: 41,
|
||
type: '简答题',
|
||
score: 10,
|
||
title: '请简述C++中面向对象编程的三大特征。',
|
||
options: [],
|
||
correctAnswer: '封装、继承、多态',
|
||
explanation: '封装:将数据和操作数据的方法封装在类中,隐藏实现细节;继承:子类继承父类的属性和方法,实现代码重用;多态:同一接口可以有多种不同的实现方式。'
|
||
},
|
||
{
|
||
id: 42,
|
||
type: '简答题',
|
||
score: 10,
|
||
title: '请解释C++中指针和引用的区别。',
|
||
options: [],
|
||
correctAnswer: '指针是变量的地址,引用是变量的别名',
|
||
explanation: '指针:存储变量的内存地址,可以为空,可以重新赋值指向其他变量;引用:是变量的别名,必须初始化,不能重新绑定到其他变量,更安全。'
|
||
},
|
||
{
|
||
id: 43,
|
||
type: '简答题',
|
||
score: 10,
|
||
title: '请说明C++中构造函数和析构函数的作用。',
|
||
options: [],
|
||
correctAnswer: '构造函数用于初始化对象,析构函数用于清理对象',
|
||
explanation: '构造函数:在创建对象时自动调用,用于初始化对象的成员变量;析构函数:在对象销毁时自动调用,用于清理资源,如释放动态分配的内存。'
|
||
},
|
||
{
|
||
id: 44,
|
||
type: '简答题',
|
||
score: 10,
|
||
title: '请解释C++中虚函数的概念和作用。',
|
||
options: [],
|
||
correctAnswer: '虚函数实现多态,允许在运行时确定调用哪个函数',
|
||
explanation: '虚函数:在基类中声明为virtual的函数,子类可以重写;作用:实现运行时多态,通过基类指针或引用调用时,会根据实际对象类型调用相应的函数。'
|
||
}
|
||
])
|
||
|
||
// 计算属性
|
||
const currentQuestion = computed(() => questions.value[currentQuestionIndex.value])
|
||
const currentQuestionType = computed(() => currentQuestion.value?.type || '')
|
||
// const totalScore = computed(() => questions.value.reduce((sum, q) => sum + q.score, 0))
|
||
|
||
// const answeredCount = computed(() => {
|
||
// let count = 0
|
||
// questions.value.forEach((question, index) => {
|
||
// if (question.type === '单选题' || question.type === '判断题') {
|
||
// if (selectedAnswers.value[index] !== -1) count++
|
||
// } else if (question.type === '多选题') {
|
||
// if (multipleAnswers.value[index] && multipleAnswers.value[index].length > 0) count++
|
||
// } else if (question.type === '填空题') {
|
||
// const answers = fillAnswers.value[index] || []
|
||
// if (answers.some(answer => answer && answer.trim() !== '')) count++
|
||
// } else if (question.type === '简答题') {
|
||
// if (essayAnswers.value[index] && essayAnswers.value[index].trim() !== '') count++
|
||
// }
|
||
// })
|
||
// return count
|
||
// }) // 暂时注释,后续需要时再启用
|
||
// const unansweredCount = computed(() => questions.value.length - answeredCount.value)
|
||
// const markedCount = computed(() => isMarked.value.filter(marked => marked).length)
|
||
|
||
// 按题型分组统计
|
||
const questionsByType = computed(() => {
|
||
const groups = {
|
||
'单选题': questions.value.filter((q: any) => q.type === '单选题'),
|
||
'多选题': questions.value.filter((q: any) => q.type === '多选题'),
|
||
'判断题': questions.value.filter((q: any) => q.type === '判断题'),
|
||
'填空题': questions.value.filter((q: any) => q.type === '填空题'),
|
||
'简答题': questions.value.filter((q: any) => q.type === '简答题')
|
||
}
|
||
return groups
|
||
})
|
||
|
||
// 监听简答题答案变化,强制更新答题卡状态
|
||
// const essayAnswersWatcher = computed(() => {
|
||
// return essayAnswers.value.map((answer: string) => answer ? answer.trim() : '')
|
||
// })
|
||
|
||
// 简答题答案变化处理
|
||
const handleEssayAnswerChange = (questionIndex: number, value: string) => {
|
||
essayAnswers.value[questionIndex] = value
|
||
|
||
// 检查简答题答案
|
||
if (questionIndex === currentQuestionIndex.value) {
|
||
checkAnswer()
|
||
}
|
||
}
|
||
|
||
// 获取正确答案文本
|
||
const getCorrectAnswerText = () => {
|
||
const question = currentQuestion.value
|
||
if (!question) return ''
|
||
|
||
if (question.type === '单选题' || question.type === '判断题') {
|
||
return String.fromCharCode(65 + Number(question.correctAnswer))
|
||
} else if (question.type === '多选题') {
|
||
const answers = Array.isArray(question.correctAnswer) ? question.correctAnswer : [question.correctAnswer]
|
||
return answers.map((answer: string | number) => String.fromCharCode(65 + Number(answer))).join(', ')
|
||
} else if (question.type === '填空题') {
|
||
const answers = Array.isArray(question.correctAnswer) ? question.correctAnswer : [question.correctAnswer]
|
||
return answers.join(', ')
|
||
} else if (question.type === '简答题') {
|
||
return question.correctAnswer
|
||
}
|
||
return ''
|
||
}
|
||
|
||
// 获取用户答案文本
|
||
const getUserAnswerText = () => {
|
||
const question = currentQuestion.value
|
||
const currentIndex = currentQuestionIndex.value
|
||
if (!question) return ''
|
||
|
||
if (question.type === '单选题' || question.type === '判断题') {
|
||
const answer = selectedAnswers.value[currentIndex]
|
||
return answer !== undefined ? String.fromCharCode(65 + answer) : '未作答'
|
||
} else if (question.type === '多选题') {
|
||
const answers = multipleAnswers.value[currentIndex] || []
|
||
return answers.length > 0 ? answers.map((answer: number) => String.fromCharCode(65 + answer)).join(', ') : '未作答'
|
||
} else if (question.type === '填空题') {
|
||
const answers = fillAnswers.value[currentIndex] || []
|
||
return answers.length > 0 ? answers.join(', ') : '未作答'
|
||
} else if (question.type === '简答题') {
|
||
const answer = essayAnswers.value[currentIndex] || ''
|
||
return answer.trim() || '未作答'
|
||
}
|
||
return '未作答'
|
||
}
|
||
|
||
|
||
|
||
// 开始练习
|
||
const startExam = () => {
|
||
examStarted.value = true
|
||
remainingTime.value = examDuration.value * 60
|
||
startTimer()
|
||
}
|
||
|
||
// 开始计时器
|
||
const startTimer = () => {
|
||
timer.value = setInterval(() => {
|
||
remainingTime.value--
|
||
if (remainingTime.value <= 0) {
|
||
// 时间到,显示提示对话框并设置3秒后自动提交
|
||
showTimeUpAndSubmit()
|
||
if (timer.value) {
|
||
clearInterval(timer.value)
|
||
}
|
||
}
|
||
}, 1000)
|
||
}
|
||
|
||
// 暂停练习
|
||
const pausePractice = () => {
|
||
if (isPaused.value) {
|
||
// 恢复练习
|
||
isPaused.value = false
|
||
if (timer.value) {
|
||
startTimer()
|
||
}
|
||
} else {
|
||
// 暂停练习
|
||
isPaused.value = true
|
||
if (timer.value) {
|
||
clearInterval(timer.value)
|
||
timer.value = null
|
||
}
|
||
}
|
||
}
|
||
|
||
// 结束练习
|
||
const endPractice = () => {
|
||
// 显示确认对话框
|
||
confirmDialogMessage.value = '确定要结束练习吗?结束后将无法继续答题。'
|
||
confirmDialogType.value = 'end'
|
||
showConfirmDialog.value = true
|
||
}
|
||
|
||
// 计算练习结果
|
||
const calculatePracticeResult = () => {
|
||
correctCount.value = 0
|
||
finalScore.value = 0
|
||
|
||
questions.value.forEach((question, index) => {
|
||
let isCorrect = false
|
||
|
||
if (question.type === '单选题' || question.type === '判断题') {
|
||
isCorrect = selectedAnswers.value[index] === question.correctAnswer
|
||
} else if (question.type === '多选题') {
|
||
const userAnswers = multipleAnswers.value[index] || []
|
||
const correctAnswers = Array.isArray(question.correctAnswer) ? question.correctAnswer : [question.correctAnswer]
|
||
isCorrect = userAnswers.length === correctAnswers.length &&
|
||
userAnswers.every(answer => correctAnswers.includes(answer))
|
||
} else if (question.type === '填空题') {
|
||
const userAnswers = fillAnswers.value[index] || []
|
||
const correctAnswers = Array.isArray(question.correctAnswer) ? question.correctAnswer : [question.correctAnswer]
|
||
isCorrect = userAnswers.length === correctAnswers.length &&
|
||
userAnswers.every((answer, i) =>
|
||
answer && answer.trim().toLowerCase() === String(correctAnswers[i]).toLowerCase()
|
||
)
|
||
} else if (question.type === '简答题') {
|
||
const userAnswer = essayAnswers.value[index] || ''
|
||
isCorrect = userAnswer.trim() !== '' // 只要有内容就算答题了
|
||
}
|
||
|
||
if (isCorrect) {
|
||
correctCount.value++
|
||
finalScore.value += question.score
|
||
}
|
||
})
|
||
}
|
||
|
||
// 检查答案是否正确
|
||
const checkAnswer = () => {
|
||
const question = currentQuestion.value
|
||
const currentIndex = currentQuestionIndex.value
|
||
|
||
if (!question) return
|
||
|
||
let isCorrect = false
|
||
|
||
if (question.type === '单选题' || question.type === '判断题') {
|
||
isCorrect = selectedAnswers.value[currentIndex] === question.correctAnswer
|
||
} else if (question.type === '多选题') {
|
||
const userAnswers = multipleAnswers.value[currentIndex] || []
|
||
const correctAnswers = Array.isArray(question.correctAnswer) ? question.correctAnswer : [question.correctAnswer]
|
||
isCorrect = userAnswers.length === correctAnswers.length &&
|
||
userAnswers.every((answer: number) => correctAnswers.includes(answer))
|
||
} else if (question.type === '填空题') {
|
||
const userAnswers = fillAnswers.value[currentIndex] || []
|
||
const correctAnswers = Array.isArray(question.correctAnswer) ? question.correctAnswer : [question.correctAnswer]
|
||
isCorrect = userAnswers.length === correctAnswers.length &&
|
||
userAnswers.every((answer: string, i: number) =>
|
||
answer && answer.trim().toLowerCase() === String(correctAnswers[i]).toLowerCase()
|
||
)
|
||
} else if (question.type === '简答题') {
|
||
const userAnswer = essayAnswers.value[currentIndex] || ''
|
||
isCorrect = userAnswer.trim() !== '' // 只要有内容就算答题了
|
||
}
|
||
|
||
// 记录答题结果message-item
|
||
questionResults.value[currentIndex] = isCorrect
|
||
|
||
// 无论正确还是错误都显示答案解析
|
||
showAnswerExplanation.value[currentIndex] = true
|
||
}
|
||
|
||
// 选择选项(通用)
|
||
const selectOption = (optionIndex: number) => {
|
||
const question = currentQuestion.value
|
||
if (question.type === '多选题') {
|
||
const currentAnswers = multipleAnswers.value[currentQuestionIndex.value] || []
|
||
const answerIndex = currentAnswers.indexOf(optionIndex)
|
||
if (answerIndex > -1) {
|
||
// 取消选择
|
||
currentAnswers.splice(answerIndex, 1)
|
||
} else {
|
||
// 添加选择
|
||
currentAnswers.push(optionIndex)
|
||
}
|
||
multipleAnswers.value[currentQuestionIndex.value] = [...currentAnswers]
|
||
|
||
// 强制更新答题卡状态
|
||
multipleAnswers.value = [...multipleAnswers.value]
|
||
|
||
// 检查多选题答案
|
||
checkAnswer()
|
||
} else {
|
||
// 单选题、判断题
|
||
// 如果点击的是已选中的选项,则取消选择
|
||
if (selectedAnswers.value[currentQuestionIndex.value] === optionIndex) {
|
||
selectedAnswers.value[currentQuestionIndex.value] = -1
|
||
// 取消选择时也显示答案解析,让用户知道正确答案
|
||
showAnswerExplanation.value[currentQuestionIndex.value] = true
|
||
// 重新检查答案状态
|
||
checkAnswer()
|
||
} else {
|
||
selectedAnswers.value[currentQuestionIndex.value] = optionIndex
|
||
// 检查单选题答案
|
||
checkAnswer()
|
||
}
|
||
|
||
// 强制更新答题卡状态
|
||
selectedAnswers.value = [...selectedAnswers.value]
|
||
}
|
||
}
|
||
|
||
// 检查选项是否被选中
|
||
const isOptionSelected = (optionIndex: number) => {
|
||
const question = currentQuestion.value
|
||
if (question.type === '多选题') {
|
||
const currentAnswers = multipleAnswers.value[currentQuestionIndex.value] || []
|
||
return currentAnswers.includes(optionIndex)
|
||
} else {
|
||
return selectedAnswers.value[currentQuestionIndex.value] === optionIndex
|
||
}
|
||
}
|
||
|
||
// 处理复选框点击
|
||
const handleCheckboxClick = (optionIndex: number, event: Event) => {
|
||
event.stopPropagation()
|
||
selectOption(optionIndex)
|
||
}
|
||
|
||
// 上一题
|
||
const previousQuestion = () => {
|
||
if (currentQuestionIndex.value > 0) {
|
||
currentQuestionIndex.value--
|
||
}
|
||
}
|
||
|
||
// 下一题
|
||
const nextQuestion = () => {
|
||
if (currentQuestionIndex.value < questions.value.length - 1) {
|
||
currentQuestionIndex.value++
|
||
}
|
||
}
|
||
|
||
// 跳转到指定题目
|
||
const goToQuestion = (index: number) => {
|
||
currentQuestionIndex.value = index
|
||
|
||
// 如果该题目已经作答,显示答案解析
|
||
if (showAnswerExplanation.value[index]) {
|
||
// 答案解析已经显示,不需要额外操作
|
||
}
|
||
}
|
||
|
||
// 处理题号点击
|
||
const handleQuestionClick = (index: number) => {
|
||
if (!examSubmitted.value) {
|
||
goToQuestion(index)
|
||
}
|
||
}
|
||
|
||
// 检查是否所有题目都已作答
|
||
// const checkAllQuestionsAnswered = () => {
|
||
// let unansweredCount = 0
|
||
|
||
// questions.value.forEach((question, index) => {
|
||
// let isAnswered = false
|
||
|
||
// if (question.type === '单选题' || question.type === '判断题') {
|
||
// isAnswered = selectedAnswers.value[index] !== undefined && selectedAnswers.value[index] !== null
|
||
// } else if (question.type === '多选题') {
|
||
// const userAnswers = multipleAnswers.value[index] || []
|
||
// isAnswered = userAnswers.length > 0
|
||
// } else if (question.type === '填空题') {
|
||
// const userAnswer = fillAnswers.value[index] || []
|
||
// isAnswered = userAnswer.some(answer => answer && answer.trim() !== '')
|
||
// } else if (question.type === '简答题') {
|
||
// const userAnswer = essayAnswers.value[index] || ''
|
||
// isAnswered = userAnswer.trim() !== ''
|
||
// }
|
||
|
||
// if (!isAnswered) {
|
||
// unansweredCount++
|
||
// }
|
||
// })
|
||
|
||
// return unansweredCount === 0
|
||
// }
|
||
|
||
// 获取题目类型简称
|
||
const getQuestionTypeShort = (type: string) => {
|
||
switch (type) {
|
||
case '单选题': return '单选题'
|
||
case '多选题': return '多选题'
|
||
case '判断题': return '判断题'
|
||
case '填空题': return '填空题'
|
||
case '简答题': return '简答题'
|
||
default: return type
|
||
}
|
||
}
|
||
|
||
// 填空题答案处理
|
||
const getFillAnswer = (questionIndex: number, blankIndex: number) => {
|
||
if (!fillAnswers.value[questionIndex]) {
|
||
fillAnswers.value[questionIndex] = []
|
||
}
|
||
return fillAnswers.value[questionIndex][blankIndex] || ''
|
||
}
|
||
|
||
const setFillAnswer = (questionIndex: number, blankIndex: number, value: string) => {
|
||
if (!fillAnswers.value[questionIndex]) {
|
||
fillAnswers.value[questionIndex] = []
|
||
}
|
||
fillAnswers.value[questionIndex][blankIndex] = value
|
||
|
||
// 强制更新答题卡状态
|
||
fillAnswers.value = [...fillAnswers.value]
|
||
|
||
// 检查填空题答案
|
||
if (questionIndex === currentQuestionIndex.value) {
|
||
checkAnswer()
|
||
}
|
||
}
|
||
|
||
// 简答题字数统计
|
||
const getEssayLength = (questionIndex: number) => {
|
||
const answer = essayAnswers.value[questionIndex] || ''
|
||
return answer.length
|
||
}
|
||
|
||
// 获取题目状态
|
||
const getQuestionStatus = (index: number) => {
|
||
const classes = []
|
||
const question = questions.value[index]
|
||
|
||
// 当前题目
|
||
if (index === currentQuestionIndex.value) {
|
||
classes.push('current')
|
||
}
|
||
|
||
// 已答题目
|
||
let isAnswered = false
|
||
if (question.type === '单选题' || question.type === '判断题') {
|
||
isAnswered = selectedAnswers.value[index] !== undefined && selectedAnswers.value[index] !== null
|
||
} else if (question.type === '多选题') {
|
||
isAnswered = multipleAnswers.value[index] && multipleAnswers.value[index].length > 0
|
||
} else if (question.type === '填空题') {
|
||
const answers = fillAnswers.value[index] || []
|
||
isAnswered = answers.some(answer => answer && answer.trim() !== '')
|
||
} else if (question.type === '简答题') {
|
||
isAnswered = !!(essayAnswers.value[index] && essayAnswers.value[index].trim() !== '')
|
||
}
|
||
|
||
if (isAnswered) {
|
||
classes.push('answered')
|
||
|
||
// 如果已答题,根据答题结果添加正确/错误状态
|
||
if (questionResults.value[index] !== undefined) {
|
||
if (questionResults.value[index]) {
|
||
classes.push('correct')
|
||
} else {
|
||
classes.push('incorrect')
|
||
}
|
||
}
|
||
}
|
||
|
||
// 标记题目
|
||
if (isMarked.value[index]) {
|
||
classes.push('marked')
|
||
}
|
||
|
||
return classes
|
||
}
|
||
|
||
// 提交练习
|
||
// const submitExam = () => {
|
||
|
||
// // 防止重复提交
|
||
// if (examSubmitted.value) {
|
||
// return
|
||
// }
|
||
|
||
// // 检查是否所有题目都已作答
|
||
// const allAnswered = checkAllQuestionsAnswered()
|
||
|
||
// if (allAnswered) {
|
||
// // 所有题目都已作答
|
||
// confirmDialogMessage.value = '您已完成所有题目的作答,是否确认交卷?交卷后不可再修改答题卡!'
|
||
// confirmDialogType.value = 'complete'
|
||
// } else {
|
||
// // 还有未作答的题目
|
||
// confirmDialogMessage.value = '检测到您当前还有未作答题目,是否确认交卷?'
|
||
// confirmDialogType.value = 'incomplete'
|
||
// }
|
||
|
||
// showConfirmDialog.value = true
|
||
// }
|
||
|
||
// 确认交卷
|
||
const confirmSubmit = () => {
|
||
showConfirmDialog.value = false
|
||
if (confirmDialogType.value === 'end') {
|
||
// 结束练习
|
||
if (timer.value) {
|
||
clearInterval(timer.value)
|
||
timer.value = null
|
||
}
|
||
// 计算练习结果
|
||
calculatePracticeResult()
|
||
showPracticeResultModal.value = true
|
||
} else {
|
||
executeSubmit()
|
||
}
|
||
}
|
||
|
||
// 取消交卷
|
||
const cancelSubmit = () => {
|
||
showConfirmDialog.value = false
|
||
}
|
||
|
||
// 执行实际的交卷操作
|
||
const executeSubmit = () => {
|
||
if (timer.value) {
|
||
clearInterval(timer.value)
|
||
}
|
||
|
||
// 计算得分
|
||
correctCount.value = 0
|
||
finalScore.value = 0
|
||
|
||
questions.value.forEach((question, index) => {
|
||
let isCorrect = false
|
||
|
||
if (question.type === '单选题' || question.type === '判断题') {
|
||
isCorrect = selectedAnswers.value[index] === question.correctAnswer
|
||
} else if (question.type === '多选题') {
|
||
const userAnswers = multipleAnswers.value[index] || []
|
||
const correctAnswers = Array.isArray(question.correctAnswer) ? question.correctAnswer : [question.correctAnswer]
|
||
isCorrect = userAnswers.length === correctAnswers.length &&
|
||
userAnswers.every(answer => correctAnswers.includes(answer))
|
||
} else if (question.type === '填空题') {
|
||
const userAnswers = fillAnswers.value[index] || []
|
||
const correctAnswers = Array.isArray(question.correctAnswer) ? question.correctAnswer : [question.correctAnswer]
|
||
// 填空题需要所有空都填对
|
||
isCorrect = userAnswers.length === correctAnswers.length &&
|
||
userAnswers.every((answer, i) =>
|
||
answer && answer.trim().toLowerCase() === String(correctAnswers[i]).toLowerCase()
|
||
)
|
||
} else if (question.type === '简答题') {
|
||
// 简答题需要人工评分,这里暂时不计分
|
||
const userAnswer = essayAnswers.value[index] || ''
|
||
isCorrect = userAnswer.trim() !== '' // 只要有内容就算答题了
|
||
}
|
||
|
||
if (isCorrect) {
|
||
correctCount.value++
|
||
finalScore.value += question.score
|
||
}
|
||
})
|
||
|
||
// 先设置提交状态,让界面变灰
|
||
examSubmitted.value = true
|
||
console.log('练习完成,得分:', finalScore.value)
|
||
|
||
// 延迟显示提交成功页面,让用户看到灰色状态
|
||
setTimeout(() => {
|
||
examFinished.value = true
|
||
}, 1500) // 延迟1.5秒让用户看到交卷完成的灰色状态
|
||
}
|
||
|
||
// 查看答案
|
||
// const reviewAnswers = () => {
|
||
// // 这里可以跳转到答案解析页面
|
||
// alert('答案解析功能开发中...')
|
||
// }
|
||
|
||
// 返回课程
|
||
// const goBack = () => {
|
||
// router.push(`/course/${courseId.value}`)
|
||
// }
|
||
|
||
// 返回课程列表
|
||
const goHome = () => {
|
||
router.push('/course/1')
|
||
}
|
||
|
||
// 时间到后前往结果页面
|
||
const goToResult = () => {
|
||
showTimeUpDialog.value = false
|
||
executeSubmit()
|
||
}
|
||
|
||
// 显示时间到对话框并设置3秒后自动提交
|
||
const showTimeUpAndSubmit = () => {
|
||
showTimeUpDialog.value = true
|
||
setTimeout(() => {
|
||
goToResult()
|
||
}, 3000)
|
||
}
|
||
|
||
// 格式化时间
|
||
// const formatTime = (seconds: number): string => {
|
||
// const minutes = Math.floor(seconds / 60)
|
||
// const remainingSeconds = seconds % 60
|
||
// return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`
|
||
// }
|
||
|
||
// 已移除未使用的时间格式化函数,避免构建时报未使用错误
|
||
|
||
// 组件卸载时清理计时器
|
||
onUnmounted(() => {
|
||
if (timer.value) {
|
||
clearInterval(timer.value)
|
||
}
|
||
})
|
||
|
||
onMounted(() => {
|
||
console.log('练习页面加载完成')
|
||
console.log('课程ID:', courseId.value)
|
||
console.log('章节ID:', sectionId.value)
|
||
console.log('练习名称:', examName.value)
|
||
|
||
// 初始化答案数组
|
||
initializeAnswers()
|
||
|
||
// 练习页面直接开始,不需要等待用户点击
|
||
startExam()
|
||
})
|
||
|
||
// 关闭练习结果弹窗
|
||
const closePracticeResult = () => {
|
||
showPracticeResultModal.value = false
|
||
}
|
||
|
||
// 查看练习详情
|
||
// const reviewPractice = () => {
|
||
// // 这里可以实现查看练习详情的功能
|
||
// console.log('查看练习详情')
|
||
// }
|
||
</script>
|
||
|
||
<style scoped>
|
||
.exam-page {
|
||
min-height: 100vh;
|
||
background: #F6F6F6;
|
||
}
|
||
|
||
.banner-section {
|
||
position: relative;
|
||
}
|
||
|
||
.banner-text {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
text-align: center;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
|
||
.banner-text h2 {
|
||
width: 128px;
|
||
height: 39px;
|
||
font-family: AlimamaShuHeiTi, AlimamaShuHeiTi;
|
||
font-weight: bold;
|
||
font-size: 32px;
|
||
color: #000000;
|
||
line-height: 39px;
|
||
text-align: center;
|
||
font-style: normal;
|
||
}
|
||
|
||
.banner-text p {
|
||
width: 252px;
|
||
height: 20px;
|
||
font-family: PingFangSC, PingFang SC;
|
||
font-weight: 400;
|
||
font-size: 14px;
|
||
color: #000000;
|
||
line-height: 20px;
|
||
text-align: center;
|
||
font-style: normal;
|
||
}
|
||
|
||
/* 练习中心标题 */
|
||
.exam-center-header {
|
||
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
|
||
padding: 40px 0;
|
||
text-align: center;
|
||
color: white;
|
||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.banner-image {
|
||
width: 100%;
|
||
height: auto;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.center-title {
|
||
font-size: 36px;
|
||
font-weight: 600;
|
||
margin: 0 0 12px 0;
|
||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.center-subtitle {
|
||
font-size: 16px;
|
||
margin: 0;
|
||
opacity: 0.9;
|
||
}
|
||
|
||
/* 课程信息导航 */
|
||
.course-info-nav {
|
||
background: #F6F6F6;
|
||
padding: 12px 0;
|
||
}
|
||
|
||
.nav-content {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-left: 390px;
|
||
padding-right: 20px;
|
||
}
|
||
|
||
.nav-left {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 14px;
|
||
color: #666;
|
||
}
|
||
|
||
.nav-title {
|
||
color: #333333;
|
||
cursor: default;
|
||
}
|
||
|
||
.nav-item {
|
||
color: #999;
|
||
cursor: default;
|
||
}
|
||
|
||
.nav-separator {
|
||
margin: 0 8px;
|
||
color: #999;
|
||
}
|
||
|
||
.nav-right {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.question-type-item {
|
||
color: #999;
|
||
font-weight: 400;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.question-type-item.active {
|
||
color: #1D94D5;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.type-separator {
|
||
margin: 0 12px;
|
||
color: #999;
|
||
}
|
||
|
||
/* 面包屑导航 */
|
||
.container {
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
padding: 0 20px;
|
||
}
|
||
|
||
/* 练习主体布局 */
|
||
.exam-main {
|
||
padding: 0;
|
||
min-height: calc(100vh - 200px);
|
||
}
|
||
|
||
.exam-layout {
|
||
display: flex;
|
||
gap: 20px;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
/* 左侧边栏 */
|
||
.exam-sidebar {
|
||
width: 370px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* 练习时间 */
|
||
.exam-timer {
|
||
background: white;
|
||
padding: 20px;
|
||
margin-bottom: 20px;
|
||
text-align: center;
|
||
width: 370px;
|
||
height: 170px;
|
||
box-sizing: border-box;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
}
|
||
|
||
.exam-timer h4 {
|
||
text-align: left;
|
||
font-size: 16px;
|
||
color: #000;
|
||
margin: 0 0 10px 0;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.timer-display {
|
||
font-size: 34px;
|
||
font-weight: 700;
|
||
color: #1D94D5;
|
||
font-family: 'Arial', sans-serif;
|
||
letter-spacing: 4px;
|
||
background: #EEF9FF;
|
||
padding: 15px 20px 0 20px;
|
||
border-radius: 4px;
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
.timer-display span {
|
||
display: inline-block;
|
||
width: 36px;
|
||
text-align: center;
|
||
}
|
||
|
||
.timer-warning {
|
||
color: #FF5722 !important;
|
||
animation: pulse 1.5s infinite;
|
||
}
|
||
|
||
.timer-separator {
|
||
color: #1D94D5;
|
||
}
|
||
|
||
.timer-labels {
|
||
margin-top: -5px;
|
||
padding-bottom: 10px;
|
||
background-color: #EEF9FF;
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 55px;
|
||
font-size: 13px;
|
||
color: #999999;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
|
||
0%,
|
||
100% {
|
||
opacity: 1;
|
||
}
|
||
|
||
50% {
|
||
opacity: 0.7;
|
||
}
|
||
}
|
||
|
||
/* 禁用状态样式 - 根据图片调整为更浅的灰色 */
|
||
.exam-timer.disabled {
|
||
background: #f5f5f5 !important;
|
||
border: 1px solid #e0e0e0 !important;
|
||
}
|
||
|
||
.exam-timer.disabled h4 {
|
||
color: #999 !important;
|
||
}
|
||
|
||
.timer-display.disabled {
|
||
color: #999 !important;
|
||
background: #f6f6f6 !important;
|
||
border-color: #ddd !important;
|
||
}
|
||
|
||
.timer-labels.disabled {
|
||
color: #999 !important;
|
||
background-color: #f6f6f6 !important;
|
||
}
|
||
|
||
/* 确保父级计时器容器背景保持白色 */
|
||
.exam-timer.disabled {
|
||
background: white !important;
|
||
}
|
||
|
||
/* 计时器分隔符在禁用状态下变灰 */
|
||
.timer-display.disabled .timer-separator {
|
||
color: #999 !important;
|
||
}
|
||
|
||
/* 确保"剩余时间"标题在禁用状态下保持黑色 */
|
||
.exam-timer.disabled h4 {
|
||
color: #000 !important;
|
||
}
|
||
|
||
.answer-sheet.disabled {
|
||
/* background: #f5f5f5 !important; */
|
||
border: 1px solid #e0e0e0 !important;
|
||
}
|
||
|
||
.answer-sheet.disabled h4 {
|
||
color: #999 !important;
|
||
}
|
||
|
||
.answer-sheet.disabled .progress-text {
|
||
color: #999 !important;
|
||
}
|
||
|
||
.answer-sheet.disabled .progress-text .progress-current {
|
||
color: #000 !important;
|
||
}
|
||
|
||
.answer-sheet.disabled .progress-text .progress-total {
|
||
color: #999 !important;
|
||
}
|
||
|
||
.answer-sheet.disabled .section-header h5 {
|
||
color: #999 !important;
|
||
}
|
||
|
||
.answer-sheet.disabled .answer-card-number {
|
||
background: white !important;
|
||
color: #999 !important;
|
||
border-color: #999 !important;
|
||
cursor: not-allowed !important;
|
||
}
|
||
|
||
/* 确保disabled状态覆盖其他状态 */
|
||
.answer-sheet.disabled .answer-card-number.answered {
|
||
background: white !important;
|
||
color: #999 !important;
|
||
border-color: #999 !important;
|
||
}
|
||
|
||
.answer-sheet.disabled .answer-card-number.current {
|
||
background: white !important;
|
||
color: #999 !important;
|
||
border-color: #999 !important;
|
||
}
|
||
|
||
.answer-sheet.disabled .answer-card-number.marked {
|
||
background: white !important;
|
||
color: #999 !important;
|
||
border-color: #999 !important;
|
||
}
|
||
|
||
.answer-sheet.disabled .btn-submit-exam {
|
||
background-color: #999 !important;
|
||
color: #fff !important;
|
||
cursor: not-allowed !important;
|
||
}
|
||
|
||
/* 交卷后计时器样式 */
|
||
.answer-sheet.disabled .timer-display {
|
||
background-color: #f6f6f6 !important;
|
||
color: #999 !important;
|
||
}
|
||
|
||
.answer-sheet.disabled .timer-labels {
|
||
color: #999 !important;
|
||
background-color: #f6f6f6 !important;
|
||
}
|
||
|
||
.answer-sheet.disabled .timer-separator {
|
||
color: #999 !important;
|
||
}
|
||
|
||
.answer-sheet.disabled .exam-timer {
|
||
background: white !important;
|
||
}
|
||
|
||
.answer-sheet.disabled .exam-timer h4 {
|
||
color: #000 !important;
|
||
}
|
||
|
||
/* 交卷后图例样式 */
|
||
.answer-sheet.disabled .legend-color.legend-unanswered {
|
||
background: white !important;
|
||
border-color: #999 !important;
|
||
}
|
||
|
||
.answer-sheet.disabled .legend-color.legend-answered {
|
||
background: #999 !important;
|
||
border-color: #999 !important;
|
||
display: block !important;
|
||
}
|
||
|
||
.answer-sheet.disabled .legend-color.legend-answered::after {
|
||
color: white !important;
|
||
font-size: 12px !important;
|
||
font-weight: bold !important;
|
||
}
|
||
|
||
|
||
/* 图例颜色块基础样式 */
|
||
.legend-color {
|
||
width: 16px;
|
||
height: 16px;
|
||
border-radius: 2px;
|
||
margin-right: 8px;
|
||
display: inline-block;
|
||
position: relative;
|
||
}
|
||
|
||
.legend-color.legend-unanswered {
|
||
background: white;
|
||
border: 2px solid #d9d9d9;
|
||
}
|
||
|
||
.legend-color.legend-answered {
|
||
background: #999;
|
||
border: 2px solid #999;
|
||
}
|
||
|
||
.legend-color.legend-answered::after {
|
||
content: '✓';
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
color: white;
|
||
font-size: 12px;
|
||
font-weight: bold;
|
||
line-height: 1;
|
||
}
|
||
|
||
/* 答题卡 */
|
||
.answer-sheet {
|
||
background: white;
|
||
padding: 20px;
|
||
width: 370px;
|
||
height: auto;
|
||
box-sizing: border-box;
|
||
overflow-y: auto;
|
||
margin-bottom: 40px;
|
||
}
|
||
|
||
/* 答题卡标题区域 */
|
||
.answer-sheet-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 12px;
|
||
padding-bottom: 12px;
|
||
border-bottom: 1px solid #e8e8e8;
|
||
}
|
||
|
||
.answer-sheet h4 {
|
||
font-size: 16px;
|
||
color: #333;
|
||
margin: 0;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.progress-text {
|
||
font-size: 16px;
|
||
color: #0088D1;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.progress-text span {
|
||
color: #999;
|
||
}
|
||
|
||
.progress-text .progress-current {
|
||
color: #000;
|
||
}
|
||
|
||
.progress-text .progress-total {
|
||
color: #999;
|
||
}
|
||
|
||
.question-section {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.section-header {
|
||
padding-bottom: 8px;
|
||
}
|
||
|
||
.section-header h5 {
|
||
font-size: 16px;
|
||
color: #999;
|
||
margin: 0;
|
||
font-weight: 400;
|
||
}
|
||
|
||
.question-numbers {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 8px;
|
||
}
|
||
|
||
.answer-card-number {
|
||
width: 64px;
|
||
height: 40px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: 1px solid #999;
|
||
font-size: 24px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
color: #999;
|
||
}
|
||
|
||
.answer-card-number:hover {
|
||
border-color: #0088d1;
|
||
color: #0088d1;
|
||
}
|
||
|
||
.answer-card-number.current {
|
||
background: white;
|
||
color: #0088D1;
|
||
border-color: #0088D1;
|
||
}
|
||
|
||
.answer-card-number.answered {
|
||
background: #0088D1;
|
||
color: #fff;
|
||
border-color: #0088D1;
|
||
}
|
||
|
||
.answer-card-number.marked {
|
||
background: #faad14;
|
||
color: white;
|
||
border-color: #faad14;
|
||
}
|
||
|
||
.answer-card-number.correct {
|
||
background: #0088D1;
|
||
color: white;
|
||
border-color: #0088D1;
|
||
}
|
||
|
||
.answer-card-number.incorrect {
|
||
background: #FE2E2F;
|
||
color: white;
|
||
border-color: #FE2E2F;
|
||
}
|
||
|
||
/* 答题状态说明 */
|
||
.answer-legend {
|
||
margin-top: 20px;
|
||
padding-top: 20px;
|
||
border-top: 1px solid #e8e8e8;
|
||
display: flex;
|
||
gap: 20px;
|
||
}
|
||
|
||
.legend-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.legend-icon {
|
||
width: 16px;
|
||
height: 16px;
|
||
border-radius: 2px;
|
||
border: 1px solid #d9d9d9;
|
||
}
|
||
|
||
.legend-icon.unanswered {
|
||
background: #fff;
|
||
border-color: #0088D1;
|
||
}
|
||
|
||
.legend-icon.answered {
|
||
background: #e6f7ff;
|
||
border-color: #91d5ff;
|
||
}
|
||
|
||
.legend-icon.correct {
|
||
background: #0088D1;
|
||
border-color: #0088D1;
|
||
}
|
||
|
||
.legend-icon.incorrect {
|
||
background: #FE2E2F;
|
||
border-color: #FE2E2F;
|
||
}
|
||
|
||
.legend-text {
|
||
font-size: 16px;
|
||
color: #000;
|
||
}
|
||
|
||
/* 提交按钮区域 */
|
||
.submit-section {
|
||
padding-top: 20px;
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.btn-pause-practice,
|
||
.btn-end-practice {
|
||
flex: 1;
|
||
padding: 10px;
|
||
border-radius: 4px;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
border: none;
|
||
}
|
||
|
||
.btn-pause-practice {
|
||
background: #E2F5FF;
|
||
color: #0088D1;
|
||
border: 1px solid #0088D1;
|
||
}
|
||
|
||
.btn-pause-practice:hover {
|
||
background: #f0f9ff;
|
||
border-color: #0088D1;
|
||
color: #0088D1;
|
||
}
|
||
|
||
.btn-end-practice {
|
||
background: #0088D1;
|
||
color: white;
|
||
border: 1px solid #0088D1;
|
||
}
|
||
|
||
.btn-end-practice:hover {
|
||
background: #0077bb;
|
||
border-color: #0077bb;
|
||
}
|
||
|
||
/* 暂停状态下的样式 */
|
||
.btn-pause-practice.paused {
|
||
background: #FFF2E8;
|
||
color: #FA8C16;
|
||
border-color: #FA8C16;
|
||
}
|
||
|
||
.btn-pause-practice.paused:hover {
|
||
background: #FFF7E6;
|
||
border-color: #FA8C16;
|
||
color: #FA8C16;
|
||
}
|
||
|
||
.btn-submit-exam {
|
||
width: 100%;
|
||
background: #0088d1;
|
||
color: white;
|
||
border: none;
|
||
padding: 10px;
|
||
border-radius: 4px;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: background-color 0.3s;
|
||
}
|
||
|
||
.btn-submit-exam:hover {
|
||
background: #0077bb;
|
||
}
|
||
|
||
/* 右侧题目内容 */
|
||
.exam-content {
|
||
flex: 1;
|
||
background: white;
|
||
padding: 24px;
|
||
min-height: 560px;
|
||
min-width: 950px;
|
||
}
|
||
|
||
.question-card {
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.question-header {
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.question-title-info {
|
||
color: #0088D1;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.question-title-info span {
|
||
background-color: #EEF9FF;
|
||
font-size: 12px;
|
||
padding: 2px 4px;
|
||
border-radius: 10px;
|
||
}
|
||
|
||
.question-content {
|
||
flex: 1;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.question-title {
|
||
font-size: 16px;
|
||
font-weight: 400;
|
||
color: #333;
|
||
margin: 0 0 24px 0;
|
||
line-height: 1.8;
|
||
}
|
||
|
||
.question-options {
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.option-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 5px 20px;
|
||
border: none;
|
||
border-radius: 6px;
|
||
margin-bottom: 16px;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
background: #EEF9FF;
|
||
min-height: 50px;
|
||
}
|
||
|
||
.option-item:hover {
|
||
background: #EEF9FF;
|
||
}
|
||
|
||
.option-item.selected {
|
||
background: #EEF9FF;
|
||
color: #333;
|
||
}
|
||
|
||
.option-checkbox {
|
||
margin-right: 12px;
|
||
position: relative;
|
||
}
|
||
|
||
.option-checkbox input[type="checkbox"] {
|
||
width: 18px;
|
||
height: 18px;
|
||
appearance: none;
|
||
border: 1.5px solid #0088D1;
|
||
border-radius: 3px;
|
||
cursor: pointer;
|
||
position: relative;
|
||
}
|
||
|
||
.option-checkbox input[type="checkbox"]:checked {
|
||
background: #0088d1;
|
||
border-color: #0088d1;
|
||
}
|
||
|
||
.option-checkbox input[type="checkbox"]:checked::after {
|
||
content: '✓';
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
color: white;
|
||
font-size: 12px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.option-label {
|
||
font-weight: 500;
|
||
color: #333;
|
||
margin-right: 8px;
|
||
min-width: 24px;
|
||
}
|
||
|
||
.option-text {
|
||
flex: 1;
|
||
color: #333;
|
||
line-height: 1.6;
|
||
font-size: 15px;
|
||
}
|
||
|
||
.option-checkbox {
|
||
margin-right: 12px;
|
||
margin-top: 2px;
|
||
cursor: pointer;
|
||
padding: 2px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.option-checkbox input[type="checkbox"],
|
||
.option-checkbox input[type="radio"] {
|
||
width: 20px;
|
||
height: 20px;
|
||
accent-color: #0088D1;
|
||
cursor: pointer;
|
||
margin: 0;
|
||
}
|
||
|
||
.option-checkbox input[type="checkbox"]:checked,
|
||
.option-checkbox input[type="radio"]:checked {
|
||
background-color: #0088D1;
|
||
border-color: #0088D1;
|
||
}
|
||
|
||
/* 填空题样式 */
|
||
.fill-blank {
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.fill-item {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 16px;
|
||
background: #EEF9FF;
|
||
padding: 8px 20px;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.fill-number {
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
color: #333;
|
||
min-width: 24px;
|
||
}
|
||
|
||
.fill-input {
|
||
flex: 1;
|
||
padding: 8px 2PX;
|
||
border: none;
|
||
border-radius: 4px;
|
||
font-size: 16px;
|
||
transition: all 0.3s;
|
||
background: #EEF9FF;
|
||
}
|
||
|
||
.fill-input:focus {
|
||
outline: none;
|
||
box-shadow: 0 0 0 2px rgba(0, 136, 209, 0.2);
|
||
}
|
||
|
||
.fill-hint {
|
||
margin-top: 12px;
|
||
padding: 8px 0;
|
||
color: #999;
|
||
font-size: 14px;
|
||
text-align: left;
|
||
}
|
||
|
||
/* 简答题样式 */
|
||
.essay-answer {
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
/* 答案解析样式 */
|
||
.answer-explanation {
|
||
margin-top: 25px;
|
||
}
|
||
|
||
.explanation-header {
|
||
display: flex;
|
||
justify-content: flex-start;
|
||
align-items: center;
|
||
margin-bottom: 16px;
|
||
flex-wrap: wrap;
|
||
gap: 25px;
|
||
}
|
||
|
||
.result-indicator {
|
||
padding: 6px 20px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: white;
|
||
min-width: 100px;
|
||
text-align: center;
|
||
}
|
||
|
||
.result-indicator.correct {
|
||
background: #0088D1;
|
||
}
|
||
|
||
.result-indicator.incorrect {
|
||
background: #FE2E2F;
|
||
}
|
||
|
||
.answer-details {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 30px;
|
||
font-size: 14px;
|
||
min-width: 250px;
|
||
}
|
||
|
||
.correct-answer {
|
||
color: #0288D1;
|
||
font-weight: 500;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.correct-answer::before {
|
||
content: '正确答案: ';
|
||
color: #333;
|
||
font-weight: normal;
|
||
}
|
||
|
||
.user-answer {
|
||
font-weight: 500;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.user-answer::before {
|
||
content: '你的答案: ';
|
||
color: #333;
|
||
font-weight: normal;
|
||
}
|
||
|
||
/* 根据答题结果显示不同颜色 */
|
||
.answer-explanation .explanation-header .result-indicator.correct~.answer-details .user-answer {
|
||
color: #0288D1;
|
||
}
|
||
|
||
.answer-explanation .explanation-header .result-indicator.incorrect~.answer-details .user-answer {
|
||
color: #FE2E2F;
|
||
}
|
||
|
||
.explanation-content {
|
||
margin-top: 10px;
|
||
padding: 12px;
|
||
background-color: #F5F8FB;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.explanation-content h4 {
|
||
color: #999999;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.explanation-content p {
|
||
margin: 0;
|
||
color: #333;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.essay-container {
|
||
background: #EEF9FF;
|
||
padding: 20px;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.essay-textarea {
|
||
width: 100%;
|
||
padding: 12px 16px;
|
||
border: none;
|
||
border-radius: 6px;
|
||
font-size: 16px;
|
||
font-family: inherit;
|
||
resize: vertical;
|
||
min-height: 120px;
|
||
transition: all 0.3s;
|
||
background: #EEF9FF;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.essay-textarea:focus {
|
||
outline: none;
|
||
box-shadow: 0 0 0 2px rgba(0, 136, 209, 0.2);
|
||
}
|
||
|
||
.essay-footer {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-top: 12px;
|
||
padding: 0;
|
||
}
|
||
|
||
.essay-hint {
|
||
color: #999;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.essay-counter {
|
||
color: #999;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.question-navigation {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 16px;
|
||
padding-top: 20px;
|
||
}
|
||
|
||
.btn-nav {
|
||
padding: 10px 32px;
|
||
border-radius: 4px;
|
||
font-size: 16px;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
min-width: 200px;
|
||
font-weight: 400;
|
||
}
|
||
|
||
.btn-prev {
|
||
background: #E2F5FF;
|
||
color: #0088d1;
|
||
border: 1px solid #0088d1;
|
||
}
|
||
|
||
.btn-next {
|
||
background: #0088d1;
|
||
color: white;
|
||
border: 1px solid #0088d1;
|
||
}
|
||
|
||
.btn-prev:hover {
|
||
background: #f0f9ff;
|
||
border-color: #0088d1;
|
||
color: #0088d1;
|
||
}
|
||
|
||
.btn-next:hover {
|
||
background: #0077bb;
|
||
border-color: #0077bb;
|
||
}
|
||
|
||
.btn-prev:disabled {
|
||
background: #f5f5f5;
|
||
color: #bfbfbf;
|
||
border-color: #e8e8e8;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
/* 练习结果 */
|
||
.exam-result {
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
.result-card {
|
||
background: white;
|
||
border-radius: 8px;
|
||
padding: 32px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
text-align: center;
|
||
max-width: 500px;
|
||
width: 100%;
|
||
}
|
||
|
||
.result-header h2 {
|
||
font-size: 24px;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin: 0 0 20px 0;
|
||
}
|
||
|
||
.result-score {
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.score-label {
|
||
font-size: 12px;
|
||
color: #999;
|
||
}
|
||
|
||
.score-value {
|
||
font-size: 36px;
|
||
font-weight: 600;
|
||
color: #52c41a;
|
||
margin: 0 8px;
|
||
}
|
||
|
||
.score-total {
|
||
font-size: 18px;
|
||
color: #666;
|
||
}
|
||
|
||
.result-details {
|
||
border-top: 1px solid #e8e8e8;
|
||
border-bottom: 1px solid #e8e8e8;
|
||
padding: 20px 0;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.result-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.result-item:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.result-item .label {
|
||
color: #666;
|
||
}
|
||
|
||
.result-item .value {
|
||
font-weight: 500;
|
||
color: #333;
|
||
}
|
||
|
||
.result-actions {
|
||
display: flex;
|
||
gap: 16px;
|
||
justify-content: center;
|
||
}
|
||
|
||
.btn-review {
|
||
background: #1890ff;
|
||
color: white;
|
||
border: none;
|
||
padding: 12px 24px;
|
||
border-radius: 6px;
|
||
font-size: 16px;
|
||
cursor: pointer;
|
||
transition: background-color 0.3s;
|
||
}
|
||
|
||
.btn-review:hover {
|
||
background: #40a9ff;
|
||
}
|
||
|
||
|
||
|
||
/* 练习结果 */
|
||
.exam-result {
|
||
min-height: 100vh;
|
||
background: #f5f5f5;
|
||
padding: 20px 0;
|
||
}
|
||
|
||
.exam-result .exam-layout {
|
||
display: flex;
|
||
gap: 24px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.exam-result .exam-sidebar {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.exam-result .exam-content {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* 提交成功页面样式 */
|
||
.submit-success {
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 60px 40px;
|
||
text-align: center;
|
||
max-width: 970px;
|
||
width: 100%;
|
||
}
|
||
|
||
.success-image {
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.icon-container {
|
||
position: relative;
|
||
display: inline-block;
|
||
font-size: 48px;
|
||
}
|
||
|
||
.lightbulb {
|
||
font-size: 48px;
|
||
margin-right: 20px;
|
||
}
|
||
|
||
.paper {
|
||
font-size: 48px;
|
||
margin-right: 20px;
|
||
}
|
||
|
||
.checkmark {
|
||
font-size: 32px;
|
||
color: #52c41a;
|
||
position: absolute;
|
||
top: -10px;
|
||
right: -10px;
|
||
background: white;
|
||
border-radius: 50%;
|
||
width: 40px;
|
||
height: 40px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.success-message {
|
||
margin: auto;
|
||
width: 372px;
|
||
height: 50px;
|
||
font-family: PingFangSC, PingFang SC;
|
||
font-weight: 400;
|
||
font-size: 18px;
|
||
color: #000000;
|
||
line-height: 25px;
|
||
text-align: center;
|
||
font-style: normal;
|
||
margin-bottom: 40px;
|
||
}
|
||
|
||
.success-message h3 {
|
||
color: #333;
|
||
font-size: 18px;
|
||
line-height: 1.6;
|
||
margin: 0;
|
||
font-weight: 500;
|
||
padding: 0 20px;
|
||
}
|
||
|
||
.success-actions {
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
.btn-return-home {
|
||
background: #0088D1;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 4px;
|
||
padding: 11px 70px;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.btn-return-home:hover {
|
||
background: #40a9ff;
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 1024px) {
|
||
.exam-layout {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.exam-sidebar {
|
||
width: 100%;
|
||
order: 2;
|
||
}
|
||
|
||
.exam-content {
|
||
order: 1;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.question-numbers {
|
||
grid-template-columns: repeat(4, 1fr);
|
||
}
|
||
}
|
||
|
||
@media (max-width: 1024px) {
|
||
.nav-content {
|
||
margin-left: 0;
|
||
padding: 0 20px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.nav-content {
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
align-items: flex-start;
|
||
margin-left: 0;
|
||
padding: 0 20px;
|
||
}
|
||
|
||
.nav-right {
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
}
|
||
|
||
.type-separator {
|
||
margin: 0 8px;
|
||
}
|
||
|
||
.center-title {
|
||
font-size: 28px;
|
||
}
|
||
|
||
.center-subtitle {
|
||
font-size: 14px;
|
||
}
|
||
|
||
|
||
|
||
.exam-actions {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.question-navigation {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.btn-nav {
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
/* 确认对话框样式 */
|
||
.confirm-dialog-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 9999;
|
||
}
|
||
|
||
.confirm-dialog {
|
||
background: white;
|
||
border-radius: 8px;
|
||
width: 320px;
|
||
height: 168px;
|
||
border-radius: 8px;
|
||
margin: 20px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.dialog-content {
|
||
text-align: center;
|
||
}
|
||
|
||
.dialog-message {
|
||
padding: 32px 24px 0 24px;
|
||
font-size: 16px;
|
||
color: #333;
|
||
line-height: 1.6;
|
||
margin: 0 0 39px 0;
|
||
font-weight: 400;
|
||
}
|
||
|
||
.dialog-buttons {
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
.btn-cancel,
|
||
.btn-confirm {
|
||
padding: 12px 32px;
|
||
font-size: 16px;
|
||
cursor: pointer;
|
||
border: 1px solid #E5E5E5;
|
||
min-width: 80px;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.btn-cancel {
|
||
width: 50%;
|
||
background: white;
|
||
color: #666;
|
||
border-color: #ddd;
|
||
border-bottom: none;
|
||
}
|
||
|
||
.btn-cancel:hover {
|
||
background: #f5f5f5;
|
||
border-color: #ccc;
|
||
}
|
||
|
||
/* 响应式调整 */
|
||
@media (max-width: 768px) {
|
||
.confirm-dialog {
|
||
max-width: 320px;
|
||
}
|
||
|
||
.dialog-content {
|
||
padding: 24px 20px 20px 20px;
|
||
}
|
||
|
||
.dialog-message {
|
||
font-size: 14px;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.dialog-buttons {
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.btn-cancel,
|
||
.btn-confirm {
|
||
width: 100%;
|
||
padding: 12px 16px;
|
||
}
|
||
}
|
||
|
||
/* 时间到提示对话框样式 */
|
||
.time-up-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 9999;
|
||
}
|
||
|
||
.time-up-dialog {
|
||
background: white;
|
||
border-radius: 5px;
|
||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||
max-width: 320px;
|
||
min-height: 168px;
|
||
width: 90%;
|
||
margin: 20px;
|
||
padding: 20px;
|
||
position: relative;
|
||
}
|
||
|
||
.time-up-content {
|
||
margin-top: 65px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
text-align: center;
|
||
}
|
||
|
||
.time-up-image {
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.time-up-img {
|
||
position: absolute;
|
||
top: -55%;
|
||
left: 25%;
|
||
|
||
width: 178px;
|
||
height: 178px;
|
||
object-fit: contain;
|
||
}
|
||
|
||
.time-up-message {
|
||
font-size: 17px;
|
||
color: #000;
|
||
line-height: 1.6;
|
||
margin: 0;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* 时间到对话框响应式调整 */
|
||
@media (max-width: 768px) {
|
||
.time-up-dialog {
|
||
max-width: 320px;
|
||
}
|
||
}
|
||
|
||
/* 交卷后提交按钮背景变灰 - 样式已在上面定义 */
|
||
|
||
/* 练习结果弹窗样式 */
|
||
.practice-result-modal {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.3);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 9999;
|
||
}
|
||
|
||
.modal-overlay {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.3);
|
||
pointer-events: auto;
|
||
}
|
||
|
||
.modal-content {
|
||
position: relative;
|
||
background-color: #fff;
|
||
z-index: 10000;
|
||
width: 300px;
|
||
height: 520px;
|
||
overflow-y: auto;
|
||
margin: auto;
|
||
padding: 20px;
|
||
}
|
||
|
||
|
||
|
||
.modal-footer {
|
||
display: flex;
|
||
justify-content: center;
|
||
padding-top: 20px;
|
||
border-top: 1px solid #f0f0f0;
|
||
}
|
||
|
||
.practice-actions {
|
||
display: flex;
|
||
flex-direction: column;
|
||
width: 100%;
|
||
gap: 12px;
|
||
}
|
||
|
||
.btn-review-practice,
|
||
.btn-return-course {
|
||
background-color: #0088d1;
|
||
color: white;
|
||
border: none;
|
||
padding: 14px 24px;
|
||
border-radius: 6px;
|
||
font-size: 16px;
|
||
cursor: pointer;
|
||
font-weight: 500;
|
||
transition: all 0.3s ease;
|
||
box-shadow: 0 2px 4px rgba(0, 136, 209, 0.2);
|
||
}
|
||
|
||
.btn-review-practice:hover,
|
||
.btn-return-course:hover {
|
||
background-color: #0066a3;
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 8px rgba(0, 136, 209, 0.3);
|
||
}
|
||
|
||
.btn-return-home {
|
||
background-color: #f8f9fa;
|
||
border: 1px solid #e9ecef;
|
||
color: #333;
|
||
padding: 14px 24px;
|
||
border-radius: 6px;
|
||
font-size: 16px;
|
||
cursor: pointer;
|
||
font-weight: 500;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.btn-return-home:hover {
|
||
background-color: #e9ecef;
|
||
border-color: #0088d1;
|
||
color: #0088d1;
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 768px) {
|
||
.modal-content {
|
||
padding: 20px;
|
||
max-width: 350px;
|
||
}
|
||
|
||
.score-section {
|
||
gap: 12px;
|
||
}
|
||
|
||
.score-card {
|
||
min-width: 100px;
|
||
}
|
||
|
||
.score-number {
|
||
font-size: 36px;
|
||
}
|
||
|
||
.score-label {
|
||
font-size: 12px;
|
||
}
|
||
|
||
.btn-review-practice,
|
||
.btn-return-course,
|
||
.btn-return-home {
|
||
padding: 12px 20px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.modal-header h3 {
|
||
font-size: 20px;
|
||
}
|
||
|
||
.summary-header h4 {
|
||
font-size: 18px;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/* 答题报告样式 */
|
||
.modal-header {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding-bottom: 15px;
|
||
border-bottom: 1.5px solid #E6E6E6;
|
||
position: relative;
|
||
}
|
||
|
||
.modal-header h3 {
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
color: #000;
|
||
margin: 0;
|
||
text-align: center;
|
||
}
|
||
|
||
.difficulty-tag {
|
||
position: absolute;
|
||
top: 10px;
|
||
right: 0;
|
||
background: #0288D1;
|
||
color: white;
|
||
width: 56px;
|
||
height: 20px;
|
||
line-height: 20px;
|
||
border-radius: 4px 4px 4px 0;
|
||
font-size: 10px;
|
||
font-weight: 500;
|
||
text-align: center;
|
||
}
|
||
|
||
.close-btn {
|
||
background: none;
|
||
border: none;
|
||
font-size: 24px;
|
||
cursor: pointer;
|
||
color: #999;
|
||
padding: 0;
|
||
width: 30px;
|
||
height: 30px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.modal-body {
|
||
/* padding: 20px; */
|
||
}
|
||
|
||
/* 得分展示区域 */
|
||
.score-display {
|
||
display: flex;
|
||
justify-content: center;
|
||
/* margin-bottom: 10px; */
|
||
padding: 25px 0;
|
||
position: relative;
|
||
}
|
||
|
||
.score-gauge {
|
||
position: relative;
|
||
width: 180px;
|
||
height: 90px;
|
||
}
|
||
|
||
.gauge-circle {
|
||
width: 180px;
|
||
height: 90px;
|
||
position: relative;
|
||
background-image: url('/images/examination/score-bg.png');
|
||
background-size: 100% 100%;
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
}
|
||
|
||
.gauge-fill {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.score-content {
|
||
position: absolute;
|
||
top: 80%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
text-align: center;
|
||
z-index: 2;
|
||
width: 100%;
|
||
}
|
||
|
||
.score-label {
|
||
font-size: 12px;
|
||
color: #999;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.score-number {
|
||
font-size: 40px;
|
||
font-weight: bold;
|
||
color: #333;
|
||
line-height: 1;
|
||
}
|
||
|
||
/* 答题详情 */
|
||
.quiz-details {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
margin-bottom: 15px;
|
||
padding: 0 34px;
|
||
padding-bottom: 15px;
|
||
gap: 2px;
|
||
border-bottom: 1.5px solid #E6E6E6;
|
||
}
|
||
|
||
.detail-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.detail-label {
|
||
display: block;
|
||
font-size: 12px;
|
||
color: #999;
|
||
margin-right: 5px;
|
||
}
|
||
|
||
.detail-value {
|
||
font-size: 12px;
|
||
color: #999;
|
||
}
|
||
|
||
/* 得分情况 */
|
||
.score-breakdown {
|
||
margin-bottom: 15px;
|
||
padding-bottom: 15px;
|
||
border-bottom: 1.5px solid #E6E6E6;
|
||
}
|
||
|
||
.score-breakdown h4 {
|
||
font-size: 16px;
|
||
color: #333;
|
||
margin: 0 0 10px 0;
|
||
text-align: center;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.breakdown-items {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.breakdown-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.item-label {
|
||
font-size: 12px;
|
||
color: #666666;
|
||
font-weight: 500;
|
||
min-width: 25px;
|
||
}
|
||
|
||
.progress-bar {
|
||
flex: 1;
|
||
height: 8px;
|
||
background: #E2F5FF;
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
margin: 0 5px;
|
||
}
|
||
|
||
.progress-fill {
|
||
height: 100%;
|
||
background: #0288D1;
|
||
border-radius: 4px;
|
||
transition: width 0.5s ease;
|
||
}
|
||
|
||
.item-score {
|
||
font-size: 12px;
|
||
color: #666666;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.item-score-number {
|
||
color: #0288D1;
|
||
margin-right: 2px;
|
||
}
|
||
|
||
/* 统计数据 */
|
||
.statistics {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.stat-item {
|
||
flex: 1;
|
||
text-align: center;
|
||
}
|
||
|
||
.stat-number {
|
||
font-size: 24px;
|
||
margin-bottom: 8px;
|
||
line-height: 1;
|
||
}
|
||
|
||
.stat-number.orange {
|
||
color: #F38505;
|
||
}
|
||
|
||
.stat-number.red {
|
||
color: #999;
|
||
font-size: 10px;
|
||
}
|
||
|
||
.stat-number.red span {
|
||
color: #FF3636;
|
||
font-size: 24px;
|
||
}
|
||
|
||
.stat-number.blue {
|
||
color: #3F76ED;
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 10px;
|
||
color: #999;
|
||
line-height: 1.2;
|
||
}
|
||
</style>
|