From f763e0bc88efa373ea0cedfdb1abbdc85046097b Mon Sep 17 00:00:00 2001
From: ZhangXianQiang <1135831638@qq.com>
Date: 星期三, 19 六月 2024 15:25:25 +0800
Subject: [PATCH] feat:考试音频题

---
 src/components/ExamAudio/index.vue                              |  157 +++++++++++++++++++++++++++++++
 src/components/ExamInfo/index.vue                               |   15 ++
 src/views/exam/components/answer-main/answer-multiple/index.vue |    2 
 src/views/exam/components/answer-main/answer-audio/index.vue    |    2 
 public/test.mp3                                                 |    0 
 src/store/modules/exam.js                                       |   70 +++++++++++++
 components.d.ts                                                 |    1 
 src/views/exam/components/answer-main/answer-single/index.vue   |    2 
 src/views/exam/index.vue                                        |    6 +
 9 files changed, 245 insertions(+), 10 deletions(-)

diff --git a/components.d.ts b/components.d.ts
index 8742b82..29dd682 100644
--- a/components.d.ts
+++ b/components.d.ts
@@ -29,6 +29,7 @@
     ElTabPane: typeof import('element-plus/es')['ElTabPane']
     ElTabs: typeof import('element-plus/es')['ElTabs']
     ElTag: typeof import('element-plus/es')['ElTag']
+    ExamAudio: typeof import('./src/components/ExamAudio/index.vue')['default']
     ExamInfo: typeof import('./src/components/ExamInfo/index.vue')['default']
     ExamInfoDialog: typeof import('./src/components/ExamInfoDialog/index.vue')['default']
     Header: typeof import('./src/components/Header/index.vue')['default']
diff --git a/public/test.mp3 b/public/test.mp3
new file mode 100644
index 0000000..7b0f622
--- /dev/null
+++ b/public/test.mp3
Binary files differ
diff --git a/src/components/ExamAudio/index.vue b/src/components/ExamAudio/index.vue
new file mode 100644
index 0000000..c96e17d
--- /dev/null
+++ b/src/components/ExamAudio/index.vue
@@ -0,0 +1,157 @@
+<template>
+  <div class="audio-container">
+    <div class="audio_wrap_content">
+      <audio ref="audioRef" @play="playFunc" @pause="pauseFunc" @timeupdate="timeupdateFunc"
+        @loadedmetadata="onLoadedmetadata" @ended="handleEnd">
+        <source :src="audioSrc" />
+      </audio>
+      <div class="cudio_control_content">
+        <el-icon :size="32" color="#3680fa" @click="startPlayOrPause" class="cursor-pointer">
+          <VideoPlay v-show="!audio.playing" />
+          <VideoPause v-show="audio.playing" />
+        </el-icon>
+        <div class="slider">
+          <span>{{ formattedCurrentTime }}</span>
+          <div @mousedown="audio.dragState = true" @mouseup="audio.dragState = false"
+            @mouseleave="audio.dragState = false"><el-slider v-model="sliderTime" size="small" :max="audio.maxTime"
+              :show-tooltip="false" @change="onChange"></el-slider></div>
+          <span>{{ formattedMaxTime }}</span>
+        </div>
+      </div>
+    </div>
+
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onBeforeUnmount } from 'vue';
+import { VideoPlay, VideoPause } from '@element-plus/icons-vue';
+
+const props = defineProps({
+  audioSrc: {
+    type: String,
+    required: true,
+    default: ''
+  }
+});
+const sliderTime = ref(0);
+const audioRef = ref(null);
+
+const audio = ref({
+  maxTime: 0,
+  currentTime: 0,
+  playing: false,
+  dragState: false
+});
+
+
+const formatTime = (second) => {
+  let m = parseInt(second / 60);
+  let s = parseInt(second % 60);
+  let time = "";
+  if (second == 0) {
+    return "0'00''";
+  }
+  if (m == 0) {
+    if (s >= 10) {
+      time = "0'" + s + "''";
+    } else {
+      time = "0'0" + s + "''";
+    }
+  } else {
+    if (s >= 10) {
+      time = m + "'" + s + "''";
+    } else {
+      time = m + "'0" + s + "''";
+    }
+  }
+  return time;
+};
+
+const formattedCurrentTime = computed(() => {
+  return formatTime(audio.value.currentTime);
+});
+const formattedMaxTime = computed(() => {
+  return formatTime(audio.value.maxTime);
+});
+
+const play = () => {
+  audioRef.value.play();
+};
+const pause = () => {
+  audioRef.value.pause();
+};
+const playFunc = () => {
+  audio.value.playing = true;
+};
+const pauseFunc = () => {
+  audio.value.playing = false;
+};
+const handleEnd = () => {
+  sliderTime.value = 0;
+  audio.value.playing = false;
+  audio.value.currentTime = 0;
+};
+const timeupdateFunc = (res) => {
+  if (!audio.value.dragState) {
+    audio.value.currentTime = res.target.currentTime;
+    sliderTime.value = res.target.currentTime;
+  }
+};
+const onLoadedmetadata = (res) => {
+  audio.value.maxTime = parseInt(res.target.duration);
+};
+const startPlayOrPause = () => {
+  audio.value.playing ? pause() : play();
+};
+const onChange = (value) => {
+  audioRef.value.currentTime = value;
+};
+
+onBeforeUnmount(() => {
+  pause();
+});
+
+</script>
+
+<style lang="scss" scoped>
+.audio-container {
+  width: 400px;
+  border: 1px solid #3680fa;
+  height: 50px;
+  border-radius: 50px;
+}
+
+.audio_wrap_content {
+  height: 100%;
+}
+
+.cudio_control_content {
+  margin: 0 auto;
+  width: 90%;
+  height: 100%;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+
+  .slider {
+    flex: 1;
+    width: 100%;
+    display: flex;
+    align-items: center;
+
+  }
+
+  .slider div {
+    flex: 1;
+  }
+
+  .slider span {
+    margin: 0 15px;
+    font-size: 12px;
+    color: rgba(34, 34, 34, 0.5);
+  }
+
+
+}
+</style>
\ No newline at end of file
diff --git a/src/components/ExamInfo/index.vue b/src/components/ExamInfo/index.vue
index 2b63fd1..7adb980 100644
--- a/src/components/ExamInfo/index.vue
+++ b/src/components/ExamInfo/index.vue
@@ -1,8 +1,13 @@
 <template>
   <div class="info-container w-full">
     <div class="exam-title break-all mb-4 text-base text-gray-700">
-      {{ title }}
+      绗瑊{questionIndex + 1}}棰�: {{ activeQuestion.title }}
     </div>
+
+    <div class="audio-container" v-if="activeQuestion.audioFile">
+      <ExamAudio :audioSrc="activeQuestion.audioFile"></ExamAudio>
+    </div>
+
     <div class="img-container flex">
       <div class="img-item">
         <img src="@/assets/test.png" class="info-img" alt="">
@@ -12,13 +17,15 @@
 </template>
 
 <script setup>
+import ExamAudio from '@/components/ExamAudio/index.vue';
+
 const props = defineProps({
   questionIndex: {
     type: Number,
     required: true
   },
-  title: {
-    type: String,
+  activeQuestion: {
+    type: Object,
     required: true
   }
 })
@@ -27,6 +34,6 @@
 <style lang="scss" scoped>
 .info-img {
   max-height: 250px;
-  margin: 20px 0;
+  margin: 20px 10px;
 }
 </style>
\ No newline at end of file
diff --git a/src/store/modules/exam.js b/src/store/modules/exam.js
index edafdef..d159b47 100644
--- a/src/store/modules/exam.js
+++ b/src/store/modules/exam.js
@@ -14,6 +14,7 @@
   const examType = ref({
     1: '鍗曢�夐',
     2: '澶氶�夐',
+    3: '闊抽棰�'
   });
 
   const currentType = ref(1);
@@ -205,7 +206,72 @@
           "difficult": 5
         }
       ]
