Browse Source

修改无声

yangg 1 month ago
parent
commit
38ba5cbe60
1 changed files with 399 additions and 25 deletions
  1. 399 25
      pages/identity-verify/identity-verify.vue

+ 399 - 25
pages/identity-verify/identity-verify.vue

@@ -161,6 +161,13 @@
         <div class="countdown-text">准备开始回答</div>
       </div>
     </div>
+
+    <!-- 添加重新录制按钮 -->
+    <div class="rerecord-button-container" v-if="showRerecordButton">
+      <button class="rerecord-button" @click="handleRerecordButtonClick">
+        重新作答
+      </button>
+    </div>
   </div>
 </template>
 
@@ -224,6 +231,19 @@ export default {
       countdownTimer: null,
       showGif: false, // 控制是否显示GIF
       gifUrl: '', // GIF图片的URL
+      globalSocketTask: null, // 添加全局 WebSocket 连接对象
+      lowScoreVideoUrl: 'https://data.qicai321.com/minlong/latentsync/0530e7f5-1957-422d-8f34-ba4a92608081_result.mp4', // 低分提示视频URL
+      showRerecordButton: false, // 控制重新录制按钮显示
+      isPlayingLowScoreVideo: false, // 标记是否正在播放低分提示视频
+      lowScoreVideoSubtitles: [
+        {
+          startTime: 0,
+          endTime: 10, // 假设视频长度为10秒,可根据实际情况调整
+          text: '我未听清楚您在说什么,请您再说一遍!'
+        }
+      ], // 用于存储低分提示视频的字幕
+      retryCount: 0, // 添加重试次数计数器
+      maxRetryAttempts: 2, // 最大重试次数限制
     }
   },
   mounted() {
@@ -242,9 +262,13 @@ export default {
         this.testAudioInput();
       }
     }, 3000);
