lsc 1 tahun lalu
induk
melakukan
ec3a47ddd1
30 mengubah file dengan 3293 tambahan dan 300 penghapusan
  1. 1 1
      dist/index.html
  2. 0 0
      dist/static/css/app.290593b2236c218171d50189d93fe30c.css
  3. 0 0
      dist/static/css/app.290593b2236c218171d50189d93fe30c.css.map
  4. TEMPAT SAMPAH
      dist/static/img/englishVoice.802b608.png
  5. TEMPAT SAMPAH
      dist/static/img/env_background.adccd37.png
  6. 0 0
      dist/static/js/app.a4b255fb20d9445519f6.js
  7. 0 0
      dist/static/js/app.a4b255fb20d9445519f6.js.map
  8. 0 0
      dist/static/js/manifest.3ad1d5771e9b13dbdad2.js.map
  9. 0 0
      dist/static/js/vendor.2ea62301d5bc18e5b15b.js
  10. 0 0
      dist/static/js/vendor.2ea62301d5bc18e5b15b.js.map
  11. 731 286
      package-lock.json
  12. 1 0
      package.json
  13. TEMPAT SAMPAH
      src/assets/icon/englishVoice/coin.png
  14. TEMPAT SAMPAH
      src/assets/icon/englishVoice/restart.png
  15. TEMPAT SAMPAH
      src/assets/icon/englishVoice/star.png
  16. TEMPAT SAMPAH
      src/assets/icon/englishVoice/start_aduio.png
  17. TEMPAT SAMPAH
      src/assets/icon/englishVoice/stop_audio.png
  18. TEMPAT SAMPAH
      src/assets/icon/env_background.png
  19. TEMPAT SAMPAH
      src/assets/icon/thirdToolList/englishVoice.png
  20. 96 0
      src/components/EnglishVoice2/component/left.vue
  21. 511 0
      src/components/EnglishVoice2/component/right.vue
  22. 230 0
      src/components/EnglishVoice2/index.vue
  23. 103 0
      src/components/checkEnglishVoice/component/left.vue
  24. 530 0
      src/components/checkEnglishVoice/component/right.vue
  25. 335 0
      src/components/checkEnglishVoice/index.vue
  26. 8 1
      src/components/courseDetail.vue
  27. 236 6
      src/components/easy2/studyStudent.vue
  28. 254 2
      src/components/easy3/studyStudent.vue
  29. 255 3
      src/components/studyStudent.vue
  30. 2 1
      src/main.js

+ 1 - 1
dist/index.html

@@ -18,7 +18,7 @@
       border-radius: 10px;
       -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
       background-color: rgba(0, 0, 0, 0.1);
