yangg 2 hónapja
szülő
commit
b871b36d7b

+ 3 - 4
pages/interview/interview.vue

@@ -365,10 +365,9 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 							success: () => {
 								// 延迟跳转,让用户看到成功提示
 								setTimeout(() => {
-									//uni.navigateBack();
-									// 或者跳转到下一个页面
-									uni.navigateTo({
-									    url: '/pages/ResumeEvaluation/ResumeEvaluation'
+									// 返回首页
+									uni.switchTab({
+										url: '/pages/index/index'
 									});
 								}, 2000);
 							}

+ 293 - 104
unpackage/dist/dev/mp-weixin/pages/identity-verify/identity-verify.js

@@ -105,8 +105,12 @@ const _sfc_main = {
       showStartRecordingButton: false,
       showRetryButton: false,
       // 控制重试按钮显示
-      lastVideoToRetry: null
+      lastVideoToRetry: null,
       // 存储上次失败的视频URL,用于重试
+      stopIOSRecording: null,
+      // 存储iOS特殊停止录制函数
+      cameraInitialized: false,
+      iosDevice: false
     };
   },
   mounted() {
@@ -126,38 +130,18 @@ const _sfc_main = {
     // 初始化相机
     async initCamera() {
       const systemInfo = common_vendor.index.getSystemInfoSync();
+      this.iosDevice = systemInfo.platform === "ios";
+      console.log("当前设备平台:", systemInfo.platform, "是否iOS:", this.iosDevice);
       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("相机");
-                }
-              });
-            }
+        await this.checkMiniProgramPermissions();
+        setTimeout(() => {
+          if (!this.cameraContext) {
+            this.cameraContext = common_vendor.index.createCameraContext();
+            console.log("相机上下文创建完成");
           }
-        });
+        }, 500);
       } else {
         try {
           const constraints = {
@@ -537,67 +521,129 @@ const _sfc_main = {
     },
     // 修改小程序环境下的录制方法
     startMiniProgramRecording() {
+      console.log("开始小程序录像,相机初始化状态:", this.cameraInitialized);
+      if (!this.cameraInitialized) {
+        common_vendor.index.showToast({
+          title: "相机正在初始化,请稍候",
+          icon: "none"
+        });
+        setTimeout(() => {
+          this.startMiniProgramRecording();
+        }, 1e3);
+        return;
+      }
       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();
+      const options = {
+        timeout: 6e4,
+        // 最长录制60秒
+        success: () => {
+          console.log("小程序录像开始成功");
+          this.isRecording = true;
+        },
+        fail: (err) => {
+          console.error("小程序录像开始失败:", err);
+          if (this.iosDevice && !this.hasRetriedIOS) {
+            this.hasRetriedIOS = true;
+            console.log("iOS设备录制失败,尝试使用备用方法");
+            setTimeout(() => {
+              this.cameraContext = common_vendor.index.createCameraContext();
+              this.cameraContext.startRecord({
+                success: () => {
+                  console.log("iOS备用方法录像开始成功");
+                  this.isRecording = true;
+                },
+                fail: (retryErr) => {
+                  console.error("iOS备用方法录像失败:", retryErr);
+                  common_vendor.index.showToast({
+                    title: "录制失败,请检查相机权限",
+                    icon: "none"
+                  });
+                  this.proceedToNextQuestion();
+                }
+              });
+            }, 500);
             return;
           }
-          const options = {
-            timeout: 6e4,
-            // 最长录制60秒
-            quality: "medium",
-            // 可选值:'low', 'medium', 'high'
-            compressed: true,
-            // 是否压缩视频
-            success: () => {
-              console.log("小程序录像开始成功");
-            },
-            fail: (err) => {
-              console.error("小程序录像开始失败:", err);
-              common_vendor.index.showToast({
-                title: "录制失败,请检查相机权限",
-                icon: "none"
-              });
-              this.proceedToNextQuestion();
-            },
-            timeoutCallback: () => {
-              console.log("录制超时自动停止");
-              this.stopRecordingAnswer();
-            }
-          };
-          this.recorder = this.cameraContext.startRecord(options);
+          common_vendor.index.showToast({
+            title: "录制失败,请检查相机权限",
+            icon: "none"
+          });
+          this.proceedToNextQuestion();
         }
-      });
+      };
+      if (this.iosDevice) {
+        delete options.timeout;
+        console.log("使用iOS专用录制参数");
+      } else {
+        options.quality = "medium";
+        options.compressed = true;
+        options.timeoutCallback = () => {
+          console.log("录制超时自动停止");
+          this.stopRecordingAnswer();
+        };
+      }
+      console.log("调用startRecord,参数:", JSON.stringify(options));
+      this.recorder = this.cameraContext.startRecord(options);
     },
     // 添加新方法:请求小程序权限
-    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("相机");
+    async checkMiniProgramPermissions() {
+      return new Promise((resolve) => {
+        common_vendor.index.getSetting({
+          success: (res) => {
+            const hasRecordAuth = res.authSetting["scope.record"];
+            const hasCameraAuth = res.authSetting["scope.camera"];
+            const authPromises = [];
+            if (!hasRecordAuth) {
+              authPromises.push(
+                new Promise((resolveAuth) => {
+                  common_vendor.index.authorize({
+                    scope: "scope.record",
+                    success: () => {
+                      console.log("录音权限已获取");
+                      resolveAuth();
+                    },
+                    fail: (err) => {
+                      console.error("录音权限获取失败:", err);
+                      this.showPermissionDialog("录音");
+                      resolveAuth();
+                    }
+                  });
+                })
+              );
             }
-          });
-        },
-        fail: (err) => {
-          console.error("录音权限获取失败:", err);
-          this.showPermissionDialog("录音");
-        }
+            if (!hasCameraAuth) {
+              authPromises.push(
+                new Promise((resolveAuth) => {
+                  common_vendor.index.authorize({
+                    scope: "scope.camera",
+                    success: () => {
+                      console.log("相机权限已获取");
+                      resolveAuth();
+                    },
+                    fail: (err) => {
+                      console.error("相机权限获取失败:", err);
+                      this.showPermissionDialog("相机");
+                      resolveAuth();
+                    }
+                  });
+                })
+              );
+            }
+            if (authPromises.length > 0) {
+              Promise.all(authPromises).then(() => {
+                resolve();
+              });
+            } else {
+              resolve();
+            }
+          },
+          fail: () => {
+            resolve();
+          }
+        });
       });
     },
     // 修改浏览器环境下的录制方法
