Ver Fonte

feat(录音转文字): 新增微软语音转文字功能及界面交互

添加录音转文字功能,包括开始/结束录音按钮、状态管理及样式
实现通过iframe嵌入微软语音转文字服务,处理转译结果并存储
新增录音数据存储逻辑和全局对象管理
lsc há 1 semana atrás
pai
commit
3e5b400aee
1 ficheiros alterados com 191 adições e 4 exclusões
  1. 191 4
      src/components/pptEasyClass/index.vue

+ 191 - 4
src/components/pptEasyClass/index.vue

@@ -1,6 +1,13 @@
 <template>
 <template>
   <div class="pptEasyClass">
   <div class="pptEasyClass">
     <div class="pec_main" v-loading="pageLoading">
     <div class="pec_main" v-loading="pageLoading">
+      <!-- 录音转文字 -->
+      <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 class="pec_header">
       <div class="pec_header">
         <div class="pec_h_left">
         <div class="pec_h_left">
           <div @click.stop="back" class="backBtn" v-if="screenType != 2 || tType == 1">
           <div @click.stop="back" class="backBtn" v-if="screenType != 2 || tType == 1">
@@ -44,10 +51,9 @@
         </div>
         </div>
         <div class="pec_h_right">
         <div class="pec_h_right">
           <div class="pec_h_r_btnArea">
           <div class="pec_h_r_btnArea">
-            <!-- <div class="pec_h_r_btn_refresh" @click="refreshCourse">
-              <img src="../../assets/icon/newIcons/refresh.png" alt="" />
-              <span>刷新</span>
-            </div> -->
+            <div class="pec_h_r_btn_refresh" :class="{ 'recording': recordedForm.status == 1 }" @click="toggleRecording" v-show="(jArray.includes(oid) || jArray.includes(org)) && courseDetail.userid == userid">
+              <span>{{ recordedForm.status == 1 ? '结束录音' : '开始录音' }}</span>
+            </div>
             <div class="pec_h_r_btn_afterClass" @click="afterClass" v-if="courseDetail.userid == userid">
             <div class="pec_h_r_btn_afterClass" @click="afterClass" v-if="courseDetail.userid == userid">
               <img src="../../assets/icon/newIcon/afterClass.svg" alt="" />
               <img src="../../assets/icon/newIcon/afterClass.svg" alt="" />
               <span>下课</span>
               <span>下课</span>