-    }</style><link href=./static/css/app.dbfd8cc35b8a4bcfa32e4f45f44656a7.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.3ad1d5771e9b13dbdad2.js></script><script type=text/javascript src=./static/js/vendor.dec56539cbda05d478b6.js></script><script type=text/javascript src=./static/js/app.804434d27742a5bc9a1e.js></script></body></html><script>function stopSafari() {
+    }</style><link href=./static/css/app.290593b2236c218171d50189d93fe30c.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.3ad1d5771e9b13dbdad2.js></script><script type=text/javascript src=./static/js/vendor.2ea62301d5bc18e5b15b.js></script><script type=text/javascript src=./static/js/app.a4b255fb20d9445519f6.js></script></body></html><script>function stopSafari() {
     //阻止safari浏览器双击放大功能
     let lastTouchEnd = 0  //更新手指弹起的时间
     document.documentElement.addEventListener("touchstart", function (event) {

File diff ditekan karena terlalu besar
+ 0 - 0
dist/static/css/app.290593b2236c218171d50189d93fe30c.css


File diff ditekan karena terlalu besar
+ 0 - 0
dist/static/css/app.290593b2236c218171d50189d93fe30c.css.map


TEMPAT SAMPAH
dist/static/img/englishVoice.802b608.png


TEMPAT SAMPAH
dist/static/img/env_background.adccd37.png


File diff ditekan karena terlalu besar
+ 0 - 0
dist/static/js/app.a4b255fb20d9445519f6.js


File diff ditekan karena terlalu besar
+ 0 - 0
dist/static/js/app.a4b255fb20d9445519f6.js.map


File diff ditekan karena terlalu besar
+ 0 - 0
dist/static/js/manifest.3ad1d5771e9b13dbdad2.js.map


File diff ditekan karena terlalu besar
+ 0 - 0
dist/static/js/vendor.2ea62301d5bc18e5b15b.js


File diff ditekan karena terlalu besar
+ 0 - 0
dist/static/js/vendor.2ea62301d5bc18e5b15b.js.map


File diff ditekan karena terlalu besar
+ 731 - 286
package-lock.json


+ 1 - 0
package.json

@@ -35,6 +35,7 @@
     "recordrtc": "^5.6.2",
     "relation-graph": "^2.0.26",
     "vue": "^2.5.2",
+    "vue-audio-better": "^3.0.1",
     "vue-cookies": "^1.7.4",
     "vue-jsmind": "^1.5.0",
     "vue-pdf": "^4.2.0",

TEMPAT SAMPAH
src/assets/icon/englishVoice/coin.png


TEMPAT SAMPAH
src/assets/icon/englishVoice/restart.png


TEMPAT SAMPAH
src/assets/icon/englishVoice/star.png


TEMPAT SAMPAH
src/assets/icon/englishVoice/start_aduio.png


TEMPAT SAMPAH
src/assets/icon/englishVoice/stop_audio.png


TEMPAT SAMPAH
src/assets/icon/env_background.png


TEMPAT SAMPAH
src/assets/icon/thirdToolList/englishVoice.png


+ 96 - 0
src/components/EnglishVoice2/component/left.vue

@@ -0,0 +1,96 @@
+<template>
+    <div class="o_box">
+        <div class="o_box_title">
+            <div>标题</div>
+            <div>{{ title }}</div>
+        </div>
+        <div class="o_box_topic">
+            <div class="o_box_child" v-for="(item, index) in checkJson" :key="index" :class="{active:checkType == index}" @click="setType(index)">
+                <span>{{ index + 1  }}.{{ item.content }}</span>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    props: {
+        title: {
+            type: String,
+        },
+        detail: {
+            type: String,
+        }, 
+        checkJson: {
+            type: Array,
+        }, 
+        checkType: {
+            type: Number,
+        },
+    },
+    methods: {
+        setType(index) {
+            this.$emit('setType', index)
+        }
+    },
+
+
+}
+
+</script>
+
+<style scoped>
+.o_box{
+    width: 100%;
+    height: 100%;
+    overflow: auto;
+    padding: 20px 15px;
+    box-sizing:border-box;
+}
+.o_box_title{
+    width:100%;
+    margin-bottom: 25px;
+}
+.o_box_title > div + div{
+    margin-top: 8px;
+}
+
+.o_box_title > div:nth-child(1){
+ color: #00000066;
+ font-size: 14px;
+ letter-spacing: 6px;
+}
+.o_box_title > div:nth-child(2){
+    color:#3981FA;
+    font-size: 24px;
+    font-style: italic;
+}
+
+.o_box_topic{}
+.o_box_child{
+    height: 42px;
+    display: flex;
+    align-items: center;
+    color: #000;
+    border-radius: 5px;
+    border: 2px solid #3681FC;
+    margin-bottom: 10px;
+    width: 100%;
+    padding: 0 10px;
+    box-sizing: border-box;
+    cursor: pointer;
+}
+.o_box_child.active{
+    background: #3681FC;
+    color: #fff;
+}
+
+.o_box_child > span{
+    width: 100%;
+    display: block;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    word-wrap: break-word;
+}
+</style>

+ 511 - 0
src/components/EnglishVoice2/component/right.vue

@@ -0,0 +1,511 @@
+<template>
+  <div class="o_box">
+    <div class="o_top">
+
+    </div>
+    <div class="o_content">
+      <div class="word_box" v-if="cjson.type == 'word' || cjson.type == 'QA'">
+        <img class="word_img" :src="cjson.img" alt="" v-if="cjson.img" @click="previewImg(cjson.img)">
+        <div class="word_content">
+          {{ cjson.content }}
+        </div>
+      </div>
+      <div class="sentence_box" v-if="cjson.type == 'sentence'">
+        <span>{{ cjson.content }}</span>
+      </div>
+    </div>
+    <div class="o_bottom" v-loading="isloading">
+      <div class="audio" v-if="!LuAudioUrl">
+        <img v-if="!isRecord" src="../../../assets/icon/englishVoice/start_aduio.png" alt="" @click="startRecorder">
+        <img v-else src="../../../assets/icon/englishVoice/stop_audio.png" alt="" @click="startRecorder">
+      </div>
+      <div class="audio_word" v-if="!LuAudioUrl">
+        <span v-if="!isRecord">点击话筒开始录音</span>
+        <span v-else>点击话筒结束录音</span>
+      </div>
+      <div v-if="LuAudioUrl" class="audio_b">
+        <mini-audio :audio-source="LuAudioUrl" class="audio_class"></mini-audio>
+      </div>
+      <div v-if="LuAudioUrl" class="audio_rerecord" @click="restart()">
+        <span>录音</span>
+      </div>
+      <div class="audio_index" v-if="!isRecord">
+        <div class="audio_index_last" :class="{ disabled: checkType == 0 }" @click="checkIndex('-1')">
+          <img src="../../../assets/icon/englishVoice/coin.png" alt="">
+        </div>
+        <div class="audio_index_content">
+          <span>{{ checkType + 1 }}</span>
+          <span>/</span>
+          <span>{{ checkJson.length }}</span>
+        </div>
+        <div class="audio_index_next" :class="{ disabled: checkType == (checkJson.length - 1) }" @click="checkIndex('1')">
+          <img src="../../../assets/icon/englishVoice/coin.png" alt="">
+        </div>
+      </div>
+      <div v-else class="audio_ing">
+        <span>正在录音...</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import Recorder from "js-audio-recorder";
+const lamejs = require("lamejs");
+
+const recorder = new Recorder({
+  sampleBits: 16, // 采样位数,支持 8 或 16,默认是16
+  sampleRate: 48000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000
+  numChannels: 1, // 声道,支持 1 或 2, 默认是1
+  // compiling: false,(0.x版本中生效,1.x增加中) // 是否边录边转换,默认是false
+});
+
+// 绑定事件-打印的是当前录音数据
+recorder.onprogress = function (params) {
+  // console.log('--------------START---------------')
+  // console.log('录音时长(秒)', params.duration);
+  // console.log('录音大小(字节)', params.fileSize);
+  // console.log('录音音量百分比(%)', params.vol);
+  // console.log('当前录音的总数据([DataView, DataView...])', params.data);
+  // console.log('--------------END---------------')
+};
+export default {
+  props: {
+    checkJson: {
+      type: Array,
+    },
+    checkType: {
+      type: Number,
+    },
+    work: {
+      type: Array
+    }
+  },
+  data() {
+    return {
+      cjson: {},
+      LuAudioUrl: "",
+      isRecord: false,
+      isPlayerRecord: false,
+      isloading: false,
+    }
+  },
+  watch: {
+    checkType: {
+      handler: function (newVal, oldVal) {
+        this.cjson = JSON.parse(JSON.stringify(this.checkJson[newVal]));
+        this.LuAudioUrl = this.work[newVal] ? JSON.parse(JSON.stringify(this.work[newVal])) : ''
+      },
+      deep: true,
+    },
+  },
+  methods: {
+    previewImg(url) {
+      this.$hevueImgPreview(url);
+    },
+    restart() {
+      this.LuAudioUrl = ""
+      setTimeout(() => {
+        this.startRecorder()
+      }, 500);
+    },
+    checkIndex(type) {
+      if (type == '1') {
+        if (this.checkType == (this.checkJson.length - 1)) {
+          return;
+        }
+        this.$emit('setType', this.checkType + 1)
+      } else {
+        if (this.checkType == 0) {
+          return;
+        }
+        this.$emit('setType', this.checkType - 1)
+      }
+    },
+    // 开始录音
+    startRecorder() {
+      let _this = this;
+      if (!_this.isRecord) {
+        recorder.destroy(); // 销毁录音
+        _this.isRecord = true;
+        recorder.start().then(
+          () => { },
+          (error) => {
+            _this.isRecord = false;
+            // _this.$message.error(`${error.name} : ${error.message}`);
+            _this.$message.error(`没有找到可使用的麦克风,或者您没有允许此网页使用麦克风`);
+            // 出错了
+            console.log(`${error.name} : ${error.message}`);
+          }
+        );
+      } else {
+        _this.isRecord = false;
+        recorder.stop(); // 结束录音
+        this.getMp3Data()
+      }
+    },
+
+    // 录音播放
+    playRecorder() {
+      if (!recorder.fileSize) {
+        return;
+      }
+      if (!this.isPlayerRecord) {
+        this.isPlayerRecord = true;
+        recorder.play();
+      } else {
+        this.isPlayerRecord = false;
+        recorder.stopPlay(); // 停止录音播放
+      }
+      recorder.onplayend = () => {
+        this.isPlayerRecord = false;
+        console.log("onplayend");
+      };
+    },
+
+    /**
+     * 文件格式转换 wav-map3
+     * */
+    getMp3Data() {
+      if (!recorder.fileSize) {
+        this.$message.error("请录音后在上传语音");
+        return;
+      }
+      const mp3Blob = this.convertToMp3(recorder.getWAV());
+      let audioFile = this.dataURLtoAudio(mp3Blob, "mp3");
+      console.log(audioFile);
+      this.beforeUpload1(audioFile, 3);
+      // recorder.download(mp3Blob, "recorder", "mp3");
+    },
+    convertToMp3(wavDataView) {
+      // 获取wav头信息
+      const wav = lamejs.WavHeader.readHeader(wavDataView); // 此处其实可以不用去读wav头信息,毕竟有对应的config配置
+      const { channels, sampleRate } = wav;
+      const mp3enc = new lamejs.Mp3Encoder(channels, sampleRate, 128);
+      // 获取左右通道数据
+      const result = recorder.getChannelData();
+      const buffer = [];
+      const leftData =
+        result.left &&
+        new Int16Array(result.left.buffer, 0, result.left.byteLength / 2);
+      const rightData =
+        result.right &&
+        new Int16Array(result.right.buffer, 0, result.right.byteLength / 2);
+      const remaining = leftData.length + (rightData ? rightData.length : 0);
+      const maxSamples = 1152;
+      for (let i = 0; i < remaining; i += maxSamples) {
+        const left = leftData.subarray(i, i + maxSamples);
+        let right = null;
+        let mp3buf = null;
+        if (channels === 2) {
+          right = rightData.subarray(i, i + maxSamples);
+          mp3buf = mp3enc.encodeBuffer(left, right);
+        } else {
+          mp3buf = mp3enc.encodeBuffer(left);
+        }
+        if (mp3buf.length > 0) {
+          buffer.push(mp3buf);
+        }
+      }
+
+      const enc = mp3enc.flush();
+      if (enc.length > 0) {
+        buffer.push(enc);
+      }
+      return new Blob(buffer, { type: "audio/mp3" });
+    },
+    dataURLtoAudio(blob, filename) {
+      return new File([blob], filename, { type: "audio/mp3" });
+    },
+    beforeUpload1(event, type) {
+      this.isloading = true
+      var file;
+      if (type == 3) {
+        file = event;
+      } else {
+        file = event.target.files[0];
+      }
+      var credentials = {
+        accessKeyId: "AKIATLPEDU37QV5CHLMH",
+        secretAccessKey: "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",
+      }; //秘钥形式的登录上传
+      window.AWS.config.update(credentials);
+      window.AWS.config.region = "cn-northwest-1"; //设置区域
+
+      var bucket = new window.AWS.S3({ params: { Bucket: "ccrb" } }); //选择桶
+      var _this = this;
+
+      if (file) {
+        var params = {
+          Key:
+            file.name.split(".")[0] +
+            new Date().getTime() +
+            "." +
+            file.name.split(".")[file.name.split(".").length - 1],
+          ContentType: file.type,
+          Body: file,
+          "Access-Control-Allow-Credentials": "*",
+          ACL: "public-read",
+        }; //key可以设置为桶的相抵路径,Body为文件, ACL最好要设置
+        var options = {
+          partSize: 2048 * 1024 * 1024,
+          queueSize: 2,
+          leavePartsOnError: true,
+        };
+        bucket
+          .upload(params, options)
+          .on("httpUploadProgress", function (evt) {
+            //这里可以写进度条
+            // console.log("Uploaded : " + parseInt((evt.loaded * 80) / evt.total) + '%');
+            // _this.progress = parseInt((evt.loaded * 80) / evt.total);
+          })
+          .send(function (err, data) {
+            _this.isloading = false
+            // _this.progress = 100;
+            if (err) {
+              var a = _this.$refs.upload1.uploadFiles;
+              a.splice(a.length - 1, a.length);
+              _this.$message.error("上传失败");
+            } else {
+              if (type == 3) {
+                _this.LuAudioUrl = data.Location;
+                _this.$emit('setWork', _this.LuAudioUrl, _this.checkType)
+              }
+              console.log(data.Location);
+            }
+          });
+      }
+    },
+  },
+  mounted() {
+    this.cjson = JSON.parse(JSON.stringify(this.checkJson[this.checkType]));
+    this.LuAudioUrl = this.work[this.checkType] ? JSON.parse(JSON.stringify(this.work[this.checkType])) : ''
+  },
+}
+</script>
+
+<style scoped>
+.o_box {
+  width: 100%;
+  height: 100%;
+  background-image: url('../../../assets/icon/env_background.png');
+  background-size: cover;
+}
+
+.o_top {
+  height: 65px;
+  position: absolute;
+}
+
+.o_content {
+  height: calc(100% - 185px);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  overflow: hidden;
+}
+
+.o_bottom {
+  height: 185px;
+}
+
+.word_box {
+  width: 500px;
+  background: #fff;
+  border-radius: 10px;
+  position: relative;
+}
+
+.sentence_box {
+  background: #e0e0e04d;
+  width: 500px;
+  border-radius: 15px;
+  max-height: 100%;
+  overflow: auto;
+  padding: 15px;
+  font-size: 16px;
+  color: #000;
+  line-height: 20px;
+  word-break: break-word;
+  white-space: pre-line;
+}
+
+.word_box::before {
+  content: '';
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  display: block;
+  box-shadow: 0 0 4px 4px #0000001c;
+  border-radius: 10px;
+  z-index: 2;
+  background: #fff;
+}
+
+.word_box::after {
+  content: '';
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  background: #fff;
+  display: block;
+  box-shadow: 0 0 4px 4px #0000001c;
+  border-radius: 10px;
+  z-index: 1;
+  top: 15px;
+  left: 15px;
+}
+
+.word_box>.word_img {
+  width: calc(100% - 30px);
+  max-height: 300px;
+  z-index: 999;
+  position: relative;
+  margin: 15px auto 0;
+  display: block;
+  border-radius: 10px;
+  cursor: pointer;
+  object-fit: cover;
+}
+
+
+.word_box>.word_content {
+  position: relative;
+  z-index: 999;
+  text-align: center;
+  font-size: 36px;
+  margin: 15px;
+  font-weight: bold;
+  color: #000;
+  width: calc(100% - 30px);
+}
+
+
+
+.o_bottom .audio {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.o_bottom .audio>img {
+  width: 75px;
+  height: 75px;
+  cursor: pointer;
+}
+
+.o_bottom .audio_word {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: #00000099;
+  font-size: 16px;
+  margin: 10px 0 8px;
+}
+
+.o_bottom .audio_index {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+
+.audio_index_last,
+.audio_index_next {
+  height: 40px;
+  width: 40px;
+  background: #3681fc;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+}
+
+.audio_index_last>img,
+.audio_index_next>img {
+  width: 15px;
+  height: auto;
+}
+
+.audio_index_last.disabled,
+.audio_index_next.disabled {
+  opacity: .6;
+}
+
+.audio_index_last>img {
+  transform: rotate(180deg);
+}
+
+.audio_index_last {
+  margin-right: 20px;
+}
+
+.audio_index_content {
+  color: #000;
+  font-size: 16px;
+}
+
+.audio_index_next {
+  margin-left: 20px;
+}
+
+
+.audio_ing {
+  color: #EE3E3E;
+  margin-top: 25px;
+  font-size: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.audio_b {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 15px;
+}
+
+.audio_rerecord {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 15px;
+}
+
+.audio_rerecord>span {
+  display: flex;
+  border: 1px solid #3981FA;
+  align-items: center;
+  color: #3981FA;
+  padding: 5px 10px;
+  border-radius: 5px;
+  cursor: pointer;
+}
+
+.audio_rerecord>span::before {
+  content: '';
+  width: 15px;
+  height: 15px;
+  background: url('../../../assets/icon/englishVoice/restart.png');
+  display: block;
+  background-size: 100% 100%;
+  margin-right: 5px;
+}
+
+.audio_class {
+  background: #3680fb !important;
+  margin: 0 !important;
+}
+
+.audio_b >>> .vueAudioBetter span:before{
+  color: #fff;
+}
+
+.audio_class>>>.slider .process {
+  background: #000;
+}
+.audio_b >>> .vueAudioBetter .iconfont:active{
+  position: unset !important;
+}</style>

+ 230 - 0
src/components/EnglishVoice2/index.vue

@@ -0,0 +1,230 @@
+<template>
+    <el-dialog title="英语口语" :visible.sync="EnglishVoiceDialog" :append-to-body="true" width="100%"
+        :before-close="handleClose" class="dialog_diy">
+        <div class="ev_box">
+            <div class="ev_box_left">
+                <left :title="title" :detail="detail" :checkJson="checkJson" :checkType="checkType" @setType="setType">
+                </left>
+            </div>
+            <div class="ev_box_right">
+                <right :checkJson="checkJson" :checkType="checkType" @setType="setType" :work="work" @setWork="setWork">
+                </right>
+            </div>
+            <div class="ev_btn">
+                <el-button type="primary" :disabled="!(checkType == (checkJson.length - 1))" @click="addEnglishWork">点击提交</el-button>
+
+            </div>
+        </div>
+        <!-- <span slot="footer" class="dialog-footer">
+            <el-button type="primary" @click="confirm()">确 认</el-button>
+            <el-button @click="close()">关 闭</el-button>
+        </span> -->
+    </el-dialog>
+</template>
+
+<script>
+import right from './component/right.vue'
+import left from './component/left.vue'
+
+export default {
+    components: {
+        right,
+        left
+    },
+    props: {
+        EnglishVoiceDialog: {
+            type: Boolean,
+            default: false
+        },
+        englishVoiceJson: {
+            type: Object
+        },
+        userid:{
+            type:String
+        },
+        id:{
+            type:String
+        },
+        courseType:{
+            type:Number
+        },
+        taskCount:{
+            type:Number
+        },
+        toolindex:{
+            type:Number
+        },
+        englishVoiceJsonWork:{
+            type: Array
+        }
+    },
+    data() {
+        return {
+            checkJson: [],
+            checkJson2: {},
+            title: '',
+            detail: '',
+            checkType: 0,
+            work: [],
+            wScore: 0,
+            scoreDetail: "",
+        }
+    },
+    methods: {
+        handleClose(done) {
+            this.close();
+            done();
+        },
+        close() {
+            this.$emit("update:EnglishVoiceDialog", false);
+        },
+        close2() {
+            this.$emit("update:EnglishVoiceDialog", false);
+        },
+        confirm() {
+            this.close2();
+        },
+        setJson(val) {
+            this.checkJson = val;
+            this.$forceUpdate()
+        },
+        setVoiceJson(val) {
+            let a = JSON.parse(JSON.stringify(val));
+            this.checkJson2 = JSON.parse(JSON.stringify(a));
+            this.title = a.title;
+            this.detail = a.detail;
+            this.checkJson = a.array
+            this.checkType = 0
+            this.work=[]
+            this.work.length = a.array.length
+            let works = JSON.parse(
+                JSON.stringify(this.englishVoiceJsonWork)
+            );
+            for (var i = 0; i < this.work.length; i++) {
+                if(i > (works.length - 1)){
+                    break
+                }
+                this.work[i] = works[i];
+            }
+        },
+        setType(index) {
+            this.checkType = index
+        },
+        setWork(url, index) {
+            this.work[index] = url
+        },
+        addEnglishWork() {
+            let params = [
+                {
+                    uid: this.userid,
+                    cid: this.id,
+                    stage: this.courseType,
+                    task: this.taskCount,
+                    tool: this.toolindex,
+                    content: JSON.stringify(this.work),
+                    type: 17,
+                    atool: 70,
+                    text: "",
+                },
+            ];
+            this.ajax
+                .post(this.$store.state.api + "addCourseWorks5", params)
+                .then((res) => {
+                    this.$message({
+                        message: "提交成功",
+                        type: "success",
+                    });
+                    this.$emit('selectSWorks')
+                    this.$emit('selectStudent')
+                    this.close2();
+                })
+                .catch((err) => {
+                    this.$message.error("提交失败");
+                    console.error(err);
+                });
+        },
+    },
+    watch: {
+        EnglishVoiceDialog: {
+            handler: function (newVal, oldVal) {
+                if (newVal) {
+                    this.setVoiceJson(this.englishVoiceJson);
+                }
+            },
+            deep: true,
+        },
+    },
+    mounted() {
+        this.setVoiceJson(this.englishVoiceJson);
+    },
+}
+</script>
+
+<style scoped>
+.dialog_diy>>>.el-dialog {
+    height: 100%;
+    margin: 0vh auto !important;
+}
+
+.dialog_diy>>>.el-dialog__header {
+    background: #454545 !important;
+    padding: 15px 20px;
+}
+
+.dialog_diy>>>.el-dialog__body {
+    height: calc(100% - 54px);
+    box-sizing: border-box;
+    padding: 0px;
+}
+
+.dialog_diy>>>.el-dialog__title {
+    color: #fff;
+}
+
+.dialog_diy>>>.el-dialog__headerbtn {
+    top: 19px;
+}
+
+.dialog_diy>>>.el-dialog__headerbtn .el-dialog__close {
+    color: #fff;
+}
+
+.dialog_diy>>>.el-dialog__headerbtn .el-dialog__close:hover {
+    color: #fff;
+}
+
+.dialog_diy>>>.el-dialog__body,
+.dialog_diy>>>.el-dialog__footer {
+    background: #e8ebf1;
+    overflow: hidden;
+}
+
+.ev_box {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    justify-content: space-between;
+    padding: 20px;
+    box-sizing: border-box;
+}
+
+.ev_box_left {
+    width: 280px;
+    margin-right: 15px;
+    border-radius: 10px;
+    background: #fff;
+    overflow: hidden;
+}
+
+.ev_box_right {
+    width: calc(100% - 280px - 15px);
+    border-radius: 10px;
+    background: #fff;
+    overflow: hidden;
+}
+
+.ev_btn {
+    position: absolute;
+    bottom: 80px;
+    right: 80px;
+}</style>

+ 103 - 0
src/components/checkEnglishVoice/component/left.vue

@@ -0,0 +1,103 @@
+<template>
+    <div class="o_box">
+        <div class="o_box_title">
+            <div>标题</div>
+            <div>{{ title }}</div>
+        </div>
+        <div class="o_box_topic">
+            <div class="o_box_child" v-for="(item, index) in checkJson" :key="index" :class="{active:checkType == index}" @click="setType(index)">
+                <span>{{ index + 1  }}.{{ item.content }}</span>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    props: {
+        title: {
+            type: String,
+        },
+        detail: {
+            type: String,
+        }, 
+        checkJson: {
+            type: Array,
+        }, 
+        checkType: {
+            type: Number,
+        },
+    },
+    methods: {
+        setType(index) {
+            this.$emit('setType', index)
+        }
+    },
+
+
+}
+
+</script>
+
+<style scoped>
+.o_box{
+    width: 100%;
+    height: 100%;
+    overflow: auto;
+    padding: 20px 15px;
+    box-sizing:border-box;
+}
+.o_box_title{
+    width:100%;
+    margin-bottom: 25px;
+}
+.o_box_title > div + div{
+    margin-top: 8px;
+}
+
+.o_box_title > div:nth-child(1){
+ color: #00000066;
+ font-size: 14px;
+ letter-spacing: 6px;
+}
+.o_box_title > div:nth-child(2){
+    color:#3981FA;
+    font-size: 24px;
+    font-style: italic;
+}
+
+.o_box_topic{
+    background: #dce9ff;
+    padding: 15px 10px;
+    border-radius: 10px;
+    width: 100%;
+    box-sizing: border-box;
+}
+.o_box_child{
+    height: 42px;
+    display: flex;
+    align-items: center;
+    color: #3681FC;
+    border-radius: 10px;
+    background: #fff;
+    /* border: 2px solid #3681FC; */
+    margin-bottom: 10px;
+    width: 100%;
+    padding: 0 10px;
+    box-sizing: border-box;
+    cursor: pointer;
+}
+.o_box_child.active{
+    background: #3681FC;
+    color: #fff;
+}
+
+.o_box_child > span{
+    width: 100%;
+    display: block;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    word-wrap: break-word;
+}
+</style>

+ 530 - 0
src/components/checkEnglishVoice/component/right.vue

@@ -0,0 +1,530 @@
+<template>
+  <div class="o_box">
+    <div class="o_top"></div>
+    <div class="o_content">
+      <div class="word_box">
+        <div class="word_box2" v-if="cjson.type == 'word' || cjson.type == 'QA'">
+          <div class="word_content">
+            {{ cjson.content }}
+          </div>
+          <img
+            class="word_img"
+            :src="cjson.img"
+            alt=""
+            v-if="cjson.img"
+            @click="previewImg(cjson.img)"
+          />
+        </div>
+        <div class="sentence_box" v-if="cjson.type == 'sentence'">
+          <div class="word_content" v-html="cjson.content">
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="o_bottom" v-loading="isloading">
+      <!-- <div class="audio" v-if="!LuAudioUrl">
+        <img v-if="!isRecord" src="../../../assets/icon/englishVoice/start_aduio.png" alt="" @click="startRecorder">
+        <img v-else src="../../../assets/icon/englishVoice/stop_audio.png" alt="" @click="startRecorder">
+      </div> -->
+      <!-- <div class="audio_word" v-if="!LuAudioUrl">
+        <span v-if="!isRecord">点击话筒开始录音</span>
+        <span v-else>点击话筒结束录音</span>
+      </div> -->
+      <div v-if="LuAudioUrl" class="audio_b">
+        <mini-audio :audio-source="LuAudioUrl" class="audio_class"></mini-audio>
+      </div>
+      <div v-else>
+        未上传录音
+      </div>
+      <!-- <div v-if="LuAudioUrl" class="audio_rerecord" @click="restart()">
+        <span>录音</span>
+      </div> -->
+      <!-- <div class="audio_index" v-if="!isRecord">
+        <div class="audio_index_last" :class="{ disabled: checkType == 0 }" @click="checkIndex('-1')">
+          <img src="../../../assets/icon/englishVoice/coin.png" alt="">
+        </div>
+        <div class="audio_index_content">
+          <span>{{ checkType + 1 }}</span>
+          <span>/</span>
+          <span>{{ checkJson.length }}</span>
+        </div>
+        <div class="audio_index_next" :class="{ disabled: checkType == (checkJson.length - 1) }" @click="checkIndex('1')">
+          <img src="../../../assets/icon/englishVoice/coin.png" alt="">
+        </div>
+      </div>
+      <div v-else class="audio_ing">
+        <span>正在录音...</span>
+      </div> -->
+    </div>
+  </div>
+</template>
+
+<script>
+import Recorder from "js-audio-recorder";
+const lamejs = require("lamejs");
+
+const recorder = new Recorder({
+  sampleBits: 16, // 采样位数,支持 8 或 16,默认是16
+  sampleRate: 48000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000
+  numChannels: 1 // 声道,支持 1 或 2, 默认是1
+  // compiling: false,(0.x版本中生效,1.x增加中) // 是否边录边转换,默认是false
+});
+
+// 绑定事件-打印的是当前录音数据
+recorder.onprogress = function(params) {
+  // console.log('--------------START---------------')
+  // console.log('录音时长(秒)', params.duration);
+  // console.log('录音大小(字节)', params.fileSize);
+  // console.log('录音音量百分比(%)', params.vol);
+  // console.log('当前录音的总数据([DataView, DataView...])', params.data);
+  // console.log('--------------END---------------')
+};
+export default {
+  props: {
+    checkJson: {
+      type: Array
+    },
+    checkType: {
+      type: Number
+    },
+    work: {
+      type: Array
+    }
+  },
+  data() {
+    return {
+      cjson: {},
+      LuAudioUrl: "",
+      isRecord: false,
+      isPlayerRecord: false,
+      isloading: false
+    };
+  },
+  watch: {
+    checkType: {
+      handler: function(newVal, oldVal) {
+        this.cjson = JSON.parse(JSON.stringify(this.checkJson[newVal]));
+        this.LuAudioUrl = this.work[newVal]
+          ? JSON.parse(JSON.stringify(this.work[newVal]))
+          : "";
+      },
+      deep: true
+    }
+  },
+  methods: {
+    previewImg(url) {
+      this.$hevueImgPreview(url);
+    },
+    restart() {
+      this.LuAudioUrl = "";
+      setTimeout(() => {
+        this.startRecorder();
+      }, 500);
+    },
+    checkIndex(type) {
+      if (type == "1") {
+        if (this.checkType == this.checkJson.length - 1) {
+          return;
+        }
+        this.$emit("setType", this.checkType + 1);
+      } else {
+        if (this.checkType == 0) {
+          return;
+        }
+        this.$emit("setType", this.checkType - 1);
+      }
+    },
+    // 开始录音
+    startRecorder() {
+      let _this = this;
+      if (!_this.isRecord) {
+        recorder.destroy(); // 销毁录音
+        _this.isRecord = true;
+        recorder.start().then(
+          () => {},
+          error => {
+            _this.isRecord = false;
+            // _this.$message.error(`${error.name} : ${error.message}`);
+            _this.$message.error(
+              `没有找到可使用的麦克风,或者您没有允许此网页使用麦克风`
+            );
+            // 出错了
+            console.log(`${error.name} : ${error.message}`);
+          }
+        );
+      } else {
+        _this.isRecord = false;
+        recorder.stop(); // 结束录音
+        this.getMp3Data();
+      }
+    },
+
+    // 录音播放
+    playRecorder() {
+      if (!recorder.fileSize) {
+        return;
+      }
+      if (!this.isPlayerRecord) {
+        this.isPlayerRecord = true;
+        recorder.play();
+      } else {
+        this.isPlayerRecord = false;
+        recorder.stopPlay(); // 停止录音播放
+      }
+      recorder.onplayend = () => {
+        this.isPlayerRecord = false;
+        console.log("onplayend");
+      };
+    },
+
+    /**
+     * 文件格式转换 wav-map3
+     * */
+    getMp3Data() {
+      if (!recorder.fileSize) {
+        this.$message.error("请录音后在上传语音");
+        return;
+      }
+      const mp3Blob = this.convertToMp3(recorder.getWAV());
+      let audioFile = this.dataURLtoAudio(mp3Blob, "mp3");
+      console.log(audioFile);
+      this.beforeUpload1(audioFile, 3);
+      // recorder.download(mp3Blob, "recorder", "mp3");
+    },
+    convertToMp3(wavDataView) {
+      // 获取wav头信息
+      const wav = lamejs.WavHeader.readHeader(wavDataView); // 此处其实可以不用去读wav头信息,毕竟有对应的config配置
+      const { channels, sampleRate } = wav;
+      const mp3enc = new lamejs.Mp3Encoder(channels, sampleRate, 128);
+      // 获取左右通道数据
+      const result = recorder.getChannelData();
+      const buffer = [];
+      const leftData =
+        result.left &&
+        new Int16Array(result.left.buffer, 0, result.left.byteLength / 2);
+      const rightData =
+        result.right &&
+        new Int16Array(result.right.buffer, 0, result.right.byteLength / 2);
+      const remaining = leftData.length + (rightData ? rightData.length : 0);
+      const maxSamples = 1152;
+      for (let i = 0; i < remaining; i += maxSamples) {
+        const left = leftData.subarray(i, i + maxSamples);
+        let right = null;
+        let mp3buf = null;
+        if (channels === 2) {
+          right = rightData.subarray(i, i + maxSamples);
+          mp3buf = mp3enc.encodeBuffer(left, right);
+        } else {
+          mp3buf = mp3enc.encodeBuffer(left);
+        }
+        if (mp3buf.length > 0) {
+          buffer.push(mp3buf);
+        }
+      }
+
+      const enc = mp3enc.flush();
+      if (enc.length > 0) {
+        buffer.push(enc);
+      }
+      return new Blob(buffer, { type: "audio/mp3" });
+    },
+    dataURLtoAudio(blob, filename) {
+      return new File([blob], filename, { type: "audio/mp3" });
+    },
+    beforeUpload1(event, type) {
+      this.isloading = true;
+      var file;
+      if (type == 3) {
+        file = event;
+      } else {
+        file = event.target.files[0];
+      }
+      var credentials = {
+        accessKeyId: "AKIATLPEDU37QV5CHLMH",
+        secretAccessKey: "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"
+      }; //秘钥形式的登录上传
+      window.AWS.config.update(credentials);
+      window.AWS.config.region = "cn-northwest-1"; //设置区域
+
+      var bucket = new window.AWS.S3({ params: { Bucket: "ccrb" } }); //选择桶
+      var _this = this;
+
+      if (file) {
+        var params = {
+          Key:
+            file.name.split(".")[0] +
+            new Date().getTime() +
+            "." +
+            file.name.split(".")[file.name.split(".").length - 1],
+          ContentType: file.type,
+          Body: file,
+          "Access-Control-Allow-Credentials": "*",
+          ACL: "public-read"
+        }; //key可以设置为桶的相抵路径,Body为文件, ACL最好要设置
+        var options = {
+          partSize: 2048 * 1024 * 1024,
+          queueSize: 2,
+          leavePartsOnError: true
+        };
+        bucket
+          .upload(params, options)
+          .on("httpUploadProgress", function(evt) {
+            //这里可以写进度条
+            // console.log("Uploaded : " + parseInt((evt.loaded * 80) / evt.total) + '%');
+            // _this.progress = parseInt((evt.loaded * 80) / evt.total);
+          })
+          .send(function(err, data) {
+            _this.isloading = false;
+            // _this.progress = 100;
+            if (err) {
+              var a = _this.$refs.upload1.uploadFiles;
+              a.splice(a.length - 1, a.length);
+              _this.$message.error("上传失败");
+            } else {
+              if (type == 3) {
+                _this.LuAudioUrl = data.Location;
+                _this.$emit("setWork", _this.LuAudioUrl, _this.checkType);
+              }
+              console.log(data.Location);
+            }
+          });
+      }
+    }
+  },
+  mounted() {
+    this.cjson = JSON.parse(JSON.stringify(this.checkJson[this.checkType]));
+    this.LuAudioUrl = this.work[this.checkType]
+      ? JSON.parse(JSON.stringify(this.work[this.checkType]))
+      : "";
+  }
+};
+</script>
+
+<style scoped>
+.o_box {
+  width: 100%;
+  height: 100%;
+  /* background-image: url('../../../assets/icon/env_background.png'); */
+  background-size: cover;
+}
+
+.o_top {
+  height: 65px;
+  position: absolute;
+}
+
+.o_content {
+  height: calc(100% - 80px);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  overflow: hidden;
+}
+
+.o_bottom {
+  height: 80px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.word_box {
+  width: calc(100% - 20px);
+  background: #f2f2f2;
+  border-radius: 10px;
+  position: relative;
+  height: 90%;
+  overflow: auto;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+.word_box2 {
+  max-height:  calc(100% - 20px);
+  max-width:  calc(100% - 20px);
+  min-width: 350px;
+  display: flex;
+  width: auto;
+  background: #fff;
+  padding: 10px;
+  border-radius: 5px;
+  box-sizing: border-box;
+  overflow: auto;
+  color: #000;
+}
+.sentence_box {
+  font-size: 16px;
+  color: #000;
+  line-height: 20px;
+  word-break: break-word;
+  white-space: pre-line;
+  max-height:  calc(100% - 20px);
+  max-width:  calc(100% - 20px);
+  min-width: 350px;
+  display: flex;
+  width: auto;
+  background: #fff;
+  padding: 10px;
+  border-radius: 5px;
+  box-sizing: border-box;
+  overflow: auto;
+}
+.sentence_box > .word_content {
+  position: relative;
+    z-index: 999;
+    /* text-align: center; */
+    color: #000;
+    width: calc(100%);
+}
+
+.word_box2 > .word_img {
+  min-width: 100px;
+  width: 100px;
+  height: 50px;
+  z-index: 999;
+  position: relative;
+  margin: 0 0 0 10px;
+  display: block;
+  border-radius: 10px;
+  cursor: pointer;
+  object-fit: cover;
+}
+
+.word_box2 > .word_content {
+  position: relative;
+    z-index: 999;
+    text-align: center;
+    font-size: 30px;
+    margin: 7px 0 7px 0px;
+    color: #000;
+    width: calc(100%);
+}
+
+.o_bottom .audio {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.o_bottom .audio > img {
+  width: 75px;
+  height: 75px;
+  cursor: pointer;
+}
+
+.o_bottom .audio_word {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: #00000099;
+  font-size: 16px;
+  margin: 10px 0 8px;
+}
+
+.o_bottom .audio_index {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.audio_index_last,
+.audio_index_next {
+  height: 40px;
+  width: 40px;
+  background: #3681fc;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+}
+
+.audio_index_last > img,
+.audio_index_next > img {
+  width: 15px;
+  height: auto;
+}
+
+.audio_index_last.disabled,
+.audio_index_next.disabled {
+  opacity: 0.6;
+}
+
+.audio_index_last > img {
+  transform: rotate(180deg);
+}
+
+.audio_index_last {
+  margin-right: 20px;
+}
+
+.audio_index_content {
+  color: #000;
+  font-size: 16px;
+}
+
+.audio_index_next {
+  margin-left: 20px;
+}
+
+.audio_ing {
+  color: #ee3e3e;
+  margin-top: 25px;
+  font-size: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.audio_b {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 15px;
+}
+
+.audio_rerecord {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-bottom: 15px;
+}
+
+.audio_rerecord > span {
+  display: flex;
+  border: 1px solid #3981fa;
+  align-items: center;
+  color: #3981fa;
+  padding: 5px 10px;
+  border-radius: 5px;
+  cursor: pointer;
+}
+
+.audio_rerecord > span::before {
+  content: "";
+  width: 15px;
+  height: 15px;
+  background: url("../../../assets/icon/englishVoice/restart.png");
+  display: block;
+  background-size: 100% 100%;
+  margin-right: 5px;
+}
+
+.audio_class {
+  background: #3680fb !important;
+  margin: 0 !important;
+}
+
+.audio_b >>> .vueAudioBetter span:before{
+  color: #fff;
+}
+
+.audio_class >>> .slider .process {
+  background: #000;
+}
+
+.audio_b >>> .vueAudioBetter .iconfont:active{
+  position: unset;
+}
+</style>

+ 335 - 0
src/components/checkEnglishVoice/index.vue

@@ -0,0 +1,335 @@
+<template>
+  <el-dialog
+    title="查看"
+    :visible.sync="dialogVisibleENScore"
+    :append-to-body="true"
+    width="800px"
+    :before-close="handleClose"
+    class="dialog_diy"
+  >
+    <div>
+      <div class="studentDetail">
+        <div class="tx"><img src="../../assets/avatar.png" alt="" /></div>
+        <div class="nameAndTime">
+          <div style="margin-bottom: 5px">{{ commentDetail.sName }}</div>
+          <div>{{ commentDetail.time }}</div>
+        </div>
+      </div>
+      <div class="en_box">
+        <div class="ev_box_left">
+          <left
+            :title="title"
+            :detail="detail"
+            :checkJson="checkJson"
+            :checkType="checkType"
+            @setType="setType"
+          >
+          </left>
+        </div>
+        <div class="ev_box_right">
+          <right
+            :checkJson="checkJson"
+            :checkType="checkType"
+            @setType="setType"
+            :work="work"
+            @setWork="setWork"
+          >
+          </right>
+        </div>
+      </div>
+      <div class="scoreBox">
+        <span class="t">请输入分数</span>
+        <el-input-number
+          :disabled="courseDetail.userid != userid"
+          v-model="wScore2"
+          :controls="false"
+          :min="0"
+          :max="100"
+        ></el-input-number>
+      </div>
+      <div class="scoreDetailBox">
+        <span class="t">评分评论</span>
+        <el-input
+          type="textarea"
+          :rows="5"
+          :disabled="courseDetail.userid != userid"
+          resize="none"
+          v-model="scoreDetail2"
+          placeholder="请输入对学生的评价"
+        >
+        </el-input>
+      </div>
+    </div>
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="close">取 消</el-button>
+      <el-button
+        type="primary"
+        v-if="courseDetail.userid == userid"
+        @click="scoreWork(commentDetail.wid)"
+        >确 定
+      </el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+import right from "./component/right.vue";
+import left from "./component/left.vue";
+export default {
+  components: {
+    right,
+    left
+  },
+  props: {
+    dialogVisibleENScore: {
+      type: Boolean,
+      default: false
+    },
+    englishVoiceJson: {
+      type: Object
+    },
+    userid: {
+      type: String
+    },
+    commentDetail: {
+      type: Object
+    },
+    courseDetail: {
+      type: Object
+    },
+    wScore: {
+      type: Number
+    },
+    scoreDetail: {
+      type: String
+    }
+  },
+  data() {
+    return {
+      checkJson: [],
+      checkJson2: {},
+      title: "",
+      detail: "",
+      checkType: 0,
+      work: [],
+      wScore2: 0,
+      scoreDetail2: ""
+    };
+  },
+  methods: {
+    handleClose(done) {
+      this.close();
+      done();
+    },
+    close() {
+      this.$emit("update:dialogVisibleENScore", false);
+    },
+    close2() {
+      this.$emit("update:dialogVisibleENScore", false);
+    },
+    confirm() {
+      this.close2();
+    },
+    setJson(val) {
+      this.checkJson = val;
+      this.$forceUpdate();
+    },
+    setVoiceJson(val) {
+      let a = JSON.parse(JSON.stringify(val));
+      this.checkJson2 = JSON.parse(JSON.stringify(a));
+      this.title = a.title;
+      this.detail = a.detail;
+      this.checkJson = a.array;
+      this.checkType = 0;
+      this.work.length = a.array.length;
+      this.wScore2 = JSON.parse(JSON.stringify(this.wScore));
+      this.scoreDetail2 = JSON.parse(JSON.stringify(this.scoreDetail2));
+      let works = JSON.parse(
+        JSON.parse(JSON.stringify(this.commentDetail.works))
+      );
+      for (var i = 0; i < this.work.length; i++) {
+        if(i > (works.length - 1)){
+            break
+        }
+        this.work[i] = works[i];
+      }
+    },
+    setType(index) {
+      this.checkType = index;
+    },
+    setWork(url, index) {
+      this.work[index] = url;
+    },
+    scoreWork(wid) {
+      if (this.wScore2 == 0) {
+        this.$message.error("请评分");
+        return;
+      }
+      let params = [
+        {
+          wid: wid,
+          score: JSON.stringify({
+            wScore: this.wScore2,
+            detail: this.scoreDetail2
+          })
+        }
+      ];
+      this.ajax
+        .post(this.$store.state.api + "scoreWork", params)
+        .then(res => {
+          this.$message({
+            message: "评分成功",
+            type: "success"
+          });
+          this.wScore2 = 0;
+          this.scoreDetail2 = "";
+          this.$emit("selectSWorks");
+          this.$emit("selectStudent");
+          this.close2();
+        })
+        .catch(err => {
+          this.$message.error("评分失败");
+          console.error(err);
+        });
+    }
+  },
+  watch: {
+    dialogVisibleENScore: {
+      handler: function(newVal, oldVal) {
+        if (newVal) {
+          this.setVoiceJson(this.englishVoiceJson);
+        }
+      },
+      deep: true
+    }
+  },
+  mounted() {
+    this.setVoiceJson(this.englishVoiceJson);
+  }
+};
+</script>
+
+<style scoped>
+.dialog_diy >>> .el-dialog {
+  /* height: 100%; */
+  /* margin: 15vh auto !important; */
+}
+
+.dialog_diy >>> .el-dialog__header {
+  background: #454545 !important;
+  padding: 15px 20px;
+}
+
+.dialog_diy >>> .el-dialog__body {
+  /* height: calc(100% - 54px); */
+  box-sizing: border-box;
+  /* padding: 0px; */
+}
+
+.dialog_diy >>> .el-dialog__title {
+  color: #fff;
+}
+
+.dialog_diy >>> .el-dialog__headerbtn {
+  top: 19px;
+}
+
+.dialog_diy >>> .el-dialog__headerbtn .el-dialog__close {
+  color: #fff;
+}
+
+.dialog_diy >>> .el-dialog__headerbtn .el-dialog__close:hover {
+  color: #fff;
+}
+
+.dialog_diy >>> .el-dialog__body,
+.dialog_diy >>> .el-dialog__footer {
+  /* background: #e8ebf1; */
+  overflow: hidden;
+}
+
+.studentDetail {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: nowrap;
+  /* align-items: center; */
+  align-items: flex-start;
+}
+
+.tx {
+  width: 50px;
+}
+
+.tx > img {
+  width: 100%;
+  height: 100%;
+}
+
+.nameAndTime {
+  display: flex;
+  flex-direction: column;
+  flex-wrap: nowrap;
+  align-items: flex-start;
+  margin-left: 10px;
+}
+
+.scoreBox,
+.scoreDetailBox {
+  display: flex;
+  align-items: center;
+  justify-content: flex-start;
+  margin-top: 20px;
+  font-size: 18px;
+  width: 100%;
+}
+
+.scoreBox .t,
+.scoreDetailBox .t {
+  margin-right: 10px;
+  width: 100px;
+  text-align: right;
+}
+
+.scoreDetailBox {
+  align-items: flex-start;
+}
+
+.scoreDetailBox >>> .el-textarea {
+  width: calc(100% - 200px);
+}
+
+.scoreBox >>> .el-input {
+  width: 130px;
+  font-size: 38px;
+}
+
+.scoreBox >>> .el-input__inner {
+  height: 60px;
+}
+
+.en_box {
+  width: 100%;
+  background: #fff;
+  border-radius: 5px;
+  border: 1px solid #cccccc;
+  height: 500px;
+  display: flex;
+  box-sizing: border-box;
+  margin: 20px 0px 0 0;
+}
+
+.ev_box_left {
+  width: 200px;
+  border-right: 1px solid #cccccc;
+  box-sizing: border-box;
+  height: 100%;
+  overflow: auto;
+}
+.ev_box_right {
+  width: calc(100% - 200px);
+  border-right: 1px solid #cccccc;
+  box-sizing: border-box;
+  height: 100%;
+  overflow: auto;
+}
+</style>

+ 8 - 1
src/components/courseDetail.vue

@@ -692,11 +692,18 @@
                               </div>
                               <div v-if="item3.tool == 69">
                                 <img
-                                  src="../assets/icon/secondToolList/timeAxis.png"
+                                  src="../assets/icon/secondToolList/english.png"
                                   alt
                                 />
                                 <div>英语写作</div>
                               </div>
+                              <div v-if="item3.tool == 70">
+                                <img
+                                  src="../assets/icon/thirdToolList/englishVoice.png"
+                                  alt
+                                />
+                                <div>英语口语</div>
+                              </div>
                               <div v-if="item3.tool == undefined">
                                 <img
                                   src="../assets/icon/empytool.png"

+ 236 - 6
src/components/easy2/studyStudent.vue

@@ -212,6 +212,7 @@
                       <div v-if="t.tool == 67">分子结构</div>
                       <div v-if="t.tool == 68">时间轴</div>
                       <div v-if="t.tool == 69">英语写作</div>
+                      <div v-if="t.tool == 70">英语口语</div>
                       <div v-if="t.tool == 25">目标管理</div>
                       <div v-if="t.tool == 26">课程设计</div>
                       <div v-if="t.tool == 62">交互视频</div>
@@ -1308,6 +1309,14 @@
                             />
                             <div style="margin: 5px 0">英语写作</div>
                           </div>
+                          <div v-if="tooC == 70">
+                            <img
+                              @click="addTools(tooC, toolIndex, taskCount)"
+                              src="../../assets/icon/thirdToolList/englishVoice.png"
+                              alt
+                            />
+                            <div style="margin: 5px 0">英语口语</div>
+                          </div>
                         </div>
                       </div>
                     </div>
@@ -2850,6 +2859,155 @@
                       </div>
                     </div>
                   </div>
+                  <div
+                    v-if="
+                      tType &&
+                      ((tType == 2 && sIsOpen == true) ||
+                        tType == 1 ||
+                        tType == 4) &&
+                      tool.tool.indexOf(70) != -1
+                    "
+                    class="worksBox"
+                  >
+                    <div
+                      class="zuoyeYulan"
+                      v-if="
+                        (worksStudent.length &&
+                        worksStudent[toolIndex].length > 0)
+                      "
+                    >
+                      <div class="worksTop">
+                        <div>作业预览</div>
+                        <div
+                          class="corOpen"
+                          @click="contract(toolIndex)"
+                          v-if="
+                            (isCloseList[toolIndex].isCloseBoolean) &&
+                            isCloseList[toolIndex].isClose == 0
+                          "
+                        >
+                          折叠
+                        </div>
+                        <div
+                          class="corOpen"
+                          @click="contract(toolIndex)"
+                          v-if="isCloseList[toolIndex].isClose == 1"
+                        >
+                          展开
+                        </div>
+                      </div>
+                      <!-- <el-button type="text" @click="jump()" v-if="tType == 2" class="buttonA">我的评价</el-button> -->
+                    </div>
+                    <div
+                      :id="'tool-' + toolIndex"
+                      :style="{
+                        height:isCloseList[toolIndex].isClose == 1 ? retrnToolHeight('tool-' + toolIndex) : 'auto',
+                        overflow: isCloseList[toolIndex].isClose == 1 ? 'hidden' : 'unset'
+                      }"
+                      class="worksDetailBox"
+                      v-if="
+                        worksStudent.length &&
+                        worksStudent[toolIndex].length > 0
+                      "
+                    >
+                      <div
+                        class="works"
+                        style="
+                          width: 200px;
+                          height: 140px;
+                          margin: 10px 10px 10px 0;
+                          border-radius: 15px;
+                          box-shadow: 0 0 6px 1px #dfdada;
+                        "
+                        v-for="(w, wIndex) in worksStudent[toolIndex]"
+                        :key="wIndex"
+                        :class="w.type == 1 ? 'isTypeOne' : ''"
+                      >
+                        <div class="workImg" @click.stop="openScore(w)">
+                          <img :src="word" alt />
+                          <img
+                            class="deleteImg"
+                            src="../../assets/deleteworks.png"
+                            v-if="
+                              w.userid == userid || tType == 1 || tType == 4
+                            "
+                            @click.stop="deleteWorks(w.wid)"
+                            alt
+                          />
+                        </div>
+
+                        <div class="comment" style="min-width: 200px">
+                          <div class="worksName">
+                            <div>{{ w.sName }}</div>
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                    <div style="font-size: 18px" v-if="courseDetail.juri != ''">
+                      未提交
+                    </div>
+                    <div
+                      class="noWorksS"
+                      v-if="noWorksS && noWorksS[toolIndex].length"
+                    >
+                      <div
+                        v-for="(s, sIndex) in noWorksS[toolIndex]"
+                        :key="sIndex"
+                        class="noWorksName"
+                      >
+                        {{ s.student }}
+                      </div>
+                    </div>
+                  </div>
+                  <div
+                    v-if="
+                      tType &&
+                      tType == 2 &&
+                      !sIsOpen &&
+                      tool.tool.indexOf(70) != -1
+                    "
+                    class="worksBox"
+                  >
+                    <div
+                      class="zuoyeYulan"
+                      v-if="workStudent[toolIndex].length > 0"
+                    >
+                      <span class="worksTitle">作业预览</span>
+                      <!-- <el-button type="text" @click="jump()" v-if="tType == 2" class="buttonA">我的评价</el-button> -->
+                    </div>
+                    <div
+                      class="worksDetailBox"
+                      v-if="workStudent[toolIndex].length > 0"
+                    >
+                      <div
+                        class="works"
+                        style="
+                          width: 200px;
+                          height: 140px;
+                          margin: 10px 10px 10px 0;
+                          box-shadow: 0 0 6px 1px #dfdada;
+                        "
+                        v-for="(w, wIndex) in workStudent[toolIndex]"
+                        :key="wIndex"
+                      >
+                        <div class="workImg" @click.stop="openScore(w)">
+                          <img :src="word" alt />
+                          <img
+                            class="deleteImg"
+                            src="../../assets/deleteworks.png"
+                            v-if="
+                              w.userid == userid || tType == 1 || tType == 4
+                            "
+                            @click.stop="deleteWorks(w.wid)"
+                            alt
+                          />
+                        </div>
+                        <div class="worksName">
+                          <div>{{ w.sName }}</div>
+                        </div>
+                      </div>
+                    </div>
+                  </div>
                   <div
                     v-if="
                       tType &&
@@ -11499,6 +11657,25 @@
         </div>
       </div>
     </el-dialog>
+    <EnglishVoice :EnglishVoiceDialog.sync='EnglishVoiceDialog' 
+    :englishVoiceJson='englishVoiceJson' 
+    :userid="userid" 
+    :id="id" 
+    :courseType="courseType" 
+    :taskCount="taskCount" 
+    :toolindex="toolindex" 
+    @selectSWorks="selectSWorks" 
+    @selectStudent="selectStudent"
+    :englishVoiceJsonWork="englishVoiceJsonWork"></EnglishVoice>
+    <checkEnglishVoice :englishVoiceJson='englishVoiceJson'  
+    :userid="userid" 
+    :dialogVisibleENScore.sync='dialogVisibleENScore'
+    :commentDetail="commentDetail" 
+    :courseDetail="courseDetail" 
+    :wScore="wScore"
+    :scoreDetail="scoreDetail"
+    @selectSWorks="selectSWorks" 
+    @selectStudent="selectStudent"></checkEnglishVoice>
   </div>
 </template>
 
@@ -11530,6 +11707,8 @@ import FileSaver from "file-saver";
 import onlineWrite from "./commpont/onlineWrite";
 import englishEva from "../components/englishEva";
 import * as Diff from 'diff'
+import EnglishVoice from '../EnglishVoice2/index.vue'
+import checkEnglishVoice from '../checkEnglishVoice/index.vue'
 
 const getFile = (url) => {
   return new Promise((resolve, reject) => {
@@ -11600,6 +11779,8 @@ export default {
     wordCloud,
     onlineWrite,
     englishEva,
+    EnglishVoice,
+    checkEnglishVoice
   },
   data() {
     return {
@@ -11617,6 +11798,7 @@ export default {
       dialogVisibleSelect: false,
       dialogVisibleSelectTeacher: false,
       dialogVisibleScore: false,
+      dialogVisibleENScore: false,
       dialogVisibleSentence: false,
       dialogVisibleSentenceTeacher: false,
       dialogVisibleSentence1: false,
@@ -11954,6 +12136,9 @@ export default {
       downLoading: false,
       greyType: false,
       correctWord: [],
+      EnglishVoiceDialog: false,
+      englishVoiceJson: {},
+      englishVoiceJsonWork:[]
     };
   },
   methods: {
@@ -13154,7 +13339,7 @@ export default {
           if (
             Object.keys(this.commentDetail).length &&
             Object.keys(this.commentIndexJson).length &&
-            !this.dialogVisibleScore
+            !this.dialogVisibleScore && !this.dialogVisibleENScore
           ) {
             let a = 1;
             let c = this.commentIndexJson;
@@ -13531,6 +13716,18 @@ export default {
                     aiCode: b[j].aiCode,
                     teacherCode: b[j].teacherCode
                   });
+                } else if (b[j].type == 17 && a[i].tool[0] == 70) {
+                  //英语写作
+                  this.workStudent[i].push({
+                    works: b[j].content,
+                    sName: b[j].name,
+                    score: b[j].score,
+                    img: b[j].img,
+                    type: 17,
+                    time: b[j].time,
+                    userid: b[j].userid,
+                    wid: b[j].id,
+                  });
                 }
               }
             }
@@ -13625,7 +13822,11 @@ export default {
         gid: gid,
       };
       this.commentDetail = [];
-      this.commentDialogVisible = true;
+      if(w.type == 17){
+        this.dialogVisibleENScore = true;
+      }else{
+        this.dialogVisibleScore = true;
+      }
       this.commentDetail = w;
       if (w.works && w.type == 1) {
         this.pptImgUrl = "";
@@ -13666,6 +13867,15 @@ export default {
           this.chapInfoList[this.courseType].chapterInfo[0].taskJson[
             this.taskCount
           ].toolChoose[toolIndex].wordJson;
+      }else if(w.type == 17){
+        let englishVoiceJson = {}
+        englishVoiceJson = this.chapInfoList[this.courseType].chapterInfo[0]
+          .taskJson[this.taskCount].toolChoose[index].englishVoiceJson
+          ? this.chapInfoList[this.courseType].chapterInfo[0].taskJson[this.taskCount]
+              .toolChoose[index].englishVoiceJson
+          : {};
+        
+        this.englishVoiceJson = JSON.parse(JSON.stringify(englishVoiceJson));
       }
     },
     openScore(w) {
@@ -14602,7 +14812,7 @@ export default {
                   _worksStudent[i].push(_work);
                   _worksStudent2[i].push(_work);
                   this.isWorksS[i].push({ uid: b[j].userid, sName: b[j].name });
-                } else if (b[j].type == 16 && a[i].tool[0] == 69) {
+                } else if (b[j].type == 17 && a[i].tool[0] == 70) {
                   //选择题
                   let _work = {
                     userid: b[j].userid,
@@ -14610,7 +14820,7 @@ export default {
                     wid: b[j].id,
                     works: b[j].content,
                     sName: b[j].name,
-                    type: 16,
+                    type: 17,
                     time: b[j].time,
                     score: b[j].score,
                     img: b[j].img,
@@ -14618,8 +14828,6 @@ export default {
                     commentCount: commentCount,
                     isLikes: isLikes,
                     commentJson: commentJson,
-                    aiCode: b[j].aiCode,
-                    teacherCode: b[j].teacherCode
                   };
                   _worksStudent[i].push(_work);
                   _worksStudent2[i].push(_work);
@@ -17618,6 +17826,28 @@ export default {
         this.englishToolIndex = i;
         this.myAnList = this.myAnswerList1;
         this.engDialogVisible = true;
+      } else if (t == 70) {
+        if (this.worksStudent[i].length) {
+          for (var k = 0; k < this.worksStudent[i].length; k++) {
+            if (this.userid == this.worksStudent[i][k].userid && this.worksStudent[i][k].type == 17) {
+              this.englishVoiceJsonWork = JSON.parse(this.worksStudent[i][k].works)
+              break;
+            } else {
+              this.englishVoiceJsonWork = [];
+            }
+          }
+        } else {
+          this.englishVoiceJsonWork = [];
+        }
+        let englishVoiceJson = {}
+        englishVoiceJson = this.chapInfoList[this.courseType].chapterInfo[0]
+          .taskJson[index].toolChoose[i].englishVoiceJson
+          ? this.chapInfoList[this.courseType].chapterInfo[0].taskJson[index]
+              .toolChoose[i].englishVoiceJson
+          : {};
+        
+        this.englishVoiceJson = JSON.parse(JSON.stringify(englishVoiceJson));
+        this.EnglishVoiceDialog = true;
       } else if (t == 65) {
         if (this.tType == 2) {
           this.$message.error("不支持学生使用");

+ 254 - 2
src/components/easy3/studyStudent.vue

@@ -212,6 +212,7 @@
                       <div v-if="t.tool == 67">分子结构</div>
                       <div v-if="t.tool == 68">时间轴</div>
                       <div v-if="t.tool == 69">英语写作</div>
+                      <div v-if="t.tool == 69">英语口语</div>
                       <div v-if="t.tool == 25">目标管理</div>
                       <div v-if="t.tool == 26">课程设计</div>
                       <div v-if="t.tool == 62">交互视频</div>
@@ -1295,6 +1296,14 @@
                             />
                             <div style="margin: 5px 0">英语写作</div>
                           </div>
+                          <div v-if="tooC == 70">
+                            <img
+                              @click="addTools(tooC, toolIndex, taskCount)"
+                              src="../../assets/icon/thirdToolList/englishVoice.png"
+                              alt
+                            />
+                            <div style="margin: 5px 0">英语口语</div>
+                          </div>
                         </div>
                       </div>
                     </div>
@@ -2837,6 +2846,155 @@
                       </div>
                     </div>
                   </div>
+                  <div
+                    v-if="
+                      tType &&
+                      ((tType == 2 && sIsOpen == true) ||
+                        tType == 1 ||
+                        tType == 4) &&
+                      tool.tool.indexOf(70) != -1
+                    "
+                    class="worksBox"
+                  >
+                    <div
+                      class="zuoyeYulan"
+                      v-if="
+                        (worksStudent.length &&
+                        worksStudent[toolIndex].length > 0)
+                      "
+                    >
+                      <div class="worksTop">
+                        <div>作业预览</div>
+                        <div
+                          class="corOpen"
+                          @click="contract(toolIndex)"
+                          v-if="
+                            (isCloseList[toolIndex].isCloseBoolean) &&
+                            isCloseList[toolIndex].isClose == 0
+                          "
+                        >
+                          折叠
+                        </div>
+                        <div
+                          class="corOpen"
+                          @click="contract(toolIndex)"
+                          v-if="isCloseList[toolIndex].isClose == 1"
+                        >
+                          展开
+                        </div>
+                      </div>
+                      <!-- <el-button type="text" @click="jump()" v-if="tType == 2" class="buttonA">我的评价</el-button> -->
+                    </div>
+                    <div
+                      :id="'tool-' + toolIndex"
+                      :style="{
+                        height:isCloseList[toolIndex].isClose == 1 ? retrnToolHeight('tool-' + toolIndex) : 'auto',
+                        overflow: isCloseList[toolIndex].isClose == 1 ? 'hidden' : 'unset'
+                      }"
+                      class="worksDetailBox"
+                      v-if="
+                        worksStudent.length &&
+                        worksStudent[toolIndex].length > 0
+                      "
+                    >
+                      <div
+                        class="works"
+                        style="
+                          width: 200px;
+                          height: 140px;
+                          margin: 10px 10px 10px 0;
+                          border-radius: 15px;
+                          box-shadow: 0 0 6px 1px #dfdada;
+                        "
+                        v-for="(w, wIndex) in worksStudent[toolIndex]"
+                        :key="wIndex"
+                        :class="w.type == 1 ? 'isTypeOne' : ''"
+                      >
+                        <div class="workImg" @click.stop="openScore(w)">
+                          <img :src="word" alt />
+                          <img
+                            class="deleteImg"
+                            src="../../assets/deleteworks.png"
+                            v-if="
+                              w.userid == userid || tType == 1 || tType == 4
+                            "
+                            @click.stop="deleteWorks(w.wid)"
+                            alt
+                          />
+                        </div>
+
+                        <div class="comment" style="min-width: 200px">
+                          <div class="worksName">
+                            <div>{{ w.sName }}</div>
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                    <div style="font-size: 18px" v-if="courseDetail.juri != ''">
+                      未提交
+                    </div>
+                    <div
+                      class="noWorksS"
+                      v-if="noWorksS && noWorksS[toolIndex].length"
+                    >
+                      <div
+                        v-for="(s, sIndex) in noWorksS[toolIndex]"
+                        :key="sIndex"
+                        class="noWorksName"
+                      >
+                        {{ s.student }}
+                      </div>
+                    </div>
+                  </div>
+                  <div
+                    v-if="
+                      tType &&
+                      tType == 2 &&
+                      !sIsOpen &&
+                      tool.tool.indexOf(70) != -1
+                    "
+                    class="worksBox"
+                  >
+                    <div
+                      class="zuoyeYulan"
+                      v-if="workStudent[toolIndex].length > 0"
+                    >
+                      <span class="worksTitle">作业预览</span>
+                      <!-- <el-button type="text" @click="jump()" v-if="tType == 2" class="buttonA">我的评价</el-button> -->
+                    </div>
+                    <div
+                      class="worksDetailBox"
+                      v-if="workStudent[toolIndex].length > 0"
+                    >
+                      <div
+                        class="works"
+                        style="
+                          width: 200px;
+                          height: 140px;
+                          margin: 10px 10px 10px 0;
+                          box-shadow: 0 0 6px 1px #dfdada;
+                        "
+                        v-for="(w, wIndex) in workStudent[toolIndex]"
+                        :key="wIndex"
+                      >
+                        <div class="workImg" @click.stop="openScore(w)">
+                          <img :src="word" alt />
+                          <img
+                            class="deleteImg"
+                            src="../../assets/deleteworks.png"
+                            v-if="
+                              w.userid == userid || tType == 1 || tType == 4
+                            "
+                            @click.stop="deleteWorks(w.wid)"
+                            alt
+                          />
+                        </div>
+                        <div class="worksName">
+                          <div>{{ w.sName }}</div>
+                        </div>
+                      </div>
+                    </div>
+                  </div>
                   <div
                     v-if="
                       tType &&
@@ -11487,6 +11645,25 @@
         </div>
       </div>
     </el-dialog>
+    <EnglishVoice :EnglishVoiceDialog.sync='EnglishVoiceDialog' 
+    :englishVoiceJson='englishVoiceJson' 
+    :userid="userid" 
+    :id="id" 
+    :courseType="courseType" 
+    :taskCount="taskCount" 
+    :toolindex="toolindex" 
+    @selectSWorks="selectSWorks" 
+    @selectStudent="selectStudent"
+    :englishVoiceJsonWork="englishVoiceJsonWork"></EnglishVoice>
+    <checkEnglishVoice :englishVoiceJson='englishVoiceJson'  
+    :userid="userid" 
+    :dialogVisibleENScore.sync='dialogVisibleENScore'
+    :commentDetail="commentDetail" 
+    :courseDetail="courseDetail" 
+    :wScore="wScore"
+    :scoreDetail="scoreDetail"
+    @selectSWorks="selectSWorks" 
+    @selectStudent="selectStudent"></checkEnglishVoice>
   </div>
 </template>
 
@@ -11518,6 +11695,8 @@ import FileSaver from "file-saver";
 import onlineWrite from "./commpont/onlineWrite";
 import englishEva from "../components/englishEva";
 import * as Diff from 'diff'
+import EnglishVoice from '../EnglishVoice2/index.vue'
+import checkEnglishVoice from '../checkEnglishVoice/index.vue'
 
 const getFile = (url) => {
   return new Promise((resolve, reject) => {
@@ -11588,6 +11767,8 @@ export default {
     wordCloud,
     onlineWrite,
     englishEva,
+    EnglishVoice,
+    checkEnglishVoice
   },
   data() {
     return {
@@ -11605,6 +11786,7 @@ export default {
       dialogVisibleSelect: false,
       dialogVisibleSelectTeacher: false,
       dialogVisibleScore: false,
+      dialogVisibleENScore: false,
       dialogVisibleSentence: false,
       dialogVisibleSentenceTeacher: false,
       dialogVisibleSentence1: false,
@@ -11943,6 +12125,9 @@ export default {
       downLoading: false,
       greyType: false,
       correctWord: [],
+      EnglishVoiceDialog: false,
+      englishVoiceJson: {},
+      englishVoiceJsonWork:[]
     };
   },
   methods: {
@@ -13144,7 +13329,7 @@ export default {
           if (
             Object.keys(this.commentDetail).length &&
             Object.keys(this.commentIndexJson).length &&
-            !this.dialogVisibleScore
+            !this.dialogVisibleScore && !this.dialogVisibleENScore
           ) {
             let a = 1;
             let c = this.commentIndexJson;
@@ -13521,6 +13706,18 @@ export default {
                     aiCode: b[j].aiCode,
                     teacherCode: b[j].teacherCode
                   });
+                } else if (b[j].type == 17 && a[i].tool[0] == 70) {
+                  //英语口语
+                  this.workStudent[i].push({
+                    works: b[j].content,
+                    sName: b[j].name,
+                    score: b[j].score,
+                    img: b[j].img,
+                    type: 17,
+                    time: b[j].time,
+                    userid: b[j].userid,
+                    wid: b[j].id,
+                  });
                 }
               }
             }
@@ -13615,7 +13812,11 @@ export default {
         gid: gid,
       };
       this.commentDetail = [];
-      this.commentDialogVisible = true;
+      if(w.type == 17){
+        this.dialogVisibleENScore = true;
+      }else{
+        this.dialogVisibleScore = true;
+      }
       this.commentDetail = w;
       if (w.works && w.type == 1) {
         this.pptImgUrl = "";
@@ -13656,6 +13857,15 @@ export default {
           this.chapInfoList[this.courseType].chapterInfo[0].taskJson[
             this.taskCount
           ].toolChoose[toolIndex].wordJson;
+      }else if(w.type == 17){
+        let englishVoiceJson = {}
+        englishVoiceJson = this.chapInfoList[this.courseType].chapterInfo[0]
+          .taskJson[this.taskCount].toolChoose[index].englishVoiceJson
+          ? this.chapInfoList[this.courseType].chapterInfo[0].taskJson[this.taskCount]
+              .toolChoose[index].englishVoiceJson
+          : {};
+        
+        this.englishVoiceJson = JSON.parse(JSON.stringify(englishVoiceJson));
       }
     },
     openScore(w) {
@@ -14614,6 +14824,26 @@ export default {
                   _worksStudent[i].push(_work);
                   _worksStudent2[i].push(_work);
                   this.isWorksS[i].push({ uid: b[j].userid, sName: b[j].name });
+                } else if (b[j].type == 17 && a[i].tool[0] == 70) {
+                  //英语口语
+                  let _work = {
+                    userid: b[j].userid,
+                    ateacher: b[j].ateacher,
+                    wid: b[j].id,
+                    works: b[j].content,
+                    sName: b[j].name,
+                    type: 17,
+                    time: b[j].time,
+                    score: b[j].score,
+                    img: b[j].img,
+                    likesCount: likesCount,
+                    commentCount: commentCount,
+                    isLikes: isLikes,
+                    commentJson: commentJson,
+                  };
+                  _worksStudent[i].push(_work);
+                  _worksStudent2[i].push(_work);
+                  this.isWorksS[i].push({ uid: b[j].userid, sName: b[j].name });
                 } else if (b[j].type == 11 && a[i].tool[0] == 49) {
                   let _gindex = JSON.parse(b[j].content);
                   if (
@@ -17612,6 +17842,28 @@ export default {
         this.englishToolIndex = i;
         this.myAnList = this.myAnswerList1;
         this.engDialogVisible = true;
+      } else if (t == 70) {
+        if (this.worksStudent[i].length) {
+          for (var k = 0; k < this.worksStudent[i].length; k++) {
+            if (this.userid == this.worksStudent[i][k].userid && this.worksStudent[i][k].type == 17) {
+              this.englishVoiceJsonWork = JSON.parse(this.worksStudent[i][k].works)
+              break;
+            } else {
+              this.englishVoiceJsonWork = [];
+            }
+          }
+        } else {
+          this.englishVoiceJsonWork = [];
+        }
+        let englishVoiceJson = {}
+        englishVoiceJson = this.chapInfoList[this.courseType].chapterInfo[0]
+          .taskJson[index].toolChoose[i].englishVoiceJson
+          ? this.chapInfoList[this.courseType].chapterInfo[0].taskJson[index]
+              .toolChoose[i].englishVoiceJson
+          : {};
+        
+        this.englishVoiceJson = JSON.parse(JSON.stringify(englishVoiceJson));
+        this.EnglishVoiceDialog = true;
       } else if (t == 65) {
         if (this.tType == 2) {
           this.$message.error("不支持学生使用");

+ 255 - 3
src/components/studyStudent.vue

@@ -223,6 +223,7 @@
                       <div v-if="t.tool == 67">分子结构</div>
                       <div v-if="t.tool == 68">时间轴</div>
                       <div v-if="t.tool == 69">英语写作</div>
+                      <div v-if="t.tool == 70">英语口语</div>
                       <div v-if="t.tool == 25">目标管理</div>
                       <div v-if="t.tool == 26">课程设计</div>
                       <div v-if="t.tool == 62">交互视频</div>
@@ -1304,6 +1305,14 @@
                             />
                             <div style="margin: 5px 0">英语写作</div>
                           </div>
+                          <div v-if="tooC == 70">
+                            <img
+                              @click="addTools(tooC, toolIndex, taskCount)"
+                              src="../assets/icon/thirdToolList/englishVoice.png"
+                              alt
+                            />
+                            <div style="margin: 5px 0">英语口语</div>
+                          </div>
                         </div>
                       </div>
                     </div>
@@ -2846,6 +2855,155 @@
                       </div>
                     </div>
                   </div>
+                  <div
+                    v-if="
+                      tType &&
+                      ((tType == 2 && sIsOpen == true) ||
+                        tType == 1 ||
+                        tType == 4) &&
+                      tool.tool.indexOf(70) != -1
+                    "
+                    class="worksBox"
+                  >
+                    <div
+                      class="zuoyeYulan"
+                      v-if="
+                        (worksStudent.length &&
+                        worksStudent[toolIndex].length > 0)
+                      "
+                    >
+                      <div class="worksTop">
+                        <div>作业预览</div>
+                        <div
+                          class="corOpen"
+                          @click="contract(toolIndex)"
+                          v-if="
+                            (isCloseList[toolIndex].isCloseBoolean) &&
+                            isCloseList[toolIndex].isClose == 0
+                          "
+                        >
+                          折叠
+                        </div>
+                        <div
+                          class="corOpen"
+                          @click="contract(toolIndex)"
+                          v-if="isCloseList[toolIndex].isClose == 1"
+                        >
+                          展开
+                        </div>
+                      </div>
+                      <!-- <el-button type="text" @click="jump()" v-if="tType == 2" class="buttonA">我的评价</el-button> -->
+                    </div>
+                    <div
+                      :id="'tool-' + toolIndex"
+                      :style="{
+                        height:isCloseList[toolIndex].isClose == 1 ? retrnToolHeight('tool-' + toolIndex) : 'auto',
+                        overflow: isCloseList[toolIndex].isClose == 1 ? 'hidden' : 'unset'
+                      }"
+                      class="worksDetailBox"
+                      v-if="
+                        worksStudent.length &&
+                        worksStudent[toolIndex].length > 0
+                      "
+                    >
+                      <div
+                        class="works"
+                        style="
+                          width: 200px;
+                          height: 140px;
+                          margin: 10px 10px 10px 0;
+                          border-radius: 15px;
+                          box-shadow: 0 0 6px 1px #dfdada;
+                        "
+                        v-for="(w, wIndex) in worksStudent[toolIndex]"
+                        :key="wIndex"
+                        :class="w.type == 1 ? 'isTypeOne' : ''"
+                      >
+                        <div class="workImg" @click.stop="openScore(w,toolIndex)">
+                          <img :src="word" alt />
+                          <img
+                            class="deleteImg"
+                            src="../assets/deleteworks.png"
+                            v-if="
+                              w.userid == userid || tType == 1 || tType == 4
+                            "
+                            @click.stop="deleteWorks(w.wid)"
+                            alt
+                          />
+                        </div>
+
+                        <div class="comment" style="min-width: 200px">
+                          <div class="worksName">
+                            <div>{{ w.sName }}</div>
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                    <div style="font-size: 18px" v-if="courseDetail.juri != ''">
+                      未提交
+                    </div>
+                    <div
+                      class="noWorksS"
+                      v-if="noWorksS && noWorksS[toolIndex].length"
+                    >
+                      <div
+                        v-for="(s, sIndex) in noWorksS[toolIndex]"
+                        :key="sIndex"
+                        class="noWorksName"
+                      >
+                        {{ s.student }}
+                      </div>
+                    </div>
+                  </div>
+                  <div
+                    v-if="
+                      tType &&
+                      tType == 2 &&
+                      !sIsOpen &&
+                      tool.tool.indexOf(70) != -1
+                    "
+                    class="worksBox"
+                  >
+                    <div
+                      class="zuoyeYulan"
+                      v-if="workStudent[toolIndex].length > 0"
+                    >
+                      <span class="worksTitle">作业预览</span>
+                      <!-- <el-button type="text" @click="jump()" v-if="tType == 2" class="buttonA">我的评价</el-button> -->
+                    </div>
+                    <div
+                      class="worksDetailBox"
+                      v-if="workStudent[toolIndex].length > 0"
+                    >
+                      <div
+                        class="works"
+                        style="
+                          width: 200px;
+                          height: 140px;
+                          margin: 10px 10px 10px 0;
+                          box-shadow: 0 0 6px 1px #dfdada;
+                        "
+                        v-for="(w, wIndex) in workStudent[toolIndex]"
+                        :key="wIndex"
+                      >
+                        <div class="workImg" @click.stop="openScore(w,toolIndex)">
+                          <img :src="word" alt />
+                          <img
+                            class="deleteImg"
+                            src="../assets/deleteworks.png"
+                            v-if="
+                              w.userid == userid || tType == 1 || tType == 4
+                            "
+                            @click.stop="deleteWorks(w.wid)"
+                            alt
+                          />
+                        </div>
+                        <div class="worksName">
+                          <div>{{ w.sName }}</div>
+                        </div>
+                      </div>
+                    </div>
+                  </div>
                   <div
                     v-if="
                       tType &&
@@ -11472,6 +11630,25 @@
         </div>
       </div>
     </el-dialog>
+    <EnglishVoice :EnglishVoiceDialog.sync='EnglishVoiceDialog' 
+    :englishVoiceJson='englishVoiceJson' 
+    :userid="userid" 
+    :id="id" 
+    :courseType="courseType" 
+    :taskCount="taskCount" 
+    :toolindex="toolindex" 
+    @selectSWorks="selectSWorks" 
+    @selectStudent="selectStudent"
+    :englishVoiceJsonWork="englishVoiceJsonWork"></EnglishVoice>
+    <checkEnglishVoice :englishVoiceJson='englishVoiceJson'  
+    :userid="userid" 
+    :dialogVisibleENScore.sync='dialogVisibleENScore'
+    :commentDetail="commentDetail" 
+    :courseDetail="courseDetail" 
+    :wScore="wScore"
+    :scoreDetail="scoreDetail"
+    @selectSWorks="selectSWorks" 
+    @selectStudent="selectStudent"></checkEnglishVoice>
   </div>
 </template>
 
@@ -11503,6 +11680,8 @@ import FileSaver from "file-saver";
 import onlineWrite from "./components/onlineWrite";
 import englishEva from "./components/englishEva";
 import * as Diff from 'diff'
+import EnglishVoice from './EnglishVoice2/index.vue'
+import checkEnglishVoice from './checkEnglishVoice/index.vue'
 
 const getFile = (url) => {
   return new Promise((resolve, reject) => {
@@ -11573,6 +11752,8 @@ export default {
     wordCloud,
     onlineWrite,
     englishEva,
+    EnglishVoice,
+    checkEnglishVoice
   },
   data() {
     return {
@@ -11590,6 +11771,7 @@ export default {
       dialogVisibleSelect: false,
       dialogVisibleSelectTeacher: false,
       dialogVisibleScore: false,
+      dialogVisibleENScore: false,
       dialogVisibleSentence: false,
       dialogVisibleSentenceTeacher: false,
       dialogVisibleSentence1: false,
@@ -11926,6 +12108,9 @@ export default {
       greyType: false,
       correctWord: [],
       isUpdateText: "",
+      EnglishVoiceDialog: false,
+      englishVoiceJson: {},
+      englishVoiceJsonWork:[]
     };
   },
   methods: {
@@ -13125,7 +13310,7 @@ export default {
           if (
             Object.keys(this.commentDetail).length &&
             Object.keys(this.commentIndexJson).length &&
-            !this.dialogVisibleScore
+            !this.dialogVisibleScore && !this.dialogVisibleENScore
           ) {
             let a = 1;
             let c = this.commentIndexJson;
@@ -13502,6 +13687,18 @@ export default {
                     aiCode: b[j].aiCode,
                     teacherCode: b[j].teacherCode
                   });
+                } else if (b[j].type == 17 && a[i].tool[0] == 70) {
+                  //英语口语
+                  this.workStudent[i].push({
+                    works: b[j].content,
+                    sName: b[j].name,
+                    score: b[j].score,
+                    img: b[j].img,
+                    type: 17,
+                    time: b[j].time,
+                    userid: b[j].userid,
+                    wid: b[j].id,
+                  });
                 }
               }
             }
@@ -13639,12 +13836,16 @@ export default {
           ].toolChoose[toolIndex].wordJson;
       }
     },
-    openScore(w) {
+    openScore(w, index) {
       this.wScore = 0;
       this.wScore = w.score ? JSON.parse(w.score).wScore : 0;
       this.scoreDetail = w.score ? JSON.parse(w.score).detail : "";
       this.commentDetail = [];
-      this.dialogVisibleScore = true;
+      if(w.type == 17){
+        this.dialogVisibleENScore = true;
+      }else{
+        this.dialogVisibleScore = true;
+      }
       this.commentDetail = w;
 
       if (w.works && w.type == 1) {
@@ -13672,6 +13873,15 @@ export default {
         this.videoDetail = {};
         this.playerOptions1.sources[0].src = w.works;
         this.videoDetail = this.playerOptions1;
+      }else if(w.type == 17){
+        let englishVoiceJson = {}
+        englishVoiceJson = this.chapInfoList[this.courseType].chapterInfo[0]
+          .taskJson[this.taskCount].toolChoose[index].englishVoiceJson
+          ? this.chapInfoList[this.courseType].chapterInfo[0].taskJson[this.taskCount]
+              .toolChoose[index].englishVoiceJson
+          : {};
+        
+        this.englishVoiceJson = JSON.parse(JSON.stringify(englishVoiceJson));
       }
     },
     addComment(wid, uid, t) {
@@ -14595,6 +14805,26 @@ export default {
                   _worksStudent[i].push(_work);
                   _worksStudent2[i].push(_work);
                   this.isWorksS[i].push({ uid: b[j].userid, sName: b[j].name });
+                } else if (b[j].type == 17 && a[i].tool[0] == 70) {
+                  //英语口语
+                  let _work = {
+                    userid: b[j].userid,
+                    ateacher: b[j].ateacher,
+                    wid: b[j].id,
+                    works: b[j].content,
+                    sName: b[j].name,
+                    type: 17,
+                    time: b[j].time,
+                    score: b[j].score,
+                    img: b[j].img,
+                    likesCount: likesCount,
+                    commentCount: commentCount,
+                    isLikes: isLikes,
+                    commentJson: commentJson,
+                  };
+                  _worksStudent[i].push(_work);
+                  _worksStudent2[i].push(_work);
+                  this.isWorksS[i].push({ uid: b[j].userid, sName: b[j].name });
                 } else if (b[j].type == 11 && a[i].tool[0] == 49) {
                   let _gindex = JSON.parse(b[j].content);
                   if (
@@ -17594,6 +17824,28 @@ export default {
         this.englishToolIndex = i;
         this.myAnList = this.myAnswerList1;
         this.engDialogVisible = true;
+      } else if (t == 70) {
+        if (this.worksStudent[i].length) {
+          for (var k = 0; k < this.worksStudent[i].length; k++) {
+            if (this.userid == this.worksStudent[i][k].userid && this.worksStudent[i][k].type == 17) {
+              this.englishVoiceJsonWork = JSON.parse(this.worksStudent[i][k].works)
+              break;
+            } else {
+              this.englishVoiceJsonWork = [];
+            }
+          }
+        } else {
+          this.englishVoiceJsonWork = [];
+        }
+        let englishVoiceJson = {}
+        englishVoiceJson = this.chapInfoList[this.courseType].chapterInfo[0]
+          .taskJson[index].toolChoose[i].englishVoiceJson
+          ? this.chapInfoList[this.courseType].chapterInfo[0].taskJson[index]
+              .toolChoose[i].englishVoiceJson
+          : {};
+        
+        this.englishVoiceJson = JSON.parse(JSON.stringify(englishVoiceJson));
+        this.EnglishVoiceDialog = true;
       } else if (t == 65) {
         if (this.tType == 2) {
           this.$message.error("不支持学生使用");

+ 2 - 1
src/main.js

@@ -17,9 +17,10 @@ import 'vue-video-player/src/custom-theme.css' //vue-video-player的样式
 import VueCookies from 'vue-cookies'
 import hevueImgPreview from './components/tools/hevue-img-preview'
 import drag from './components/directive/el-drag-dialog';
+import VueAudio from 'vue-audio-better'
 
 
-Vue.use(VideoPlayer).use(VueCookies).use(hevueImgPreview, {
+Vue.use(VideoPlayer).use(VueAudio).use(VueCookies).use(hevueImgPreview, {
     clickMaskCLose: true
 }).use(drag)
 Vue.prototype.$echarts = echarts

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini