11wqe1 1 bulan lalu
induk
melakukan
745d856a62

+ 99 - 0
src/components/EnglishVoice3/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>

+ 18 - 0
src/components/EnglishVoice3/component/report.vue

@@ -0,0 +1,18 @@
+<template>
+  <div class="reportBox"></div>
+</template>
+
+<script>
+export default {};
+</script>
+
+<style scoped>
+.reportBox {
+  background: #fff;
+  width: 800px;
+  height: 300px;
+  position: absolute;
+  bottom: -320px;
+  right: 45px;
+}
+</style>

+ 1009 - 0
src/components/EnglishVoice3/component/right.vue

@@ -0,0 +1,1009 @@
+<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
+				let _result = ``;
+        iiframe.contentWindow.onRecognizedResult = function (e) {
+          console.log('onRecognizedResult', e);
+          let privText = e.privText
+					_result+=privText;
+
+          // _this.beforeUpload1(audioFile, 3, privText);
+        }
+
+				iiframe.contentWindow.onSessionStopped = function(e){
+					console.log("转译完成")
+					console.log(e);
+					_this.beforeUpload1(audioFile, 3, _result);
+				}
+
+        iiframe.contentWindow.doContinuousPronunciationAssessment('', { files: [audioFile] })
+      } else if (this.cjson.type == 'createRole') {
+        // this.isloading = true
+        // this.beforeUpload1(audioFile, 3);
+        this.isloading = true
+        let _this = this
+				let _result = ``;
+        iiframe.contentWindow.onRecognizedResult = function (e) {
+          console.log('onRecognizedResult', e);
+          let privText = e.privText
+					_result+=privText;
+          // _this.beforeUpload1(audioFile, 3, privText);
+        }
+
+				iiframe.contentWindow.onSessionStopped = function(e){
+					console.log("转译完成")
+					console.log(e);
+					_this.beforeUpload1(audioFile, 3, _result);
+				}
+
+        iiframe.contentWindow.doContinuousPronunciationAssessment('', { files: [audioFile] })
+      } else {
+        this.isloading = true
+        let _this = this
+				let _result = ``;
+				let _star = ``;
+        iiframe.contentWindow.onRecognizedResult = function (e) {
+          console.log('onRecognizedResult', e);
+          let privText = e.privText
+					_result +=privText;
+          _star = JSON.parse(e.privJson).NBest[0].PronunciationAssessment
+					console.log(_star)
+          // console.log('star', star)
+          // e.privText 
+          // JSON.parse(e.privJson).NBest[0].PronunciationAssessment
+
+          // _this.beforeUpload1(audioFile, 3, privText, star);
+        }
+
+				iiframe.contentWindow.onSessionStopped = function(e){
+					console.log("转译完成")
+					console.log(e);
+					_this.beforeUpload1(audioFile, 3, _result, _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;
+      if (msg) {
+        _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);
+        });
+      } else {
+        _this.answerArray.push(
+          {
+            isY: false,
+            content: "抱歉,您刚刚没有成功录入内容,请再说一遍!",
+            name: _this.answerArray[0].name,
+            img: _this.answerArray[0].img
+          }
+        )
+        _this.isloading = false
+      }
+    },
+    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>

+ 382 - 0
src/components/EnglishVoice3/component/testRole new.vue

@@ -0,0 +1,382 @@
+<template>
+  <div class="d_box" v-loading="isloading">
+    <div class="dialog">
+      <div class="d_img">
+        <img
+          :src="require('../../../assets/icon/englishVoice/icon_portal.png')"
+          alt=""
+        />
+      </div>
+      <div class="d_content_message">
+        <div>Hello,以下是我们这次口语练习的要求:</div>
+        <div class="d_name" v-if="answerArray.time">
+          练习时长:{{
+            answerArray.time == "all" ? "不限时" : answerArray.time + "min"
+          }}
+        </div>
+        <div class="d_name" v-if="answerArray.difficulty">
+          设置难度:{{
+            answerArray.difficulty == 0
+              ? "简单"
+              : answerArray.difficulty == 1
+              ? "一般"
+              : "困难"
+          }}
+        </div>
+        <div
+          class="d_name"
+          v-if="fName && sName"
+          style="
+            display: flex;
+            flex-direction: row;
+            flex-wrap: nowrap;
+            align-items: center;
+            width: 100%;
+          "
+        >
+          <div style="min-width: 60px">对话主题:</div>
+          <div style="width: calc(100% - 80px)">
+            {{ fName - sName }}
+          </div>
+        </div>
+        <div class="gotoTalk">我已了解,开始对话</div>
+        <div class="nowTime">{{ currentTime }}</div>
+      </div>
+    </div>
+    <div class="dialog">
+      <div class="d_img">
+        <img
+          :src="require('../../../assets/icon/englishVoice/icon_portal.png')"
+          alt=""
+        />
+      </div>
+      <div class="d_content">
+        <div class="d_voice">
+          <mini-audio :audio-source="voice" class="audio_class"></mini-audio>
+        </div>
+        <div class="d_log">
+          {{ content }}
+        </div>
+      </div>
+      <div class="nowTime" style="margin: 0 0 0 50px">
+        {{ currentTime }}
+      </div>
+    </div>
+    <div class="dialog dialog_right">
+      <div class="d_img">
+        <img
+          :src="require('../../../assets/icon/englishVoice/icon_portal.png')"
+          alt=""
+        />
+      </div>
+      <div class="d_content">
+        <div class="d_voice">
+          <mini-audio :audio-source="voice" class="audio_class"></mini-audio>
+        </div>
+        <div class="d_log2">
+          {{ content }}
+        </div>
+      </div>
+      <report v-if="lookReport"></report>
+      <div
+        @click="lookReport = true"
+        class="reportButton"
+        :style="{ bottom: lookReport ? '-360px' : '-35px' }"
+      >
+        查看报告
+      </div>
+      <div
+        class="nowTime"
+        style="margin: 0 50px 0 0; bottom: -60px"
+        :style="{ bottom: lookReport ? '-390px' : '-60px' }"
+      >
+        {{ currentTime }}
+      </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>
+import report from "./report.vue";
+export default {
+  components: {
+    report,
+  },
+  props: {
+    checkJson: {
+      type: Object,
+    },
+  },
+  data() {
+    return {
+      json: [],
+      answerArray: [],
+      isRecord: false,
+      isPlayerRecord: false,
+      isloading: false,
+      fName: "",
+      sName: "",
+      currentTime: "",
+      lookReport: false,
+      content:
+        "Read the following text aloud.A Car Accident I saw a car accident this morning. A boy wanted to cross the road but he did not look carefully. He just ran out into the road. A car was coming and the driver stopped at once. Luckily, the boy was not hurt. However, there was a bus behind the car. It could not stop and hit the back of the car. Someone called the police and they arrived quickly.",
+    };
+  },
+  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
+      //     }
+      // )
+    },
+    formatDate(date) {
+      const year = date.getFullYear();
+      const month = (date.getMonth() + 1).toString().padStart(2, "0");
+      const day = date.getDate().toString().padStart(2, "0");
+      const hours = date.getHours().toString().padStart(2, "0");
+      const minutes = date.getMinutes().toString().padStart(2, "0");
+      const seconds = date.getSeconds().toString().padStart(2, "0");
+
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+    },
+  },
+  watch: {
+    checkJson: {
+      handler: function (newVal, oldVal) {
+        if (newVal) {
+          this.setVoiceJson(this.checkJson);
+        }
+      },
+      deep: true,
+    },
+  },
+  created() {
+    this.currentTime = this.formatDate(new Date());
+  },
+  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;
+  position: relative;
+}
+
+.dialog + .dialog {
+  margin-top: 40px;
+}
+
+.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,
+.dialog > .d_content_message {
+  width: 100%;
+}
+.dialog > .d_content_message {
+  display: flex;
+  width: 300px;
+  background: #f6f9ff;
+  flex-direction: column;
+  flex-wrap: nowrap;
+  align-items: flex-start;
+  border-radius: 10px;
+  margin: 0 0 0 10px;
+  position: relative;
+}
+.dialog > .d_content_message > div {
+  padding: 5px;
+}
+.nowTime {
+  position: absolute;
+  bottom: -30px;
+  color: #9c9d9f;
+  font-size: 12px;
+}
+.dialog_right > .d_content,
+.dialog_right > .d_content_message {
+  display: flex;
+  flex-direction: column;
+  align-items: flex-end;
+}
+
+.dialog > .d_content > .d_name,
+.dialog > .d_content_message > .d_name {
+  color: #7c7c7c;
+  font-size: 12px;
+  margin-bottom: 5px;
+}
+
+.dialog_right > .d_content > .d_name,
+.dialog_right > .d_content_message > .d_name {
+  text-align: right;
+}
+
+.dialog > .d_content > .d_log,
+.dialog > .d_content_message > .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,
+.dialog > .d_content_message > .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,
+.dialog > .d_content_message > .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,
+.dialog > .d_content_message > .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;
+}
+
+.gotoTalk {
+  margin: 0 5px 10px 5px;
+  background: #3681fc;
+  color: #fff;
+  text-align: center;
+  width: calc(100% - 20px);
+  height: 30px;
+  line-height: 30px;
+  padding: 0 !important;
+  border-radius: 5px;
+  cursor: pointer;
+}
+
+.checkFooter {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: nowrap;
+  align-items: center;
+  justify-content: flex-end;
+}
+.checkFooter > div {
+  min-width: 100px;
+  width: 100px;
+  text-align: center;
+  border-radius: 5px;
+  background: #ffffff;
+  color: #3681fc;
+  border: 1px solid #3681fc;
+  height: 35px;
+  line-height: 35px;
+  margin-right: 10px;
+  font-size: 12px;
+  cursor: pointer;
+}
+
+.reportButton {
+  position: absolute;
+  bottom: -35px;
+  right: 50px;
+  background: #16c267;
+  color: #fff;
+  width: 70px;
+  height: 25px;
+  text-align: center;
+  line-height: 25px;
+  cursor: pointer;
+  border-radius: 5px;
+}
+</style>
+    

