需求
实现一个答题组件,点击正确的选项,该选项背景变绿色;点击错误的选项,该选项背景变红色。不管点击了什么选项,延迟一秒后切换下一题。
每次出题,从题库中选随机选择一道用户此次进入这个页面后还没有做过的题目,如果此次进入这个页面把所有题都做了,则重置,重新开始随机选题。
页面结构
html
<div class="question">
{{questions[selectedQuestionIndex].question}}
</div>
<div class="choices">
<div
class="choice"
v-for="(item, index) in questions[selectedQuestionIndex].choices"
:key="item.content"
// 当点击了当前的选项,则开始处理背景颜色
:style="{ background: selectedChoiceIndex === index ? feedbackColor : '' }" // 用来显示点击之后的背景颜色
@click="handleChoice(index)"
>
<span>{{item.index}}</span>
<span>{{item.content}}</span>
</div>
</div>
数据结构
javascript
data() {
return {
selectedQuestionIndex: 0, // 当前显示的问题的索引
selectedChoiceIndex: null, // 记录用户当前选择的选项索引
feedbackColor: '', // 记录选项的反馈颜色
answeredQuestions: new Set(), // 已回答题目索引
questions: [ // 题库
// 1
{
question: '下列哪一项不是森林生态系统服务的一部分?',
choices: [
{ index: 'A: ', content: '氧气生产' },
{ index: 'B: ', content: '水土保持' },
{ index: 'C: ', content: '提供化石燃料' },
{ index: 'D: ', content: '生物多样性维持' },
],
correctChoiceIndex: 2
},
// 2
{
question: '碳汇是指什么?',
choices: [
{ index: 'A: ', content: '大量排放二氧化碳的地方' },
{ index: 'B: ', content: '能够吸收并储存二氧化碳的自然系统' },
{ index: 'C: ', content: '一种工业过程,用于减少温室气体' },
{ index: 'D: ', content: '用于监测大气中二氧化碳水平的技术' },
],
correctChoiceIndex: 1
},
]
};
},
方法解析
有三个方法:
handleChoice
:根据用户的选择决定显示什么背景颜色;切换下一题nextQuestion
:决定下一题选哪一道;重置选择状态和选项背景颜色resetQuiz
:重新开始测试
javascript
handleChoice(choiceIndex) {
// 判断是否答对
const currentQuestion = this.questions[this.selectedQuestionIndex]; // 获取当前问题
// 判断用户点击的是否是正确选项,计算对应的背景颜色
if (choiceIndex === currentQuestion.correctChoiceIndex) {
this.feedbackColor = '#adce74'; // 绿色表示答对
} else {
this.feedbackColor = '#ea6458'; // 红色表示答错
}
// 设置选中选项索引
// 一旦selectedChoiceIndex有值了,那么页面上就会渲染背景颜色
this.selectedChoiceIndex = choiceIndex;
// 延迟一段时间切换到下一题
setTimeout(() => {
this.nextQuestion();
}, 1000); // 延迟1秒
},
javascript
nextQuestion() {
// 将当前题目加入已回答集合
this.answeredQuestions.add(this.selectedQuestionIndex);
// 筛选未回答的题目索引
const unansweredIndexes = this.questions
.map((_, index) => index) // 得到一个全是问题编号的数组
.filter(index => !this.answeredQuestions.has(index)); // 过滤出不在answeredQuestions里面的问题的序号
if (unansweredIndexes.length > 0) {
// 随机选取一道未回答的题目
const randomIndex = Math.floor(Math.random() * unansweredIndexes.length);
this.selectedQuestionIndex = unansweredIndexes[randomIndex];
} else {
// 如果没有未回答的题目,重置已回答集合并重新开始
this.answeredQuestions.clear();
this.resetQuiz();
}
// 重置选中状态
this.selectedChoiceIndex = null;
this.feedbackColor = '';
},
javascript
resetQuiz() {
// 重新随机选择一个题目
this.selectedQuestionIndex = Math.floor(Math.random() * this.questions.length);
}
组件代码
vue
<template>
<div class="question-container">
<div class="question">
{{questions[selectedQuestionIndex].question}}
</div>
<div class="choices">
<div
class="choice"
v-for="(item, index) in questions[selectedQuestionIndex].choices"
:key="item.content"
:style="{ background: selectedChoiceIndex === index ? feedbackColor : '' }"
@click="handleChoice(index)"
>
<span>{{item.index}}</span>
<span>{{item.content}}</span>
</div>
</div>
</div>
</template>
<script>
import {mapMutations} from "vuex";
export default {
name: "questionDetail",
data() {
return {
selectedQuestionIndex: 0,
selectedChoiceIndex: null, // 记录用户当前选择的选项索引
feedbackColor: '', // 记录选项的反馈颜色
answeredQuestions: new Set(), // 已回答题目索引
questions: [
// 1
{
question: '下列哪一项不是森林生态系统服务的一部分?',
choices: [
{ index: 'A: ', content: '氧气生产' },
{ index: 'B: ', content: '水土保持' },
{ index: 'C: ', content: '提供化石燃料' },
{ index: 'D: ', content: '生物多样性维持' },
],
correctChoiceIndex: 2
},
// 2
{
question: '碳汇是指什么?',
choices: [
{ index: 'A: ', content: '大量排放二氧化碳的地方' },
{ index: 'B: ', content: '能够吸收并储存二氧化碳的自然系统' },
{ index: 'C: ', content: '一种工业过程,用于减少温室气体' },
{ index: 'D: ', content: '用于监测大气中二氧化碳水平的技术' },
],
correctChoiceIndex: 1
},
]
};
},
methods: {
...mapMutations(['addQuestion']),
handleChoice(choiceIndex) {
// 判断是否答对
const currentQuestion = this.questions[this.selectedQuestionIndex];
if (choiceIndex === currentQuestion.correctChoiceIndex) {
this.feedbackColor = '#adce74'; // 绿色表示答对
this.addQuestion();
} else {
this.feedbackColor = '#ea6458'; // 红色表示答错
}
// 设置选中选项索引
this.selectedChoiceIndex = choiceIndex;
// 延迟一段时间切换到下一题
setTimeout(() => {
this.nextQuestion();
}, 1000); // 延迟1秒
},
nextQuestion() {
// 将当前题目加入已回答集合
this.answeredQuestions.add(this.selectedQuestionIndex);
// 筛选未回答的题目索引
const unansweredIndexes = this.questions
.map((_, index) => index)
.filter(index => !this.answeredQuestions.has(index));
if (unansweredIndexes.length > 0) {
// 随机选取一道未回答的题目
const randomIndex = Math.floor(Math.random() * unansweredIndexes.length);
this.selectedQuestionIndex = unansweredIndexes[randomIndex];
} else {
// 如果没有未回答的题目,重置已回答集合并重新开始
this.answeredQuestions.clear();
this.resetQuiz();
}
// 重置选中状态
this.selectedChoiceIndex = null;
this.feedbackColor = '';
},
resetQuiz() {
// 重新随机选择一个题目
this.selectedQuestionIndex = Math.floor(Math.random() * this.questions.length);
}
}
};
</script>
<style scoped lang="scss">
.question-container {
width: 98%;
height: calc(100vh - 210px);
margin-top: 15px;
padding: 0 15px 0 15px;
//border: 1px red solid;
overflow: auto;
.question {
font-family: 'SanJinSong-Cu', serif;
color: #7c9a92;
font-size: 35px;
}
.choices {
margin-top: 30px;
display: flex;
flex-direction: column;
gap: 15px;
.choice {
box-sizing: border-box;
padding: 10px 20px;
color: white;
font-family: 'SanJinSong-Xi', serif;
font-size: 25px;
width: 100%;
min-height: 80px;
display: flex;
align-items: center;
border-radius: 8px;
background: linear-gradient(to right, #1F6D5E, #43D6B9);
cursor: pointer;
transition: background 0.3s ease;
}
}
}
</style>