【通俗易懂】vue移动端项目实现一个考试题目功能,解决问题(如何循环多组单选框并获取到对应选中的值)

效果图

在这里插入图片描述

代码

<template>
  <!-- 考试题目页面 -->
  <div>
    <!-- 最外层div,循环几组题目 -->
    <div
      v-for="(item, index) of list2"
      :key="index"
      style="margin-bottom: 10px"
    >
      <!-- 这是题目 -->
      <p class="title">{{ item.title }}</p>
      <!-- 这是选项 -->
      <van-radio-group v-model="form.radioArray[index]">
        <van-radio
        class="box"
          v-for="it of item.list"
          :key="it.id"
          :label="it.id"
          :name="it.anames"
          @click="handleRadioChanges(item, it.id)"
          >{{ it.anames }}</van-radio
        >
      </van-radio-group>
    </div>
  </div>
</template>

<script>
/*
第一步:循环数组,里面有几组题目就循环多少个div

第二步:里面的van-radio-group部分也就是一组单选框,循环每组题目内的选项部分。配合上面的题目就实现了一组题目加选项的试题出来了。

第三步:选项点击后存入的变量form.radioArray[index],这里index是最外层容器循环的角标。比如第一次循环,出来了第一组题目和选项,这个时候index=0.那么这时候你点击存入的选项数据就存在form.radioArray[0],
然后第二次循环,出现第二组试题和选项,点击后选项数据存在form.radioArray[1],以此类推,就能把每一组的选项值存入到数组对应的位置,不会乱。这样数组内的第一个数据就是你第一组的题目选项。

第四步:点击选项的时候会触发方法,参数把你当前的这一组试题传过去,还有你选中项的id,然后handleRadioChanges方法接收后处理步骤:1,遍历每一个选项看哪一个和你传进来的id是一致的就代表这一项是你选中的单选框,
就把他的 pcStatus = 1,反之则=0,这个pcStatus代表是否选中,1代表选中,0代表未选中。然后把你的这一组数据去重,这一步是为了防止你点击第一组的选项1后存进去,然后点击选项2又存进去,就重复了,我们要的是单选,
所以用这个去重,如何去重呢,就是对比数组中的每组题目的id,就是那个000,001的。比如我发现我的数组内有两个id:000的那就去重,去掉前面的,我取最后一个,因为你点击的最后一项肯定是最终选中的项。最后经过筛选得出的
这个数组reslist,就是我们所有题目点击选项后的数据。里面是一个数组套几个对象,其实就是几个题目,根据每个题目中的pcStatus是否为1来判断这个题目选中的是哪一个选项。

注意事项:这里的van-radio标签上有个name属性,这个是标识符的意思,vant组件自带的属性。这个标识符需要用不同的,否则如果相同,你点击一个选项会把所有选项都选中,因为你没有区分标识符,程序认不出来你点击的是哪一个。
就会把所有相同标识符的都选中,这个地方只要不写死就行,我直接用的选项的名字当表示符,那基本就不会一样了。

最后:数据结构方面就需要跟后端配合,让后端给你传一个这样的数据结构,这样你就可以做出来,然后你选中后还是传一个这样的数据结构过去后端,后端根据每一组题目的哪一个选项pcStatus = 1来判断这一组选中的是什么,
从而给你返回这个人对应的考试分数
*/
export default {
  data() {
    return {
      form: {
        //单选部分渲染数据
        radioArray: [],
      },
      //用来去重的数组
      reslist: [],
      act: '',
      //后台传来的数据(模拟)
      list2: [
        {
          title: "第一题",
          id: "000",
          list: [
            { id: "11", anames: "第一组1", pcStatus: null },
            { id: "12", anames: "第一组2", pcStatus: null },
            { id: "13", anames: "第一组3", pcStatus: null },
          ],
        },
        {
          title: "第二题",
          id: "001",
          list: [
            { id: "21", anames: "第二组4", pcStatus: null },
            { id: "22", anames: "第二组5", pcStatus: null },
            { id: "23", anames: "第二组6", pcStatus: null },
          ],
        },
        {
          title: "第三题",
          id: "002",
          list: [
            { id: "31", anames: "第三组1", pcStatus: null },
            { id: "32", anames: "第三组2", pcStatus: null },
            { id: "33", anames: "第三组3", pcStatus: null },
          ],
        },
      ],
    };
  },
  created() {
    this.handCheck();
  },
  methods: {
    // 赋值
    handCheck() {
      const aaa = [];
      //循环每一项
      this.list2.forEach((item, index) => {
        item.list.forEach((it) => {
          //看pcStatus是否等于1把id存进去
          if (it.pcStatus === 1) {
            aaa.push(it.id);
          }
        });
        //循环时判断如果aaa的长度等于index加1.代表循环到的这一项被选中,就不动,如果不等于,代表没有选择,给null
        if (aaa.length !== index + 1) {
          aaa.push(null);
        }
      });
      //过滤一遍后给到form.radioArray
      this.form.radioArray = aaa;
    },
    // 取值
    handleRadioChanges(item, id) {
      console.log(item, id);
      this.act = id;
      //循环判断传过来的id和遍历的哪一个id一致就把他的pcStatus改为1,代表选中,否则给0带表未选中
      item.list.forEach((res) => {
        if (res.id === id) {
          res.pcStatus = 1;
        } else {
          res.pcStatus = 0;
        }
      });
      //把每一项给到新的数组去重,防止点的多个一样的存进数组。
      this.reslist.push(item);
      let newArry = this.reslist;
      //数组去重选择最后一条数据
      for (var i = 0; i < newArry.length; i++) {
        for (var j = i + 1; j < newArry.length; j++) {
          if (newArry[i].id == newArry[j].id) {
            newArry.splice(i, 1);
            j--;
          }
        }
      }
      this.reslist = newArry;
      console.log(this.reslist);
    },
  },
};
</script>

<style scoped>
.box {
  height: 50px;
  text-align: center;
  line-height: 50px;
  border-radius: 20px;
  margin-bottom: 2px;
  padding-left: 40px;
  font-size: 16px;
  border-bottom: 1px #ccc solid;
  
}
.title {
  height: 50px;
  text-align: center;
  line-height: 50px;
  margin-bottom: 2px;
  background-color: #007bbb;
    font-size: 16px;
    color: #fff;
    font-weight: bold;
}

</style>

8.30号更新!!!

经过和后端的沟通,发现他们给我的数据格式不是我上面想要的这种,要求我返回给他的数据是一个题目的id和对应选中的答案,所以经过摸索,我又想出了一套写法。特地放出来让大家看看,有遇到一样情况的可以参考

先上效果图(后端给的数据格式)

在这里插入图片描述

代码

注意事项:

1,因为后端给我传的数据格式我不好循环答案,所以我要先写死四个,然后他还有判断题,这样我就把后两个答案用v-if根据他给的类型来判断,如果是判断题那就隐藏两个就行了。
2,:name属性一定要有,后面跟不同的值用来区分,否则你点一个单选框会全部都选中的。

思路:通过van-radio-group的v-model,我给了角标index区分,会把对应选中的数据保存到form.radioArray内角标对应位置,然后我们再通过change事件传递对应的id和角标,然后我们创建一个对象存放id和答案,然后去重过后就获得了一个答案的数组。数组内answer代表答案,id代表是哪一题,这样我们把数据转成字符串穿给后端就可以了,他们能根据对应的id和答案来给我们返回成绩

<template>
  <!-- 考试题目页面 -->
  <div>
    <!-- 最外层div,循环几组题目 -->
    <div
      v-for="(item, index) of list3"
      :key="index"
      style="margin-bottom: 10px"
    >
      <!-- 这是题目 -->
      <p class="title">{{ item.question }}</p>
      <!-- 这是选项 -->
      <van-radio-group v-model="form.radioArray[index]" @change="hand(item,item.id,index)">
        <van-radio
        :name="item.optiona"
        class="box"
          >{{ item.optiona }}</van-radio
        >
        <van-radio
        :name="item.optionb"
        class="box"
          >{{ item.optionb }}</van-radio
        ><van-radio
        v-if="item.questionType=='判断题'?false:true"
        :name="item.optionc"
        class="box"
          >{{ item.optionc }}</van-radio
        ><van-radio
        v-if="item.questionType=='判断题'?false:true"
        :name="item.optiond"
        class="box"
          >{{ item.optiond }}</van-radio
        >
      </van-radio-group>
    </div>
    <div>
      <van-button icon="edit" type="primary"  @click="getinfo">完成考试</van-button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      form: {
        //单选部分渲染数据
        radioArray: [],
      },
      //获取选中项对应题目和答案
      value:[],
      //单选项循环数据
      list3:[]
    };
  },
  created() {
    this.getinfo2()
  },
  methods: {
    //单选数据
    hand(item,id,index){
        let aa={id:id,answer:this.form.radioArray[index]}
        this.value.push(aa)
         for (var i = 0; i < this.value.length; i++) {
        for (var j = i + 1; j < this.value.length; j++) {
          if (this.value[i].id == this.value[j].id) {
            this.value.splice(i, 1);
            j--;
          }
        }
      }
    },
    getinfo(){
      //退回主页面
      this.$router.go(-2)
    },
    getinfo2(){
      this.$http({
        url: this.$http.adornUrl("/app/shotexam/listOfType"),
        method: "post",
        params: this.$http.adornParams({
          type:'jx'
        }),
      }).then(({ data }) => {
        this.list3=data.list
        console.log(data);
      })
    }
  },
};
</script>

<style scoped>
.box {
  height: 50px;
  text-align: center;
  line-height: 50px;
  border-radius: 20px;
  margin-bottom: 2px;
  padding-left: 40px;
  font-size: 16px;
  border-bottom: 1px #ccc solid;
  
}
.title {
  text-align: center;
  line-height: 25px;
  margin-bottom: 2px;
  border: solid #ccc 1px;
  background-color: #1989fa;
    font-size: 16px;
    color: #fff;
}

</style>

传给后端的数据格式图

这就是我们经过去重和筛选后,获取到的每一题的数据格式传给后端的。
answer代表答案,id代表是哪一题
在这里插入图片描述

  • 10
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

接口写好了吗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值