用vue.js写一个问卷调查

admin 102 0
使用Vue.js开发问卷调查,可依托其组件化与响应式特性高效实现,通过将问题、选项、提交按钮拆分为独立组件,提升代码复用性;利用v-model双向绑定实时同步表单数据,结合计算属性或自定义指令实现动态验证(如必填、格式校验),借助v-if/v-show根据用户选择控制问题显示逻辑,配合axios或本地存储完成数据提交,通过计算进度条实时反馈完成度,优化交互体验,Vue.js的轻量级与灵活性,使问卷开发兼具高效性与可维护性,适合快速构建交互式表单应用。

使用Vue.js构建交互式问卷调查系统

问卷调查是收集用户反馈、市场调研、产品迭代的重要工具,随着前端技术的飞速发展,利用Vue.js这类响应式框架开发动态问卷系统,已成为提升用户体验和开发效率的优选方案,Vue.js的组件化架构、数据双向绑定和灵活的状态管理机制,能够轻松实现问卷的动态渲染、实时交互和数据收集功能,本文将详细介绍如何从零开始,使用Vue.js构建一个功能完善的现代化问卷调查系统。

环境准备与项目初始化

在开始开发前,需确保本地已安装Node.js(建议v14+版本)和npm(或yarn)包管理器,我们使用Vue CLI(或Vite)快速初始化项目,这里以Vue CLI为例:

创建Vue项目

# 全局安装Vue CLI
npm install -g @vue/cli
# 创建新项目
vue create questionnaire-system
# 进入项目目录
cd questionnaire-system

安装必要依赖

问卷系统通常涉及路由管理、HTTP请求、状态管理等复杂功能,建议安装以下依赖:

# 安装路由管理
npm install vue-router
# 安装HTTP请求库
npm install axios
# 安装状态管理(推荐使用Pinia)
npm install pinia
# 安装UI组件库(这里以Element Plus为例)
npm install element-plus
# 安装表单验证库
npm install vee-validate
# 安装日期处理库
npm install dayjs

项目结构设计

合理的项目结构是系统可维护性的基础,建议采用以下目录结构:

src/
├── assets/          # 静态资源
├── components/      # 通用组件
│   ├── Question/    # 问卷相关组件
│   │   ├── QuestionItem.vue
│   │   ├── MultipleChoice.vue
│   │   ├── TextAnswer.vue
│   │   └── RatingScale.vue
│   └── Common/      # 通用组件
├── views/           # 页面视图
│   ├── Home.vue
│   ├── Questionnaire.vue
│   └── Result.vue
├── router/          # 路由配置
├── store/           # 状态管理
├── utils/           # 工具函数
└── App.vue          # 根组件

核心组件开发

问卷项组件设计

问卷系统核心在于灵活的问卷项组件设计,我们可以创建一个基础问卷组件,然后根据不同题型扩展:

<!-- QuestionItem.vue -->
<template>
  <div class="question-item">
    <h3>{{ question.title }} <span v-if="question.required">*</span></h3>
    <p class="description" v-if="question.description">{{ question.description }}</p>
    <!-- 根据题型渲染不同内容 -->
    <component 
      :is="currentComponent" 
      :question="question"
      :value="answer"
      @input="handleAnswerChange"
    />
  </div>
</template>
<script>
import { computed } from 'vue'
import MultipleChoice from './MultipleChoice.vue'
import TextAnswer from './TextAnswer.vue'
import RatingScale from './RatingScale.vue'
export default {
  components: { MultipleChoice, TextAnswer, RatingScale },
  props: {
    question: {
      type: Object,
      required: true
    }
  },
  data() {
    return {
      answer: null
    }
  },
  computed: {
    currentComponent() {
      const typeMap = {
        'multiple': 'MultipleChoice',
        'text': 'TextAnswer',
        'rating': 'RatingScale'
      }
      return typeMap[this.question.type] || 'TextAnswer'
    }
  },
  methods: {
    handleAnswerChange(value) {
      this.answer = value
      this.$emit('answer-change', {
        questionId: this.question.id,
        answer: value
      })
    }
  }
}
</script>

问卷容器组件

<!-- Questionnaire.vue -->
<template>
  <div class="questionnaire-container">
    <div class="progress-bar">
      <el-progress 
        :percentage="progressPercentage" 
        :format="progressFormat"
      />
    </div>
    <div class="questions-wrapper">
      <QuestionItem
        v-for="question in currentQuestions"
        :key="question.id"
        :question="question"
        :answer="answers[question.id]"
        @answer-change="handleAnswerChange"
      />
    </div>
    <div class="navigation-buttons">
      <el-button 
        v-if="currentStep > 0" 
        @click="previousStep"
        plain
      >
        上一题
      </el-button>
      <el-button 
        v-if="currentStep < totalSteps - 1" 
        @click="nextStep"
        type="primary"
        :disabled="!isCurrentStepValid"
      >
        下一题
      </el-button>
      <el-button 
        v-if="currentStep === totalSteps - 1" 
        @click="submitQuestionnaire"
        type="success"
        :disabled="!isFormValid"
      >
        提交问卷
      </el-button>
    </div>
  </div>
</template>
<script>
import { ref, computed } from 'vue'
import { ElMessage } from 'element-plus'
export default {
  props: {
    questions: {
      type: Array,
      required: true
    }
  },
  setup(props) {
    const answers = ref({})
    const currentStep = ref(0)
    const questionsPerStep = 3 // 每页显示的题目数量
    const currentQuestions = computed(() => {
      const start = currentStep.value * questionsPerStep
      const end = start + questionsPerStep
      return props.questions.slice(start, end)
    })
    const totalSteps = computed(() => {
      return Math.ceil(props.questions.length / questionsPerStep)
    })
    const progressPercentage = computed(() => {
      return Math.round((currentStep.value / (totalSteps.value - 1)) * 100)
    })
    const progressFormat = (percentage) => {
      return `第 ${currentStep.value + 1} 步 / 共 ${totalSteps.value} 步`
    }
    const isCurrentStepValid = computed(() => {
      return currentQuestions.value.every(q => {
        return !q.required || answers.value[q.id] !== undefined && answers.value[q.id] !== ''
      })
    })
    const isFormValid = computed(() => {
      return props.questions.every(q => {
        return !q.required || answers.value[q.id] !== undefined && answers.value[q.id] !== ''
      })
    })
    const handleAnswerChange = (data) => {
      answers.value = {
        ...answers.value,
        [data.questionId]: data.answer
      }
    }
    const nextStep = () => {
      if (isCurrentStepValid.value) {
        currentStep.value++
      } else {
        ElMessage.warning('请完成当前页面的必填项')
      }
    }
    const previousStep = () => {
      if (currentStep.value > 0) {
        currentStep.value--
      }
    }
    const submitQuestionnaire = () => {
      if (isFormValid.value) {
        // 提交逻辑
        console.log('提交的答案:', answers.value)
        ElMessage

标签: #js #问卷调查 #前端开发 #表单