【发布时间】:2022-01-09 07:47:12
【问题描述】:
我正在使用 Vue 3 和 Bootstrap 4 构建一个测验应用程序。测验可以双向导航(有nextQuestion 方法和prevQuestion 方法)。
理想情况下,我会在 Trivia API 的帮助下将它变成一个全栈应用程序,我无法修改它,所以我将拥有在 Vue 中做尽可能多的逻辑。
const quizApp = {
data() {
return {
isScoreVisible: false,
questionCount: 0,
selectedAnswer: "",
correctAnswers: [],
results: [{
question: 'The book "The Little Prince" was written by...',
correct_answer: "Antoine de Saint-Exupéry",
selectedAnswer: "",
incorrect_answers: [
"Miguel de Cervantes Saavedra",
"Jane Austen",
"F. Scott Fitzgerald"
]
},
{
question: "Which novel by John Grisham was conceived on a road trip to Florida while thinking about stolen books with his wife?",
selectedAnswer: "",
correct_answer: "Camino Island",
incorrect_answers: ["Rogue Lawyer", "Gray Mountain", "The Litigators"]
},
{
question: 'In Terry Pratchett\'s Discworld novel "Wyrd Sisters", which of these are not one of the three main witches?',
selectedAnswer: "",
correct_answer: "Winny Hathersham",
incorrect_answers: [
"Granny Weatherwax",
"Nanny Ogg",
"Magrat Garlick"
]
}
]
};
},
methods: {
nextQuestion() {
if (this.questionCount < this.results.length - 1) {
this.questionCount++;
}
// Clear selected answer as you advance through the questions
this.selectedAnswer = "";
},
prevQuestion() {
if (this.questionCount >= 1) {
this.questionCount--;
}
},
checkAnswer(answer) {
let answersList = document.querySelectorAll("ul.answers li");
answersList.forEach(function(item) {
item.removeAttribute("class");
});
// check if the clicked anwser is equal to the correct answer
this.selectedAnswer = answer;
if (answer == this.results[this.questionCount].correct_answer) {
// Update the correct answers arary (make sure thare are no duplicates)
if (this.correctAnswers.indexOf(answer) === -1) {
this.correctAnswers.push(answer);
}
// Add correct answer class
event.target.classList.add("text-white", "bg-success");
} else {
// Add incorrect answer
event.target.classList.add("text-white", "bg-danger");
}
},
showScore() {
this.isScoreVisible = true;
},
resetQuiz() {
this.questionCount = 0;
this.correctAnswers = [];
this.selectedAnswer = "";
this.isScoreVisible = false;
},
removeElementFromArray(arr, elm) {
return arr.filter((el) => el !== elm);
},
shuffle(arr) {
var len = arr.length;
var d = len;
var array = [];
var k, i;
for (i = 0; i < d; i++) {
k = Math.floor(Math.random() * len);
array.push(arr[k]);
arr.splice(k, 1);
len = arr.length;
}
for (i = 0; i < d; i++) {
arr[i] = array[i];
}
return arr;
}
},
computed: {
answers() {
let incorrectAnswers = this.results[this.questionCount].incorrect_answers;
let correctAnswer = this.results[this.questionCount].correct_answer;
// return all answers, shuffled
return this.shuffle(incorrectAnswers.concat(correctAnswer));
},
showScoreBtn() {
return (
this.questionCount + 1 == this.results.length &&
this.selectedAnswer != ""
);
}
}
};
Vue.createApp(quizApp).mount("#quiz_app");
#quiz_app {
height: 100vh;
}
.container {
flex: 1;
}
.logo {
width: 30px;
}
.nav-item {
width: 100%;
}
.category-name {
font-size: 2rem !important;
font-weight: 400;
text-align: center;
}
.questions .card {
width: 100%;
min-height: 330px;
}
.questions .card-header {
padding-top: 1.25rem;
padding-bottom: 1.25rem;
}
.questions .card-footer {
padding-top: 0.7rem;
padding-bottom: 0.7rem;
}
.answers li {
cursor: pointer;
display: block;
padding: 7px 15px;
margin-bottom: 5px;
border-radius: 6px;
border: 1px solid rgba(0, 0, 0, 0.1);
background: #fff;
}
.answers li:last-child {
margin-bottom: 0;
}
.answers li:hover {
background: #fafafa;
}
.pager {
list-style-type: none;
margin: 0;
padding: 0;
display: flex;
justify-content: space-between;
}
.pager li > a {
display: inline-block;
padding: 5px 10px;
text-align: center;
width: 100px;
background-color: #fff;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 999px;
text-decoration: none !important;
color: #fff;
}
.pager li > a.disabled {
pointer-events: none;
background-color: #9d9d9d !important;
}
.results a,
.results a:hover {
color: #fff;
font-weight: 500;
border-radius: 999px;
}
.results a .fas {
font-size: 85%;
margin-right: 3px;
}
@media (min-width: 768px) {
.nav-item {
width: auto;
}
.questions .card {
width: 67%;
}
}
@media (min-width: 992px) {
.questions .card {
width: 50%;
}
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://kit.fontawesome.com/3f9baea05e.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/vue@next"></script>
<div id="quiz_app" class="container d-flex flex-column justify-content-center my-3">
<h2 class="display-4 category-name">Books</h2>
<div class="questions d-flex flex-column align-items-center">
<div v-if="results.length" class="card shadow-sm" :class="{'bg-light': isScoreVisible}">
<div v-if="isScoreVisible" class="results my-auto text-center">
<h5 class="mb-3">Your result: {{correctAnswers.length}} / {{results.length}}</h5>
<a href="#" class="btn btn-md bg-dark" @click="resetQuiz">
<i class="fas fa-sync-alt"></i> Play again
</a>
</div>
<template v-else>
<div class="card-header bg-light h6">
{{results[questionCount].question}}
</div>
<div class="card-body">
<ul class="answers list-unstyled m-0">
<li v-for="answer in answers" :key="answer" @click="checkAnswer(answer)">
{{answer}}
</li>
</ul>
</div>
<div class="card-footer bg-white">
<ul class="pager">
<li><a href="#" @click="prevQuestion" class="bg-dark" :class="{'disabled' : questionCount == 0}">Previous</a></li>
<li class="d-flex align-items-center text-secondary font-weight-bold small">
Question {{questionCount + 1}} of {{results.length}}
</li>
<li v-if="showScoreBtn">
<a href="#" class="bg-dark" @click="showScore">Score</a>
</li>
<li v-else>
<a href="#" class="bg-dark" :class="{'disabled' : selectedAnswer == ''}" @click="nextQuestion">Next</a>
</li>
</ul>
</div>
</template>
</div>
</div>
</div>
问题
如果我在测验问题中来回导航,我已经选择的答案不会保留。
问题
- 是否有可靠的方法来仅使用 JavaScript 来保留给定的答案,或者我应该使用将它们保存在数据库中的 API?
- 如果我需要 API,Trivia API 会完成这项工作(还是只允许获取请求)?
【问题讨论】:
-
我要做的是创建一个 API。但是如果你仍然希望它被永久保存,你可以使用 LocalStorage 或 Cookies 来拥有一个持久的数据。如果只需要时间状态,可以使用 Vuex。
-
“时间状态”是什么意思?
-
当您刷新页面 (F5) 时,数据将返回初始状态并且不保存。我的意思是暂时的,因为它们只有在你不重新加载 vue APP 时才会存在
标签: javascript vue.js vuejs3