@@ -612,6 +658,8 @@ const _sfc_main = {
         return;
       }
       try {
+        const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
+        console.log("当前设备是否为iOS:", isIOS);
         const hasAudio = this.cameraStream.getAudioTracks().length > 0;
         if (!hasAudio) {
           console.warn("警告:媒体流中没有音频轨道,尝试重新获取带音频的媒体流");
@@ -639,18 +687,18 @@ const _sfc_main = {
                 videoElement.srcObject = combinedStream;
                 videoElement.muted = true;
               }
-              this.setupMediaRecorder(combinedStream);
+              this.setupMediaRecorder(combinedStream, isIOS);
             } else {
               console.warn("仍然无法获取音频轨道");
-              this.setupMediaRecorder(this.cameraStream);
+              this.setupMediaRecorder(this.cameraStream, isIOS);
             }
           }).catch((err) => {
             console.error("获取音频失败:", err);
-            this.setupMediaRecorder(this.cameraStream);
+            this.setupMediaRecorder(this.cameraStream, isIOS);
           });
         } else {
           console.log("检测到音频轨道,直接使用");
-          this.setupMediaRecorder(this.cameraStream);
+          this.setupMediaRecorder(this.cameraStream, isIOS);
         }
       } catch (error) {
         console.error("浏览器录制失败:", error);
@@ -661,11 +709,16 @@ const _sfc_main = {
         this.proceedToNextQuestion();
       }
     },