+ 206 - 0
src/components/EnglishVoice3/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>
+    

+ 228 - 0
src/components/EnglishVoice3/index.vue

@@ -0,0 +1,228 @@
+<template>
+    <div class="ev_box">
+        <div class="ev_box_left">
+            <left :title="title" :detail="detail" :checkJson="checkJson" :checkType="checkType" @setType="setType">
+            </left>
+        </div>
+        <div class="ev_box_right">
+            <right :checkJson="checkJson" :checkType="checkType" @setType="setType" :work="work" @setWork="setWork">
+            </right>
+        </div>
+        <div class="ev_btn">
+            <el-button type="primary" style="padding: 5px 10px;font-size: 14px;height: 30px;" size="mini" :disabled="!(checkType == (checkJson.length - 1))" @click="addEnglishWork">点击提交</el-button>
+        </div>
+    </div>
+</template>
+
+<script>
+import right from './component/right.vue'
+import left from './component/left.vue'
+
+export default {
+    components: {
+        right,
+        left
+    },
+    props: {
+
+        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();
+        },
+       
+        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);
+                });
+        },
+    },
+
+    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>

+ 538 - 111
src/components/components/choseWorksDetailDialog.vue

@@ -471,27 +471,18 @@
                     <el-button type="primary" @click="addEnglishWork">确 定</el-button>
                   </div>
                 </div>
-                <!-- 英语写  -->
+                <!-- 英语写口语  -->
                 <div v-if="[70].includes(toolType)" class="s_b_m_b_i_m_evaluation">
-                  <!-- <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> -->
+                  <EnglishVoice 
+                  :englishVoiceJson='englishVoiceJson'
+                  :userid="userId"
+                  :id="id"
+                  :courseType="courseType"
+                  :taskCount="taskCount"
+                  :toolindex="toolIndex"
+                  @selectSWorks="selectSWorks"
+                  @selectStudent="selectStudent"
+                  :englishVoiceJsonWork="englishVoiceJsonWork"></EnglishVoice>
                 </div>
 
 
@@ -525,94 +516,364 @@
                   <div>
                     <div
                       class="g_d_box"
-                      v-if="testData.groupJson.group"
+                      v-if="groupJson.group"
                     >
                       <div
                         class="g_d_group"
-                        v-for="(g, gindex) in testData.groupJson.group"
+                        v-for="(g, gindex) in groupJson.group"
                         :key="gindex"
                       >
 
-                      <div class="g_d_group_chair">
-                        <div v-if="testData.groupJson.number > 1">
-                          <span class="isChair">
-                              <span class="updateChairBtn">修改</span>
-                          </span>
-                          <span>空位置</span>
-                        </div>
-                        <div v-if="testData.groupJson.number > 4">
-                          <span class="isChair">
-                              <span class="updateChairBtn">修改</span>
-                          </span>
-                          <span>空位置</span>
-                        </div>
-                        <div v-if="testData.groupJson.number > 8">
-                          <span class="isChair">
-                              <span class="updateChairBtn">修改</span>
-                          </span>
-                          <span>空位置</span>
-                        </div>
-                      </div>
-                      <div class="g_d_group_tableBox">
-                        <div class="g_d_group_chair2">
-                          <div v-if="testData.groupJson.number > 2">
-                            <span class="isChair">
-                              <span class="updateChairBtn">修改</span>
-                            </span>
-                            <span>空位置</span>
-                          </div>
-                          <div v-if="testData.groupJson.number > 6">
-                            <span class="isChair">
-                              <span class="updateChairBtn">修改</span>
-                            </span>
-                            <span>空位置</span>
-                          </div>
-                        </div>
-                        <div class="g_d_group_table">
-                          <div>
-                            {{ g.name }}
-                          </div>
-                          <div>
-                            <div>
-                              加入分组
+                          <div class="g_d_group_chair">
+                            <div v-if="groupJson.number > 1">
+                              <span
+                                :class="{
+                                  isChair:
+                                    groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][0],
+                                  updateChair:
+                                    courseDetail.userid == userId &&
+                                    groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][0] &&
+                                    groupJson.islock == 2,
+                                }"
+                                ><span
+                                  class="updateChairBtn"
+                                  @click="
+                                    updateGroupChair(
+                                      groupStudent[toolIndex][gindex] &&
+                                        groupStudent[toolIndex][gindex][0]
+                                    )
+                                  "
+                                  >修改</span
+                                ></span
+                              ><span>{{
+                                groupStudent[toolIndex][gindex] &&
+                                groupStudent[toolIndex][gindex][0]
+                                  ? groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][0].name
+                                  : "空位置"
+                              }}</span>
+                            </div>
+                            <div v-if="groupJson.number > 4">
+                              <span
+                                :class="{
+                                  isChair:
+                                    groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][4],
+                                  updateChair:
+                                    courseDetail.userid == userId &&
+                                    groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][4] &&
+                                    groupJson.islock == 2,
+                                }"
+                                ><span
+                                  class="updateChairBtn"
+                                  @click="
+                                    updateGroupChair(
+                                      groupStudent[toolIndex][gindex] &&
+                                        groupStudent[toolIndex][gindex][4]
+                                    )
+                                  "
+                                  >修改</span
+                                ></span
+                              ><span>{{
+                                groupStudent[toolIndex][gindex] &&
+                                groupStudent[toolIndex][gindex][4]
+                                  ? groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][4].name
+                                  : "空位置"
+                              }}</span>
+                            </div>
+                            <div v-if="groupJson.number > 8">
+                              <span
+                                :class="{
+                                  isChair:
+                                    groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][8],
+                                  updateChair:
+                                    courseDetail.userid == userId &&
+                                    groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][8] &&
+                                    groupJson.islock == 2,
+                                }"
+                                ><span
+                                  class="updateChairBtn"
+                                  @click="
+                                    updateGroupChair(
+                                      groupStudent[toolIndex][gindex] &&
+                                        groupStudent[toolIndex][gindex][8]
+                                    )
+                                  "
+                                  >修改</span
+                                ></span
+                              ><span>{{
+                                groupStudent[toolIndex][gindex] &&
+                                groupStudent[toolIndex][gindex][8]
+                                  ? groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][8].name
+                                  : "空位置"
+                              }}</span>
                             </div>
                           </div>
-                        </div>
-                        <div class="g_d_group_chair2">
-                          <div v-if="testData.groupJson.number > 3">
-                            <span class="isChair">
-                              <span class="updateChairBtn">修改</span>
-                            </span>
-                            <span>空位置</span>
+                          <div class="g_d_group_tableBox">
+                            <div class="g_d_group_chair2">
+                              <div v-if="groupJson.number > 2">
+                                <span
+                                  :class="{
+                                    isChair:
+                                      groupStudent[toolIndex][gindex] &&
+                                      groupStudent[toolIndex][gindex][2],
+                                    updateChair:
+                                      courseDetail.userid == userId &&
+                                      groupStudent[toolIndex][gindex] &&
+                                      groupStudent[toolIndex][gindex][2] &&
+                                      groupJson.islock == 2,
+                                  }"
+                                  ><span
+                                    class="updateChairBtn"
+                                    @click="
+                                      updateGroupChair(
+                                        groupStudent[toolIndex][gindex] &&
+                                          groupStudent[toolIndex][gindex][2]
+                                      )
+                                    "
+                                    >修改</span
+                                  ></span
+                                ><span>{{
+                                  groupStudent[toolIndex][gindex] &&
+                                  groupStudent[toolIndex][gindex][2]
+                                    ? groupStudent[toolIndex][gindex] &&
+                                      groupStudent[toolIndex][gindex][2].name
+                                    : "空位置"
+                                }}</span>
+                              </div>
+                              <div v-if="groupJson.number > 6">
+                                <span
+                                  :class="{
+                                    isChair:
+                                      groupStudent[toolIndex][gindex] &&
+                                      groupStudent[toolIndex][gindex][6],
+                                    updateChair:
+                                      courseDetail.userid == userId &&
+                                      groupStudent[toolIndex][gindex] &&
+                                      groupStudent[toolIndex][gindex][6] &&
+                                      groupJson.islock == 2,
+                                  }"
+                                  ><span
+                                    class="updateChairBtn"
+                                    @click="
+                                      updateGroupChair(
+                                        groupStudent[toolIndex][gindex] &&
+                                          groupStudent[toolIndex][gindex][6]
+                                      )
+                                    "
+                                    >修改</span
+                                  ></span
+                                ><span>{{
+                                  groupStudent[toolIndex][gindex] &&
+                                  groupStudent[toolIndex][gindex][6]
+                                    ? groupStudent[toolIndex][gindex] &&
+                                      groupStudent[toolIndex][gindex][6].name
+                                    : "空位置"
+                                }}</span>
+                              </div>
+                            </div>
+                            <div class="g_d_group_table">
+                              <div>
+                                {{ g.name }}
+                              </div>
+                              <div>
+                                <div
+                                  v-if="
+                                    courseDetail.userid == userId && groupJson.islock == 2
+                                  "
+                                  @click="deleteGroupChair(gindex)"
+                                >
+                                  移除组员
+                                </div>
+                                <div
+                                  @click="selectGroup(gindex)"
+                                  v-else-if="
+                                    groupStudentUid[toolIndex] &&
+                                    groupStudentUid[toolIndex][gindex].indexOf(userId) == -1
+                                  "
+                                >
+                                  加入分组
+                                </div>
+                                <div
+                                  @click="
+                                    exitGroup(
+                                      groupStudent[toolIndex][gindex] &&
+                                        groupStudent[toolIndex][gindex][
+                                          groupStudentUid[toolIndex][gindex].indexOf(userId)
+                                        ].id
+                                    )
+                                  "
+                                  v-else-if="
+                                    groupStudentUid[toolIndex] &&
+                                    groupStudentUid[toolIndex][gindex].indexOf(userId) != -1
+                                  "
+                                >
+                                  退出分组
+                                </div>
+                              </div>
+                            </div>
+                            <div class="g_d_group_chair2">
+                              <div v-if="groupJson.number > 3">
+                                <span
+                                  :class="{
+                                    isChair:
+                                      groupStudent[toolIndex][gindex] &&
+                                      groupStudent[toolIndex][gindex][3],
+                                    updateChair:
+                                      courseDetail.userid == userId &&
+                                      groupStudent[toolIndex][gindex] &&
+                                      groupStudent[toolIndex][gindex][3] &&
+                                      groupJson.islock == 2,
+                                  }"
+                                  ><span
+                                    class="updateChairBtn"
+                                    @click="
+                                      updateGroupChair(
+                                        groupStudent[toolIndex][gindex] &&
+                                          groupStudent[toolIndex][gindex][3]
+                                      )
+                                    "
+                                    >修改</span
+                                  ></span
+                                ><span>{{
+                                  groupStudent[toolIndex][gindex] &&
+                                  groupStudent[toolIndex][gindex][3]
+                                    ? groupStudent[toolIndex][gindex] &&
+                                      groupStudent[toolIndex][gindex][3].name
+                                    : "空位置"
+                                }}</span>
+                              </div>
+                              <div v-if="groupJson.number > 7">
+                                <span
+                                  :class="{
+                                    isChair:
+                                      groupStudent[toolIndex][gindex] &&
+                                      groupStudent[toolIndex][gindex][7],
+                                    updateChair:
+                                      courseDetail.userid == userId &&
+                                      groupStudent[toolIndex][gindex] &&
+                                      groupStudent[toolIndex][gindex][7] &&
+                                      groupJson.islock == 2,
+                                  }"
+                                  ><span
+                                    class="updateChairBtn"
+                                    @click="
+                                      updateGroupChair(
+                                        groupStudent[toolIndex][gindex] &&
+                                          groupStudent[toolIndex][gindex][7]
+                                      )
+                                    "
+                                    >修改</span
+                                  ></span
+                                ><span>{{
+                                  groupStudent[toolIndex][gindex] &&
+                                  groupStudent[toolIndex][gindex][7]
+                                    ? groupStudent[toolIndex][gindex] &&
+                                      groupStudent[toolIndex][gindex][7].name
+                                    : "空位置"
+                                }}</span>
+                              </div>
+                            </div>
                           </div>
-                          <div v-if="testData.groupJson.number > 7">
-                            <span class="isChair">
-                              <span class="updateChairBtn">修改</span>
-                            </span>
-                            <span>空位置</span>
+                          <div class="g_d_group_chair">
+                            <div v-if="groupJson.number > 1">
+                              <span
+                                :class="{
+                                  isChair:
+                                    groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][1],
+                                  updateChair:
+                                    courseDetail.userid == userId &&
+                                    groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][1] &&
+                                    groupJson.islock == 2,
+                                }"
+                                ><span
+                                  class="updateChairBtn"
+                                  @click="
+                                    updateGroupChair(
+                                      groupStudent[toolIndex][gindex] &&
+                                        groupStudent[toolIndex][gindex][1]
+                                    )
+                                  "
+                                  >修改</span
+                                ></span
+                              ><span>{{
+                                groupStudent[toolIndex][gindex] &&
+                                groupStudent[toolIndex][gindex][1]
+                                  ? groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][1].name
+                                  : "空位置"
+                              }}</span>
+                            </div>
+                            <div v-if="groupJson.number > 5">
+                              <span
+                                :class="{
+                                  isChair:
+                                    groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][5],
+                                  updateChair:
+                                    courseDetail.userid == userId &&
+                                    groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][5] &&
+                                    groupJson.islock == 2,
+                                }"
+                                ><span
+                                  class="updateChairBtn"
+                                  @click="
+                                    updateGroupChair(
+                                      groupStudent[toolIndex][gindex] &&
+                                        groupStudent[toolIndex][gindex][5]
+                                    )
+                                  "
+                                  >修改</span
+                                ></span
+                              ><span>{{
+                                groupStudent[toolIndex][gindex] &&
+                                groupStudent[toolIndex][gindex][5]
+                                  ? groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][5].name
+                                  : "空位置"
+                              }}</span>
+                            </div>
+                            <div v-if="groupJson.number > 9">
+                              <span
+                                :class="{
+                                  isChair:
+                                    groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][9],
+                                  updateChair:
+                                    courseDetail.userid == userId &&
+                                    groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][9] &&
+                                    groupJson.islock == 2,
+                                }"
+                                ><span
+                                  class="updateChairBtn"
+                                  @click="
+                                    updateGroupChair(
+                                      groupStudent[toolIndex][gindex] &&
+                                        groupStudent[toolIndex][gindex][9]
+                                    )
+                                  "
+                                  >修改</span
+                                ></span
+                              ><span>{{
+                                groupStudent[toolIndex][gindex] &&
+                                groupStudent[toolIndex][gindex][9]
+                                  ? groupStudent[toolIndex][gindex] &&
+                                    groupStudent[toolIndex][gindex][9].name
+                                  : "空位置"
+                              }}</span>
+                            </div>
                           </div>
-                        </div>
-                      </div>
-                      <div class="g_d_group_chair">
-                        <div v-if="testData.groupJson.number > 1">
-                          <span class="isChair">
-                              <span class="updateChairBtn">修改</span>
-                          </span>
-                          <span>空位置</span>
-                        </div>
-                        <div v-if="testData.groupJson.number > 5">
-                          <span class="isChair">
-                              <span class="updateChairBtn">修改</span>
-                          </span>
-                          <span>空位置</span>
-                        </div>
-                        <div v-if="testData.groupJson.number > 9">
-                          <span class="isChair">
-                              <span class="updateChairBtn">修改</span>
-                          </span>
-                          <span>空位置</span>
-                        </div>
-                      </div>
                       </div>
                     </div>
                   </div>
@@ -883,6 +1144,7 @@ import eChartsView from './eChartsView.vue'
 import choseWordCloud from './choseWordCloud.vue'
 import studentWorkPreviewDialog from './studentWorkPreviewDialog.vue';
 import EditorBar from "../tools/wangEnduit.vue";
+import EnglishVoice from '../EnglishVoice3/index.vue'
 
 import onlineWrite from "./onlineWrite";
 
