Quellcode durchsuchen

修改摄像头及其其他内容

yangg vor 2 Monaten
Ursprung
Commit
8164bed9d1

+ 7 - 0
pages.json

@@ -61,6 +61,13 @@
 			{
 				"navigationBarTitleText" : ""
 			}
+		},
+		{
+			"path" : "pages/interview-question/interview-question",
+			"style" : 
+			{
+				"navigationBarTitleText" : ""
+			}
 		}
 	],
 	"globalStyle": {

+ 75 - 45
pages/camera/camera.vue

@@ -16,10 +16,15 @@
 		<view class="content">
 			<!-- 数字人头像 -->
 			<view class="digital-avatar">
-				<camera v-if="!digitalHumanUrl" device-position="front" flash="auto" class="camera" 
-						:mode="mode" @error="handleCameraError"></camera>
-				<web-view v-else-if="digitalHumanUrl" :src="digitalHumanUrl" class="digital-human-webview"></web-view>
-				<image v-else src="/static/avatar.png" mode="aspectFill" class="avatar-image"></image>
+				<!-- <view class="message-box">
+					<text class="message-text">面试官会根据您的自我介绍内容对您提出以下问题,请您回答相关问题,请知悉答题即可</text>
+				</view> -->
+				<view class="user-avatar">
+					<camera v-if="!digitalHumanUrl" device-position="front" flash="auto" class="camera" 
+							:mode="mode" @error="handleCameraError"></camera>
+					<web-view v-else-if="digitalHumanUrl" :src="digitalHumanUrl" class="digital-human-webview"></web-view>
+					<image v-else src="/static/avatar.png" mode="aspectFill" class="avatar-image"></image>
+				</view>
 			</view>
 
 			<!-- 进度指示器 -->
@@ -33,7 +38,7 @@
         </view>
       </view> -->
 
-			<!-- 试内容区域 -->
+			<!-- 试内容区域 -->
 			<view class="interview-content">
 				<!-- 问题编号和进度 -->
 				<view class="question-number">
@@ -43,8 +48,8 @@
 				<!-- 问题和选项区域 -->
 				<view class="question-area">
 					<view class="question-importance">
-						<text v-if="currentQuestion.isImportant">重点题</text> 
-						<text class="question-type">[{{currentQuestion.questionTypeName}}]</text> 
+						<!-- <text v-if="currentQuestion.isImportant">重点题</text>  -->
+						<text class="question-type">{{currentQuestion.questionTypeName}}</text> 
 						{{currentQuestion.text}}
 					</view>
 
@@ -342,7 +347,7 @@
 								} else {
 									// 如果是最后一题,显示结束模态框或跳转
 									uni.navigateTo({
-										url: '/pages/interview/interview'
+										url: '/pages/interview-question/interview-question'
 									});
 								}
 							}, 1500); // 1.5秒后自动进入下一题
