vue3實現(xiàn)問卷調(diào)查的示例代碼
前言
問卷調(diào)查,這個東西真的隨處可見,那不如自己做一個問卷調(diào)查?話不多說,我們來實現(xiàn)它?。?!
我們需要實現(xiàn)的效果圖如下:

開發(fā)工具
vscode(里面預(yù)先裝好vue)
思路準備
通過分析調(diào)查問卷的功能,我們來梳理一下實現(xiàn)它的方式:
- 首先我們把這個問卷調(diào)查分為三個板塊:首頁(home),答題頁(item),得分頁(score)。實現(xiàn)這三個板塊的跳轉(zhuǎn)采用路由的方式。
- 我們可以看到 首頁(home) 和 答題頁(item) 中每道題目的切換都只是中間部分發(fā)生變化,其他的地方并沒有改變,那么我們可以把其作為一個組件分離出去,在這個組件中完成代碼編寫。
- 最后單獨編寫得分頁(score)。
一、創(chuàng)建vue3項目
我們使用vue create xxx命令創(chuàng)建這個項目,我這以happy命名,并安裝好路由,less預(yù)處理器。
二、構(gòu)建目錄結(jié)構(gòu)

創(chuàng)建完畢后,我們對這些文件夾做進一步操作:
assets:
1.創(chuàng)建images文件夾放置圖片
2.創(chuàng)建style文件夾,在其中創(chuàng)建common.less,讓html5常用的標簽初始化
components:
1.創(chuàng)建item.vue作為組件
mock:
1.創(chuàng)建index.js,其中存放后端數(shù)據(jù)( 這里采用靜態(tài)的數(shù)據(jù) )
router:
1.創(chuàng)建index.js,這是對路由的配置
utils:
1.創(chuàng)建rem.js,為了讓用戶在不同設(shè)備上有更好的查看效果,這里做了一個適配
views:
1.創(chuàng)建home文件夾,其中放置 index.vue用于首頁的頁面編寫
2.創(chuàng)建item文件夾,其中放置 item.vue用于答題頁的頁面編寫
3.創(chuàng)建score文件夾,其中放置 score.vue用于得分頁的頁面編寫
另外我們需在App.vue中的template中添加router-view:
<template> <router-view/> </template> <style lang="less"> </style>
以及在main.js中引入必要的文件:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import '@/utils/rem.js'
import '@/assets/style/common.less'
createApp(App).use(router).mount('#app')三、代碼編寫
1. 屏幕適配(rem.js)
(function (doc) {
let docEl = doc.documentElement
doc.addEventListener('DOMContentLoaded', () => {
let clientWidth = docEl.clientWidth //獲取屏幕寬度
docEl.style.fontSize = 20 * (clientWidth / 320) + 'px' //讓1rem=20px
})
})(document)2. 標簽初始化(common.less)
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
html{
height: 100%;
width: 100%;
}
body{
height: 100%;
width: 100%;
background: url(../images/1-1.jpg) no-repeat; //設(shè)置背景圖片
background-size: 100% 100%;
}
.clear:after{
content: '';
display: block;
clear: both;
}
.clear{
zoom:1;
}
.back_img{
background-repeat: no-repeat;
background-size: 100% 100%;
}
.margin{
margin: 0 auto;
}
.left{
float: left;
}
.right{
float:right;
}
.hide{
display: none;
}
.show{
display: block;
}3. 后端數(shù)據(jù)(mock/index.js)
export const questions=[
{
"topic_id": 20,
"active_topic_id": 4,
"type": "ONE",
"topic_name": "題目一",
"active_id": 1,
"active_title": "歡樂星期五標題",
"active_topic_phase": "第一周",
"active_start_time": "1479139200",
"active_end_time": "1482163200",
"topic_answer": [{
"topic_answer_id": 1,
"topic_id": 20,
"answer_name": "答案aaaa",
"is_standard_answer": 0
}, {
"topic_answer_id": 2,
"topic_id": 20,
"answer_name": "答案bbbb",
"is_standard_answer": 0
}, {
"topic_answer_id": 3,
"topic_id": 20,
"answer_name": "答案cccc",
"is_standard_answer": 0
}, {
"topic_answer_id": 4,
"topic_id": 20,
"answer_name": "正確答案",
"is_standard_answer": 1
}]
},
{
"topic_id": 21,
"active_topic_id": 4,
"type": "MORE",
"topic_name": "題目二",
"active_id": 1,
"active_title": "歡樂星期五標題",
"active_topic_phase": "第一周",
"active_start_time": "1479139200",
"active_end_time": "1482163200",
"topic_answer": [{
"topic_answer_id": 5,
"topic_id": 21,
"answer_name": "正確答案",
"is_standard_answer": 1
}, {
"topic_answer_id": 6,
"topic_id": 21,
"answer_name": "答案B",
"is_standard_answer": 0
}, {
"topic_answer_id": 7,
"topic_id": 21,
"answer_name": "答案C",
"is_standard_answer": 0
}, {
"topic_answer_id": 8,
"topic_id": 21,
"answer_name": "答案D",
"is_standard_answer": 0
}]
}
//后面數(shù)據(jù)省略,數(shù)據(jù)條數(shù)可多條,看題目量
]4. 組件編寫(item.vue)
由于首頁和答題頁擁有共同部分,我們把其作為組件單獨拿出來編寫,那么在home頁面和item頁面中只需引入這個組件即可。
(1)首先編寫組件中的首頁:
思路:
首頁分為圖片背景,云朵里的第一周,開始按鈕(用路由跳轉(zhuǎn))
<template>
<section>
<header class="top_tips">
<span class="num_tip">第一周</span>
</header>
<!-- 首頁 -->
<div class="home_logo item_container_style">
<router-link to="/item" class="start button_style"></router-link> <!-- 路由跳轉(zhuǎn)至答題頁面 -->
</div>
</section>
</template>(2)編寫組件中的答題頁:
思路:
- 用props實現(xiàn)父子組件的通信,v-if用來判斷主頁面和答題頁面誰顯示在頁面上。
- 用index記錄選項的下標,若選中的下標等于這個選項的下標即添加這個樣式,將其變?yōu)?strong>黃色。
- 點擊下一題按鈕時未選擇選項,需彈出提示框。
- 后端數(shù)據(jù)的選項的topic_answer_id是唯一值,可以利用這個特點將選中的所有選項的id值保存在result數(shù)組中,以便之后判斷是否為正確答案計算得分。
- 當答題為最后一題時,按鈕變?yōu)?strong>提交按鈕,為其綁定點擊事件,利用router的push方法實現(xiàn)路由傳參和跳轉(zhuǎn),參數(shù)為存放用戶選擇答案的數(shù)組result。
- 點擊提交按鈕時,最后一題還未選擇,需彈出提示框。
<template>
<section>
<header class="top_tips">
<span class="num_tip" v-if="fatherComponent === 'home'">第一周</span>
<span class="num_tip" v-if="fatherComponent === 'item'">{{ ques[itemNum].topic_name }}</span>
</header>
<!-- 首頁 -->
<div v-if="fatherComponent === 'home'">
<div class="home_logo item_container_style">
<router-link to="/item" class="start button_style"></router-link> <!-- 路由跳轉(zhuǎn)至答題頁面 -->
</div>
</div>
<!-- item答題頁面 -->
<div v-if="fatherComponent === 'item'">
<div class="item_back item_container_style">
<div class="item_list_container" v-if="ques && ques.length > 0">
<!-- 題目 -->
<header class="item_title">{{ ques[itemNum].topic_name }}</header>
<!-- 選項 -->
<ul>
<li class="item_list" @click="choosed(item.topic_answer_id, index)"
v-for="(item, index) in ques[itemNum].topic_answer" :key="index">
<!-- 雙向綁定一個類名,這個類名可修改選中的樣式 -->
<span class="option_style" :class="{ 'has_choosed': chooseNum === index }">{{ chooseType(index) }}</span>
<span class="option_detail">{{ item.answer_name }}</span>
</li>
</ul>
</div>
</div>
<!-- 下一題按鈕 到倒數(shù)第二題這個按鈕就不出現(xiàn)-->
<span class="next_item button_style" @click="nextItem" v-if="itemNum < ques.length - 1"></span>
<!-- 提交按鈕 倒數(shù)第一題時出現(xiàn)-->
<span class="submit_item button_style" @click="submitItem" v-else></span>
</div>
</section>
</template>
<script>
import { questions } from '@/mock'
import { ref } from 'vue';
import { useRouter } from 'vue-router'
export default {
props: {
fatherComponent: String
},
setup(props, context) {
const ques = ref(questions)
console.log(questions);
let chooseNum = ref(null) //選中的答案
let itemNum = ref(0) //第幾題
let result = [] //記錄用戶選中的答案
const chooseType = (type) => { //選項
switch (type) {
case 0: return 'A';
case 1: return 'B';
case 2: return 'C';
case 3: return 'D';
}
}
const choosed = (id, index) => { //選中的id號push進result數(shù)組
console.log(index);
chooseNum.value = index
result.push(id)
}
const nextItem = () => { //下一題
if (chooseNum.value == null) {
alert('你還沒有選擇')
return
}
//切換題目數(shù)據(jù)
console.log(result);
itemNum.value++
chooseNum.value = null //切換題目后將選中的選項置為空(不選中)
}
//提交
const router = useRouter()
const submitItem = () => {
if (chooseNum.value == null) {
alert('你還沒有選擇')
return
}
//跳去score頁面
router.push({path:'/score',query:{answer:result}})
}
return { choosed, chooseNum, itemNum, ques, chooseType, nextItem, submitItem }
}
}
</script>
<style lang="less" scoped>
.top_tips {
position: absolute;
width: 3.25rem;
height: 7.35rem;
top: -1.3rem;
right: 1.6rem;
background: url('@/assets/images/WechatIMG2.png') no-repeat;
background-size: 100% 100%;
.num_tip {
position: absolute;
width: 2.5rem;
height: 0.7rem;
left: 0.48rem;
bottom: 1.1rem;
font-size: 0.6rem;
font-family: '黑體';
font-weight: 600;
color: #a57c50;
text-align: center;
}
}
.item_container_style {
position: absolute;
width: 13.15rem;
height: 11.625rem;
top: 4.1rem;
left: 1rem;
}
.next_item {
background-image: url(@/assets/images/2-2.png);
}
.submit_item {
background-image: url(@/assets/images/3-1.png);
}
.home_logo {
background: url('@/assets/images/1-2.png') no-repeat;
background-size: 100% 100%;
}
.button_style {
display: block;
width: 4.35rem;
height: 2.1rem;
position: absolute;
top: 16.5rem;
left: 50%;
margin-left: -2.175rem;
background-size: 100% 100%;
background-repeat: no-repeat;
}
.start {
background-image: url('@/assets/images/1-4.png');
}
.item_back {
background-image: url('@/assets/images/2-1.png');
background-size: 100% 100%;
.item_list_container {
position: absolute;
width: 8rem;
height: 7rem;
top: 2.4rem;
left: 3rem;
.item_title {
font-size: 0.65rem;
color: #fff;
line-height: 0.7rem;
}
.item_list {
width: 10rem;
margin-top: 0.4rem;
span {
display: inline-block;
font-size: 0.6rem;
color: #fff;
text-align: center;
line-height: 0.725rem;
margin-left: 0.3rem;
}
.option_style {
width: 0.725rem;
height: 0.725rem;
border: 1px solid #fff;
border-radius: 50%;
}
.has_choosed {
background-color: #ffd400;
color: #575757;
border-color: #ffd400;
}
}
}
}
</style>5. 組件引入
home/index.vue:
<template>
<div class="home_container">
<Item father-component="home"></Item>
</div>
</template>
<script>
import Item from '@/components/item.vue'
export default {
components:{
Item
}
}
</script>
<style lang="less" scoped></style>item/index.vue:
<template>
<div>
<Item father-component="item"></Item>
</div>
</template>
<script>
import Item from '@/components/item.vue'
export default {
components:{
Item
}
}
</script>
<style lang="less" scoped>
</style>6. 路由配置(router/index.js)
import { createRouter, createWebHistory } from 'vue-router'
import Home from '@/views/home'
const routes = [
{
path:'/',
redirect:'/home' //重定向
},
{
path:'/home', //根路徑下展示
name:'home',
component:Home
},
{
path:'/item',
name:'item',
component:()=>import('@/views/item')
},
{
path:'/score',
name:'score',
component:()=>import('@/views/score')
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router7. 得分頁(score/index.vue)
思路:
- 修改圖片背景。
- 得到用戶選中答案的result數(shù)組后,利用forEach遍歷找到和后端數(shù)據(jù)匹配題目的選項并判斷選中的選項是否為正確答案,是則加上這一題的分數(shù)。
- 利用得到的總分計算答對的題數(shù),把它作為提示語數(shù)組的下標,這樣不同的分數(shù)就能對應(yīng)不同的提示語了。
<template>
<div class="score_container">
<header class="your_score">
<span class="score_num">{{ score }}分</span>
<span class="res_tip">{{ getScoreTip() }}</span>
</header>
</div>
</template>
<script>
import { questions } from '@/mock'
import { useRoute } from 'vue-router'
export default {
setup() {
const route = useRoute()
console.log(route.query.answer);
//修改body的背景
const bg = require('@/assets/images/4-1.jpg')
document.body.style.backgroundImage = `url(${bg})`
let score = 0
//計算得分
function calcScore(id, idx) { //id為選中的選項,idx為第幾題
questions[idx].topic_answer.forEach((answerItem) => {
if (answerItem.topic_answer_id == id && answerItem.is_standard_answer === 1) {
score += (100 / questions.length)
}
})
}
route.query.answer.forEach((id, index) => {
calcScore(id, index)
})
const scoreTipsArr = [
"你說,是不是把知識都還給小學老師了?",
"還不錯,但還需要繼續(xù)加油哦!",
"不要嘚瑟還有進步的空間!",
"智商離爆表只差一步了!",
"你也太聰明啦!",
]
const getScoreTip = () => {
let every=100/questions.length
let index=Math.ceil(score/every)-1
return scoreTipsArr[index]
}
return { score,getScoreTip }
}
}
</script>
<style lang="less">
#app {
overflow: hidden;
}
.score_container {
width: 9.7rem;
height: 9.1rem;
background-image: url('@/assets/images/4-2.png');
background-repeat: no-repeat;
background-size: 100% 100%;
margin: 0 auto;
margin-top: 1rem;
position: relative;
.your_score {
position: absolute;
right: 0;
width: 9rem;
text-align: center;
font-size: 1.4rem;
top: 4.7rem;
font-weight: 900;
-webkit-text-stroke: 0.05rem #412318;
.score_num {
color: #a51d31
}
.res_tip {
display: block;
color: #3e2415;
font-size: 0.7rem;
font-weight: 200;
margin-top: 1rem;
}
}
}
</style>最后
到此這篇關(guān)于vue3實現(xiàn)問卷調(diào)查的示例代碼的文章就介紹到這了,更多相關(guān)vue3 問卷調(diào)查內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue+relation-graph繪制關(guān)系圖實用組件操作方法
這篇文章主要介紹了vue+relation-graph繪制關(guān)系圖實用組件操作方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07
element中TimePicker時間選擇器禁用部分時間(顯示禁用到分鐘)
這篇文章主要介紹了element中TimePicker時間選擇器禁用部分時間(顯示禁用到分鐘),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-03-03
Vue3?pinia管理數(shù)據(jù)的3種方式代碼
在Vue3中Pinia是一個狀態(tài)管理庫,它提供了一種簡單而強大的方式來管理應(yīng)用程序的狀態(tài),這篇文章主要給大家介紹了關(guān)于Vue3?pinia管理數(shù)據(jù)的3種方式,需要的朋友可以參考下2024-04-04
vue-router3.x和vue-router4.x相互影響的問題分析
這篇文章主要介紹了vue-router3.x和vue-router4.x相互影響的問題分析,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-04-04
vue.js+Echarts開發(fā)圖表放大縮小功能實例
本篇文章主要介紹了vue.js+Echarts開發(fā)圖表放大縮小功能實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06
在Vue中實現(xiàn)網(wǎng)頁截圖與截屏功能詳解
在Web開發(fā)中,有時候需要對網(wǎng)頁進行截圖或截屏,Vue作為一個流行的JavaScript框架,提供了一些工具和庫,可以方便地實現(xiàn)網(wǎng)頁截圖和截屏功能,本文將介紹如何在Vue中進行網(wǎng)頁截圖和截屏,需要的朋友可以參考下2023-06-06

