Browse Source

Merge branch 'dsy2'

dusenyao 3 years ago
parent
commit
adb97f781a

BIN
src/assets/NPC/pink-pencil.png


+ 44 - 14
src/components/Adult/preview/AudioCompareMatrix.vue

@@ -1,7 +1,11 @@
 <template>
   <div class="compara-content">
     <template v-if="wavblob">
-      <div v-if="!isRecord" style="width: 16px; height: 16px; margin-left: 8px" @click.stop.capture="playAudio">
+      <div
+        v-if="!isRecord"
+        :class="[type == 'full' ? 'compare-box-big' : 'compare-box']"
+        @click.stop.capture="playAudio"
+      >
         <audio-line
           ref="audioLine"
           :mp3="url"
@@ -9,26 +13,35 @@
           audio-id="audioCompareMatrix"
           :stop-audio="stopAudio"
           :hide-slider="true"
+          :type="type"
           @handleChangeStopAudio="handleChangeStopAudio"
           @sentPause="sentPause"
           @playChange="playChange"
         />
       </div>
-      <div v-else style="width: 16px; height: 16px; margin-left: 8px">
+      <div v-else :class="[type == 'full' ? 'compare-box-big' : 'compare-box']">
         <audio-red
           ref="audioRed"
           :mp3="wavblob"
           :is-compare="true"
           :theme-color="themeColor"
+          :type="type"
           @sentPause="sentPause"
         />
       </div>
     </template>
     <template v-else>
       <img
-        src="../../../assets/NPC/compare-disable.png"
-        class="compare-disable"
-      >
+        :src="
+          type == 'full'
+            ? require('../../../assets/NPC/compare-disable-24.png')
+            : require('../../../assets/NPC/compare-disable.png')
+        "
+        :class="[
+          'compare-disable',
+          type == 'full' ? 'compare-disable-big' : '',
+        ]"
+      />
     </template>
   </div>
 </template>