@@ -434,7 +439,7 @@
 							// 如果是最后一题,跳转到面试页面
 							if (this.currentQuestionIndex >= this.questions.length - 1) {
 								uni.navigateTo({
-									url: '/pages/interview/interview'
+									url: '/pages/interview-question/interview-question'
 								});
 							} else {
 								// 否则进入下一题
@@ -447,7 +452,7 @@
 						setTimeout(() => {
 							if (this.currentQuestionIndex >= this.questions.length - 1) {
 								uni.navigateTo({
-									url: '/pages/interview/interview'
+									url: '/pages/interview-question/interview-question'
 								});
 							} else {
 								this.goToNextQuestion();
@@ -461,7 +466,7 @@
 				// 如果是最后一题,直接跳转到手部照片采集页面
 				if (this.currentQuestionIndex >= this.questions.length - 1) {
 					uni.navigateTo({
-						url: '/pages/interview/interview'
+						url: '/pages/interview-question/interview-question'
 					});
 					return;
 				}
@@ -691,25 +696,25 @@
 				}
 				
 				// 否则尝试返回上一页,如果失败则跳转到首页
-				try {
-					const pages = getCurrentPages();
-					if (pages.length > 1) {
-						uni.reLaunch({
-							url: '/pages/index/index'
-						});
-					} else {
-						// 如果当前是第一页,则跳转到首页
-						uni.reLaunch({
-							url: '/pages/index/index'
-						});
-					}
-				} catch (e) {
-					console.error('导航错误:', e);
-					// 出错时也跳转到首页
-					uni.reLaunch({
-						url: '/pages/index/index'
-					});
-				}
+				// try {
+				// 	const pages = getCurrentPages();
+				// 	if (pages.length > 1) {
+				// 		uni.reLaunch({
+				// 			url: '/pages/index/index'
+				// 		});
+				// 	} else {
+				// 		// 如果当前是第一页,则跳转到首页
+				// 		uni.reLaunch({
+				// 			url: '/pages/index/index'
+				// 		});
+				// 	}
+				// } catch (e) {
+				// 	console.error('导航错误:', e);
+				// 	// 出错时也跳转到首页
+				// 	uni.reLaunch({
+				// 		url: '/pages/index/index'
+				// 	});
+				// }
 			},
 
 			error(e) {
@@ -1185,31 +1190,56 @@
 	}
 
 	.digital-avatar {
-		width: 120rpx;
-		height: 120rpx;
-		z-index: 10;
-		overflow: hidden;
-		border-radius: 20rpx;
+		display: flex;
+		flex-direction: row;
+		align-items: flex-start;
+		justify-content: space-between;
+		width: 100%;
+		margin-bottom: 20rpx;
 	}
 	
+	.message-box {
+		width: 65%;
+		min-height: 120rpx;
+		background-color: #f5f5f5;
+		border-radius: 10rpx;
+		padding: 15rpx;
+		margin-right: 20rpx;
+		display: flex;
+		align-items: center;
+	}
+
+	.message-text {
+		font-size: 24rpx;
+		color: #333;
+		line-height: 1.5;
+	}
+
+	.user-avatar {
+		width: 110px;
+		height: 160px;
+		border-radius: 10rpx;
+		overflow: hidden;
+	}
+
 	.camera {
-		width: 120rpx;
-		height: 120rpx;
-		border-radius: 20rpx;
+		width: 100%;
+		height: 100%;
+		border-radius: 10rpx;
 		border: 2rpx solid #e0e0e0;
 	}
 
 	.avatar-image {
-		width: 120rpx;
-		height: 120rpx;
-		border-radius: 20rpx;
+		width: 100%;
+		height: 100%;
+		border-radius: 10rpx;
 		border: 2rpx solid #e0e0e0;
 	}
 
 	.digital-human-webview {
-		width: 120rpx;
-		height: 120rpx;
-		border-radius: 20rpx;
+		width: 100%;
+		height: 100%;
+		border-radius: 10rpx;
 		border: 2rpx solid #e0e0e0;
 	}
 
@@ -1305,7 +1335,7 @@
 
 	.question-type {
 		display: inline-block;
-		background-color: #0039b3;
+		/* background-color: #0039b3; */
 		color: white;
 		font-size: 24rpx;
 		padding: 4rpx 10rpx;

+ 2868 - 0
pages/interview-question/interview-question.vue

@@ -0,0 +1,2868 @@
+<template>
+  <div class="identity-verify-container">
+    <div class="digital-human-container">
+      <!-- AI数字人视频/图像显示区域 -->
+      <div class="digital-human-video">
+        <!-- <image v-if="!videoPlaying" src="/static/images/digital-human-placeholder.jpg" mode="aspectFit"></image> -->
+        <video 
+          :src="videoUrl"
+          id="myVideo"
+          ref="videoPlayer" 
+          autoplay 
+          playsinline
+          disablePictureInPicture
+          controlsList="nodownload nofullscreen noremoteplayback"
+          class="video-player"
+          :controls="false"
+          @error="handleVideoError"
+          @ended="handleVideoEnded"
+          @timeupdate="handleTimeUpdate">
+        </video>
+         <div class="answer-button-container" v-if="showAnswerButton">
+          <button class="answer-button" @click="handleAnswerButtonClick">
+            开始回答
+          </button>
+        </div>
+        <!-- 添加字幕覆盖层 :data-time="formatTime(recordingTimerCount) || '03:30'"-->
+        <div class="subtitle-overlay" v-if="currentSubtitle" >
+          {{ currentSubtitle }}
+        </div>
+        
+        <!-- 添加答题按钮 -->
+       
+      </div>
+      
+      <!-- 用户摄像头视频显示区域 -->
+      <div class="user-camera-container">
+        <!-- 在小程序环境中使用camera组件 -->
+        <camera v-if="useMiniProgramCameraComponent" 
+                device-position="front" 
+                flash="off" 
+                class="user-camera-video"
+                @error="handleCameraError"
+                :record-audio="true"
+                frame-size="small"
+                resolution="low"
+                :disable-zoom="true"
+                :enable-camera="true"
+                :enable-record="true"
+                mode="normal">
+        </camera>
+        <!-- 在H5/App环境中使用video元素 -->
+        <video v-else
+          id="userCamera"
+          ref="userCameraVideo"
+          autoplay
+          playsinline
+          muted
+          class="user-camera-video"
+          :controls="false">
+        </video>
+      </div>
+      
+      <!-- 字幕/文本覆盖区域 -->
+      <!-- <div class="subtitle-overlay" v-if="assistantResponse">
+        {{ assistantResponse }}
+      </div> -->
+    </div>
+
+    <!-- 加载状态 -->
+    <div v-if="loading" class="loading">加载中...</div>
+    
+    <!-- 控制面板(可选,可以隐藏) -->
+    
+    <!-- 响应数据(可以设为隐藏,仅用于调试) -->
+    <div v-if="showDebugInfo" class="response-container">
+      <div v-if="assistantResponse" class="response-item">
+        <div class="response-content">
+          <span>助手回复: {{ assistantResponse }}</span>
+        </div>
+      </div>
+      <div v-if="audioTranscript" class="response-item">
+        <div class="response-content">
+          <span>音频转写: {{ audioTranscript }}</span>
+        </div>
+      </div>
+      <div v-for="(item, index) in processedResponses" :key="index" class="response-item">
+        <div class="response-content">
+          <span v-if="item.role">角色: {{ item.role }}</span>
+          <span v-if="item.transcript">文本: {{ item.transcript }}</span>
+        </div>
+      </div>
+    </div>
+
+    <!-- 添加停止录制按钮 -->
+    <div class="stop-recording-button-container" v-if="showStopRecordingButton">
+      <button class="stop-recording-button" @click="stopRecordingAnswer">
+        完成作答
+      </button>
+    </div>
+
+    <!-- 添加录制指示器 -->
+    <div class="recording-indicator" v-if="isRecording">
+      <div class="recording-dot"></div>
+		<span class="recording-text">正在录制...</span>
+	    <span class="timer-text">{{ recordingTimeDisplay || '00:00 / 05:00' }}</span>
+    </div>
+
+    <!-- 添加开始回答按钮 -->
+    <div class="start-recording-button-container" v-if="showStartRecordingButton">
+      <button class="start-recording-button" @click="handleStartRecordingClick">
+        开始作答
+      </button>
+    </div>
+
+    <!-- 添加重试上传按钮 -->
+    <div class="retry-button-container" v-if="showRetryButton">
+      <button class="retry-button" @click="retryVideoUpload">
+        重新上传
+      </button>
+    </div>
+
+    <!-- 在模板部分添加录制时长显示 -->
+    <!-- <div class="recording-timer" v-if="isRecording">
+      <span class="timer-text">{{ recordingTimeDisplay || '00:00' }}</span>
+    </div> -->
+
+    <!-- 添加上传状态指示器 -->
+   <!-- <div class="upload-status-indicator" v-if="showUploadStatus && uploadStatusText">
+      <div class="upload-status-content">
+        <div class="upload-status-icon" :class="{'uploading': isUploading}"></div>
+        <span class="upload-status-text">{{ uploadStatusText }}</span>
+      </div>
+    </div> -->
+
+    <!-- 修改录制时长显示 -->
+    <!-- <div class="recording-timer" v-if="isRecording">
+      <span class="timer-text">{{ recordingTimeDisplay || '00:00 / 05:00' }}</span>
+      <span class="remaining-time" :class="{'warning': remainingTime <= 10}">
+        剩余: {{ formatTime(remainingTime) }}
+      </span>
+    </div> -->
+  </div>
+</template>
+
+<script>
+import { apiBaseUrl } from '@/common/config.js';
+export default {
+  name: 'IdentityVerify',
+  data() {
+    return {
+      loading: false,
+      responses: [],
+      processedResponses: [],
+      assistantResponse: '',
+      audioTranscript: '',
+      videoPlaying: false,
+      showDebugInfo: false, // 设置为true可以显示调试信息
+      videoUrl: 'http://121.36.251.245:9000/minlong/tenant_1/general_uploads/abdaa6fda8494e3a8613304743ed0433.mp4', // 用于存储AI数字人视频URL
+      showReplayButton: false,
+      cameraStream: null, // 存储摄像头流
+      cameraError: null, // 存储摄像头错误信息
+      useMiniProgramCameraComponent: false, // 添加小程序相机组件标志
+      cameraContext: null, // 添加相机上下文
+      currentSubtitle: '',
+      subtitles: [
+        {
+          startTime: 0, // 开始时间(秒)
+          endTime: 6,   // 结束时间(秒)
+          text: '您已完成本次面试全部题目,请问您对于这个岗位还有什么想要了解的吗?'
+        },
+      ],
+      // secondVideoSubtitles: [
+      //   {
+      //     startTime: 0,
+      //     endTime: 10,
+      //     text: '请结合您的基本信息与过往履历进行简单的自我介绍,并讲一讲您有哪些优势胜任本岗位:'
+      //   }
+      // ],
+      // thirdVideoSubtitles: [
+      //   {
+      //     startTime: 0,
+      //     endTime: 4,
+      //     text: '在工作中,你如何确保个人防护装备的正确使用?'
+      //   }
+      // ],
+      // fourthVideoSubtitles: [
+      //   {
+      //     startTime: 0,
+      //     endTime: 4,
+      //     text: '描述一次你与团队合作改善生产流程的经历。'
+      //   }
+      // ],
+      // fifthVideoSubtitles: [
+      //   {
+      //     startTime: 0,
+      //     endTime: 6,
+      //     text: '你在团队合作中曾遇到过哪些挑战?如何解决团队内部的分歧?'
+      //   }
+      // ],
+      // sixthVideoSubtitles: [
+      //   {
+      //     startTime: 0,
+      //     endTime: 5,
+      //     text: '您已完成本次面试全部题目,请问您对于这个岗位还有什么想要了解的吗?'
+      //   }
+      // ],
+      showAnswerButton: false, // 控制答题按钮显示
+      currentVideoIndex: 0, // 当前播放的视频索引
+      videoList: [
+        'http://121.36.251.245:9000/minlong/tenant_1/general_uploads/abdaa6fda8494e3a8613304743ed0433.mp4'//结束
+      ],
+      isRecording: false,
+      recordingTimer: null,
+      showStopRecordingButton: false,
+      mediaRecorder: null,
+      recordedChunks: [],
+      recorder: null,
+      lastUploadedVideoUrl: '',
+      showStartRecordingButton: false,
+      showRetryButton: false, // 控制重试按钮显示
+      lastVideoToRetry: null, // 存储上次失败的视频URL,用于重试
+      recordingStartTime: null, // 录制开始时间
+      recordingTimerCount: 0, // 录制计时器计数
+      recordingTimeDisplay: '00:00', // 格式化的录制时间显示
+      
+      // 添加上传队列相关数据
+      uploadQueue: [], // 存储待上传的视频
+      isUploading: false, // 标记是否正在上传
+      uploadProgress: {}, // 存储每个视频的上传进度
+      uploadStatus: {}, // 存储每个视频的上传状态
+      showUploadStatus: false, // 是否显示上传状态指示器
+      uploadStatusText: '', // 上传状态文本
+      mediaRecorderTimeout: null, // 用于存储MediaRecorder的超时机制
+      maxRecordingTime: 300, // 最大录制时间(秒)- 从60秒改为300秒(5分钟)
+      remainingTime: 300,    // 剩余录制时间(秒)- 从60秒改为300秒
+      
+      // 修改初始视频索引,添加参数控制
+      startFromLastVideo: false, // 是否从最后一个视频开始播放
+    }
+  },
+  onLoad(options) {
+    // 检查是否有参数指定从最后一个视频开始
+    if (options && options.startFromLast === 'true') {
+      this.startFromLastVideo = true;
+      // 设置为最后一个视频的索引
+      this.currentVideoIndex = this.videoList.length - 1;
+    }
+  },
+  mounted() {
+    /* this.fetchData() */
+    
+    // 如果需要从最后一个视频开始,直接设置
+    if (this.startFromLastVideo) {
+      console.log('从最后一个视频开始播放');
+      this.currentVideoIndex = this.videoList.length - 1;
+      this.videoUrl = this.videoList[this.currentVideoIndex];
+    }
+    
+    this.playDigitalHumanVideo();
+    this.checkAudioPermission();
+    this.initCamera();
+    this.checkIOSCameraRecordPermission();
+    
+    // 添加防御性检查,避免渲染错误
+    this.checkAndFixRenderingIssues();
+    
+    // 添加音频测试
+    setTimeout(() => {
+      if (this.cameraStream && !this.useMiniProgramCameraComponent) {
+        this.testAudioInput();
+      }
+    }, 3000);
+    
+    uni.setKeepScreenOn({
+      keepScreenOn: true
+    });
+  },
+  beforeDestroy() {
+    // 组件销毁前停止摄像头
+    this.stopUserCamera();
+  },
+  methods: {
+    // 初始化相机
+    async initCamera() {
+      // 检查平台
+      const systemInfo = uni.getSystemInfoSync();
+      
+      // 判断是否在小程序环境中
+      const isMiniProgram = systemInfo.uniPlatform === 'mp-weixin' || 
+                            systemInfo.uniPlatform === 'mp-alipay' || 
+                            systemInfo.uniPlatform === 'mp-baidu';
+      
+      if (isMiniProgram) {
+        // 小程序环境使用小程序的相机API
+        this.useMiniProgramCameraComponent = true;
+        
+        // 创建相机上下文
+        this.cameraContext = uni.createCameraContext();
+        
+        // 确保已获取录音和相机权限
+        uni.getSetting({
+          success: (res) => {
+            // 检查录音权限
+            if (!res.authSetting['scope.record']) {
+              uni.authorize({
+                scope: 'scope.record',
+                success: () => {
+                  console.log('录音权限已获取');
+                },
+                fail: (err) => {
+                  console.error('录音权限获取失败:', err);
+                  this.showPermissionDialog('录音');
+                }
+              });
+            }
+            
+            // 检查相机权限
+            if (!res.authSetting['scope.camera']) {
+              uni.authorize({
+                scope: 'scope.camera',
+                success: () => {
+                  console.log('相机权限已获取');
+                },
+                fail: (err) => {
+                  console.error('相机权限获取失败:', err);
+                  this.showPermissionDialog('相机');
+                }
+              });
+            }
+            
+            // 在iOS上,可能需要额外的权限检查
+            const systemInfo = uni.getSystemInfoSync();
+            if (systemInfo.platform === 'ios') {
+              // 在iOS上,不要使用无效的权限范围
+              // 移除以下代码块
+              /*
+              if (!res.authSetting['scope.camera.record']) {
+                uni.authorize({
+                  scope: 'scope.camera.record',
+                  success: () => {
+                    console.log('相机录制权限已获取');
+                  },
+                  fail: (err) => {
+                    console.error('相机录制权限获取失败:', err);
+                    this.showPermissionDialog('相机录制');
+                  }
+                });
+              }
+              */
+              
+              // 确保同时获取了相机和录音权限
+              if (!res.authSetting['scope.camera'] || !res.authSetting['scope.record']) {
+                console.log('iOS需要同时获取相机和录音权限');
+              }
+            }
+          }
+        });
+      } else {
+        // H5/App环境使用浏览器的API
+        try {
+          // 请求摄像头和麦克风权限并获取媒体流,添加更多约束
+          const constraints = {
+            audio: {
+              echoCancellation: true,
+              noiseSuppression: true,
+              autoGainControl: true
+            },
+            video: {
+              width: { ideal: 640, max: 1280 }, // 控制视频宽度
+              height: { ideal: 480, max: 720 }, // 控制视频高度
+              frameRate: { ideal: 15, max: 24 }, // 控制帧率
+              facingMode: 'user'
+            }
+          };
+          
+          const stream = await navigator.mediaDevices.getUserMedia(constraints);
+          
+          // 保存流以便后续使用
+          this.cameraStream = stream;
+          
+          // 检查音频轨道
+          const audioTracks = stream.getAudioTracks();
+          console.log('音频轨道数量:', audioTracks.length);
+          if (audioTracks.length > 0) {
+            console.log('音频轨道已获取:', audioTracks[0].label);
+            // 确保音频轨道已启用
+            audioTracks[0].enabled = true;
+          } else {
+            console.warn('未检测到音频轨道,尝试单独获取音频');
+            this.tryGetAudioOnly();
+          }
+          
+          // 将流设置到视频元素
+          const videoElement = this.$refs.userCameraVideo;
+          if (videoElement) {
+            videoElement.srcObject = stream;
+            videoElement.muted = true; // 避免回声,但仍然录制声音
+          }
+        } catch (error) {
+          console.error('获取摄像头失败:', error);
+          this.cameraError = error.message || '无法访问摄像头';
+          
+          // 显示错误提示
+          uni.showToast({
+            title: '无法访问摄像头,请检查权限设置',
+            icon: 'none'
+          });
+        }
+      }
+    },
+    
+    // 停止用户摄像头
+    stopUserCamera() {
+      if (this.cameraStream) {
+        // 停止所有轨道
+        this.cameraStream.getTracks().forEach(track => {
+          track.stop();
+        });
+        this.cameraStream = null;
+      }
+    },
+    
+    async fetchData() {
+      this.loading = true
+      this.assistantResponse = ''
+      this.audioTranscript = ''
+      this.processedResponses = []
+      try {
+        // 使用uni.request代替fetch
+        const requestTask = uni.request({
+          url: 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions',
+          method: 'POST',
+          header: {
+            'Content-Type': 'application/json',
+            'Authorization': 'Bearer sk-9e1ec73a7d97493b8613c63f06b6110c'
+          },
+          data: {
+            "model": "qwen-omni-turbo",
+            "messages":  [
+              {
+                "role": "user",
+                "content": [
+                  {
+                    "type": "input_audio",
+                    "input_audio": {
+                      "data": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20250211/tixcef/cherry.wav",
+                      "format": "wav"
+                    }
+                  },
+                  {
+                    "type": "text",
+                    "text": "这段音频在说什么"
+                  }
+                ]
+              }
+            ],
+            "stream":true,
+            "stream_options":{
+                "include_usage":true
+            },
+            "modalities":["text","audio"],
+            "audio":{"voice":"Cherry","format":"wav"}
+          },
+          success: (res) => {
+            console.log('请求成功,响应数据:', res.data);
+            // 检查响应数据是否包含多个JSON对象
+            if (typeof res.data === 'string' && res.data.includes('data: {')) {
+              // 处理包含多个JSON对象的情况
+              const chunks = res.data.split('data: ').filter(chunk => chunk.trim() !== '');
+              chunks.forEach(chunk => {
+                this.handleStreamResponse(chunk);
+              });
+            } else {
+              // 处理单个响应对象的情况
+              this.handleStreamResponse(res.data);
+            }
+            
+            // 模拟获取到数字人视频并播放
+            this.playDigitalHumanVideo();
+          },
+          fail: (err) => {
+            console.error('请求失败:', err);
+          },
+          complete: () => {
+            this.loading = false;
+          }
+        });
+      } catch (error) {
+        console.error('获取数据失败:', error);
+        this.loading = false;
+      }
+    },
+    
+    handleStreamResponse(data) {
+      // 处理流式响应数据
+      if (typeof data === 'string') {
+        // 处理字符串格式的响应
+        if (data === '[DONE]') return;
+        
+        try {
+          // 移除可能存在的换行符和多余空格
+          const cleanData = data.trim();
+          // 检查是否是有效的JSON字符串
+          if (cleanData.startsWith('{') && cleanData.endsWith('}')) {
+            const jsonData = JSON.parse(cleanData);
+            this.processStreamChunk(jsonData);
+          }
+        } catch (e) {
+          console.error('解析JSON失败:', e, '原始数据:', data);
+        }
+      } else {
+        // 处理对象格式的响应
+        this.processStreamChunk(data);
+      }
+    },
+    
+    processStreamChunk(chunk) {
+      if (chunk.choices && chunk.choices.length > 0) {
+        const choice = chunk.choices[0];
+        
+        // 处理助手回复内容
+        if (choice.delta && choice.delta.content) {
+          this.assistantResponse += choice.delta.content;
+        }
+        
+        // 处理音频转写内容
+        if (choice.delta && choice.delta.audio && choice.delta.audio.transcript) {
+          this.audioTranscript += choice.delta.audio.transcript;
+        }
+        
+        // 处理角色和音频转写
+        if (choice.delta) {
+          const result = {};
+          
+          if (choice.delta.role) {
+            result.role = choice.delta.role;
+          }
+          
+          if (choice.delta.audio && choice.delta.audio.transcript) {
+            result.transcript = choice.delta.audio.transcript;
+          }
+          
+          if (Object.keys(result).length > 0) {
+            this.processedResponses.push(result);
+          }
+        }
+      }
+    },
+    
+    processResponseData() {
+      // 处理返回的数据
+      this.processedResponses = this.responses.map(item => {
+        const result = {}
+        
+        // 处理角色信息
+        if (item.delta && item.delta.role) {
+          result.role = item.delta.role
+        }
+        
+        // 处理音频转写文本
+        if (item.delta && item.delta.audio && item.delta.audio.transcript) {
+          result.transcript = item.delta.audio.transcript
+        }
+        
+        return result
+      }).filter(item => Object.keys(item).length > 0)
+    },
+    
+    // 播放数字人视频
+    playDigitalHumanVideo() {
+      // 设置当前视频
+      this.videoUrl = this.videoList[this.currentVideoIndex];
+      this.videoPlaying = true;
+      
+      console.log(`播放视频 ${this.currentVideoIndex + 1}/${this.videoList.length}: ${this.videoUrl}`);
+      
+      // 使用 uni.createVideoContext 来控制视频
+      this.$nextTick(() => {
+        const videoContext = uni.createVideoContext('myVideo', this);
+        if (videoContext) {
+          videoContext.play();
+          
+          // 设置超时检查,确认视频是否真的在播放
+          setTimeout(() => {
+            if (this.videoPlaying && this.$refs.videoPlayer) {
+              console.log('视频应该正在播放');
+            } else {
+              console.log('视频可能未成功播放,尝试替代方案');
+              this.tryAlternativeVideoPath();
+            }
+          }, 1000);
+        } else {
+          console.error('无法创建视频上下文');
+          this.tryAlternativeVideoPath();
+        }
+      });
+    },
+    
+    // 修改 tryAlternativeVideoPath 方法
+    tryAlternativeVideoPath() {
+      console.log('尝试使用替代路径');
+      
+      // 尝试不同的路径格式
+      const alternativePaths = [
+        './static/demo.mp4',
+        '../static/demo.mp4',
+        'static/demo.mp4',
+        '/static/demo.mp4',
+        // 添加绝对路径
+        `${window.location.origin}/static/demo.mp4`
+      ];
+      
+      // 获取当前路径索引
+      const currentPathIndex = alternativePaths.indexOf(this.videoUrl);
+      const nextPathIndex = (currentPathIndex + 1) % alternativePaths.length;
+      
+      // 设置下一个路径
+      this.videoUrl = alternativePaths[nextPathIndex];
+      console.log('尝试新路径:', this.videoUrl);
+      
+      this.$nextTick(() => {
+        const videoContext = uni.createVideoContext('myVideo', this);
+        if (videoContext) {
+          videoContext.stop();
+          videoContext.play();
+          
+          // 检查是否成功播放
+          setTimeout(() => {
+            if (nextPathIndex === alternativePaths.length - 1 && !this.videoPlaying) {
+              console.log('所有路径均失败,尝试使用uni.getVideoInfo检查视频');
+              this.checkVideoWithAPI();
+            }
+          }, 1000);
+        }
+      });
+    },
+    
+    // 添加新方法:使用uni API检查视频
+    checkVideoWithAPI() {
+      // 尝试使用uni.getVideoInfo API检查视频是否可用
+      uni.getVideoInfo({
+        src: '/static/demo.mp4',
+        success: (res) => {
+          console.log('视频信息获取成功:', res);
+          // 如果能获取到视频信息,再次尝试播放
+          this.videoUrl = '/static/demo.mp4';
+          this.$nextTick(() => {
+            const videoContext = uni.createVideoContext('myVideo', this);
+            if (videoContext) {
+              videoContext.play();
+            }
+          });
+        },
+        fail: (err) => {
+          console.error('视频信息获取失败:', err);
+          // 最后尝试使用uni.chooseVideo API
+          this.fallbackToLocalVideo();
+        }
+      });
+    },
+    
+    // 添加新方法:回退到本地视频
+    fallbackToLocalVideo() {
+      console.log('尝试使用本地视频资源');
+      
+      // 检查平台
+      const platform = uni.getSystemInfoSync().platform;
+      if (platform === 'android' || platform === 'ios') {
+        // 移动端可以尝试使用本地资源
+        this.videoUrl = platform === 'android' ? 'android.resource://package_name/raw/demo' : 'file:///assets/demo.mp4';
+        this.$nextTick(() => {
+          const videoContext = uni.createVideoContext('myVideo', this);
+          if (videoContext) {
+            videoContext.play();
+          }
+        });
+      } else {
+        // 最终回退到静态图片
+        this.videoPlaying = false;
+        uni.showToast({
+          title: '视频加载失败,显示静态图片',
+          icon: 'none'
+        });
+      }
+    },
+    
+    // 修改 handleVideoError 方法
+    handleVideoError(e) {
+      console.error('视频加载错误:', e);
+      
+      // 记录更详细的错误信息
+      if (e && e.detail) {
+        console.error('详细错误信息:', e.detail);
+      }
+      
+      // 检查视频文件是否存在
+      uni.getFileInfo({
+        filePath: this.videoUrl.startsWith('/') ? this.videoUrl.substring(1) : this.videoUrl,
+        success: (res) => {
+          console.log('文件存在,大小:', res.size);
+          // 文件存在但播放失败,可能是格式问题
+          this.tryDifferentFormat();
+        },
+        fail: (err) => {
+          console.error('文件不存在或无法访问:', err);
+          // 尝试不同路径
+          this.tryAlternativeVideoPath();
+        }
+      });
+      
+      // 如果多次尝试后仍然失败,显示错误信息
+      uni.showToast({
+        title: '视频加载失败,请检查文件是否存在',
+        icon: 'none',
+        duration: 2000
+      });
+    },
+
+    // 添加新方法:尝试不同格式
+    tryDifferentFormat() {
+      console.log('尝试不同的视频格式');
+      
+      // 尝试不同的视频格式
+      const formats = [
+        { ext: 'mp4', mime: 'video/mp4' },
+        { ext: 'webm', mime: 'video/webm' },
+        { ext: 'ogg', mime: 'video/ogg' },
+        { ext: 'mov', mime: 'video/quicktime' }
+      ];
+      
+      // 获取当前文件名(不含扩展名)
+      const currentPath = this.videoUrl;
+      const basePath = currentPath.substring(0, currentPath.lastIndexOf('.')) || '/static/demo';
+      
+      // 尝试下一个格式
+      let nextFormat = formats.find(f => !currentPath.endsWith(f.ext));
+      if (nextFormat) {
+        this.videoUrl = `${basePath}.${nextFormat.ext}`;
+        console.log('尝试新格式:', this.videoUrl);
+        
+        this.$nextTick(() => {
+          const videoContext = uni.createVideoContext('myVideo', this);
+          if (videoContext) {
+            videoContext.stop();
+            videoContext.play();
+          }
+        });
+      } else {
+        // 所有格式都尝试过了,使用内置资源
+        this.useBuiltInResource();
+      }
+    },
+
+    // 添加新方法:使用内置资源
+    useBuiltInResource() {
+      console.log('尝试使用内置资源');
+      
+      // 检查平台
+      const platform = uni.getSystemInfoSync().platform;
+      
+      // 根据平台选择合适的视频源
+      if (platform === 'windows') {
+        // Windows平台特定处理
+        // 尝试使用相对于应用根目录的路径
+        const appRoot = process.env.UNI_INPUT_DIR || '';
+        this.videoUrl = `./static/demo.mp4`;
+        
+        // 或者尝试使用file://协议
+        // this.videoUrl = `file:///${appRoot.replace(/\\/g, '/')}/static/demo.mp4`;
+        
+        console.log('Windows平台尝试路径:', this.videoUrl);
+      } else if (platform === 'android' || platform === 'ios') {
+        // 移动端
+        this.useNativeVideo();
+      } else {
+        // Web平台
+        // 尝试使用完整URL
+        const baseUrl = window.location.origin;
+        this.videoUrl = `${baseUrl}/static/demo.mp4`;
+        console.log('Web平台尝试URL:', this.videoUrl);
+      }
+      
+      this.$nextTick(() => {
+        const videoContext = uni.createVideoContext('myVideo', this);
+        if (videoContext) {
+          videoContext.play();
+        }
+      });
+    },
+
+    // 添加新方法:使用原生视频能力
+    useNativeVideo() {
+      console.log('尝试使用原生视频能力');
+      
+      // 在移动端,可以尝试使用原生视频播放器
+      uni.chooseVideo({
+        sourceType: ['album'],
+        success: (res) => {
+          this.videoUrl = res.tempFilePath;
+          this.$nextTick(() => {
+            const videoContext = uni.createVideoContext('myVideo', this);
+            if (videoContext) {
+              videoContext.play();
+            }
+          });
+        },
+        fail: () => {
+          // 如果用户取消选择,回退到静态图片
+          this.videoPlaying = false;
+          uni.showToast({
+            title: '无法加载视频,显示静态图片',
+            icon: 'none'
+          });
+        }
+      });
+    },
+
+    // 处理视频结束事件
+    handleVideoEnded() {
+      console.log('视频播放结束,当前视频索引:', this.currentVideoIndex);
+      this.videoPlaying = false;
+      
+      // 视频结束后直接显示"开始作答"按钮
+      this.showStartRecordingButton = true;
+      this.showAnswerButton = false;
+    },
+    
+    // 添加新方法:开始录制用户回答
+    startRecordingAnswer() {
+      console.log('开始录制用户回答');
+      this.isRecording = true;
+      
+      // 记录录制开始时间
+      this.recordingStartTime = Date.now();
+      this.recordingTimerCount = 0;
+      this.remainingTime = this.maxRecordingTime;
+      
+      // 启动计时器,每秒更新计时
+      this.recordingTimer = setInterval(() => {
+        this.recordingTimerCount++;
+        this.remainingTime = Math.max(0, this.maxRecordingTime - this.recordingTimerCount);
+        
+        // 更新UI显示录制时长和剩余时间
+        this.recordingTimeDisplay = this.formatTime(this.recordingTimerCount) + 
+                                   ' / ' + this.formatTime(this.maxRecordingTime);
+        
+        // 检查是否达到最大录制时间
+        if (this.recordingTimerCount >= this.maxRecordingTime) {
+          console.log('已达到最大录制时间(60秒),自动停止录制');
+          this.stopRecordingAnswer();
+        }
+      }, 1000);
+      
+      // 显示录制中的提示
+      // uni.showLoading({
+      //   title: '正在录制...',
+      //   mask: true
+      // });
+      
+      // 根据平台选择不同的录制方法
+      const systemInfo = uni.getSystemInfoSync();
+      const isMiniProgram = systemInfo.uniPlatform && systemInfo.uniPlatform.startsWith('mp-');
+      
+      if (isMiniProgram) {
+        // 小程序环境使用小程序的录制API
+        this.startMiniProgramRecording();
+      } else {
+        // H5/App环境使用MediaRecorder API
+        this.startBrowserRecording();
+      }
+      
+      // 显示停止录制按钮
+      this.showStopRecordingButton = true;
+    },
+
+    // 添加一个新方法:重置相机组件
+    resetCamera() {
+      console.log('重置相机组件');
+      
+      // 先完全移除相机组件
+      this.useMiniProgramCameraComponent = false;
+      
+      // 释放相机上下文
+      if (this.cameraContext) {
+        this.cameraContext = null;
+      }
+      
+      // 延迟后重新创建相机组件
+      setTimeout(() => {
+        this.useMiniProgramCameraComponent = true;
+        
+        // 再次延迟创建相机上下文,确保组件已完全渲染
+        setTimeout(() => {
+          this.cameraContext = uni.createCameraContext();
+          console.log('相机组件已重置');
+        }, 500);
+      }, 500);
+    },
+
+    // 修改 startMiniProgramRecording 方法
+    startMiniProgramRecording() {
+      console.log('开始小程序录制方法');
+      
+      // 获取平台信息
+      const systemInfo = uni.getSystemInfoSync();
+      const isIOS = systemInfo.platform === 'ios';
+      
+      // 在iOS上,先重置相机
+      if (isIOS) {
+        // 先重置相机组件
+        this.resetCamera();
+        
+        // 延迟执行录制,给相机组件足够的初始化时间
+        setTimeout(() => {
+          this.actualStartRecording(isIOS);
+        }, 1000);
+      } else {
+        // Android直接开始
+        this.actualStartRecording(isIOS);
+      }
+    },
+
+    // 添加新方法:实际开始录制
+    actualStartRecording(isIOS) {
+      if (!this.cameraContext) {
+        this.cameraContext = uni.createCameraContext();
+        console.log('创建新的相机上下文');
+      }
+      
+      // 确保有录音权限
+      uni.getSetting({
+        success: (res) => {
+          const hasRecordAuth = res.authSetting['scope.record'];
+          const hasCameraAuth = res.authSetting['scope.camera'];
+          
+          if (!hasRecordAuth || !hasCameraAuth) {
+            console.warn('缺少必要权限,请求权限');
+            this.requestMiniProgramPermissions();
+            return;
+          }
+          
+          // 在iOS上,先检查相机状态
+          if (isIOS) {
+            console.log('iOS: 检查相机状态');
+            
+            // 使用最简单的选项,设置最大录制时间为5分钟
+            const options = {
+              timeout: 300000, // 300秒超时 (5分钟)
+              quality: 'low', // 降低质量
+              compressed: true,
+              success: () => {
+                console.log('iOS录制开始成功');
+              },
+              fail: (err) => {
+                console.error('iOS录制失败:', err);
+                
+                // 如果失败,尝试使用不同的方法
+                this.useAlternativeRecordingMethod();
+              }
+            };
+            
+            try {
+              console.log('尝试开始录制');
+              this.recorder = this.cameraContext.startRecord(options);
+            } catch (e) {
+              console.error('开始录制异常:', e);
+              this.useAlternativeRecordingMethod();
+            }
+          } else {
+            // Android使用标准选项,设置最大录制时间为5分钟
+            const options = {
+              timeout: 300000, // 300秒超时 (5分钟)
+              quality: 'medium',
+              compressed: true,
+              success: () => {
+                console.log('Android录制开始成功');
+              },
+              fail: (err) => {
+                console.error('Android录制失败:', err);
+                uni.showToast({
+                  title: '录制失败,请检查相机权限',
+                  icon: 'none'
+                });
+                this.proceedToNextQuestion();
+              }
+            };
+            
+            this.recorder = this.cameraContext.startRecord(options);
+          }
+        }
+      });
+    },
+
+    // 添加新方法:使用替代录制方法
+    useAlternativeRecordingMethod() {
+      console.log('使用替代录制方法');
+      
+      // 在iOS上,可以尝试使用chooseVideo API作为备选
+      uni.showActionSheet({
+        itemList: ['使用相册中的视频', '跳过此问题'],
+        success: (res) => {
+          if (res.tapIndex === 0) {
+            // 选择相册中的视频
+            uni.chooseVideo({
+              sourceType: ['album'],
+              maxDuration: 300, // 从60秒改为300秒
+              camera: 'front',
+              success: (res) => {
+                console.log('选择视频成功:', res.tempFilePath);
+                // 停止录制状态
+                this.isRecording = false;
+                this.showStopRecordingButton = false;
+                
+                // 上传选择的视频
+                this.uploadRecordedVideo(res.tempFilePath);
+              },
+              fail: () => {
+                console.log('用户取消选择视频');
+                this.proceedToNextQuestion();
+              }
+            });
+          } else {
+            // 跳过此问题
+            console.log('用户选择跳过问题');
+            this.proceedToNextQuestion();
+          }
+        },
+        fail: () => {
+          console.log('操作取消');
+          this.proceedToNextQuestion();
+        }
+      });
+    },
+
+    // 添加新方法:请求小程序权限
+    requestMiniProgramPermissions() {
+      // 请求录音权限
+      uni.authorize({
+        scope: 'scope.record',
+        success: () => {
+          console.log('录音权限已获取');
+          
+          // 请求相机权限
+          uni.authorize({
+            scope: 'scope.camera',
+            success: () => {
+              console.log('相机权限已获取');
+              // 权限都获取到后,开始录制
+              this.startMiniProgramRecording();
+            },
+            fail: (err) => {
+              console.error('相机权限获取失败:', err);
+              this.showPermissionDialog('相机');
+            }
+          });
+        },
+        fail: (err) => {
+          console.error('录音权限获取失败:', err);
+          this.showPermissionDialog('录音');
+        }
+      });
+    },
+
+    // 修改浏览器环境下的录制方法
+    startBrowserRecording() {
+      if (!this.cameraStream) {
+        console.error('没有可用的摄像头流');
+        uni.showToast({
+          title: '录制失败,摄像头未就绪',
+          icon: 'none'
+        });
+        this.proceedToNextQuestion();
+        return;
+      }
+      
+      try {
+        // 检查流中是否包含音频轨道
+        const hasAudio = this.cameraStream.getAudioTracks().length > 0;
+        if (!hasAudio) {
+          console.warn('警告:媒体流中没有音频轨道,尝试重新获取带音频的媒体流');
+          
+          // 尝试重新获取带音频的媒体流
+          navigator.mediaDevices.getUserMedia({ 
+            audio: {
+              echoCancellation: true,
+              noiseSuppression: true,
+              autoGainControl: true
+            }, 
+            video: true 
+          })
+            .then(newStream => {
+              // 检查新流是否包含音频轨道
+              const audioTracks = newStream.getAudioTracks();
+              if (audioTracks.length > 0) {
+                console.log('成功获取音频轨道:', audioTracks[0].label);
+                
+                // 合并视频和音频轨道
+                const videoTrack = this.cameraStream.getVideoTracks()[0];
+                const audioTrack = newStream.getAudioTracks()[0];
+                
+                // 创建新的媒体流,包含视频和音频轨道
+                const combinedStream = new MediaStream();
+                if (videoTrack) combinedStream.addTrack(videoTrack);
+                if (audioTrack) combinedStream.addTrack(audioTrack);
+                
+                this.cameraStream = combinedStream;
+                
+                // 更新视频元素的源
+                const videoElement = this.$refs.userCameraVideo;
+                if (videoElement) {
+                  videoElement.srcObject = combinedStream;
+                  videoElement.muted = true; // 避免回声,但仍然录制声音
+                }
+                
+                // 继续录制过程
+                this.setupMediaRecorder(combinedStream);
+              } else {
+                console.warn('仍然无法获取音频轨道');
+                // 使用现有流继续
+                this.setupMediaRecorder(this.cameraStream);
+              }
+            })
+            .catch(err => {
+              console.error('获取音频失败:', err);
+              // 使用现有流继续
+              this.setupMediaRecorder(this.cameraStream);
+            });
+        } else {
+          console.log('检测到音频轨道,直接使用');
+          // 已有音频轨道,直接使用
+          this.setupMediaRecorder(this.cameraStream);
+        }
+      } catch (error) {
+        console.error('浏览器录制失败:', error);
+        uni.showToast({
+          title: '录制失败,浏览器可能不支持此功能',
+          icon: 'none'
+        });
+        this.proceedToNextQuestion();
+      }
+    },
+
+    // 修改 setupMediaRecorder 方法
+    setupMediaRecorder(stream) {
+      // 检查流中的轨道
+      const videoTracks = stream.getVideoTracks();
+      const audioTracks = stream.getAudioTracks();
+      
+      console.log('设置MediaRecorder - 视频轨道:', videoTracks.length, '音频轨道:', audioTracks.length);
+      
+      // 尝试使用支持度更广的编码格式
+      let mimeType = '';
+      const supportedTypes = [
+        'video/webm;codecs=vp9,opus',
+        'video/webm;codecs=vp8,opus',
+        'video/webm;codecs=h264,opus',
+        'video/mp4;codecs=h264,aac',
+        'video/webm',
+        'video/mp4'
+      ];
+      
+      for (const type of supportedTypes) {
+        if (MediaRecorder.isTypeSupported(type)) {
+          mimeType = type;
+          console.log('使用支持的MIME类型:', mimeType);
+          break;
+        }
+      }
+      
+      // 创建MediaRecorder实例,设置较低的比特率以减小文件大小
+      const options = {
+        mimeType: mimeType || '',
+        audioBitsPerSecond: 64000,    // 降低音频比特率
+        videoBitsPerSecond: 1000000,  // 降低视频比特率到1Mbps
+      };
+      
+      try {
+        this.mediaRecorder = new MediaRecorder(stream, options);
+        console.log('MediaRecorder创建成功,使用选项:', options);
+      } catch (e) {
+        console.warn('使用指定选项创建MediaRecorder失败,尝试使用默认选项');
+        this.mediaRecorder = new MediaRecorder(stream);
+      }
+      
+      // 存储录制的数据块
+      this.recordedChunks = [];
+      
+      // 监听数据可用事件
+      this.mediaRecorder.ondataavailable = (event) => {
+        if (event.data && event.data.size > 0) {
+          this.recordedChunks.push(event.data);
+          console.log(`收到数据块: ${event.data.size} 字节`);
+        }
+      };
+      
+      // 监听录制停止事件
+      this.mediaRecorder.onstop = async () => {
+        console.log('MediaRecorder停止,数据块数量:', this.recordedChunks.length);
+        
+        if (this.recordedChunks.length === 0) {
+          console.error('没有录制到数据');
+          uni.showToast({
+            title: '录制失败,未捕获到数据',
+            icon: 'none'
+          });
+          this.proceedToNextQuestion();
+          return;
+        }
+        
+        // 创建Blob对象
+        const mimeType = this.mediaRecorder.mimeType || 'video/webm';
+        const blob = new Blob(this.recordedChunks, { type: mimeType });
+        console.log('创建Blob,原始大小:', blob.size, '类型:', mimeType);
+        
+        // 显示压缩中提示
+        uni.showLoading({
+          title: '正在处理视频...',
+          mask: true
+        });
+        
+        try {
+          // 压缩视频
+          const compressedBlob = await this.compressVideo(blob);
+          
+          // 将Blob转换为File对象
+          const fileName = `answer_${this.currentVideoIndex}_${Date.now()}.webm`;
+          const file = new File([compressedBlob], fileName, { type: mimeType });
+          
+          // 隐藏压缩提示
+          uni.hideLoading();
+          
+          // 上传文件
+          this.uploadRecordedVideo(file);
+        } catch (error) {
+          console.error('视频处理失败:', error);
+          uni.hideLoading();
+          
+          // 如果压缩失败,使用原始视频
+          const fileName = `answer_${this.currentVideoIndex}_${Date.now()}.webm`;
+          const file = new File([blob], fileName, { type: mimeType });
+          this.uploadRecordedVideo(file);
+        }
+      };
+      
+      // 监听错误
+      this.mediaRecorder.onerror = (event) => {
+        console.error('MediaRecorder错误:', event.error);
+      };
+      
+      // 开始录制
+      try {
+        this.mediaRecorder.start(1000); // 每秒触发一次dataavailable事件
+        console.log('MediaRecorder开始录制');
+        
+        // 设置最大录制时间为60秒
+        // 注意:我们已经在startRecordingAnswer方法中设置了全局计时器
+        // 这里作为备份机制,确保即使全局计时器失效,也能停止录制
+        this.mediaRecorderTimeout = setTimeout(() => {
+          if (this.mediaRecorder && this.mediaRecorder.state === 'recording') {
+            console.log('MediaRecorder备份超时机制触发,停止录制');
+            this.mediaRecorder.stop();
+          }
+        }, 300000); // 300秒 (5分钟)
+      } catch (e) {
+        console.error('开始录制失败:', e);
+      }
+    },
+
+    // 添加新方法:停止录制用户回答
+    stopRecordingAnswer() {
+      console.log('停止录制用户回答');
+      
+      // 检查录制时长
+      const recordingDuration = this.getRecordingDuration();
+      const minimumDuration = 5; // 最小录制时长(秒),从3秒改为5秒
+      
+      if (recordingDuration < minimumDuration) {
+        // 录制时间过短,显示提示
+        uni.showToast({
+          title: '录制时间过短,请至少录制5秒',
+          icon: 'none',
+          duration: 2000
+        });
+        // 不执行停止录制逻辑,继续录制
+        return;
+      }
+      
+      // 录制时长足够,直接停止
+      this.completeRecordingStop();
+    },
+
+    // 添加新方法:获取录制时长
+    getRecordingDuration() {
+      // 如果有明确的录制开始时间,计算实际录制时长
+      if (this.recordingStartTime) {
+        return (Date.now() - this.recordingStartTime) / 1000;
+      }
+      
+      // 如果没有明确记录开始时间,尝试从视频元素获取
+      if (this.mediaRecorder && this.$refs.userCameraVideo) {
+        return this.$refs.userCameraVideo.currentTime || 0;
+      }
+      
+      // 如果无法获取准确时长,返回估计值(如果有计时器)
+      if (this.recordingTimerCount) {
+        return this.recordingTimerCount;
+      }
+      
+      // 默认返回0
+      return 0;
+    },
+
+    // 添加新方法:重置录制
+    resetRecording() {
+      // 保持录制状态,但重置计时器
+      if (this.recordingTimer) {
+        clearTimeout(this.recordingTimer);
+      }
+      
+      // 重置录制开始时间
+      this.recordingStartTime = Date.now();
+      this.recordingTimerCount = 0;
+      
+      // 如果是浏览器环境,需要重置MediaRecorder
+      if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
+        // 停止当前录制
+        this.mediaRecorder.stop();
+        
+        // 清空已录制的数据块
+        this.recordedChunks = [];
+        
+        // 短暂延迟后重新开始录制
+        setTimeout(() => {
+          this.startBrowserRecording();
+        }, 500);
+      } 
+      // 如果是小程序环境,需要重置相机录制
+      else if (this.cameraContext) {
+        // 停止当前录制
+        this.cameraContext.stopRecord({
+          success: () => {
+            console.log('重置录制:停止当前录制成功');
+            // 短暂延迟后重新开始录制
+            setTimeout(() => {
+              this.startMiniProgramRecording();
+            }, 500);
+          },
+          fail: (err) => {
+            console.error('重置录制:停止当前录制失败', err);
+            // 尝试直接重新开始录制
+            this.startMiniProgramRecording();
+          }
+        });
+      }
+      
+      // 显示提示
+      uni.showToast({
+        title: '请重新开始回答',
+        icon: 'none',
+        duration: 2000
+      });
+    },
+
+    // 添加新方法:完成录制停止流程
+    completeRecordingStop() {
+      this.isRecording = false;
+      
+      // 清除定时器
+      if (this.recordingTimer) {
+        clearInterval(this.recordingTimer);
+        this.recordingTimer = null;
+      }
+      
+      // 清除MediaRecorder超时定时器
+      if (this.mediaRecorderTimeout) {
+        clearTimeout(this.mediaRecorderTimeout);
+        this.mediaRecorderTimeout = null;
+      }
+      
+      // 隐藏录制中提示
+      uni.hideLoading();
+      
+      // 隐藏停止录制按钮
+      this.showStopRecordingButton = false;
+      
+      // 根据平台选择不同的停止录制方法
+      const systemInfo = uni.getSystemInfoSync();
+      const isMiniProgram = systemInfo.uniPlatform && systemInfo.uniPlatform.startsWith('mp-');
+      
+      if (isMiniProgram) {
+        // 小程序环境停止录制
+        this.stopMiniProgramRecording();
+      } else {
+        // H5/App环境停止录制
+        this.stopBrowserRecording();
+      }
+    },
+
+    // 修改 stopMiniProgramRecording 方法
+    stopMiniProgramRecording() {
+      if (!this.cameraContext) {
+        console.error('相机上下文不存在');
+        this.proceedToNextQuestion();
+        return;
+      }
+      
+      const systemInfo = uni.getSystemInfoSync();
+      const isIOS = systemInfo.platform === 'ios';
+      
+      // 在iOS上添加额外的错误处理
+      const stopOptions = {
+        success: (res) => {
+          console.log('小程序录像停止成功:', res);
+          // 获取临时文件路径
+          const tempFilePath = res.tempVideoPath;
+          if (!tempFilePath) {
+            console.error('未获取到视频文件路径');
+            uni.showToast({
+              title: '录制失败,未获取到视频文件',
+              icon: 'none'
+            });
+            this.proceedToNextQuestion();
+            return;
+          }
+          
+          // 在iOS上,检查文件是否存在
+          if (isIOS) {
+            uni.getFileInfo({
+              filePath: tempFilePath,
+              success: () => {
+                // 文件存在,上传视频
+                this.uploadRecordedVideo(tempFilePath);
+              },
+              fail: (err) => {
+                console.error('视频文件不存在:', err);
+                uni.showToast({
+                  title: '录制失败,视频文件不存在',
+                  icon: 'none'
+                });
+                this.proceedToNextQuestion();
+              }
+            });
+          } else {
+            // Android直接上传
+            this.uploadRecordedVideo(tempFilePath);
+          }
+        },
+        fail: (err) => {
+          console.error('小程序录像停止失败:', err);
+          uni.showToast({
+            title: '录制失败',
+            icon: 'none'
+          });
+          this.proceedToNextQuestion();
+        }
+      };
+      
+      // 停止录制
+      this.cameraContext.stopRecord(stopOptions);
+    },
+
+    // 添加新方法:停止浏览器录制
+    stopBrowserRecording() {
+      if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
+        this.mediaRecorder.stop();
+        console.log('浏览器录制停止成功');
+      } else {
+        console.error('MediaRecorder不存在或已经停止');
+        this.proceedToNextQuestion();
+      }
+    },
+
+    // 修改上传录制的视频方法
+    uploadRecordedVideo(fileOrPath) {
+      console.log('准备上传视频:', typeof fileOrPath === 'string' ? fileOrPath : fileOrPath.name);
+      
+      // 设置固定的问题ID
+      let questionId = 10;
+      
+      // 创建上传任务对象
+      const uploadTask = {
+        id: Date.now().toString(), // 生成唯一ID
+        file: fileOrPath,
+        questionId: questionId,
+        attempts: 0, // 上传尝试次数
+        maxAttempts: 3, // 最大尝试次数
+      };
+      
+      // 添加到上传队列
+      this.uploadQueue.push(uploadTask);
+      
+      // 初始化上传进度和状态
+      this.uploadProgress[uploadTask.id] = 0;
+      this.uploadStatus[uploadTask.id] = 'pending'; // pending, uploading, success, failed
+      
+      // 显示简短的上传状态提示
+      uni.showToast({
+        title: '视频已加入上传队列',
+        icon: 'none',
+        duration: 1500
+      });
+      
+      // 更新上传状态文本
+      this.updateUploadStatusText();
+      
+      // 如果当前没有上传任务在进行,开始处理队列
+      if (!this.isUploading) {
+        this.processUploadQueue();
+      }
+      
+      // 不再等待上传完成,直接准备跳转
+      this.proceedToNextQuestion();
+    },
+    
+    // 修改 processUploadQueue 方法,添加后台上传支持
+    processUploadQueue() {
+      // 如果队列为空,结束处理
+      if (this.uploadQueue.length === 0) {
+        this.isUploading = false;
+        this.showUploadStatus = false;
+        
+        // 清除上传状态
+        try {
+          uni.removeStorageSync('videoUploadStatus');
+        } catch (e) {
+          console.error('清除上传状态失败:', e);
+        }
+        
+        // 发送上传完成通知
+        this.notifyUploadComplete();
+        return;
+      }
+      
+      // 标记为正在上传
+      this.isUploading = true;
+      this.showUploadStatus = true;
+      
+      // 获取队列中的第一个任务
+      const task = this.uploadQueue[0];
+      
+      // 更新任务状态
+      this.uploadStatus[task.id] = 'uploading';
+      this.updateUploadStatusText();
+      
+      // 增加尝试次数
+      task.attempts++;
+      
+      // 根据文件类型选择上传方法
+      if (typeof task.file !== 'string') {
+        // 浏览器环境,使用XMLHttpRequest上传
+        this.uploadFileWithXHR(task);
+      } else {
+        // 小程序环境,使用uni.uploadFile上传
+        this.uploadFileWithUni(task);
+      }
+      
+      // 更新上传状态到本地存储
+      this.saveUploadStatus();
+    },
+    
+    // 添加新方法:发送上传完成通知
+    notifyUploadComplete() {
+      // 使用uni.postMessage在页面间通信(小程序环境)
+      try {
+        uni.$emit('videoUploadComplete', {
+          timestamp: Date.now()
+        });
+        
+        console.log('发送上传完成通知');
+      } catch (e) {
+        console.error('发送上传完成通知失败:', e);
+      }
+      
+      // 如果在App环境,可以使用本地通知
+      const systemInfo = uni.getSystemInfoSync();
+      if (systemInfo.platform === 'android' || systemInfo.platform === 'ios') {
+        // 检查是否支持本地通知
+        if (plus && plus.push) {
+          plus.push.createMessage('视频上传完成', '您的面试视频已成功上传', {});
+        }
+      }
+    },
+    
+    // 添加新方法:使用XMLHttpRequest上传文件
+    uploadFileWithXHR(task) {
+      // 获取用户信息
+      const userInfo = uni.getStorageSync('userInfo');
+      const openid = userInfo ? (JSON.parse(userInfo).openid || '') : '';
+      const tenant_id = uni.getStorageSync('tenant_id') || '1';
+      
+      // 创建FormData
+      const formData = new FormData();
+      formData.append('file', task.file);
+      formData.append('openid', openid);
+      formData.append('tenant_id', tenant_id);
+      formData.append('application_id', uni.getStorageSync('appId'));
+      formData.append('question_id', task.questionId);
+      formData.append('video_duration', 0);
+      formData.append('has_audio', 'true');
+      
+      // 创建XMLHttpRequest
+      const xhr = new XMLHttpRequest();
+      xhr.open('POST', `${apiBaseUrl}/api/upload/`, true);
+      xhr.timeout = 120000; // 2分钟超时
+      
+      // 监听上传进度
+      xhr.upload.onprogress = (event) => {
+        if (event.lengthComputable) {
+          const percentComplete = Math.round((event.loaded / event.total) * 100);
+          this.uploadProgress[task.id] = percentComplete;
+          this.updateUploadStatusText();
+        }
+      };
+      
+      // 监听请求完成
+      xhr.onload = () => {
+        if (xhr.status === 200) {
+          try {
+            const res = JSON.parse(xhr.responseText);
+            console.log('上传响应:', res);
+            if (res.code === 2000) {
+              // 获取上传后的视频URL
+              const videoUrl = res.data.url || res.data.photoUrl || '';
+              if (videoUrl) {
+                // 上传成功,更新状态
+                this.uploadStatus[task.id] = 'success';
+                this.updateUploadStatusText();
+                
+                // 提交到面试接口
+                this.submitVideoToInterview(videoUrl, task);
+              } else {
+                this.handleUploadFailure(task, '视频URL获取失败');
+              }
+            } else {
+              this.handleUploadFailure(task, res.msg || '上传失败');
+            }
+          } catch (e) {
+            this.handleUploadFailure(task, '解析响应失败');
+          }
+        } else {
+          this.handleUploadFailure(task, 'HTTP状态: ' + xhr.status);
+        }
+      };
+      
+      // 监听错误
+      xhr.onerror = () => {
+        this.handleUploadFailure(task, '网络错误');
+      };
+      
+      // 监听超时
+      xhr.ontimeout = () => {
+        this.handleUploadFailure(task, '上传超时');
+      };
+      
+      // 发送请求
+      xhr.send(formData);
+    },
+    
+    // 添加新方法:使用uni.uploadFile上传文件
+    uploadFileWithUni(task) {
+      // 获取用户信息
+      const userInfo = uni.getStorageSync('userInfo');
+      const openid = userInfo ? (JSON.parse(userInfo).openid || '') : '';
+      const tenant_id = uni.getStorageSync('tenant_id') || '1';
+      
+      // 创建上传任务
+      const uploadTask = uni.uploadFile({
+        url: `${apiBaseUrl}/api/upload/`,
+        filePath: task.file,
+        name: 'file',
+        formData: {
+          openid: openid,
+          tenant_id: tenant_id,
+          application_id: uni.getStorageSync('appId'),
+          question_id: task.questionId,
+          video_duration: 0,
+          has_audio: 'true'
+        },
+        success: (uploadRes) => {
+          try {
+            const res = JSON.parse(uploadRes.data);
+            console.log('上传响应:', res);
+            if (res.code === 2000) {
+              // 获取上传后的视频URL
+              const videoUrl = res.data.permanent_link || res.data.url || '';
+              if (videoUrl) {
+                // 上传成功,更新状态
+                this.uploadStatus[task.id] = 'success';
+                this.updateUploadStatusText();
+                
+                // 提交到面试接口
+                this.submitVideoToInterview(videoUrl, task);
+              } else {
+                this.handleUploadFailure(task, '视频URL获取失败');
+              }
+            } else {
+              this.handleUploadFailure(task, res.msg || '上传失败');
+            }
+          } catch (e) {
+            this.handleUploadFailure(task, '解析响应失败');
+          }
+        },
+        fail: (err) => {
+          this.handleUploadFailure(task, err.errMsg || '上传失败');
+        }
+      });
+      
+      // 监听上传进度
+      uploadTask.onProgressUpdate((res) => {
+        this.uploadProgress[task.id] = res.progress;
+        this.updateUploadStatusText();
+      });
+    },
+    
+    // 添加新方法:处理上传失败
+    handleUploadFailure(task, errorMsg) {
+      console.error('上传失败:', errorMsg);
+      
+      // 更新任务状态
+      this.uploadStatus[task.id] = 'failed';
+      this.updateUploadStatusText();
+      
+      // 检查是否还有重试机会
+      if (task.attempts < task.maxAttempts) {
+        console.log(`将在5秒后重试上传,当前尝试次数: ${task.attempts}/${task.maxAttempts}`);
+        
+        // 5秒后重试
+        setTimeout(() => {
+          // 重置进度
+          this.uploadProgress[task.id] = 0;
+          
+          // 重新开始上传
+          if (typeof task.file !== 'string') {
+            this.uploadFileWithXHR(task);
+          } else {
+            this.uploadFileWithUni(task);
+          }
+        }, 5000);
+      } else {
+        // 超过最大重试次数,移除任务并继续处理队列
+        console.log('超过最大重试次数,放弃上传');
+        
+        // 显示错误提示
+        uni.showToast({
+          title: '视频上传失败,请稍后重试',
+          icon: 'none',
+          duration: 2000
+        });
+        
+        // 从队列中移除当前任务
+        this.uploadQueue.shift();
+        
+        // 继续处理队列中的下一个任务
+        this.processUploadQueue();
+      }
+    },
+    
+    // 修改 submitVideoToInterview 方法
+    submitVideoToInterview(videoUrl, task = null) {
+      console.log('提交视频URL到面试接口:', videoUrl);
+      
+      // 确定问题ID
+      let questionId;
+      if (task) {
+        questionId = task.questionId;
+      } else {
+        // 根据当前视频索引映射到正确的问题ID(兼容旧代码)
+        switch(this.currentVideoIndex) {
+          case 1:
+            questionId = 36;
+            break;
+          case 2:
+            questionId = 11;
+            break;
+          case 3:
+            questionId = 12;
+            break;
+          case 4:
+            questionId = 13;
+            break;
+          default:
+            questionId = 10;
+        }
+      }
+      
+      // 准备请求参数
+      const requestData = {
+        application_id: uni.getStorageSync('appId'),
+        question_id: questionId,
+        video_url: videoUrl,
+        tenant_id: uni.getStorageSync('tenant_id') || '1'
+      };
+      
+      // 发送请求到面试接口
+      uni.request({
+        url: `${apiBaseUrl}/api/job/upload_video`,
+        method: 'POST',
+        data: requestData,
+        header: {
+          'content-type': 'application/x-www-form-urlencoded'
+        },
+        success: (res) => {
+          console.log('面试接口提交成功:', res);
+          
+          if (res.data.code === 0 || res.data.code === 2000) {
+            // 提交成功
+            if (task) {
+              // 从队列中移除当前任务
+              this.uploadQueue.shift();
+              
+              // 显示简短的成功提示
+              // uni.showToast({
+              //   title: '视频提交成功',
+              //   icon: 'success',
+              //   duration: 1500
+              // });
+              
+              // 继续处理队列中的下一个任务
+              this.processUploadQueue();
+            } else {
+              // 兼容旧代码的处理逻辑
+              uni.showToast({
+                title: '回答已提交',
+                icon: 'success'
+              });
+              
+              // 保存最后上传的视频URL
+              this.lastUploadedVideoUrl = videoUrl;
+              
+              // 隐藏重试按钮(如果之前显示了)
+              this.showRetryButton = false;
+              this.lastVideoToRetry = null;
+            }
+          } else {
+            // 提交失败
+            if (task) {
+              this.handleSubmitFailure(task, res.data.msg || '提交失败');
+            } else {
+              // 兼容旧代码的处理逻辑
+              uni.showToast({
+                title: res.data.msg || '提交失败,请重试',
+                icon: 'none'
+              });
+              
+              // 保存失败的视频URL,用于重试
+              this.lastVideoToRetry = videoUrl;
+              
+              // 显示重试按钮
+              this.showRetryButton = true;
+            }
+          }
+        },
+        fail: (err) => {
+          console.error('面试接口提交失败:', err);
+          
+          if (task) {
+            this.handleSubmitFailure(task, err.errMsg || '网络错误');
+          } else {
+            // 兼容旧代码的处理逻辑
+            uni.hideLoading();
+            uni.showToast({
+              title: '网络错误,请重试',
+              icon: 'none'
+            });
+            
+            // 保存失败的视频URL,用于重试
+            this.lastVideoToRetry = videoUrl;
+            
+            // 显示重试按钮
+            this.showRetryButton = true;
+          }
+        }
+      });
+    },
+    
+    // 添加新方法:处理提交失败
+    handleSubmitFailure(task, errorMsg) {
+      console.error('提交失败:', errorMsg);
+      
+      // 更新任务状态
+      this.uploadStatus[task.id] = 'failed';
+      this.updateUploadStatusText();
+      
+      // 检查是否还有重试机会
+      if (task.attempts < task.maxAttempts) {
+        console.log(`将在5秒后重试提交,当前尝试次数: ${task.attempts}/${task.maxAttempts}`);
+        
+        // 5秒后重试
+        setTimeout(() => {
+          // 重新提交
+          this.submitVideoToInterview(task.videoUrl, task);
+        }, 5000);
+      } else {
+        // 超过最大重试次数,移除任务并继续处理队列
+        console.log('超过最大重试次数,放弃提交');
+        
+        // 显示错误提示
+        uni.showToast({
+          title: '视频提交失败,请稍后重试',
+          icon: 'none',
+          duration: 2000
+        });
+        
+        // 从队列中移除当前任务
+        this.uploadQueue.shift();
+        
+        // 继续处理队列中的下一个任务
+        this.processUploadQueue();
+      }
+    },
+    
+    // 添加新方法:更新上传状态文本
+    updateUploadStatusText() {
+      if (this.uploadQueue.length === 0) {
+        this.uploadStatusText = '';
+        return;
+      }
+      
+      const currentTask = this.uploadQueue[0];
+      const progress = this.uploadProgress[currentTask.id] || 0;
+      const status = this.uploadStatus[currentTask.id] || 'pending';
+      
+      let statusText = '';
+      switch (status) {
+        case 'pending':
+          statusText = '等待上传';
+          break;
+        case 'uploading':
+          statusText = `上传中 ${progress}%`;
+          break;
+        case 'success':
+          statusText = '上传成功,提交中...';
+          break;
+        case 'failed':
+          statusText = `上传失败,${currentTask.attempts < currentTask.maxAttempts ? '即将重试' : '已放弃'}`;
+          break;
+      }
+      
+      this.uploadStatusText = `问题${currentTask.questionId - 9}:${statusText}`;
+      
+      // 如果队列中有多个任务,显示总数
+      if (this.uploadQueue.length > 1) {
+        this.uploadStatusText += ` (${this.uploadQueue.length}个视频待处理)`;
+      }
+    },
+    
+    // 修改 proceedToNextQuestion 方法
+    proceedToNextQuestion() {
+      console.log('准备跳转到下一页面');
+      
+      // 不再等待上传队列完成,直接跳转
+      this.navigateToNextPage();
+      
+      // 如果有未完成的上传任务,在后台继续处理
+      if (this.uploadQueue.length > 0) {
+        console.log('上传队列将在后台继续处理...');
+        
+        // 显示简短的提示
+        // uni.showToast({
+        //   title: '视频将在后台上传',
+        //   icon: 'none',
+        //   duration: 2000
+        // });
+        
+        // 保存上传状态到本地存储,以便在其他页面可以查看
+        this.saveUploadStatus();
+      }
+    },
+    
+    // 修改 navigateToNextPage 方法
+    navigateToNextPage() {
+      console.log('导航到下一个页面');
+      
+      // 显示成功提示
+      // uni.showToast({
+      //   title: '面试完成',
+      //   icon: 'success',
+      //   duration: 2000
+      // });
+      
+      // 延迟跳转,让用户看到成功提示
+      setTimeout(() => {
+        // 跳转到结果页面,并传递上传状态参数
+        uni.navigateTo({
+          url: '/pages/interview-result/interview-result?uploading=' + (this.uploadQueue.length > 0 ? 'true' : 'false'),
+          success: () => {
+            console.log('成功跳转到结果页面');
+          },
+          fail: (err) => {
+            console.error('跳转失败:', err);
+            
+            // 如果跳转失败,尝试跳转到其他页面
+            uni.navigateTo({
+              url: '/pages/interview/interview',
+              fail: (err2) => {
+                console.error('备用跳转也失败:', err2);
+                
+                // 最后尝试返回上一页
+                uni.navigateBack({
+                  delta: 1
+                });
+              }
+            });
+          }
+        });
+      }, 2000);
+    },
+
+    // 修改 handleAnswerButtonClick 方法
+    handleAnswerButtonClick() {
+      // 隐藏答题按钮
+      this.showAnswerButton = false;
+      
+      // 直接进入下一个问题
+      this.proceedToNextQuestion();
+    },
+
+    // 处理相机错误
+    handleCameraError(e) {
+      console.error('相机错误:', e);
+      
+      // 获取平台信息
+      const systemInfo = uni.getSystemInfoSync();
+      const isIOS = systemInfo.platform === 'ios';
+      
+      if (isIOS) {
+        console.log('iOS相机错误,尝试重新初始化');
+        
+        // 显示提示
+        uni.showToast({
+          title: '相机初始化中...',
+          icon: 'loading',
+          duration: 2000
+        });
+        
+        // 重置相机
+        this.resetCamera();
+        
+        // 如果正在录制,停止录制
+        if (this.isRecording) {
+          this.isRecording = false;
+          this.showStopRecordingButton = false;
+          
+          // 提供替代选项
+          setTimeout(() => {
+            this.useAlternativeRecordingMethod();
+          }, 1000);
+        }
+      } else {
+        // 显示错误提示
+        uni.showToast({
+          title: '相机初始化失败,请检查权限设置',
+          icon: 'none'
+        });
+        
+        // 尝试备用选项
+        this.tryFallbackOptions();
+      }
+    },
+
+    // 添加新方法:尝试备用选项
+    tryFallbackOptions() {
+      // 检查环境
+      const systemInfo = uni.getSystemInfoSync();
+      
+      // 在小程序环境中使用小程序API
+      if (systemInfo.uniPlatform === 'mp-weixin' || systemInfo.uniPlatform === 'mp-alipay') {
+        this.useMiniProgramCamera();
+      } 
+      // 在H5环境中显示静态图像
+      else {
+        this.showStaticCameraPlaceholder();
+      }
+    },
+
+    // 添加新方法:使用小程序相机API
+    useMiniProgramCamera() {
+      console.log('尝试使用小程序相机组件');
+      // 这里需要在模板中添加小程序相机组件
+      // 并设置一个标志来控制显示
+      this.useMiniProgramCameraComponent = true;
+    },
+
+    // 添加新方法:显示静态图像
+    showStaticCameraPlaceholder() {
+      console.log('显示静态摄像头占位图');
+      // 创建一个图像元素
+      const img = document.createElement('img');
+      img.src = '/static/images/camera-placeholder.png'; // 确保有这个图片资源
+      img.className = 'static-camera-image';
+      img.style.width = '100%';
+      img.style.height = '100%';
+      img.style.objectFit = 'cover';
+      
+      // 获取容器并添加图像
+      const container = this.$refs.userCameraVideo.parentNode;
+      container.appendChild(img);
+    },
+
+    // 处理视频时间更新事件
+    handleTimeUpdate(e) {
+      // 获取当前视频播放时间
+      const currentTime = e.target.currentTime;
+      
+      // 如果正在录制,更新录制时间显示
+      if (this.isRecording && this.recordingTimerCount) {
+        this.recordingTimeDisplay = this.formatTime(this.recordingTimerCount);
+      }
+      
+      // 根据当前播放的视频索引选择对应的字幕数组
+      let currentSubtitles;
+      if (this.currentVideoIndex === 0) {
+        currentSubtitles = this.subtitles;
+      } else if (this.currentVideoIndex === 1) {
+        currentSubtitles = this.secondVideoSubtitles;
+      } else if (this.currentVideoIndex === 2) {
+        currentSubtitles = this.thirdVideoSubtitles;
+      } else if (this.currentVideoIndex === 3) {
+        currentSubtitles = this.fourthVideoSubtitles;
+      } else if (this.currentVideoIndex === 4) {
+        currentSubtitles = this.fifthVideoSubtitles;
+      } else if (this.currentVideoIndex === 5) {
+        currentSubtitles = this.sixthVideoSubtitles;
+      } else {
+        // 如果有更多视频,可以继续添加条件
+        currentSubtitles = [];
+      }
+      
+      // 查找当前时间应该显示的字幕
+      const subtitle = currentSubtitles.find(
+        sub => currentTime >= sub.startTime && currentTime < sub.endTime
+      );
+      
+      // 更新当前字幕
+      this.currentSubtitle = subtitle ? subtitle.text : '';
+    },
+
+    // Add a new method to handle the "Start Recording" button click
+    handleStartRecordingClick() {
+      // 隐藏"开始回答"按钮
+      this.showStartRecordingButton = false;
+      
+      // 开始录制
+      this.startRecordingAnswer();
+    },
+
+    // 修改 checkAudioPermission 方法,确保在录制前获取音频权限
+    checkAudioPermission() {
+      // 在小程序环境中
+      const systemInfo = uni.getSystemInfoSync();
+      const isMiniProgram = systemInfo.uniPlatform && systemInfo.uniPlatform.startsWith('mp-');
+      
+      if (isMiniProgram) {
+        // 小程序环境下的权限请求
+        uni.getSetting({
+          success: (res) => {
+            if (!res.authSetting['scope.record']) {
+              uni.authorize({
+                scope: 'scope.record',
+                success: () => {
+                  console.log('录音权限已获取');
+                },
+                fail: (err) => {
+                  console.error('录音权限获取失败:', err);
+                  this.showPermissionDialog('录音');
+                }
+              });
+            }
+            
+            if (!res.authSetting['scope.camera']) {
+              uni.authorize({
+                scope: 'scope.camera',
+                success: () => {
+                  console.log('相机权限已获取');
+                },
+                fail: (err) => {
+                  console.error('相机权限获取失败:', err);
+                  this.showPermissionDialog('相机');
+                }
+              });
+            }
+          }
+        });
+      } else {
+        // 浏览器环境下的权限请求
+        // ... 保持原有代码不变
+      }
+    },
+
+    // 添加新方法:测试音频输入
+    testAudioInput() {
+      if (!this.cameraStream) {
+        console.warn('没有可用的媒体流,无法测试音频');
+        return;
+      }
+      
+      const audioTracks = this.cameraStream.getAudioTracks();
+      if (audioTracks.length === 0) {
+        console.warn('没有检测到音频轨道,尝试重新获取');
+        this.tryGetAudioOnly();
+        return;
+      }
+      
+      console.log('音频轨道信息:', audioTracks[0].getSettings());
+      
+      // 创建音频上下文和分析器
+      try {
+        const AudioContext = window.AudioContext || window.webkitAudioContext;
+        if (!AudioContext) {
+          console.warn('浏览器不支持AudioContext');
+          return;
+        }
+        
+        const audioContext = new AudioContext();
+        const analyser = audioContext.createAnalyser();
+        const microphone = audioContext.createMediaStreamSource(this.cameraStream);
+        microphone.connect(analyser);
+        
+        // 设置分析器
+        analyser.fftSize = 256;
+        const bufferLength = analyser.frequencyBinCount;
+        const dataArray = new Uint8Array(bufferLength);
+        
+        // 检测音频输入
+        let silenceCounter = 0;
+        const checkAudio = () => {
+          if (this.isRecording) return; // 如果正在录制,停止检测
+          
+          analyser.getByteFrequencyData(dataArray);
+          
+          // 计算平均音量
+          let sum = 0;
+          for (let i = 0; i < bufferLength; i++) {
+            sum += dataArray[i];
+          }
+          const average = sum / bufferLength;
+          
+          if (average > 10) {
+            console.log('检测到音频输入,音量:', average);
+            silenceCounter = 0;
+          } else {
+            silenceCounter++;
+            if (silenceCounter > 10) {
+              console.warn('持续检测不到音频输入,可能麦克风未正常工作');
+              silenceCounter = 0;
+            }
+          }
+          
+          // 继续检测
+          requestAnimationFrame(checkAudio);
+        };
+        
+        // 开始检测
+        checkAudio();
+      } catch (e) {
+        console.error('音频测试失败:', e);
+      }
+    },
+
+    // 添加新方法:尝试单独获取音频
+    tryGetAudioOnly() {
+      navigator.mediaDevices.getUserMedia({ audio: true })
+        .then(audioStream => {
+          // 如果已有视频流,合并音频和视频轨道
+          if (this.cameraStream) {
+            const videoTrack = this.cameraStream.getVideoTracks()[0];
+            const audioTrack = audioStream.getAudioTracks()[0];
+            
+            // 创建新的媒体流,包含视频和音频轨道
+            const combinedStream = new MediaStream();
+            if (videoTrack) combinedStream.addTrack(videoTrack);
+            if (audioTrack) combinedStream.addTrack(audioTrack);
+            
+            // 更新摄像头流
+            this.cameraStream = combinedStream;
+            
+            // 更新视频元素的源
+            const videoElement = this.$refs.userCameraVideo;
+            if (videoElement) {
+              videoElement.srcObject = combinedStream;
+              videoElement.muted = true; // 避免回声,但仍然录制声音
+            }
+            
+            console.log('成功合并音频和视频轨道');
+          } else {
+            console.warn('没有视频流可合并');
+          }
+        })
+        .catch(err => {
+          console.error('单独获取音频失败:', err);
+        });
+    },
+
+    // 添加新方法:显示权限对话框
+    showPermissionDialog(permissionType) {
+      uni.showModal({
+        title: '需要权限',
+        content: `请允许使用${permissionType}权限,否则可能影响面试功能`,
+        confirmText: '去设置',
+        success: (res) => {
+          if (res.confirm) {
+            uni.openSetting({
+              success: (settingRes) => {
+                console.log('设置页面打开成功', settingRes);
+              }
+            });
+          }
+        }
+      });
+    },
+
+    // 添加重试上传方法
+    retryVideoUpload() {
+      if (this.lastVideoToRetry) {
+        // 隐藏重试按钮
+        this.showRetryButton = false;
+        
+        // 显示加载提示
+        uni.showLoading({
+          title: '正在重新提交...',
+          mask: true
+        });
+        
+        // 重新提交视频
+        this.submitVideoToInterview(this.lastVideoToRetry);
+      } else {
+        uni.showToast({
+          title: '没有可重试的视频',
+          icon: 'none'
+        });
+      }
+    },
+
+    // 添加一个新方法用于压缩视频
+    async compressVideo(videoBlob) {
+      // 如果视频大小小于某个阈值,直接返回原视频
+      if (videoBlob.size < 5 * 1024 * 1024) { // 小于5MB
+        return videoBlob;
+      }
+      
+      console.log('开始压缩视频,原始大小:', videoBlob.size);
+      
+      // 创建一个视频元素来加载视频
+      const videoElement = document.createElement('video');
+      videoElement.muted = true;
+      videoElement.autoplay = false;
+      
+      // 创建一个canvas元素用于绘制视频帧
+      const canvas = document.createElement('canvas');
+      const ctx = canvas.getContext('2d');
+      
+      // 设置视频源
+      videoElement.src = URL.createObjectURL(videoBlob);
+      
+      return new Promise((resolve) => {
+        videoElement.onloadedmetadata = () => {
+          // 设置canvas尺寸为视频的一半
+          const width = Math.floor(videoElement.videoWidth / 2);
+          const height = Math.floor(videoElement.videoHeight / 2);
+          canvas.width = width;
+          canvas.height = height;
+          
+          // 创建MediaRecorder来录制canvas
+          const stream = canvas.captureStream(15); // 15fps
+          
+          // 如果原视频有音轨,添加到新流中
+          if (videoElement.captureStream) {
+            const originalStream = videoElement.captureStream();
+            const audioTracks = originalStream.getAudioTracks();
+            if (audioTracks.length > 0) {
+              stream.addTrack(audioTracks[0]);
+            }
+          }
+          
+          // 设置较低的比特率
+          const options = {
+            mimeType: 'video/webm;codecs=vp8,opus',
+            audioBitsPerSecond: 64000,
+            videoBitsPerSecond: 800000 // 800kbps
+          };
+          
+          const mediaRecorder = new MediaRecorder(stream, options);
+          const chunks = [];
+          
+          mediaRecorder.ondataavailable = (e) => {
+            if (e.data.size > 0) {
+              chunks.push(e.data);
+            }
+          };
+          
+          mediaRecorder.onstop = () => {
+            const compressedBlob = new Blob(chunks, { type: 'video/webm' });
+            console.log('视频压缩完成,压缩后大小:', compressedBlob.size);
+            resolve(compressedBlob);
+          };
+          
+          // 开始播放视频并录制
+          videoElement.onplay = () => {
+            mediaRecorder.start(100);
+            
+            // 绘制视频帧到canvas
+            const drawFrame = () => {
+              if (videoElement.paused || videoElement.ended) {
+                mediaRecorder.stop();
+                return;
+              }
+              
+              ctx.drawImage(videoElement, 0, 0, width, height);
+              requestAnimationFrame(drawFrame);
+            };
+            
+            drawFrame();
+          };
+          
+          videoElement.play();
+        };
+      });
+    },
+
+    // 修改 checkIOSCameraRecordPermission 方法
+    checkIOSCameraRecordPermission() {
+      const systemInfo = uni.getSystemInfoSync();
+      if (systemInfo.platform !== 'ios') return;
+      
+      // 在iOS上,需要同时请求相机和录音权限
+      uni.getSetting({
+        success: (res) => {
+          // 检查相机权限
+          if (!res.authSetting['scope.camera']) {
+            uni.authorize({
+              scope: 'scope.camera',
+              success: () => {
+                console.log('iOS相机权限已获取');
+              },
+              fail: (err) => {
+                console.error('iOS相机权限获取失败:', err);
+                this.showPermissionDialog('相机');
+              }
+            });
+          }
+          
+          // 检查录音权限
+          if (!res.authSetting['scope.record']) {
+            uni.authorize({
+              scope: 'scope.record',
+              success: () => {
+                console.log('iOS录音权限已获取');
+              },
+              fail: (err) => {
+                console.error('iOS录音权限获取失败:', err);
+                this.showPermissionDialog('录音');
+              }
+            });
+          }
+        }
+      });
+    },
+
+    // 添加新方法:检查并修复渲染问题
+    checkAndFixRenderingIssues() {
+      // 检查全局对象,防止渲染错误
+      try {
+        // 检查是否有全局变量u
+        if (typeof u !== 'undefined' && u) {
+          // 检查currentQuestion属性
+          if (!u.currentQuestion) {
+            console.log('修复: 创建缺失的currentQuestion对象');
+            u.currentQuestion = {};
+          }
+          // 确保isImportant属性存在
+          if (u.currentQuestion && typeof u.currentQuestion.isImportant === 'undefined') {
+            console.log('修复: 设置缺失的isImportant属性');
+            u.currentQuestion.isImportant = false;
+          }
+        }
+      } catch (e) {
+        console.log('防御性检查异常:', e);
+      }
+    },
+
+    // 添加格式化时间的辅助方法
+    formatTime(seconds) {
+      if (!seconds && seconds !== 0) return '03:30'; // 默认显示03:30
+      const minutes = Math.floor(seconds / 60);
+      const remainingSeconds = seconds % 60;
+      return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
+    },
+
+    // 添加新方法:保存上传状态到本地存储
+    saveUploadStatus() {
+      const uploadStatus = {
+        isUploading: this.isUploading,
+        uploadQueue: this.uploadQueue.length,
+        timestamp: Date.now()
+      };
+      
+      try {
+        uni.setStorageSync('videoUploadStatus', JSON.stringify(uploadStatus));
+      } catch (e) {
+        console.error('保存上传状态失败:', e);
+      }
+    },
+  }
+}
+</script>
+
+<style scoped>
+.identity-verify-container {
+  padding: 0;
+  max-width: 100%;
+  margin: 0 auto;
+  height: 100vh;
+  display: flex;
+  flex-direction: column;
+  background-color: #f5f5f5;
+}
+
+.digital-human-container {
+  position: relative;
+  width: 100%;
+  height: 100vh;
+  overflow: hidden;
+  background-color: #f0f0f0;
+}
+
+.digital-human-video {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+/* 用户摄像头容器样式 */
+.user-camera-container {
+  position: absolute;
+  top: 50px;
+  right: 5px;
+  width: 110px; /* 稍微增加宽度 */
+  height: 160px; /* 稍微增加高度 */
+  border-radius: 4px; /* 减小圆角 */
+  overflow: hidden;
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
+  z-index: 20;
+ 
+}
+
+/* 用户摄像头视频样式 */
+.user-camera-video {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+  background-color: #333;
+}
+
+.video-player {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+  outline: none; /* 移除视频获得焦点时的轮廓 */
+  -webkit-tap-highlight-color: transparent; /* 移除移动设备上的点击高亮 */
+}
+
+/* 隐藏视频控制条 */
+video::-webkit-media-controls {
+  display: none !important;
+}
+
+video::-webkit-media-controls-enclosure {
+  display: none !important;
+}
+
+video::-webkit-media-controls-panel {
+  display: none !important;
+}
+
+video::-webkit-media-controls-play-button {
+  display: none !important;
+}
+
+video::-webkit-media-controls-timeline {
+  display: none !important;
+}
+
+video::-webkit-media-controls-current-time-display {
+  display: none !important;
+}
+
+video::-webkit-media-controls-time-remaining-display {
+  display: none !important;
+}
+
+video::-webkit-media-controls-mute-button {
+  display: none !important;
+}
+
+video::-webkit-media-controls-volume-slider {
+  display: none !important;
+}
+
+video::-webkit-media-controls-fullscreen-button {
+  display: none !important;
+}
+
+/* 修改字幕覆盖层样式,使其与图片中的样式一致 */
+.subtitle-overlay {
+  position: absolute;
+  bottom: 180px; /* 调整位置,使其更靠近底部 */
+  left: 5%; /* 从左侧留出5%的空间 */
+  width: 80%; /* 宽度设为90%,两侧各留5%实现居中 */
+  padding: 15px 20px; /* 增加左右内边距 */
+  border-radius: 15px;
+  background-color: rgba(255, 255, 255, 0.9); /* 白色半透明背景 */
+  color: #333; /* 文字颜色为深色 */
+  text-align: left; /* 文字左对齐 */
+  font-size: 16px;
+  line-height: 1.5;
+  z-index: 10;
+  margin: 0 auto; /* 添加自动边距实现居中 */
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); /* 添加轻微阴影 */
+}
+
+/* 添加计时器样式,右侧显示橙色圆点和时间 */
+.subtitle-overlay::after {
+  content: attr(data-time);
+  position: absolute;
+  right: 20px;
+  bottom: 15px;
+  color: #333;
+  font-size: 14px;
+}
+
+.control-panel {
+  padding: 15px;
+  display: flex;
+  justify-content: center;
+}
+
+.control-button {
+  padding: 10px 20px;
+  background-color: #4CAF50;
+  color: white;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+}
+
+.loading {
+  text-align: center;
+  margin: 20px 0;
+  font-size: 16px;
+}
+
+.response-container {
+  margin-top: 20px;
+  padding: 0 20px;
+  display: none; /* 默认隐藏调试信息 */
+}
+
+/* 当showDebugInfo为true时显示 */
+.showDebugInfo .response-container {
+  display: block;
+}
+
+.response-item {
+  padding: 10px;
+  border: 1px solid #eee;
+  border-radius: 4px;
+  margin-bottom: 10px;
+  background-color: #f9f9f9;
+}
+
+.response-content {
+  display: flex;
+  flex-direction: column;
+}
+
+.answer-button-container {
+  position: absolute;
+  bottom: 50px; /* 将按钮放在底部而不是75%的位置 */
+  left: 50%;
+  transform: translateX(-50%); /* 只在X轴上平移,保持水平居中 */
+  z-index: 20;
+}
+
+/* 修改回答按钮样式 */
+.answer-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;
+  animation: none; /* 移除脉动动画 */
+}
+
+.stop-recording-button-container {
+  position: absolute;
+  bottom: 50px; /* 统一与其他按钮的位置 */
+  left: 50%;
+  transform: translateX(-50%);
+  z-index: 20;
+}
+
+.stop-recording-button {
+  width: 120px;
+  height: 40px; /* 改为矩形按钮 */
+  border-radius: 20px; /* 圆角矩形 */
+  background-color: #e74c3c; /* 白色背景 */
+  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;
+  animation: none; /* 移除脉动动画 */
+}
+
+.recording-indicator {
+  position: absolute;
+  top: 20px;
+  left: 20px;
+  display: flex;
+  align-items: center;
+  background-color: rgba(255, 255, 255, 0.9); /* 改为白色半透明背景 */
+  padding: 5px 10px;
+  border-radius: 15px;
+  z-index: 20;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.recording-dot {
+  width: 12px;
+  height: 12px;
+  background-color: #ff6b00; /* 改为橙色,与图片中的颜色一致 */
+  border-radius: 50%;
+  margin-right: 8px;
+  animation: blink 1s infinite;
+}
+
+.recording-text {
+  color: #333; /* 改为深色文字 */
+  font-size: 14px;
+}
+
+.timer-text {
+  color: #333; /* 改为深色文字 */
+  font-size: 14px;
+  margin-left: 8px;
+}
+
+.start-recording-button-container {
+  position: absolute;
+  bottom: 50px; /* 统一与其他按钮的位置 */
+  left: 50%;
+  transform: translateX(-50%);
+  z-index: 20;
+}
+
+.start-recording-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;
+  animation: none; /* 移除脉动动画 */
+}
+
+.retry-button-container {
+  position: absolute;
+  bottom: 30px; /* 统一与其他按钮的位置 */
+  left: 50%;
+  transform: translateX(-50%);
+  z-index: 20;
+}
+
+.retry-button {
+  padding: 10px 20px;
+  background-color: #ffffff; /* 白色背景 */
+  color: #333; /* 深色文字 */
+  font-size: 16px;
+  border: none;
+  border-radius: 20px; /* 圆角矩形 */
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
+  cursor: pointer;
+}
+
+.retry-button:hover {
+  background-color: #2980b9;
+}
+
+/* 录制时长显示 */
+.recording-timer {
+  position: absolute;
+  top: 20px;
+  left: 130px; /* 放在录制指示器旁边 */
+  background-color: rgba(0, 0, 0, 0.6);
+  padding: 5px 10px;
+  border-radius: 15px;
+  z-index: 20;
+  display: flex;
+  flex-direction: column;
+}
+
+.timer-text {
+  color: #000;
+  font-size: 14px;
+  font-family: monospace; /* 使用等宽字体,使时间显示更稳定 */
+}
+
+.remaining-time {
+  color: white;
+  font-size: 12px;
+  margin-top: 2px;
+}
+
+.remaining-time.warning {
+  color: #ff6b6b; /* 剩余时间少时显示红色 */
+  animation: blink 1s infinite; /* 使用闪烁动画提醒用户 */
+}
+
+/* 添加上传状态指示器 */
+.upload-status-indicator {
+  position: absolute;
+  top: 20px;
+  left: 130px; /* 放在录制指示器旁边 */
+  background-color: rgba(0, 0, 0, 0.6);
+  padding: 5px 10px;
+  border-radius: 15px;
+  z-index: 20;
+}
+
+.upload-status-content {
+  display: flex;
+  align-items: center;
+}
+
+.upload-status-icon {
+  width: 12px;
+  height: 12px;
+  border-radius: 50%;
+  margin-right: 8px;
+}
+
+.upload-status-text {
+  color: white;
+  font-size: 14px;
+}
+
+.uploading {
+  background-color: #e74c3c;
+}
+</style>