-    }
+    },
+    {
+      questionType: 3,
+      questionList: [
+        {
+          "id": null,
+          "questionType": 1,
+          "gradeLevel": null,
+          "subjectId": 2,
+          "title": "娴嬭瘯闊抽1",
+          "audioFile": '/test.mp3',
+          "items": [
+            {
+              "prefix": "A",
+              "content": "1"
+            },
+            {
+              "prefix": "B",
+              "content": "2"
+            },
+            {
+              "prefix": "C",
+              "content": "3"
+            },
+            {
+              "prefix": "D",
+              "content": "4"
+            }
+          ],
+          "analyze": "闂皬鏈嬪弸",
+          "correct": "",
+          "score": "3",
+          "difficult": 5
+        },
+        {
+          "id": null,
+          "questionType": 1,
+          "gradeLevel": null,
+          "subjectId": 2,
+          "title": "娴嬭瘯闊抽2",
+          "audioFile": '/test.mp3',
+          "items": [
+            {
+              "prefix": "A",
+              "content": "1"
+            },
+            {
+              "prefix": "B",
+              "content": "2"
+            },
+            {
+              "prefix": "C",
+              "content": "3"
+            },
+            {
+              "prefix": "D",
+              "content": "4"
+            }
+          ],
+          "analyze": "闂皬鏈嬪弸",
+          "correct": "",
+          "score": "3",
+          "difficult": 5
+        }
+      ]
+    },
   ]);
 
   const getActiveQuestion = computed(() => {
@@ -248,7 +314,7 @@
 
   const setProgress = (progress) => {
     answerProgress.value = progress;
-  }
+  };
 
 
   return {
diff --git a/src/views/exam/components/answer-main/answer-audio/index.vue b/src/views/exam/components/answer-main/answer-audio/index.vue
index b58b59f..705d1d0 100644
--- a/src/views/exam/components/answer-main/answer-audio/index.vue
+++ b/src/views/exam/components/answer-main/answer-audio/index.vue
@@ -1,7 +1,7 @@
 <template>
   <div class="answer-container w-full h-full">
     <el-scrollbar>
-      <ExamInfo class="mb-5" :questionIndex="currentIndex" :title="activeQuestion.title"></ExamInfo>
+      <ExamInfo class="mb-5" :questionIndex="currentIndex" :activeQuestion="activeQuestion"></ExamInfo>
 
       <div class="answer-content">
         <div class="answer-item flex" v-for="item, index in activeQuestion.items" @click="answerClick(item)"
diff --git a/src/views/exam/components/answer-main/answer-multiple/index.vue b/src/views/exam/components/answer-main/answer-multiple/index.vue
index e925454..b127aff 100644
--- a/src/views/exam/components/answer-main/answer-multiple/index.vue
+++ b/src/views/exam/components/answer-main/answer-multiple/index.vue
@@ -1,7 +1,7 @@
 <template>
   <div class="answer-container w-full h-full">
     <el-scrollbar>
-      <ExamInfo class="mb-5" :questionIndex="currentIndex" :title="activeQuestion.title"></ExamInfo>
+      <ExamInfo class="mb-5" :questionIndex="currentIndex" :activeQuestion="activeQuestion"></ExamInfo>
 
       <div class="answer-content">
         <div class="answer-item flex" v-for="item, index in activeQuestion.items" @click="answerClick(item)"
diff --git a/src/views/exam/components/answer-main/answer-single/index.vue b/src/views/exam/components/answer-main/answer-single/index.vue
index b58b59f..705d1d0 100644
--- a/src/views/exam/components/answer-main/answer-single/index.vue
+++ b/src/views/exam/components/answer-main/answer-single/index.vue
@@ -1,7 +1,7 @@
 <template>
   <div class="answer-container w-full h-full">
     <el-scrollbar>
-      <ExamInfo class="mb-5" :questionIndex="currentIndex" :title="activeQuestion.title"></ExamInfo>
+      <ExamInfo class="mb-5" :questionIndex="currentIndex" :activeQuestion="activeQuestion"></ExamInfo>
 
       <div class="answer-content">
         <div class="answer-item flex" v-for="item, index in activeQuestion.items" @click="answerClick(item)"
diff --git a/src/views/exam/index.vue b/src/views/exam/index.vue
index 585a0ec..090e41e 100644
--- a/src/views/exam/index.vue
+++ b/src/views/exam/index.vue
@@ -103,9 +103,12 @@
 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 AnswerTime from './components/answer-time/index.vue';
+import AnswerAudio from './components/answer-main/answer-audio/index.vue';
+
 import { useExamStore } from '@/store/index.js';
 import { useRouter } from 'vue-router';
 
@@ -117,6 +120,7 @@
 const typeComponent = {
   1: AnswerSingle,
   2: AnswerMultiple,
+  3: AnswerAudio,
 };
 
 const dialogVisible = ref(false);

--
Gitblit v1.8.0