@@ -895,7 +1157,8 @@ export default {
     choseWordCloud,
     studentWorkPreviewDialog,
     EditorBar,
-    onlineWrite
+    onlineWrite,
+    EnglishVoice
 	},
   props: {
     worksStudentData: {
@@ -908,7 +1171,11 @@ export default {
     org: {
       type: String
     },
-    groupStudent: {
+    groupStudent2: {
+      type: Array,
+      default: () => []
+    },
+    groupStudentUid2: {
       type: Array,
       default: () => []
     },
@@ -951,9 +1218,15 @@ export default {
       showType: 0,
       myAnswerList1:{}, //英语写作
       englishList:{},//英语写作
+      englishVoiceJsonWork:[], //英语口语
+      englishVoiceJson:{}, //英语口语
+      groupJson:[], //学生分组
+      groupStudentUid:[], //学生分组
+      groupStudent:[], //学生分组
       userId: this.$route.query.userid,
       courseId:this.$route.query.courseId,
       id: this.$route.query.courseId,
+      classid: this.$route.query.cid,
       wordCloudData:[], //词云数据
       dialogVisibleWordCloud:false,
 			showStatisticsType:0,
@@ -971,7 +1244,7 @@ export default {
         worksPreview: false
       },
       dialogTypeList: [
-        { label: "作业详细", value: 0,showType:[1,7,15,40,41,45,47,48,49,52,3,4,57,18,21,71,69], loading: false },
+        { label: "作业详细", value: 0,showType:[1,7,15,40,41,45,47,48,49,52,3,4,57,18,21,71,69,70], loading: false },
         { label: "题目统计", value: 1,showType:[45,15], loading: false },
         { label: "学生统计", value: 2,showType:[1,7,15,40,41,45,47,48,52,3,4,57], loading: false }
       ],
@@ -980,6 +1253,7 @@ export default {
 				{label:"训练平台",value:18,img:require("../../assets/icon/thirdToolList/trainPlatform.png")},
 				{label:"AIoT Blockly",value:21,img:require("../../assets/icon/fourthToolList/program.png")},
 				{label:"英语写作",value:69,img:require("../../assets/icon/secondToolList/english.png")},
+				{label:"英语口语",value:70,img:require("../../assets/icon/thirdToolList/englishVoice.png")},
 				{label:"AI智能体",value:71,img:require("../../assets/icon/fourthToolList/chatgpt.png")},
 				{label:"CocoPi",value:57,img:require("../../assets/icon/fourthToolList/cocopi.png")},
 				{label:"问卷调查",value:4,img:require("../../assets/icon/thirdToolList/ask.png")},
@@ -1233,10 +1507,9 @@ export default {
   watch: {
     worksStudentData: {
       handler(newValue) {
-
-
-        if (this.showType != 2) return 
-           
+     
+        if (this.toolIndex == null) return 
+   
         let workSData = []
 
         // 工具几的学生作业
@@ -1374,6 +1647,130 @@ export default {
       console.log('分组设置');
       
     },
+
+    // 加入分组前检查人员是否已经满了
+    selectGroup(index) {
+      
+        //教师查看全部作业
+        let params = {
+          cid: this.id,
+          s: this.courseType,
+          t: this.taskCount,
+        };
+        this.ajax
+          .get(this.$store.state.api + "selectSWorks", params)
+          .then((res) => {
+            var workS = res.data[3];
+            // 每组人数
+            let GroupSize = this.groupJson.number
+            
+            // 当前组人数
+            let nowSize = 0
+            
+            console.log('groupJson',this.groupJson);
+            
+            console.log('workS',workS);
+
+            let classID = [];
+            if (this.classid != '') {
+              classID = this.classid.split(',')
+            }
+            console.log('classID',classID);
+
+            workS.forEach(e=>{
+              if (index == e.group) {
+                if (!e.classid || classID.length == 0) {
+                  let classID2 = e.classid.split(',')
+                  const result = this.hasOverlap(classID,classID2);
+                  if (result) {
+                    nowSize++
+                  }
+                }else{
+                  nowSize++
+                }
+              }
+            })
+
+            console.log("nowSize",nowSize);
+            
+
+
+
+          })
+          .catch((err) => {
+            console.error(err);
+          });
+          
+
+    },
+    // 学生分组判断与学生一个班级的学生
+    hasOverlap(arr1, arr2) {
+      const set1 = new Set(arr1);
+      for (let item of arr2) {
+        if (set1.has(item)) {
+          return true;
+        }
+      }
+      return false;
+    },
+    joinGroup(index) {
+      if (this.groupJson.islock == 2) {
+        this.$message.error("位置已被锁定,无法加入");
+        return;
+      }
+      if (
+        this.groupStudent[this.toolIndex][index].length >
+          this.groupJson.number ||
+        this.groupStudent[this.toolIndex][index].length == this.groupJson.number
+      ) {
+        this.$message.error("位置已满,无法加入");
+        return;
+      }
+      let params = [
+        {
+          cid: this.id,
+          stage: this.courseType,
+          task: this.taskCount,
+          tool: this.toolIndex,
+          g: index,
+          uid: this.userId,
+        },
+      ];
+      this.ajax
+        .post(this.$store.state.api + "joinGroup", params)
+        .then((res) => {
+          this.$message.success("加入成功");
+          this.$emit("getCourseDetail",2)
+        })
+        .catch((err) => {
+          this.$message.error("网络不佳");
+          console.error(err);
+        });
+    },
+
+    // 退出分组
+    exitGroup(id) {
+      if (this.groupJson.islock == 2) {
+        this.$message.error("位置已被锁定,无法退出");
+        return;
+      }
+      let params = [
+        {
+          gid: id,
+        },
+      ];
+
+      this.ajax
+        .post(this.$store.state.api + "exitGroup", params)
+        .then((res) => {
+          this.$message.success("退出成功");
+          this.$emit.getCourseDetail(2);
+        })
+        .catch((err) => {
+          this.$message.error("网络不佳");
+          console.error(err);
+        });
+    },
     // 提交Cocopi作业
     upLoadCocoPi(){
 
@@ -1641,6 +2038,12 @@ export default {
           });
       
     },
+    selectSWorks(){
+      this.$emit("selectSWorks")
+    },
+    selectStudent(){
+      this.$emit("selectStudent")
+    },
     // 排序操作答案
     sentenceBtn2(val,val1){
       let yym = this.testData.sentenceList[val].addSentence
@@ -1836,24 +2239,48 @@ export default {
                 this.radioS.push('')
               }
             })
-          } else if (this.toolType == 69) {
+          }else if (this.toolType == 69) {
             console.log('this.testJsonCopy',this.testJsonCopy);
             
-
             // if (this.testJsonCopy.length) {
             //     this.myAnswerList1 = JSON.parse(this.testJsonCopy[0].works)
             // } else {
-                this.myAnswerList1 = {engTitle: "",engText: "",imgList: [],};
+            this.myAnswerList1 = {engTitle: "",engText: "",imgList: [],};
             // }
 
-            console.log('this.testData',this.testData);
-            
-
+            // console.log('this.testData',this.testData);
 
             this.englishList = this.testData.englishList
               ? this.testData.englishList
               : {};
+          }else if (this.toolType == 70) {  //英语口语
+
+            if (this.testJsonCopy.length) {
+                  this.englishVoiceJsonWork = JSON.parse(this.testJsonCopy.works)
+            } else {
+              this.englishVoiceJsonWork = [];
+            }
+
+            console.log('this.testData',this.testData);
+            
+            let englishVoiceJson = {}
+            englishVoiceJson = this.testData.englishVoiceJson
+              ? this.testData.englishVoiceJson
+              : {};
+
+            this.englishVoiceJson = JSON.parse(JSON.stringify(englishVoiceJson));
+          }else if (this.toolType == 49) {
+            this.groupStudent=this.groupStudent2
+            this.groupStudentUid=this.groupStudentUid2
+
+            // console.log('this.groupStudent',this.groupStudent);
+            // console.log('groupStudentUid',this.groupStudentUid);
+
+            // console.log('this.testData',this.testData);
+            
 
+            this.groupJson = this.testData.groupJson;
+            this.islock = this.groupJson.islock == 1 ? true : false;
           }
       }
     },

+ 4 - 0
src/components/easy2/studyStudent.vue

@@ -1505,6 +1505,10 @@
                               alt
                             />
                             <div style="margin: 5px 0">英语口语</div>
+                            <div class="upload_toolBtn"  @click="openChoseWorksDetailDialog(tooC,toolIndex,taskCount,70)"
+                            style="position: absolute;right: 33px;top: -30px;">
+                              投影
+                            </div>
                           </div>
                         </div>
                       </div>

+ 7 - 1
src/components/easy3/studyStudent.vue

@@ -1438,6 +1438,10 @@
                               alt
                             />
                             <div style="margin: 5px 0">英语口语</div>
+                            <div class="upload_toolBtn"  @click="openChoseWorksDetailDialog(tooC,toolIndex,taskCount,70)"
+                            style="position: absolute;right: 33px;top: -30px;">
+                              投影
+                            </div>
                           </div>
                         </div>
                       </div>
@@ -12067,9 +12071,11 @@
       :tType="tType"
       :sIsOpen="sIsOpen"
       :courseDetail="courseDetail"
-      :groupStudent="groupStudent"
+      :groupStudent2="groupStudent"
+      :groupStudentUid2="groupStudentUid"
       :oid="oid"
       :org="org"
+      @getCourseDetail="getCourseDetail"
       @selectSLook="selectSLook"
       @selectSWorks="selectSWorks"
       @selectStudent="selectStudent"

+ 4 - 0
src/components/studyStudent.vue

@@ -1400,6 +1400,10 @@
                               alt
                             />
                             <div style="margin: 5px 0">英语口语</div>
+                            <div class="upload_toolBtn"  @click="openChoseWorksDetailDialog(tooC,toolIndex,taskCount,70)"
+                            style="position: absolute;right: 33px;top: -30px;">
+                              投影
+                            </div>
                           </div>
                         </div>
                       </div>

+ 4 - 0
src/components/studySutdentClass/studyStudent.vue

@@ -1458,6 +1458,10 @@
                               alt
                             />
                             <div style="margin: 5px 0">英语口语</div>
+                            <div class="upload_toolBtn"  @click="openChoseWorksDetailDialog(tooC,toolIndex,taskCount,70)"
+                            style="position: absolute;right: 33px;top: -30px;">
+                              投影
+                            </div>
                           </div>
                         </div>
                       </div>