Vue3+ts实现答题卡

868 阅读1分钟

设计一个答题卡需要考虑用户体验、功能性和界面设计等多个方面,通过合理的数据结构和简单易用的组件设计,可以提升答题效率和用户的参与感。以下是一个基础参考,可以基于此基础添加扩展各种答题效果来增强体验感~

实现效果

image.png

代码演示

代码参考

<template>
  <div class="subject-container">
    <header>
      <p>
        共计 {{ totalQuestions }} 题,已做 {{ answeredQuestions }} 题,完成度
        {{ completionPercentage }}%,考试时间 1 小时 30 分钟,距离考试结束还有
        01:29:28
      </p>
      <button @click="handleSubmit">提交</button>
    </header>
    <!-- 试卷题目 -->
    <div class="questions-list">
      <div
        v-for="(item, index) in questions"
        :key="index"
        class="question-item"
      >
        <div class="question-header">
          <span>{{ index + 1 }}. {{ item.question }} (单选题)</span>
          <span>本题 3 分</span>
        </div>
        <div class="question-anwswer">
          <label v-for="row in item.options" :key="row">
            <input
              type="radio"
              :name="'question-' + index"
              :value="row"
              v-model="userAnswers[index]"
            />
            {{ row }}
          </label>
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { ref, computed } from 'vue'

interface Question {
  question: string
  options: string[]
  answer: string
}

// 模拟题目数据 实际情况是接口返回 也可以增加各种字段满足业务需求
const questions: Question[] = [
  {
    question: 'Vue.js 是什么?',
    options: ['框架', '库', '工具', '语言'],
    answer: '框架',
  },
  {
    question: 'JavaScript 是静态类型语言吗?',
    options: ['是', '否'],
    answer: '否',
  },
  {
    question: 'TypeScript 是 JavaScript 的超集吗?',
    options: ['是', '否'],
    answer: '是',
  },
]

const userAnswers = ref<string[]>(new Array(questions.length).fill(''))

const totalQuestions = questions.length
const answeredQuestions = computed(
  () => userAnswers.value.filter((answer) => answer !== '').length
)
const completionPercentage = computed(() =>
  Math.round((answeredQuestions.value / totalQuestions) * 100)
)

const handleSubmit = () => {
  // 提交答题逻辑
  alert('提交成功!')
}
</script>

<style scoped>
.subject-container {
  width: 80%;
  margin: auto;
  background-color: #f8f8f8;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-bottom: 10px;
  border-bottom: 1px solid #ddd;
  margin-bottom: 20px;
}

.questions-list {
  margin-top: 20px;
}

.question-item {
  margin-bottom: 20px;
  background: #fff;
  padding: 15px;
  border: 1px solid #ddd;
  border-radius: 5px;
}

.question-header {
  display: flex;
  justify-content: space-between;
  margin-bottom: 10px;
}

.question-anwswer {
  display: flex;
  align-items: flex-start;
  flex-direction: column;
}

.question-anwswer label {
  display: block;
  margin-bottom: 5px;
}
</style>