-    // 修改 setupMediaRecorder 方法
-    setupMediaRecorder(stream) {
+    // 修改 setupMediaRecorder 方法,添加 isIOS 参数
+    setupMediaRecorder(stream, isIOS) {
       const videoTracks = stream.getVideoTracks();
       const audioTracks = stream.getAudioTracks();
       console.log("设置MediaRecorder - 视频轨道:", videoTracks.length, "音频轨道:", audioTracks.length);
+      if (isIOS) {
+        console.log("检测到iOS设备,使用替代录制方法");
+        this.setupIOSRecording(stream);
+        return;
+      }
       let mimeType = "";
       const supportedTypes = [
         "video/webm;codecs=vp9,opus",
@@ -745,6 +798,109 @@ const _sfc_main = {
         console.error("开始录制失败:", e);
       }
     },
+    // 添加新方法:为iOS设备设置特殊录制方法
+    setupIOSRecording(stream) {
+      console.log("设置iOS特殊录制方法");
+      const videoElement = this.$refs.userCameraVideo;
+      const canvas = document.createElement("canvas");
+      const ctx = canvas.getContext("2d");
+      canvas.width = 640;
+      canvas.height = 480;
+      this.recordedFrames = [];
+      this.recordingStartTime = Date.now();
+      this.frameCount = 0;
+      const audioTracks = stream.getAudioTracks();
+      let audioRecorder = null;
+      let audioChunks = [];
+      if (audioTracks.length > 0) {
+        const audioStream = new MediaStream([audioTracks[0]]);
+        try {
+          audioRecorder = new MediaRecorder(audioStream);
+          audioRecorder.ondataavailable = (e) => {
+            if (e.data.size > 0) {
+              audioChunks.push(e.data);
+            }
+          };
+          audioRecorder.start(1e3);
+        } catch (e) {
+          console.warn("iOS音频录制器创建失败:", e);
+        }
+      }
+      const captureFrame = () => {
+        if (!this.isRecording)
+          return;
+        try {
+          ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
+          const imageData = canvas.toDataURL("image/jpeg", 0.8);
+          this.recordedFrames.push({
+            image: imageData,
+            timestamp: Date.now() - this.recordingStartTime
+          });
+          this.frameCount++;
+          if (this.frameCount % 15 === 0) {
+            console.log(`已捕获 ${this.frameCount} 帧`);
+          }
+          this.frameCapturingId = requestAnimationFrame(captureFrame);
+        } catch (e) {
+          console.error("捕获视频帧失败:", e);
+        }
+      };
+      this.frameCapturingId = requestAnimationFrame(captureFrame);
+      this.stopIOSRecording = async () => {
+        console.log("停止iOS录制,已捕获帧数:", this.recordedFrames.length);
+        if (this.frameCapturingId) {
+          cancelAnimationFrame(this.frameCapturingId);
+          this.frameCapturingId = null;
+        }
+        if (audioRecorder && audioRecorder.state !== "inactive") {
+          audioRecorder.stop();
+        }
+        common_vendor.index.showLoading({
+          title: "正在处理视频...",
+          mask: true
+        });
+        try {
+          if (audioRecorder) {
+            await new Promise((resolve) => {
+              audioRecorder.onstop = resolve;
+              if (audioRecorder.state === "inactive")
+                resolve();
+            });
+          }
+          const videoBlob = await this.createVideoFromFrames(this.recordedFrames, audioChunks);
+          const fileName = `answer_${this.currentVideoIndex}_${Date.now()}.mp4`;
+          const file = new File([videoBlob], fileName, { type: "video/mp4" });
+          common_vendor.index.hideLoading();
+          this.uploadRecordedVideo(file);
+        } catch (error) {
+          console.error("iOS视频处理失败:", error);
+          common_vendor.index.hideLoading();
+          common_vendor.index.showToast({
+            title: "视频处理失败",
+            icon: "none"
+          });
+          this.proceedToNextQuestion();
+        }
+      };
+    },
+    // 添加新方法:从帧创建视频
+    async createVideoFromFrames(frames, audioChunks) {
+      console.log("从帧创建视频,帧数:", frames.length);
+      const videoData = {
+        frames: frames.slice(0, Math.min(frames.length, 300)),
+        // 限制帧数以减小大小
+        audioBlob: audioChunks.length > 0 ? await new Blob(audioChunks, { type: "audio/webm" }).arrayBuffer() : null,
+        metadata: {
+          duration: frames.length > 0 ? frames[frames.length - 1].timestamp : 0,
+          frameCount: frames.length,
+          hasAudio: audioChunks.length > 0,
+          deviceInfo: navigator.userAgent,
+          timestamp: Date.now()
+        }
+      };
+      const jsonString = JSON.stringify(videoData);
+      return new Blob([jsonString], { type: "application/json" });
+    },
     // 添加新方法:停止录制用户回答
     stopRecordingAnswer() {
       console.log("停止录制用户回答");
@@ -755,28 +911,48 @@ const _sfc_main = {
       }
       common_vendor.index.hideLoading();
       this.showStopRecordingButton = false;
+      const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
       const systemInfo = common_vendor.index.getSystemInfoSync();
       const isMiniProgram = systemInfo.uniPlatform && systemInfo.uniPlatform.startsWith("mp-");
       if (isMiniProgram) {
         this.stopMiniProgramRecording();
+      } else if (isIOS && this.stopIOSRecording) {
+        this.stopIOSRecording();
       } else {
         this.stopBrowserRecording();
       }
     },
     // 添加新方法:停止小程序录制
     stopMiniProgramRecording() {
+      console.log("停止小程序录像");
       if (!this.cameraContext) {
         console.error("相机上下文不存在");
         this.proceedToNextQuestion();
         return;
       }
+      common_vendor.index.showLoading({
+        title: "正在处理视频...",
+        mask: true
+      });
       this.cameraContext.stopRecord({
         success: (res) => {
           console.log("小程序录像停止成功:", res);
-          const tempFilePath = res.tempVideoPath;
-          this.uploadRecordedVideo(tempFilePath);
+          common_vendor.index.hideLoading();
+          const tempFilePath = res.tempVideoPath || res.tempThumbPath || res.tempFilePath;
+          if (tempFilePath) {
+            console.log("获取到视频文件路径:", tempFilePath);
+            this.uploadRecordedVideo(tempFilePath);
+          } else {
+            console.error("未获取到视频文件路径");
+            common_vendor.index.showToast({
+              title: "录制失败,未获取到视频文件",
+              icon: "none"
+            });
+            this.proceedToNextQuestion();
+          }
         },
         fail: (err) => {
+          common_vendor.index.hideLoading();
           console.error("小程序录像停止失败:", err);
           common_vendor.index.showToast({
             title: "录制失败",
@@ -799,6 +975,7 @@ const _sfc_main = {
     // 修改上传录制的视频方法
     uploadRecordedVideo(fileOrPath) {
       console.log("准备上传视频:", typeof fileOrPath === "string" ? fileOrPath : fileOrPath.name);
+      const isIOSSpecialFormat = typeof fileOrPath !== "string" && fileOrPath.type === "application/json";
       common_vendor.index.showLoading({
         title: "正在上传...",
         mask: true
@@ -832,6 +1009,9 @@ const _sfc_main = {
         formData.append("question_id", questionId);
         formData.append("video_duration", 0);
         formData.append("has_audio", "true");
+        if (isIOSSpecialFormat) {
+          formData.append("is_ios_format", "true");
+        }
         const xhr = new XMLHttpRequest();
         xhr.open("POST", `${common_config.apiBaseUrl}/api/system/upload/`, true);
         xhr.timeout = 12e4;
@@ -1327,6 +1507,14 @@ const _sfc_main = {
           videoElement.play();
         };
       });
+    },
+    // 添加相机初始化完成的处理方法
+    handleCameraInit() {
+      console.log("相机初始化完成");
+      this.cameraInitialized = true;
+      if (!this.cameraContext && this.useMiniProgramCameraComponent) {
+        this.cameraContext = common_vendor.index.createCameraContext();
+      }
     }
   }
 };
@@ -1346,21 +1534,22 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
   } : {}, {
     i: $data.useMiniProgramCameraComponent
   }, $data.useMiniProgramCameraComponent ? {
-    j: common_vendor.o((...args) => $options.handleCameraError && $options.handleCameraError(...args))
+    j: common_vendor.o((...args) => $options.handleCameraError && $options.handleCameraError(...args)),
+    k: common_vendor.o((...args) => $options.handleCameraInit && $options.handleCameraInit(...args))
   } : {}, {
-    k: $data.loading
+    l: $data.loading
   }, $data.loading ? {} : {}, {
-    l: $data.showDebugInfo
+    m: $data.showDebugInfo
   }, $data.showDebugInfo ? common_vendor.e({
-    m: $data.assistantResponse
+    n: $data.assistantResponse
   }, $data.assistantResponse ? {
-    n: common_vendor.t($data.assistantResponse)
+    o: common_vendor.t($data.assistantResponse)
   } : {}, {
-    o: $data.audioTranscript
+    p: $data.audioTranscript
   }, $data.audioTranscript ? {
-    p: common_vendor.t($data.audioTranscript)
+    q: common_vendor.t($data.audioTranscript)
   } : {}, {
-    q: common_vendor.f($data.processedResponses, (item, index, i0) => {
+    r: common_vendor.f($data.processedResponses, (item, index, i0) => {
       return common_vendor.e({
         a: item.role
       }, item.role ? {
@@ -1374,19 +1563,19 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
       });
     })
   }) : {}, {
-    r: $data.showStopRecordingButton
+    s: $data.showStopRecordingButton
   }, $data.showStopRecordingButton ? {
-    s: common_vendor.o((...args) => $options.stopRecordingAnswer && $options.stopRecordingAnswer(...args))
+    t: common_vendor.o((...args) => $options.stopRecordingAnswer && $options.stopRecordingAnswer(...args))
   } : {}, {
-    t: $data.isRecording
+    v: $data.isRecording
   }, $data.isRecording ? {} : {}, {
-    v: $data.showStartRecordingButton
+    w: $data.showStartRecordingButton
   }, $data.showStartRecordingButton ? {
-    w: common_vendor.o((...args) => $options.handleStartRecordingClick && $options.handleStartRecordingClick(...args))
+    x: common_vendor.o((...args) => $options.handleStartRecordingClick && $options.handleStartRecordingClick(...args))
   } : {}, {
-    x: $data.showRetryButton
+    y: $data.showRetryButton
   }, $data.showRetryButton ? {
-    y: common_vendor.o((...args) => $options.retryVideoUpload && $options.retryVideoUpload(...args))
+    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-464e78c6"]]);

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
unpackage/dist/dev/mp-weixin/pages/identity-verify/identity-verify.wxml


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

@@ -236,8 +236,8 @@ const _sfc_main = {
             duration: 2e3,
             success: () => {
               setTimeout(() => {
-                common_vendor.index.navigateTo({
-                  url: "/pages/ResumeEvaluation/ResumeEvaluation"
+                common_vendor.index.switchTab({
+                  url: "/pages/index/index"
                 });
               }, 2e3);
             }

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott