Jelajahi Sumber

Merge branch 'beta' into HK

lsc 1 tahun lalu
induk
melakukan
55adf63316
44 mengubah file dengan 4148 tambahan dan 30 penghapusan
  1. 4 0
      dist/index.html
  2. 0 0
      dist/static/css/app.14c6b77afbbd6e8be373d4dd39a5059f.css
  3. 0 0
      dist/static/css/app.14c6b77afbbd6e8be373d4dd39a5059f.css.map
  4. 0 0
      dist/static/css/app.ba10903d92e947f0a50f5c7a615b1813.css
  5. 0 0
      dist/static/css/app.ba10903d92e947f0a50f5c7a615b1813.css.map
  6. TEMPAT SAMPAH
      dist/static/img/englishVoice.802b608.png
  7. TEMPAT SAMPAH
      dist/static/img/env_background.adccd37.png
  8. 0 0
      dist/static/js/app.2969749ef052d6a4fdcd.js
  9. 1 0
      dist/static/js/app.2969749ef052d6a4fdcd.js.map
  10. 0 0
      dist/static/js/app.ea2bde3ba8e83c857405.js.map
  11. 0 0
      dist/static/js/manifest.3ad1d5771e9b13dbdad2.js.map
  12. 0 0
      dist/static/js/vendor.2ea62301d5bc18e5b15b.js
  13. 1 0
      dist/static/js/vendor.2ea62301d5bc18e5b15b.js.map
  14. 0 0
      dist/static/js/vendor.f997da79de948e78b011.js
  15. 0 0
      dist/static/js/vendor.f997da79de948e78b011.js.map
  16. 53 4
      package-lock.json
  17. 2 0
      package.json
  18. 3 1
      src/App.vue
  19. TEMPAT SAMPAH
      src/assets/icon/englishVoice/coin.png
  20. TEMPAT SAMPAH
      src/assets/icon/englishVoice/icon_portal.png
  21. TEMPAT SAMPAH
      src/assets/icon/englishVoice/restart.png
  22. TEMPAT SAMPAH
      src/assets/icon/englishVoice/star-no.png
  23. TEMPAT SAMPAH
      src/assets/icon/englishVoice/star.png
  24. TEMPAT SAMPAH
      src/assets/icon/englishVoice/start_aduio.png
  25. TEMPAT SAMPAH
      src/assets/icon/englishVoice/stop_audio.png
  26. TEMPAT SAMPAH
      src/assets/icon/env_background.png
  27. TEMPAT SAMPAH
      src/assets/icon/thirdToolList/englishVoice.png
  28. 1 1
      src/common/axios.config.js
  29. 1 0
      src/common/tools.js
  30. 99 0
      src/components/EnglishVoice2/component/left.vue
  31. 967 0
      src/components/EnglishVoice2/component/right.vue
  32. 206 0
      src/components/EnglishVoice2/component/testRole.vue
  33. 253 0
      src/components/EnglishVoice2/index.vue
  34. 103 0
      src/components/checkEnglishVoice/component/left.vue
  35. 690 0
      src/components/checkEnglishVoice/component/right.vue
  36. 206 0
      src/components/checkEnglishVoice/component/testRole.vue
  37. 407 0
      src/components/checkEnglishVoice/data/index.vue
  38. 354 0
      src/components/checkEnglishVoice/index.vue
  39. 8 1
      src/components/courseDetail.vue
  40. 237 7
      src/components/easy2/studyStudent.vue
  41. 255 3
      src/components/easy3/studyStudent.vue
  42. 40 9
      src/components/studentIndex.vue
  43. 255 3
      src/components/studyStudent.vue
  44. 2 1
      src/main.js

+ 4 - 0
dist/index.html

@@ -18,7 +18,11 @@
       border-radius: 10px;
       -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
       background-color: rgba(0, 0, 0, 0.1);
+<<<<<<< HEAD
     }</style><link href=./static/css/app.14c6b77afbbd6e8be373d4dd39a5059f.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.f997da79de948e78b011.js></script><script type=text/javascript src=./static/js/app.ea2bde3ba8e83c857405.js></script></body></html><script>function stopSafari() {
+=======
+    }</style><link href=./static/css/app.ba10903d92e947f0a50f5c7a615b1813.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.2969749ef052d6a4fdcd.js></script></body></html><script>function stopSafari() {
+>>>>>>> beta
     //阻止safari浏览器双击放大功能
     let lastTouchEnd = 0  //更新手指弹起的时间
     document.documentElement.addEventListener("touchstart", function (event) {

File diff ditekan karena terlalu besar
+ 0 - 0
dist/static/css/app.14c6b77afbbd6e8be373d4dd39a5059f.css


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


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


File diff ditekan karena terlalu besar
+ 0 - 0
dist/static/css/app.ba10903d92e947f0a50f5c7a615b1813.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.2969749ef052d6a4fdcd.js


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


File diff ditekan karena terlalu besar
+ 0 - 0
dist/static/js/app.ea2bde3ba8e83c857405.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
+ 1 - 0
dist/static/js/vendor.2ea62301d5bc18e5b15b.js.map


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


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


+ 53 - 4
package-lock.json

@@ -11,6 +11,7 @@
         "axios": "^0.21.3",
         "big.js": "^6.2.1",
         "dayjs": "^1.11.7",
+        "diff": "^5.1.0",
         "echarts": "^5.2.2",
         "element-ui": "^2.15.6",
         "file-saver": "^2.0.5",
@@ -31,6 +32,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",
@@ -38,6 +40,7 @@
         "vue-video-player": "^5.0.2",
         "vuex": "^3.6.2",
         "wangeditor": "^4.7.15",
+        "wav-encoder": "^1.3.0",
         "worker-loader": "^2.0.0"
       },
       "devDependencies": {
@@ -10485,10 +10488,9 @@
       "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="
     },
     "node_modules/diff": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npm.taobao.org/diff/download/diff-1.4.0.tgz?cache=0&sync_timestamp=1604803664325&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdiff%2Fdownload%2Fdiff-1.4.0.tgz",
-      "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=",
-      "dev": true,
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/diff/-/diff-5.1.0.tgz",
+      "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==",
       "engines": {
         "node": ">=0.3.1"
       }
@@ -12632,6 +12634,11 @@
       "resolved": "https://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.8.9.tgz",
       "integrity": "sha1-3/wL+aIcAiCQkPKqaUKeFBTa8/k="
     },
+    "node_modules/howler": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmmirror.com/howler/-/howler-2.2.4.tgz",
+      "integrity": "sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w=="
+    },
     "node_modules/hpack.js": {
       "version": "2.1.6",
       "resolved": "https://registry.npmmirror.com/hpack.js/-/hpack.js-2.1.6.tgz",
@@ -15078,6 +15085,15 @@
         "ms": "0.7.1"
       }
     },
+    "node_modules/mocha-nightwatch/node_modules/diff": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/diff/-/diff-1.4.0.tgz",
+      "integrity": "sha512-VzVc42hMZbYU9Sx/ltb7KYuQ6pqAw+cbFWVy4XKdkuEL2CFaRLGEnISPs7YdzaUGpi+CpIqvRmu7hPQ4T7EQ5w==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.3.1"
+      }
+    },
     "node_modules/mocha-nightwatch/node_modules/glob": {
       "version": "7.0.5",
       "resolved": "https://registry.npmmirror.com/glob/download/glob-7.0.5.tgz?cache=0&sync_timestamp=1632353796482&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fglob%2Fdownload%2Fglob-7.0.5.tgz",
@@ -22310,6 +22326,15 @@
       "resolved": "https://registry.npmmirror.com/vue/download/vue-2.6.14.tgz",
       "integrity": "sha1-5RqlJQJQ1Wmj+606ilpofWA24jU="
     },
+    "node_modules/vue-audio-better": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/vue-audio-better/-/vue-audio-better-3.0.1.tgz",
+      "integrity": "sha512-rr0cItOgMHYOLy7VvIHowWGqm+qLcCpj4BZ+4saAHQCSwGcV8TTCefLyGpdIjlJtqL8ytBjjtObft6WSkQoAKQ==",
+      "dependencies": {
+        "howler": "^2.1.2",
+        "vue": "^2.6.10"
+      }
+    },
     "node_modules/vue-cookies": {
       "version": "1.7.4",
       "resolved": "https://registry.npm.taobao.org/vue-cookies/download/vue-cookies-1.7.4.tgz",
@@ -22974,6 +22999,11 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/wav-encoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/wav-encoder/-/wav-encoder-1.3.0.tgz",
+      "integrity": "sha512-FXJdEu2qDOI+wbVYZpu21CS1vPEg5NaxNskBr4SaULpOJMrLE6xkH8dECa7PiS+ZoeyvP7GllWUAxPN3AvFSEw=="
+    },
     "node_modules/wbuf": {
       "version": "1.7.3",
       "resolved": "https://registry.npmmirror.com/wbuf/-/wbuf-1.7.3.tgz",
@@ -34544,6 +34574,11 @@
       "resolved": "https://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.8.9.tgz",
       "integrity": "sha1-3/wL+aIcAiCQkPKqaUKeFBTa8/k="
     },
+    "howler": {
+      "version": "2.2.4",
+      "resolved": "https://registry.npmmirror.com/howler/-/howler-2.2.4.tgz",
+      "integrity": "sha512-iARIBPgcQrwtEr+tALF+rapJ8qSc+Set2GJQl7xT1MQzWaVkFebdJhR3alVlSiUf5U7nAANKuj3aWpwerocD5w=="
+    },
     "hpack.js": {
       "version": "2.1.6",
       "resolved": "https://registry.npmmirror.com/hpack.js/-/hpack.js-2.1.6.tgz",
@@ -42575,6 +42610,15 @@
       "resolved": "https://registry.npmmirror.com/vue/download/vue-2.6.14.tgz",
       "integrity": "sha1-5RqlJQJQ1Wmj+606ilpofWA24jU="
     },
+    "vue-audio-better": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/vue-audio-better/-/vue-audio-better-3.0.1.tgz",
+      "integrity": "sha512-rr0cItOgMHYOLy7VvIHowWGqm+qLcCpj4BZ+4saAHQCSwGcV8TTCefLyGpdIjlJtqL8ytBjjtObft6WSkQoAKQ==",
+      "requires": {
+        "howler": "^2.1.2",
+        "vue": "^2.6.10"
+      }
+    },
     "vue-cookies": {
       "version": "1.7.4",
       "resolved": "https://registry.npm.taobao.org/vue-cookies/download/vue-cookies-1.7.4.tgz",
@@ -43097,6 +43141,11 @@
         }
       }
     },
+    "wav-encoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/wav-encoder/-/wav-encoder-1.3.0.tgz",
+      "integrity": "sha512-FXJdEu2qDOI+wbVYZpu21CS1vPEg5NaxNskBr4SaULpOJMrLE6xkH8dECa7PiS+ZoeyvP7GllWUAxPN3AvFSEw=="
+    },
     "wbuf": {
       "version": "1.7.3",
       "resolved": "https://registry.npmmirror.com/wbuf/-/wbuf-1.7.3.tgz",

+ 2 - 0
package.json

@@ -36,6 +36,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",
@@ -43,6 +44,7 @@
     "vue-video-player": "^5.0.2",
     "vuex": "^3.6.2",
     "wangeditor": "^4.7.15",
+    "wav-encoder": "^1.3.0",
     "worker-loader": "^2.0.0"
   },
   "devDependencies": {

+ 3 - 1
src/App.vue

@@ -464,7 +464,9 @@ html::-webkit-scrollbar-thumb {
   color: #FFF !important;
 }
 
-
+.text_tooltip2 {
+  max-width: 250px;
+}
 
 .el-pagination.is-background .el-pager li:not(.disabled):hover {
     color: #0061FF !important;

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


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


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


TEMPAT SAMPAH
src/assets/icon/englishVoice/star-no.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


+ 1 - 1
src/common/axios.config.js

@@ -15,7 +15,7 @@ axios.interceptors.request.use((config) => {
             'Content-Type': 'application/x-www-form-urlencoded'
         }
     }
-    if (config.url === 'https://gpt.cocorobo.cn/chat' || config.url === 'https://gpt4.cocorobo.cn/imageAnalyse') {
+    if (config.url === 'https://gpt.cocorobo.cn/chat' || config.url === 'https://gpt4.cocorobo.cn/imageAnalyse' || config.url === 'https://gpt4.cocorobo.cn/create_free_assistants' || config.url === 'https://gpt4.cocorobo.cn/assistants_completion_response') {
         config.data = config.data //序列化post 参数
     } else if (config.data && config.data[0].post == '1' && config.method === 'post') {
         config.data = 'mode=' + (Object.values(config.data[0]).join(',')) //序列化post 参数

+ 1 - 0
src/common/tools.js

@@ -32,6 +32,7 @@ export const tools = {
     67: { name: "分子结构" },
     68: { name: "时间轴" },
     69: { name: "英语写作" },
+    69: { name: "英语口语" },
     25: { name: "目标管理" },
     26: { name: "课程设计" },
     62: { name: "交互视频" }

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

@@ -0,0 +1,99 @@
+<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)">
+                <el-tooltip :content="item.content.slice(0,130) + (item.content.length > 130 ? '...' : '')" placement="top" effect="dark" popper-class="text_tooltip2">
+                    <!-- content to trigger tooltip here -->
+                    <span>{{ index + 1  }}.{{ item.content }}</span>
+                </el-tooltip>
+            </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: 1px 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>

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

@@ -0,0 +1,967 @@
+<template>
+  <div class="o_box" ref="obox">
+    <div class="o_top">
+
+    </div>
+    <div class="o_content">
+      <div class="type_box" :style="{ width: oWidth }" v-if="cjson.type !== 'createRole'">
+        {{ getType(cjson) }}
+      </div>
+      <div class="word_box" v-if="cjson.type == 'word' || cjson.type == 'QA'" ref="wb">
+        <div class="word_bbox" :style="{ maxHeight: oheight }">
+          <img class="word_img" :src="cjson.img" alt="" v-if="cjson.img" @click="previewImg(cjson.img)">
+          <div class="word_content" v-html="cjson.content">
+          </div>
+        </div>
+      </div>
+      <div class="sentence_box" v-if="cjson.type == 'sentence'" ref="wb">
+        <span v-html="cjson.content"></span>
+        <div v-if="cjson.img" class="sentence_div">
+          <img :src="cjson.img" alt="" @click="previewImg(cjson.img)">
+        </div>
+      </div>
+      <div class="word_box" v-if="cjson.type == 'theme'" ref="wb" style="max-height: calc(100% - 95px);">
+        <div class="word_bbox" :style="{ maxHeight: oheight }">
+          <div class="word_content" v-html="cjson.content"></div>
+          <div class="word_content2" v-html="cjson.content2" v-if="cjson.content2"></div>
+        </div>
+      </div>
+      <div class="tips_box" v-if="cjson.type == 'theme' && !isRecord && !LuAudioUrl">提示:准备完成后,点击话筒开始录音</div>
+      <div class="time_box" v-if="cjson.type == 'theme' && isRecord">
+        <span>倒计时</span>
+        <span>{{ Times.min }}:{{ Times.secode }}</span>
+      </div>
+      <testRole v-if="cjson.type == 'createRole'" :checkJson="answerArray"></testRole>
+    </div>
+    <div class="o_bottom" v-loading="isloading">
+      <div class="star_box" v-if="LuAudioUrl && star > 0">
+        <div class="star" v-for="index2 in 5" :key="'star' + index2" :class="{ starA: star >= (index2) }">
+        </div>
+      </div>
+      <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>
+    <iframe allow="camera *; microphone *;display-capture;midi;encrypted-media;"
+      src="https://beta.cloud.cocorobo.cn/browser/public/index.html" ref="iiframe" v-show="false"></iframe>
+  </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---------------')
+};
+import testRole from "./testRole.vue";
+export default {
+  components: {
+    testRole,
+  },
+  props: {
+    checkJson: {
+      type: Array,
+    },
+    checkType: {
+      type: Number,
+    },
+    work: {
+      type: Array
+    }
+  },
+  data() {
+    return {
+      cjson: {},
+      LuAudioUrl: "",
+      isRecord: false,
+      isPlayerRecord: false,
+      isloading: false,
+      oheight: 'auto',
+      oWidth: '500px',
+      calcTimer: null,
+      totalSeconds: 0,
+      answerArray: [],
+      id: this.guid(),
+      star: 0
+    }
+  },
+  computed: {
+    // oheight: function() {
+    //   // 获取父元素
+    //   var parentElement = this.$refs['wb'];
+
+    //   // 计算父元素的高度
+    //   var parentHeight = parentElement.offsetHeight;
+    //   return parentHeight + 'px'
+    // }
+    getType() {
+      return function (json) {
+        if (json.type == 'word') {
+          return '单词/词组'
+        } else if (json.type == 'QA') {
+          return '问答题目'
+        } else if (json.type == 'sentence') {
+          return '句子/短文'
+        } else if (json.type == 'theme') {
+          return '主题陈述'
+        }
+      };
+    },
+    Times() {
+      let min = this.totalSeconds ? Math.floor(this.totalSeconds / 60) : 0
+      let secode = this.totalSeconds ? this.totalSeconds % 60 : 0
+      let time = {
+        min: min >= 10 ? min : '0' + min,
+        secode: secode >= 10 ? secode : '0' + secode
+      }
+      return time;
+    },
+  },
+  watch: {
+    checkType: {
+      handler: function (newVal, oldVal) {
+        this.isloading = false
+        this.cjson = JSON.parse(JSON.stringify(this.checkJson[newVal]));
+        this.LuAudioUrl = ''
+        if (typeof this.work[newVal] == 'string') {
+          this.LuAudioUrl = this.work[newVal] ? JSON.parse(JSON.stringify(this.work[newVal])) : ''
+        } else if (typeof this.work[newVal] == 'object' && this.cjson.type != 'createRole') {
+          this.LuAudioUrl = this.work[newVal].audio ? JSON.parse(JSON.stringify(this.work[newVal].audio)) : ''
+        } else if (typeof this.work[newVal] == 'object' && this.cjson.type == 'createRole') {
+          var a = Array.isArray(this.work[newVal])
+          if (a) {
+            this.answerArray = JSON.parse(JSON.stringify(this.work[newVal]))
+          } else {
+            this.answerArray = []
+            this.answerArray.push(
+              {
+                isY: false,
+                content: this.cjson.content3,
+                name: this.cjson.content,
+                img: this.cjson.img
+              }
+            )
+          }
+          this.$emit('setWork', this.answerArray, this.checkType)
+        }
+        if (!this.work[newVal] && this.cjson.type == 'createRole') {
+          var a = Array.isArray(this.work[newVal])
+          if (a) {
+            this.answerArray = JSON.parse(JSON.stringify(this.work[newVal]))
+          } else {
+            this.answerArray = []
+            this.answerArray.push(
+              {
+                isY: false,
+                content: this.cjson.content3,
+                name: this.cjson.content,
+                img: this.cjson.img
+              }
+            )
+          }
+          this.$emit('setWork', this.answerArray, this.checkType)
+        }
+        this.star = this.work[newVal] ? (this.work[newVal].score ? JSON.parse(JSON.stringify(this.work[newVal].score)) : 0) : 0
+        this.oheight = 'auto'
+        this.oWidth = '500px'
+        const images = this.$refs['obox'].querySelectorAll('img');
+        let loadedCount = 0;
+
+        // if(images.length){
+        //   images.forEach((image) => {
+        //     image.addEventListener('load', () => {
+        //       loadedCount++;
+
+        //       if (loadedCount === images.length) {
+        //         this.calculateParentHeight()
+        //       }
+        //     });
+        //   });
+        // }else{
+        if (this.cjson.type != "createRole") {
+          this.calculateParentHeight()
+        }
+        if (this.cjson.type == "createRole") {
+          this.createRole(this.cjson.content2, this.cjson.content)
+        }
+        // }
+
+      },
+      deep: true,
+    },
+  },
+  methods: {
+    previewImg(url) {
+      this.$hevueImgPreview(url);
+    },
+    restart() {
+      let _this = this
+      _this.$confirm("确定重新录音么?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(() => {
+          _this.LuAudioUrl = ""
+          setTimeout(() => {
+            _this.startRecorder()
+          }, 500);
+        })
+        .catch(() => { });
+    },
+    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;
+        if (this.cjson.type == 'theme') {
+          this.setSecodes()
+        }
+        recorder.start().then(
+          () => { },
+          (error) => {
+            _this.isRecord = false;
+            // _this.$message.error(`${error.name} : ${error.message}`);
+            _this.$message.error(`没有找到可使用的麦克风,或者您没有允许此网页使用麦克风`);
+            // 出错了
+            console.log(`${error.name} : ${error.message}`);
+            if (_this.calcTimer) {
+              clearInterval(_this.calcTimer)
+              _this.calcTimer = null;
+            }
+          }
+        );
+      } else {
+        if (_this.calcTimer) {
+          clearInterval(_this.calcTimer)
+          _this.calcTimer = null;
+        }
+        _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 = recorder.getWAVBlob();
+      // const mp3Blob = this.convertToMp3(recorder.getWAV());
+      let audioFile = this.dataURLtoAudio(mp3Blob, "wav");
+      console.log(audioFile);
+      let iiframe = this.$refs['iiframe']
+
+
+
+      // this.isloading = true
+      //   this.beforeUpload1(audioFile, 3);
+      //   return;
+      if (this.cjson.type == 'theme' || this.cjson.type == 'QA') {
+        this.isloading = true
+        let _this = this
+        iiframe.contentWindow.onRecognizedResult = function (e) {
+          console.log('onRecognizedResult', e);
+          let privText = e.privText
+          _this.beforeUpload1(audioFile, 3, privText);
+        }
+
+        iiframe.contentWindow.doContinuousPronunciationAssessment('', { files: [audioFile] })
+      } else if (this.cjson.type == 'createRole') {
+        // this.isloading = true
+        // this.beforeUpload1(audioFile, 3);
+        this.isloading = true
+        let _this = this
+        iiframe.contentWindow.onRecognizedResult = function (e) {
+          console.log('onRecognizedResult', e);
+          let privText = e.privText
+          _this.beforeUpload1(audioFile, 3, privText);
+        }
+
+        iiframe.contentWindow.doContinuousPronunciationAssessment('', { files: [audioFile] })
+      } else {
+        this.isloading = true
+        let _this = this
+        iiframe.contentWindow.onRecognizedResult = function (e) {
+          console.log('onRecognizedResult', e);
+          let privText = e.privText
+          let star = JSON.parse(e.privJson).NBest[0].PronunciationAssessment
+          console.log('star', star)
+          // e.privText 
+          // JSON.parse(e.privJson).NBest[0].PronunciationAssessment
+
+          _this.beforeUpload1(audioFile, 3, privText, star);
+        }
+        iiframe.contentWindow.doContinuousPronunciationAssessment(this.cjson.content, { files: [audioFile] })
+      }
+
+      // 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/wav" });
+    },
+    dataURLtoAudio(blob, filename) {
+      return new File([blob], filename, { type: "audio/wav" });
+    },
+    beforeUpload1(event, type, text, star) {
+      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) {
+            if (_this.cjson.type != 'createRole') {
+              _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) {
+                if (_this.cjson.type == 'createRole') {
+                  _this.answerArray.push(
+                    {
+                      isY: true,
+                      content: text,
+                      voice: data.Location,
+                      name: '',
+                      img: ''
+                    }
+                  )
+                  _this.answerCode(text)
+                } else {
+                  _this.LuAudioUrl = data.Location;
+                  _this.$emit('setWork', _this.LuAudioUrl, _this.checkType, text, star)
+                }
+
+
+              }
+              console.log(data.Location);
+            }
+          });
+      }
+    },
+    calculateParentHeight() {
+      this.$nextTick(() => {
+        setTimeout(() => {
+          // 获取父元素
+          var parentElement = this.$refs['wb'];
+
+          // 计算父元素的高度
+          // var parentHeight = parentElement.offsetHeight + 1;
+          var parentWidth = parentElement.offsetWidth;
+          // this.oheight = parentHeight + 'px'
+          this.oWidth = parentWidth + 'px'
+        }, 50);
+      });
+    },
+    setSecodes() {
+      this.totalSeconds = this.checkJson[this.checkType].oTime * 60
+      // this.totalSeconds = 10
+      this.calcTimer = setInterval(() => {
+        if (this.totalSeconds > 0) {
+          this.totalSeconds--;
+        } else {
+          clearInterval(this.calcTimer);
+          this.calcTimer = null
+          this.startRecorder()
+          console.log("倒计时结束"); // 输出日志
+        }
+      }, 1000);
+    },
+    answerCode(msg) {
+      var _this = this;
+      _this.ajax.post('https://gpt4.cocorobo.cn/assistants_completion_response', {
+        uid: _this.id,
+        message: msg,
+      }).then(function (response) {
+        console.log(response);
+        _this.answerArray.push(
+          {
+            isY: false,
+            content: response.data.FunctionResponse,
+            name: _this.answerArray[0].name,
+            img: _this.answerArray[0].img
+          }
+        )
+        console.log(_this.answerArray);
+        _this.$forceUpdate()
+        _this.isloading = false
+        _this.$emit('setWork', _this.answerArray, _this.checkType)
+      }).catch(function (error) {
+        _this.isloading = false
+        console.log(error);
+      });
+    },
+    guid() {
+      var _num,
+        i,
+        _guid = "";
+      for (i = 0; i < 32; i++) {
+        _guid += Math.floor(Math.random() * 16).toString(16); //随机0  - 16 的数字 转变为16进制的字符串
+        _num = Math.floor((i - 7) / 4); //计算 (i-7)除4
+        if (_num > -1 && _num < 4 && i == 7 + 4 * _num) {
+          //会使guid中间加 "-"   形式为xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+          _guid += "-";
+        }
+      }
+      return _guid;
+    },
+    createRole(content, name) {
+      var _this = this;
+      _this.ajax.post('https://gpt4.cocorobo.cn/create_free_assistants', {
+        fileName: [],
+        url: [],
+        uid: _this.id,
+        instructions: content,
+        assistantName: name
+      }).then(function (response) {
+        console.log(response);
+      }).catch(function (error) {
+        console.log(error);
+      });
+    }
+  },
+  beforeDestroy() {
+    if (!this.isRecord) {
+    } else {
+      if (this.calcTimer) {
+        clearInterval(this.calcTimer)
+        this.calcTimer = null;
+      }
+      recorder.stop(); // 结束录音
+    }
+  },
+  mounted() {
+
+    this.cjson = JSON.parse(JSON.stringify(this.checkJson[this.checkType]));
+    this.LuAudioUrl = ''
+    if (typeof this.work[this.checkType] == 'string') {
+      this.LuAudioUrl = this.work[this.checkType] ? JSON.parse(JSON.stringify(this.work[this.checkType])) : ''
+    } else if (typeof this.work[this.checkType] == 'object' && this.cjson.type != 'createRole') {
+      this.LuAudioUrl = this.work[this.checkType].audio ? JSON.parse(JSON.stringify(this.work[this.checkType].audio)) : ''
+    } else if (typeof this.work[this.checkType] == 'object' && this.cjson.type == 'createRole') {
+      var a = Array.isArray(this.work[this.checkType])
+      if (a) {
+        this.answerArray = JSON.parse(JSON.stringify(this.work[this.checkType]))
+      } else {
+        this.answerArray = []
+        this.answerArray.push(
+          {
+            isY: false,
+            content: this.cjson.content3,
+            name: this.cjson.content,
+            img: this.cjson.img
+          }
+        )
+      }
+      this.$emit('setWork', this.answerArray, this.checkType)
+    }
+    if (!this.work[this.checkType] && this.cjson.type == 'createRole') {
+      var a = Array.isArray(this.work[this.checkType])
+      if (a) {
+        this.answerArray = JSON.parse(JSON.stringify(this.work[this.checkType]))
+      } else {
+        this.answerArray = []
+        this.answerArray.push(
+          {
+            isY: false,
+            content: this.cjson.content3,
+            name: this.cjson.content,
+            img: this.cjson.img
+          }
+        )
+      }
+      this.$emit('setWork', this.answerArray, this.checkType)
+    }
+    this.star = this.work[this.checkType] ? (this.work[this.checkType].score ? JSON.parse(JSON.stringify(this.work[this.checkType].score)) : 0) : 0
+
+    if (this.cjson.type != "createRole") {
+      this.calculateParentHeight()
+    }
+    if (this.cjson.type == "createRole") {
+      this.createRole(this.cjson.content2, this.cjson.content)
+    }
+  },
+}
+</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% - 210px);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  overflow: hidden;
+  flex-direction: column;
+}
+
+.o_bottom {
+  height: 210px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.word_box {
+  min-width: 500px;
+  max-width: 70%;
+  background: #fff;
+  border-radius: 10px;
+  position: relative;
+  /* max-height: calc(100% - 40px); */
+  max-height: calc(100% - 70px);
+  /* overflow: auto; */
+}
+
+.tips_box {
+  margin-top: 30px;
+  color: #727272;
+}
+
+.word_box>.word_bbox {
+  width: 100%;
+  position: relative;
+  z-index: 999;
+  max-height: 100%;
+  overflow: auto;
+}
+
+.sentence_box {
+  background: #e0e0e04d;
+  min-width: 500px;
+  max-width: 70%;
+  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;
+  max-height: calc(100% - 80px);
+}
+
+.word_box::before {
+  content: '';
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  display: block;
+  box-shadow: 0 0 4px 4px #1d39830d;
+  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 #1d39830d;
+  border-radius: 10px;
+  z-index: 1;
+  top: 15px;
+  left: 15px;
+}
+
+.word_box>.word_bbox>.word_img {
+  width: calc(100% - 30px);
+  max-height: 300px;
+  z-index: 999;
+  position: relative;
+  margin: 15px auto;
+  display: block;
+  border-radius: 10px;
+  cursor: pointer;
+  object-fit: contain;
+}
+
+
+.word_box>.word_bbox>.word_content {
+  position: relative;
+  z-index: 999;
+  text-align: center;
+  font-size: 36px;
+  margin: 15px;
+  font-weight: bold;
+  color: #000;
+  width: calc(100% - 30px);
+  word-break: break-word;
+  white-space: pre-line;
+}
+
+.word_box>.word_bbox>.word_content2 {
+  position: relative;
+  z-index: 999;
+  text-align: left;
+  font-size: 16px;
+  margin: 15px;
+  color: #727272;
+  width: calc(100% - 30px);
+  word-break: break-word;
+  white-space: pre-line;
+  /* margin-top: 10px; */
+}
+
+
+.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;
+}
+
+.time_box {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  margin-top: 25px;
+}
+
+.time_box>span:nth-child(1) {
+  color: #727272;
+  font-size: 16px;
+}
+
+.time_box>span:nth-child(2) {
+  /* margin-top: 10px; */
+  font-size: 32px;
+  color: #3581FC;
+}
+
+.type_box {
+  min-width: 500px;
+  width: 70%;
+  text-align: right;
+  color: #a5a5a5;
+  margin-bottom: 10px;
+}
+
+.sentence_div {
+  width: 100%;
+  overflow: hidden;
+  margin-top: 10px;
+  display: flex;
+  justify-content: flex-end;
+}
+
+.sentence_div>img {
+  width: 60px;
+  height: 60px;
+  object-fit: cover;
+  cursor: pointer;
+  border-radius: 4px
+}
+
+.star_box {
+  display: flex;
+  align-items: center;
+  margin-bottom: 10px
+}
+
+.star_box>.star {
+  width: 25px;
+  height: 25px;
+  display: block;
+  background-image: url('../../../assets/icon/englishVoice/star-no.png');
+  background-size: 100% 100%;
+}
+
+.star_box>.star+.star {
+  margin-left: 5px;
+}
+
+.star_box>.starA {
+  background-image: url('../../../assets/icon/englishVoice/star.png');
+}
+</style>

+ 206 - 0
src/components/EnglishVoice2/component/testRole.vue

@@ -0,0 +1,206 @@
+<template>
+    <div class="d_box" v-loading="isloading">
+        <div class="dialog" v-for="(item, index) in answerArray" :key="index" :class="{ dialog_right: item.isY }">
+            <div class="d_img">
+                <img :src="item.img ? item.img : require('../../../assets/icon/englishVoice/icon_portal.png')" alt="">
+            </div>
+            <div class="d_content">
+                <div class="d_name" v-if="item.name">{{ item.name }}</div>
+                <div class="d_voice" v-if="item.voice">
+                    <mini-audio :audio-source="item.voice" class="audio_class"></mini-audio>
+                </div>
+                <div :class="{d_log: !item.isY, d_log2: item.isY}" v-if="item.content">{{ item.content }}</div>
+            </div>
+        </div>
+        <!-- <iframe allow="camera *; microphone *;display-capture;midi;encrypted-media;"
+                src="https://beta.cloud.cocorobo.cn/browser/public/index.html" ref="iiframe"></iframe> -->
+    </div>
+</template>
+    
+<script>
+export default {
+    components: {
+    },
+    props: {
+        checkJson: {
+            type: Object,
+        }
+    },
+    data() {
+        return {
+            json: [],
+            answerArray: [],
+            isRecord: false,
+            isPlayerRecord: false,
+            isloading: false,
+        };
+    },
+    methods: {
+        setVoiceJson(val) {
+            let a = JSON.parse(JSON.stringify(val));
+            // this.json = a;
+            this.answerArray = a
+            // this.answerArray.push(
+            //     {
+            //         isY: false,
+            //         content: a.content3,
+            //         name: a.content,
+            //         img: a.img
+            //     }
+            // )
+
+        },
+    },
+    watch: {
+        checkJson: {
+            handler: function (newVal, oldVal) {
+                if (newVal) {
+                    this.setVoiceJson(this.checkJson);
+                }
+            },
+            deep: true
+        }
+    },
+
+    mounted() {
+        this.setVoiceJson(this.checkJson);
+    }
+};
+</script>
+    
+<style scoped>
+.d_box {
+    width: 100%;
+    height: 100%;
+    /* background: #000; */
+    padding: 25px 50px;
+    box-sizing: border-box;
+    overflow: auto;
+}
+
+.dialog_answer>img {
+    height: 100%;
+    margin-left: auto;
+    cursor: pointer;
+}
+
+
+.dialog {
+    display: flex;
+}
+
+.dialog+.dialog {
+    margin-top: 15px;
+}
+
+.dialog_right {
+    flex-direction: row-reverse;
+}
+
+.dialog>.d_img {
+    width: 40px;
+    height: 40px;
+    min-width: 40px;
+    overflow: hidden;
+    border-radius: 50%;
+    margin-right: 5px;
+}
+
+.dialog>.d_img>img {
+    width: 100%;
+    height: 100%;
+    object-fit: cover;
+}
+
+.dialog_right>.d_img {
+    margin-right: 0;
+    margin-left: 5px;
+}
+
+.dialog>.d_content {
+    width: 100%;
+}
+.dialog_right>.d_content{
+    display: flex;
+    flex-direction: column;
+    align-items: flex-end;
+}
+
+
+.dialog>.d_content>.d_name {
+    color: #7C7C7C;
+    font-size: 12px;
+    margin-bottom: 5px;
+}
+
+.dialog_right>.d_content>.d_name {
+    text-align: right;
+}
+
+.dialog>.d_content>.d_log {
+    color: #000;
+    font-size: 16px;
+    background: #fff;
+    width: 100%;
+    max-width: 800px;
+    border: 2px solid #e05d63;
+    border-radius: 40px;
+    line-height: 26px;
+    padding: 20px 35px;
+    box-sizing: border-box;
+    position: relative;
+}
+
+.dialog>.d_content>.d_log::before{
+    content: '';
+    width: calc(100% - 20px);
+    height: calc(100% - 20px);
+    position: absolute;
+    border: 1px dashed #e05d63;
+    box-sizing: border-box;
+    top: 50%;
+    left: 50%;
+    border-radius: 30px;
+    transform: translate(-50%, -50%);
+}
+
+.dialog>.d_content>.d_log2 {
+    color: #000;
+    font-size: 14px;
+    background: #fff;
+    width: 100%;
+    max-width: 800px;
+    border: 1px dashed #0f94ce;
+    border-radius: 5px;
+    line-height: 26px;
+    padding: 10px;
+    box-sizing: border-box;
+    position: relative;
+    margin-top:10px;
+}
+
+.dialog>.d_content>.d_voice {
+    width: 100%;
+    max-width: 600px;
+}
+
+.audio_class {
+    background: #3680fb !important;
+    margin: 0 !important;
+    width: 100% !important;
+    box-sizing: border-box !important;
+}
+
+.d_voice>>>.vueAudioBetter span:before {
+    color: #fff;
+}
+
+.audio_class>>>.slider .process {
+    background: #000;
+}
+
+.d_voice>>>.vueAudioBetter .iconfont:active {
+    position: unset !important;
+}
+</style>
+    

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

