黄何裕
2024-07-12 ca9a92cfc2f2bd8c724020ee5e46094633eb80a5
src/views/exam/index.vue
@@ -1,5 +1,7 @@
<template>
  <div class="exam-container w-screen h-screen bg-slate-50 relative overflow-hidden">
  <div
    class="exam-container w-screen h-screen bg-slate-50 relative overflow-hidden"
  >
    <div class="top-bg bg-blue-500"></div>
    <div class="exam-content">
      <div class="exam-wrapper container mx-auto h-full flex flex-col">
@@ -24,8 +26,10 @@
          <!-- 答题卡区 -->
          <div class="answer-wrapper answer-left mr-8 shadow-xl p-4 box-border">
            <div class="wrapper h-full flex flex-col items-center">
              <div class="title-wrapper w-full flex justify-between items-center mb-5">
                <div class="title text-xl font-semibold ">答题卡</div>
              <div
                class="title-wrapper w-full flex justify-between items-center mb-5"
              >
                <div class="title text-xl font-semibold">答题卡</div>
                <AnswerTag></AnswerTag>
              </div>
@@ -40,9 +44,13 @@
              </div>
              <div class="submit-wrapper">
                <el-button type="primary" class="submit-button" @click="submitExamHandle">提交试卷</el-button>
                <el-button
                  type="primary"
                  class="submit-button"
                  @click="submitExamHandle"
                  >提交试卷</el-button
                >
              </div>
            </div>
          </div>
@@ -50,15 +58,20 @@
          <div class="answer-wrapper answer-right grow shadow-xl p-4">
            <div class="wrapper h-full flex flex-col">
              <div class="title-wrapper w-full flex mb-5">
                <div class="title text-xl font-semibold ">{{ examType[currentType] }} ({{
                  examStore.getActiveQuestion.questionScore }}分)
                <div class="title text-xl font-semibold">
                  {{ examType[currentType] }} ({{
                    examStore.getActiveQuestion.questionScore
                  }}分)
                </div>
              </div>
              <div class="main-wrapper w-full grow relative my-5">
                <div class="main-content absolute top-0 bottom-0 w-full">
                  <Transition appear name="fade-transform" mode="out-in">
                    <component :is="typeComponent[currentType]" :key="currentIndex"></component>
                    <component
                      :is="typeComponent[currentType]"
                      :key="currentIndex"
                    ></component>
                  </Transition>
                </div>
              </div>
@@ -66,10 +79,17 @@
              <div class="tool-wrapper flex justify-end">
                <div class="button-container flex items-center">
                  <div class="button-item">
                    <el-button class="tool-button" @click="prevQuestion">上一题</el-button>
                    <el-button class="tool-button" @click="prevQuestion"
                      >上一题</el-button
                    >
                  </div>
                  <div class="button-item">
                    <el-button class="tool-button" type="primary" @click="nextQuestion">下一题</el-button>
                    <el-button
                      class="tool-button"
                      type="primary"
                      @click="nextQuestion"
                      >下一题</el-button
                    >
                  </div>
                </div>
              </div>
@@ -78,7 +98,6 @@
        </div>
      </div>
    </div>
    <!-- 退出考试提示弹窗 -->
    <el-dialog v-model="quitDialog" title="注意" width="500">
@@ -89,9 +108,7 @@
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="quitDialog = false">继续作答</el-button>
          <el-button type="danger" @click="confirmQuit">
            确定退出
          </el-button>
          <el-button type="danger" @click="confirmQuit"> 确定退出 </el-button>
        </div>
      </template>
    </el-dialog>
@@ -113,45 +130,50 @@
    </el-dialog>
    <!-- 考试时间弹窗 -->
    <el-dialog v-model="timeDialog" align-center width="500" :close-on-click-modal="false"
      :close-on-press-escape="false" :show-close="false">
    <el-dialog
      v-model="timeDialog"
      align-center
      width="500"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      :show-close="false"
    >
      <div class="dialog-container flex flex-col items-center">
        <div class="icon-container">
          <el-icon :size="50" color="#3680fa">
            <Timer />
          </el-icon>
        </div>
        <div class="dialog-info">
          考试结束,系统自动收卷中.......
        </div>
        <div class="dialog-info">考试结束,系统自动收卷中.......</div>
      </div>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, watchEffect, watch, onMounted } from 'vue';
