溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

vue如何實(shí)現(xiàn)自定義多選與單選的答題功能

發(fā)布時(shí)間:2021-04-23 12:53:57 來源:億速云 閱讀:1033 作者:小新 欄目:web開發(fā)

這篇文章主要介紹vue如何實(shí)現(xiàn)自定義多選與單選的答題功能,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

vue是什么

Vue是一套用于構(gòu)建用戶界面的漸進(jìn)式JavaScript框架,Vue與其它大型框架的區(qū)別是,使用Vue可以自底向上逐層應(yīng)用,其核心庫只關(guān)注視圖層,方便與第三方庫和項(xiàng)目整合,且使用Vue可以采用單文件組件和Vue生態(tài)系統(tǒng)支持的庫開發(fā)復(fù)雜的單頁應(yīng)用。

ui圖: 

vue如何實(shí)現(xiàn)自定義多選與單選的答題功能 

可悲的是,這個(gè)項(xiàng)目兩個(gè)月后,我才來做項(xiàng)目復(fù)盤,

話說也就在此時(shí),我才發(fā)現(xiàn)有一種更簡單的方式來實(shí)現(xiàn)并且應(yīng)用上v-model,

為什么要為了樣式放棄功能然后自己吭哧吭哧傻-滴-呼呼的用js來實(shí)現(xiàn)了類似雙向綁定的感覺?。?!

flag:今天先專注把我費(fèi)勁巴拉手動(dòng)搬得磚總結(jié)一下,明天(07-05)我再把所謂的最簡單的方法做出來貼這里~

這個(gè)需求的難點(diǎn)在于以下幾點(diǎn):

1.單選點(diǎn)擊后選中狀態(tài),需滿足如下:X

a) 每次點(diǎn)擊只能選中其中一個(gè)

b) 當(dāng)選中時(shí)再次點(diǎn)擊其他選項(xiàng)需要切換選擇對應(yīng)點(diǎn)擊項(xiàng)

c) 選中時(shí)點(diǎn)擊自身無顯示上的反應(yīng)(同樣的邏輯再做一遍也無妨,即再加一遍類名也看不出來)

2.多選樣式展示,需滿足如下:

a) 同時(shí)可以選中多個(gè)X

b) 多選已選中狀態(tài)再次點(diǎn)擊取消選中X

3.多選選中項(xiàng)的記錄,需滿足如下:

a) 選擇幾個(gè)記錄幾個(gè)

b) 選中再取消時(shí)需要將本條記錄的數(shù)據(jù)通時(shí)消除(依據(jù)點(diǎn)擊事件,事件點(diǎn)擊觸發(fā)判斷哪個(gè)被選中了)

4.單選選中項(xiàng)的記錄,方便提交數(shù)據(jù)

5.未點(diǎn)擊選項(xiàng)不可提交,并給提示

6.可提交狀態(tài),需滿足如下:

a) 單選選中任意一個(gè),即可提交。再次修改對提交沒有影響

b) 多選至少選中一個(gè)可提交,再次修改需判斷是不是沒選東西

7.第十四題點(diǎn)下一題切換提交按鈕

8.快速點(diǎn)擊下一題,多次提交

9.點(diǎn)擊下一題提交數(shù)據(jù)后,拿響應(yīng)結(jié)果調(diào)取彈層提示用戶選擇是否正確

=============接下來一 一解決====================

首先先說結(jié)構(gòu)

看似十道題,其實(shí)是一道題不停的換數(shù)據(jù),所以我的外部結(jié)構(gòu)就是一個(gè)form加一個(gè)空的div

別問我為什么多余一個(gè)空的,我也很無措。

form.question(v-if="state.ExamInfo")
 div

然后題目標(biāo)題很傻瓜式得使用了h4

h4.qus-title(:data-id="state.ExamInfo.QuestionID") {{state.ExamInfo.ExamQuestionNo}} {{state.ExamInfo.Description}}

選項(xiàng)上,我使用ul>li的形式描述了多個(gè)選項(xiàng)

ul.qus-list
  li(v-for="(item,index) in state.ExamInfo.QuestionAnswerCode" @click="choosed(index)" v-bind:class="{'li-focus' : chooseNum==index}" ref="liId") {{item.Code}}、{{item.Description}}

大致幾個(gè)屬性

  • v-for是為了遍歷題中的每一個(gè)選項(xiàng),

  • click綁定了點(diǎn)擊當(dāng)前l(fā)i時(shí)的事件,v-bind同步click綁定了動(dòng)態(tài)的類名,用于展示選中狀態(tài)。

  • 這里值得注意的一個(gè)點(diǎn)也是當(dāng)時(shí)抓蝦的一個(gè)點(diǎn)是,v-on:click和v-bind:class結(jié)合,

  • click的時(shí)候,每次把當(dāng)前點(diǎn)擊的li的index值傳出去,

然后定義一個(gè)變量chooseNum,點(diǎn)擊函數(shù)中,將參數(shù)index賦給他
this.chooseNum = index;

靠這種間接拿到點(diǎn)擊索引值得曲線救國方式,在v-bind的監(jiān)視下,每次點(diǎn)擊獲得的索引chooseNum

和這幾個(gè)li中自己的index對上眼以后,就如正確的鑰匙對上了合適的鎖,類名綁定就成了。

也就是那十幾條難題中的第一個(gè)被輕松干掉的難題的前半部分: 單選點(diǎn)擊后選中狀態(tài) 。

vue如何實(shí)現(xiàn)自定義多選與單選的答題功能 vue如何實(shí)現(xiàn)自定義多選與單選的答題功能 vue如何實(shí)現(xiàn)自定義多選與單選的答題功能

費(fèi)這么半天勁,才解決一個(gè)點(diǎn)??!我不服!別急,接下來還有好戲。

但其實(shí)這個(gè)思路還是挺重要的,靠這一點(diǎn)“死皮賴臉”拉關(guān)系的勁頭,這個(gè)法子以后還倒是可以有很多用武之地。

好戲在下一個(gè)屬性,沒錯(cuò)就是ref,這也是我步入萬丈深淵一去不復(fù)返的梯子??!

ref

要知道人家可以vue里邊的特殊特性,

要知道人家可是很有能力的,

要知道我老是連著打不出妖之道這三個(gè)字!我就不行了?。?/p>

好了不皮了。

官網(wǎng)記載ref這個(gè)特殊特性,被用來給元素或子組件注冊引用信息。 引用信息將會注冊在父組件的 $refs 對象上。

如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素; 如果用在子組件上,引用就指向組件實(shí)例。

我的理解大白話來說,他就是給dom元素或者組件實(shí)例一個(gè)身份證號,身份證號有的特性他也就有,那就是唯一不重復(fù)。

如果配合上v-for,就能獲取這一批帶有ref特性所組成的數(shù)組。

通過數(shù)組下標(biāo)索引出來的個(gè)體,也就是對應(yīng)的dom元素本身或者組件實(shí)例本人無疑了。

就好像拿著身份證號去公安局查人一樣,快速不說,還很高效有沒有,一查一個(gè)準(zhǔn)!

但需要說明的是,在created鉤子中,這個(gè)特性拿不到東西,生命周期鉤子里只有在mounted里(可能還有后邊的鉤子里,我沒用過不準(zhǔn)確)可以用,畢竟你想啊,身份證號雖說一出生就有了,但是只有掛載到網(wǎng)上你才能查到大的嘛!

所以,我究竟用它做了什么呢?那就是多選功能??!

還是先回到上邊說的,綁定了一個(gè)事件,并且會傳遞一個(gè)當(dāng)前點(diǎn)擊li的索引號,并且前邊也提到過,

ref返回的是數(shù)組,有數(shù)組有索引號,簡直是萬事俱備啊。于是乎讓我們來呼喚東風(fēng)(別看了,就是說我們自己)!

在choose點(diǎn)擊函數(shù)中就有了這么一段:

if(this.$refs.liId[index].className.length <= 0){
  //首先先判斷當(dāng)前l(fā)i有沒有被選中,因?yàn)槲疫@里li除了選中狀態(tài)的有類名,其他沒有類名,所以我就這么判斷了。
  //這么看有時(shí)候舍棄一小丟丟規(guī)范的東西反而省力。
  this.$refs.liId[index].className = 'li-focus';// 添加類
}else{
  //當(dāng)前l(fā)i已經(jīng)被選中,那么在多選的邏輯里,是允許人們選中后再取消選中的,所以前端展示層面上把樣式去掉。
  this.$refs.liId[index].className = '';// 選中再取消的情況
}

好了,第二個(gè) 多選樣式 搞定。

vue如何實(shí)現(xiàn)自定義多選與單選的答題功能 vue如何實(shí)現(xiàn)自定義多選與單選的答題功能 vue如何實(shí)現(xiàn)自定義多選與單選的答題功能

那么接下來,選擇的結(jié)果呢,能不能來一次“趁火打劫”趁點(diǎn)擊的時(shí)候偷偷記錄下用戶的選擇?答案當(dāng)然是可以的啦!

首先說多選功能的趁火打劫吧,就著上邊增刪類名的熱乎勁,緊接著在每次點(diǎn)擊時(shí)我們記錄下當(dāng)前點(diǎn)擊的是誰

// 獲取選中結(jié)果
    for (let i = 0; i < this.$refs.liId.length; i++) {
      if(this.$refs.liId[i].className.length > 0){
        this.chooseNumStr += this.$refs.liId[i].innerText.substring(0,1);
      }
    }

這一段再次利用了ref的特性,找到當(dāng)前點(diǎn)擊的dom,截取人家選項(xiàng)里的第一個(gè)字,那就是ABC or D;

拼接到事先準(zhǔn)備好的字符串chooseNumStr中(要發(fā)給數(shù)據(jù)用的),因?yàn)檫@里和后端提前約定的就是將選擇結(jié)果以字符串的形式提交。

if判斷那里,條件再次是利用了li誰有類名就是選了誰的不講理原則。第三個(gè) 多選記錄選項(xiàng)功能 問題搞定。

第四個(gè)問題是,既然多選記錄搞定了,那么單選呢,也應(yīng)該在每次點(diǎn)擊的時(shí)候搞定他吧?那是自然!

這里我剛剛突然又想到了一個(gè)解決方法,于是這里我將呈現(xiàn)倆個(gè):

1.那就是我當(dāng)時(shí)腦殘的解決方法,不過這種方法唯一的好處可能是,

產(chǎn)品大大過來說,那sei,你把選項(xiàng)中的ABCD去掉吧,不好看,那我就傻逼了。

事實(shí)上,本來人家設(shè)計(jì)稿里選項(xiàng)處就沒有ABCD,我本著你好我好大家好的原則,說服了他們加上的。。。。。

不廢話了,我發(fā)現(xiàn)我進(jìn)入中年了,絮絮叨叨總是進(jìn)不了正題,或許這和我上課愛走神有關(guān)吧。

//索引0-3對應(yīng)答案A-B,依次類推
    // 注意,這里看看最多的選項(xiàng)是多少個(gè),進(jìn)行下配置,當(dāng)前只是配置到了F
    switch(index){
     case 0: this.chooseNumStr = 'A';
     break;
     case 1: this.chooseNumStr = 'B';
     break;
     case 2: this.chooseNumStr = 'C';
     break;
     case 3: this.chooseNumStr = 'D';
     break;
     case 4: this.chooseNumStr = 'E';
     break;
     case 5: this.chooseNumStr = 'F';
     break;
    }

沒錯(cuò),還是在choose中,我判斷是單選后,用switch來判斷index的值,進(jìn)而匹配到chooseNumStr的結(jié)果。

雖然這種方法很笨拙,而且有超出設(shè)置范圍的選項(xiàng)的危險(xiǎn),但是,我傻?。∧挠惺裁捶椒?!

當(dāng)初就是覺得這么干很不妥,可是直到今天我再看自己的代碼才想到更好的解決方案的啊!那他是啥?。?!那就是:

2. 就還是強(qiáng)大的ref登場,規(guī)則和選擇多選一樣,只不過不用for循環(huán)。你是不是已經(jīng)想到了啊哈!

對的,每次單選點(diǎn)的是那個(gè)就截取 this.$refs.liId[i].innerText.substring(0,1);   簡直soeasy

好了,第四個(gè)問題 單選的答案記錄 問題解決。

然后,我們接著趁熱打鐵(才發(fā)現(xiàn)他和趁火打劫好像是兄弟啊?。?解決下邊點(diǎn)擊按鈕的問題。

需求是沒選是灰色,選擇選項(xiàng)后可提交:

首先是兩個(gè)按鈕的結(jié)構(gòu),為了避免后期下一題和提交按鈕的交班時(shí)我還得判斷點(diǎn)擊事件是他倆誰和誰,

所以我用了兩個(gè)按鈕,綁了兩個(gè)事件,把不同功能的事件分開綁定了。

.public-btn(v-if="!isLast" @click="nextItem" v-bind:class="{'public-btn-gray': unclickable}") 下一題
.public-btn(v-else @click="submitItem" v-bind:class="{'public-btn-gray': unclickable}") 提交

可以看到,除了事件我還綁定了class,那個(gè)public-btn-gray的生存與否取決于unclickable。

先說沒選是灰色的處理:

這個(gè)思路上就是肯定是默認(rèn)提交按鈕就是灰色的,也就是有著public-btn-gray類名的。

這里有一個(gè)用于描述按鈕是不可點(diǎn)擊狀態(tài)的變量unclickable,專門管理按鈕是否可點(diǎn)擊的。

初始化時(shí)是true不可點(diǎn)擊的。這樣,按鈕的gray類名public-btn-gray就加了。

邏輯上,點(diǎn)擊按鈕的時(shí)候先判斷這個(gè)值,如果為true就提示用戶要先選擇答案:

if(this.unclickable){
  alert('您還沒有選擇答案哦!');
}else{// do someting you wanted;}

vue如何實(shí)現(xiàn)自定義多選與單選的答題功能 

然后是 選擇選項(xiàng)后可提交 。

那這不好說嘛!我只要點(diǎn)擊事件一觸發(fā),就把可點(diǎn)擊狀態(tài)放開不就好了嘛!

那好,我是用戶,我在如圖第15題選擇a、c解鎖提交按鈕,然后我再點(diǎn)擊a、c抹掉我的記錄,

但這時(shí)我的提交按鈕已打開,我可以在他毫無防備的情況下趁虛而入(中華文化真博大,這是第三個(gè)同意義的成語了)!哈哈哈。

這當(dāng)然不可以了,直接點(diǎn)擊事件就放開下一題按鈕,在單選場景下是通的。但是多選的時(shí)候我們還要再防御一層。

那就是:

// 置灰提交按鈕與否
    if(this.chooseNumStr.length > 0){
    //多選的時(shí)候,因?yàn)樵俅吸c(diǎn)擊會把記錄抹除,所以chooseNumStr會是動(dòng)態(tài)改變的,
    //如果一個(gè)也沒選擇,多選也好單選也罷,這個(gè)字符串肯定是空的,故而判斷長度小于0就不讓他提交!
     this.unclickable = false;
    }else{
     // 沒有選東西,就置灰按鈕
     this.unclickable = true;
    }

vue如何實(shí)現(xiàn)自定義多選與單選的答題功能 

耶!第六點(diǎn) 多選功能與下一題 按鈕高亮可跳轉(zhuǎn)功能 的結(jié)合也完成啦

至此,關(guān)于按鈕的樣式和邏輯就完畢了,每次點(diǎn)擊下一題下一題的功能就跑通了。

但是,一直跑到 第十四題點(diǎn)擊下一題 ,15題內(nèi)按鈕文案還是下一題,可是這是最后一題了啊,講點(diǎn)理吧!

好,那就講理點(diǎn),讓他改成提交,這時(shí)下一題和提交按鈕換崗。

換崗的時(shí)機(jī)我是在數(shù)據(jù)響應(yīng)回來后判斷本題目的題號/id,如果是14題,那么下一題就是最后一題,點(diǎn)擊下一題就讓提交按鈕上崗,下一題退休。

說了這么多,說的最多的是點(diǎn)擊下一題。所以在下一題里綁定的事件,就有一個(gè)角落是來干這個(gè)事的:

// 下一題
if(_this.state.ExamInfo.QuestionID == 14){ 
  //點(diǎn)擊下一題,數(shù)據(jù)響應(yīng)回來后,新數(shù)據(jù)替換前,判斷如果當(dāng)前是第14題就改變按鈕。
  //判斷切換下一題和提交按鈕
   _this.isLast = true;
}

然后,提交和下一題倆按鈕的樣式就靠這個(gè)狀態(tài)值控制,只要在事實(shí)的時(shí)候改變狀態(tài)值讓他倆交崗即可。

(仔細(xì)總結(jié)會發(fā)現(xiàn),都是這么一個(gè)套路,數(shù)據(jù)改變某個(gè)狀態(tài)值,狀態(tài)值綁定在結(jié)構(gòu)上,影響視圖的不同展示)
后來,還發(fā)現(xiàn)一個(gè)隱藏的問題:

點(diǎn)擊下一題后,因?yàn)槭菃雾搼?yīng)用,頁面結(jié)構(gòu)和數(shù)據(jù)都沒有刷新, 上一道題用戶選擇的結(jié)果綁在li上邊的樣式還需要清空,