+ 128 - 46
pages/interview/interview.vue

@@ -16,7 +16,7 @@
 				</view>
 			</view>
 			
-			<view class="thumbnails">
+			<view class="thumbnails"> 
 				<view v-for="(photo, index) in photos" :key="index" 
 					class="thumbnail-item" 
 					:class="{ active: currentStep === index, completed: photo }">
@@ -40,7 +40,7 @@
 		<!-- 为小程序环境添加camera组件 -->
 		<view v-if="!isH5 && showCamera" class="camera-container">
 			<camera 
-				device-position="back" 
+				:device-position="cameraPosition" 
 				flash="auto" 
 				@error="handleCameraError" 
 				class="camera-component"
@@ -61,7 +61,10 @@
 			
 			<cover-view class="camera-controls">
 				<cover-view class="capture-btn" @tap="capturePhotoMP"></cover-view>
-				<cover-view class="cancel-btn" @tap="cancelCamera">取消</cover-view>
+				<cover-view class="camera-buttons">
+					<!-- <cover-view class="switch-camera-btn" @tap="switchCamera">切换摄像头</cover-view> -->
+					<cover-view class="cancel-btn" @tap="cancelCamera">取消</cover-view>
+				</cover-view>
 			</cover-view>
 		</view>
 		
@@ -81,8 +84,11 @@
 				</view>
 			</view>
 			