@@ -0,0 +1,253 @@
+<template>
+    <el-dialog title="英语口语" :visible.sync="EnglishVoiceDialog" :append-to-body="true" width="100%"
+        :before-close="handleClose" class="dialog_diy">
+        <div class="ev_box" v-if="EnglishVoiceDialog">
+            <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" style="padding: 5px 10px;font-size: 14px;height: 30px;" size="mini" :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, text, star) {
+            if(this.checkJson[index].type == 'createRole'){
+                console.log(url);
+                this.work[index] = JSON.parse(JSON.stringify(url))
+                this.$forceUpdate()
+            }else{
+                if(star){
+                    let score = parseInt(star.AccuracyScore / 20)
+                    this.work[index] = {
+                        audio:url,
+                        text:text,
+                        score:score,
+                        star:star
+                    }
+                }else{
+                    this.work[index] = {
+                        audio:url,
+                        text:text
+                    }
+                }
+
+            }
+        },
+        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: 0;
+    right: 80px;
+    height: 210px;
+    display: flex;
+    align-items: center;
+}</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>

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

@@ -0,0 +1,690 @@
+<template>
+  <div class="o_box">
+    <div class="o_top"></div>
+    <div class="o_content">
+      <div class="type_box">
+        {{ getType(cjson) }}
+      </div>
+      <div class="word_box">
+        <div class="word_box2" v-if="cjson.type == 'word' || cjson.type == 'QA'">
+          <div class="word_content" v-html="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>
+          <img class="word_img" :src="cjson.img" alt="" v-if="cjson.img" @click="previewImg(cjson.img)" />
+        </div>
+        <div class="word_box2" v-if="cjson.type == 'theme'" style="flex-direction: column;">
+          <div class="word_content" v-html="cjson.content"></div>
+          <div class="word_content2" v-html="cjson.content2"></div>
+        </div>
+        <testRole v-if="cjson.type == 'createRole'" :checkJson="answerArray"></testRole>
+      </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 class="star_box" v-if="LuAudioUrl && star > 0">
+        <div class="star" v-for="index2 in 5" :key="'star' + index2" :class="{ starA: star >= (index2) }">
+        </div>
+      </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---------------')
+};
+import testRole from "./testRole.vue";
+export default {
+  components: {
+    testRole,
+  },
+  props: {
+    checkJson: {
+      type: Array
+    },
+    checkType: {
+      type: Number
+    },
+    work: {
+      type: Array
+    }
+  },
+  data() {
+    return {
+      cjson: {},
+      LuAudioUrl: "",
+      isRecord: false,
+      isPlayerRecord: false,
+      isloading: false,
+      answerArray: [],
+      star: 0
+    };
+  },
+  computed: {
+    getType() {
+      return function (json) {
+        if (json.type == 'word') {
+          return '单词/词组'
+        } else if (json.type == 'QA') {
+          return '问答题目'
+        } else if (json.type == 'sentence') {
+          return '句子/短文'
+        } else if (json.type == 'theme') {
+          return '主题陈述'
+        } else if (json.type == 'createRole') {
+          return '角色对话'
+        }
+      };
+    },
+  },
+  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]))
+        //   : "";
+        this.LuAudioUrl = ''
+        if (typeof this.work[this.checkType] == 'string') {
+          this.LuAudioUrl = this.work[this.checkType] ? JSON.parse(JSON.stringify(this.work[this.checkType])) : ''
+        } else if (typeof this.work[this.checkType] == 'object' && this.cjson.type != 'createRole') {
+          this.LuAudioUrl = this.work[this.checkType].audio ? JSON.parse(JSON.stringify(this.work[this.checkType].audio)) : ''
+        } else if (typeof this.work[newVal] == 'object' && this.cjson.type == 'createRole') {
+          var a = Array.isArray(this.work[newVal])
+          if (a) {
+            this.answerArray = JSON.parse(JSON.stringify(this.work[newVal]))
+          } else {
+            this.answerArray = []
+            this.answerArray.push(
+              {
+                isY: false,
+                content: this.cjson.content3,
+                name: this.cjson.content,
+                img: this.cjson.img
+              }
+            )
+          }
+          this.$emit('setWork', this.answerArray, this.checkType)
+        }
+        if (!this.work[newVal] && this.cjson.type == 'createRole') {
+          var a = Array.isArray(this.work[newVal])
+          if (a) {
+            this.answerArray = JSON.parse(JSON.stringify(this.work[newVal]))
+          } else {
+            this.answerArray = []
+            this.answerArray.push(
+              {
+                isY: false,
+                content: this.cjson.content3,
+                name: this.cjson.content,
+                img: this.cjson.img
+              }
+            )
+          }
+        }
+        this.star = this.work[this.checkType] ? (this.work[this.checkType].score ? JSON.parse(JSON.stringify(this.work[this.checkType].score)) : 0) : 0
+      },
+      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 = ''
+    if (typeof this.work[this.checkType] == 'string') {
+      this.LuAudioUrl = this.work[this.checkType] ? JSON.parse(JSON.stringify(this.work[this.checkType])) : ''
+    } else if (typeof this.work[this.checkType] == 'object' && this.cjson.type != 'createRole') {
+      this.LuAudioUrl = this.work[this.checkType].audio ? JSON.parse(JSON.stringify(this.work[this.checkType].audio)) : ''
+    } else if (typeof this.work[newVal] == 'object' && this.cjson.type == 'createRole') {
+      var a = Array.isArray(this.work[newVal])
+      if (a) {
+        this.answerArray = JSON.parse(JSON.stringify(this.work[newVal]))
+      } else {
+        this.answerArray = []
+        this.answerArray.push(
+          {
+            isY: false,
+            content: this.cjson.content3,
+            name: this.cjson.content,
+            img: this.cjson.img
+          }
+        )
+      }
+      this.$emit('setWork', this.answerArray, this.checkType)
+    }
+    if (!this.work[newVal] && this.cjson.type == 'createRole') {
+      var a = Array.isArray(this.work[newVal])
+      if (a) {
+        this.answerArray = JSON.parse(JSON.stringify(this.work[newVal]))
+      } else {
+        this.answerArray = []
+        this.answerArray.push(
+          {
+            isY: false,
+            content: this.cjson.content3,
+            name: this.cjson.content,
+            img: this.cjson.img
+          }
+        )
+      }
+    }
+    this.star = this.work[this.checkType] ? (this.work[this.checkType].score ? JSON.parse(JSON.stringify(this.work[this.checkType].score)) : 0) : 0
+    // 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;
+  position: relative;
+}
+
+.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-break: break-word;
+  white-space: pre-line;
+}
+
+.sentence_box>.word_img,
+.word_box2>.word_img {
+  min-width: 100px;
+  width: 100px;
+  height: 50px;
+  z-index: 999;
+  position: sticky;
+  margin: 0 0 0 10px;
+  display: block;
+  border-radius: 10px;
+  cursor: pointer;
+  object-fit: cover;
+  top: calc(50% - 25px);
+}
+
+.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%);
+  word-break: break-word;
+  white-space: pre-line;
+}
+
+.word_box2>.word_content2 {
+  position: relative;
+  z-index: 999;
+  /* text-align: center; */
+  /* font-size: 30px; */
+  margin: 7px 0 7px 0px;
+  color: #727272;
+  width: calc(100%);
+  word-break: break-word;
+  white-space: pre-line;
+  font-size: 16px;
+
+}
+
+.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;
+}
+
+.star_box {
+  display: flex;
+  align-items: center;
+  margin-bottom: 10px
+}
+
+.star_box>.star {
+  width: 25px;
+  height: 25px;
+  display: block;
+  background-image: url('../../../assets/icon/englishVoice/star-no.png');
+  background-size: 100% 100%;
+}
+
+.star_box>.star+.star {
+  margin-left: 5px;
+}
+
+.star_box>.starA {
+  background-image: url('../../../assets/icon/englishVoice/star.png');
+}
+
+.type_box {
+  width: calc(100% - 20px);
+    text-align: right;
+    color: #a5a5a5;
+    position: absolute;
+    top: 0;
+}
+</style>