@@ -95,6 +101,29 @@ export default {
       startTime: "",
       startTime: "",
       freeBrowse: true, // 默认自由浏览
       freeBrowse: true, // 默认自由浏览
       opertimer: null, // 定时器
       opertimer: null, // 定时器
+      jArray: [],
+      // 录音相关变量
+      languageRadio: 2, // 语言选择
+      recordedForm: {
+        status: 0, // 0: 未开始, 1: 录音中, 2: 暂停, 3: 结束
+        startTime: 0,
+        endTime: 0,
+        timeDuration: 0,
+        textList: [],
+        audioBlob: []
+      },
+      controlsStatus: 0, // 控制状态
+      showIndexPage: true, // 显示索引页
+      pageStatus: 1, // 页面状态
+      editorBarData: {
+        type: "0",
+        content: ""
+      },
+      uploadFileLoading: false, // 上传文件加载状态
+      transcriptionData: {
+        content: ""
+      },
+      showGetTextLoading: false, // 显示获取文本加载状态
     };
     };
   },
   },
   methods: {
   methods: {
@@ -104,6 +133,136 @@ export default {
     refreshCourse() {
     refreshCourse() {
       this.getCourseDetail();
       this.getCourseDetail();
     },
     },
+    audioStart(){
+      this.onStartRecordWithMicrosoft();
+    },
+    toggleRecording() {
+      if (this.recordedForm.status == 1) {
+        this.onFinishRecordWithMicrosoft();
+      } else {
+        this.onStartRecordWithMicrosoft();
+      }
+    },
+    // ============ start 微软录音转译
+    onStartRecordWithMicrosoft() {
+      let iiframe = this.$refs["iiframe"];
+      iiframe.contentWindow.window.document.getElementById(
+        "languageOptions"
+      ).selectedIndex = this.languageRadio;
+
+      // 录音开始
+      let flag = true;
+      console.log("开始录音", iiframe);
+      this.recordedForm.status = 1;
+      iiframe.contentWindow.window.onRecognizedResult = e => {
+        console.log("onRecognizedResult", e);
+        this.recordedForm.endTime = this.recordedForm.timeDuration;
+        if (flag) {
+          this.controlsStatus = 1;
+          this.showIndexPage = false;
+          this.pageStatus = 2;
+          this.editorBarData.type = "0";
+          flag = false;
+          this.uploadFileLoading = false;
+          this.transcriptionData.content = "";
+          this.editorBarData.content = "";
+          this.recordedForm.textList = [];
+        }
+        this.showGetTextLoading = true;
+        let privText = e.privText;
+        let privSpeakerId = e.privSpeakerId;
+        let _copyPrivSpeakerId = privSpeakerId;
+        console.log("👇转译对象👇");
+        console.log(e);
+        console.log("👇转译结果👇");
+        console.log(privText);
+        if (!privText || !privSpeakerId || privSpeakerId == "Unknown") {
+          return;
+        }
+
+        const newItem = {
+          value: privText,
+          role: "",
+          startTime: this.updateRecordedTime({
+            duration: this.recordedForm.startTime
+          }),
+          endTime: this.updateRecordedTime({
+            duration: this.recordedForm.endTime
+          }),
+          time: this.updateRecordedTime({
+            duration: this.recordedForm.endTime - this.recordedForm.startTime
+          })
+        };
+        this.recordedForm.textList.push(newItem);
+        this.recordedForm.startTime = this.recordedForm.timeDuration + 1;
+        this.transcriptionData.content +=
+          _copyPrivSpeakerId + ":" + privText + "\n";
+        this.onRecordAddLine(newItem);
+      };
+
+      iiframe.contentWindow.ConversationTranscriber();
+    },
+    onFinishRecordWithMicrosoft() {
+      if (this.recordedForm.status == 1) {
+        //正在录音时
+        let iiframe = this.$refs["iiframe"];
+        iiframe.contentWindow.window.document
+          .getElementById("scenarioStopButton")
+          .click();
+        // 录音借宿
+        iiframe.contentWindow.onSessionStopped = (s, e) => {
+          this.recordedForm.status = 0;
+          this.controlsStatus = 2;
+          this.showGetTextLoading = false;
+          this.$message.success("已结束录音");
+          console.log("结束录音👇");
+          console.log("结束录音", e);
+          this.recordedForm.audioBlob.push(e.preaudio);
+          let blob = new Blob(this.recordedForm.audioBlob, {
+            type: "audio/wav"
+          });
+          let file = new File([blob], "recordedFile.wav", {
+            type: "audio/wav"
+          });
+          // 存储文件和文本到全局对象
+          this.storeRecordingData(file);
+          iiframe.contentWindow.onSessionStopped = null;
+          iiframe.contentWindow.window.onRecognizedResult = null;
+        };
+      } else if (this.recordedForm.status == 2) {
+        //暂停录音时
+        this.recordedForm.status = 0;
+        this.controlsStatus = 2;
+        this.showGetTextLoading = false;
+        let blob = new Blob(this.recordedForm.audioBlob, {
+          type: "audio/wav"
+        });
+        let file = new File([blob], "recordedFile.wav", { type: "audio/wav" });
+        // 存储文件和文本到全局对象
+        this.storeRecordingData(file);
+      }
+    },
+    storeRecordingData(file) {
+      // 配置全局 window 对象存储录音数据
+      if (!window.recordingData) {
+        window.recordingData = {};
+      }
+      window.recordingData.file = file;
+      window.recordingData.text = this.transcriptionData.content;
+      window.recordingData.textList = this.recordedForm.textList;
+      console.log("录音数据已存储到全局对象:", window.recordingData);
+    },
+    updateRecordedTime({ duration }) {
+      // 格式化录音时间
+      const minutes = Math.floor(duration / 60);
+      const seconds = Math.floor(duration % 60);
+      return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
+    },
+    onRecordAddLine(item) {
+      // 添加录音文本行
+      console.log("添加录音文本行:", item);
+      // 这里可以根据需要添加更多处理逻辑
+    },
     getCourseDetail() {
     getCourseDetail() {
       this.pageLoading = true;
       this.pageLoading = true;
       let params = {
       let params = {
@@ -264,6 +423,15 @@ export default {
           console.error(err);
           console.error(err);
         });
         });
     },
     },
+    getAIJ(){
+			this.ajax.get(this.$store.state.api+"getAIJ","").then(res=>{
+				let a = res.data[4];
+        console.log(a)
+				let Array = [];
+				a.forEach(i=>Array.push(i.oid))
+				this.jArray = Array;
+			})
+		},
   },
   },
   destroyed() {
   destroyed() {
     clearInterval(this.opertimer);
     clearInterval(this.opertimer);
@@ -292,6 +460,7 @@ export default {
       timeZone: "Asia/Shanghai"
       timeZone: "Asia/Shanghai"
     }).replace(/\//g, "-")
     }).replace(/\//g, "-")
     this.getClassName()
     this.getClassName()
+    this.getAIJ();
     this.getCourseDetail();
     this.getCourseDetail();
     this.setOperationTime();
     this.setOperationTime();
     window.onFreeBrowseChange = (value) => {
     window.onFreeBrowseChange = (value) => {
@@ -415,6 +584,24 @@ export default {
   border-color: #0061ff;
   border-color: #0061ff;
 }
 }
 
 
+.pec_h_r_btnArea>.pec_h_r_btn_refresh.recording {
+  background-color: #F53F3F;
+  border-color: #F53F3F;
+  animation: pulse 1.5s infinite;
+}
+
+@keyframes pulse {
+  0% {
+    box-shadow: 0 0 0 0 rgba(245, 63, 63, 0.4);
+  }
+  70% {
+    box-shadow: 0 0 0 10px rgba(245, 63, 63, 0);
+  }
+  100% {
+    box-shadow: 0 0 0 0 rgba(245, 63, 63, 0);
+  }
+}
+
 .pec_h_r_btnArea>.pec_h_r_btn_afterClass {
 .pec_h_r_btnArea>.pec_h_r_btn_afterClass {
   border-color: #F0E1DD;
   border-color: #F0E1DD;
   background-color: #FFF7F5;
   background-color: #FFF7F5;