-			<button class="capture-btn" @click="capturePhoto">拍照</button>
-			<button class="cancel-btn" @click="cancelCamera">取消</button>
+			<view class="camera-buttons">
+				<button class="capture-btn" @click="capturePhoto">拍照</button>
+				<button class="switch-camera-btn" @click="switchCamera">切换摄像头</button>
+				<button class="cancel-btn" @click="cancelCamera">取消</button>
+			</view>
 		</view>
 	</view>
 </template>
@@ -131,6 +137,7 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 					'请将右手拳头对准轮廓线'
 				],
 				photoLinks: [null, null, null, null, null, null], // 存储上传后的图片链接
+				cameraPosition: 'front', // 修改默认为前置摄像头
 			}
 		},
 		computed: {
@@ -366,8 +373,8 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 								// 延迟跳转,让用户看到成功提示
 								setTimeout(() => {
 									// 返回首页
-									uni.switchTab({
-										url: '/pages/index/index'
+									uni.navigateTo({
+										url: '/pages/success/success'
 									});
 								}, 2000);
 							}
@@ -386,25 +393,30 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 				
 				// 确保浏览器支持getUserMedia
 				if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
-					navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } })
-						.then(stream => {
-							this.mediaStream = stream;
-							// 等待DOM更新后再设置视频源
-							this.$nextTick(() => {
-								const videoElement = this.$refs.videoElement;
-								if (videoElement) {
-									videoElement.srcObject = stream;
-								}
-							});
-						})
-						.catch(error => {
-							console.error('获取摄像头失败:', error);
-							uni.showToast({
-								title: '无法访问摄像头,请检查权限设置',
-								icon: 'none'
-							});
-							this.showCamera = false;
+					// 根据当前摄像头位置设置facingMode
+					const facingMode = this.cameraPosition === 'front' ? 'user' : 'environment';
+					
+					navigator.mediaDevices.getUserMedia({ 
+						video: { facingMode: facingMode } 
+					})
+					.then(stream => {
+						this.mediaStream = stream;
+						// 等待DOM更新后再设置视频源
+						this.$nextTick(() => {
+							const videoElement = this.$refs.videoElement;
+							if (videoElement) {
+								videoElement.srcObject = stream;
+							}
 						});
+					})
+					.catch(error => {
+						console.error('获取摄像头失败:', error);
+						uni.showToast({
+							title: '无法访问摄像头,请检查权限设置',
+							icon: 'none'
+						});
+						this.showCamera = false;
+					});
 				} else {
 					uni.showToast({
 						title: '您的浏览器不支持摄像头功能',
@@ -416,36 +428,46 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 			
 			// H5环境下拍照
 			capturePhoto() {
+				console.log('执行H5拍照方法');
 				const videoElement = this.$refs.videoElement;
 				const canvasElement = this.$refs.canvasElement;
 				
 				if (videoElement && canvasElement) {
-					const context = canvasElement.getContext('2d');
-					
-					// 设置canvas尺寸与视频一致
-					canvasElement.width = videoElement.videoWidth;
-					canvasElement.height = videoElement.videoHeight;
-					
-					// 在canvas上绘制当前视频帧
-					context.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);
-					
-					// 将canvas内容转为base64图片
-					const photoData = canvasElement.toDataURL('image/jpeg');
-					
-					// 更新照片数据
-					this.$set(this.photos, this.currentStep, photoData);
-					
-					// 上传照片
-					this.uploadPhoto(photoData, this.photoTypes[this.currentStep]);
-					
-					// 关闭摄像头
-					this.stopMediaStream();
-					this.showCamera = false;
+					try {
+						const context = canvasElement.getContext('2d');
+						
+						// 设置canvas尺寸与视频一致
+						canvasElement.width = videoElement.videoWidth;
+						canvasElement.height = videoElement.videoHeight;
+						
+						// 在canvas上绘制当前视频帧
+						context.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);
+						
+						// 将canvas内容转为base64图片
+						const photoData = canvasElement.toDataURL('image/jpeg');
+						
+						// 更新照片数据
+						this.$set(this.photos, this.currentStep, photoData);
+						
+						// 上传照片
+						this.uploadPhoto(photoData, this.photoTypes[this.currentStep]);
+						
+						// 关闭摄像头
+						this.stopMediaStream();
+						this.showCamera = false;
+					} catch (err) {
+						console.error('H5拍照失败:', err);
+						uni.showToast({
+							title: '拍照失败,请重试',
+							icon: 'none'
+						});
+					}
 				}
 			},
 			
 			// 小程序环境下拍照
 			capturePhotoMP() {
+				console.log('执行小程序拍照方法');
 				if (!this.cameraContext) {
 					console.error('相机上下文不存在');
 					this.handleCameraInitError();
@@ -453,10 +475,16 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 				}
 				
 				try {
+					uni.showLoading({
+						title: '拍照中...',
+						mask: true
+					});
+					
 					this.cameraContext.takePhoto({
 						quality: 'high',
 						success: (res) => {
 							console.log('拍照成功:', res);
+							uni.hideLoading();
 							// 更新当前步骤的照片
 							this.$set(this.photos, this.currentStep, res.tempImagePath);
 							
@@ -467,6 +495,7 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 							this.showCamera = false;
 						},
 						fail: (err) => {
+							uni.hideLoading();
 							console.error('拍照失败:', err);
 							uni.showToast({
 								title: '拍照失败,请重试',
@@ -477,6 +506,7 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 						}
 					});
 				} catch (err) {
+					uni.hideLoading();
 					console.error('takePhoto方法执行失败:', err);
 					this.fallbackToChooseImage();
 				}
@@ -597,6 +627,18 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 			toggleGestureOverlay() {
 				// 实现蒙层显示/隐藏的逻辑
 				// 如果需要的话
+			},
+			
+			// 切换摄像头
+			switchCamera() {
+				// 切换摄像头位置
+				this.cameraPosition = this.cameraPosition === 'front' ? 'back' : 'front';
+				
+				// 如果是H5环境,需要重新启动摄像头
+				if (this.isH5 && this.showCamera) {
+					this.stopMediaStream();
+					this.startCamera();
+				}
 			}
 		}
 	}
@@ -834,5 +876,45 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 	font-size: 28rpx;
 	text-align: center;
 }
+
+/* 添加切换摄像头按钮样式 */
+.switch-camera-btn {
+	width: 200rpx;
+	height: 80rpx;
+	line-height: 80rpx;
+	background-color: rgba(255, 255, 255, 0.3);
+	color: #ffffff;
+	text-align: center;
+	border-radius: 40rpx;
+	margin: 20rpx 0;
+}
+
+.camera-buttons {
+	display: flex;
+	flex-direction: row;
+	justify-content: center;
+	width: 100%;
+	margin-top: 20rpx;
+}
+
+.capture-btn {
+	width: 200rpx;
+	height: 200rpx;
+	border-radius: 50%;
+	background-color: #ffffff;
+	border: 10rpx solid #0039b3;
+	margin: 30rpx 0;
+}
+
+.switch-camera-btn, .cancel-btn {
+	width: 200rpx;
+	height: 80rpx;
+	line-height: 80rpx;
+	background-color: rgba(255, 255, 255, 0.3);
+	color: #ffffff;
+	text-align: center;
+	border-radius: 40rpx;
+	margin: 0 20rpx;
+}
 </style>
 

+ 1 - 1
pages/login/login.vue

@@ -1,7 +1,7 @@
 <template>
   <view class="login-container">
     <image class="login-image" src="/static/login-image.png" mode="aspectFit"></image>
-    <view class="login-title">登录才,开始面试</view>
+    <view class="login-title">登录才,开始面试</view>
     <button class="login-btn" @click="handleLogin">身份验证并登录</button>
     <view class="agreement">
       <checkbox :checked="isAgreed" @tap="toggleAgreement" color="#0039b3" />

+ 1 - 0
unpackage/dist/dev/mp-weixin/app.js

@@ -13,6 +13,7 @@ if (!Math) {
   "./pages/my/my.js";
   "./pages/interview/interview.js";
   "./pages/ResumeEvaluation/ResumeEvaluation.js";
+  "./pages/interview-question/interview-question.js";
 }
 const _sfc_main = {
   onLaunch: function() {

+ 2 - 1
unpackage/dist/dev/mp-weixin/app.json

@@ -9,7 +9,8 @@
     "pages/camera/camera",
     "pages/my/my",
     "pages/interview/interview",
-    "pages/ResumeEvaluation/ResumeEvaluation"
+    "pages/ResumeEvaluation/ResumeEvaluation",
+    "pages/interview-question/interview-question"
   ],
   "window": {
     "navigationBarTextStyle": "black",

+ 21 - 40
unpackage/dist/dev/mp-weixin/pages/camera/camera.js

@@ -173,7 +173,7 @@ const _sfc_main = {
                 this.goToNextQuestion();
               } else {
                 common_vendor.index.navigateTo({
-                  url: "/pages/interview/interview"
+                  url: "/pages/interview-question/interview-question"
                 });
               }
             }, 1500);
@@ -236,7 +236,7 @@ const _sfc_main = {
           setTimeout(() => {
             if (this.currentQuestionIndex >= this.questions.length - 1) {
               common_vendor.index.navigateTo({
-                url: "/pages/interview/interview"
+                url: "/pages/interview-question/interview-question"
               });
             } else {
               this.goToNextQuestion();
@@ -247,7 +247,7 @@ const _sfc_main = {
           setTimeout(() => {
             if (this.currentQuestionIndex >= this.questions.length - 1) {
               common_vendor.index.navigateTo({
-                url: "/pages/interview/interview"
+                url: "/pages/interview-question/interview-question"
               });
             } else {
               this.goToNextQuestion();
@@ -258,7 +258,7 @@ const _sfc_main = {
       }
       if (this.currentQuestionIndex >= this.questions.length - 1) {
         common_vendor.index.navigateTo({
-          url: "/pages/interview/interview"
+          url: "/pages/interview-question/interview-question"
         });
         return;
       }
@@ -414,23 +414,6 @@ const _sfc_main = {
         });
         return;
       }
-      try {
-        const pages = getCurrentPages();
-        if (pages.length > 1) {
-          common_vendor.index.reLaunch({
-            url: "/pages/index/index"
-          });
-        } else {
-          common_vendor.index.reLaunch({
-            url: "/pages/index/index"
-          });
-        }
-      } catch (e) {
-        console.error("导航错误:", e);
-        common_vendor.index.reLaunch({
-          url: "/pages/index/index"
-        });
-      }
     },
     error(e) {
       console.error(e.detail);
@@ -535,13 +518,11 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
     d: $data.digitalHumanUrl,
     g: common_vendor.t($data.currentQuestionIndex + 1),
     h: common_vendor.t($data.questions.length),
-    i: $options.currentQuestion.isImportant
-  }, $options.currentQuestion.isImportant ? {} : {}, {
-    j: common_vendor.t($options.currentQuestion.questionTypeName),
-    k: common_vendor.t($options.currentQuestion.text),
-    l: $options.currentQuestion.questionType !== 0
+    i: common_vendor.t($options.currentQuestion.questionTypeName),
+    j: common_vendor.t($options.currentQuestion.text),
+    k: $options.currentQuestion.questionType !== 0
   }, $options.currentQuestion.questionType !== 0 ? {
-    m: common_vendor.f($options.currentQuestion.options, (option, index, i0) => {
+    l: common_vendor.f($options.currentQuestion.options, (option, index, i0) => {
       return {
         a: common_vendor.t(String.fromCharCode(65 + index)),
         b: common_vendor.t(option.option_text || (typeof option === "string" ? option : JSON.stringify(option))),
@@ -552,31 +533,31 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
       };
     })
   } : {}, {
-    n: common_vendor.t($data.remainingTime),
-    o: common_vendor.t("进入下一题"),
-    p: common_vendor.o(($event) => $options.nextQuestion(_ctx.option)),
-    q: $options.currentQuestion.questionType === 0 && $data.openQuestionAnswer.trim() === "" || $options.currentQuestion.questionType === 1 && $data.selectedOption === null || $options.currentQuestion.questionType === 2 && $data.selectedOptions.length === 0,
-    r: $data.showEndModal
+    m: common_vendor.t($data.remainingTime),
+    n: common_vendor.t("进入下一题"),
+    o: common_vendor.o(($event) => $options.nextQuestion(_ctx.option)),
+    p: $options.currentQuestion.questionType === 0 && $data.openQuestionAnswer.trim() === "" || $options.currentQuestion.questionType === 1 && $data.selectedOption === null || $options.currentQuestion.questionType === 2 && $data.selectedOptions.length === 0,
+    q: $data.showEndModal
   }, $data.showEndModal ? {
-    s: common_vendor.o((...args) => $options.navigateToInterview && $options.navigateToInterview(...args))
+    r: common_vendor.o((...args) => $options.navigateToInterview && $options.navigateToInterview(...args))
   } : {}, {
-    t: $data.interviewCompleted
+    s: $data.interviewCompleted
   }, $data.interviewCompleted ? {
-    v: common_assets._imports_0$1,
-    w: common_vendor.o((...args) => $options.back && $options.back(...args))
+    t: common_assets._imports_0$1,
+    v: common_vendor.o((...args) => $options.back && $options.back(...args))
   } : {}, {
-    x: $data.loading
+    w: $data.loading
   }, $data.loading ? {
-    y: common_vendor.p({
+    x: common_vendor.p({
       status: "loading",
       contentText: {
         contentdown: "加载中..."
       }
     })
   } : {}, {
-    z: !$data.loading && $data.loadError
+    y: !$data.loading && $data.loadError
   }, !$data.loading && $data.loadError ? {
-    A: common_vendor.o((...args) => $options.fetchInterviewData && $options.fetchInterviewData(...args))
+    z: common_vendor.o((...args) => $options.fetchInterviewData && $options.fetchInterviewData(...args))
   } : {});
 }
 const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
unpackage/dist/dev/mp-weixin/pages/camera/camera.wxml


+ 36 - 14
unpackage/dist/dev/mp-weixin/pages/camera/camera.wxss

@@ -305,28 +305,50 @@
 		animation: speaking 1.5s infinite;
 }
 .digital-avatar {
-		width: 120rpx;
-		height: 120rpx;
-		z-index: 10;
+		display: flex;
+		flex-direction: row;
+		align-items: flex-start;
+		justify-content: space-between;
+		width: 100%;
+		margin-bottom: 20rpx;
+}
+.message-box {
+		width: 65%;
+		min-height: 120rpx;
+		background-color: #f5f5f5;
+		border-radius: 10rpx;
+		padding: 15rpx;
+		margin-right: 20rpx;
+		display: flex;
+		align-items: center;
+}
+.message-text {
+		font-size: 24rpx;
+		color: #333;
+		line-height: 1.5;
+}
+.user-avatar {
+		width: 110px;
+		height: 160px;
+		border-radius: 10rpx;
 		overflow: hidden;
-		border-radius: 20rpx;
 }
 .camera {
-		width: 120rpx;
-		height: 120rpx;
-		border-radius: 20rpx;
+		width: 100%;
+		height: 100%;
+		border-radius: 10rpx;
 		border: 2rpx solid #e0e0e0;
 }
 .avatar-image {
-		width: 120rpx;
-		height: 120rpx;
-		border-radius: 20rpx;
+		width: 100%;
+		height: 100%;
+		border-radius: 10rpx;
 		border: 2rpx solid #e0e0e0;
 }
 .digital-human-webview {
-		width: 120rpx;
-		height: 120rpx;
-		border-radius: 20rpx;
+		width: 100%;
+		height: 100%;
+		border-radius: 10rpx;
 		border: 2rpx solid #e0e0e0;
 }
 .interview-complete-screen {
@@ -411,7 +433,7 @@
 }
 .question-type {
 		display: inline-block;
-		background-color: #0039b3;
+		/* background-color: #0039b3; */
 		color: white;
 		font-size: 24rpx;
 		padding: 4rpx 10rpx;

+ 1818 - 0
unpackage/dist/dev/mp-weixin/pages/interview-question/interview-question.js

@@ -0,0 +1,1818 @@
+"use strict";
+const common_vendor = require("../../common/vendor.js");
+const common_config = require("../../common/config.js");
+const _sfc_main = {
+  name: "IdentityVerify",
+  data() {
+    return {
+      loading: false,
+      responses: [],
+      processedResponses: [],
+      assistantResponse: "",
+      audioTranscript: "",
+      videoPlaying: false,
+      showDebugInfo: false,
+      // 设置为true可以显示调试信息
+      videoUrl: "http://121.36.251.245:9000/minlong/tenant_1/general_uploads/abdaa6fda8494e3a8613304743ed0433.mp4",
+      // 用于存储AI数字人视频URL
+      showReplayButton: false,
+      cameraStream: null,
+      // 存储摄像头流
+      cameraError: null,
+      // 存储摄像头错误信息
+      useMiniProgramCameraComponent: false,
+      // 添加小程序相机组件标志
+      cameraContext: null,
+      // 添加相机上下文
+      currentSubtitle: "",
+      subtitles: [
+        {
+          startTime: 0,
+          // 开始时间(秒)
+          endTime: 6,
+          // 结束时间(秒)
+          text: "您已完成本次面试全部题目,请问您对于这个岗位还有什么想要了解的吗?"
+        }
+      ],
+      // secondVideoSubtitles: [
+      //   {
+      //     startTime: 0,
+      //     endTime: 10,
+      //     text: '请结合您的基本信息与过往履历进行简单的自我介绍,并讲一讲您有哪些优势胜任本岗位:'
+      //   }
+      // ],
+      // thirdVideoSubtitles: [
+      //   {
+      //     startTime: 0,
+      //     endTime: 4,
+      //     text: '在工作中,你如何确保个人防护装备的正确使用?'
+      //   }
+      // ],
+      // fourthVideoSubtitles: [
+      //   {
+      //     startTime: 0,
+      //     endTime: 4,
+      //     text: '描述一次你与团队合作改善生产流程的经历。'
+      //   }
+      // ],
+      // fifthVideoSubtitles: [
+      //   {
+      //     startTime: 0,
+      //     endTime: 6,
+      //     text: '你在团队合作中曾遇到过哪些挑战?如何解决团队内部的分歧?'
+      //   }
+      // ],
+      // sixthVideoSubtitles: [
+      //   {
+      //     startTime: 0,
+      //     endTime: 5,
+      //     text: '您已完成本次面试全部题目,请问您对于这个岗位还有什么想要了解的吗?'
+      //   }
+      // ],
+      showAnswerButton: false,
+      // 控制答题按钮显示
+      currentVideoIndex: 0,
+      // 当前播放的视频索引
+      videoList: [
+        "http://121.36.251.245:9000/minlong/tenant_1/general_uploads/abdaa6fda8494e3a8613304743ed0433.mp4"
+        //结束
+      ],
+      isRecording: false,
+      recordingTimer: null,
+      showStopRecordingButton: false,
+      mediaRecorder: null,
+      recordedChunks: [],
+      recorder: null,
+      lastUploadedVideoUrl: "",
+      showStartRecordingButton: false,
+      showRetryButton: false,
+      // 控制重试按钮显示
+      lastVideoToRetry: null,
+      // 存储上次失败的视频URL,用于重试
+      recordingStartTime: null,
+      // 录制开始时间
+      recordingTimerCount: 0,
+      // 录制计时器计数
+      recordingTimeDisplay: "00:00",
+      // 格式化的录制时间显示
+      // 添加上传队列相关数据
+      uploadQueue: [],
+      // 存储待上传的视频
+      isUploading: false,
+      // 标记是否正在上传
+      uploadProgress: {},
+      // 存储每个视频的上传进度
+      uploadStatus: {},
+      // 存储每个视频的上传状态
+      showUploadStatus: false,
+      // 是否显示上传状态指示器
+      uploadStatusText: "",
+      // 上传状态文本
+      mediaRecorderTimeout: null,
+      // 用于存储MediaRecorder的超时机制
+      maxRecordingTime: 300,
+      // 最大录制时间(秒)- 从60秒改为300秒(5分钟)
+      remainingTime: 300,
+      // 剩余录制时间(秒)- 从60秒改为300秒
+      // 修改初始视频索引,添加参数控制
+      startFromLastVideo: false
+      // 是否从最后一个视频开始播放
+    };
+  },
+  onLoad(options) {
+    if (options && options.startFromLast === "true") {
+      this.startFromLastVideo = true;
+      this.currentVideoIndex = this.videoList.length - 1;
+    }
+  },
+  mounted() {
+    if (this.startFromLastVideo) {
+      console.log("从最后一个视频开始播放");
+      this.currentVideoIndex = this.videoList.length - 1;
+      this.videoUrl = this.videoList[this.currentVideoIndex];
+    }
+    this.playDigitalHumanVideo();
+    this.checkAudioPermission();
+    this.initCamera();
+    this.checkIOSCameraRecordPermission();
+    this.checkAndFixRenderingIssues();
+    setTimeout(() => {
+      if (this.cameraStream && !this.useMiniProgramCameraComponent) {
+        this.testAudioInput();
+      }
+    }, 3e3);
+    common_vendor.index.setKeepScreenOn({
+      keepScreenOn: true
+    });
+  },
+  beforeDestroy() {
+    this.stopUserCamera();
+  },
+  methods: {
+    // 初始化相机
+    async initCamera() {
+      const systemInfo = common_vendor.index.getSystemInfoSync();
+      const isMiniProgram = systemInfo.uniPlatform === "mp-weixin" || systemInfo.uniPlatform === "mp-alipay" || systemInfo.uniPlatform === "mp-baidu";
+      if (isMiniProgram) {
+        this.useMiniProgramCameraComponent = true;
+        this.cameraContext = common_vendor.index.createCameraContext();
+        common_vendor.index.getSetting({
+          success: (res) => {
+            if (!res.authSetting["scope.record"]) {
+              common_vendor.index.authorize({
+                scope: "scope.record",
+                success: () => {
+                  console.log("录音权限已获取");
+                },
+                fail: (err) => {
+                  console.error("录音权限获取失败:", err);
+                  this.showPermissionDialog("录音");
+                }
+              });
+            }
+            if (!res.authSetting["scope.camera"]) {
+              common_vendor.index.authorize({
+                scope: "scope.camera",
+                success: () => {
+                  console.log("相机权限已获取");
+                },
+                fail: (err) => {
+                  console.error("相机权限获取失败:", err);
+                  this.showPermissionDialog("相机");
+                }
+              });
+            }
+            const systemInfo2 = common_vendor.index.getSystemInfoSync();
+            if (systemInfo2.platform === "ios") {
+              if (!res.authSetting["scope.camera"] || !res.authSetting["scope.record"]) {
+                console.log("iOS需要同时获取相机和录音权限");
+              }
+            }
+          }
+        });
+      } else {
+        try {
+          const constraints = {
+            audio: {
+              echoCancellation: true,
+              noiseSuppression: true,
+              autoGainControl: true
+            },
+            video: {
+              width: { ideal: 640, max: 1280 },
+              // 控制视频宽度
+              height: { ideal: 480, max: 720 },
+              // 控制视频高度
+              frameRate: { ideal: 15, max: 24 },
+              // 控制帧率
+              facingMode: "user"
+            }
+          };
+          const stream = await navigator.mediaDevices.getUserMedia(constraints);
+          this.cameraStream = stream;
+          const audioTracks = stream.getAudioTracks();
+          console.log("音频轨道数量:", audioTracks.length);
+          if (audioTracks.length > 0) {
+            console.log("音频轨道已获取:", audioTracks[0].label);
+            audioTracks[0].enabled = true;
+          } else {
+            console.warn("未检测到音频轨道,尝试单独获取音频");
+            this.tryGetAudioOnly();
+          }
+          const videoElement = this.$refs.userCameraVideo;
+          if (videoElement) {
+            videoElement.srcObject = stream;
+            videoElement.muted = true;
+          }
+        } catch (error) {
+          console.error("获取摄像头失败:", error);
+          this.cameraError = error.message || "无法访问摄像头";
+          common_vendor.index.showToast({
+            title: "无法访问摄像头,请检查权限设置",
+            icon: "none"
+          });
+        }
+      }
+    },
+    // 停止用户摄像头
+    stopUserCamera() {
+      if (this.cameraStream) {
+        this.cameraStream.getTracks().forEach((track) => {
+          track.stop();
+        });
+        this.cameraStream = null;
+      }
+    },
+    async fetchData() {
+      this.loading = true;
+      this.assistantResponse = "";
+      this.audioTranscript = "";
+      this.processedResponses = [];
+      try {
+        const requestTask = common_vendor.index.request({
+          url: "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
+          method: "POST",
+          header: {
+            "Content-Type": "application/json",
+            "Authorization": "Bearer sk-9e1ec73a7d97493b8613c63f06b6110c"
+          },
+          data: {
+            "model": "qwen-omni-turbo",
+            "messages": [
+              {
+                "role": "user",
+                "content": [
+                  {
+                    "type": "input_audio",
+                    "input_audio": {
+                      "data": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20250211/tixcef/cherry.wav",
+                      "format": "wav"
+                    }
+                  },
+                  {
+                    "type": "text",
+                    "text": "这段音频在说什么"
+                  }
+                ]
+              }
+            ],
+            "stream": true,
+            "stream_options": {
+              "include_usage": true
+            },
+            "modalities": ["text", "audio"],
+            "audio": { "voice": "Cherry", "format": "wav" }
+          },
+          success: (res) => {
+            console.log("请求成功,响应数据:", res.data);
+            if (typeof res.data === "string" && res.data.includes("data: {")) {
+              const chunks = res.data.split("data: ").filter((chunk) => chunk.trim() !== "");
+              chunks.forEach((chunk) => {
+                this.handleStreamResponse(chunk);
+              });
+            } else {
+              this.handleStreamResponse(res.data);
+            }
+            this.playDigitalHumanVideo();
+          },
+          fail: (err) => {
+            console.error("请求失败:", err);
+          },
+          complete: () => {
+            this.loading = false;
+          }
+        });
+      } catch (error) {
+        console.error("获取数据失败:", error);
+        this.loading = false;
+      }
+    },
+    handleStreamResponse(data) {
+      if (typeof data === "string") {
+        if (data === "[DONE]")
+          return;
+        try {
+          const cleanData = data.trim();
+          if (cleanData.startsWith("{") && cleanData.endsWith("}")) {
+            const jsonData = JSON.parse(cleanData);
+            this.processStreamChunk(jsonData);
+          }
+        } catch (e) {
+          console.error("解析JSON失败:", e, "原始数据:", data);
+        }
+      } else {
+        this.processStreamChunk(data);
+      }
+    },
+    processStreamChunk(chunk) {
+      if (chunk.choices && chunk.choices.length > 0) {
+        const choice = chunk.choices[0];
+        if (choice.delta && choice.delta.content) {
+          this.assistantResponse += choice.delta.content;
+        }
+        if (choice.delta && choice.delta.audio && choice.delta.audio.transcript) {
+          this.audioTranscript += choice.delta.audio.transcript;
+        }
+        if (choice.delta) {
+          const result = {};
+          if (choice.delta.role) {
+            result.role = choice.delta.role;
+          }
+          if (choice.delta.audio && choice.delta.audio.transcript) {
+            result.transcript = choice.delta.audio.transcript;
+          }
+          if (Object.keys(result).length > 0) {
+            this.processedResponses.push(result);
+          }
+        }
+      }
+    },
+    processResponseData() {
+      this.processedResponses = this.responses.map((item) => {
+        const result = {};
+        if (item.delta && item.delta.role) {
+          result.role = item.delta.role;
+        }
+        if (item.delta && item.delta.audio && item.delta.audio.transcript) {
+          result.transcript = item.delta.audio.transcript;
+        }
+        return result;
+      }).filter((item) => Object.keys(item).length > 0);
+    },
+    // 播放数字人视频
+    playDigitalHumanVideo() {
+      this.videoUrl = this.videoList[this.currentVideoIndex];
+      this.videoPlaying = true;
+      console.log(`播放视频 ${this.currentVideoIndex + 1}/${this.videoList.length}: ${this.videoUrl}`);
+      this.$nextTick(() => {
+        const videoContext = common_vendor.index.createVideoContext("myVideo", this);
+        if (videoContext) {
+          videoContext.play();
+          setTimeout(() => {
+            if (this.videoPlaying && this.$refs.videoPlayer) {
+              console.log("视频应该正在播放");
+            } else {
+              console.log("视频可能未成功播放,尝试替代方案");
+              this.tryAlternativeVideoPath();
+            }
+          }, 1e3);
+        } else {
+          console.error("无法创建视频上下文");
+          this.tryAlternativeVideoPath();
+        }
+      });
+    },
+    // 修改 tryAlternativeVideoPath 方法
+    tryAlternativeVideoPath() {
+      console.log("尝试使用替代路径");
+      const alternativePaths = [
+        "./static/demo.mp4",
+        "../static/demo.mp4",
+        "static/demo.mp4",
+        "/static/demo.mp4",
+        // 添加绝对路径
+        `${window.location.origin}/static/demo.mp4`
+      ];
+      const currentPathIndex = alternativePaths.indexOf(this.videoUrl);
+      const nextPathIndex = (currentPathIndex + 1) % alternativePaths.length;
+      this.videoUrl = alternativePaths[nextPathIndex];
+      console.log("尝试新路径:", this.videoUrl);
+      this.$nextTick(() => {
+        const videoContext = common_vendor.index.createVideoContext("myVideo", this);
+        if (videoContext) {
+          videoContext.stop();
+          videoContext.play();
+          setTimeout(() => {
+            if (nextPathIndex === alternativePaths.length - 1 && !this.videoPlaying) {
+              console.log("所有路径均失败,尝试使用uni.getVideoInfo检查视频");
+              this.checkVideoWithAPI();
+            }
+          }, 1e3);
+        }
+      });
+    },
+    // 添加新方法:使用uni API检查视频
+    checkVideoWithAPI() {
+      common_vendor.index.getVideoInfo({
+        src: "/static/demo.mp4",
+        success: (res) => {
+          console.log("视频信息获取成功:", res);
+          this.videoUrl = "/static/demo.mp4";
+          this.$nextTick(() => {
+            const videoContext = common_vendor.index.createVideoContext("myVideo", this);
+            if (videoContext) {
+              videoContext.play();
+            }
+          });
+        },
+        fail: (err) => {
+          console.error("视频信息获取失败:", err);
+          this.fallbackToLocalVideo();
+        }
+      });
+    },
+    // 添加新方法:回退到本地视频
+    fallbackToLocalVideo() {
+      console.log("尝试使用本地视频资源");
+      const platform = common_vendor.index.getSystemInfoSync().platform;
+      if (platform === "android" || platform === "ios") {
+        this.videoUrl = platform === "android" ? "android.resource://package_name/raw/demo" : "file:///assets/demo.mp4";
+        this.$nextTick(() => {
+          const videoContext = common_vendor.index.createVideoContext("myVideo", this);
+          if (videoContext) {
+            videoContext.play();
+          }
+        });
+      } else {
+        this.videoPlaying = false;
+        common_vendor.index.showToast({
+          title: "视频加载失败,显示静态图片",
+          icon: "none"
+        });
+      }
+    },
+    // 修改 handleVideoError 方法
+    handleVideoError(e) {
+      console.error("视频加载错误:", e);
+      if (e && e.detail) {
+        console.error("详细错误信息:", e.detail);
+      }
+      common_vendor.index.getFileInfo({
+        filePath: this.videoUrl.startsWith("/") ? this.videoUrl.substring(1) : this.videoUrl,
+        success: (res) => {
+          console.log("文件存在,大小:", res.size);
+          this.tryDifferentFormat();
+        },
+        fail: (err) => {
+          console.error("文件不存在或无法访问:", err);
+          this.tryAlternativeVideoPath();
+        }
+      });
+      common_vendor.index.showToast({
+        title: "视频加载失败,请检查文件是否存在",
+        icon: "none",
+        duration: 2e3
+      });
+    },
+    // 添加新方法:尝试不同格式
+    tryDifferentFormat() {
+      console.log("尝试不同的视频格式");
+      const formats = [
+        { ext: "mp4", mime: "video/mp4" },
+        { ext: "webm", mime: "video/webm" },
+        { ext: "ogg", mime: "video/ogg" },
+        { ext: "mov", mime: "video/quicktime" }
+      ];
+      const currentPath = this.videoUrl;
+      const basePath = currentPath.substring(0, currentPath.lastIndexOf(".")) || "/static/demo";
+      let nextFormat = formats.find((f) => !currentPath.endsWith(f.ext));
+      if (nextFormat) {
+        this.videoUrl = `${basePath}.${nextFormat.ext}`;
+        console.log("尝试新格式:", this.videoUrl);
+        this.$nextTick(() => {
+          const videoContext = common_vendor.index.createVideoContext("myVideo", this);
+          if (videoContext) {
+            videoContext.stop();
+            videoContext.play();
+          }
+        });
+      } else {
+        this.useBuiltInResource();
+      }
+    },
+    // 添加新方法:使用内置资源
+    useBuiltInResource() {
+      console.log("尝试使用内置资源");
+      const platform = common_vendor.index.getSystemInfoSync().platform;
+      if (platform === "windows") {
+        process.env.UNI_INPUT_DIR || "";
+        this.videoUrl = `./static/demo.mp4`;
+        console.log("Windows平台尝试路径:", this.videoUrl);
+      } else if (platform === "android" || platform === "ios") {
+        this.useNativeVideo();
+      } else {
+        const baseUrl = window.location.origin;
+        this.videoUrl = `${baseUrl}/static/demo.mp4`;
+        console.log("Web平台尝试URL:", this.videoUrl);
+      }
+      this.$nextTick(() => {
+        const videoContext = common_vendor.index.createVideoContext("myVideo", this);
+        if (videoContext) {
+          videoContext.play();
+        }
+      });
+    },
+    // 添加新方法:使用原生视频能力
+    useNativeVideo() {
+      console.log("尝试使用原生视频能力");
+      common_vendor.index.chooseVideo({
+        sourceType: ["album"],
+        success: (res) => {
+          this.videoUrl = res.tempFilePath;
+          this.$nextTick(() => {
+            const videoContext = common_vendor.index.createVideoContext("myVideo", this);
+            if (videoContext) {
+              videoContext.play();
+            }
+          });
+        },
+        fail: () => {
+          this.videoPlaying = false;
+          common_vendor.index.showToast({
+            title: "无法加载视频,显示静态图片",
+            icon: "none"
+          });
+        }
+      });
+    },
+    // 处理视频结束事件
+    handleVideoEnded() {
+      console.log("视频播放结束,当前视频索引:", this.currentVideoIndex);
+      this.videoPlaying = false;
+      this.showStartRecordingButton = true;
+      this.showAnswerButton = false;
+    },
+    // 添加新方法:开始录制用户回答
+    startRecordingAnswer() {
+      console.log("开始录制用户回答");
+      this.isRecording = true;
+      this.recordingStartTime = Date.now();
+      this.recordingTimerCount = 0;
+      this.remainingTime = this.maxRecordingTime;
+      this.recordingTimer = setInterval(() => {
+        this.recordingTimerCount++;
+        this.remainingTime = Math.max(0, this.maxRecordingTime - this.recordingTimerCount);
+        this.recordingTimeDisplay = this.formatTime(this.recordingTimerCount) + " / " + this.formatTime(this.maxRecordingTime);
+        if (this.recordingTimerCount >= this.maxRecordingTime) {
+          console.log("已达到最大录制时间(60秒),自动停止录制");
+          this.stopRecordingAnswer();
+        }
+      }, 1e3);
+      const systemInfo = common_vendor.index.getSystemInfoSync();
+      const isMiniProgram = systemInfo.uniPlatform && systemInfo.uniPlatform.startsWith("mp-");
+      if (isMiniProgram) {
+        this.startMiniProgramRecording();
+      } else {
+        this.startBrowserRecording();
+      }
+      this.showStopRecordingButton = true;
+    },
+    // 添加一个新方法:重置相机组件
+    resetCamera() {
+      console.log("重置相机组件");
+      this.useMiniProgramCameraComponent = false;
+      if (this.cameraContext) {
+        this.cameraContext = null;
+      }
+      setTimeout(() => {
+        this.useMiniProgramCameraComponent = true;
+        setTimeout(() => {
+          this.cameraContext = common_vendor.index.createCameraContext();
+          console.log("相机组件已重置");
+        }, 500);
+      }, 500);
+    },
+    // 修改 startMiniProgramRecording 方法
+    startMiniProgramRecording() {
+      console.log("开始小程序录制方法");
+      const systemInfo = common_vendor.index.getSystemInfoSync();
+      const isIOS = systemInfo.platform === "ios";
+      if (isIOS) {
+        this.resetCamera();
+        setTimeout(() => {
+          this.actualStartRecording(isIOS);
+        }, 1e3);
+      } else {
+        this.actualStartRecording(isIOS);
+      }
+    },
+    // 添加新方法:实际开始录制
+    actualStartRecording(isIOS) {
+      if (!this.cameraContext) {
+        this.cameraContext = common_vendor.index.createCameraContext();
+        console.log("创建新的相机上下文");
+      }
+      common_vendor.index.getSetting({
+        success: (res) => {
+          const hasRecordAuth = res.authSetting["scope.record"];
+          const hasCameraAuth = res.authSetting["scope.camera"];
+          if (!hasRecordAuth || !hasCameraAuth) {
+            console.warn("缺少必要权限,请求权限");
+            this.requestMiniProgramPermissions();
+            return;
+          }
+          if (isIOS) {
+            console.log("iOS: 检查相机状态");
+            const options = {
+              timeout: 3e5,
+              // 300秒超时 (5分钟)
+              quality: "low",
+              // 降低质量
+              compressed: true,
+              success: () => {
+                console.log("iOS录制开始成功");
+              },
+              fail: (err) => {
+                console.error("iOS录制失败:", err);
+                this.useAlternativeRecordingMethod();
+              }
+            };
+            try {
+              console.log("尝试开始录制");
+              this.recorder = this.cameraContext.startRecord(options);
+            } catch (e) {
+              console.error("开始录制异常:", e);
+              this.useAlternativeRecordingMethod();
+            }
+          } else {
+            const options = {
+              timeout: 3e5,
+              // 300秒超时 (5分钟)
+              quality: "medium",
+              compressed: true,
+              success: () => {
+                console.log("Android录制开始成功");
+              },
+              fail: (err) => {
+                console.error("Android录制失败:", err);
+                common_vendor.index.showToast({
+                  title: "录制失败,请检查相机权限",
+                  icon: "none"
+                });
+                this.proceedToNextQuestion();
+              }
+            };
+            this.recorder = this.cameraContext.startRecord(options);
+          }
+        }
+      });
+    },
+    // 添加新方法:使用替代录制方法
+    useAlternativeRecordingMethod() {
+      console.log("使用替代录制方法");
+      common_vendor.index.showActionSheet({
+        itemList: ["使用相册中的视频", "跳过此问题"],
+        success: (res) => {
+          if (res.tapIndex === 0) {
+            common_vendor.index.chooseVideo({
+              sourceType: ["album"],
+              maxDuration: 300,
+              // 从60秒改为300秒
+              camera: "front",
+              success: (res2) => {
+                console.log("选择视频成功:", res2.tempFilePath);
+                this.isRecording = false;
+                this.showStopRecordingButton = false;
+                this.uploadRecordedVideo(res2.tempFilePath);
+              },
+              fail: () => {
+                console.log("用户取消选择视频");
+                this.proceedToNextQuestion();
+              }
+            });
+          } else {
+            console.log("用户选择跳过问题");
+            this.proceedToNextQuestion();
+          }
+        },
+        fail: () => {
+          console.log("操作取消");
+          this.proceedToNextQuestion();
+        }
+      });
+    },
+    // 添加新方法:请求小程序权限
+    requestMiniProgramPermissions() {
+      common_vendor.index.authorize({
+        scope: "scope.record",
+        success: () => {
+          console.log("录音权限已获取");
+          common_vendor.index.authorize({
+            scope: "scope.camera",
+            success: () => {
+              console.log("相机权限已获取");
+              this.startMiniProgramRecording();
+            },
+            fail: (err) => {
+              console.error("相机权限获取失败:", err);
+              this.showPermissionDialog("相机");
+            }
+          });
+        },
+        fail: (err) => {
+          console.error("录音权限获取失败:", err);
+          this.showPermissionDialog("录音");
+        }
+      });
+    },
+    // 修改浏览器环境下的录制方法
+    startBrowserRecording() {
+      if (!this.cameraStream) {
+        console.error("没有可用的摄像头流");
+        common_vendor.index.showToast({
+          title: "录制失败,摄像头未就绪",
+          icon: "none"
+        });
+        this.proceedToNextQuestion();
+        return;
+      }
+      try {
+        const hasAudio = this.cameraStream.getAudioTracks().length > 0;
+        if (!hasAudio) {
+          console.warn("警告:媒体流中没有音频轨道,尝试重新获取带音频的媒体流");
+          navigator.mediaDevices.getUserMedia({
+            audio: {
+              echoCancellation: true,
+              noiseSuppression: true,
+              autoGainControl: true
+            },
+            video: true
+          }).then((newStream) => {
+            const audioTracks = newStream.getAudioTracks();
+            if (audioTracks.length > 0) {
+              console.log("成功获取音频轨道:", audioTracks[0].label);
+              const videoTrack = this.cameraStream.getVideoTracks()[0];
+              const audioTrack = newStream.getAudioTracks()[0];
+              const combinedStream = new MediaStream();
+              if (videoTrack)
+                combinedStream.addTrack(videoTrack);
+              if (audioTrack)
+                combinedStream.addTrack(audioTrack);
+              this.cameraStream = combinedStream;
+              const videoElement = this.$refs.userCameraVideo;
+              if (videoElement) {
+                videoElement.srcObject = combinedStream;
+                videoElement.muted = true;
+              }
+              this.setupMediaRecorder(combinedStream);
+            } else {
+              console.warn("仍然无法获取音频轨道");
+              this.setupMediaRecorder(this.cameraStream);
+            }
+          }).catch((err) => {
+            console.error("获取音频失败:", err);
+            this.setupMediaRecorder(this.cameraStream);
+          });
+        } else {
+          console.log("检测到音频轨道,直接使用");
+          this.setupMediaRecorder(this.cameraStream);
+        }
+      } catch (error) {
+        console.error("浏览器录制失败:", error);
+        common_vendor.index.showToast({
+          title: "录制失败,浏览器可能不支持此功能",
+          icon: "none"
+        });
+        this.proceedToNextQuestion();
+      }
+    },
+    // 修改 setupMediaRecorder 方法
+    setupMediaRecorder(stream) {
+      const videoTracks = stream.getVideoTracks();
+      const audioTracks = stream.getAudioTracks();
+      console.log("设置MediaRecorder - 视频轨道:", videoTracks.length, "音频轨道:", audioTracks.length);
+      let mimeType = "";
+      const supportedTypes = [
+        "video/webm;codecs=vp9,opus",
+        "video/webm;codecs=vp8,opus",
+        "video/webm;codecs=h264,opus",
+        "video/mp4;codecs=h264,aac",
+        "video/webm",
+        "video/mp4"
+      ];
+      for (const type of supportedTypes) {
+        if (MediaRecorder.isTypeSupported(type)) {
+          mimeType = type;
+          console.log("使用支持的MIME类型:", mimeType);
+          break;
+        }
+      }
+      const options = {
+        mimeType: mimeType || "",
+        audioBitsPerSecond: 64e3,
+        // 降低音频比特率
+        videoBitsPerSecond: 1e6
+        // 降低视频比特率到1Mbps
+      };
+      try {
+        this.mediaRecorder = new MediaRecorder(stream, options);
+        console.log("MediaRecorder创建成功,使用选项:", options);
+      } catch (e) {
+        console.warn("使用指定选项创建MediaRecorder失败,尝试使用默认选项");
+        this.mediaRecorder = new MediaRecorder(stream);
+      }
+      this.recordedChunks = [];
+      this.mediaRecorder.ondataavailable = (event) => {
+        if (event.data && event.data.size > 0) {
+          this.recordedChunks.push(event.data);
+          console.log(`收到数据块: ${event.data.size} 字节`);
+        }
+      };
+      this.mediaRecorder.onstop = async () => {
+        console.log("MediaRecorder停止,数据块数量:", this.recordedChunks.length);
+        if (this.recordedChunks.length === 0) {
+          console.error("没有录制到数据");
+          common_vendor.index.showToast({
+            title: "录制失败,未捕获到数据",
+            icon: "none"
+          });
+          this.proceedToNextQuestion();
+          return;
+        }
+        const mimeType2 = this.mediaRecorder.mimeType || "video/webm";
+        const blob = new Blob(this.recordedChunks, { type: mimeType2 });
+        console.log("创建Blob,原始大小:", blob.size, "类型:", mimeType2);
+        common_vendor.index.showLoading({
+          title: "正在处理视频...",
+          mask: true
+        });
+        try {
+          const compressedBlob = await this.compressVideo(blob);
+          const fileName = `answer_${this.currentVideoIndex}_${Date.now()}.webm`;
+          const file = new File([compressedBlob], fileName, { type: mimeType2 });
+          common_vendor.index.hideLoading();
+          this.uploadRecordedVideo(file);
+        } catch (error) {
+          console.error("视频处理失败:", error);
+          common_vendor.index.hideLoading();
+          const fileName = `answer_${this.currentVideoIndex}_${Date.now()}.webm`;
+          const file = new File([blob], fileName, { type: mimeType2 });
+          this.uploadRecordedVideo(file);
+        }
+      };
+      this.mediaRecorder.onerror = (event) => {
+        console.error("MediaRecorder错误:", event.error);
+      };
+      try {
+        this.mediaRecorder.start(1e3);
+        console.log("MediaRecorder开始录制");
+        this.mediaRecorderTimeout = setTimeout(() => {
+          if (this.mediaRecorder && this.mediaRecorder.state === "recording") {
+            console.log("MediaRecorder备份超时机制触发,停止录制");
+            this.mediaRecorder.stop();
+          }
+        }, 3e5);
+      } catch (e) {
+        console.error("开始录制失败:", e);
+      }
+    },
+    // 添加新方法:停止录制用户回答
+    stopRecordingAnswer() {
+      console.log("停止录制用户回答");
+      const recordingDuration = this.getRecordingDuration();
+      const minimumDuration = 5;
+      if (recordingDuration < minimumDuration) {
+        common_vendor.index.showToast({
+          title: "录制时间过短,请至少录制5秒",
+          icon: "none",
+          duration: 2e3
+        });
+        return;
+      }
+      this.completeRecordingStop();
+    },
+    // 添加新方法:获取录制时长
+    getRecordingDuration() {
+      if (this.recordingStartTime) {
+        return (Date.now() - this.recordingStartTime) / 1e3;
+      }
+      if (this.mediaRecorder && this.$refs.userCameraVideo) {
+        return this.$refs.userCameraVideo.currentTime || 0;
+      }
+      if (this.recordingTimerCount) {
+        return this.recordingTimerCount;
+      }
+      return 0;
+    },
+    // 添加新方法:重置录制
+    resetRecording() {
+      if (this.recordingTimer) {
+        clearTimeout(this.recordingTimer);
+      }
+      this.recordingStartTime = Date.now();
+      this.recordingTimerCount = 0;
+      if (this.mediaRecorder && this.mediaRecorder.state !== "inactive") {
+        this.mediaRecorder.stop();
+        this.recordedChunks = [];
+        setTimeout(() => {
+          this.startBrowserRecording();
+        }, 500);
+      } else if (this.cameraContext) {
+        this.cameraContext.stopRecord({
+          success: () => {
+            console.log("重置录制:停止当前录制成功");
+            setTimeout(() => {
+              this.startMiniProgramRecording();
+            }, 500);
+          },
+          fail: (err) => {
+            console.error("重置录制:停止当前录制失败", err);
+            this.startMiniProgramRecording();
+          }
+        });
+      }
+      common_vendor.index.showToast({
+        title: "请重新开始回答",
+        icon: "none",
+        duration: 2e3
+      });
+    },
+    // 添加新方法:完成录制停止流程
+    completeRecordingStop() {
+      this.isRecording = false;
+      if (this.recordingTimer) {
+        clearInterval(this.recordingTimer);
+        this.recordingTimer = null;
+      }
+      if (this.mediaRecorderTimeout) {
+        clearTimeout(this.mediaRecorderTimeout);
+        this.mediaRecorderTimeout = null;
+      }
+      common_vendor.index.hideLoading();
+      this.showStopRecordingButton = false;
+      const systemInfo = common_vendor.index.getSystemInfoSync();
+      const isMiniProgram = systemInfo.uniPlatform && systemInfo.uniPlatform.startsWith("mp-");
+      if (isMiniProgram) {
+        this.stopMiniProgramRecording();
+      } else {
+        this.stopBrowserRecording();
+      }
+    },
+    // 修改 stopMiniProgramRecording 方法
+    stopMiniProgramRecording() {
+      if (!this.cameraContext) {
+        console.error("相机上下文不存在");
+        this.proceedToNextQuestion();
+        return;
+      }
+      const systemInfo = common_vendor.index.getSystemInfoSync();
+      const isIOS = systemInfo.platform === "ios";
+      const stopOptions = {
+        success: (res) => {
+          console.log("小程序录像停止成功:", res);
+          const tempFilePath = res.tempVideoPath;
+          if (!tempFilePath) {
+            console.error("未获取到视频文件路径");
+            common_vendor.index.showToast({
+              title: "录制失败,未获取到视频文件",
+              icon: "none"
+            });
+            this.proceedToNextQuestion();
+            return;
+          }
+          if (isIOS) {
+            common_vendor.index.getFileInfo({
+              filePath: tempFilePath,
+              success: () => {
+                this.uploadRecordedVideo(tempFilePath);
+              },
+              fail: (err) => {
+                console.error("视频文件不存在:", err);
+                common_vendor.index.showToast({
+                  title: "录制失败,视频文件不存在",
+                  icon: "none"
+                });
+                this.proceedToNextQuestion();
+              }
+            });
+          } else {
+            this.uploadRecordedVideo(tempFilePath);
+          }
+        },
+        fail: (err) => {
+          console.error("小程序录像停止失败:", err);
+          common_vendor.index.showToast({
+            title: "录制失败",
+            icon: "none"
+          });
+          this.proceedToNextQuestion();
+        }
+      };
+      this.cameraContext.stopRecord(stopOptions);
+    },
+    // 添加新方法:停止浏览器录制
+    stopBrowserRecording() {
+      if (this.mediaRecorder && this.mediaRecorder.state !== "inactive") {
+        this.mediaRecorder.stop();
+        console.log("浏览器录制停止成功");
+      } else {
+        console.error("MediaRecorder不存在或已经停止");
+        this.proceedToNextQuestion();
+      }
+    },
+    // 修改上传录制的视频方法
+    uploadRecordedVideo(fileOrPath) {
+      console.log("准备上传视频:", typeof fileOrPath === "string" ? fileOrPath : fileOrPath.name);
+      let questionId = 10;
+      const uploadTask = {
+        id: Date.now().toString(),
+        // 生成唯一ID
+        file: fileOrPath,
+        questionId,
+        attempts: 0,
+        // 上传尝试次数
+        maxAttempts: 3
+        // 最大尝试次数
+      };
+      this.uploadQueue.push(uploadTask);
+      this.uploadProgress[uploadTask.id] = 0;
+      this.uploadStatus[uploadTask.id] = "pending";
+      common_vendor.index.showToast({
+        title: "视频已加入上传队列",
+        icon: "none",
+        duration: 1500
+      });
+      this.updateUploadStatusText();
+      if (!this.isUploading) {
+        this.processUploadQueue();
+      }
+      this.proceedToNextQuestion();
+    },
+    // 修改 processUploadQueue 方法,添加后台上传支持
+    processUploadQueue() {
+      if (this.uploadQueue.length === 0) {
+        this.isUploading = false;
+        this.showUploadStatus = false;
+        try {
+          common_vendor.index.removeStorageSync("videoUploadStatus");
+        } catch (e) {
+          console.error("清除上传状态失败:", e);
+        }
+        this.notifyUploadComplete();
+        return;
+      }
+      this.isUploading = true;
+      this.showUploadStatus = true;
+      const task = this.uploadQueue[0];
+      this.uploadStatus[task.id] = "uploading";
+      this.updateUploadStatusText();
+      task.attempts++;
+      if (typeof task.file !== "string") {
+        this.uploadFileWithXHR(task);
+      } else {
+        this.uploadFileWithUni(task);
+      }
+      this.saveUploadStatus();
+    },
+    // 添加新方法:发送上传完成通知
+    notifyUploadComplete() {
+      try {
+        common_vendor.index.$emit("videoUploadComplete", {
+          timestamp: Date.now()
+        });
+        console.log("发送上传完成通知");
+      } catch (e) {
+        console.error("发送上传完成通知失败:", e);
+      }
+      const systemInfo = common_vendor.index.getSystemInfoSync();
+      if (systemInfo.platform === "android" || systemInfo.platform === "ios") {
+        if (plus && plus.push) {
+          plus.push.createMessage("视频上传完成", "您的面试视频已成功上传", {});
+        }
+      }
+    },
+    // 添加新方法:使用XMLHttpRequest上传文件
+    uploadFileWithXHR(task) {
+      const userInfo = common_vendor.index.getStorageSync("userInfo");
+      const openid = userInfo ? JSON.parse(userInfo).openid || "" : "";
+      const tenant_id = common_vendor.index.getStorageSync("tenant_id") || "1";
+      const formData = new FormData();
+      formData.append("file", task.file);
+      formData.append("openid", openid);
+      formData.append("tenant_id", tenant_id);
+      formData.append("application_id", common_vendor.index.getStorageSync("appId"));
+      formData.append("question_id", task.questionId);
+      formData.append("video_duration", 0);
+      formData.append("has_audio", "true");
+      const xhr = new XMLHttpRequest();
+      xhr.open("POST", `${common_config.apiBaseUrl}/api/upload/`, true);
+      xhr.timeout = 12e4;
+      xhr.upload.onprogress = (event) => {
+        if (event.lengthComputable) {
+          const percentComplete = Math.round(event.loaded / event.total * 100);
+          this.uploadProgress[task.id] = percentComplete;
+          this.updateUploadStatusText();
+        }
+      };
+      xhr.onload = () => {
+        if (xhr.status === 200) {
+          try {
+            const res = JSON.parse(xhr.responseText);
+            console.log("上传响应:", res);
+            if (res.code === 2e3) {
+              const videoUrl = res.data.url || res.data.photoUrl || "";
+              if (videoUrl) {
+                this.uploadStatus[task.id] = "success";
+                this.updateUploadStatusText();
+                this.submitVideoToInterview(videoUrl, task);
+              } else {
+                this.handleUploadFailure(task, "视频URL获取失败");
+              }
+            } else {
+              this.handleUploadFailure(task, res.msg || "上传失败");
+            }
+          } catch (e) {
+            this.handleUploadFailure(task, "解析响应失败");
+          }
+        } else {
+          this.handleUploadFailure(task, "HTTP状态: " + xhr.status);
+        }
+      };
+      xhr.onerror = () => {
+        this.handleUploadFailure(task, "网络错误");
+      };
+      xhr.ontimeout = () => {
+        this.handleUploadFailure(task, "上传超时");
+      };
+      xhr.send(formData);
+    },
+    // 添加新方法:使用uni.uploadFile上传文件
+    uploadFileWithUni(task) {
+      const userInfo = common_vendor.index.getStorageSync("userInfo");
+      const openid = userInfo ? JSON.parse(userInfo).openid || "" : "";
+      const tenant_id = common_vendor.index.getStorageSync("tenant_id") || "1";
+      const uploadTask = common_vendor.index.uploadFile({
+        url: `${common_config.apiBaseUrl}/api/upload/`,
+        filePath: task.file,
+        name: "file",
+        formData: {
+          openid,
+          tenant_id,
+          application_id: common_vendor.index.getStorageSync("appId"),
+          question_id: task.questionId,
+          video_duration: 0,
+          has_audio: "true"
+        },
+        success: (uploadRes) => {
+          try {
+            const res = JSON.parse(uploadRes.data);
+            console.log("上传响应:", res);
+            if (res.code === 2e3) {
+              const videoUrl = res.data.permanent_link || res.data.url || "";
+              if (videoUrl) {
+                this.uploadStatus[task.id] = "success";
+                this.updateUploadStatusText();
+                this.submitVideoToInterview(videoUrl, task);
+              } else {
+                this.handleUploadFailure(task, "视频URL获取失败");
+              }
+            } else {
+              this.handleUploadFailure(task, res.msg || "上传失败");
+            }
+          } catch (e) {
+            this.handleUploadFailure(task, "解析响应失败");
+          }
+        },
+        fail: (err) => {
+          this.handleUploadFailure(task, err.errMsg || "上传失败");
+        }
+      });
+      uploadTask.onProgressUpdate((res) => {
+        this.uploadProgress[task.id] = res.progress;
+        this.updateUploadStatusText();
+      });
+    },
+    // 添加新方法:处理上传失败
+    handleUploadFailure(task, errorMsg) {
+      console.error("上传失败:", errorMsg);
+      this.uploadStatus[task.id] = "failed";
+      this.updateUploadStatusText();
+      if (task.attempts < task.maxAttempts) {
+        console.log(`将在5秒后重试上传,当前尝试次数: ${task.attempts}/${task.maxAttempts}`);
+        setTimeout(() => {
+          this.uploadProgress[task.id] = 0;
+          if (typeof task.file !== "string") {
+            this.uploadFileWithXHR(task);
+          } else {
+            this.uploadFileWithUni(task);
+          }
+        }, 5e3);
+      } else {
+        console.log("超过最大重试次数,放弃上传");
+        common_vendor.index.showToast({
+          title: "视频上传失败,请稍后重试",
+          icon: "none",
+          duration: 2e3
+        });
+        this.uploadQueue.shift();
+        this.processUploadQueue();
+      }
+    },
+    // 修改 submitVideoToInterview 方法
+    submitVideoToInterview(videoUrl, task = null) {
+      console.log("提交视频URL到面试接口:", videoUrl);
+      let questionId;
+      if (task) {
+        questionId = task.questionId;
+      } else {
+        switch (this.currentVideoIndex) {
+          case 1:
+            questionId = 36;
+            break;
+          case 2:
+            questionId = 11;
+            break;
+          case 3:
+            questionId = 12;
+            break;
+          case 4:
+            questionId = 13;
+            break;
+          default:
+            questionId = 10;
+        }
+      }
+      const requestData = {
+        application_id: common_vendor.index.getStorageSync("appId"),
+        question_id: questionId,
+        video_url: videoUrl,
+        tenant_id: common_vendor.index.getStorageSync("tenant_id") || "1"
+      };
+      common_vendor.index.request({
+        url: `${common_config.apiBaseUrl}/api/job/upload_video`,
+        method: "POST",
+        data: requestData,
+        header: {
+          "content-type": "application/x-www-form-urlencoded"
+        },
+        success: (res) => {
+          console.log("面试接口提交成功:", res);
+          if (res.data.code === 0 || res.data.code === 2e3) {
+            if (task) {
+              this.uploadQueue.shift();
+              this.processUploadQueue();
+            } else {
+              common_vendor.index.showToast({
+                title: "回答已提交",
+                icon: "success"
+              });
+              this.lastUploadedVideoUrl = videoUrl;
+              this.showRetryButton = false;
+              this.lastVideoToRetry = null;
+            }
+          } else {
+            if (task) {
+              this.handleSubmitFailure(task, res.data.msg || "提交失败");
+            } else {
+              common_vendor.index.showToast({
+                title: res.data.msg || "提交失败,请重试",
+                icon: "none"
+              });
+              this.lastVideoToRetry = videoUrl;
+              this.showRetryButton = true;
+            }
+          }
+        },
+        fail: (err) => {
+          console.error("面试接口提交失败:", err);
+          if (task) {
+            this.handleSubmitFailure(task, err.errMsg || "网络错误");
+          } else {
+            common_vendor.index.hideLoading();
+            common_vendor.index.showToast({
+              title: "网络错误,请重试",
+              icon: "none"
+            });
+            this.lastVideoToRetry = videoUrl;
+            this.showRetryButton = true;
+          }
+        }
+      });
+    },
+    // 添加新方法:处理提交失败
+    handleSubmitFailure(task, errorMsg) {
+      console.error("提交失败:", errorMsg);
+      this.uploadStatus[task.id] = "failed";
+      this.updateUploadStatusText();
+      if (task.attempts < task.maxAttempts) {
+        console.log(`将在5秒后重试提交,当前尝试次数: ${task.attempts}/${task.maxAttempts}`);
+        setTimeout(() => {
+          this.submitVideoToInterview(task.videoUrl, task);
+        }, 5e3);
+      } else {
+        console.log("超过最大重试次数,放弃提交");
+        common_vendor.index.showToast({
+          title: "视频提交失败,请稍后重试",
+          icon: "none",
+          duration: 2e3
+        });
+        this.uploadQueue.shift();
+        this.processUploadQueue();
+      }
+    },
+    // 添加新方法:更新上传状态文本
+    updateUploadStatusText() {
+      if (this.uploadQueue.length === 0) {
+        this.uploadStatusText = "";
+        return;
+      }
+      const currentTask = this.uploadQueue[0];
+      const progress = this.uploadProgress[currentTask.id] || 0;
+      const status = this.uploadStatus[currentTask.id] || "pending";
+      let statusText = "";
+      switch (status) {
+        case "pending":
+          statusText = "等待上传";
+          break;
+        case "uploading":
+          statusText = `上传中 ${progress}%`;
+          break;
+        case "success":
+          statusText = "上传成功,提交中...";
+          break;
+        case "failed":
+          statusText = `上传失败,${currentTask.attempts < currentTask.maxAttempts ? "即将重试" : "已放弃"}`;
+          break;
+      }
+      this.uploadStatusText = `问题${currentTask.questionId - 9}:${statusText}`;
+      if (this.uploadQueue.length > 1) {
+        this.uploadStatusText += ` (${this.uploadQueue.length}个视频待处理)`;
+      }
+    },
+    // 修改 proceedToNextQuestion 方法
+    proceedToNextQuestion() {
+      console.log("准备跳转到下一页面");
+      this.navigateToNextPage();
+      if (this.uploadQueue.length > 0) {
+        console.log("上传队列将在后台继续处理...");
+        this.saveUploadStatus();
+      }
+    },
+    // 修改 navigateToNextPage 方法
+    navigateToNextPage() {
+      console.log("导航到下一个页面");
+      setTimeout(() => {
+        common_vendor.index.navigateTo({
+          url: "/pages/interview-result/interview-result?uploading=" + (this.uploadQueue.length > 0 ? "true" : "false"),
+          success: () => {
+            console.log("成功跳转到结果页面");
+          },
+          fail: (err) => {
+            console.error("跳转失败:", err);
+            common_vendor.index.navigateTo({
+              url: "/pages/interview/interview",
+              fail: (err2) => {
+                console.error("备用跳转也失败:", err2);
+                common_vendor.index.navigateBack({
+                  delta: 1
+                });
+              }
+            });
+          }
+        });
+      }, 2e3);
+    },
+    // 修改 handleAnswerButtonClick 方法
+    handleAnswerButtonClick() {
+      this.showAnswerButton = false;
+      this.proceedToNextQuestion();
+    },
+    // 处理相机错误
+    handleCameraError(e) {
+      console.error("相机错误:", e);
+      const systemInfo = common_vendor.index.getSystemInfoSync();
+      const isIOS = systemInfo.platform === "ios";
+      if (isIOS) {
+        console.log("iOS相机错误,尝试重新初始化");
+        common_vendor.index.showToast({
+          title: "相机初始化中...",
+          icon: "loading",
+          duration: 2e3
+        });
+        this.resetCamera();
+        if (this.isRecording) {
+          this.isRecording = false;
+          this.showStopRecordingButton = false;
+          setTimeout(() => {
+            this.useAlternativeRecordingMethod();
+          }, 1e3);
+        }
+      } else {
+        common_vendor.index.showToast({
+          title: "相机初始化失败,请检查权限设置",
+          icon: "none"
+        });
+        this.tryFallbackOptions();
+      }
+    },
+    // 添加新方法:尝试备用选项
+    tryFallbackOptions() {
+      const systemInfo = common_vendor.index.getSystemInfoSync();
+      if (systemInfo.uniPlatform === "mp-weixin" || systemInfo.uniPlatform === "mp-alipay") {
+        this.useMiniProgramCamera();
+      } else {
+        this.showStaticCameraPlaceholder();
+      }
+    },
+    // 添加新方法:使用小程序相机API
+    useMiniProgramCamera() {
+      console.log("尝试使用小程序相机组件");
+      this.useMiniProgramCameraComponent = true;
+    },
+    // 添加新方法:显示静态图像
+    showStaticCameraPlaceholder() {
+      console.log("显示静态摄像头占位图");
+      const img = document.createElement("img");
+      img.src = "/static/images/camera-placeholder.png";
+      img.className = "static-camera-image";
+      img.style.width = "100%";
+      img.style.height = "100%";
+      img.style.objectFit = "cover";
+      const container = this.$refs.userCameraVideo.parentNode;
+      container.appendChild(img);
+    },
+    // 处理视频时间更新事件
+    handleTimeUpdate(e) {
+      const currentTime = e.target.currentTime;
+      if (this.isRecording && this.recordingTimerCount) {
+        this.recordingTimeDisplay = this.formatTime(this.recordingTimerCount);
+      }
+      let currentSubtitles;
+      if (this.currentVideoIndex === 0) {
+        currentSubtitles = this.subtitles;
+      } else if (this.currentVideoIndex === 1) {
+        currentSubtitles = this.secondVideoSubtitles;
+      } else if (this.currentVideoIndex === 2) {
+        currentSubtitles = this.thirdVideoSubtitles;
+      } else if (this.currentVideoIndex === 3) {
+        currentSubtitles = this.fourthVideoSubtitles;
+      } else if (this.currentVideoIndex === 4) {
+        currentSubtitles = this.fifthVideoSubtitles;
+      } else if (this.currentVideoIndex === 5) {
+        currentSubtitles = this.sixthVideoSubtitles;
+      } else {
+        currentSubtitles = [];
+      }
+      const subtitle = currentSubtitles.find(
+        (sub) => currentTime >= sub.startTime && currentTime < sub.endTime
+      );
+      this.currentSubtitle = subtitle ? subtitle.text : "";
+    },
+    // Add a new method to handle the "Start Recording" button click
+    handleStartRecordingClick() {
+      this.showStartRecordingButton = false;
+      this.startRecordingAnswer();
+    },
+    // 修改 checkAudioPermission 方法,确保在录制前获取音频权限
+    checkAudioPermission() {
+      const systemInfo = common_vendor.index.getSystemInfoSync();
+      const isMiniProgram = systemInfo.uniPlatform && systemInfo.uniPlatform.startsWith("mp-");
+      if (isMiniProgram) {
+        common_vendor.index.getSetting({
+          success: (res) => {
+            if (!res.authSetting["scope.record"]) {
+              common_vendor.index.authorize({
+                scope: "scope.record",
+                success: () => {
+                  console.log("录音权限已获取");
+                },
+                fail: (err) => {
+                  console.error("录音权限获取失败:", err);
+                  this.showPermissionDialog("录音");
+                }
+              });
+            }
+            if (!res.authSetting["scope.camera"]) {
+              common_vendor.index.authorize({
+                scope: "scope.camera",
+                success: () => {
+                  console.log("相机权限已获取");
+                },
+                fail: (err) => {
+                  console.error("相机权限获取失败:", err);
+                  this.showPermissionDialog("相机");
+                }
+              });
+            }
+          }
+        });
+      }
+    },
+    // 添加新方法:测试音频输入
+    testAudioInput() {
+      if (!this.cameraStream) {
+        console.warn("没有可用的媒体流,无法测试音频");
+        return;
+      }
+      const audioTracks = this.cameraStream.getAudioTracks();
+      if (audioTracks.length === 0) {
+        console.warn("没有检测到音频轨道,尝试重新获取");
+        this.tryGetAudioOnly();
+        return;
+      }
+      console.log("音频轨道信息:", audioTracks[0].getSettings());
+      try {
+        const AudioContext = window.AudioContext || window.webkitAudioContext;
+        if (!AudioContext) {
+          console.warn("浏览器不支持AudioContext");
+          return;
+        }
+        const audioContext = new AudioContext();
+        const analyser = audioContext.createAnalyser();
+        const microphone = audioContext.createMediaStreamSource(this.cameraStream);
+        microphone.connect(analyser);
+        analyser.fftSize = 256;
+        const bufferLength = analyser.frequencyBinCount;
+        const dataArray = new Uint8Array(bufferLength);
+        let silenceCounter = 0;
+        const checkAudio = () => {
+          if (this.isRecording)
+            return;
+          analyser.getByteFrequencyData(dataArray);
+          let sum = 0;
+          for (let i = 0; i < bufferLength; i++) {
+            sum += dataArray[i];
+          }
+          const average = sum / bufferLength;
+          if (average > 10) {
+            console.log("检测到音频输入,音量:", average);
+            silenceCounter = 0;
+          } else {
+            silenceCounter++;
+            if (silenceCounter > 10) {
+              console.warn("持续检测不到音频输入,可能麦克风未正常工作");
+              silenceCounter = 0;
+            }
+          }
+          requestAnimationFrame(checkAudio);
+        };
+        checkAudio();
+      } catch (e) {
+        console.error("音频测试失败:", e);
+      }
+    },
+    // 添加新方法:尝试单独获取音频
+    tryGetAudioOnly() {
+      navigator.mediaDevices.getUserMedia({ audio: true }).then((audioStream) => {
+        if (this.cameraStream) {
+          const videoTrack = this.cameraStream.getVideoTracks()[0];
+          const audioTrack = audioStream.getAudioTracks()[0];
+          const combinedStream = new MediaStream();
+          if (videoTrack)
+            combinedStream.addTrack(videoTrack);
+          if (audioTrack)
+            combinedStream.addTrack(audioTrack);
+          this.cameraStream = combinedStream;
+          const videoElement = this.$refs.userCameraVideo;
+          if (videoElement) {
+            videoElement.srcObject = combinedStream;
+            videoElement.muted = true;
+          }
+          console.log("成功合并音频和视频轨道");
+        } else {
+          console.warn("没有视频流可合并");
+        }
+      }).catch((err) => {
+        console.error("单独获取音频失败:", err);
+      });
+    },
+    // 添加新方法:显示权限对话框
+    showPermissionDialog(permissionType) {
+      common_vendor.index.showModal({
+        title: "需要权限",
+        content: `请允许使用${permissionType}权限,否则可能影响面试功能`,
+        confirmText: "去设置",
+        success: (res) => {
+          if (res.confirm) {
+            common_vendor.index.openSetting({
+              success: (settingRes) => {
+                console.log("设置页面打开成功", settingRes);
+              }
+            });
+          }
+        }
+      });
+    },
+    // 添加重试上传方法
+    retryVideoUpload() {
+      if (this.lastVideoToRetry) {
+        this.showRetryButton = false;
+        common_vendor.index.showLoading({
+          title: "正在重新提交...",
+          mask: true
+        });
+        this.submitVideoToInterview(this.lastVideoToRetry);
+      } else {
+        common_vendor.index.showToast({
+          title: "没有可重试的视频",
+          icon: "none"
+        });
+      }
+    },
+    // 添加一个新方法用于压缩视频
+    async compressVideo(videoBlob) {
+      if (videoBlob.size < 5 * 1024 * 1024) {
+        return videoBlob;
+      }
+      console.log("开始压缩视频,原始大小:", videoBlob.size);
+      const videoElement = document.createElement("video");
+      videoElement.muted = true;
+      videoElement.autoplay = false;
+      const canvas = document.createElement("canvas");
+      const ctx = canvas.getContext("2d");
+      videoElement.src = URL.createObjectURL(videoBlob);
+      return new Promise((resolve) => {
+        videoElement.onloadedmetadata = () => {
+          const width = Math.floor(videoElement.videoWidth / 2);
+          const height = Math.floor(videoElement.videoHeight / 2);
+          canvas.width = width;
+          canvas.height = height;
+          const stream = canvas.captureStream(15);
+          if (videoElement.captureStream) {
+            const originalStream = videoElement.captureStream();
+            const audioTracks = originalStream.getAudioTracks();
+            if (audioTracks.length > 0) {
+              stream.addTrack(audioTracks[0]);
+            }
+          }
+          const options = {
+            mimeType: "video/webm;codecs=vp8,opus",
+            audioBitsPerSecond: 64e3,
+            videoBitsPerSecond: 8e5
+            // 800kbps
+          };
+          const mediaRecorder = new MediaRecorder(stream, options);
+          const chunks = [];
+          mediaRecorder.ondataavailable = (e) => {
+            if (e.data.size > 0) {
+              chunks.push(e.data);
+            }
+          };
+          mediaRecorder.onstop = () => {
+            const compressedBlob = new Blob(chunks, { type: "video/webm" });
+            console.log("视频压缩完成,压缩后大小:", compressedBlob.size);
+            resolve(compressedBlob);
+          };
+          videoElement.onplay = () => {
+            mediaRecorder.start(100);
+            const drawFrame = () => {
+              if (videoElement.paused || videoElement.ended) {
+                mediaRecorder.stop();
+                return;
+              }
+              ctx.drawImage(videoElement, 0, 0, width, height);
+              requestAnimationFrame(drawFrame);
+            };
+            drawFrame();
+          };
+          videoElement.play();
+        };
+      });
+    },
+    // 修改 checkIOSCameraRecordPermission 方法
+    checkIOSCameraRecordPermission() {
+      const systemInfo = common_vendor.index.getSystemInfoSync();
+      if (systemInfo.platform !== "ios")
+        return;
+      common_vendor.index.getSetting({
+        success: (res) => {
+          if (!res.authSetting["scope.camera"]) {
+            common_vendor.index.authorize({
+              scope: "scope.camera",
+              success: () => {
+                console.log("iOS相机权限已获取");
+              },
+              fail: (err) => {
+                console.error("iOS相机权限获取失败:", err);
+                this.showPermissionDialog("相机");
+              }
+            });
+          }
+          if (!res.authSetting["scope.record"]) {
+            common_vendor.index.authorize({
+              scope: "scope.record",
+              success: () => {
+                console.log("iOS录音权限已获取");
+              },
+              fail: (err) => {
+                console.error("iOS录音权限获取失败:", err);
+                this.showPermissionDialog("录音");
+              }
+            });
+          }
+        }
+      });
+    },
+    // 添加新方法:检查并修复渲染问题
+    checkAndFixRenderingIssues() {
+      try {
+        if (typeof u !== "undefined" && u) {
+          if (!u.currentQuestion) {
+            console.log("修复: 创建缺失的currentQuestion对象");
+            u.currentQuestion = {};
+          }
+          if (u.currentQuestion && typeof u.currentQuestion.isImportant === "undefined") {
+            console.log("修复: 设置缺失的isImportant属性");
+            u.currentQuestion.isImportant = false;
+          }
+        }
+      } catch (e) {
+        console.log("防御性检查异常:", e);
+      }
+    },
+    // 添加格式化时间的辅助方法
+    formatTime(seconds) {
+      if (!seconds && seconds !== 0)
+        return "03:30";
+      const minutes = Math.floor(seconds / 60);
+      const remainingSeconds = seconds % 60;
+      return `${minutes.toString().padStart(2, "0")}:${remainingSeconds.toString().padStart(2, "0")}`;
+    },
+    // 添加新方法:保存上传状态到本地存储
+    saveUploadStatus() {
+      const uploadStatus = {
+        isUploading: this.isUploading,
+        uploadQueue: this.uploadQueue.length,
+        timestamp: Date.now()
+      };
+      try {
+        common_vendor.index.setStorageSync("videoUploadStatus", JSON.stringify(uploadStatus));
+      } catch (e) {
+        console.error("保存上传状态失败:", e);
+      }
+    }
+  }
+};
+function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
+  return common_vendor.e({
+    a: $data.videoUrl,
+    b: common_vendor.o((...args) => $options.handleVideoError && $options.handleVideoError(...args)),
+    c: common_vendor.o((...args) => $options.handleVideoEnded && $options.handleVideoEnded(...args)),
+    d: common_vendor.o((...args) => $options.handleTimeUpdate && $options.handleTimeUpdate(...args)),
+    e: $data.showAnswerButton
+  }, $data.showAnswerButton ? {
+    f: common_vendor.o((...args) => $options.handleAnswerButtonClick && $options.handleAnswerButtonClick(...args))
+  } : {}, {
+    g: $data.currentSubtitle
+  }, $data.currentSubtitle ? {
+    h: common_vendor.t($data.currentSubtitle)
+  } : {}, {
+    i: $data.useMiniProgramCameraComponent
+  }, $data.useMiniProgramCameraComponent ? {
+    j: common_vendor.o((...args) => $options.handleCameraError && $options.handleCameraError(...args))
+  } : {}, {
+    k: $data.loading
+  }, $data.loading ? {} : {}, {
+    l: $data.showDebugInfo
+  }, $data.showDebugInfo ? common_vendor.e({
+    m: $data.assistantResponse
+  }, $data.assistantResponse ? {
+    n: common_vendor.t($data.assistantResponse)
+  } : {}, {
+    o: $data.audioTranscript
+  }, $data.audioTranscript ? {
+    p: common_vendor.t($data.audioTranscript)
+  } : {}, {
+    q: common_vendor.f($data.processedResponses, (item, index, i0) => {
+      return common_vendor.e({
+        a: item.role
+      }, item.role ? {
+        b: common_vendor.t(item.role)
+      } : {}, {
+        c: item.transcript
+      }, item.transcript ? {
+        d: common_vendor.t(item.transcript)
+      } : {}, {
+        e: index
+      });
+    })
+  }) : {}, {
+    r: $data.showStopRecordingButton
+  }, $data.showStopRecordingButton ? {
+    s: common_vendor.o((...args) => $options.stopRecordingAnswer && $options.stopRecordingAnswer(...args))
+  } : {}, {
+    t: $data.isRecording
+  }, $data.isRecording ? {
+    v: common_vendor.t($data.recordingTimeDisplay || "00:00 / 05:00")
+  } : {}, {
+    w: $data.showStartRecordingButton
+  }, $data.showStartRecordingButton ? {
+    x: common_vendor.o((...args) => $options.handleStartRecordingClick && $options.handleStartRecordingClick(...args))
+  } : {}, {
+    y: $data.showRetryButton
+  }, $data.showRetryButton ? {
+    z: common_vendor.o((...args) => $options.retryVideoUpload && $options.retryVideoUpload(...args))
+  } : {});
+}
+const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-2a02c54e"]]);
+wx.createPage(MiniProgramPage);

+ 4 - 0
unpackage/dist/dev/mp-weixin/pages/interview-question/interview-question.json

@@ -0,0 +1,4 @@
+{
+  "navigationBarTitleText": "",
+  "usingComponents": {}
+}

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
unpackage/dist/dev/mp-weixin/pages/interview-question/interview-question.wxml


+ 323 - 0
unpackage/dist/dev/mp-weixin/pages/interview-question/interview-question.wxss

@@ -0,0 +1,323 @@
+
+.identity-verify-container.data-v-2a02c54e {
+  padding: 0;
+  max-width: 100%;
+  margin: 0 auto;
+  height: 100vh;
+  display: flex;
+  flex-direction: column;
+  background-color: #f5f5f5;
+}
+.digital-human-container.data-v-2a02c54e {
+  position: relative;
+  width: 100%;
+  height: 100vh;
+  overflow: hidden;
+  background-color: #f0f0f0;
+}
+.digital-human-video.data-v-2a02c54e {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+/* 用户摄像头容器样式 */
+.user-camera-container.data-v-2a02c54e {
+  position: absolute;
+  top: 50px;
+  right: 5px;
+  width: 110px; /* 稍微增加宽度 */
+  height: 160px; /* 稍微增加高度 */
+  border-radius: 4px; /* 减小圆角 */
+  overflow: hidden;
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
+  z-index: 20;
+}
+
+/* 用户摄像头视频样式 */
+.user-camera-video.data-v-2a02c54e {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+  background-color: #333;
+}
+.video-player.data-v-2a02c54e {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+  outline: none; /* 移除视频获得焦点时的轮廓 */
+  -webkit-tap-highlight-color: transparent; /* 移除移动设备上的点击高亮 */
+}
+
+/* 隐藏视频控制条 */
+video.data-v-2a02c54e::-webkit-media-controls {
+  display: none !important;
+}
+video.data-v-2a02c54e::-webkit-media-controls-enclosure {
+  display: none !important;
+}
+video.data-v-2a02c54e::-webkit-media-controls-panel {
+  display: none !important;
+}
+video.data-v-2a02c54e::-webkit-media-controls-play-button {
+  display: none !important;
+}
+video.data-v-2a02c54e::-webkit-media-controls-timeline {
+  display: none !important;
+}
+video.data-v-2a02c54e::-webkit-media-controls-current-time-display {
+  display: none !important;
+}
+video.data-v-2a02c54e::-webkit-media-controls-time-remaining-display {
+  display: none !important;
+}
+video.data-v-2a02c54e::-webkit-media-controls-mute-button {
+  display: none !important;
+}
+video.data-v-2a02c54e::-webkit-media-controls-volume-slider {
+  display: none !important;
+}
+video.data-v-2a02c54e::-webkit-media-controls-fullscreen-button {
+  display: none !important;
+}
+
+/* 修改字幕覆盖层样式,使其与图片中的样式一致 */
+.subtitle-overlay.data-v-2a02c54e {
+  position: absolute;
+  bottom: 180px; /* 调整位置,使其更靠近底部 */
+  left: 5%; /* 从左侧留出5%的空间 */
+  width: 80%; /* 宽度设为90%,两侧各留5%实现居中 */
+  padding: 15px 20px; /* 增加左右内边距 */
+  border-radius: 15px;
+  background-color: rgba(255, 255, 255, 0.9); /* 白色半透明背景 */
+  color: #333; /* 文字颜色为深色 */
+  text-align: left; /* 文字左对齐 */
+  font-size: 16px;
+  line-height: 1.5;
+  z-index: 10;
+  margin: 0 auto; /* 添加自动边距实现居中 */
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); /* 添加轻微阴影 */
+}
+
+/* 添加计时器样式,右侧显示橙色圆点和时间 */
+.subtitle-overlay.data-v-2a02c54e::after {
+  content: attr(data-time);
+  position: absolute;
+  right: 20px;
+  bottom: 15px;
+  color: #333;
+  font-size: 14px;
+}
+.control-panel.data-v-2a02c54e {
+  padding: 15px;
+  display: flex;
+  justify-content: center;
+}
+.control-button.data-v-2a02c54e {
+  padding: 10px 20px;
+  background-color: #4CAF50;
+  color: white;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+}
+.loading.data-v-2a02c54e {
+  text-align: center;
+  margin: 20px 0;
+  font-size: 16px;
+}
+.response-container.data-v-2a02c54e {
+  margin-top: 20px;
+  padding: 0 20px;
+  display: none; /* 默认隐藏调试信息 */
+}
+
+/* 当showDebugInfo为true时显示 */
+.showDebugInfo .response-container.data-v-2a02c54e {
+  display: block;
+}
+.response-item.data-v-2a02c54e {
+  padding: 10px;
+  border: 1px solid #eee;
+  border-radius: 4px;
+  margin-bottom: 10px;
+  background-color: #f9f9f9;
+}
+.response-content.data-v-2a02c54e {
+  display: flex;
+  flex-direction: column;
+}
+.answer-button-container.data-v-2a02c54e {
+  position: absolute;
+  bottom: 50px; /* 将按钮放在底部而不是75%的位置 */
+  left: 50%;
+  transform: translateX(-50%); /* 只在X轴上平移,保持水平居中 */
+  z-index: 20;
+}
+
+/* 修改回答按钮样式 */
+.answer-button.data-v-2a02c54e {
+  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;
+  animation: none; /* 移除脉动动画 */
+}
+.stop-recording-button-container.data-v-2a02c54e {
+  position: absolute;
+  bottom: 50px; /* 统一与其他按钮的位置 */
+  left: 50%;
+  transform: translateX(-50%);
+  z-index: 20;
+}
+.stop-recording-button.data-v-2a02c54e {
+  width: 120px;
+  height: 40px; /* 改为矩形按钮 */
+  border-radius: 20px; /* 圆角矩形 */
+  background-color: #e74c3c; /* 白色背景 */
+  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;
+  animation: none; /* 移除脉动动画 */
+}
+.recording-indicator.data-v-2a02c54e {
+  position: absolute;
+  top: 20px;
+  left: 20px;
+  display: flex;
+  align-items: center;
+  background-color: rgba(255, 255, 255, 0.9); /* 改为白色半透明背景 */
+  padding: 5px 10px;
+  border-radius: 15px;
+  z-index: 20;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+.recording-dot.data-v-2a02c54e {
+  width: 12px;
+  height: 12px;
+  background-color: #ff6b00; /* 改为橙色,与图片中的颜色一致 */
+  border-radius: 50%;
+  margin-right: 8px;
+  animation: blink 1s infinite;
+}
+.recording-text.data-v-2a02c54e {
+  color: #333; /* 改为深色文字 */
+  font-size: 14px;
+}
+.timer-text.data-v-2a02c54e {
+  color: #333; /* 改为深色文字 */
+  font-size: 14px;
+  margin-left: 8px;
+}
+.start-recording-button-container.data-v-2a02c54e {
+  position: absolute;
+  bottom: 50px; /* 统一与其他按钮的位置 */
+  left: 50%;
+  transform: translateX(-50%);
+  z-index: 20;
+}
+.start-recording-button.data-v-2a02c54e {
+  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;
+  animation: none; /* 移除脉动动画 */
+}
+.retry-button-container.data-v-2a02c54e {
+  position: absolute;
+  bottom: 30px; /* 统一与其他按钮的位置 */
+  left: 50%;
+  transform: translateX(-50%);
+  z-index: 20;
+}
+.retry-button.data-v-2a02c54e {
+  padding: 10px 20px;
+  background-color: #ffffff; /* 白色背景 */
+  color: #333; /* 深色文字 */
+  font-size: 16px;
+  border: none;
+  border-radius: 20px; /* 圆角矩形 */
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
+  cursor: pointer;
+}
+.retry-button.data-v-2a02c54e:hover {
+  background-color: #2980b9;
+}
+
+/* 录制时长显示 */
+.recording-timer.data-v-2a02c54e {
+  position: absolute;
+  top: 20px;
+  left: 130px; /* 放在录制指示器旁边 */
+  background-color: rgba(0, 0, 0, 0.6);
+  padding: 5px 10px;
+  border-radius: 15px;
+  z-index: 20;
+  display: flex;
+  flex-direction: column;
+}
+.timer-text.data-v-2a02c54e {
+  color: #000;
+  font-size: 14px;
+  font-family: monospace; /* 使用等宽字体,使时间显示更稳定 */
+}
+.remaining-time.data-v-2a02c54e {
+  color: white;
+  font-size: 12px;
+  margin-top: 2px;
+}
+.remaining-time.warning.data-v-2a02c54e {
+  color: #ff6b6b; /* 剩余时间少时显示红色 */
+  animation: blink 1s infinite; /* 使用闪烁动画提醒用户 */
+}
+
+/* 添加上传状态指示器 */
+.upload-status-indicator.data-v-2a02c54e {
+  position: absolute;
+  top: 20px;
+  left: 130px; /* 放在录制指示器旁边 */
+  background-color: rgba(0, 0, 0, 0.6);
+  padding: 5px 10px;
+  border-radius: 15px;
+  z-index: 20;
+}
+.upload-status-content.data-v-2a02c54e {
+  display: flex;
+  align-items: center;
+}
+.upload-status-icon.data-v-2a02c54e {
+  width: 12px;
+  height: 12px;
+  border-radius: 50%;
+  margin-right: 8px;
+}
+.upload-status-text.data-v-2a02c54e {
+  color: white;
+  font-size: 14px;
+}
+.uploading.data-v-2a02c54e {
+  background-color: #e74c3c;
+}

+ 57 - 25
unpackage/dist/dev/mp-weixin/pages/interview/interview.js

@@ -46,8 +46,10 @@ const _sfc_main = {
         "请将右手背对准轮廓线",
         "请将右手拳头对准轮廓线"
       ],
-      photoLinks: [null, null, null, null, null, null]
+      photoLinks: [null, null, null, null, null, null],
       // 存储上传后的图片链接
+      cameraPosition: "front"
+      // 修改默认为前置摄像头
     };
   },
   computed: {
@@ -236,8 +238,8 @@ const _sfc_main = {
             duration: 2e3,
             success: () => {
               setTimeout(() => {
-                common_vendor.index.switchTab({
-                  url: "/pages/index/index"
+                common_vendor.index.navigateTo({
+                  url: "/pages/success/success"
                 });
               }, 2e3);
             }
@@ -254,7 +256,10 @@ const _sfc_main = {
     startCamera() {
       this.showCamera = true;
       if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
-        navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } }).then((stream) => {
+        const facingMode = this.cameraPosition === "front" ? "user" : "environment";
+        navigator.mediaDevices.getUserMedia({
+          video: { facingMode }
+        }).then((stream) => {
           this.mediaStream = stream;
           this.$nextTick(() => {
             const videoElement = this.$refs.videoElement;
@@ -280,37 +285,53 @@ const _sfc_main = {
     },
     // H5环境下拍照
     capturePhoto() {
+      console.log("执行H5拍照方法");
       const videoElement = this.$refs.videoElement;
       const canvasElement = this.$refs.canvasElement;
       if (videoElement && canvasElement) {
-        const context = canvasElement.getContext("2d");
-        canvasElement.width = videoElement.videoWidth;
-        canvasElement.height = videoElement.videoHeight;
-        context.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);
-        const photoData = canvasElement.toDataURL("image/jpeg");
-        this.$set(this.photos, this.currentStep, photoData);
-        this.uploadPhoto(photoData, this.photoTypes[this.currentStep]);
-        this.stopMediaStream();
-        this.showCamera = false;
+        try {
+          const context = canvasElement.getContext("2d");
+          canvasElement.width = videoElement.videoWidth;
+          canvasElement.height = videoElement.videoHeight;
+          context.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);
+          const photoData = canvasElement.toDataURL("image/jpeg");
+          this.$set(this.photos, this.currentStep, photoData);
+          this.uploadPhoto(photoData, this.photoTypes[this.currentStep]);
+          this.stopMediaStream();
+          this.showCamera = false;
+        } catch (err) {
+          console.error("H5拍照失败:", err);
+          common_vendor.index.showToast({
+            title: "拍照失败,请重试",
+            icon: "none"
+          });
+        }
       }
     },
     // 小程序环境下拍照
     capturePhotoMP() {
+      console.log("执行小程序拍照方法");
       if (!this.cameraContext) {
         console.error("相机上下文不存在");
         this.handleCameraInitError();
         return;
       }
       try {
+        common_vendor.index.showLoading({
+          title: "拍照中...",
+          mask: true
+        });
         this.cameraContext.takePhoto({
           quality: "high",
           success: (res) => {
             console.log("拍照成功:", res);
+            common_vendor.index.hideLoading();
             this.$set(this.photos, this.currentStep, res.tempImagePath);
             this.uploadPhoto(res.tempImagePath, this.photoTypes[this.currentStep]);
             this.showCamera = false;
           },
           fail: (err) => {
+            common_vendor.index.hideLoading();
             console.error("拍照失败:", err);
             common_vendor.index.showToast({
               title: "拍照失败,请重试",
@@ -320,6 +341,7 @@ const _sfc_main = {
           }
         });
       } catch (err) {
+        common_vendor.index.hideLoading();
         console.error("takePhoto方法执行失败:", err);
         this.fallbackToChooseImage();
       }
@@ -415,6 +437,14 @@ const _sfc_main = {
     },
     // 可以添加一个方法来切换蒙层显示
     toggleGestureOverlay() {
+    },
+    // 切换摄像头
+    switchCamera() {
+      this.cameraPosition = this.cameraPosition === "front" ? "back" : "front";
+      if (this.isH5 && this.showCamera) {
+        this.stopMediaStream();
+        this.startCamera();
+      }
     }
   }
 };
@@ -450,20 +480,22 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
     l: common_vendor.o((...args) => $options.nextStep && $options.nextStep(...args)),
     m: !$data.isH5 && $data.showCamera
   }, !$data.isH5 && $data.showCamera ? {
-    n: common_vendor.o((...args) => $options.handleCameraError && $options.handleCameraError(...args)),
-    o: $data.cameraMode,
-    p: $data.gestureOverlays[$data.currentStep],
-    q: common_vendor.t($data.gestureHints[$data.currentStep]),
-    r: common_vendor.o((...args) => $options.capturePhotoMP && $options.capturePhotoMP(...args)),
-    s: common_vendor.o((...args) => $options.cancelCamera && $options.cancelCamera(...args))
+    n: $data.cameraPosition,
+    o: common_vendor.o((...args) => $options.handleCameraError && $options.handleCameraError(...args)),
+    p: $data.cameraMode,
+    q: $data.gestureOverlays[$data.currentStep],
+    r: common_vendor.t($data.gestureHints[$data.currentStep]),
+    s: common_vendor.o((...args) => $options.capturePhotoMP && $options.capturePhotoMP(...args)),
+    t: common_vendor.o((...args) => $options.cancelCamera && $options.cancelCamera(...args))
   } : {}, {
-    t: $data.isH5
+    v: $data.isH5
   }, $data.isH5 ? {
-    v: $data.gestureOverlays[$data.currentStep],
-    w: common_vendor.t($data.gestureHints[$data.currentStep]),
-    x: common_vendor.o((...args) => $options.capturePhoto && $options.capturePhoto(...args)),
-    y: common_vendor.o((...args) => $options.cancelCamera && $options.cancelCamera(...args)),
-    z: $data.showCamera
+    w: $data.gestureOverlays[$data.currentStep],
+    x: common_vendor.t($data.gestureHints[$data.currentStep]),
+    y: common_vendor.o((...args) => $options.capturePhoto && $options.capturePhoto(...args)),
+    z: common_vendor.o((...args) => $options.switchCamera && $options.switchCamera(...args)),
+    A: common_vendor.o((...args) => $options.cancelCamera && $options.cancelCamera(...args)),
+    B: $data.showCamera
   } : {});
 }
 const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 1
unpackage/dist/dev/mp-weixin/pages/interview/interview.wxml


+ 37 - 0
unpackage/dist/dev/mp-weixin/pages/interview/interview.wxss

@@ -202,3 +202,40 @@
 	font-size: 28rpx;
 	text-align: center;
 }
+
+/* 添加切换摄像头按钮样式 */
+.switch-camera-btn {
+	width: 200rpx;
+	height: 80rpx;
+	line-height: 80rpx;
+	background-color: rgba(255, 255, 255, 0.3);
+	color: #ffffff;
+	text-align: center;
+	border-radius: 40rpx;
+	margin: 20rpx 0;
+}
+.camera-buttons {
+	display: flex;
+	flex-direction: row;
+	justify-content: center;
+	width: 100%;
+	margin-top: 20rpx;
+}
+.capture-btn {
+	width: 200rpx;
+	height: 200rpx;
+	border-radius: 50%;
+	background-color: #ffffff;
+	border: 10rpx solid #0039b3;
+	margin: 30rpx 0;
+}
+.switch-camera-btn, .cancel-btn {
+	width: 200rpx;
+	height: 80rpx;
+	line-height: 80rpx;
+	background-color: rgba(255, 255, 255, 0.3);
+	color: #ffffff;
+	text-align: center;
+	border-radius: 40rpx;
+	margin: 0 20rpx;
+}

+ 1 - 1
unpackage/dist/dev/mp-weixin/pages/login/login.wxml

@@ -1 +1 @@
-<view class="login-container"><image class="login-image" src="{{a}}" mode="aspectFit"></image><view class="login-title">登录才,开始面试</view><button class="login-btn" bindtap="{{b}}">身份验证并登录</button><view class="agreement"><checkbox checked="{{c}}" bindtap="{{d}}" color="#0039b3"/><text class="agreement-text"> 我已阅读并同意 <text class="agreement-link">《用户协议》</text> 和 <text class="agreement-link">《隐私政策》</text></text></view></view>
+<view class="login-container"><image class="login-image" src="{{a}}" mode="aspectFit"></image><view class="login-title">登录才,开始面试</view><button class="login-btn" bindtap="{{b}}">身份验证并登录</button><view class="agreement"><checkbox checked="{{c}}" bindtap="{{d}}" color="#0039b3"/><text class="agreement-text"> 我已阅读并同意 <text class="agreement-link">《用户协议》</text> 和 <text class="agreement-link">《隐私政策》</text></text></view></view>

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.