Browse Source

Merge branch 'beta'

SanHQin 1 day ago
parent
commit
31e2665412

+ 1 - 1
dist/index.html

@@ -32,7 +32,7 @@
     html,
     body{
       font-family: '黑体';
-    }</style><link href=./static/css/app.2c466d06caba6d8776e8bf82282bb49a.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.3ad1d5771e9b13dbdad2.js></script><script type=text/javascript src=./static/js/vendor.becc9317549795ada446.js></script><script type=text/javascript src=./static/js/app.9619376fb75e883693bf.js></script></body></html><script>function stopSafari() {
+    }</style><link href=./static/css/app.3c9f12166a90643c57c499fb6f4e36c1.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.3ad1d5771e9b13dbdad2.js></script><script type=text/javascript src=./static/js/vendor.becc9317549795ada446.js></script><script type=text/javascript src=./static/js/app.7339cdf41a5072f02476.js></script></body></html><script>function stopSafari() {
     //阻止safari浏览器双击放大功能
     let lastTouchEnd = 0  //更新手指弹起的时间
     document.documentElement.addEventListener("touchstart", function (event) {

File diff suppressed because it is too large
+ 0 - 0
dist/static/css/app.3c9f12166a90643c57c499fb6f4e36c1.css


File diff suppressed because it is too large
+ 0 - 0
dist/static/css/app.3c9f12166a90643c57c499fb6f4e36c1.css.map


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/app.7339cdf41a5072f02476.js


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/app.7339cdf41a5072f02476.js.map


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/manifest.3ad1d5771e9b13dbdad2.js.map


+ 2 - 2
src/components/courseDetail.vue

@@ -82,14 +82,14 @@
                 <div
                   style="display: flex; align-items: center; margin-left: 32px"
                 >
-                  <div style="color: #717c8d; font-weight: 400">{{ lang.ssParticipants }}</div>
+                  <div style="color: #717c8d; font-weight: 400">{{ lang.ssParticipants }}:</div>
                   <!-- <div class="man">
                     <img src="../assets/people.png" alt />
                   </div> -->
                   <div class="person" style="font-weight: bold; color: #0e1e33">
                     {{
                       courseDetail.vcount != null ? courseDetail.vcount : 0
-                    }} {{ lang.ssPerson }}
+                    }} <span v-if="lang.lang != 'com'">{{ lang.ssPerson }}</span>
                   </div>
                 </div>
               </div>

+ 30 - 6
src/components/index.vue

@@ -379,6 +379,29 @@ export default {
           console.error(err);
         });
     },