import { storeToRefs } from 'pinia';
import { Close, Timer } from '@element-plus/icons-vue';
import AnswerTag from './components/answer-tag/index.vue';
import AnswerProgress from './components/answer-progress/index.vue';
import AnswerSheet from './components/answer-sheet/index.vue';
import AnswerTime from './components/answer-time/index.vue';
import { ref, watchEffect, watch, onMounted ,onUnmounted} from "vue";
import { storeToRefs } from "pinia";
import { Close, Timer } from "@element-plus/icons-vue";
import AnswerTag from "./components/answer-tag/index.vue";
import AnswerProgress from "./components/answer-progress/index.vue";
import AnswerSheet from "./components/answer-sheet/index.vue";
import AnswerTime from "./components/answer-time/index.vue";
import AnswerSingle from './components/answer-main/answer-single/index.vue';
import AnswerMultiple from './components/answer-main/answer-multiple/index.vue';
import AnswerAudio from './components/answer-main/answer-audio/index.vue';
import AnswerFill from './components/answer-main/answer-fill/index.vue';
import AnswerDetermine from './components/answer-main/answer-determine/index.vue';
import AnswerShort from './components/answer-main/answer-short/index.vue';
import AnswerCount from './components/answer-main/answer-count/index.vue';
import AnswerSingle from "./components/answer-main/answer-single/index.vue";
import AnswerMultiple from "./components/answer-main/answer-multiple/index.vue";
import AnswerAudio from "./components/answer-main/answer-audio/index.vue";
import AnswerFill from "./components/answer-main/answer-fill/index.vue";
import AnswerDetermine from "./components/answer-main/answer-determine/index.vue";
import AnswerShort from "./components/answer-main/answer-short/index.vue";
import AnswerCount from "./components/answer-main/answer-count/index.vue";
import { useExamStore, useUserStore } from '@/store/index.js';
import { useRouter } from 'vue-router';
import { useExamStore, useUserStore } from "@/store/index.js";
import { useRouter } from "vue-router";
import { submitExam } from '@/api/modules/exam.js';
import useWebScoket from '@/hooks/useWebScoket.js';
import { submitExam } from "@/api/modules/exam.js";
import useWebScoket from "@/hooks/useWebScoket.js";
import { ElMessage, ElMessageBox } from "element-plus";
const router = useRouter();
@@ -159,7 +181,8 @@
const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);
const { currentType, currentIndex, examDetail, examType, examInfo } = storeToRefs(examStore);
const { currentType, currentIndex, examDetail, examType, examInfo } =
  storeToRefs(examStore);
const typeComponent = {
  1: AnswerSingle,
@@ -181,11 +204,11 @@
//   heartBeatData: 'ping'
// });
const { status, message, error, connect, disconnect, sendMessage } = useWebScoket({
  url: '//192.168.3.64:8000/websocket/' + userInfo.value.id,
  heartBeatData: 'ping'
});
const { status, message, error, connect, disconnect, sendMessage } =
  useWebScoket({
    url: "//192.168.3.64:8000/websocket/" + userInfo.value.id,
    heartBeatData: "ping",
  });
// 上一题
const prevQuestion = () => {
@@ -220,19 +243,18 @@
        currentType.value = typeQuestion.questionType;
        currentIndex.value = typeQuestion.questionList.length - 1;
      }
    } else if (currentIndex.value < 0) {
      tempIndex--;
      if (examDetail.value[tempIndex]) {
        currentType.value = examDetail.value[tempIndex].questionType;
        currentIndex.value = examDetail.value[tempIndex].questionList.length - 1;
        currentIndex.value =
          examDetail.value[tempIndex].questionList.length - 1;
      } else {
        currentType.value = typeQuestion.questionType;
        currentIndex.value = 0;
      }
    }
  }
};
// 退出考试
@@ -262,18 +284,20 @@
const timeOut = () => {
  const temp = {
    ...examInfo.value,
    titleList: examDetail.value
    titleList: examDetail.value,
  };
  timeDialog.value = true;
  resetAllDialog();
  disconnect();
  submitExam(temp).then(res => {
    returnBack();
  }).catch(() => {
    returnBack();
  });
  submitExam(temp)
    .then((res) => {
      returnBack();
    })
    .catch(() => {
      returnBack();
    });
};
const returnBack = () => {
@@ -284,30 +308,55 @@
watchEffect(() => {
  let progress = 0;
  examDetail.value.forEach(item => {
    item.questionList.forEach(question => {
      if (question.answer || (Array.isArray(question.answerList) && question.answerList.length)) {
  examDetail.value.forEach((item) => {
    item.questionList.forEach((question) => {
      if (
        question.answer ||
        (Array.isArray(question.answerList) && question.answerList.length)
      ) {
        progress += 1;
      }
    });
  });
  examStore.setProgress(progress);
});
const answerTime = ref()
watch(() => message.value, (msg) => {
  if(msg.commend=="delayed"){
    answerTime.value.addTime(msg.data.addTimeM)
  }else if(msg.commend=="forceSubmit"){
    confirmSubmit()
const answerTime = ref();
watch(
  () => message.value,
  (msg) => {
    if (msg.commend == "delayed") {
      answerTime.value.addTime(msg.data.addTimeM);
    } else if (msg.commend == "forceSubmit") {
      confirmSubmit();
    }
  }
});
);
// document.addEventListener('blur', function() {
//     console.log('页面失去焦点');
// });
const warningNum = ref(0)
const handleBlur = () => {
  if (document.visibilityState === "visible") {
  } else if (document.visibilityState === "hidden") {
    ElMessageBox.alert("考试过程中请勿离开考试页面!警告三次将强制收卷", "警告", {
      confirmButtonText: "确定",
    });
    warningNum.value++
    if (warningNum.value==3) {
      timeOut();
    }
  }
};
// -----------------------------------生命周期
onMounted(() => {
  // 连接webscoket
  connect();
  document.addEventListener("visibilitychange", handleBlur);
});
onUnmounted(()=>{
  document.removeEventListener("visibilitychange", handleBlur);
})
</script>
<style lang="scss" scoped>