3368 lines
85 KiB
Vue
Raw Normal View History

2025-08-16 19:51:39 +08:00
<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>/{{
2025-08-18 12:03:30 +08:00
String(questions.length).padStart(2, '0') }}</span></span>
2025-08-16 19:51:39 +08:00
</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">
2025-08-18 12:03:30 +08:00
<h3>答题报告</h3>
<!-- <button class="close-btn" @click="closePracticeResult">&times;</button> -->
2025-08-16 19:51:39 +08:00
</div>
<div class="modal-body">
2025-08-18 12:03:30 +08:00
<!-- 得分展示区域 -->
<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>
2025-08-16 19:51:39 +08:00
</div>
2025-08-18 12:03:30 +08:00
</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>
2025-08-16 19:51:39 +08:00
</div>
2025-08-18 12:03:30 +08:00
<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>
2025-08-16 19:51:39 +08:00
</div>
2025-08-18 12:03:30 +08:00
<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>
2025-08-16 19:51:39 +08:00
</div>
</div>
</div>
2025-08-18 12:03:30 +08:00
<!-- 统计数据 -->
<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>
2025-08-16 19:51:39 +08:00
</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 {
2025-08-18 12:03:30 +08:00
executeSubmit()
2025-08-16 19:51:39 +08:00
}
}
// 取消交卷
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('答案解析功能开发中...')
// }
// 返回课程
2025-08-18 22:09:42 +08:00
// const goBack = () => {
// router.push(`/course/${courseId.value}`)
// }
2025-08-16 19:51:39 +08:00
// 返回课程列表
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()
// 练习页面直接开始,不需要等待用户点击
2025-08-18 12:03:30 +08:00
startExam()
2025-08-16 19:51:39 +08:00
})
// 关闭练习结果弹窗
const closePracticeResult = () => {
showPracticeResultModal.value = false
}
// 查看练习详情
2025-08-18 22:09:42 +08:00
// const reviewPractice = () => {
// // 这里可以实现查看练习详情的功能
// console.log('查看练习详情')
// }
2025-08-16 19:51:39 +08:00
</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 {
2025-08-18 12:03:30 +08:00
font-size: 12px;
color: #999;
2025-08-16 19:51:39 +08:00
}
.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;
2025-08-18 12:03:30 +08:00
height: 520px;
2025-08-16 19:51:39 +08:00
overflow-y: auto;
margin: auto;
2025-08-18 12:03:30 +08:00
padding: 20px;
2025-08-16 19:51:39 +08:00
}
.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;
}
}
2025-08-18 12:03:30 +08:00
/* 答题报告样式 */
.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;
}
2025-08-16 19:51:39 +08:00
</style>