-	uni.setKeepScreenOn({
-	    keepScreenOn: true
-	});
+    
+    // 初始化 WebSocket 连接
+    this.initWebSocketConnection();
+    
+    uni.setKeepScreenOn({
+      keepScreenOn: true
+    });
   },
   beforeDestroy() {
     // 组件销毁前停止摄像头
@@ -252,6 +276,9 @@ export default {
     
     // 清除倒计时定时器
     this.clearCountdown();
+    
+    // 关闭 WebSocket 连接
+    this.closeWebSocketConnection();
   },
   methods: {
     // 初始化相机
@@ -798,24 +825,34 @@ export default {
       console.log('视频播放结束');
       this.videoPlaying = false;
       
-     /*  // 设置对应问题的GIF图片URL
-      if (this.currentVideoIndex === 0) {
-        this.gifUrl = 'http://121.36.251.245:9000/minlong/cae2da8f92ce4af278d3929ab8c5973f.mp4';
-      } else if (this.currentVideoIndex === 1) {
-        this.gifUrl = 'http://121.36.251.245:9000/minlong/cae2da8f92ce4af278d3929ab8c5973f.mp4';
-      } else if (this.currentVideoIndex === 2) {
-        this.gifUrl = 'http://121.36.251.245:9000/minlong/cae2da8f92ce4af278d3929ab8c5973f.mp4';
-      } else if (this.currentVideoIndex === 3) {
-        this.gifUrl = 'http://121.36.251.245:9000/minlong/cae2da8f92ce4af278d3929ab8c5973f.mp4';
-      } else if (this.currentVideoIndex === 4) {
-        this.gifUrl = 'http://121.36.251.245:9000/minlong/cae2da8f92ce4af278d3929ab8c5973f.mp4';
-      } else if (this.currentVideoIndex === 5) {
-        this.gifUrl = 'http://121.36.251.245:9000/minlong/cae2da8f92ce4af278d3929ab8c5973f.mp4';
-      }
-      
-      // 显示GIF图片
-      this.showGif = true; */
+      // 检查是否是低分提示视频
+      if (this.isPlayingLowScoreVideo) {
+        console.log('低分提示视频播放完成');
+        
+        // 检查重试次数
+        if (this.retryCount < this.maxRetryAttempts) {
+          console.log(`显示重新录制按钮 (重试次数: ${this.retryCount + 1}/${this.maxRetryAttempts})`);
+          
+          // 显示重新录制按钮
+          this.showRerecordButton = true;
+        } else {
+          console.log('已达到最大重试次数,继续下一题');
+          
+          // 重置重试次数
+          this.retryCount = 0;
+          
+          // 继续下一个问题
+          this.proceedToNextQuestion();
+        }
+        
+        // 重置标记和清除字幕
+        this.isPlayingLowScoreVideo = false;
+        this.currentSubtitle = '';
+        
+        return;
+      }
       
+      // 恢复原有的视频结束处理逻辑
       // 对于所有视频,都显示相应的按钮
       if (this.currentVideoIndex >= 1) {
         // 对于问题视频,显示"开始回答"按钮
@@ -1600,8 +1637,8 @@ export default {
         this.processUploadQueue();
       }
       
-      // 立即进入下一个问题,不等待上传完成
-      this.proceedToNextQuestion();
+      // 注意:不再自动进入下一个问题
+      // 我们会在WebSocket回调中决定是否进入下一个问题
     },
     
     // 添加新方法:处理上传队列
@@ -1811,7 +1848,7 @@ export default {
       }
     },
     
-    // 修改 submitVideoToInterview 方法
+    // 修改 submitVideoToInterview 方法,添加 WebSocket 连接和分数检查
     submitVideoToInterview(videoUrl, task = null) {
       console.log('提交视频URL到面试接口:', videoUrl);
       
@@ -1862,7 +1899,7 @@ export default {
         success: (res) => {
           console.log('面试接口提交成功:', res);
           
-          if (res.data.code === 0 || res.data.code === 2000) {
+          if (res.data.code === 200 || res.data.code === 200) {
             // 提交成功
             if (task) {
               // 从队列中移除当前任务
@@ -1875,6 +1912,9 @@ export default {
               //   duration: 1500
               // });
               
+              // 连接WebSocket获取分析结果
+              this.connectToWebSocketForAnalysis(questionId, videoUrl);
+              
               // 继续处理队列中的下一个任务
               this.processUploadQueue();
             } else {
@@ -1884,6 +1924,9 @@ export default {
                 icon: 'success'
               });
               
+              // 连接WebSocket获取分析结果
+              this.connectToWebSocketForAnalysis(questionId, videoUrl);
+              
               // 保存最后上传的视频URL
               this.lastUploadedVideoUrl = videoUrl;
               
@@ -2004,8 +2047,12 @@ export default {
       }
     },
     
-    // 修改 proceedToNextQuestion 方法
+    // 修改 proceedToNextQuestion 方法,不要自动进入下一个问题
+    // 因为现在我们会在WebSocket回调中决定是否进入下一个问题
     proceedToNextQuestion() {
+      // 重置重试计数器
+      this.retryCount = 0;
+      
       // 检查是否完成了第五个视频问题(索引为4)
       if (this.currentVideoIndex === 4) {
         // 完成第五个问题后,跳转到相机页面
@@ -2158,6 +2205,18 @@ export default {
         this.recordingTimeDisplay = this.formatTime(this.recordingTimerCount);
       }
       
+      // 如果正在播放低分提示视频,使用专门的字幕数组
+      if (this.isPlayingLowScoreVideo) {
+        if (this.lowScoreVideoSubtitles) {
+          const subtitle = this.lowScoreVideoSubtitles.find(
+            sub => currentTime >= sub.startTime && currentTime < sub.endTime
+          );
+          this.currentSubtitle = subtitle ? subtitle.text : '';
+        }
+        return;
+      }
+      
+      // 以下是原有的字幕处理逻辑
       // 根据当前播放的视频索引选择对应的字幕数组
       let currentSubtitles;
       if (this.currentVideoIndex === 0) {
@@ -2780,6 +2839,292 @@ export default {
       
       // 每次页面显示时重新获取问题
       this.fetchQuestions();
+    },
+
+    // 添加新方法:连接WebSocket获取分析结果
+    connectToWebSocketForAnalysis(questionId, videoUrl) {
+      console.log('使用已有的 WebSocket 连接发送分析请求');
+      
+      // 如果没有全局 WebSocket 连接,则创建一个
+      if (!this.globalSocketTask) {
+        console.log('全局 WebSocket 连接不存在,重新初始化');
+        this.initWebSocketConnection();
+        
+        // 延迟发送,确保连接已建立
+        setTimeout(() => {
+          this.sendAnalysisRequest(questionId, videoUrl);
+        }, 1000);
+      } else {
+        // 直接使用已有连接发送请求
+        this.sendAnalysisRequest(questionId, videoUrl);
+      }
+    },
+
+    // 添加新方法:发送分析请求
+    sendAnalysisRequest(questionId, videoUrl) {
+      // 如果 WebSocket 连接已打开,发送分析请求
+      if (this.globalSocketTask && this.globalSocketTask.readyState === 1) {
+        // 构建请求数据
+        const requestData = {
+          type: 'start_analysis',
+          question_id: questionId,
+          video_url: videoUrl
+        };
+        
+        // 发送请求
+        this.globalSocketTask.send({
+          data: JSON.stringify(requestData),
+          success: () => {
+            console.log('分析请求发送成功');
+          },
+          fail: (err) => {
+            console.error('分析请求发送失败:', err);
+            // 发送失败时,继续下一个问题
+            this.proceedToNextQuestion();
+          }
+        });
+      } else {
+        console.error('WebSocket 连接未打开,无法发送分析请求');
+        // 连接未打开时,继续下一个问题
+        this.proceedToNextQuestion();
+      }
+    },
+
+    // 添加新方法:重置录制状态,准备重新回答
+    resetForRerecording() {
+      console.log('重置录制状态,准备重新回答');
+      
+      // 重置录制状态
+      this.isRecording = false;
+      this.showStopRecordingButton = false;
+      
+      // 显示"开始回答"按钮
+      this.showStartRecordingButton = true;
+      
+      // 清除倒计时
+      this.clearCountdown();
+      
+      // 重置录制计时器
+      if (this.recordingTimer) {
+        clearInterval(this.recordingTimer);
+        this.recordingTimer = null;
+      }
+      
+      // 重置录制时间显示
+      this.recordingTimerCount = 0;
+      this.recordingTimeDisplay = '00:00 / 05:00';
+      this.remainingTime = this.maxRecordingTime;
+      
+      // 如果是浏览器环境,停止MediaRecorder
+      if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
+        this.mediaRecorder.stop();
+        this.recordedChunks = [];
+      }
+      
+      // 如果是小程序环境,停止相机录制
+      if (this.cameraContext) {
+        this.cameraContext.stopRecord({
+          success: () => {
+            console.log('相机录制已停止');
+          },
+          fail: (err) => {
+            console.error('停止相机录制失败:', err);
+          }
+        });
+      }
+    },
+
+    // 添加新方法:初始化 WebSocket 连接
+    initWebSocketConnection() {
+      console.log('初始化 WebSocket 连接');
+      
+      // 获取 appId
+      const appId = uni.getStorageSync('appId') || '98'; // 使用默认值 98,如图中所示
+      console.log('使用 appId:', appId);
+      
+      // 获取 tenant_id
+      const tenant_id = JSON.parse(uni.getStorageSync('userInfo')).tenant_id || '1';
+      
+      // 从 apiBaseUrl 构建 WebSocket URL
+      // 将 http:// 或 https:// 替换为 ws:// 或 wss://
+      let wsBaseUrl = apiBaseUrl.replace(/^http/, 'ws');
+      
+      // 构建完整的 WebSocket URL
+      const wsUrl = `${wsBaseUrl}/ws/video_analysis/${appId}/`;
+      console.log('WebSocket URL:', wsUrl);
+      
+      // 创建 WebSocket 连接
+      this.globalSocketTask = uni.connectSocket({
+        url: wsUrl,
+        success: () => {
+          console.log('WebSocket 连接成功');
+        },
+        fail: (err) => {
+          console.error('WebSocket 连接失败:', err);
+        }
+      });
+      
+      // 监听 WebSocket 连接打开
+      this.globalSocketTask.onOpen(() => {
+        console.log('WebSocket 连接已打开');
+      });
+      
+      // 监听 WebSocket 接收到服务器的消息
+      this.globalSocketTask.onMessage((res) => {
+        console.log('收到 WebSocket 消息:', res.data);
+        
+        try {
+          // 解析消息数据
+          const data = JSON.parse(res.data);
+          
+          // 检查是否是分析完成的消息
+          if (data.type === 'analysis_complete') {
+            // 检查 overall_score 是否小于 10
+            const overallScore = data.data && data.data.answer_status;
+            console.log('分析完成,得分:', overallScore);
+            
+            if (overallScore == 1) {
+              console.log(`得分较低,检查重试次数 (当前: ${this.retryCount}/${this.maxRetryAttempts})`);
+              
+              // 检查是否已经达到最大重试次数
+              if (this.retryCount >= this.maxRetryAttempts) {
+                console.log('已达到最大重试次数,直接进入下一题');
+                // 重置重试次数并继续下一题
+                this.retryCount = 0;
+                this.proceedToNextQuestion();
+              } else {
+                // 还有重试机会,播放提示视频
+                this.playLowScoreVideo();
+              }
+            } else {
+              console.log('得分正常,继续下一个问题');
+              // 重置重试次数并继续下一题
+              this.retryCount = 0;
+              this.proceedToNextQuestion();
+            }
+          }
+        } catch (e) {
+          console.error('解析 WebSocket 消息失败:', e);
+        }
+      });
+      
+      // 监听 WebSocket 错误
+      this.globalSocketTask.onError((err) => {
+        console.error('WebSocket 错误:', err);
+      });
+      
+      // 监听 WebSocket 连接关闭
+      this.globalSocketTask.onClose(() => {
+        console.log('WebSocket 连接已关闭');
+        // 可以在这里添加重连逻辑
+        setTimeout(() => {
+          this.initWebSocketConnection();
+        }, 5000);
+      });
+    },
+
+    // 修改 playLowScoreVideo 方法,优化字幕显示
+    playLowScoreVideo() {
+      console.log('播放低分提示视频');
+      
+      // 标记正在播放低分提示视频
+      this.isPlayingLowScoreVideo = true;
+      
+      // 隐藏其他按钮
+      this.showStartRecordingButton = false;
+      this.showStopRecordingButton = false;
+      this.showRerecordButton = false;
+      
+      // 设置视频URL
+      this.videoUrl = this.lowScoreVideoUrl;
+      this.videoPlaying = true;
+      
+      // 清除现有字幕
+      this.currentSubtitle = '';
+      
+      // 使用 nextTick 确保 DOM 更新后再播放视频
+      this.$nextTick(() => {
+        const videoContext = uni.createVideoContext('myVideo', this);
+        if (videoContext) {
+          videoContext.play();
+          
+          // 视频开始播放后短暂延迟显示字幕,确保用户注意到
+          setTimeout(() => {
+            // 设置低分提示视频的字幕
+            this.currentSubtitle = '我未听清楚您在说什么,请您再说一遍!';
+            
+            // 创建专门的字幕数组用于低分提示视频
+            this.lowScoreVideoSubtitles = [
+              {
+                startTime: 0,
+                endTime: 10, // 假设视频长度为10秒,可根据实际情况调整
+                text: '我未听清楚您在说什么,请您再说一遍!'
+              }
+            ];
+          }, 500);
+        }
+      });
+    },
+
+    // 添加新方法:处理重新录制按钮点击
+    handleRerecordButtonClick() {
+      console.log('点击重新录制按钮');
+      
+      // 增加重试计数
+      this.retryCount++;
+      console.log(`当前重试次数: ${this.retryCount}/${this.maxRetryAttempts}`);
+      
+      // 隐藏重新录制按钮
+      this.showRerecordButton = false;
+      
+      // 直接调用开始录制的方法
+      this.handleStartRecordingClick();
+    },
+
+    // 添加新方法:关闭 WebSocket 连接
+    closeWebSocketConnection() {
+      if (this.globalSocketTask) {
+        console.log('关闭 WebSocket 连接');
+        
+        // 发送关闭消息给服务器
+        try {
+          this.globalSocketTask.send({
+            data: JSON.stringify({ type: 'client_disconnect' }),
+            fail: () => {
+              console.log('发送断开连接消息失败');
+            }
+          });
+        } catch (e) {
+          console.error('发送断开连接消息异常:', e);
+        }
+        
+        // 关闭连接
+        this.globalSocketTask.close({
+          code: 1000,
+          reason: 'User left the page',
+          success: () => {
+            console.log('WebSocket 连接已成功关闭');
+          },
+          fail: (err) => {
+            console.error('WebSocket 连接关闭失败:', err);
+          },
+          complete: () => {
+            this.globalSocketTask = null;
+          }
+        });
+      }
+    },
+
+    // 添加 onUnload 生命周期钩子,确保在页面卸载时关闭连接
+    onUnload() {
+      console.log('页面卸载,关闭 WebSocket 连接');
+      this.closeWebSocketConnection();
+    },
+
+    // 添加 onHide 生命周期钩子,在页面隐藏时也关闭连接
+    onHide() {
+      console.log('页面隐藏,关闭 WebSocket 连接');
+      this.closeWebSocketConnection();
     }
   }
 }
@@ -3189,4 +3534,33 @@ video::-webkit-media-controls-fullscreen-button {
   left: 0;
   z-index: 5; /* 确保GIF在视频上方但在字幕和按钮下方 */
 }
+
+/* 重新录制按钮容器样式 */
+.rerecord-button-container {
+  position: absolute;
+  bottom: 50px; /* 统一与其他按钮的位置 */
+  left: 50%;
+  transform: translateX(-50%);
+  z-index: 20;
+}
+
+/* 重新录制按钮样式 */
+.rerecord-button {
+  width: 120px;
+  height: 40px;
+  border-radius: 20px;
+  background-color: #ffffff;
+  color: #333;
+  font-size: 16px;
+  border: none;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  cursor: pointer;
+}
+
+.rerecord-button:hover {
+  background-color: #f0f0f0;
+}
 </style>