@@ -40,23 +53,24 @@ import AudioRed from "./components/AudioRed.vue";
 export default {
   components: {
     AudioLine,
-    AudioRed
+    AudioRed,
   },
   props: [
     "themeColor",
     "isRecord",
     "wavblob",
     "url",
+    "type",
     "sentPause",
     "matrixSelectLrc",
     "getCurTime",
-    "curTime"
+    "curTime",
   ],
   data() {
     return {
       playing: false,
       stopAudio: true,
-      unWatch: null
+      unWatch: null,
     };
   },
   watch: {
@@ -76,7 +90,7 @@ export default {
       if (!newVal) {
         this.playChange(false);
       }
-    }
+    },
   },
   methods: {
     playAudio() {
@@ -94,7 +108,7 @@ export default {
         this.$refs.audioLine.PlayAudio();
         if (end_time === -1) return;
         let end = end_time / 1000 - 0.01;
-        this.unWatch = this.$watch("curTime", val => {
+        this.unWatch = this.$watch("curTime", (val) => {
           if (val >= end) {
             this.unWatch();
             this.handleParentPlay();
@@ -112,7 +126,7 @@ export default {
 
     playChange(playing) {
       this.playing = playing;
-      this.$emit('playing', playing);
+      this.$emit("playing", playing);
     },
 
     // 暂停音频播放
@@ -122,16 +136,32 @@ export default {
     // 音频播放时改变布尔值
     handleChangeStopAudio() {
       this.stopAudio = false;
-    }
-  }
+    },
+  },
 };
 </script>
 
 <style lang="scss" scoped>
+.compare-box {
+  width: 16px;
+  height: 16px;
+  margin-left: 8px;
+  &-big {
+    width: 24px;
+    height: 24px;
+    margin-left: 0px;
+  }
+}
 .compare-disable {
-  display: block;
   width: 16px;
   height: 16px;
+  display: block;
   margin-left: 8px;
+  &-big {
+    width: 24px;
+    height: 24px;
+    display: block;
+    margin-left: 0px;
+  }
 }
 </style>

+ 8 - 8
src/components/Adult/preview/AudioLineSentence.vue

@@ -30,7 +30,7 @@
         />
         <span :class="bgIndex == 1 ? 'color-white' : ''">
           {{ realFormatSecond(audio.maxTime) }}
-        
+
         </span>
       </template>
     </div> -->
@@ -160,7 +160,7 @@ export default {
     _this.$refs[audioId].addEventListener("pause", function () {
       _this.audio.playing = false;
       if (_this.audio.currentTime * 1000 + 500 > _this.ed) {
-        //_this.$emit("sentPause", true);
+        // _this.$emit("sentPause", true);
         _this.playValue = 0;
         _this.audio.isPlaying = false;
       }
@@ -195,7 +195,7 @@ export default {
     PlayAudio() {
       let audioId = this.audioId;
       let audio = document.getElementsByTagName("audio");
-      audio.forEach((item) => {
+      audio.forEach(item => {
         if (item.src == this.mp3) {
           if (item.id !== audioId) {
             item.pause();
@@ -205,7 +205,7 @@ export default {
         }
       });
       let video = document.getElementsByTagName("video");
-      video.forEach((vItem) => {
+      video.forEach(vItem => {
         vItem.pause();
       });
       this.$set(this.audio, "isPlaying", true);
@@ -259,9 +259,9 @@ export default {
     // 音频加载完之后
     onLoadedmetadata(res) {
       this.audio.maxTime = parseInt(this.maxTime);
-      //this.audio.maxTime = parseInt(res.target.duration);
-      //this.playTime = parseInt(this.maxTime);
-      //this.audioAllTime = this.realFormatSecond(this.audio.maxTime);
+      // this.audio.maxTime = parseInt(res.target.duration);
+      // this.playTime = parseInt(this.maxTime);
+      // this.audioAllTime = this.realFormatSecond(this.audio.maxTime);
     },
     // 当音频当前时间改变后,进度条也要改变
     onTimeupdate(res) {
@@ -289,7 +289,7 @@ export default {
       this.playValue = (time / this.audio.maxTime) * 100;
       if (playFlag) {
         let audio = document.getElementsByTagName("audio");
-        audio.forEach((item) => {
+        audio.forEach(item => {
           if (item.id !== audioId) {
             item.pause();
           }

+ 8 - 8
src/components/Adult/preview/Soundrecorddiff.vue

@@ -10,8 +10,8 @@
       </div>
     </div>
     <div
-      :class="['hasRecord', bgIndex == 1 ? 'hasRecord-white' : '']"
       v-if="!microphoneStatus && recordList.length > 0"
+      :class="['hasRecord', bgIndex == 1 ? 'hasRecord-white' : '']"
     >
       <div
         :class="[bgIndex == 1 ? 'playBack-white' : 'playBack', hasMicro]"
@@ -23,9 +23,9 @@
           )
         "
       />
-      <span class="record-time"
-        >{{ isPlaying ? "-" : "" }}{{ handleDateTime(recordtime) }}</span
-      >
+      <span
+        class="record-time"
+      >{{ isPlaying ? "-" : "" }}{{ handleDateTime(recordtime) }}</span>
       <el-select
         v-model="selectIndex"
         placeholder="无录音"
@@ -157,11 +157,12 @@ export default {
           //   ) {
           _this.wavblob = _this.recordList[_this.selectIndex].wavData;
           _this.$emit("getWavblob", _this.wavblob);
-          if (this.recordList[this.selectIndex].selectData)
+          if (this.recordList[this.selectIndex].selectData) {
             this.$emit(
               "getSelectData",
               this.recordList[this.selectIndex].selectData
             );
+          }
           //   }
         };
       }
@@ -249,8 +250,7 @@ export default {
       this.audio.pause();
       this.oldIndex = null;
       this.$emit("getWavblob", this.wavblob);
-      if (this.recordList[index].selectData)
-        this.$emit("getSelectData", this.recordList[index].selectData);
+      if (this.recordList[index].selectData) { this.$emit("getSelectData", this.recordList[index].selectData); }
       this.$emit("sentPause", false);
     },
     handleDelete() {
@@ -278,7 +278,7 @@ export default {
   }, // 如果页面有keep-alive缓存功能,这个函数会触发
 };
 </script>
-<style lang='scss' scoped>
+<style lang="scss" scoped>
 //@import url(); 引入公共css类
 .NNPE-Book-record {
   display: flex;

+ 181 - 57
src/components/Adult/preview/VoiceMatrix.vue

@@ -6,16 +6,24 @@
           :class="[
             themeColor.length === 0 || themeColor === 'red'
               ? 'serial-number'
-              : `serial-number-${themeColor}`
+              : `serial-number-${themeColor}`,
           ]"
         >
           {{ curQue.voiceMatrix.audioSerialNumber }}
         </span>
       </div>
       <div v-show="hasSelectedCell" class="audio-simple">
-        <img :src="playing ? voicePlaySrc : voicePauseSrc" @click="playAudio">
+        <img
+          class="audio-simple-image"
+          :src="playing ? voicePlaySrc : voicePauseSrc"
+          @click="playAudio"
+        >
         <span
-          :class="['Repeat-16', isRepeat ? '' : 'disabled']"
+          :class="[
+            'Repeat-16',
+            'audio-simple-repeat',
+            isRepeat ? '' : 'disabled',
+          ]"
           @click="isRepeat = !isRepeat"
         />
       </div>
@@ -37,7 +45,7 @@
         v-if="curQue.voiceMatrix.matrix.length > 0"
         class="matrix"
         :style="{
-          'grid-template': `36px repeat(${curQue.voiceMatrix.matrix.length}, auto) minmax(36px, 1fr) / 36px repeat(${curQue.voiceMatrix.matrix[0].length}, auto) minmax(36px, 1fr)`
+          'grid-template': `36px repeat(${curQue.voiceMatrix.matrix.length}, auto) minmax(36px, 1fr) / 36px repeat(${curQue.voiceMatrix.matrix[0].length}, auto) minmax(36px, 1fr)`,
         }"
         @mouseleave="clearSelectCell"
       >
@@ -52,7 +60,7 @@
                 (selectColumn === i ||
                   (selectedLine.type === 'column' && selectedLine.index === i))
                 ? 'read'
-                : ''
+                : '',
             ]"
             @mouseenter="checkboxMouseenter(selectColumn === i, 'column')"
           >
@@ -68,7 +76,7 @@
                 `matrix-checkbox-row-${themeColor}`,
                 selectedLine.type === 'column' && selectedLine.index === i
                   ? 'active'
-                  : ''
+                  : '',
               ]"
               @click="selectRowOrColumn(i, 'column')"
             />
@@ -85,7 +93,7 @@
                 (selectRow === i ||
                   (selectedLine.type === 'row' && selectedLine.index === i))
                 ? 'read'
-                : ''
+                : '',
             ]"
             @mouseenter="checkboxMouseenter(selectRow === i, 'row')"
           >
@@ -99,7 +107,7 @@
                 `matrix-checkbox-column-${themeColor}`,
                 selectedLine.type === 'row' && selectedLine.index === i
                   ? 'active'
-                  : ''
+                  : '',
               ]"
               @click="selectRowOrColumn(i, 'row')"
             />
@@ -123,7 +131,7 @@
                 (i === 0 && curQue.voiceMatrix.firstLineHighlight) ||
                   (j === row.length - 1 && curQue.voiceMatrix.lastColumnHighlight)
                   ? `highlight-${themeColor}`
-                  : ''
+                  : '',
               ]"
               @mouseenter="matrixCellMouseenter(i, j, column.type)"
             >
@@ -145,7 +153,7 @@
                       column.lrc_data.end_time === -1)
                     ? 'playing'
                     : '',
-                  column.isTitle ? 'title' : ''
+                  column.isTitle ? 'title' : '',
                 ]"
                 @click="matrixCellClick(i, j)"
               >
@@ -159,7 +167,7 @@
                   'connection',
                   i === 0 && curQue.voiceMatrix.firstLineHighlight
                     ? `highlight-bc-${themeColor}`
-                    : ''
+                    : '',
                 ]"
               />
               <!-- 分词 -->
@@ -180,10 +188,10 @@
                       column.lrc_data.end_time === -1)
                     ? 'playing'
                     : '',
-                  column.isTitle ? 'title' : ''
+                  column.isTitle ? 'title' : '',
                 ]"
                 :style="{
-                  'grid-template-columns': `repeat(${column.sentence_data.wordsList.length}, auto)`
+                  'grid-template-columns': `repeat(${column.sentence_data.wordsList.length}, auto)`,
                 }"
                 @click="matrixCellClick(i, j)"
               >
@@ -230,7 +238,7 @@
                       column.lrc_data.end_time === -1)
                     ? 'playing'
                     : '',
-                  column.isTitle ? 'title' : ''
+                  column.isTitle ? 'title' : '',
                 ]"
                 @click="matrixCellClick(i, j)"
               >
@@ -261,7 +269,7 @@
                       column.lrc_data.end_time === -1)
                     ? 'playing'
                     : '',
-                  column.isTitle ? 'title' : ''
+                  column.isTitle ? 'title' : '',
                 ]"
                 @click="matrixCellClick(i, j)"
               >
@@ -286,7 +294,7 @@
                 (selectRow === i ||
                   (selectedLine.type === 'row' && selectedLine.index === i))
                 ? 'read'
-                : ''
+                : '',
             ]"
             @mouseenter="clearSelectCell"
           />
@@ -302,7 +310,7 @@
                 (selectColumn === i ||
                   (selectedLine.type === 'column' && selectedLine.index === i))
                 ? 'read'
-                : ''
+                : '',
             ]"
             @mouseenter="clearSelectCell"
           />
@@ -310,19 +318,21 @@
         <div class="matrix-bottom" @mouseenter="clearSelectCell" />
       </div>
     </div>
-
+    <!-- 录音 -->
     <div class="voice-luyin">
       <soundrecord
+        ref="luyin"
         type="promax"
         class="luyin-box"
         :file-name="fileName"
         :select-data="selectData"
         @getWavblob="getWavblob"
         @getSelectData="getSelectData"
-        @handleParentPlay="handleParentPlay"
+        @handleParentPlay="pauseOtherAudio"
         @sentPause="sentPause"
       />
       <audio-compare
+        :style="{ flex: 1 }"
         :theme-color="themeColor"
         :wavblob="wavblob"
         :url="mp3Url"
@@ -334,6 +344,20 @@
         :handle-change-stop-audio="handleChangeStopAudio"
         @playing="playChange"
       />
+      <span ref="fullscreen" class="fullscreen" @click="fullScreen">
+        <span>全屏模式</span>
+        <el-image :src="fullscreenSrc" />
+      </span>
+    </div>
+    <div :id="`screen-${cid}`" class="voice-full-screen">
+      <voice-fullscreen
+        v-if="isFull"
+        :theme-color="themeColor"
+        :cur-que="curQue"
+        :mp3="mp3Url"
+        @exitFullscreen="exitFullscreen"
+        @changeIsFull="changeIsFull"
+      />
     </div>
   </div>
 </template>
@@ -343,22 +367,26 @@ import Bus from "./components/Bus.js";
 import AudioLine from "./AudioLine.vue";
 import Soundrecord from "./Soundrecord.vue";
 import AudioCompare from "./AudioCompareMatrix.vue";
+import VoiceFullscreen from "./VoiceMatrixFullscreen.vue";
 
 export default {
   components: {
     AudioLine,
     Soundrecord,
-    AudioCompare
+    AudioCompare,
+    VoiceFullscreen,
   },
   props: ["curQue", "themeColor"],
   data() {
     return {
+      // 组件id
+      cid: Math.random().toString(36).substr(2, 10),
+      isFull: false,
       curTime: 0,
       playing: false,
       stopAudio: true,
       unWatch: null,
       lrcArray: [],
-      cellTimer: null,
       fileName: "",
       // 底色行、列
       selectRow: -1,
@@ -366,18 +394,18 @@ export default {
       // 行、列选中
       selectedLine: {
         type: "",
-        index: 0
+        index: 0,
       },
       // 点击选中
       selectCell: {
         row: -1,
-        column: -1
+        column: -1,
       },
       isRepeat: false,
       // 跟读所需属性
       wavblob: null,
       isRecord: false,
-      matrixSelectLrc: null
+      matrixSelectLrc: null,
     };
   },
   computed: {
@@ -411,35 +439,59 @@ export default {
         type: type.length > 0 && index >= 0 ? type : "cell",
         index,
         row,
-        column
+        column,
       };
     },
 
     voicePauseSrc() {
-      let themeColor = this.themeColor;
+      const themeColor = this.themeColor;
       if (themeColor.length === 0 || themeColor === "red") {
-        return require("../../../assets/NPC/play-red.png");
+        return require("@/assets/NPC/play-red.png");
       }
-      return require(`../../../assets/NPC/play-${themeColor}.png`);
+      return require(`@/assets/NPC/play-${themeColor}.png`);
     },
     voicePlaySrc() {
-      let themeColor = this.themeColor;
+      const themeColor = this.themeColor;
       if (themeColor.length === 0 || themeColor === "red") {
-        return require("../../../assets/NPC/icon-voice-play-red.png");
+        return require("@/assets/NPC/icon-voice-play-red.png");
       }
-      return require(`../../../assets/NPC/icon-voice-play-${themeColor}.png`);
-    }
+      return require(`@/assets/NPC/icon-voice-play-${themeColor}.png`);
+    },
+    fullscreenSrc() {
+      const themeColor = this.themeColor;
+      if (themeColor.length === 0 || themeColor === "red") {
+        return require("@/assets/NPC/full-screen-red.png");
+      }
+      return require(`@/assets/NPC/full-screen-${themeColor}.png`);
+    },
   },
   watch: {
     hasSelectedCell() {
       this.handleParentPlay();
-    }
+    },
   },
   created() {
-    Bus.$on("audioPause", () => {
+    Bus.$on("audioPause", id => {
+      if (this.cid === id) return;
+      if (this.$refs.luyin.microphoneStatus) this.$refs.luyin.microphone();
       this.handleParentPlay();
     });
   },
+
+  mounted() {
+    document
+      .querySelector("body")
+      .addEventListener("click", this.restoreAudioStatus);
+    // 如果一行内有两个语音矩阵,隐藏 全屏模式 文字
+    if (Number(window.getComputedStyle(this.$refs.fullscreen).width.replace('px', '')) < 80) {
+      this.$refs.fullscreen.children[0].hidden = true;
+    }
+  },
+  beforeDestroy() {
+    document
+      .querySelector("body")
+      .removeEventListener("click", this.restoreAudioStatus);
+  },
   methods: {
     // 鼠标移入移出
     matrixCellMouseenter(i, j, type) {
@@ -476,13 +528,8 @@ export default {
     },
 
     setRecordingFileName(row, column) {
-      let {
-        type,
-        text,
-        sentence_data,
-        pinyin_english_data,
-        text_brackets
-      } = this.curQue.voiceMatrix.matrix[row][column];
+      let { type, text, sentence_data, pinyin_english_data, text_brackets } =
+        this.curQue.voiceMatrix.matrix[row][column];
       if (type === "text") this.fileName = text;
       if (type === "SentenceSegwordChs") this.fileName = sentence_data.sentence;
       if (type === "PinyinEnglish") this.fileName = pinyin_english_data.pinyin;
@@ -491,6 +538,32 @@ export default {
       }
     },
 
+    // 判断 click 点击是否语音矩阵可操作区域
+    restoreAudioStatus(event) {
+      const whitePath = [
+        "column-green",
+        "column-red",
+        "column-brown",
+        "matrix-checkbox-column-",
+        "matrix-checkbox-row-",
+        "audio-simple-image",
+        "audio-simple-repeat",
+        "luyin-box",
+      ];
+
+      let operable = event.path.some(item => {
+        let className = item.className;
+        if (!className) return false;
+        return whitePath.some(path => className.includes(path));
+      });
+      if (!operable) {
+        this.selectedLine = { type: "", index: -1 };
+        this.selectCell = { row: -1, column: -1 };
+        if (this.playing) this.handleParentPlay();
+        if (this.unWatch) this.unWatch();
+      }
+    },
+
     checkboxMouseenter(isSelected, type) {
       if (!isSelected) return this.clearSelectCell();
       if (type === "row") this.selectColumn = -1;
@@ -530,12 +603,10 @@ export default {
       this.lrcArray = [];
       let { type, index } = this.selectedLine;
       if (type.length > 0 && index >= 0 && type === "row") {
-        this.curQue.voiceMatrix.matrix[index].forEach(
-          item => {
-            let data = this.getLrcData(item);
-            if (data) this.lrcArray.push(data);
-          }
-        );
+        this.curQue.voiceMatrix.matrix[index].forEach(item => {
+          let data = this.getLrcData(item);
+          if (data) this.lrcArray.push(data);
+        });
         if (this.lrcArray.length > 0) this.lrcPlay(this.lrcArray[0], 0);
         return;
       }
@@ -586,7 +657,12 @@ export default {
     playChange(playing) {
       this.playing = playing;
       // 子组件通信,同时只能播放一个音频
-      if (playing) Bus.$emit("audioPause");
+      if (playing) Bus.$emit("audioPause", this.cid);
+    },
+
+    pauseOtherAudio() {
+      Bus.$emit("audioPause", this.cid);
+      this.stopAudio = true;
     },
 
     // 暂停音频播放
@@ -610,12 +686,10 @@ export default {
       if (type === "") return;
       let arr = [];
       if (type.length > 0 && index >= 0 && type === "row") {
-        this.curQue.voiceMatrix.matrix[index].forEach(
-          item => {
-            let data = this.getLrcData(item);
-            if (data) arr.push(data);
-          }
-        );
+        this.curQue.voiceMatrix.matrix[index].forEach(item => {
+          let data = this.getLrcData(item);
+          if (data) arr.push(data);
+        });
         this.matrixSelectLrc = arr;
         return;
       }
@@ -647,7 +721,7 @@ export default {
           return {
             begin_time: lrc_data.begin_time,
             end_time: this.mp3Duration,
-            text: lrc_data.text
+            text: lrc_data.text,
           };
         }
         return lrc_data;
@@ -659,6 +733,45 @@ export default {
       this.isRecord = isRecord;
     },
 
+    pauseAudio() {
+      let audio = document.getElementsByTagName("audio");
+      audio.forEach(item => {
+        item.pause();
+      });
+    },
+    fullScreen() {
+      this.pauseAudio();
+      this.isFull = true;
+      this.goFullscreen();
+    },
+    goFullscreen() {
+      let element = document.getElementById(`screen-${this.cid}`);
+      if (element.requestFullscreen) {
+        element.requestFullscreen();
+      } else if (element.msRequestFullscreen) {
+        element.msRequestFullscreen();
+      } else if (element.mozRequestFullScreen) {
+        element.mozRequestFullScreen();
+      } else if (element.webkitRequestFullscreen) {
+        element.webkitRequestFullscreen();
+      }
+    },
+    exitFullscreen() {
+      this.isFull = false;
+      if (document.exitFullscreen) {
+        document.exitFullscreen();
+      } else if (document.msExitFullscreen) {
+        document.msExitFullscreen();
+      } else if (document.mozCancelFullScreen) {
+        document.mozCancelFullScreen();
+      } else if (document.webkitExitFullscreen) {
+        document.webkitExitFullscreen();
+      }
+    },
+    changeIsFull() {
+      this.isFull = false;
+    },
+
     handleChangeTime({ begin_time, end_time }) {
       if (this.unWatch) this.unWatch();
       this.handleParentPlay();
@@ -677,8 +790,8 @@ export default {
           }
         });
       });
-    }
-  }
+    },
+  },
 };
 </script>
 
@@ -1154,6 +1267,17 @@ $select-color-brown-active: #a37557;
     align-items: center;
     padding: 3px 16px;
     height: 40px;
+
+    .fullscreen {
+      cursor: pointer;
+
+      .el-image {
+        width: 16px;
+        height: 16px;
+        margin-left: 8px;
+        vertical-align: text-bottom;
+      }
+    }
   }
 }
 </style>

+ 1665 - 0
src/components/Adult/preview/VoiceMatrixFullscreen.vue

@@ -0,0 +1,1665 @@
+<template>
+  <div :class="['voicefull', bgIndex === 0 ? 'bg1' : 'bg2']">
+    <div
+      class="voicefull-top"
+      @mouseover="setTopShow(true)"
+      @mouseleave="setTopShow(false)"
+    >
+      <div :class="[isTopShow ? 'voicefull-top-show' : 'voicefull-top-hidden']">
+        <div class="top-left">
+          <div :class="['select-bg', bgIndex === 1 ? 'select-bg-blue' : '']">
+            <div :class="['bg-green-box', bgIndex === 1 ? 'active' : '']">
+              <span
+                :class="['bg-green', bgIndex === 1 ? 'active' : '']"
+                @click="changeBg(1)"
+              />
+            </div>
+            <div :class="['bg-white-box', bgIndex === 0 ? 'active' : '']">
+              <span
+                :class="['bg-white', bgIndex === 0 ? 'active' : '']"
+                @click="changeBg(0)"
+              />
+            </div>
+          </div>
+          <div
+            :class="['set-fontSize', bgIndex === 1 ? 'set-fontSize-green' : '']"
+          >
+            <template v-if="hzSize >= 34">
+              <span
+                :class="[
+                  'font-jian-black',
+                  bgIndex === 1 ? 'font-jian-yellow' : '',
+                ]"
+                @click="setFontSize('-')"
+              />
+            </template>
+            <template v-else>
+              <span
+                :class="[
+                  'font-jian-black',
+                  bgIndex === 1
+                    ? 'font-jian-yellow-disabled'
+                    : 'font-jian-white-disabled',
+                ]"
+              />
+            </template>
+            <span
+              :class="[
+                'font-img-black',
+                bgIndex === 1 ? 'font-img-yellow' : '',
+              ]"
+            />
+            <template v-if="hzSize <= 76">
+              <span
+                :class="[
+                  'font-jia-black',
+                  bgIndex === 1 ? 'font-jia-yellow' : '',
+                ]"
+                @click="setFontSize('+')"
+              />
+            </template>
+            <template v-else>
+              <span
+                :class="[
+                  'font-jia-black',
+                  bgIndex === 1
+                    ? 'font-jia-yellow-disabled'
+                    : 'font-jia-white-disabled',
+                ]"
+              />
+            </template>
+          </div>
+          <div
+            :class="['op-btn', bgIndex === 1 ? 'op-btn-green' : '']"
+            @click="changeStatus('isKeyboard')"
+          >
+            <span
+              :class="[
+                'keyboard-icon',
+                !isKeyboard ? 'disabled' : '',
+                isKeyboard && bgIndex === 1 ? 'keyboard-icon-yellow' : '',
+              ]"
+            />
+          </div>
+        </div>
+        <div class="top-middle">
+          <template v-if="mp3">
+            <voice-matrix-fullscreen-audio
+              ref="audioLine"
+              audio-id="voiceMatrixAudio"
+              :bg-index="bgIndex"
+              :mp3="mp3"
+              :get-cur-time="getCurTime"
+              :stop-audio="stopAudio"
+              :has-selected-cell="hasSelectedCell"
+              @playChange="playChange"
+              @parentPlayAudio="playAudio"
+              @handleChangeStopAudio="handleChangeStopAudio"
+            />
+          </template>
+          <div
+            :class="['op-btn', bgIndex === 1 ? 'op-btn-green' : '']"
+            @click="setStatus"
+          >
+            <span
+              :class="[
+                'repeat-icon',
+                !isRepeat && !isAuto ? 'disabled' : '',
+                isRepeat && !isAuto ? 'auto-icon' : '',
+                !isRepeat && bgIndex === 1 ? 'repeat-icon-yellow' : '',
+                isRepeat && !isAuto && bgIndex === 1 ? 'auto-icon-yellow' : '',
+              ]"
+            />
+          </div>
+        </div>
+        <div
+          :class="['op-btn', bgIndex === 1 ? 'op-btn-green' : '']"
+          @click="exitFullScreen"
+        >
+          <span
+            :class="['close-icon', bgIndex === 1 ? 'close-icon-white' : '']"
+          />
+        </div>
+      </div>
+    </div>
+
+    <div class="voicefull-content">
+      <div
+        v-if="curQue.voiceMatrix.matrix.length > 0"
+        class="matrix"
+        :style="{
+          'grid-template': `96px repeat(${curQue.voiceMatrix.matrix.length}, auto) minmax(1.5em, 1fr) / 112px repeat(${curQue.voiceMatrix.matrix[0].length}, auto) minmax(1.5em, 1fr)`,
+          'font-size': `${hzSize}px`,
+        }"
+        @mouseleave="clearSelectCell"
+      >
+        <!-- 顶部单元格 -->
+        <div class="matrix-top" @mouseenter="clearSelectCell" />
+        <template v-for="(row, i) in curQue.voiceMatrix.matrix[0]">
+          <div
+            :key="`top-${i}`"
+            :class="['matrix-top']"
+            @mouseenter="checkboxMouseenter(selectColumn === i, 'column')"
+          >
+            <span
+              v-if="
+                row.type !== 'connection' && curQue.voiceMatrix.columnSelection
+              "
+              v-show="
+                selectColumn === i ||
+                  (selectedLine.type === 'column' && selectedLine.index === i)
+              "
+              :class="[
+                `matrix-checkbox-row-${themeColor}`,
+                selectedLine.type === 'column' && selectedLine.index === i
+                  ? 'active'
+                  : '',
+              ]"
+              @click="selectRowOrColumn(i, 'column')"
+            />
+          </div>
+        </template>
+        <div class="matrix-top" @mouseenter="clearSelectCell" />
+        <!-- 主矩阵 -->
+        <template v-for="(row, i) in curQue.voiceMatrix.matrix">
+          <div
+            :key="`start-${i}`"
+            :class="['column-wrapper']"
+            @mouseenter="checkboxMouseenter(selectRow === i, 'row')"
+          >
+            <span
+              v-if="curQue.voiceMatrix.rowSelection"
+              v-show="
+                selectRow === i ||
+                  (selectedLine.type === 'row' && selectedLine.index === i)
+              "
+              :class="[
+                `matrix-checkbox-column-${themeColor}`,
+                selectedLine.type === 'row' && selectedLine.index === i
+                  ? 'active'
+                  : '',
+              ]"
+              @click="selectRowOrColumn(i, 'row')"
+            />
+          </div>
+          <!-- 单元格 -->
+          <template v-for="(column, j) in row">
+            <div
+              :key="`wrapper-${i}-${j}`"
+              :class="[
+                'column-wrapper',
+                (i === 0 && curQue.voiceMatrix.firstLineHighlight) ||
+                  (j === row.length - 1 && curQue.voiceMatrix.lastColumnHighlight)
+                  ? `highlight-${themeColor}`
+                  : '',
+              ]"
+              @mouseenter="matrixCellMouseenter(i, j, column.type)"
+            >
+              <!-- 文本 -->
+              <div
+                v-if="column.type === 'text'"
+                :key="`column-${i}-${j}`"
+                :class="[
+                  column.text.length === 0 ? 'space' : `column-${themeColor}`,
+                  (selectCell.row === i && selectCell.column === j) ||
+                    (selectedLine.type === 'column' &&
+                      selectedLine.index === j) ||
+                    (selectedLine.type === 'row' && selectedLine.index === i)
+                    ? 'selected'
+                    : '',
+                  playing &&
+                    column.lrc_data.begin_time / 1000 <= curTime &&
+                    (curTime < column.lrc_data.end_time / 1000 ||
+                      column.lrc_data.end_time === -1)
+                    ? 'playing'
+                    : '',
+                  column.isTitle ? 'title' : '',
+                ]"
+                @click="matrixCellClick(i, j)"
+              >
+                <span>{{ column.text }}</span>
+              </div>
+              <!-- 连接线 -->
+              <div
+                v-else-if="column.type === 'connection'"
+                :key="`column-${i}-${j}`"
+                :class="[
+                  'connection',
+                  i === 0 && curQue.voiceMatrix.firstLineHighlight
+                    ? `highlight-bc-${themeColor}`
+                    : '',
+                ]"
+              />
+              <!-- 分词 -->
+              <div
+                v-else-if="column.type === 'SentenceSegwordChs'"
+                :key="`column-${i}-${j}`"
+                :class="[
+                  `sentence-${themeColor}`,
+                  (selectCell.row === i && selectCell.column === j) ||
+                    (selectedLine.type === 'column' &&
+                      selectedLine.index === j) ||
+                    (selectedLine.type === 'row' && selectedLine.index === i)
+                    ? 'selected'
+                    : '',
+                  playing &&
+                    column.lrc_data.begin_time / 1000 <= curTime &&
+                    (curTime < column.lrc_data.end_time / 1000 ||
+                      column.lrc_data.end_time === -1)
+                    ? 'playing'
+                    : '',
+                  column.isTitle ? 'title' : '',
+                ]"
+                :style="{
+                  'grid-template-columns': `repeat(${column.sentence_data.wordsList.length}, auto)`,
+                }"
+                @click="matrixCellClick(i, j)"
+              >
+                <template v-for="(word, w) in column.sentence_data.wordsList">
+                  <span
+                    v-if="column.sentence_data.pyPosition === 'top'"
+                    :key="`pinyin-${w}`"
+                    class="pinyin"
+                  >
+                    {{ word.pinyin }}
+                  </span>
+                  <span v-else :key="`chs-${w}`" class="chs">
+                    {{ word.chs }}
+                  </span>
+                </template>
+                <template v-for="(word, w) in column.sentence_data.wordsList">
+                  <span
+                    v-if="column.sentence_data.pyPosition === 'top'"
+                    :key="`chs-${w}`"
+                    class="chs"
+                  >
+                    {{ word.chs }}
+                  </span>
+                  <span v-else :key="`pinyin-${w}`" class="pinyin">
+                    {{ word.pinyin }}
+                  </span>
+                </template>
+              </div>
+              <!-- 拼音 + 英文 -->
+              <div
+                v-else-if="column.type === 'PinyinEnglish'"
+                :key="`column-${i}-${j}`"
+                :class="[
+                  `pinyinEnglish-${themeColor}`,
+                  (selectCell.row === i && selectCell.column === j) ||
+                    (selectedLine.type === 'column' &&
+                      selectedLine.index === j) ||
+                    (selectedLine.type === 'row' && selectedLine.index === i)
+                    ? 'selected'
+                    : '',
+                  playing &&
+                    column.lrc_data.begin_time / 1000 <= curTime &&
+                    (curTime < column.lrc_data.end_time / 1000 ||
+                      column.lrc_data.end_time === -1)
+                    ? 'playing'
+                    : '',
+                  column.isTitle ? 'title' : '',
+                ]"
+                @click="matrixCellClick(i, j)"
+              >
+                <div class="inside-wrapper">
+                  <div class="pinyin">
+                    {{ column.pinyin_english_data.pinyin }}
+                  </div>
+                  <div class="english">
+                    {{ column.pinyin_english_data.english }}
+                  </div>
+                </div>
+              </div>
+              <!-- 文本中有括号 -->
+              <div
+                v-else-if="column.type === 'textBrackets'"
+                :key="`column-${i}-${j}`"
+                :class="[
+                  `textBrackets-${themeColor}`,
+                  (selectCell.row === i && selectCell.column === j) ||
+                    (selectedLine.type === 'column' &&
+                      selectedLine.index === j) ||
+                    (selectedLine.type === 'row' && selectedLine.index === i)
+                    ? 'selected'
+                    : '',
+                  playing &&
+                    column.lrc_data.begin_time / 1000 <= curTime &&
+                    (curTime < column.lrc_data.end_time / 1000 ||
+                      column.lrc_data.end_time === -1)
+                    ? 'playing'
+                    : '',
+                  column.isTitle ? 'title' : '',
+                ]"
+                @click="matrixCellClick(i, j)"
+              >
+                <span>
+                  <span class="brackets-text">{{
+                    column.text_brackets.brackets_outer
+                  }}</span>
+                  <span class="brackets">&nbsp;[&nbsp;</span>
+                  <span class="brackets-text">{{
+                    column.text_brackets.brackets_inner
+                  }}</span>
+                  <span class="brackets">&nbsp;]</span>
+                </span>
+              </div>
+            </div>
+          </template>
+
+          <div :key="`end-${i}`" @mouseenter="clearSelectCell" />
+        </template>
+        <!-- 底部格子 -->
+        <div class="matrix-bottom" @mouseenter="clearSelectCell" />
+        <template v-for="(row, i) in curQue.voiceMatrix.matrix[0]">
+          <div :key="`bottom-${i}`" @mouseenter="clearSelectCell" />
+        </template>
+        <div class="matrix-bottom" @mouseenter="clearSelectCell" />
+      </div>
+    </div>
+
+    <div
+      class="voicefull-bottom"
+      @mouseover="setBottomShow(true)"
+      @mouseleave="setBottomShow(false)"
+    >
+      <div :class="['voicefull-bottom-show', isBottomShow ? '' : 'hidden']">
+        <div class="bottom-left">
+          <soundrecorddiff
+            ref="Soundrecorddiff"
+            :bg-index="bgIndex"
+            :file-name="fileName"
+            :select-data="selectData"
+            @getSelectData="getSelectData"
+            @getWavblob="getWavblob"
+            @handleParentPlay="handleParentPlay"
+            @sentPause="sentPause"
+            @getRerordStatus="getRerordStatus"
+            @getMicrophoneStatus="getMicrophoneStatus"
+            @getPlayStatus="getPlayStatus"
+          />
+          <div
+            v-if="isShowCompare"
+            :class="['compare-box', bgIndex === 1 ? 'compare-box-white' : '']"
+          >
+            <audio-compare
+              type="full"
+              :theme-color="themeColor"
+              :url="mp3"
+              :wavblob="wavblob"
+              :sent-pause="sentPause"
+              :is-record="isRecord"
+              :handle-change-stop-audio="handleChangeStopAudio"
+              :get-play-status="getPlayStatus"
+              :matrix-select-lrc="matrixSelectLrc"
+              :get-cur-time="getCurTime"
+              :cur-time="curTime"
+            />
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import VoiceMatrixFullscreenAudio from "./VoiceMatrixFullscreenAudio.vue";
+import Soundrecorddiff from "./Soundrecorddiff.vue";
+import AudioCompare from "./AudioCompareMatrix.vue";
+import Wordcard from "./components/Wordcard.vue";
+
+export default {
+  components: {
+    VoiceMatrixFullscreenAudio,
+    Soundrecorddiff,
+    AudioCompare,
+    Wordcard,
+  },
+  props: ["mp3", "curQue", "themeColor"],
+  data() {
+    return {
+      hzSize: 48,
+      bgIndex: 1,
+      item: null,
+      curTime: 0,
+      stopAudio: false,
+      isShowCompare: false,
+      clientY: 0,
+      top: 0,
+      left: 0,
+      isShow: false,
+      curWordTime: 0,
+      playing: false,
+      isAuto: false,
+      autoCount: 0,
+      key: "isRepeat",
+      isKeyboard: true,
+      isTopShow: false,
+      isBottomShow: false,
+      isRecording: false,
+      recordPlaying: false,
+      fileName: "",
+      // 底色行、列
+      selectRow: -1,
+      selectColumn: -1,
+      // 行、列选中
+      selectedLine: {
+        type: "",
+        index: 0,
+      },
+      // 点击选中
+      selectCell: {
+        row: -1,
+        column: -1,
+      },
+      isRepeat: false,
+      // 跟读所需属性
+      wavblob: null,
+      isRecord: false,
+      matrixSelectLrc: null,
+      unWatch: null,
+      lrcArray: [],
+      cellTimer: null,
+    };
+  },
+  computed: {
+    hasSelectedCell() {
+      let { type, index } = this.selectedLine;
+      let { row, column } = this.selectCell;
+      return (type.length > 0 && index >= 0) || (row >= 0 && column >= 0);
+    },
+    selectData() {
+      let { type, index } = this.selectedLine;
+      let { row, column } = this.selectCell;
+      return {
+        type: type.length > 0 && index >= 0 ? type : "cell",
+        index,
+        row,
+        column,
+      };
+    },
+    // 矩阵的行、列数从 0 开始
+    matrix() {
+      const matrixArr = this.curQue.voiceMatrix.matrix;
+      return {
+        rows: matrixArr.length - 1,
+        columns: matrixArr.length > 0 ? matrixArr[0].length - 1 : -1,
+      };
+    },
+  },
+  watch: {
+    isRecording(newVal) {
+      if (newVal) {
+        this.isBottomShow = newVal;
+      }
+    },
+    recordPlaying(newVal) {
+      if (newVal) {
+        this.isBottomShow = newVal;
+      }
+    },
+    isShow(val) {
+      if (val) {
+        setTimeout(() => {
+          this.cardHeight = this.$refs.wordcard.offsetHeight;
+          if (this.screenHeight - this.clientY > this.cardHeight) {
+            this.top = this.clientY + 20;
+          } else {
+            this.top = this.clientY - this.cardHeight - 30;
+          }
+        }, 50);
+      }
+    },
+  },
+  created() {
+    document.addEventListener("keyup", this.handleKeyup);
+
+    [
+      "fullscreenchange",
+      "mozfullscreenchange",
+      "webkitfullscreenchange",
+      "msfullscreenchange",
+    ].forEach(event => {
+      document.addEventListener(event, this.handleFullscreen);
+    });
+  },
+  beforeDestroy() {
+    document.removeEventListener("keyup", this.handleKeyup);
+    [
+      "fullscreenchange",
+      "mozfullscreenchange",
+      "webkitfullscreenchange",
+      "msfullscreenchange",
+    ].forEach(event => {
+      document.removeEventListener(event, this.handleFullscreen);
+    });
+  },
+  // 方法集合
+  methods: {
+    // #region
+    /** 语音矩阵方法开始 **/
+    // 鼠标移入移出
+    matrixCellMouseenter(i, j, type) {
+      if (type === "connection") {
+        this.selectRow = -1;
+        this.selectColumn = -1;
+      } else {
+        this.selectRow = i;
+        this.selectColumn = j;
+      }
+    },
+
+    clearSelectCell() {
+      this.selectRow = -1;
+      this.selectColumn = -1;
+    },
+
+    // 单击单元格
+    matrixCellClick(row, column) {
+      if (this.playing) this.handleParentPlay();
+      if (this.unWatch) this.unWatch();
+      this.lrcArray = [];
+      if (row === this.selectCell.row && column === this.selectCell.column) {
+        this.selectCell = { row: -1, column: -1 };
+        return;
+      }
+      this.selectedLine = { type: "", index: -1 };
+      this.selectCell = { row, column };
+      this.handleChangeTime(
+        this.curQue.voiceMatrix.matrix[row][column].lrc_data
+      );
+      // 设置录音文件名
+      this.setRecordingFileName(row, column);
+    },
+
+    setRecordingFileName(row, column) {
+      let { type, text, sentence_data, pinyin_english_data, text_brackets } =
+        this.curQue.voiceMatrix.matrix[row][column];
+      if (type === "text") this.fileName = text;
+      if (type === "SentenceSegwordChs") this.fileName = sentence_data.sentence;
+      if (type === "PinyinEnglish") this.fileName = pinyin_english_data.pinyin;
+      if (type === "textBrackets") {
+        this.fileName = `${text_brackets.brackets_outer}[${text_brackets.brackets_inner}]`;
+      }
+    },
+
+    checkboxMouseenter(isSelected, type) {
+      if (!isSelected) return this.clearSelectCell();
+      if (type === "row") this.selectColumn = -1;
+      if (type === "column") this.selectRow = -1;
+    },
+
+    // 选中行、列
+    selectRowOrColumn(index, type) {
+      this.handleParentPlay();
+      this.lrcArray = [];
+      this.selectCell = { row: -1, column: -1 };
+      if (this.unWatch) this.unWatch();
+      if (
+        this.selectedLine.type === type &&
+        this.selectedLine.index === index
+      ) {
+        this.selectedLine = { type: "", index: -1 };
+        return;
+      }
+      this.selectedLine = { type, index };
+      let number = index;
+      if (type === "column") {
+        this.curQue.voiceMatrix.matrix[index].forEach(({ type }, i) => {
+          if (i >= index) return;
+          if (type === "connection") number -= 1;
+        });
+      }
+      this.fileName = `第 ${number + 1} ${type === "row" ? "行" : "列"}`;
+    },
+
+    playAudio() {
+      if (!this.hasSelectedCell) return;
+      if (this.playing) return this.handleParentPlay();
+      if (this.lrcArray.length > 0) return this.$refs.audioLine.PlayAudio();
+      if (this.unWatch) this.unWatch();
+
+      this.lrcArray = [];
+      let { type, index } = this.selectedLine;
+      if (type.length > 0 && index >= 0 && type === "row") {
+        this.curQue.voiceMatrix.matrix[index].forEach(item => {
+          let data = this.getLrcData(item);
+          if (data) this.lrcArray.push(data);
+        });
+        if (this.lrcArray.length > 0) this.lrcPlay(this.lrcArray[0], 0);
+        return;
+      }
+
+      if (type.length > 0 && index >= 0 && type === "column") {
+        this.curQue.voiceMatrix.matrix.forEach(item => {
+          let data = this.getLrcData(item[index]);
+          if (data) this.lrcArray.push(data);
+        });
+        if (this.lrcArray.length > 0) this.lrcPlay(this.lrcArray[0], 0);
+        return;
+      }
+
+      let { row, column } = this.selectCell;
+      if (row >= 0 && column >= 0) {
+        this.handleChangeTime(
+          this.curQue.voiceMatrix.matrix[row][column].lrc_data
+        );
+      }
+    },
+
+    lrcPlay({ begin_time, end_time }, index) {
+      this.handleParentPlay();
+      this.$nextTick(() => {
+        this.$refs.audioLine.onTimeupdateTime(begin_time / 1000);
+        this.$refs.audioLine.PlayAudio();
+        if (end_time === -1) return;
+        let end = end_time / 1000 - 0.01;
+        this.unWatch = this.$watch("curTime", val => {
+          if (val >= end) {
+            if (!this.hasSelectedCell) return this.unWatch();
+            this.handleParentPlay();
+            this.$refs.audioLine.onTimeupdateTime(end);
+            this.unWatch();
+            let i = index + 1;
+            if (i < this.lrcArray.length) {
+              return this.lrcPlay(this.lrcArray[i], i);
+            }
+            // 多次循环
+            if (this.isRepeat) {
+              return this.lrcPlay(this.lrcArray[0], 0);
+            }
+            // 单次循环
+            if (this.isAuto && this.autoCount === 1) {
+              this.autoCount = 0;
+              this.lrcArray = [];
+              return;
+            }
+            if (this.isAuto && this.autoCount === 0) {
+              this.autoCount += 1;
+              return this.lrcPlay(this.lrcArray[0], 0);
+            }
+
+            this.lrcArray = [];
+          }
+        });
+      });
+    },
+
+    // 暂停音频播放
+    handleParentPlay() {
+      this.stopAudio = true;
+    },
+    // 音频播放时改变布尔值
+    handleChangeStopAudio() {
+      this.stopAudio = false;
+    },
+
+    getCurTime(curTime) {
+      this.curTime = curTime;
+    },
+
+    getWavblob(wavblob) {
+      this.wavblob = wavblob;
+    },
+
+    getSelectData({ type, index, row, column }) {
+      if (type === "") return;
+      let arr = [];
+      if (type.length > 0 && index >= 0 && type === "row") {
+        this.curQue.voiceMatrix.matrix[index].forEach(item => {
+          let data = this.getLrcData(item);
+          if (data) arr.push(data);
+        });
+        this.matrixSelectLrc = arr;
+        return;
+      }
+
+      if (type.length > 0 && index >= 0 && type === "column") {
+        this.curQue.voiceMatrix.matrix.forEach(item => {
+          let data = this.getLrcData(item[index]);
+          if (data) arr.push(data);
+        });
+        this.matrixSelectLrc = arr;
+        return;
+      }
+
+      if (type === "cell" && row >= 0 && column >= 0) {
+        let lrcData = this.curQue.voiceMatrix.matrix[row][column].lrc_data;
+        if (lrcData.end_time === -1) lrcData.end_time = this.mp3Duration;
+        this.matrixSelectLrc = [lrcData];
+      }
+    },
+
+    getLrcData({ type, text, lrc_data }) {
+      if (
+        type === "SentenceSegwordChs" ||
+        type === "PinyinEnglish" ||
+        type === "textBrackets" ||
+        (type === "text" && text.length > 0)
+      ) {
+        if (lrc_data.end_time === -1) {
+          return {
+            begin_time: lrc_data.begin_time,
+            end_time: this.mp3Duration,
+            text: lrc_data.text,
+          };
+        }
+        return lrc_data;
+      }
+      return false;
+    },
+
+    sentPause(isRecord) {
+      this.isRecord = isRecord;
+    },
+
+    handleChangeTime({ begin_time, end_time }) {
+      if (this.unWatch) this.unWatch();
+      this.handleParentPlay();
+      this.$nextTick(() => {
+        this.$refs.audioLine.onTimeupdateTime(begin_time / 1000);
+        this.$refs.audioLine.PlayAudio();
+        // 监听是否已到结束时间,为了选中效果 - 0.01
+        if (end_time === -1) return;
+        let end = end_time / 1000 - 0.01;
+        this.unWatch = this.$watch("curTime", val => {
+          if (val >= end) {
+            this.handleParentPlay();
+            this.$refs.audioLine.onTimeupdateTime(end);
+            this.unWatch();
+            this.unWatch = null;
+          }
+        });
+      });
+    },
+    /** 语音矩阵方法结束 **/
+    // #endregion
+
+    /* 全局事件处理 */
+    handleKeyup({ key }) {
+      if (!this.isKeyboard) return;
+      if (key === "Enter") {
+        this.$refs.Soundrecorddiff.microphone();
+      }
+      if (key === " ") {
+        this.hasSelectedCell
+          ? this.playAudio()
+          : this.$refs.audioLine.PlayAudio();
+      }
+
+      if (!this.hasSelectedCell) return;
+      if (key === "ArrowUp") {
+        let { type, index, row, column } = this.selectData;
+        if (type === "cell" && row > 0) {
+          return this.matrixCellClick(row - 1, column);
+        }
+        if ((type === "column" || type === "row") && index > 0) {
+          return this.selectRowOrColumn(index - 1, type);
+        }
+      }
+      if (key === "ArrowDown") {
+        let { type, index, row, column } = this.selectData;
+        let { rows } = this.matrix;
+        if (type === "cell" && row < rows) {
+          return this.matrixCellClick(row + 1, column);
+        }
+        if ((type === "column" || type === "row") && index < rows) {
+          return this.selectRowOrColumn(index + 1, type);
+        }
+      }
+      if (key === "ArrowLeft") {
+        let { type, row, column } = this.selectData;
+        if (type !== "cell") return;
+        if (column > 0) {
+          return this.matrixCellClick(row, column - 1);
+        }
+      }
+      if (key === "ArrowRight") {
+        let { columns } = this.matrix;
+        let { type, row, column } = this.selectData;
+        if (type !== "cell") return;
+        if (column < columns) {
+          return this.matrixCellClick(row, column + 1);
+        }
+      }
+    },
+
+    handleFullscreen() {
+      let isFullscreen = Boolean(
+        document.fullScreen ||
+          document.mozFullScreen ||
+          document.webkitIsFullScreen ||
+          document.webkitFullScreen ||
+          document.msFullScreen
+      );
+      if (!isFullscreen) this.changeFullScreen();
+    },
+
+    setTopShow(bool) {
+      this.isTopShow = bool;
+    },
+    setBottomShow(bool) {
+      if (!this.recordPlaying && !this.isRecording) {
+        this.isBottomShow = bool;
+      }
+    },
+    getPlayStatus(bool) {
+      this.recordPlaying = bool;
+    },
+    setFontSize(type) {
+      if (this.hzSize >= 34 || this.hzSize <= 76) {
+        type === "+" ? (this.hzSize += 4) : (this.hzSize -= 4);
+      }
+    },
+    playChange(bool) {
+      this.playing = bool;
+    },
+    changeStatus(key) {
+      this[key] = !this[key];
+    },
+    setStatus() {
+      if (this.key === "isRepeat") {
+        if (this.isRepeat) {
+          this.isRepeat = false;
+          this.isAuto = true;
+          this.autoCount = 0;
+          this.key = "isAuto";
+        } else {
+          this.isRepeat = true;
+          this.key = "isRepeat";
+        }
+      } else if (this.key === "isAuto") {
+        if (this.isAuto) {
+          this.isRepeat = false;
+          this.isAuto = false;
+          this.key = "isRepeat";
+        }
+      }
+    },
+    getRerordStatus(bool) {
+      this.isShowCompare = bool;
+    },
+    getMicrophoneStatus(bool) {
+      this.isRecording = bool;
+    },
+
+    getCurCompareTime(curTime) {
+      this.curTime = curTime * 1000;
+    },
+    getCurWordTime(curTime) {
+      this.curWordTime = curTime * 1000;
+    },
+    changeBg(bgIndex) {
+      this.bgIndex = bgIndex;
+    },
+    pauseAudio() {
+      let audio = document.getElementsByTagName("audio");
+      audio.forEach(item => {
+        item.pause();
+      });
+    },
+    exitFullScreen() {
+      this.pauseAudio();
+      this.$emit("exitFullscreen");
+    },
+    changeFullScreen() {
+      this.pauseAudio();
+      this.$emit("changeIsFull");
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+$select-color: #de4444;
+$border-color: #e6e6e6;
+
+$select-color-green: #24b99e;
+$select-color-green-bc: rgba(36, 185, 158, 0.25);
+$select-color-green-hover: #3dd4b8;
+$select-color-green-active: #1fa189;
+
+$select-color-brown: #bd8865;
+$select-color-brown-bc: rgba(189, 136, 101, 0.25);
+$select-color-brown-hover: #d6a687;
+$select-color-brown-active: #a37557;
+
+$dark-color: #ffc600;
+$dark-color-play: #fff2c6;
+
+.voicefull {
+  width: 100%;
+  height: 100vh;
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+
+  &.bg1 {
+    background: #fff;
+    color: #062211;
+
+    .playing {
+      background: url("../../../assets/NPC/pink-pencil.png") no-repeat left top /
+        100% 100%;
+    }
+  }
+
+  // 黑暗模式 强制替换颜色
+  &.bg2 {
+    background: linear-gradient(180deg, #274533 0%, #385f45 100%);
+    color: #fff;
+
+    .column-wrapper {
+      &:hover {
+        color: $dark-color !important;
+      }
+
+      .selected {
+        color: $dark-color !important;
+      }
+
+      .playing {
+        color: $dark-color-play;
+        text-shadow: 0 0 0.1em, 0 0 0.3em;
+      }
+
+      &.highlight-,
+      &.highlight-red,
+      &.highlight-brown,
+      &.highlight-green {
+        color: $dark-color !important;
+      }
+
+      .matrix-checkbox-column-,
+      .matrix-checkbox-column-red,
+      .matrix-checkbox-column-brown,
+      .matrix-checkbox-column-green {
+        &.active {
+          border-color: $dark-color !important;
+
+          &::after {
+            border-color: $dark-color !important;
+          }
+        }
+      }
+    }
+
+    .matrix-top {
+      .matrix-checkbox-row-,
+      .matrix-checkbox-row-green,
+      .matrix-checkbox-row-brown,
+      .matrix-checkbox-row-red {
+        &.active {
+          border-color: $dark-color !important;
+
+          &::after {
+            border-color: $dark-color !important;
+          }
+        }
+      }
+    }
+  }
+
+  &-top {
+    height: 136px;
+    width: 100%;
+    padding: 0 40px;
+
+    .voicefull-top-hidden {
+      width: 100%;
+      height: 136px;
+      visibility: hidden;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
+    .voicefull-top-show {
+      width: 100%;
+      height: 136px;
+      visibility: visible;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
+    .top-left {
+      display: flex;
+      justify-content: flex-start;
+      align-items: center;
+    }
+    .select-bg {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      width: 96px;
+      height: 56px;
+      border: 1px solid rgba(0, 0, 0, 0.1);
+      border-radius: 40px;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      margin-right: 32px;
+
+      &.select-bg-blue {
+        background: rgba(255, 255, 255, 0.1);
+        border: 1px solid rgba(0, 0, 0, 0.1);
+      }
+
+      > div {
+        width: 36px;
+        height: 36px;
+        border-radius: 100%;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        &.bg-white-box {
+          background: 0 0;
+          margin-right: 4px;
+          &.active {
+            background: #de4444;
+          }
+        }
+        &.bg-green-box {
+          background: #fff;
+          &.active {
+            background: #ffc600;
+          }
+        }
+
+        > span {
+          width: 24px;
+          height: 24px;
+          border-radius: 100%;
+
+          cursor: pointer;
+          &.bg-white {
+            background: #fff;
+          }
+          &.bg-green {
+            background: linear-gradient(180deg, #274533 0%, #385f45 100%);
+          }
+        }
+      }
+    }
+
+    .set-fontSize {
+      padding: 0 20px;
+      height: 56px;
+      background: #ffffff;
+      border: 1px solid rgba(0, 0, 0, 0.1);
+      border-radius: 40px;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      &-green {
+        background: rgba(255, 255, 255, 0.1);
+        border: 1px solid rgba(0, 0, 0, 0.1);
+      }
+      > span {
+        width: 24px;
+        height: 24px;
+        margin: 0 4px;
+        &.font-jian {
+          &-black {
+            background: url("../../../assets/NPC/jian-black.png") no-repeat left
+              top;
+            background-size: 100% 100%;
+            cursor: pointer;
+          }
+          &-yellow {
+            background: url("../../../assets/NPC/jian-white.png") no-repeat left
+              top;
+            background-size: 100% 100%;
+            cursor: pointer;
+          }
+          &-white-disabled {
+            background: url("../../../assets/NPC/jian-white-disabled.png")
+              no-repeat left top;
+            background-size: 100% 100%;
+            cursor: pointer;
+          }
+          &-yellow-disabled {
+            background: url("../../../assets/NPC/jian-yellow-disabled.png")
+              no-repeat left top;
+            background-size: 100% 100%;
+            cursor: pointer;
+          }
+        }
+        &.font-img {
+          &-black {
+            background: url("../../../assets/NPC/fontSize-black.png") no-repeat
+              left top;
+            background-size: 100% 100%;
+          }
+          &-yellow {
+            background: url("../../../assets/NPC/fontSize-white.png") no-repeat
+              left top;
+            background-size: 100% 100%;
+          }
+        }
+        &.font-jia {
+          &-black {
+            background: url("../../../assets/NPC/jia-black.png") no-repeat left
+              top;
+            background-size: 100% 100%;
+            cursor: pointer;
+          }
+          &-yellow {
+            background: url("../../../assets/NPC/jia-white.png") no-repeat left
+              top;
+            background-size: 100% 100%;
+            cursor: pointer;
+          }
+          &-white-disabled {
+            background: url("../../../assets/NPC/jia-white-disabled.png")
+              no-repeat left top;
+            background-size: 100% 100%;
+            cursor: pointer;
+          }
+          &-yellow-disabled {
+            background: url("../../../assets/NPC/jia-yellow-disabled.png")
+              no-repeat left top;
+            background-size: 100% 100%;
+            cursor: pointer;
+          }
+        }
+      }
+    }
+
+    .top-middle {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      .audio-box {
+        width: 56px;
+        height: 56px;
+        background: #ffffff;
+        border: 1px solid rgba(0, 0, 0, 0.1);
+        border-radius: 40px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        &-green {
+          background: rgba(255, 255, 255, 0.1);
+          border: 1px solid rgba(0, 0, 0, 0.1);
+        }
+      }
+    }
+  }
+
+  .op-btn {
+    width: 56px;
+    height: 56px;
+    border-radius: 100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    cursor: pointer;
+    margin-left: 32px;
+    background: #ffffff;
+    border: 1px solid rgba(0, 0, 0, 0.1);
+
+    &-green {
+      background: rgba(255, 255, 255, 0.1);
+      border: 1px solid rgba(0, 0, 0, 0.1);
+    }
+    &.close-btn {
+      background: #274533;
+      border: 1px solid rgba(0, 0, 0, 0.1);
+    }
+    > span {
+      width: 24px;
+      height: 24px;
+      &.close-icon {
+        background: url("../../../assets/icon/cross-24-normal-black.png")
+          no-repeat left top;
+        background-size: 100% 100%;
+        &-white {
+          background: url("../../../assets/icon/cross-24-normal-white.png")
+            no-repeat left top;
+          background-size: 100% 100%;
+        }
+      }
+    }
+  }
+  .repeat-icon {
+    background: url("../../../assets/icon/Repeat-24-normal-red.png") no-repeat
+      left top;
+    background-size: 100% 100%;
+    &.disabled {
+      background: url("../../../assets/icon/Repeat-24-disable-Black.png")
+        no-repeat left top;
+      background-size: 100% 100%;
+    }
+    &-yellow {
+      background: url("../../../assets/icon/Repeat-24-normal-yellow.png")
+        no-repeat left top;
+      background-size: 100% 100%;
+    }
+    &.auto-icon {
+      background: url("../../../assets/icon/Auto-24-next-red.png") no-repeat
+        left top;
+      background-size: 100% 100%;
+      &-yellow {
+        background: url("../../../assets/icon/Auto-24-next-yellow.png")
+          no-repeat left top;
+        background-size: 100% 100%;
+      }
+    }
+  }
+  .keyboard-icon {
+    background: url("../../../assets/icon/enter-24-keyboard-red.png") no-repeat
+      left top;
+    background-size: 100% 100%;
+    &.disabled {
+      background: url("../../../assets/icon/enter-24-keyboard-disable-Black.png")
+        no-repeat left top;
+      background-size: 100% 100%;
+    }
+    &-yellow {
+      background: url("../../../assets/icon/enter-24-keyboard-yellow.png")
+        no-repeat left top;
+      background-size: 100% 100%;
+    }
+  }
+
+  &-content {
+    flex: 1;
+    width: 100%;
+    display: flex;
+    align-items: center;
+
+    // 语音矩阵
+
+    .matrix {
+      display: inline-grid;
+      gap: 20px 48px;
+      width: 100%;
+      height: 100%;
+      word-break: break-word;
+
+      %matrix-checkbox {
+        position: relative;
+        top: calc(50% - 5px);
+        display: block;
+        width: 0.5em;
+        height: 0.5em;
+        border: 1.5px solid #b0b0b0;
+        border-radius: 4px;
+        margin: 0 auto;
+        cursor: pointer;
+
+        &.active {
+          border-color: $select-color;
+
+          &::after {
+            box-sizing: content-box;
+            content: "";
+            border: 1px solid $select-color;
+            border-left: 0;
+            border-top: 0;
+            width: 0.1em;
+            height: 0.25em;
+            top: 18%;
+            left: 37%;
+            position: absolute;
+            transform: rotate(45deg) scaleY(1);
+            transition: transform 0.15s ease-in 0.05s;
+            transform-origin: center;
+          }
+        }
+      }
+
+      .matrix-checkbox-row-,
+      .matrix-checkbox-row-red {
+        @extend %matrix-checkbox;
+      }
+
+      .matrix-checkbox-row-green {
+        @extend %matrix-checkbox;
+
+        &.active {
+          border-color: $select-color-green-active;
+
+          &::after {
+            border-color: $select-color-green-active;
+          }
+        }
+      }
+
+      .matrix-checkbox-row-brown {
+        @extend %matrix-checkbox;
+
+        &.active {
+          border-color: $select-color-brown-active;
+
+          &::after {
+            border-color: $select-color-brown-active;
+          }
+        }
+      }
+
+      %matrix-checkbox-column,
+      .matrix-checkbox-column-,
+      .matrix-checkbox-column-red {
+        @extend %matrix-checkbox;
+
+        top: calc(50% - 7px);
+        right: -48px;
+      }
+
+      .matrix-checkbox-column-green {
+        @extend %matrix-checkbox-column;
+
+        &.active {
+          border-color: $select-color-green-active;
+
+          &::after {
+            border-color: $select-color-green-active;
+          }
+        }
+      }
+
+      .matrix-checkbox-column-brown {
+        @extend %matrix-checkbox-column;
+
+        &.active {
+          border-color: $select-color-brown-active;
+
+          &::after {
+            border-color: $select-color-brown-active;
+          }
+        }
+      }
+
+      .read {
+        background-color: #eaeaea;
+      }
+
+      .highlight-,
+      .highlight-red {
+        color: $select-color;
+      }
+
+      .highlight-green {
+        color: $select-color-green;
+      }
+
+      .highlight-brown {
+        color: $select-color-brown;
+      }
+
+      .column-wrapper {
+        padding: 4px;
+
+        %column {
+          width: 100%;
+          height: 100%;
+          min-height: 32px;
+          border-radius: 8px;
+          transition: 0.2s;
+          cursor: pointer;
+          user-select: none;
+
+          &:hover {
+            border-color: #8c8c8c;
+          }
+
+          &.selected {
+            color: $select-color;
+            border-color: $select-color;
+          }
+
+          &.title {
+            background-color: transparent;
+            border-color: transparent;
+          }
+
+          > span {
+            display: inline-block;
+            padding: 4px 12px;
+          }
+        }
+
+        %column-red,
+        .column-,
+        .column-red {
+          @extend %column;
+
+          position: relative;
+          font-family: "GB-PINYINOK-B", "FZJCGFKTK";
+
+          &::before {
+            display: inline-block;
+            content: "";
+            vertical-align: middle;
+          }
+        }
+
+        .column-green {
+          @extend %column-red;
+
+          &.selected {
+            color: $select-color-green;
+            border-color: $select-color-green;
+          }
+        }
+
+        .column-brown {
+          @extend %column-red;
+
+          &.selected {
+            color: $select-color-brown;
+            border-color: $select-color-brown;
+          }
+        }
+
+        %sentence,
+        .sentence-,
+        .sentence-red {
+          @extend %column;
+
+          display: inline-grid;
+          padding: 4px 12px;
+          column-gap: 8px;
+          justify-items: center;
+          justify-content: start;
+
+          > span {
+            padding: 0;
+          }
+
+          .pinyin {
+            font-family: "GB-PINYINOK-B";
+            opacity: 0.45;
+            font-size: 0.75em;
+          }
+
+          .chs {
+            font-family: "FZJCGFKTK";
+            font-size: 1em;
+          }
+        }
+
+        .sentence-green {
+          @extend %sentence;
+
+          &.selected {
+            color: $select-color-green;
+            border-color: $select-color-green;
+          }
+        }
+
+        .sentence-brown {
+          @extend %sentence;
+
+          &.selected {
+            color: $select-color-brown;
+            border-color: $select-color-brown;
+          }
+        }
+
+        .connection {
+          position: relative;
+          top: calc(50% - 1px);
+          height: 2px;
+          width: 16px;
+          margin: 0 -4px;
+          border-radius: 4px;
+          background-color: #252525;
+
+          &.highlight-bc-,
+          &.highlight-bc-red {
+            background-color: $select-color;
+          }
+
+          &.highlight-bc-green {
+            background-color: $select-color-green;
+          }
+
+          &.highlight-bc-brown {
+            background-color: $select-color-brown;
+          }
+        }
+        // 拼音 + 文字
+        %pinyinEnglish,
+        .pinyinEnglish-,
+        .pinyinEnglish-red {
+          @extend %column;
+
+          .inside-wrapper {
+            padding: 4px 12px;
+
+            .pinyin {
+              font-family: "GB-PINYINOK-B";
+              font-size: 1em;
+            }
+
+            .english {
+              font-family: "robot";
+              opacity: 0.45;
+              font-size: 0.75em;
+            }
+          }
+        }
+
+        .pinyinEnglish-green {
+          @extend %pinyinEnglish;
+
+          &.selected {
+            color: $select-color-green;
+            border-color: $select-color-green;
+          }
+        }
+
+        .pinyinEnglish-brown {
+          @extend %pinyinEnglish;
+
+          &.selected {
+            color: $select-color-brown;
+            border-color: $select-color-brown;
+          }
+        }
+
+        %textBrackets,
+        .textBrackets-,
+        .textBrackets-red {
+          @extend %column;
+
+          .brackets-text {
+            font-family: "GB-PINYINOK-B";
+          }
+
+          .brackets {
+            font-size: 1em;
+            font-family: "FZJCGFKTK";
+          }
+        }
+
+        .textBrackets-green {
+          @extend %textBrackets;
+
+          &.selected {
+            color: $select-color-green;
+            border-color: $select-color-green;
+          }
+        }
+
+        .textBrackets-brown {
+          @extend %textBrackets;
+
+          &.selected {
+            color: $select-color-brown;
+            border-color: $select-color-brown;
+          }
+        }
+      }
+    }
+  }
+
+  &-bottom {
+    height: 136px;
+    width: 100%;
+
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding-right: 40px;
+
+    .voicefull-bottom-show {
+      height: 136px;
+      width: 100%;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      visibility: visible;
+
+      &.hidden {
+        visibility: hidden;
+      }
+    }
+
+    .bottom-left {
+      display: flex;
+      justify-content: flex-start;
+      align-items: center;
+
+      .compare-box {
+        height: 56px;
+        padding: 16px 16px;
+
+        border: 1px solid rgba(0, 0, 0, 0.1);
+        border-radius: 0 40px 40px 0;
+        border-left: 0px solid rgba(0, 0, 0, 0.1);
+        &-white {
+          background: rgba(255, 255, 255, 0.1);
+          border: 1px solid rgba(0, 0, 0, 0.1);
+          border-left: 0;
+        }
+      }
+    }
+  }
+}
+</style>
+
+<style lang="scss">
+.NPC-Big-Book-preview-green {
+  .bg1 {
+    .repeat-icon {
+      background: url("../../../assets/icon/Repeat-24-normal-Green.png")
+        no-repeat left top;
+      background-size: 100% 100%;
+    }
+  }
+}
+
+.NPC-Big-Book-preview-brown {
+  .bg1 {
+    .repeat-icon {
+      background: url("../../../assets/icon/Repeat-24-normal-Brown.png")
+        no-repeat left top;
+      background-size: 100% 100%;
+    }
+  }
+}
+</style>

+ 476 - 0
src/components/Adult/preview/VoiceMatrixFullscreenAudio.vue

@@ -0,0 +1,476 @@
+<template>
+  <div :class="['Audio', 'AudioFull']">
+    <div
+      :class="['audioLine3', bgIndex == 1 ? 'audioLine3-green' : '']"
+      @click="hasSelectedCell ? parentPalyAudio() : PlayAudio()"
+    >
+      <div
+        class="play"
+        :class="[
+          audio.loading ? 'loadBtn' : audio.playing ? 'playBtn' : 'pauseBtn',
+        ]"
+      />
+    </div>
+
+    <audio
+      :id="audioId"
+      :ref="audioId"
+      :src="mp3"
+      preload="meta"
+      @loadedmetadata="onLoadedmetadata"
+      @timeupdate="onTimeupdate"
+      @canplaythrough="oncanplaythrough"
+    />
+  </div>
+</template>
+
+<script>
+export default {
+  props: [
+    "mp3",
+    "bgIndex",
+    "getCurTime",
+    "stopAudio",
+    "width",
+    "isRepeat",
+    "themeColor",
+    "hideSlider",
+    "ed",
+    "bg",
+    "audioId",
+    "type",
+    "hasSelectedCell",
+  ],
+  data() {
+    // 这里存放数据
+    return {
+      playValue: 0,
+      audio: {
+        // 该字段是音频是否处于播放状态的属性
+        playing: false,
+        // 音频当前播放时长
+        currentTime: 0,
+        // 音频最大播放时长
+        maxTime: 0,
+        isPlaying: false,
+        loading: false,
+      },
+      audioAllTime: null, // 展示总时间
+      duioCurrentTime: null, // 剩余时间
+      count: 0,
+      loading: null,
+      isClick: false,
+    };
+  },
+  // 计算属性 类似于data概念
+  computed: {
+    sliderWidth() {
+      let width = 0;
+      if (this.width) {
+        width = this.width;
+      } else {
+        width = 662;
+      }
+      return width;
+    },
+  },
+  // 监控data中数据变化
+  watch: {
+    stopAudio: {
+      handler(val) {
+        if (val) {
+          this.$refs[this.audioId].pause();
+          this.audio.playing = false;
+        }
+      }
+    },
+    "audio.playing": {
+      handler(val) {
+        this.$emit("playChange", val);
+      },
+    },
+  },
+  mounted() {
+    let audioId = this.audioId;
+    this.$refs[audioId].addEventListener("loadstart", () => {});
+    this.$refs[audioId].addEventListener("play", () => {
+      this.audio.playing = true;
+      this.audio.isPlaying = true;
+      this.audio.loading = false;
+    });
+    this.$refs[audioId].addEventListener("pause", () => {
+      this.audio.playing = false;
+      if (this.hideSlider && this.audio.currentTime * 1000 + 500 > this.ed) {
+        this.$emit("sentPause", true);
+      }
+      this.$emit("handleListenRead", false);
+    });
+    this.$refs[audioId].addEventListener("ended", () => {
+      this.audio.playing = false;
+      this.audio.isPlaying = false;
+      this.$emit("handleListenRead", false);
+      this.isClick = false;
+    });
+
+    this.$nextTick(() => {
+      document
+        .getElementsByClassName("el-slider__button-wrapper")[0]
+        .addEventListener("mousedown", () => {
+          this.$refs[audioId].pause();
+          this.audio.playing = false;
+        });
+    });
+  },
+  methods: {
+    parentPalyAudio() {
+      if (this.hasSelectedCell) {
+        return this.$emit("parentPlayAudio");
+      }
+    },
+
+    PlayAudio() {
+      let audioId = this.audioId;
+      let audio = document.getElementsByTagName("audio");
+      audio.forEach(item => {
+        if (item.src == this.mp3) {
+          if (item.id !== audioId) {
+            item.pause();
+          }
+        } else {
+          item.pause();
+        }
+      });
+      let video = document.getElementsByTagName("video");
+      video.forEach(vItem => {
+        vItem.pause();
+      });
+
+      if (this.audio.playing) {
+        this.$refs[audioId].pause();
+        this.audio.playing = false;
+        this.$emit("handleListenRead", false);
+        this.isClick = false;
+      } else {
+        if (this.count == 0) {
+          this.audio.loading = true;
+          this.count++;
+        }
+        if (this.hideSlider) {
+          this.$refs[audioId].play();
+          this.onTimeupdateTime(this.bg / 1000);
+        } else {
+          this.$refs[audioId].play();
+        }
+        this.$emit("handleChangeStopAudio");
+        this.$emit("handleListenRead", true);
+        this.isClick = true;
+      }
+    },
+    oncanplaythrough() {
+      // setTimeout(() => {
+      this.audio.loading = false;
+
+      // }, 10000);
+    },
+    // 点击 拖拽播放音频
+    changeCurrentTime(value) {
+      let audioId = this.audioId;
+      this.$refs[audioId].play();
+      this.audio.playing = true;
+      this.$refs[audioId].currentTime = parseInt(
+        (value / 100) * this.audio.maxTime
+      );
+    },
+    mousedown() {
+      let audioId = this.audioId;
+      this.$refs[audioId].pause();
+      this.audio.playing = false;
+    },
+    // 进度条格式化toolTip
+    formatProcessToolTip(index) {
+      return this.realFormatSecond(
+        parseInt((this.audio.maxTime / 100) * index)
+      );
+    },
+    // 音频加载完之后
+    onLoadedmetadata(res) {
+      this.audio.maxTime = parseInt(res.target.duration);
+      this.audioAllTime = this.realFormatSecond(this.audio.maxTime);
+    },
+    // 当音频当前时间改变后,进度条也要改变
+    onTimeupdate(res) {
+      let audioId = this.audioId;
+      this.audio.currentTime = res.target.currentTime;
+      this.getCurTime(res.target.currentTime);
+      this.playValue = (this.audio.currentTime / this.audio.maxTime) * 100;
+      if (this.type == "audioLine") {
+        if (!this.isClick && this.audio.currentTime * 1000 > this.ed) {
+          this.$refs[audioId].pause();
+          this.$emit("emptyEd");
+        }
+      } else if (this.audio.currentTime * 1000 > this.ed) {
+        this.$refs[audioId].pause();
+      }
+    },
+    onTimeupdateTime(res, playFlag) {
+      if (!res) return;
+      let audioId = this.audioId;
+      this.$refs[audioId].currentTime = res;
+      this.playValue = (res / this.audio.maxTime) * 100;
+      if (playFlag) {
+        let audio = document.getElementsByTagName("audio");
+        audio.forEach(item => {
+          if (item.id !== audioId) {
+            item.pause();
+          }
+        });
+        this.$refs[audioId].play();
+      }
+    },
+    // 将整数转换成 时:分:秒的格式
+    realFormatSecond(value) {
+      let theTime = parseInt(value); // 秒
+      let theTime1 = 0; // 分
+      let theTime2 = 0; // 小时
+      if (theTime > 60) {
+        theTime1 = parseInt(theTime / 60);
+        theTime = parseInt(theTime % 60);
+        if (theTime1 > 60) {
+          theTime2 = parseInt(theTime1 / 60);
+          theTime1 = parseInt(theTime1 % 60);
+        }
+      }
+      let result = String(parseInt(theTime));
+      if (result < 10) {
+        result = "0" + result;
+      }
+      if (theTime1 > 0) {
+        result = String(parseInt(theTime1)) + ":" + result;
+        if (theTime1 < 10) {
+          result = "0" + result;
+        }
+      } else {
+        result = "00:" + result;
+      }
+      if (theTime2 > 0) {
+        result = String(parseInt(theTime2)) + ":" + result;
+        if (theTime2 < 10) {
+          result = "0" + result;
+        }
+      } else {
+        // result = "00:" + result;
+      }
+      return result;
+    },
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.AudioFull {
+  .audioLine {
+    display: flex;
+    align-items: center;
+    width: 100%;
+    height: 40px;
+    background: #ffffff;
+    box-sizing: border-box;
+    border-radius: 4px;
+    &-green {
+      background: 0 0;
+    }
+    .play {
+      margin-right: 12px;
+      margin-left: 8px;
+      width: 16px;
+      min-width: 16px;
+      height: 16px;
+      cursor: pointer;
+      display: block;
+    }
+
+    span {
+      font-size: 16px;
+      line-height: 19px;
+      color: #000;
+      margin-left: 8px;
+      margin-right: 12px;
+      font-weight: bold;
+      font-size: 16px;
+      line-height: 24px;
+      text-align: right;
+      &.color-white {
+        color: #fff;
+      }
+    }
+  }
+  .audioLine2 {
+    .play-icon {
+      width: 16px;
+      height: 16px;
+      cursor: pointer;
+      &.playBtn-icon {
+        background: url("../../../assets/icon/pauseC-16-normal-red.png")
+          no-repeat left top;
+        background-size: 100% 100%;
+      }
+      &.pauseBtn-icon {
+        background: url("../../../assets/NPC/compare-pause-red.png") no-repeat
+          left top;
+        background-size: 100% 100%;
+      }
+    }
+  }
+  .loadBtn {
+    background: url("../../../assets/NPC/loading-red.png") no-repeat left top;
+    background-size: 100% 100%;
+  }
+  .audioLine3 {
+    width: 56px;
+    height: 56px;
+    background: #ffffff;
+    border: 1px solid rgba(0, 0, 0, 0.1);
+    border-radius: 40px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    .play {
+      width: 24px;
+      height: 24px;
+      cursor: pointer;
+      &.playBtn {
+        background: url("../../../assets/icon/pause-24-normal-red.png")
+          no-repeat left top;
+        background-size: 100% 100%;
+      }
+      &.pauseBtn {
+        background: url("../../../assets/icon/play-24-normal-red.png") no-repeat
+          left top;
+        background-size: 100% 100%;
+      }
+    }
+    &-green {
+      background: rgba(255, 255, 255, 0.1);
+      border: 1px solid rgba(0, 0, 0, 0.1);
+      .play {
+        &.playBtn {
+          background: url("../../../assets/icon/pause-24-normal-yellow.png")
+            no-repeat left top;
+          background-size: 100% 100%;
+        }
+        &.pauseBtn {
+          background: url("../../../assets/icon/play-24-normal-yellow.png")
+            no-repeat left top;
+          background-size: 100% 100%;
+        }
+      }
+    }
+  }
+}
+</style>
+<style lang="scss">
+.Audio {
+  .el-slider__bar {
+    height: 2px;
+    background: #de4444;
+  }
+  .el-slider__button {
+    background: #de4444;
+    border: none;
+  }
+  .el-slider__button-wrapper {
+    width: 25px;
+  }
+  .el-slider__button-wrapper {
+    position: relative;
+    z-index: 0;
+  }
+  .el-slider__button {
+    width: 8px;
+    height: 8px;
+    top: 12px;
+    position: absolute;
+  }
+  .el-slider__runway {
+    margin: 0;
+    padding: 0;
+    background: #e5e5e5;
+    border-radius: 0px;
+    height: 2px;
+  }
+  .el-slider {
+    position: relative;
+  }
+}
+.NPC-Book-Sty {
+  .Audio {
+    .el-slider__bar {
+      height: 2px;
+      background: #de4444;
+    }
+    .el-slider__button {
+      background: #de4444;
+      border: none;
+    }
+  }
+}
+.NPC-Big-Book-preview-green {
+  .Audio {
+    .el-slider__bar {
+      background: #24b99e !important;
+    }
+    .el-slider__button {
+      background: #24b99e !important;
+    }
+    .audioLine2 {
+      .play-icon {
+        &.playBtn-icon {
+          background: url("../../../assets/icon/pauseC-16-normal-Green.png")
+            no-repeat left top;
+          background-size: 100% 100%;
+        }
+        &.pauseBtn-icon {
+          background: url("../../../assets/NPC/compare-pause-green.png")
+            no-repeat left top;
+          background-size: 100% 100%;
+        }
+      }
+    }
+    .loadBtn {
+      background: url("../../../assets/NPC/loading-green.png") no-repeat left
+        top;
+      background-size: 100% 100%;
+    }
+  }
+}
+.NPC-Big-Book-preview-brown {
+  .Audio {
+    .el-slider__bar {
+      background: #bd8865 !important;
+    }
+    .el-slider__button {
+      background: #bd8865 !important;
+    }
+    .audioLine2 {
+      .play-icon {
+        &.playBtn-icon {
+          background: url("../../../assets/icon/pauseC-16-normal-Brown.png")
+            no-repeat left top;
+          background-size: 100% 100%;
+        }
+        &.pauseBtn-icon {
+          background: url("../../../assets/NPC/compare-pause-brown.png")
+            no-repeat left top;
+          background-size: 100% 100%;
+        }
+      }
+    }
+    .loadBtn {
+      background: url("../../../assets/NPC/loading-brown.png") no-repeat left
+        top;
+      background-size: 100% 100%;
+    }
+  }
+}
+</style>

+ 4 - 4
vue.config.js

@@ -43,16 +43,16 @@ module.exports = {
       // change xxx-api/login => mock/login
       // detail: https://cli.vuejs.org/config/#devserver-proxy
       [process.env.VUE_APP_BASE_API]: {
-        target: `http://gcls.utschool.cn/`,
-        //target: `http://gcls.helxsoft.cn/`,
+        // target: `http://gcls.utschool.cn/`,
+        target: `http://gcls.helxsoft.cn/`,
         changeOrigin: true,
         pathRewrite: {
           ['^' + process.env.VUE_APP_BASE_API]: ''
         }
       },
       [process.env.VUE_APP_PDF]: {
-        // target: 'https://file-kf.helxsoft.cn/',
-        target: 'https://file-cs.helxsoft.cn/',
+        target: 'https://file-kf.helxsoft.cn/',
+        // target: 'https://file-cs.helxsoft.cn/',
         changeOrigin: true,
         pathRewrite: {
           ['^' + process.env.VUE_APP_PDF]: ''