+ 206 - 0
src/components/checkEnglishVoice/component/testRole.vue

@@ -0,0 +1,206 @@
+<template>
+    <div class="d_box" v-loading="isloading">
+        <div class="dialog" v-for="(item, index) in answerArray" :key="index" :class="{ dialog_right: item.isY }">
+            <div class="d_img">
+                <img :src="item.img ? item.img : require('../../../assets/icon/englishVoice/icon_portal.png')" alt="">
+            </div>
+            <div class="d_content">
+                <div class="d_name" v-if="item.name">{{ item.name }}</div>
+                <div class="d_voice" v-if="item.voice">
+                    <mini-audio :audio-source="item.voice" class="audio_class"></mini-audio>
+                </div>
+                <div :class="{d_log: !item.isY, d_log2: item.isY}" v-if="item.content">{{ item.content }}</div>
+            </div>
+        </div>
+        <!-- <iframe allow="camera *; microphone *;display-capture;midi;encrypted-media;"
+                src="https://beta.cloud.cocorobo.cn/browser/public/index.html" ref="iiframe"></iframe> -->
+    </div>
+</template>
+    
+<script>
+export default {
+    components: {
+    },
+    props: {
+        checkJson: {
+            type: Object,
+        }
+    },
+    data() {
+        return {
+            json: [],
+            answerArray: [],
+            isRecord: false,
+            isPlayerRecord: false,
+            isloading: false,
+        };
+    },
+    methods: {
+        setVoiceJson(val) {
+            let a = JSON.parse(JSON.stringify(val));
+            // this.json = a;
+            this.answerArray = a
+            // this.answerArray.push(
+            //     {
+            //         isY: false,
+            //         content: a.content3,
+            //         name: a.content,
+            //         img: a.img
+            //     }
+            // )
+
+        },
+    },
+    watch: {
+        checkJson: {
+            handler: function (newVal, oldVal) {
+                if (newVal) {
+                    this.setVoiceJson(this.checkJson);
+                }
+            },
+            deep: true
+        }
+    },
+
+    mounted() {
+        this.setVoiceJson(this.checkJson);
+    }
+};
+</script>
+    
+<style scoped>
+.d_box {
+    width: 100%;
+    height: 100%;
+    /* background: #000; */
+    padding: 10px;
+    box-sizing: border-box;
+    overflow: auto;
+}
+
+.dialog_answer>img {
+    height: 100%;
+    margin-left: auto;
+    cursor: pointer;
+}
+
+
+.dialog {
+    display: flex;
+}
+
+.dialog+.dialog {
+    margin-top: 15px;
+}
+
+.dialog_right {
+    flex-direction: row-reverse;
+}
+
+.dialog>.d_img {
+    width: 40px;
+    height: 40px;
+    min-width: 40px;
+    overflow: hidden;
+    border-radius: 50%;
+    margin-right: 5px;
+}
+
+.dialog>.d_img>img {
+    width: 100%;
+    height: 100%;
+    object-fit: cover;
+}
+
+.dialog_right>.d_img {
+    margin-right: 0;
+    margin-left: 5px;
+}
+
+.dialog>.d_content {
+    width: 100%;
+}
+.dialog_right>.d_content{
+    display: flex;
+    flex-direction: column;
+    align-items: flex-end;
+}
+
+
+.dialog>.d_content>.d_name {
+    color: #7C7C7C;
+    font-size: 12px;
+    margin-bottom: 5px;
+}
+
+.dialog_right>.d_content>.d_name {
+    text-align: right;
+}
+
+.dialog>.d_content>.d_log {
+    color: #000;
+    font-size: 16px;
+    background: #fff;
+    width: 100%;
+    max-width: 800px;
+    border: 2px solid #e05d63;
+    border-radius: 40px;
+    line-height: 26px;
+    padding: 20px 35px;
+    box-sizing: border-box;
+    position: relative;
+}
+
+.dialog>.d_content>.d_log::before{
+    content: '';
+    width: calc(100% - 20px);
+    height: calc(100% - 20px);
+    position: absolute;
+    border: 1px dashed #e05d63;
+    box-sizing: border-box;
+    top: 50%;
+    left: 50%;
+    border-radius: 30px;
+    transform: translate(-50%, -50%);
+}
+
+.dialog>.d_content>.d_log2 {
+    color: #000;
+    font-size: 14px;
+    background: #fff;
+    width: 100%;
+    max-width: 800px;
+    border: 1px dashed #0f94ce;
+    border-radius: 5px;
+    line-height: 26px;
+    padding: 10px;
+    box-sizing: border-box;
+    position: relative;
+    margin-top:10px;
+}
+
+.dialog>.d_content>.d_voice {
+    width: 100%;
+    max-width: 600px;
+}
+
+.audio_class {
+    background: #3680fb !important;
+    margin: 0 !important;
+    width: 100% !important;
+    box-sizing: border-box !important;
+}
+
+.d_voice>>>.vueAudioBetter span:before {
+    color: #fff;
+}
+
+.audio_class>>>.slider .process {
+    background: #000;
+}
+
+.d_voice>>>.vueAudioBetter .iconfont:active {
+    position: unset !important;
+}
+</style>
+    