+    //获取banner
+    getbanndBanner() {
+      // const loading = this.openLoading(document.querySelector(".main_box"));
+      let params = {
+        oid: this.oid,
+        lang: this.lang.lang,
+      };
+      this.ajax
+        .get(this.$store.state.api + "selectBarOid", params)
+        .then((res) => {
+          console.log(res);
+          if (res.data[0].length > 0) {
+            this.bannerList = res.data[0];
+          }else if(res.data[1].length > 0){
+            this.bannerList = res.data[1];
+          }else{
+            this.bannerList = res.data[2];
+          }
+        })
+        .catch((err) => {
+          console.error(err);
+        });
+    },
     getCourse(typeName, ftypeId, typeid, type) {
       this.page = 1;
       this.zoneClass = [];
@@ -677,9 +700,9 @@ export default {
           // this.selectType({
           //   data: [res.data[1], res.data[2], res.data[3], res.data[4]],
           // });
-          this.getBanner({
-            data: [res.data[6], res.data[5], res.data[7]],
-          });
+          // this.getBanner({
+          //   data: [res.data[6], res.data[5], res.data[7]],
+          // });
         })
         .catch((err) => {
           console.error(err);
@@ -717,9 +740,9 @@ export default {
           // this.selectType({
           //   data: [res.data[1], res.data[2], res.data[3], res.data[4]],
           // });
-          this.getBanner({
-            data: [res.data[6], res.data[5], res.data[7]],
-          });
+          // this.getBanner({
+          //   data: [res.data[6], res.data[5], res.data[7]],
+          // });
         })
         .catch((err) => {
           console.error(err);
@@ -972,6 +995,7 @@ export default {
     let typea = this.$route.query.typea
     let typeb = this.$route.query.typeb
     let typed = this.$route.query.typed
+    this.getbanndBanner()
     if(this.oidArray.indexOf(this.oid) !== -1){
       this.typeCheck2 = true
     }

+ 234 - 34
src/components/pptEasyClass/index.vue

@@ -23,10 +23,18 @@
           </div>
         </div>
         <div class="pec_h_center">
-          <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 && tcid">
+          <el-tooltip effect="dark" :content="lang.ssRefresh" placement="bottom">
+            <div class="refresh_icon" @click="refreshCourse">
+                <img src="../../assets/icon/course/refresh-2.svg" />
+            </div>
+          </el-tooltip>
+          <el-tooltip class="item" effect="dark" :content="recordedForm.status == 1 ? lang.ssFinishClassRecording : lang.ssBeginClassRecording" placement="bottom" v-show="(jArray.includes(oid) || jArray.includes(org)) && courseDetail.userid == userid && tcid">
+            <div class="pec_h_r_btn_refresh" :class="{ 'recording': recordedForm.status == 1 }" @click="toggleRecording">
             <svg t="1772588344140"  viewBox="0 0 1024 1024" p-id="1693" width="200" height="200"><path d="M512 1024a512.568889 512.568889 0 0 1-512-512 512.625778 512.625778 0 0 1 512-512 512.568889 512.568889 0 0 1 512 512 512.568889 512.568889 0 0 1-512 512zM512 73.329778c-241.948444 0-438.670222 196.835556-438.670222 438.670222S270.051556 950.670222 512 950.670222s438.670222-196.835556 438.670222-438.670222S753.948444 73.329778 512 73.329778z m0 686.592a245.191111 245.191111 0 1 1 0-490.382222 245.191111 245.191111 0 0 1 0 490.382222z" p-id="1694"></path></svg>
               <span>{{ recordedForm.status == 1 ? lang.ssStopRecording2 : lang.ssRecord }}</span>
           </div>
+    </el-tooltip>
+
           <el-tooltip effect="dark" :content="courseDetail.title" placement="bottom">
             <div class="pec_h_l_title">
               <span>{{ courseDetail.title }}</span>
@@ -47,20 +55,22 @@
           <div class="free-browse-switch" v-if="tType == 2">
             <span class="switch-label" :class="{ active: freeBrowse }">{{ freeBrowse ? lang.ssFreeBrowse : lang.ssFollowMode }}</span>
           </div>
-          <el-tooltip effect="dark" :content="lang.ssRefresh" placement="bottom">
-            <div class="refresh_icon" @click="refreshCourse">
-                <img src="../../assets/icon/course/refresh-2.svg" />
-            </div>
-          </el-tooltip>
+
         </div>
         <div class="pec_h_right">
           <div class="pec_h_r_btnArea">
             <!-- openObserveDialog -->
             <!-- toggleRecording -->
+
+            <div class="pec_h_r_btn_uploadVoiceBtn" @click="uploadVoiceBtn" v-if="courseDetail.userid == userid" v-show="false">
+              <span>{{ lang.ssUploadRecordingFile }}</span>
+            </div>
+
             <div class="pec_h_r_btn_afterClass" @click="afterClass" v-if="courseDetail.userid == userid">
               <img src="../../assets/icon/newIcon/afterClass.svg" alt="" />
               <span>{{ lang.ssEndClass }}</span>
             </div>
+
             <div class="name_box" v-if="tType == 2">
               {{ userJson.username }}
             </div>
@@ -164,6 +174,7 @@ export default {
       this.onStartRecordWithMicrosoft();
     },
     toggleRecording() {
+
       if (this.recordedForm.status == 1) {
         // 检查录音时间是否至少为5秒
         const now = new Date();
@@ -198,6 +209,7 @@ export default {
         }
         this.onStartRecordWithMicrosoft();
       }
+    // })
     },
     // ============ start 微软录音转译
     onStartRecordWithMicrosoft() {
@@ -262,8 +274,32 @@ export default {
       };
 
       iiframe.contentWindow.ConversationTranscriber();
+      setTimeout(()=>{
+        navigator.permissions && navigator.permissions.query({ name: 'microphone' }).then(permissionStatus => {
+        if (permissionStatus.state !== "granted") {
+          // 没有开启录音权限,直接确定停止录音
+          this.recordedForm.status = "0";
+          let iframe = this.$refs["iiframe"];
+          iframe.contentWindow.onSessionStopped = null;
+          iframe.contentWindow.window.onRecognizedResult = null;
+          this.$message.success(this.lang.ssNoPermStop);
+          return;
+        }
+      })
+      },10000)
+
     },
-    onFinishRecordWithMicrosoft() {
+    async onFinishRecordWithMicrosoft() {
+      navigator.permissions && navigator.permissions.query({ name: 'microphone' }).then(async permissionStatus => {
+        if (permissionStatus.state !== "granted") {
+          // 没有开启录音权限,直接确定停止录音
+          this.recordedForm.status = "0";
+          let iframe = this.$refs["iiframe"];
+          iframe.contentWindow.onSessionStopped = null;
+          iframe.contentWindow.window.onRecognizedResult = null;
+          this.$message.success(this.lang.ssNoPermStop);
+          return;
+        }
       if (this.recordedForm.status == 1) {
         //正在录音时
         let iiframe = this.$refs["iiframe"];
@@ -271,16 +307,12 @@ export default {
           .getElementById("scenarioStopButton")
           .click();
         // 录音借宿
-        iiframe.contentWindow.onSessionStopped = (s, e) => {
+        iiframe.contentWindow.onSessionStopped = async (s, e) => {
           this.recordedForm.status = 0;
           this.controlsStatus = 2;
           this.showGetTextLoading = false;
           // this.$message.success("已结束录音");
-          this.$message({
-            dangerouslyUseHTMLString: true,
-            customClass:"pptEasyClassMessage",
-            message: `${this.lang.ssStoppedRecording} <p style="color:#3AB855;text-decoration: underline;cursor: pointer;float:right;margin-left:10px" target="_blank">${this.lang.ssViewRecordingResult}</p>`
-          });
+
           console.log("结束录音👇");
           console.log("结束录音", e);
           this.recordedForm.audioBlob.push(e.preaudio);
@@ -291,14 +323,14 @@ export default {
             type: "audio/wav"
           });
           // 存储文件和文本到全局对象
-          this.storeRecordingData(file);
+          await this.storeRecordingData(file);
           // 记录结束录音时间
           this.recordingEndTime = new Date().toLocaleString("zh-CN", {
             hour12: false,
             timeZone: "Asia/Shanghai"
           }).replace(/\//g, "-");
           // 调用 addPPTClass 接口
-          this.addPPTClass();
+          this.addPPTClass(file);
           iiframe.contentWindow.onSessionStopped = null;
           iiframe.contentWindow.window.onRecognizedResult = null;
         };
@@ -312,31 +344,151 @@ export default {
         });
         let file = new File([blob], "recordedFile.wav", { type: "audio/wav" });
         // 存储文件和文本到全局对象
-        this.storeRecordingData(file);
+        await this.storeRecordingData(file);
         // 记录结束录音时间
         this.recordingEndTime = new Date().toLocaleString("zh-CN", {
           hour12: false,
           timeZone: "Asia/Shanghai"
         }).replace(/\//g, "-");
         // 调用 addPPTClass 接口
-        this.addPPTClass();
+        this.addPPTClass(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);
+      return new Promise((resolve)=>{
+        if (!window.recordingData) {
+          window.recordingData = {};
+        }
+        window.recordingData.file = file;
+        window.recordingData.text = this.transcriptionData.content;
+        window.recordingData.textList = this.recordedForm.textList;
+        // 将录音文件转为base64并存入localStorage
+        // const reader = new FileReader();
+        // reader.onload = function(e) {
+        //   const base64data = e.target.result;
+        //   try {
+        //     localStorage.setItem("recordedFileBase64", base64data);
+        //     localStorage.setItem('recordedFileName', file.name);
+        //     localStorage.setItem('recordedFileType', file.type);
+        //     resolve(true)
+        //     console.log("录音数据已存储到全局对象:", window.recordingData);
+        //   } catch (err) {
+        //     resolve(false)
+        //     console.error("localStorage存储base64文件失败:", err);
+        //   }
+        // };
+        // reader.readAsDataURL(file);
+      })
     },
-    openObserveDialog(pptid) {
-      this.observeDialogUrl = `https://observe.cocorobo.cn/#/newClassroom?userid=${this.userid}&oid=${this.oid}&org=${this.org}&pptid=${pptid}`;
-      this.showObserveDialog = true;
+    openObserveDialog(pptid,file) {
+      // this.observeDialogUrl = `https://observe.cocorobo.cn/#/newClassroom?userid=${this.userid}&oid=${this.oid}&org=${this.org}&pptid=${pptid}`;
+      // this.showObserveDialog = true;
+
+      const url = `https://observe.cocorobo.cn/#/newClassroom?userid=${this.userid}&oid=${this.oid}&org=${this.org}&pptid=${pptid}`;
+
+      const _pageWindow = window.open(url, '_blank');
+
+      // if(!_pageWindow){
+      //   alert('浏览器弹窗被拦截,请允许弹窗后重试');
+      //   return;
+      // }
+      const sendData = {
+        type: 'fileData',
+        file,
+      }
+      const sendMessageToVue3 = () => {
+        _pageWindow.postMessage(
+          sendData,
+          '*' // targetOrigin:必须是Vue3的实际域名,不要用*(安全)
+        );
+      };
+
+      const handleVue3Ready = (e) => {
+        // 校验消息来源(安全:只处理Vue3域名的消息)
+
+        // 校验消息类型(确认是Vue3的就绪通知)
+        if (e.data.type === 'READY') {
+          console.log('已就绪,开始发送数据');
+          sendMessageToVue3();
+        }
+
+        // 校验Vue3的"接收成功"确认(可选,进一步确保送达)
+        if (e.data.type === 'DATA_RECEIVED') {
+          console.log('数据已被接收');
+          // 收到确认后移除监听,避免内存泄漏
+          window.removeEventListener('message', handleVue3Ready);
+        }
+      };
+      window.addEventListener('message', handleVue3Ready);
+      const timeoutTimer = setTimeout(() => {
+        console.log('未收到就绪通知,主动发送数据');
+        sendMessageToVue3();
+        // 移除监听(兜底后清理)
+        window.removeEventListener('message', handleVue3Ready);
+      }, 5000);
+
+      const checkWinClosed = setInterval(() => {
+      if (_pageWindow.closed) {
+        clearTimeout(timeoutTimer);
+        clearInterval(checkWinClosed);
+        window.removeEventListener('message', handleVue3Ready);
+      }
+    }, 1000);
+      // 优化:仅通过轮询方式检查窗口是否加载完成并发送消息,被打开页面无需做任何处理
+      // const sendFileData = () => {
+      //   if (_pageWindow && !_pageWindow.closed) {
+      //     _pageWindow.postMessage(
+      //       {
+      //         type: 'fileData',
+      //         file,
+      //       },
+      //       '*'
+      //     );
+      //   }
+      // };
+
+      // let checkCount = 0;
+      // const checkLoadedInterval = setInterval(() => {
+      //   if (!_pageWindow || _pageWindow.closed) {
+      //     clearInterval(checkLoadedInterval);
+      //     return;
+      //   }
+      //   try {
+      //     if (_pageWindow.document && _pageWindow.document.readyState === 'complete') {
+      //       clearInterval(checkLoadedInterval);
+      //       sendFileData();
+      //     }
+      //   } catch (err) {
+      //     // 跨域下直接尝试发送
+      //     clearInterval(checkLoadedInterval);
+      //     sendFileData();
+      //   }
+      //   if (++checkCount > 20) {
+      //     // 最多检测2秒(20次)
+      //     clearInterval(checkLoadedInterval);
+      //     sendFileData();
+      //   }
+      // }, 100);
+
+      setTimeout(()=>{
+        window.focus()
+      },100)
+
+      function openPageWindow(){
+        _pageWindow.focus()
+      }
+
+      this.$message({
+        dangerouslyUseHTMLString: true,
+        customClass:"pptEasyClassMessage",
+        duration:3000,
+        message: `${this.lang.ssStoppedRecording} <p style="color:#3AB855;text-decoration: underline;cursor: pointer;float:right;margin-left:10px" target="_blank" onclick="(${openPageWindow.toString()})()">${this.lang.ssViewRecordingResult}</p>`
+      });
+
     },
-    addPPTClass() {
+    addPPTClass(file) {
       let params = {
         pptid: this.id,
         cid: this.tcid,
@@ -348,7 +500,7 @@ export default {
         .then(res => {
           console.log("addPPTClass", res);
           let id = res.data[0][0].id;
-          this.openObserveDialog(id);
+          this.openObserveDialog(id,file);
         })
         .catch(err => {
           console.error(err);
@@ -554,6 +706,48 @@ export default {
         console.log('同步数据')
       }
     },
+    // 直接上传录制音频/视频
+    uploadVoiceBtn(){
+      const input = document.createElement('input');
+      input.type = 'file';
+      input.accept = '.m4a,.mp4,.mov,.mp3,.wav';
+      input.click();
+      input.onchange = (e) => {
+        const fileList = e.target.files;
+        const file = fileList[0]
+        this.recordingStartTime = new Date(window.recordingStartTime?window.recordingStartTime:new Date()).toLocaleString("zh-CN", {
+        hour12: false,
+        timeZone: "Asia/Shanghai"
+      }).replace(/\//g, "-");
+        this.recordingEndTime = new Date(window.recordingEndTime?window.recordingEndTime:new Date()).toLocaleString("zh-CN", {
+        hour12: false,
+        timeZone: "Asia/Shanghai"
+      }).replace(/\//g, "-");
+        this.addPPTClass(file)
+      // 判断文件类型并获取音频或视频时长
+      // if (file && (file.type.startsWith('audio/') || file.type.startsWith('video/'))) {
+      //   const url = URL.createObjectURL(file);
+      //   let media;
+      //   if (file.type.startsWith('audio/')) {
+      //     media = new Audio();
+      //   } else {
+      //     media = document.createElement('video');
+      //   }
+      //   media.preload = 'metadata';
+      //   media.src = url;
+      //   media.onloadedmetadata = () => {
+      //     const duration = media.duration;
+      //     console.log('文件时长(秒):', duration);
+      //     // 这里可以赋值或者进一步处理时长 duration
+      //     URL.revokeObjectURL(url);
+      //   };
+      //   media.onerror = () => {
+      //     console.error('无法读取文件时长');
+      //     URL.revokeObjectURL(url);
+      //   };
+      // }
+      }
+    }
   },
   destroyed() {
     clearInterval(this.opertimer);
@@ -738,6 +932,12 @@ export default {
   color: #F53F3F;
 }
 
+.pec_h_r_btn_uploadVoiceBtn{
+  border-color: #F0E1DD;
+  background-color: #FFF7F5;
+  color: #000;
+}
+
 .backBtn {
   width: 15px;
   height: 15px;
@@ -802,21 +1002,21 @@ export default {
 }
 
 .refresh_icon {
-  width: 40px;
-  height: 40px;
+  width: 35px;
+  height: 35px;
   display: flex;
   align-items: center;
   justify-content: center;
   background-color: #fff7f5;
   border-radius: 45%;
   cursor: pointer;
-  margin-left: 15px;
+  margin-right: 15px;
   padding: 0 3px;
 }
 
 .refresh_icon img {
-  width: 20px;
-  height: 20px;
+  width: 16px;
+  height: 16px;
 }
 
 .name_box {

+ 6 - 2
src/lang/cn.json

@@ -182,7 +182,7 @@
   "ssGrade": "年级",
   "ssSubjectName": "学科",
   "ssCreator": "创建者:",
-  "ssParticipants": "参与人数",
+  "ssParticipants": "参与人数",
   "ssCollaborativeMembers": "协同成员:",
   "ssExpandText": "展开",
   "ssCollapseText": "收起",
@@ -895,5 +895,9 @@
   "ssStopRecordingNotice": "停止录制后,录音文件将发送至课堂分析应用。",
   "ssStopRecordingConfirm": "确定停止课堂录制?",
   "ssStoppedRecording": "已停止录制",
-  "ssViewRecordingResult": "查看结果"
+  "ssViewRecordingResult": "查看结果",
+  "ssUploadRecordingFile": "上传录制",
+  "ssBeginClassRecording": "开始课堂录制",
+  "ssFinishClassRecording": "结束课堂录制",
+  "ssNoPermStop":"未开启录音权限,已停止录音"
 }

+ 7 - 3
src/lang/en.json

@@ -180,7 +180,7 @@
   "ssGrade": "Grade",
   "ssSubjectName": "Subject",
   "ssCreator": "Creator:",
-  "ssParticipants": "Participants:",
+  "ssParticipants": "Number of Participants",
   "ssCollaborativeMembers": "Collabotators:",
   "ssExpandText": "More",
   "ssCollapseText": "Collapse",
@@ -228,7 +228,7 @@
   "ssCategoryColon": "Category:",
   "ssStageText": "Stage",
   "ssTaskText": "Task",
-  "ssStageNumFormat": "stage(s) *",
+  "ssStageNumFormat": "Stage(s) *",
   "ssTaskNumFormat": "task(s) *:",
   "ssStepNumFormat": "Step *:",
   "ssTasksFormat": "task(s) *",
@@ -893,5 +893,9 @@
   "ssStopRecordingNotice": "After stopping the recording, the file will be sent to the classroom analysis application.",
   "ssStopRecordingConfirm": "Are you sure you want to stop classroom recording?",
   "ssStoppedRecording": "Recording stopped",
-  "ssViewRecordingResult": "View result"
+  "ssViewRecordingResult": "View result",
+  "ssUploadRecordingFile": "Upload Recording",
+  "ssBeginClassRecording": "Begin Class Recording",
+  "ssFinishClassRecording": "End Class Recording",
+  "ssNoPermStop":"Recording permission not enabled, recording has been stopped"
 }

+ 6 - 2
src/lang/hk.json

@@ -180,7 +180,7 @@
   "ssGrade": "年級",
   "ssSubjectName": "學科",
   "ssCreator": "創建者:",
-  "ssParticipants": "參與人數",
+  "ssParticipants": "參與人數",
   "ssCollaborativeMembers": "協同成員:",
   "ssExpandText": "展開",
   "ssCollapseText": "收起",
@@ -893,5 +893,9 @@
   "ssStopRecordingNotice": "停止錄製後,錄音檔案將發送至課堂分析應用。",
   "ssStopRecordingConfirm": "確定停止課堂錄製?",
   "ssStoppedRecording": "已停止錄製",
-  "ssViewRecordingResult": "查看結果"
+  "ssViewRecordingResult": "查看結果",
+  "ssUploadRecordingFile": "上傳錄製",
+  "ssBeginClassRecording": "開始課堂錄製",
+  "ssFinishClassRecording": "結束課堂錄製",
+  "ssNoPermStop":"未開啟錄音權限,已停止錄音"
 }

Some files were not shown because too many files changed in this diff