所以每次點(diǎn)擊下一題甚至提交后都需要在重新填新題目數(shù)據(jù)時(shí)把li的樣式選中都清空,也就是把類名都清空。

// 樣式清空
for (let i = 0; i < _this.$refs.liId.length; i++) {
   _this.$refs.liId[i].className = '';
}

也需要把上一題的選擇數(shù)據(jù)清空,也就是 chooseNumStr字符串='';

且如果用戶翻到下邊再還數(shù)據(jù),雖然用戶看著像換了頁面,但其實(shí)還在這一頁。為了把假象做的更逼真點(diǎn),需要頁面定位到頂部:

// 點(diǎn)擊下一題,新頁面應(yīng)該定位到頂頭題干位置
document.body.scrollTop = 0;

正當(dāng)我看著這個(gè)天衣無縫的假功能玩的開心的時(shí)候,測試大大跑過來說:

~我快速點(diǎn)擊多次提交就提交了好多次。。

~exm??!你沒事一直點(diǎn)提交干嘛?

~我是測試

~好,大大,你別說了,我這就改嘎。

第⑧個(gè)問題: 多次點(diǎn)擊下一題/提交按鈕

好吧,這個(gè)問題確實(shí)是我沒考慮到,以后做這種表單提交的,肯定要防御用戶多次點(diǎn)擊提交。

有了上面幾次的經(jīng)驗(yàn),我現(xiàn)在很會利用data里某個(gè)變量來充當(dāng)狀態(tài)記錄了!

定義一個(gè)變量isClicked專門用于看管按鈕是否被提交過,如果在可點(diǎn)擊的狀態(tài)下點(diǎn)擊過,那么抱歉,邏輯中斷!

初始化這個(gè)isClicked肯定是沒有點(diǎn)擊狀態(tài),為false,然后在下一題和提交按鈕的點(diǎn)擊事件中判斷

if(!this.isClicked){//沒點(diǎn)擊過
  //該干啥干啥!
}else{
  //該干嘛干嘛去!
}

所以,到底應(yīng)該干嗎?!

終于說到最后,我好困,如果不是自娛自樂我可能坐著睜眼就睡著了,不,我已經(jīng)進(jìn)入夢鄉(xiāng)了

說到拿響應(yīng)結(jié)果,,這無非就是 根據(jù)相應(yīng)結(jié)果彈層 而已,我不想說什么了。

睡了。晚安世界~

 vue如何實(shí)現(xiàn)自定義多選與單選的答題功能

以上是“vue如何實(shí)現(xiàn)自定義多選與單選的答題功能”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

vue
AI