马宇豪
2024-07-22 7ac5363a676588078fdce082a45a28300479e158
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>