vue3實(shí)現(xiàn)問卷調(diào)查的示例代碼
前言
問卷調(diào)查,這個(gè)東西真的隨處可見,那不如自己做一個(gè)問卷調(diào)查?話不多說,我們來實(shí)現(xiàn)它?。?!
我們需要實(shí)現(xiàn)的效果圖如下:
開發(fā)工具
vscode(里面預(yù)先裝好vue)
思路準(zhǔn)備
通過分析調(diào)查問卷的功能,我們來梳理一下實(shí)現(xiàn)它的方式:
- 首先我們把這個(gè)問卷調(diào)查分為三個(gè)板塊:首頁(home),答題頁(item),得分頁(score)。實(shí)現(xiàn)這三個(gè)板塊的跳轉(zhuǎn)采用路由的方式。
- 我們可以看到 首頁(home) 和 答題頁(item) 中每道題目的切換都只是中間部分發(fā)生變化,其他的地方并沒有改變,那么我們可以把其作為一個(gè)組件分離出去,在這個(gè)組件中完成代碼編寫。
- 最后單獨(dú)編寫得分頁(score)。
一、創(chuàng)建vue3項(xiàng)目
我們使用vue create xxx
命令創(chuàng)建這個(gè)項(xiàng)目,我這以happy命名,并安裝好路由,less預(yù)處理器。
二、構(gòu)建目錄結(jié)構(gòu)
創(chuàng)建完畢后,我們對這些文件夾做進(jìn)一步操作:
assets
:
1.創(chuàng)建images
文件夾放置圖片
2.創(chuàng)建style
文件夾,在其中創(chuàng)建common.less
,讓html5常用的標(biāo)簽初始化
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è)備上有更好的查看效果,這里做了一個(gè)適配
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. 標(biāo)簽初始化(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": "歡樂星期五標(biāo)題", "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": "歡樂星期五標(biāo)題", "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)
由于首頁和答題頁擁有共同部分,我們把其作為組件單獨(dú)拿出來編寫,那么在home頁面和item頁面中只需引入這個(gè)組件即可。
(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實(shí)現(xiàn)父子組件的通信,v-if用來判斷主頁面和答題頁面誰顯示在頁面上。
- 用index記錄選項(xiàng)的下標(biāo),若選中的下標(biāo)等于這個(gè)選項(xiàng)的下標(biāo)即添加這個(gè)樣式,將其變?yōu)?strong>黃色。
- 點(diǎn)擊下一題按鈕時(shí)未選擇選項(xiàng),需彈出提示框。
- 后端數(shù)據(jù)的選項(xiàng)的topic_answer_id是唯一值,可以利用這個(gè)特點(diǎn)將選中的所有選項(xiàng)的id值保存在result數(shù)組中,以便之后判斷是否為正確答案計(jì)算得分。
- 當(dāng)答題為最后一題時(shí),按鈕變?yōu)?strong>提交按鈕,為其綁定點(diǎn)擊事件,利用router的push方法實(shí)現(xiàn)路由傳參和跳轉(zhuǎn),參數(shù)為存放用戶選擇答案的數(shù)組result。
- 點(diǎn)擊提交按鈕時(shí),最后一題還未選擇,需彈出提示框。
<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> <!-- 選項(xiàng) --> <ul> <li class="item_list" @click="choosed(item.topic_answer_id, index)" v-for="(item, index) in ques[itemNum].topic_answer" :key="index"> <!-- 雙向綁定一個(gè)類名,這個(gè)類名可修改選中的樣式 --> <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ù)第二題這個(gè)按鈕就不出現(xiàn)--> <span class="next_item button_style" @click="nextItem" v-if="itemNum < ques.length - 1"></span> <!-- 提交按鈕 倒數(shù)第一題時(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) => { //選項(xiàng) switch (type) { case 0: return 'A'; case 1: return 'B'; case 2: return 'C'; case 3: return 'D'; } } const choosed = (id, index) => { //選中的id號push進(jìn)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 //切換題目后將選中的選項(xiàng)置為空(不選中) } //提交 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 router
7. 得分頁(score/index.vue)
思路:
- 修改圖片背景。
- 得到用戶選中答案的result數(shù)組后,利用forEach遍歷找到和后端數(shù)據(jù)匹配題目的選項(xiàng)并判斷選中的選項(xiàng)是否為正確答案,是則加上這一題的分?jǐn)?shù)。
- 利用得到的總分計(jì)算答對的題數(shù),把它作為提示語數(shù)組的下標(biāo),這樣不同的分?jǐn)?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 //計(jì)算得分 function calcScore(id, idx) { //id為選中的選項(xiàng),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 = [ "你說,是不是把知識都還給小學(xué)老師了?", "還不錯(cuò),但還需要繼續(xù)加油哦!", "不要嘚瑟還有進(jìn)步的空間!", "智商離爆表只差一步了!", "你也太聰明啦!", ] 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實(shí)現(xiàn)問卷調(diào)查的示例代碼的文章就介紹到這了,更多相關(guān)vue3 問卷調(diào)查內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue+relation-graph繪制關(guān)系圖實(shí)用組件操作方法
這篇文章主要介紹了vue+relation-graph繪制關(guān)系圖實(shí)用組件操作方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07element中TimePicker時(shí)間選擇器禁用部分時(shí)間(顯示禁用到分鐘)
這篇文章主要介紹了element中TimePicker時(shí)間選擇器禁用部分時(shí)間(顯示禁用到分鐘),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03Vue3?pinia管理數(shù)據(jù)的3種方式代碼
在Vue3中Pinia是一個(gè)狀態(tài)管理庫,它提供了一種簡單而強(qiáng)大的方式來管理應(yīng)用程序的狀態(tài),這篇文章主要給大家介紹了關(guān)于Vue3?pinia管理數(shù)據(jù)的3種方式,需要的朋友可以參考下2024-04-04vue-router3.x和vue-router4.x相互影響的問題分析
這篇文章主要介紹了vue-router3.x和vue-router4.x相互影響的問題分析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04vue.js+Echarts開發(fā)圖表放大縮小功能實(shí)例
本篇文章主要介紹了vue.js+Echarts開發(fā)圖表放大縮小功能實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06在Vue中實(shí)現(xiàn)網(wǎng)頁截圖與截屏功能詳解
在Web開發(fā)中,有時(shí)候需要對網(wǎng)頁進(jìn)行截圖或截屏,Vue作為一個(gè)流行的JavaScript框架,提供了一些工具和庫,可以方便地實(shí)現(xiàn)網(wǎng)頁截圖和截屏功能,本文將介紹如何在Vue中進(jìn)行網(wǎng)頁截圖和截屏,需要的朋友可以參考下2023-06-06