From 7ac5363a676588078fdce082a45a28300479e158 Mon Sep 17 00:00:00 2001
From: 马宇豪 <978517621@qq.com>
Date: 星期一, 22 七月 2024 14:09:22 +0800
Subject: [PATCH] 提交

---
 pages/tabBar/firstPage/exam.vue |  501 +++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 369 insertions(+), 132 deletions(-)

diff --git a/pages/tabBar/firstPage/exam.vue b/pages/tabBar/firstPage/exam.vue
index 72f2f06..9a64d19 100644
--- a/pages/tabBar/firstPage/exam.vue
+++ b/pages/tabBar/firstPage/exam.vue
@@ -16,13 +16,17 @@
       </view>
     </view>
     <view class="m-p-15">
-      <u-divider
-          :text="'第'+ (currentIndex+1) +'题'"
-          textColor="#2979ff"
-          lineColor="#2979ff"
-          textSize="16"
-          style="margin: 40px 0"
-      ></u-divider>
+      <view class="timer" v-if="viewType == 1">
+        <u--image :showLoading="true" :src="sandPic" width="18px" height="18px"></u--image>
+        <view class="timer-b">
+          <span>开考时间:</span>
+          <span>{{ formattedTime }}</span>
+        </view>
+      </view>
+      <view class="top">
+        <view class="ind">{{'第 ' + (curTotalIndex+1) + ' 题'}}</view>
+        <view class="panelBtn" @click="openPanel">答题卡</view>
+      </view>
       <view class="questions">
         <view class="title">
           <span>【{{currentQ.questionType == 1?'单选题':currentQ.questionType == 2?'多选题':'判断题'}}】</span>
@@ -31,8 +35,10 @@
         <view class="content">
           <u-checkbox-group
               v-if="currentQ.questionType==2"
-              v-model="currentQ.exExerciseAnswer.answer"
+              v-model="currentB"
               placement="column"
+              class="myRadio"
+              iconPlacement="right"
               :disabled="viewType==1?false:true"
               @change="checkboxChange"
           >
@@ -42,10 +48,11 @@
                 :key="index"
                 :label="item.prefix +':'+ item.content"
                 :name="item.prefix"
+                :class="{'picked': currentB.includes(item.prefix)}"
             >
             </u-checkbox>
           </u-checkbox-group>
-          <u-radio-group v-model="currentQ.exExerciseAnswer.answer" placement="column" @change="groupChange" v-if="currentQ.questionType==1||currentQ.questionType==3" :disabled="viewType==1?false:true">
+          <u-radio-group v-model="currentA" class="myRadio" iconPlacement="right" placement="column" @change="groupChange" v-if="currentQ.questionType==1||currentQ.questionType==3" :disabled="viewType==1?false:true">
             <u-radio
                 :customStyle="{marginBottom: '15px'}"
                 v-for="(item, index) in currentQ.content.items"
@@ -54,18 +61,18 @@
                 :label="item.prefix +':'+ item.content"
                 :name="item.prefix"
                 @change="radioChange"
+                :class="{'picked': currentA == item.prefix}"
             >
             </u-radio>
           </u-radio-group>
+
           <u-button style="width: 80%;margin: 30px auto"
-                    v-if="viewType ==1 && (currentQ.exExerciseAnswer && currentQ.exExerciseAnswer.answer && (typeof currentQ.exExerciseAnswer.answer == String?currentQ.exExerciseAnswer.answer !=='':currentQ.exExerciseAnswer.answer.length>0))"
-                    type="primary" shape="circle" text="确认答案"
-                    @click="confirmAnswer">
-          </u-button>
+                    v-if="currentQ.questionType==2 && currentB.length>0 && viewType ==1"
+                    type="primary" shape="circle" text="确认答案" @click="confirmAnswer"></u-button>
           <view class="answers" v-if="viewType == 2 && currentQ.studentAnswer && currentQ.studentAnswer.answer">
             <view>你的答案:
-              <span v-if="currentQ.questionType==2" :class="currentQ.answer == currentQ.studentAnswer.answer?'right':'wrong'">{{currentQ.studentAnswer.answer}}</span>
-              <span v-else :class="currentQ.answer == currentQ.studentAnswer.answer?'right':'wrong'">{{currentQ.studentAnswer.answer}}</span>
+              <span :class="currentQ.answer == currentQ.studentAnswer.answer?'right':'wrong'">{{currentQ.studentAnswer.answer}}</span>
+<!--              <span v-else :class="currentQ.answer == currentQ.studentAnswer.answer?'right':'wrong'">{{currentQ.studentAnswer.answer}}</span>-->
             </view>
             <view>正确答案:<span class="right">{{currentQ.answer}}</span></view>
           </view>
@@ -74,20 +81,34 @@
     </view>
     <view class="btns">
       <u-button style="width: 30%" type="error" shape="circle" size="small" text="上一题" @click="prevQ"></u-button>
-<!--      <u-button style="width: 30%" type="error" shape="circle" plain size="small" text="查看答案" @click="showA"></u-button>-->
-      <u-button v-if="currentIndex<questionList.length - 1" style="width: 30%" type="error" shape="circle" size="small" text="下一题" @click="nextQ"></u-button>
-      <u-button v-if="currentIndex==questionList.length - 1 && viewType == 1" style="width: 30%" type="primary" shape="circle" size="small" text="保存退出" @click="endExam"></u-button>
-      <u-button v-if="currentIndex==questionList.length - 1 && viewType == 2" style="width: 30%" type="primary" shape="circle" size="small" text="退出" @click="goBack"></u-button>
+      <u-button v-if="viewType == 1" style="width: 30%" type="primary" shape="circle" size="small" text="交卷" @click="endExam"></u-button>
+      <u-button v-if="curTotalIndex < idList.length - 1" style="width: 30%" type="error" shape="circle" size="small" text="下一题" @click="nextQ"></u-button>
+      <u-button v-if="curTotalIndex == idList.length - 1 && viewType == 2" style="width: 30%" type="primary" shape="circle" size="small" text="退出" @click="goBack"></u-button>
     </view>
+    <u-popup :show="showPanel" :round="40" mode="right" @close="close" @open="open">
+      <view class="panel">
+        <view :class="item.passed==1?'right-a':item.passed==0?'wrong-a':''" v-for="(item,index) in idList" @click="toQuestion(item,index)">
+          {{index + 1}}
+        </view>
+      </view>
+    </u-popup>
 	</view>
 </template>
 
 <script>
 import {
   getExamIdIdList,
-  getExamListByIds, postExamAnswer,postEndExam
+  getExamListByIds,
+  postExamAnswer,
+  postEndExam,
+  getQuestionIdList,
+  getErrorsIdList,
+  getQuestionByIds,
+  postExerciseAnswer
 } from '../../../api/wearhouse.js'
 import Vue from 'vue'
+import {loginOut} from "../../../api";
+import sand from '../../../static/sand.gif'
 
 export default {
 	components: {
@@ -96,14 +117,19 @@
 	data() {
 		return {
       statusBarHeight: 0,
+      sandPic: sand,
       bank: {},
       idList: [],
       questionList: [],
-      currentIndex: 0,
+      curTotalIndex: 0,
       currentQ: {},
       currentA: '',
       currentB: [],
-      viewType: null
+      viewType: null,
+      startTime: null,
+      elapsedTime: 0,
+      timerInterval: null,
+      showPanel: false
 		}
 	},
   onReady(){
@@ -112,54 +138,94 @@
     this.statusBarHeight = uni.getSystemInfoSync()['statusBarHeight']
     this.bank = e.bank && JSON.parse(decodeURIComponent(e.bank))
     this.viewType = e.type && JSON.parse(decodeURIComponent(e.type))
+    this.startTime = this.bank.startTime?this.bank.startTime:0
     this.getQuestionIds(this.bank.paperId)
+
 	},
 	onShow(){
 
 	},
 	mounted() {
-
+    if(this.viewType == 1){
+      this.startTimer()
+    }
 	},
   computed: {
-
+    formattedTime() {
+      const totalSeconds = Math.floor(this.elapsedTime / 1000);
+      const hours = Math.floor(totalSeconds / 3600);
+      const minutes = Math.floor((totalSeconds % 3600) / 60);
+      const seconds = totalSeconds % 60;
+      return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
+    }
   },
 	beforeUnmount() {
 
   },
 
   beforeDestroy() {
-
+    clearInterval(this.timerInterval)
   },
-
   methods: {
+    async openPanel(){
+      const t = this
+      t.showPanel = true
+      const res = await getExamIdIdList({paperId: this.bank.paperId,viewType: this.viewType})
+      if(res.code == 200){
+        let list = res.data || []
+        if(list.length>0){
+          t.idList = list.sort((a, b) => a.id - b.id)
+        }else{
+          t.idList = []
+          uni.showToast({
+            title: '本题库暂无题目',
+            duration: 1000
+          });
+        }
+      }else{
+        uni.$u.toast(res.message)
+      }
+    },
+
+    async toQuestion(item,index){
+      this.curTotalIndex = index
+      const curIdList = this.idList.slice(this.curTotalIndex,this.curTotalIndex + 20)
+      this.currentId = this.idList[this.curTotalIndex].id
+      await this.getQuestionsByIds(curIdList)
+      this.currentQ = this.questionList[0]
+      this.showAnswer()
+      this.showPanel = false
+    },
+
+    open() {
+      // console.log('open');
+    },
+    close() {
+      this.showPanel = false
+      // console.log('close');
+    },
+
+    updateTimer() {
+      const currentTime = Date.now();
+      this.elapsedTime = currentTime - this.startTimestamp;
+    },
+    startTimer() {
+      this.startTimestamp = this.startTime ? this.startTime : Date.now();
+      this.timerInterval = setInterval(this.updateTimer, 1000);
+    },
+
     async getQuestionIds(id){
       const res = await getExamIdIdList({paperId: id,viewType: this.viewType})
       if(res.code == 200){
         let list = res.data || []
         if(list.length>0){
-          this.idList = list
-          getExamListByIds({paperId: id,questionIds: list.map(i=>i.id)}).then(re=>{
-            if(re.code == 200){
-              this.questionList = re.data.map(i=>{
-                i.content = JSON.parse(i.content)
-                Vue.set(i,'passed',null)
-                if(i.studentAnswer){
-                  i.exExerciseAnswer.answer = i.studentAnswer.answer
-                  i.exExerciseAnswer.passed = i.studentAnswer.passed
-                }
-                if(i.questionType == 2){
-                  if(i.exExerciseAnswer.answer){
-                    i.exExerciseAnswer.answer = i.exExerciseAnswer.answer.split(',')
-                  }
-                }
-                return i
-              })
-              this.currentQ = this.questionList[this.currentIndex]
-              console.log(this.currentQ,'当前问题')
-            }else{
-              uni.$u.toast(res.message)
-            }
-          })
+          this.idList = list.sort((a, b) => a.id - b.id)
+          this.curTotalIndex = this.bank.questionId ? this.idList.findIndex(i=>i.id == this.bank.questionId):0
+          const curIdList = this.idList.slice(this.curTotalIndex,this.curTotalIndex + 20)
+          this.currentId = this.idList[this.curTotalIndex].id
+          await this.getQuestionsByIds(curIdList)
+          this.currentQ = this.questionList[0]
+          this.showAnswer()
         }else{
           this.idList = []
           uni.showToast({
@@ -171,11 +237,50 @@
         uni.$u.toast(res.message)
       }
     },
+
+    async getErrorIds(id){
+      const res = await getErrorsIdList({bankId: id})
+      if(res.code == 200){
+        let list = res.data || []
+        if(list.length>0){
+          this.idList = list.sort((a, b) => a - b)
+          this.curTotalIndex = 0
+          const curIdList = this.idList.slice(0,20)
+          this.currentId = this.idList[0].id
+          await this.getQuestionsByIds(curIdList)
+          this.currentQ = this.questionList[0]
+          this.showAnswer()
+        }else{
+          this.idList = []
+          uni.showToast({
+            title: '本题库暂无错题',
+            duration: 1000
+          });
+        }
+      }else{
+        uni.$u.toast(res.message)
+      }
+    },
+
+    async getQuestionsByIds(idList){
+      const res = await getExamListByIds({paperId: this.bank.paperId,questionIds: idList.map(i=>i.id)})
+      if(res.code == 200){
+        this.questionList = res.data.map(i=>{
+          i.content = JSON.parse(i.content)
+          return i
+        })
+      }else{
+        uni.$u.toast(res.message)
+      }
+    },
+
     checkboxChange(n) {
       console.log('change', n);
     },
     groupChange(n) {
-      console.log('groupChange', n);
+      if(this.currentA !== ''){
+        this.confirmAnswer()
+      }
     },
     radioChange(n) {
       console.log('radioChange', n);
@@ -183,7 +288,7 @@
 
     confirmAnswer(){
       const data = {
-        answer: this.currentQ.questionType==2?this.currentQ.exExerciseAnswer.answer.join(','):this.currentQ.exExerciseAnswer.answer,
+        answer: this.currentQ.questionType==2?this.currentB.join(','):this.currentA,
         paperId: this.bank.paperId,
         questionId: this.currentQ.id,
         studentId: uni.getStorageSync('uid')
@@ -191,7 +296,9 @@
       postExamAnswer(data).then(res=>{
         if(res.code == 200){
           this.currentQ.passed = res.data
-          uni.$u.toast('答案已提交')
+          // this.currentQ.exExerciseAnswer.passed = res.data.passed
+          this.currentQ.studentAnswer.answer = res.data.answer
+          // uni.$u.toast('答案已提交')
         }else{
           uni.$u.toast(res.message)
         }
@@ -199,9 +306,29 @@
     },
 
     prevQ(){
-      if(this.currentIndex - 1>=0){
-        this.currentIndex--
-        this.currentQ = this.questionList[this.currentIndex]
+      if(this.curTotalIndex - 1>=0){
+        this.curTotalIndex--
+        if(this.curTotalIndex == 0){
+          this.getQuestionsByIds([this.idList[0]]).then(()=>{
+            // this.currentQ = this.questionList[this.questionList.length-1]
+            this.$set(this, 'currentQ', this.questionList[this.questionList.length-1])
+            this.showAnswer()
+          })
+        }else{
+          this.currentId = this.idList[this.curTotalIndex].id
+          if(this.questionList.find(i=>i.id == this.currentId)){
+            this.$set(this, 'currentQ', this.questionList.find(i=>i.id == this.currentId))
+            this.showAnswer()
+          }else{
+            const startIndex = Math.max(0, this.curTotalIndex - 19);
+            const curIdList = this.idList.slice(startIndex, this.curTotalIndex+1);
+            this.getQuestionsByIds(curIdList).then(()=>{
+              // this.currentQ = this.questionList[this.questionList.length-1]
+              this.$set(this, 'currentQ', this.questionList[this.questionList.length-1])
+              this.showAnswer()
+            })
+          }
+        }
       }else{
         uni.showToast({
           title: '已经是第一题了',
@@ -211,76 +338,45 @@
     },
 
     nextQ(){
-      if(this.currentQ.passed==null && this.currentQ.exExerciseAnswer.passed == null){
-        uni.$u.toast('请先完成当前题目')
-        return
-      }
-      if(this.currentIndex + 1<this.questionList.length){
-        this.currentIndex++
-        this.currentQ = this.questionList[this.currentIndex]
+      if(this.curTotalIndex + 1<this.idList.length){
+        this.curTotalIndex++
+        this.currentId = this.idList[this.curTotalIndex].id
+        if(this.questionList.find(i=>i.id == this.currentId)){
+          this.$set(this, 'currentQ', this.questionList.find(i=>i.id == this.currentId))
+          this.showAnswer()
+        }else{
+          const curIdList = this.idList.slice(this.curTotalIndex,this.curTotalIndex + 20)
+          this.getQuestionsByIds(curIdList).then(()=>{
+            this.$set(this, 'currentQ', this.questionList[0])
+            // this.currentQ = this.questionList[0]
+            this.showAnswer()
+          })
+        }
       }else{
         uni.showToast({
           title: '已经是最后一题了',
           duration: 1000
         });
       }
-      console.log(this.currentQ,'current')
     },
 
-    endExam(){
-      if(this.currentQ.passed==null && this.currentQ.exExerciseAnswer.passed == null){
-        uni.$u.toast('请先完成当前题目')
-        return
-      }
-      const data = {
-        id: this.bank.id,
-        // paperId: this.bank.paperId,
-        // studentId: uni.getStorageSync('uid')
-      }
-      postEndExam(data).then(res=>{
-        if(res.code == 200){
-          uni.showToast({
-            title: '本次考试结束',
-            duration: 1500
-          })
-          setTimeout(()=>{
-            const url = uni.getStorageSync("prevPage");
-            if(url){
-              if(url == '/pages/tabBar/firstPage/firstPage'){
-                uni.reLaunch({
-                  url:'/pages/tabBar/firstPage/firstPage'
-                });
-              }else{
-                uni.navigateTo({
-                  url: url
-                })
-              }
-            } else{
-              uni.reLaunch({
-                url: '/pages/tabBar/firstPage/firstPage'
-              })
-            }
-          },1500)
+    showAnswer(){
+      if(this.currentQ.studentAnswer && this.currentQ.studentAnswer.answer){
+        if(this.currentQ.questionType == 2){
+          this.currentB = this.currentQ.studentAnswer.answer.split(',')
         }else{
-          uni.$u.toast(res.message)
+          this.currentA = this.currentQ.studentAnswer.answer
         }
-      })
+      }else{
+        this.currentB = []
+        this.currentA = ''
+      }
     },
 
-    showA(){
-      uni.showModal({
-        title: '正确答案',
-        content: this.currentQ.answer,
-        showCancel: false,
-        success: function (res) {
-          if (res.confirm) {
-            console.log('用户点击确定');
-          }
-        }
-      });
-    },
+
 
     goBack(){
+      clearInterval(this.timerInterval);
       const url = uni.getStorageSync("prevPage");
       if(url){
         uni.reLaunch({
@@ -291,6 +387,51 @@
           url: '/pages/tabBar/firstPage/firstPage'
         })
       }
+    },
+
+    endExam(){
+      const t = this
+      uni.showModal({
+        title: '提示',
+        content: '交卷后将无法再次做题,是否继续?',
+        success: async function (res) {
+          if (res.confirm) {
+            const data = {
+              id: t.bank.id,
+            }
+            postEndExam(data).then(res=>{
+              if(res.code == 200){
+                uni.showToast({
+                  title: '本次考试结束',
+                  duration: 1500
+                })
+                setTimeout(()=>{
+                  const url = uni.getStorageSync("prevPage");
+                  if(url){
+                    if(url == '/pages/tabBar/firstPage/firstPage'){
+                      uni.reLaunch({
+                        url:'/pages/tabBar/firstPage/firstPage'
+                      });
+                    }else{
+                      uni.navigateTo({
+                        url: url
+                      })
+                    }
+                  } else{
+                    uni.reLaunch({
+                      url: '/pages/tabBar/firstPage/firstPage'
+                    })
+                  }
+                },1500)
+              }else{
+                uni.$u.toast(res.message)
+              }
+            })
+          } else if (res.cancel) {
+            console.log('用户点击取消');
+          }
+        }
+      })
     }
 	}
 };
@@ -322,15 +463,110 @@
   margin-right: 10rpx;
 }
 
+.panel{
+  height: 100vh;
+  width: 60vw;
+  background: #f2f2f2;
+  overflow-y: auto;
+  padding: 15px;
+  box-sizing: border-box;
+  display: grid;
+  grid-template-columns: repeat(5, 1fr);
+  gap: 15px;
+  grid-auto-rows: min-content;
+
+  &>view{
+    padding: 5px;
+    min-width: calc(20% - 12px);
+    border-radius: 4px;
+    text-align: center;
+    background: rgba(41,121,255,0);
+    border: 1px solid #fff;
+    color: #333;
+    background: #f2f2f2;
+    transition: all 100ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
+    box-shadow: 0px -6px 10px rgba(255, 255, 255, 1), 0px 4px 15px rgba(0, 0, 0, 0.15);
+    cursor: pointer;
+
+    &:active {
+      box-shadow: 0 15px 20px rgba(0, 0, 0, 0.02);
+      &:after {
+        box-shadow: inset 0px -2px 5px rgb(255, 255, 255),
+        inset 0px 2px 5px rgba(0, 0, 0, 0.15);
+      }
+    }
+
+  }
+
+  .right-a{
+    background: rgba(41,121,255,.1);
+    border: 1px solid #2979ff;
+    color: #2979ff;
+  }
+
+  .wrong-a{
+    background: rgba(237,100,100,.1);
+    border: 1px solid #ed6464;
+    color: #ed6464;
+  }
+}
+
 .m-p-15{
   width: 100%;
   padding: 0 15px;
   box-sizing: border-box;
 
+  .timer{
+    background: #fff;
+    width: auto;
+    margin: 10px auto;
+    padding: 10px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    border-radius: 99px;
+
+    .timer-b{
+      margin-left: 5px;
+      display: flex;
+      align-items: center;
+      span:last-of-type{
+        font-size: 16px;
+        font-weight: bold;
+        color: #0f7ff9;
+      }
+    }
+
+  }
+
   .top{
-    text-align: center;
-    margin: 20rpx 0;
-    font-weight: bolder;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    margin: 20px 0;
+
+    .ind{
+      font-size: 16px;
+      font-weight: bold;
+      color: #2979ff
+    }
+    .panelBtn{
+      width: 25%;
+      margin: 0;
+      text-align: center;
+      padding: 4px 0;
+      border-radius: 4px;
+      color: #2979ff;
+      background: #f5f7fa;
+      border: 1px solid rgba(41,121,255,.4);
+      box-shadow: 0px -6px 10px rgba(255, 255, 255, 1), 0px 4px 15px rgba(0, 0, 0, 0.1);
+      transition: box-shadow .25s ease !important;
+    }
+    .panelBtn:active{
+      background: #f5f7fa;
+      border: 1px solid rgba(41,121,255,1);
+      box-shadow: 0 15px 20px rgba(0, 0, 0, 0.02);
+    }
   }
 
   .questions{
@@ -387,31 +623,32 @@
   border-radius: 8px;
   background: #f5f7fa;
   border: 1px solid #fff;
-  box-shadow: 7px 7px 15px rgba(55, 84, 170, .15),
-  -7px -7px 20px rgba(255, 255, 255, 1),
-  inset 0px 0px 4px rgba(255, 255, 255, .2),
-  inset 7px 7px 15px rgba(55, 84, 170, 0),
-  inset -7px -7px 20px rgba(255, 255, 255, 0),
-  0px 0px 4px rgba(255, 255, 255, 0) !important;
+  box-shadow: 0px -6px 10px rgba(255, 255, 255, 1), 0px 4px 15px rgba(0, 0, 0, 0.1);
   transition: box-shadow .25s ease !important;
 }
 
+.myRadio{
+  /deep/ .picked{
+    border: 2px solid #2979ff;
+
+    .u-radio__icon-wrap,.u-checkbox__icon-wrap{
+      border-color: #fff !important;
+      span{
+        color: #fff !important;
+      }
+    }
+    span{
+      color: #2979ff
+    }
+  }
+}
+
 /deep/ .u-checkbox:active {
-  box-shadow: 7px 7px 15px rgba(55, 84, 170, .15),
-  -7px -7px 20px rgba(255, 255, 255, 1),
-  inset 0px 0px 4px rgba(255, 255, 255, 0),
-  inset 7px 7px 15px rgba(55, 84, 170, .15),
-  inset -7px -7px 20px rgba(255, 255, 255, 1),
-  0px 0px 4px rgba(255, 255, 255, .2) !important;
+  box-shadow: 0 15px 20px rgba(0, 0, 0, 0.02);
 }
 
 /deep/ .u-radio:active {
-  box-shadow: 7px 7px 15px rgba(55, 84, 170, .15),
-  -7px -7px 20px rgba(255, 255, 255, 1),
-  inset 0px 0px 4px rgba(255, 255, 255, 0),
-  inset 7px 7px 15px rgba(55, 84, 170, .15),
-  inset -7px -7px 20px rgba(255, 255, 255, 1),
-  0px 0px 4px rgba(255, 255, 255, .2) !important;
+  box-shadow: 0 15px 20px rgba(0, 0, 0, 0.02);
 }
 
 </style>

--
Gitblit v1.9.2