+ 407 - 0
src/components/checkEnglishVoice/data/index.vue

@@ -0,0 +1,407 @@
+<template>
+  <el-dialog title="学生报告" :visible.sync="dataDialog" :append-to-body="true" :before-close="handleClose"
+    class="dialog_diy">
+    <div>
+      <div class="whiteBox">
+        <div>{{ title }}</div>
+        <div class="data_progress">
+          <div class="depth">
+              <div>
+                <el-progress :width="80" type="circle" :percentage="score.Pronunciation" :stroke-width="15" :format="format" color="#40b640"></el-progress>
+              </div>
+              <span>Pronunciation score</span>
+          </div>
+          <div class="depth">
+              <div>
+                <el-progress :width="80" type="circle" :percentage="score.Accuracy" :stroke-width="15" :format="format" color="#bc66c3"></el-progress>
+              </div>
+              <span>Accuracy score</span>
+          </div>
+          <div class="depth">
+              <div>
+                <el-progress :width="80" type="circle" :percentage="score.Fluency" :stroke-width="15" :format="format" color="#e871bc"></el-progress>
+              </div>
+              <span>Fluency score</span>
+          </div>
+          <div class="depth">
+              <div>
+                <el-progress :width="80" type="circle" :percentage="score.Completeness" :stroke-width="15" :format="format" color="#697bda"></el-progress>
+              </div>
+              <span>Completeness score</span>
+          </div>
+        </div>
+      </div>
+      <div class="whiteBox" v-if="wordArray.length">
+        <div class="word_box" v-for="(item, index) in wordArray" :key="index">
+          <div class="word"><span>{{ index + 1 }}.</span><span>{{ item.content }}</span></div>
+          <div class="star_box" v-if="item.work && item.work.score">
+            <div class="star" v-for="index2 in 5" :key="'star' + index2" :class="{ starA: item.work.score >= (index2) }">
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="whiteBox" v-if="sentenceArray.length">
+        <div class="setenceBox" v-for="(item, index) in sentenceArray" :key="index">
+          <div class="setence">
+            <div class="title">{{ index+1 }}.原文:</div>
+            <div class="content">{{ item.content }}</div>
+          </div>
+          <div class="star_box" style="margin: 10px 0;" v-if="item.work && item.work.score">
+            <div class="star" v-for="index2 in 5" :key="'star' + index2" :class="{ starA: item.work.score >= (index2) }">
+            </div>
+          </div>
+          <div class="setence">
+            <div class="title">录音译文:</div>
+            <span>{{ (item.work && item.work.text) ? item.work.text : '无' }}</span>
+          </div>
+        </div>
+      </div>
+      <div class="whiteBox" v-if="QAArray.length">
+        <div class="setenceBox" v-for="(item, index) in QAArray" :key="index">
+          <div class="setence">
+            <div class="title">{{ index+1 }}.问题:</div>
+            <div class="content">{{ item.content }}</div>
+          </div>
+          <!-- <div class="star_box" style="margin: 10px 0;">
+            <div class="star" v-for="index2 in 5" :key="'star' + index2" :class="{ starA: 3 >= (index2) }">
+            </div>
+          </div> -->
+          <div class="setence">
+            <div class="title">录音回答译文:</div>
+            <span>{{ (item.work && item.work.text) ? item.work.text : '无' }}</span>
+          </div>
+        </div>
+      </div>
+      <div class="whiteBox" v-if="themeArray.length">
+        <div class="setenceBox" v-for="(item, index) in themeArray" :key="index">
+          <div class="setence">
+            <div class="title">{{ index+1 }}.要点:</div>
+            <div class="content2">
+              <span>{{ item.content }}</span>
+              <span>{{ item.content2 }}</span>
+            </div>
+          </div>
+          <!-- <div class="star_box" style="margin: 10px 0;">
+            <div class="star" v-for="index2 in 5" :key="'star' + index2" :class="{ starA: 3 >= (index2) }">
+            </div>
+          </div> -->
+          <div class="setence">
+            <div class="title">录音回答译文:</div>
+            <div class="content2">
+              <!-- <span>{{ item.content }}</span> -->
+              <span>{{ (item.work && item.work.text) ? item.work.text : '无' }}</span>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="whiteBox" v-if="createRoleArray.length">
+        <div class="setenceBox" v-for="(item, index) in createRoleArray" :key="index">
+          <div class="setence">
+            <div class="title">{{ index+1 }}.对话:</div>
+            <div class="content2" v-if="item.work && item.work.length">
+              <testRole :checkJson="item.work"></testRole>
+            </div>
+            <div class="content2" v-else>
+              暂未对话
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+  
+<script>
+import testRole from "../component/testRole.vue";
+export default {
+  components: {
+    testRole
+  },
+  props: {
+    dataDialog: {
+      type: Boolean,
+      default: false
+    },
+    englishVoiceJson: {
+      type: Object,
+      default: () => { }
+    },
+    commentDetail: {
+      type: Object,
+      default: () => { }
+    }
+  },
+  data() {
+    return {
+      checkJson: [],
+      title: "",
+      detail: "",
+      checkType: 0,
+      work: [],
+      wScore2: 0,
+      scoreDetail2: "",
+      wordArray: [],
+      sentenceArray: [],
+      QAArray: [],
+      themeArray: [],
+      createRoleArray: [],
+      score:{
+        Pronunciation: 0,
+        Accuracy: 0,
+        Fluency: 0,
+        Completeness: 0,
+      }
+    };
+  },
+  methods: {
+    handleClose(done) {
+      this.close();
+      done();
+    },
+    close() {
+      this.$emit("update:dataDialog", false);
+    },
+    close2() {
+      this.$emit("update:dataDialog", false);
+    },
+    confirm() {
+      this.close2();
+    },
+    setVoiceJson(val) {
+      let score = {
+        Pronunciation: 0,
+        Accuracy: 0,
+        Fluency: 0,
+        Completeness: 0,
+      }
+      let scoreCont = 0
+      let a = JSON.parse(JSON.stringify(val));
+      this.title = a.title;
+      this.detail = a.detail;
+      this.checkJson = a.array;
+      this.checkType = 0;
+      this.work.length = a.array.length;
+      let works = JSON.parse(
+        JSON.parse(JSON.stringify(this.commentDetail.works))
+      );
+      for(var i = 0; i < works.length; i++){
+        if (i > (this.checkJson.length - 1)) {
+          break
+        }
+        if(works[i] && works[i].star){
+          scoreCont++
+          score.Pronunciation += works[i].star.PronScore
+          score.Accuracy += works[i].star.AccuracyScore
+          score.Fluency += works[i].star.FluencyScore
+          score.Completeness += works[i].star.CompletenessScore
+        }
+      }
+      this.score = {
+        Pronunciation: parseInt((score.Pronunciation / scoreCont).toFixed(0)),
+        Accuracy: parseInt((score.Accuracy / scoreCont).toFixed(0)),
+        Fluency: parseInt((score.Fluency / scoreCont).toFixed(0)),
+        Completeness: parseInt((score.Completeness / scoreCont).toFixed(0)),
+      }
+      for (var i = 0; i < this.checkJson.length; i++) {
+        this.checkJson[i].work = ''
+        if (i > (works.length - 1)) {
+          break
+        }
+        this.checkJson[i].work = works[i];
+      }
+
+      let wordArray = []
+      let sentenceArray = []
+      let QAArray = []
+      let themeArray = []
+      let createRoleArray = []
+
+      for (var i = 0; i < this.checkJson.length; i++) {
+        if (this.checkJson[i].type == 'word') {
+          wordArray.push(this.checkJson[i])
+        } else if (this.checkJson[i].type == 'sentence') {
+          sentenceArray.push(this.checkJson[i])
+        } else if (this.checkJson[i].type == 'QA') {
+          QAArray.push(this.checkJson[i])
+        } else if (this.checkJson[i].type == 'theme') {
+          themeArray.push(this.checkJson[i])
+        } else if (this.checkJson[i].type == 'createRole') {
+          createRoleArray.push(this.checkJson[i])
+        }
+      }
+
+      this.wordArray = wordArray
+      this.sentenceArray = sentenceArray
+      this.QAArray = QAArray
+      this.themeArray = themeArray
+      this.createRoleArray = createRoleArray
+      this.$forceUpdate()
+    },
+    format(percentage) {
+      return percentage;
+    },
+  },
+  watch: {
+    dataDialog: {
+      handler: function (newVal, oldVal) {
+        if (newVal) {
+          this.setVoiceJson(this.englishVoiceJson);
+        }
+      },
+      deep: true
+    }
+  },
+  mounted() {
+    this.setVoiceJson(this.englishVoiceJson);
+  }
+};
+</script>
+  
+<style scoped>
+.dialog_diy>>>.el-dialog {
+  width: 100%;
+  max-width: 1000px;
+  height: 100%;
+  margin: 0vh auto !important;
+}
+
+.dialog_diy>>>.el-dialog__header {
+  background: #454545 !important;
+  padding: 15px;
+}
+
+.dialog_diy>>>.el-dialog__body {
+  height: calc(100% - 54px);
+  box-sizing: border-box;
+  padding: 15px;
+}
+
+.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: #f0f4fa;
+  overflow: auto;
+}
+
+.whiteBox {
+  background: #fff;
+  /* margin-bottom: 15px; */
+  padding: 15px 30px;
+  border-radius: 5px;
+  width: 100%;
+  box-sizing: border-box;
+}
+.whiteBox +.whiteBox {
+  margin-top: 15px;
+}
+.word_box {
+  font-size: 14px;
+  font-weight: bold;
+  display: flex;
+  color: #000;
+}
+
+.word_box+.word_box {
+  margin-top: 10px;
+}
+
+.word_box>.word {
+  width: 250px;
+}
+
+.star_box {
+  display: flex;
+  align-items: center;
+}
+
+.star_box>.star {
+  width: 20px;
+  height: 20px;
+  display: block;
+  background-image: url('../../../assets/icon/englishVoice/star-no.png');
+  background-size: 100% 100%;
+}
+
+.star_box>.star+.star {
+  margin-left: 5px;
+}
+
+.star_box>.starA {
+  background-image: url('../../../assets/icon/englishVoice/star.png');
+}
+
+
+.setenceBox{}
+.setenceBox + .setenceBox{
+  margin-top: 15px;
+}
+.setenceBox > .setence{
+  color: #000;
+}
+.setenceBox > .setence > .title{
+  font-size: 16px;
+  margin-bottom: 10px;
+  font-weight: bold;
+}
+.setenceBox > .setence > .content{
+  word-break: break-word;
+  white-space: pre-line;
+  font-size: 14px;
+}
+
+.setenceBox > .setence > .content2{
+  font-size: 14px;
+}
+.setenceBox > .setence > .content2 > span{
+  display: block;
+}
+.setenceBox > .setence > .content2 > span:nth-child(1){
+  font-size: 14px;
+}
+
+.setenceBox > .setence > .content2 > span:nth-child(2){
+  word-break: break-word;
+  white-space: pre-line;
+  font-size: 14px;
+  margin-top: 10px;
+}
+
+.data_progress{
+  margin-top: 10px;
+  display: flex;
+  justify-content: space-between;
+}
+.data_progress > .depth{
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  width: calc(100% / 4);
+}
+.data_progress > .depth > div{
+  display: flex;
+  justify-content: center;
+}
+.data_progress > .depth > span{
+  font-size: 14px;
+  font-weight: bold;
+  color: #000;
+  margin-top: 5px;
+}
+</style>
+  

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

@@ -0,0 +1,354 @@
+<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 class="btn_box">
+          <el-button type="primary" size="small" @click="opanData">
+            学生报告
+          </el-button>
+        </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>
+    <dataBoard :dataDialog.sync="dataDialog" :englishVoiceJson="englishVoiceJson" :commentDetail="commentDetail"></dataBoard>
+  </el-dialog>
+</template>
+
+<script>
+import right from "./component/right.vue";
+import left from "./component/left.vue";
+import dataBoard from "./data/index.vue";
+export default {
+  components: {
+    right,
+    left,
+    dataBoard
+  },
+  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: "",
+      dataDialog: false 
+    };
+  },
+  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.scoreDetail));
+      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);
+        });
+    },
+    opanData(){
+      this.dataDialog = true
+    }
+  },
+  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;
+}
+
+.btn_box{
+  display: flex;
+  align-items: center;
+  height: 50px;
+  margin-left: 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"

+ 237 - 7
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, 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 &&
@@ -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,
+                  });
                 }
               }
             }
@@ -13668,12 +13865,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) {
@@ -13701,6 +13902,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) {
@@ -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("不支持学生使用");

+ 255 - 3
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 == 70">英语口语</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, 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 &&
@@ -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,
+                  });
                 }
               }
             }
@@ -13658,12 +13855,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) {
@@ -13691,6 +13892,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) {
@@ -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("不支持学生使用");

+ 40 - 9
src/components/studentIndex.vue

@@ -417,7 +417,27 @@ export default {
             }
           }
         }
-      } else if (typeid == "34629ce3-d02f-11ec-8c78-005056b86db5"  || ftypeId == '34629ce3-d02f-11ec-8c78-005056b86db5') {
+      } else if (typeName == "学科") {
+        if (type == 1) {
+          if (this.typeE.indexOf(typeid) != -1) {
+            this.typeE.splice(this.typeE.indexOf(typeid), 1);
+          } else {
+            this.typeE.push(typeid);
+            if (this.typed != "") {
+              this.typed = "";
+            }
+          }
+        } else {
+          if (this.typed == typeid) {
+            this.typed = "";
+          } else {
+            this.typed = typeid;
+            if (this.typeE.indexOf(ftypeId) != -1) {
+              this.typeE.splice(this.typeE.indexOf(ftypeId), 1);
+            }
+          }
+        }
+      } else if (typeName == "项目类型") {
         if (type == 1) {
           if (this.typeE.indexOf(typeid) != -1) {
             this.typeE.splice(this.typeE.indexOf(typeid), 1);
@@ -702,6 +722,11 @@ export default {
               res.data[0][i].name = "赛道";
             }else if(res.data[0][i].id == "2f8beae3-d030-11ec-8c78-005056b86db5"){
               res.data[0][i].name = "主题";
+<<<<<<< HEAD
+=======
+            }else if(res.data[0][i].id == "34628934-d02f-11ec-8c78-005056b86db5"){
+              res.data[0][i].name = "年级";
+>>>>>>> beta
             }
             if (!this.CourseTypeJson[res.data[0][i].id]) {
               this.CourseTypeJson[res.data[0][i].id] = [];
@@ -710,22 +735,28 @@ export default {
               if (res.data[0][i].id == "34629ce3-d02f-11ec-8c78-005056b86db5") {
                 this.CourseType[0][i].name = "项目类型";
               }
+            }else if(this.org == '150e3120-9195-11ed-b13d-005056b86db5'){
+
+            }else{
+              if (res.data[0][i].name == "赛道") {
+                this.CourseType[0][i].name = "学科";
+              }
             }
 
             if (res.data[2].length == 0 && res.data[3].length == 0) {
-              // for (var j = 0; j < res.data[1].length; j++) {
-              //   if (res.data[0][i].id == res.data[1][j].pid) {
-              //     this.CourseTypeJson[res.data[0][i].id].push(res.data[1][j]); // 去除公共分类
-              //   }
-              // }
-              if (this.org == '150e3120-9195-11ed-b13d-005056b86db5') {
+              if(this.org == '150e3120-9195-11ed-b13d-005056b86db5'){
+                for (var j = 0; j < res.data[1].length; j++) {
+                  if (res.data[0][i].id == res.data[1][j].pid) {
+                    this.CourseTypeJson[res.data[0][i].id].push(res.data[1][j]); // 去除公共分类
+                  }
+                }
+              }else{
+                // this.CourseType = []
                 for (var j = 0; j < res.data[1].length; j++) {
                   if (res.data[0][i].id == res.data[1][j].pid) {
                     this.CourseTypeJson[res.data[0][i].id].push(res.data[1][j]); // 去除公共分类
                   }
                 }
-              } else {
-                this.CourseType = []
               }
             } else {
               if (res.data[2].length > 0) {

+ 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