当前位置: 首页 > ops >正文

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | QuizApp(交互式在线测验应用组件)

📅 我们继续 50 个小项目挑战!—— QuizApp组件

仓库地址:https://github.com/SunACong/50-vue-projects

项目预览地址:https://50-vue-projects.vercel.app/

在这里插入图片描述


使用 Vue 3<script setup> 语法和 Tailwind CSS 实用优先的样式框架,从零开始构建一个现代化、响应式的在线测验(Quiz)应用。这个应用不仅界面美观,而且功能完整,包含了题目展示、答案选择、提交验证、得分统计和重新开始等核心功能。

准备好让你的前端技能更上一层楼了吗?让我们开始吧!✨


📝 应用目标

  • 创建一个包含多道题目的交互式在线测验
  • 实现单选题的答题、提交和得分逻辑
  • 在所有题目完成后显示最终得分和重新开始按钮
  • 使用 Vue 3 的响应式系统管理应用状态
  • 利用 Tailwind CSS 快速构建现代化、响应式的 UI 界面

🔧 技术实现点

技术点描述
Vue 3 <script setup>使用 refcomputed 创建响应式数据和计算属性
v-if / v-else根据 isQuizCompleted 状态条件渲染“问题页面”或“结果页面”
v-for遍历 options 计算属性,动态生成单选题选项
v-model将单选按钮 (<input type="radio">) 与 selectedAnswer 双向绑定
@click 事件监听监听“提交”和“重新开始”按钮的点击事件
:disabled根据 selectedAnswer 是否有值,动态禁用/启用“提交”按钮
computed 计算属性动态计算 currentQuestionoptions,确保视图随数据变化自动更新
ref 响应式变量currentQuestionIndex, selectedAnswer, score, isQuizCompleted 共同管理应用状态

📚 核心数据与状态

1. 测验数据 (quizData)

这是一个包含所有题目信息的数组,每道题是一个对象,包含问题 (question)、四个选项 (a, b, c, d) 和正确答案 (correct)。

const quizData = [{question: 'Which language runs in a web browser?',a: 'Java',b: 'C',c: 'Python',d: 'JavaScript',correct: 'd', // 正确答案的键},// ... 其他题目
]

2. 响应式状态管理

状态变量类型初始值作用
currentQuestionIndexref(Number)0当前显示题目的索引
selectedAnswerref(String/null)null用户选择的答案 (a, b, c, d)
scoreref(Number)0用户的当前得分
isQuizCompletedref(Boolean)false测验是否已完成

3. 计算属性

计算属性作用
currentQuestion返回 quizData[currentQuestionIndex.value],即当前题目对象
options将当前题目的 a, b, c, d 选项转换成 { key, text } 格式的数组,便于 v-for 渲染

🖌️ 组件实现

🎨 模板结构 <template>

<template><divclass="font-poppins m-0 flex min-h-screen items-center justify-center overflow-hidden bg-gradient-to-br from-blue-100 to-purple-100 p-4"><divclass="w-full max-w-2xl overflow-hidden rounded-lg bg-white shadow-md transition-all duration-300"><!-- 问题页面 --><div v-if="!isQuizCompleted" class="p-8"><h2 class="py-4 text-center text-xl font-medium text-gray-800">{{ currentQuestion.question }}</h2><ul class="list-none p-0"><li v-for="(option, index) in options" :key="index" class="mb-4"><label class="flex cursor-pointer items-center"><inputtype="radio"name="answer":value="option.key"v-model="selectedAnswer"class="mr-3 h-5 w-5 text-purple-600" /><span class="text-gray-700">{{ option.text }}</span></label></li></ul></div><!-- 结果页面 --><div v-else class="p-8 text-center"><h2 class="mb-6 text-2xl font-bold text-gray-800">You answered {{ score }}/{{ quizData.length }} questions correctly</h2><button@click="restartQuiz"class="w-full rounded-none bg-purple-600 px-6 py-3 font-medium text-white transition-colors duration-300 hover:bg-purple-700">Restart</button></div><!-- 提交按钮 (仅在问题页面显示) --><buttonv-if="!isQuizCompleted"@click="submitAnswer"class="w-full bg-purple-600 py-3.5 font-medium text-white transition-colors duration-300 hover:bg-purple-700":disabled="!selectedAnswer">Submit</button></div></div>
</template>

模板结构清晰地分为三个逻辑区域:

  1. 外层容器 (div)

    • flex min-h-screen items-center justify-center:使用 Flexbox 将测验卡片在视口中水平和垂直居中。
    • bg-gradient-to-br from-blue-100 to-purple-100:创建一个从左下到右上、由浅蓝到浅紫的渐变背景,美观且现代。
    • font-poppins:使用 Poppins 字体(需在项目中引入)。
    • p-4:内边距,确保在小屏幕上内容不会紧贴边缘。
  2. 测验卡片 (div)

    • max-w-2xl:限制卡片最大宽度,保证在大屏幕上不会过宽。
    • bg-white:白色背景,与渐变背景形成对比。
    • rounded-lg / shadow-md:圆角和阴影,提升卡片的立体感和美观度。
    • transition-all duration-300:为卡片添加平滑的过渡效果(虽然本例中变化不明显,但为未来扩展留有余地)。
  3. 内容区域

    • 问题页面 (v-if="!isQuizCompleted")
      • h2 显示当前问题。
      • ulv-for 遍历 options,为每个选项创建一个 label
      • label 内包含 input[type="radio"]span,实现点击文字也能选中单选框的友好交互 (cursor-pointer)。
      • v-model="selectedAnswer" 将选中的值绑定到 selectedAnswer
    • 结果页面 (v-else)
      • 显示最终得分 You answered X/Y questions correctly
      • “重新开始”按钮调用 restartQuiz 方法。
    • 提交按钮
      • 仅在问题页面显示 (v-if="!isQuizCompleted")。
      • :disabled="!selectedAnswer":当 selectedAnswernull(即未选择任何选项)时,按钮为禁用状态,防止用户提交空答案。
      • 点击后触发 submitAnswer 方法。

💻 脚本逻辑 <script setup>

<script setup>import { ref, computed } from 'vue'// 测验数据const quizData = [// ... 题目数据]// 响应式状态const currentQuestionIndex = ref(0)const selectedAnswer = ref(null)const score = ref(0)const isQuizCompleted = ref(false)// 计算属性 - 当前问题const currentQuestion = computed(() => quizData[currentQuestionIndex.value])// 计算属性 - 选项列表const options = computed(() => {if (!currentQuestion.value) return []return [{ key: 'a', text: currentQuestion.value.a },{ key: 'b', text: currentQuestion.value.b },{ key: 'c', text: currentQuestion.value.c },{ key: 'd', text: currentQuestion.value.d },]})// 提交答案const submitAnswer = () => {if (!selectedAnswer.value) return // 防御性编程:未选择则不执行// 检查答案是否正确if (selectedAnswer.value === currentQuestion.value.correct) {score.value++}// 移动到下一题或结束测验if (currentQuestionIndex.value < quizData.length - 1) {currentQuestionIndex.value++selectedAnswer.value = null // 重置选择,为下一题准备} else {isQuizCompleted.value = true // 所有题目完成}}// 重新开始测验const restartQuiz = () => {currentQuestionIndex.value = 0selectedAnswer.value = nullscore.value = 0isQuizCompleted.value = false}
</script>

脚本部分是应用的“大脑”:

  • submitAnswer 方法

    1. 首先检查是否有选择 (selectedAnswer.value)。
    2. 比较 selectedAnswercurrentQuestion.correct,如果匹配则 score 加 1。
    3. 检查是否还有下一题 (currentQuestionIndex < quizData.length - 1)。如果有,索引加 1 并重置 selectedAnswer;如果没有,则将 isQuizCompleted 设为 true,触发视图切换到结果页面。
  • restartQuiz 方法

    • 将所有状态变量重置为初始值,实现测验的重新开始。
  • computed 属性的优势

    • currentQuestionoptions 会自动监听 currentQuestionIndex 的变化。当 currentQuestionIndex 改变时,这两个计算属性会立即重新计算,确保 template 中显示的是正确的题目和选项,无需手动更新。

🎨 Tailwind CSS 样式重点

类名作用
font-poppins使用 Poppins 字体
m-0 / p-4 / p-8 / py-4 / px-6 / py-3.5外边距和内边距
flex / items-center / justify-centerFlexbox 布局
min-h-screen最小高度为视口高度
overflow-hidden隐藏溢出内容
bg-gradient-to-br from-blue-100 to-purple-100渐变背景
w-full / max-w-2xl / min-w-[320px]宽度设置
overflow-hidden隐藏溢出
rounded-lg圆角
bg-white / bg-purple-600背景颜色
shadow-md阴影
transition-all / transition-colors / duration-300过渡效果和持续时间
text-center文字居中
text-xl / text-2xl / text-gray-800 / text-gray-700 / text-white文字大小和颜色
font-medium / font-bold / font-semibold字体粗细
list-none移除列表默认样式
cursor-pointer鼠标指针为手型
h-5 / w-5固定单选框尺寸
text-purple-600单选框选中颜色
mr-3 / mb-4 / mb-3 / mb-6 / mt-2外边距
hover:bg-purple-700悬停时背景色变深
rounded-none移除按钮圆角(可选)
disabled:opacity-50 cursor-not-allowed(虽然代码中未显式写出,但通常会添加)禁用状态样式

📁 常量定义 + 组件路由

constants/index.js 添加组件预览常量:

{id: 46,title: 'QuizApp',image: 'https://50projects50days.com/img/projects-img/46-quiz-app.png',link: 'QuizApp',},

router/index.js 中添加路由选项:

{path: '/QuizApp',name: 'QuizApp',component: () => import('@/projects/QuizApp.vue'),},

🏁 总结

通过这篇教程,我们成功构建了一个功能完整、界面美观的在线测验应用。我们深入实践了 Vue 3 Composition API 的核心概念,如 refcomputed<script setup>,并充分利用了 Tailwind CSS 的实用类来快速搭建 UI。

这个测验应用是一个很好的起点,可以在此基础上进行很多有趣的扩展:

  • 加载动画:在题目切换时添加淡入淡出 (fade-in) 或滑动动画。
  • 反馈机制:提交答案后,立即显示“正确”或“错误”的反馈(例如,正确选项变绿,错误选项变红)。
  • 计时器:为每道题或整个测验添加倒计时功能。
  • 进度条:显示当前进度 (Question 2 of 4)。
  • 数据持久化:使用 localStorage 保存用户的最高分。
  • 动态数据源:从 API 接口动态获取题目数据,而不是硬编码在组件中。
  • 多种题型:支持多选题、判断题等。
  • 结果详情:在结果页面展示每道题的答题情况(正确/错误)。

👉 下一篇,我们将完成TestimonialBoxSwitcher组件,一个用于展示用户 testimonial(评价、推荐语)的组件,核心功能是实现不同评价内容的切换展示。。🚀

感谢阅读,欢迎点赞、收藏和分享 😊

http://www.xdnf.cn/news/16853.html

相关文章:

  • Mujoco(MuJoCo,全称Multi - Joint dynamics with Contact)一种高性能的物理引擎
  • 基于Postman进行http的请求和响应
  • linux基本系统服务——DNS服务
  • 【嵌入式汇编基础】-ARM架构基础(三)
  • 宝塔配置文件缺失导致无法正常启动
  • Java 集合框架: LinkedHashSet
  • 进程 Vs 线程
  • 【OpenGL】LearnOpenGL学习笔记01 - 环境配置、窗口创建
  • Flask + YARA-Python*实现文件扫描功能
  • 开源列式分布式数据库clickhouse
  • 深入 Go 底层原理(十三):interface 的内部表示与动态派发
  • Redisson高并发实战:Netty IO线程免遭阻塞的守护指南
  • 算法提升之数学(快速幂+逆元求法)
  • 【20min 急速入门】使用Demucs进行音轨分离
  • Redis7 String类型数据
  • 【iOS】KVO
  • MyBatisPlus之CRUD接口(IService与BaseMapper)
  • 28Rsync免密传输与定时备份
  • 关于Web前端安全防御XSS攻防的几点考虑
  • Spring Boot 全 YAML 配置 Liquibase 教程
  • C++之vector类的代码及其逻辑详解 (中)
  • DockerFile文件执行docker bulid自动构建镜像
  • CMake指令:mark_as_advanced
  • Python序列去重高级指南:保持顺序的高效去重技术
  • 错误: 找不到或无法加载主类 原因: java.lang.ClassNotFoundException
  • 云原生三剑客:Kubernetes + Docker + Spring Cloud 实战指南与深度整合
  • 分类任务当中常见指标 F1分数、recall、准确率分别是什么含义
  • 类似 Pixso 但更侧重「网页 / 软件界面设计」「前后端可视化开发」的工具
  • 【贪心】P11112 [ROI 2024] 机器人物流 (Day 1)|普及+
  • 基于python多光谱遥感数据处理、图像分类、定量评估及机器学习方法应用