Răsfoiți Sursa

修改内容

yangg 2 zile în urmă
părinte
comite
56cad70460

+ 1 - 1
api/user.js

@@ -98,7 +98,7 @@ export const getJobList = (params = {}) => {
   // 设置默认参数
   const defaultParams = {
     page: 1,
-    pageSize: 10,
+    limit: 50,
     searchTerms: '',
     status: '',
     tenant_id: 1

+ 24 - 48
pages/camera/camera.vue

@@ -243,11 +243,11 @@
     // 移除截屏
     this.cleanupPersonDetectionWebSocket();
   },
-		methods: {
+  methods: {
 			initPersonDetectionWebSocket() {
-				if (this.personDetectionSocket) {
-					this.cleanupPersonDetectionWebSocket();
-				}
+      if (this.personDetectionSocket) {
+        this.cleanupPersonDetectionWebSocket();
+      }
 
       try {
         this.personDetectionSocket = uni.connectSocket({
@@ -325,7 +325,7 @@
             }
           });
         }
-      }, 5000);
+      }, 3000);
     },
 
     cleanupPersonDetectionWebSocket() {
@@ -572,7 +572,7 @@
 								} else {
 									// 如果是最后一题,显示结束模态框或跳转
 									uni.navigateTo({
-										url: '/pages/posture-guide/posture-guide'//'/pages/interview-question/interview-question'
+										url: '/pages/posture-guide/posture-guide'
 									});
 								}
 							}, 1500);
@@ -602,10 +602,8 @@
 					return;
 				}
 
-				// 如果已经显示结果,重置结果状态
-				if (this.showResult) {
-					this.showResult = false;
-				}
+				// 如果已经显示结果,不允许再次选择
+				if (this.showResult) return;
 				
 				console.log('selectOption', index);
 				// 判断当前题目类型
@@ -629,23 +627,12 @@
 						
 						this.submitCurrentAnswer().then(() => {
 							setTimeout(() => {
-								// 如果是在回答上一题,完成后要回到当前题目
-								if (this.currentQuestionIndex < this.answers.length - 1) {
-									this.currentQuestionIndex++;
-									this.currentScrollId = 'question-' + this.currentQuestionIndex;
-								} else {
-									this.goToNextQuestion();
-								}
+								this.goToNextQuestion();
 							}, 800);
 						}).catch(error => {
 							console.error('提交答案失败:', error);
 							setTimeout(() => {
-								if (this.currentQuestionIndex < this.answers.length - 1) {
-									this.currentQuestionIndex++;
-									this.currentScrollId = 'question-' + this.currentQuestionIndex;
-								} else {
-									this.goToNextQuestion();
-								}
+								this.goToNextQuestion();
 							}, 1000);
 						});
 					}, 500);
@@ -673,23 +660,12 @@
 						
 						this.submitCurrentAnswer().then(() => {
 							setTimeout(() => {
-								// 如果是在回答上一题,完成后要回到当前题目
-								if (this.currentQuestionIndex < this.answers.length - 1) {
-									this.currentQuestionIndex++;
-									this.currentScrollId = 'question-' + this.currentQuestionIndex;
-								} else {
-									this.goToNextQuestion();
-								}
+								this.goToNextQuestion();
 							}, 800);
 						}).catch(error => {
 							console.error('提交答案失败:', error);
 							setTimeout(() => {
-								if (this.currentQuestionIndex < this.answers.length - 1) {
-									this.currentQuestionIndex++;
-									this.currentScrollId = 'question-' + this.currentQuestionIndex;
-								} else {
-									this.goToNextQuestion();
-								}
+								this.goToNextQuestion();
 							}, 1000);
 						});
 					}, 500);
@@ -776,7 +752,7 @@
 							} else {
 								// 如果是最后一组的最后一题,跳转到面试页面
 								uni.navigateTo({
-									url:  '/pages/posture-guide/posture-guide'//'/pages/interview-question/interview-question'
+									url: '/pages/posture-guide/posture-guide'
 								});
 							}
 						} else {
@@ -800,7 +776,7 @@
 							} else {
 								// 如果是最后一组的最后一题,跳转到面试页面
 								uni.navigateTo({
-									url: '/pages/posture-guide/posture-guide'//'/pages/interview-question/interview-question'
+									url: '/pages/posture-guide/posture-guide'
 								});
 							}
 						} else {
@@ -987,13 +963,13 @@
 
 			// 修改 handleOptionClick 方法
 			handleOptionClick(qIndex, index) {
-				// 如果点击的是已完成的题目,但不是上一题
-				if (qIndex < this.currentQuestionIndex - 1) {
-					/* uni.showToast({
-						title: '只能修改上一题的答案',
+				// 如果点击的是已完成的题目
+				if (qIndex < this.currentQuestionIndex-1) {
+					uni.showToast({
+						title: '该题目已完成,无法修改',
 						icon: 'none',
 						duration: 2000
-					}); */
+					});
 					return;
 				}
 				
@@ -1044,7 +1020,7 @@
 						this.selectedOptions = [];
 					}
 					
-					// 调用选择逻辑
+					// 如果是当前题目,调用选择逻辑
 					this.selectOption(index);
 					
 					// 设置延时,防止快速重复点击
@@ -1096,7 +1072,7 @@
 				} else {
 					// 所有组都完成了,跳转到下一个页面
 					uni.navigateTo({
-						url:  '/pages/posture-guide/posture-guide'//'/pages/interview-question/interview-question'
+						url: '/pages/posture-guide/posture-guide'
 					});
 				}
 			},
@@ -1183,9 +1159,9 @@
 					this.isSubmitting = true;
 					
 					// 显示提交中提示
-					// uni.showLoading({
-					// 	title: '正在提交答案...'
-					// });
+				/* 	uni.showLoading({
+						title: '正在提交答案...'
+					}); */
 					
 					// 构建提交数据
 					let answerContent = '';

+ 590 - 36
pages/components/voice-check-modal.vue

@@ -6,8 +6,13 @@
       
       <!-- 语音波形动画 -->
       <view class="wave-container" v-if="isRecording">
-        <view class="wave-bar" v-for="(item, index) in waveData" :key="index" 
-              :style="{ height: item + 'rpx' }"></view>
+        <view 
+          class="wave-bar" 
+          v-for="(item, index) in waveData" 
+          :key="index" 
+          :style="{ height: item + 'rpx' }"
+        ></view>
+       <!--  <view class="timer">{{ formatTime(recordingTime) }}</view> -->
       </view>
       
       <!-- 待朗读文字 -->
@@ -15,8 +20,12 @@
       
       <!-- 按钮 -->
       <button class="confirm-btn" @tap="handleConfirm">
-        {{ isRecording ? '读完了' : '开始' }}
+        {{ isRecording ? '停止录制' : '开始录制' }}
       </button>
+      
+      <view class="status-message">
+        {{ statusMessage }}
+      </view>
     </view>
   </view>
 </template>
@@ -35,48 +44,546 @@ export default {
   data() {
     return {
       isRecording: false,
-      waveData: Array(20).fill(20), // 初始波形数据
-      waveTimer: null
+      waveData: Array(30).fill(20),
+      recordingTime: 0,
+      statusMessage: '点击"开始录制"按钮朗读文字',
+      silenceCounter: 0,
+      recordingTimer: null,
+      volumeLevel: 0,
+      animationTimer: null,
+      lastVolume: 0,
+      hasPermission: false, // 添加录音权限状态
+      noSoundTimeout: null, // 添加无声音超时计时器
+      recordingStarted: false, // 添加录音开始标志
+      audioPath: null, // 添加录音文件路径
+      platform: '', // 添加平台标识
+      isRetrying: false // 添加重试标记
     }
   },
   methods: {
-    // 开始录音
-    startRecording() {
+    // 检查录音权限 - 跨平台兼容版本
+    async checkPermission() {
+      // 获取系统信息
+      const systemInfo = uni.getSystemInfoSync();
+      const platform = systemInfo.platform;
+      
+      try {
+        // iOS平台处理
+        if (platform === 'ios') {
+          // iOS使用getSystemSetting检查权限状态
+          const res = await new Promise((resolve, reject) => {
+            uni.getSetting({
+              success: (result) => {
+                if (result.authSetting['scope.record']) {
+                  resolve(true);
+                } else {
+                  // 请求权限
+                  uni.authorize({
+                    scope: 'scope.record',
+                    success: () => resolve(true),
+                    fail: (err) => reject(err)
+                  });
+                }
+              },
+              fail: (err) => reject(err)
+            });
+          });
+          this.hasPermission = true;
+          return true;
+        } 
+        // 安卓和鸿蒙平台处理
+        else {
+          const res = await uni.authorize({
+            scope: 'scope.record'
+          });
+          this.hasPermission = true;
+          return true;
+        }
+      } catch (error) {
+        console.error('权限请求失败:', error);
+        uni.showModal({
+          title: '提示',
+          content: '需要录音权限才能进行录音,请在设置中开启录音权限',
+          confirmText: '去设置',
+          success: (res) => {
+            if (res.confirm) {
+              uni.openSetting();
+            }
+          }
+        });
+        this.hasPermission = false;
+        return false;
+      }
+    },
+
+    async startRecording() {
+      // 检查权限
+      if (!await this.checkPermission()) {
+        return;
+      }
+
       this.isRecording = true;
-      recorderManager.start({
-        duration: 10000,
-        sampleRate: 16000,
-        numberOfChannels: 1,
-        encodeBitRate: 48000,
-        format: 'wav'
-      });
+      this.recordingTime = 0;
+      this.silenceCounter = 0;
+      this.statusMessage = '正在检测声音...';
+      this.waveData = this.waveData.map(() => 20);
+      this.lastVolume = 0;
+      this.recordingStarted = true;
+      
+      // 获取系统信息以适配不同平台
+      const systemInfo = uni.getSystemInfoSync();
+      const platform = systemInfo.platform;
+      
+      // 根据不同平台配置录音参数
+      const recorderOptions = {
+        duration: 60000, // 最长录音时间
+        format: 'mp3', // 使用mp3格式提高兼容性
+        frameSize: 5
+      };
+      
+      // iOS平台特殊处理
+      if (platform === 'ios') {
+        // iOS推荐参数
+        recorderOptions.sampleRate = 44100;
+        recorderOptions.numberOfChannels = 1;
+        recorderOptions.encodeBitRate = 96000;
+      } else {
+        // 安卓和鸿蒙平台参数
+        recorderOptions.sampleRate = 16000;
+        recorderOptions.numberOfChannels = 1;
+        recorderOptions.encodeBitRate = 48000;
+      }
+      
+      // 开始录音
+      recorderManager.start(recorderOptions);
       
-      // 模拟波形动画
-      this.waveTimer = setInterval(() => {
-        this.waveData = this.waveData.map(() => Math.random() * 40 + 20);
-      }, 100);
+      // 计时器
+      this.recordingTimer = setInterval(() => {
+        this.recordingTime++;
+      }, 1000);
+
+      // 动画定时器
+      this.animationTimer = setInterval(() => {
+        if (this.isRecording) {
+          this.updateWaveform();
+        }
+      }, 50);
+
+      // 设置无声音检测超时
+      this.noSoundTimeout = setTimeout(() => {
+        if (this.silenceCounter > 20) {
+          this.showNoSoundTip();
+        }
+      }, 3000);
     },
     
-    // 停止录音
     stopRecording() {
       this.isRecording = false;
+      this.recordingStarted = false;
+      recorderManager.stop();
+      clearInterval(this.recordingTimer);
+      clearInterval(this.animationTimer);
+      clearTimeout(this.noSoundTimeout);
+      this.statusMessage = '录制完成!';
+      
+      setTimeout(() => {
+        this.waveData = Array(30).fill(20);
+      }, 500);
+
+      // 延迟触发完成事件,确保audioPath已经设置
+      setTimeout(() => {
+        // 处理音频文件
+        this.processAudioFile();
+        
+        // 触发完成事件
+        this.$emit('complete', {
+          success: true,
+          audioPath: this.audioPath,
+          platform: this.platform
+        });
+      }, 300);
+    },
+    
+    // 添加音频文件处理方法
+    processAudioFile() {
+      if (!this.audioPath) return;
+      
+      // 获取系统信息
+      const systemInfo = uni.getSystemInfoSync();
+      this.platform = systemInfo.platform;
+      
+      console.log('处理音频文件:', this.audioPath, '平台:', this.platform);
+      
+      // 针对不同平台的音频文件处理
+      if (this.platform === 'ios') {
+        // iOS平台特殊处理
+        if (!this.audioPath.startsWith('file://')) {
+          this.audioPath = 'file://' + this.audioPath;
+        }
+      } else if (this.platform === 'android') {
+        // 安卓平台特殊处理
+        // 通常不需要特殊处理,但如果有特殊需求可以在这里添加
+      } else if (this.platform === 'harmony') {
+        // 鸿蒙平台特殊处理
+        // 根据鸿蒙系统的特性进行处理
+      }
+      
+      // 验证音频文件是否存在
+      uni.getFileInfo({
+        filePath: this.audioPath,
+        success: (res) => {
+          console.log('音频文件信息:', res);
+          if (res.size === 0) {
+            console.warn('警告: 音频文件大小为0');
+            // 可以选择重新录制或提示用户
+          }
+        },
+        fail: (err) => {
+          console.error('获取音频文件信息失败:', err);
+          // 处理文件不存在的情况
+        }
+      });
+    },
+
+    // 显示无声音提示
+    showNoSoundTip() {
+      if (!this.isRecording) return;
+      
+      uni.showModal({
+        title: '未检测到声音',
+        content: '请检查以下问题:\n1. 麦克风是否正常工作\n2. 是否允许使用麦克风\n3. 是否靠近麦克风说话\n4. 说话音量是否足够',
+        showCancel: true,
+        cancelText: '重新录制',
+        confirmText: '继续录制',
+        success: (res) => {
+          if (res.cancel) {
+            // 重新录制:停止当前录音并重新开始
+            this.restartRecording();
+          } else {
+            // 继续录制:重置计数器继续当前录音
+            this.continueRecording();
+          }
+        }
+      });
+    },
+
+    // 重新录制方法
+    restartRecording() {
+      // 先停止当前录音
       recorderManager.stop();
-      clearInterval(this.waveTimer);
-      this.waveData = Array(20).fill(20);
+      clearInterval(this.recordingTimer);
+      clearInterval(this.animationTimer);
+      clearTimeout(this.noSoundTimeout);
+      
+      // 重置所有状态
+      this.isRecording = false;
+      this.recordingStarted = false;
+      this.recordingTime = 0;
+      this.silenceCounter = 0;
+      this.volumeLevel = 0;
+      this.lastVolume = 0;
+      this.waveData = Array(30).fill(20);
+      
+      // 短暂延迟后开始新的录音
+      setTimeout(() => {
+        this.startRecording();
+      }, 500);
+    },
+
+    // 继续录制方法
+    continueRecording() {
+      // 重置计数器和状态,但保持录音继续
+      this.silenceCounter = 0;
+      this.statusMessage = '正在检测声音...';
+      
+      // 重新设置无声音检测超时
+      clearTimeout(this.noSoundTimeout);
+      this.noSoundTimeout = setTimeout(() => {
+        if (this.silenceCounter > 20) {
+          this.showNoSoundTip();
+        }
+      }, 3000);
+    },
+
+    detectAudio(int16Array) {
+      // 获取系统信息
+      const systemInfo = uni.getSystemInfoSync();
+      const platform = systemInfo.platform;
+      
+      // 计算平均音量 - 优化算法以提高准确性
+      let sum = 0;
+      let peak = 0;
+      
+      // 使用分块处理以提高性能
+      const blockSize = 128;
+      for (let i = 0; i < int16Array.length; i += blockSize) {
+        const end = Math.min(i + blockSize, int16Array.length);
+        for (let j = i; j < end; j++) {
+          const absValue = Math.abs(int16Array[j]);
+          sum += absValue;
+          peak = Math.max(peak, absValue);
+        }
+      }
+      
+      const avg = sum / int16Array.length;
+      
+      // 根据不同平台调整阈值
+      let SPEAK_THRESHOLD = 500; // 默认阈值
+      let volumeScale = 2000;
+      
+      if (platform === 'ios') {
+        SPEAK_THRESHOLD = 300; // iOS通常需要更低的阈值
+        volumeScale = 1500;
+      } else if (platform === 'android') {
+        SPEAK_THRESHOLD = 500; // 安卓标准阈值
+        volumeScale = 2000;
+      } else {
+        // 鸿蒙或其他平台
+        SPEAK_THRESHOLD = 400;
+        volumeScale = 1800;
+      }
+      
+      // 使用峰值和平均值的组合来计算音量级别
+      const combinedLevel = (avg * 0.7 + peak * 0.3) / volumeScale;
+      this.volumeLevel = this.smoothVolume(combinedLevel);
+      
+      // 更新状态
+      if (avg > SPEAK_THRESHOLD || peak > SPEAK_THRESHOLD * 3) {
+        this.statusMessage = '检测到声音:录音中...';
+        this.silenceCounter = 0;
+        clearTimeout(this.noSoundTimeout); // 清除无声音超时
+        
+        // 立即更新波形以反映实际声音
+        this.updateWaveform(false);
+      } else {
+        this.silenceCounter++;
+        if (this.silenceCounter > 10) {
+          this.statusMessage = '未检测到声音,请靠近麦克风并说话...';
+          // 如果持续30次都没有声音,显示提示
+          if (this.silenceCounter === 30 && this.isRecording) {
+            this.showNoSoundTip();
+          }
+          
+          // 静音时逐渐降低波形
+          this.updateWaveform(true);
+        }
+      }
+    },
+
+    // 添加音量平滑处理函数
+    smoothVolume(newVolume) {
+      const smoothFactor = 0.3; // 平滑系数
+      const smoothedVolume = this.lastVolume + (newVolume - this.lastVolume) * smoothFactor;
+      this.lastVolume = smoothedVolume;
+      return Math.min(1, Math.max(0, smoothedVolume));
     },
     
-    // 处理确认按钮点击
+    updateWaveform(isSilent = false) {
+      // 获取系统信息以优化动画性能
+      const systemInfo = uni.getSystemInfoSync();
+      const platform = systemInfo.platform;
+      
+      // 根据平台调整动画参数
+      let decayFactor = 0.95; // 静音时波形衰减因子
+      let animationScale = 1.0; // 动画缩放因子
+      
+      if (platform === 'ios') {
+        // iOS上动画通常需要更平滑
+        decayFactor = 0.97;
+        animationScale = 0.9;
+      } else if (platform === 'android') {
+        // 安卓标准参数
+        decayFactor = 0.95;
+        animationScale = 1.0;
+      } else {
+        // 鸿蒙或其他平台
+        decayFactor = 0.96;
+        animationScale = 0.95;
+      }
+      
+      if (isSilent) {
+        // 静音时波形逐渐降低
+        this.waveData = this.waveData.map(height => Math.max(20, height * decayFactor));
+      } else {
+        const now = Date.now() * 0.002;
+        
+        // 使用当前音量级别动态调整波形
+        const volumeEffect = this.volumeLevel * animationScale;
+        const baseHeight = 20 + volumeEffect * 60;
+        const waveAmplitude = volumeEffect * 40;
+        
+        // 更新波形数据
+        this.waveData = this.waveData.map((currentHeight, i) => {
+          // 使用余弦和正弦的组合来创建更流畅的波形
+          const phase = i * 0.2 + now;
+          const wave1 = Math.cos(phase) * 0.5;
+          const wave2 = Math.sin(phase * 1.5) * 0.3;
+          
+          // 添加随机因素使波形更自然
+          const randomFactor = Math.random() * 0.1;
+          const combinedWave = (wave1 + wave2 + randomFactor) * volumeEffect;
+          
+          // 计算目标高度
+          const targetHeight = baseHeight + combinedWave * waveAmplitude;
+          
+          // 平滑过渡到新高度 (LERP - 线性插值)
+          const smoothFactor = platform === 'ios' ? 0.3 : 0.4; // iOS上更平滑
+          const newHeight = currentHeight + (targetHeight - currentHeight) * smoothFactor;
+          
+          // 确保高度在合理范围内
+          return Math.max(20, Math.min(120, newHeight));
+        });
+      }
+    },
+
     handleConfirm() {
       if (!this.isRecording) {
         this.startRecording();
       } else {
         this.stopRecording();
-        this.$emit('complete');
       }
+    },
+    
+    formatTime(seconds) {
+      const mins = Math.floor(seconds / 60);
+      const secs = seconds % 60;
+      return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
     }
   },
+  mounted() {
+    // 获取系统信息
+    const systemInfo = uni.getSystemInfoSync();
+    const platform = systemInfo.platform;
+    
+    // 监听录音错误 - 增强错误处理
+    recorderManager.onError((error) => {
+      console.error('录音错误:', error);
+      
+      // 根据不同平台处理错误
+      let errorMessage = '录音出现错误,请检查麦克风权限或重试';
+      
+      // 针对iOS的特定错误处理
+      if (platform === 'ios') {
+        if (error.errMsg && error.errMsg.includes('authorize')) {
+          errorMessage = 'iOS需要麦克风权限,请在设置中允许访问麦克风';
+        } else if (error.errMsg && error.errMsg.includes('busy')) {
+          errorMessage = '麦克风正被其他应用使用,请关闭其他使用麦克风的应用';
+        }
+      } 
+      // 针对安卓的特定错误处理
+      else if (platform === 'android') {
+        if (error.errMsg && error.errMsg.includes('permission')) {
+          errorMessage = '安卓系统需要麦克风权限,请在设置中允许访问麦克风';
+        }
+      }
+      // 针对鸿蒙的特定错误处理
+      else if (platform === 'harmony') {
+        errorMessage = '请确保已授予麦克风权限并重试';
+      }
+      
+      uni.showModal({
+        title: '录音错误',
+        content: errorMessage,
+        showCancel: false,
+        success: () => {
+          this.stopRecording();
+        }
+      });
+    });
+
+    // 监听录音开始
+    recorderManager.onStart(() => {
+      console.log('录音开始');
+      this.recordingStarted = true;
+      
+      // 平台特定处理
+      if (platform === 'ios') {
+        // iOS上可能需要额外延迟来确保录音真正开始
+        setTimeout(() => {
+          // 如果没有检测到声音,开始模拟波形动画
+          if (this.silenceCounter > 5) {
+            this.updateWaveform(false);
+          }
+        }, 500);
+      }
+    });
+
+    // 监听录音帧数据
+    recorderManager.onFrameRecorded(res => {
+      if (!res.frameBuffer || !this.recordingStarted) return;
+      
+      try {
+        const int16Array = new Int16Array(res.frameBuffer);
+        this.detectAudio(int16Array);
+      } catch (error) {
+        console.error('处理音频帧数据错误:', error);
+        // 出错时使用默认波形动画
+        this.updateWaveform(false);
+      }
+    });
+    
+    // 录音结束回调
+    recorderManager.onStop(res => {
+      this.audioPath = res.tempFilePath;
+      this.isRecording = false;
+      this.recordingStarted = false;
+      clearInterval(this.recordingTimer);
+      clearInterval(this.animationTimer);
+      clearTimeout(this.noSoundTimeout);
+      this.statusMessage = '录制完成!';
+      
+      // 确保在所有平台上都能正确获取录音文件
+      if (res.tempFilePath) {
+        console.log('录音文件路径:', res.tempFilePath);
+        
+        // 针对iOS的特殊处理
+        if (platform === 'ios' && !res.tempFilePath.startsWith('file://')) {
+          this.audioPath = 'file://' + res.tempFilePath;
+        } else {
+          this.audioPath = res.tempFilePath;
+        }
+      } else {
+        console.error('未获取到录音文件路径');
+        uni.showToast({
+          title: '录音保存失败',
+          icon: 'none'
+        });
+      }
+    });
+    
+    // 添加中断处理 - 主要针对iOS
+    recorderManager.onInterruptionBegin && recorderManager.onInterruptionBegin(() => {
+      console.log('录音被中断');
+      this.statusMessage = '录音被中断,请重试';
+      this.stopRecording();
+    });
+    
+    recorderManager.onInterruptionEnd && recorderManager.onInterruptionEnd(() => {
+      console.log('录音中断结束');
+      // 可以选择自动重新开始录音
+      // this.startRecording();
+    });
+  },
   beforeDestroy() {
-    clearInterval(this.waveTimer);
+    // 停止所有计时器
+    clearInterval(this.recordingTimer);
+    clearInterval(this.animationTimer);
+    clearTimeout(this.noSoundTimeout);
+    
+    // 确保录音停止
+    if (this.isRecording) {
+      recorderManager.stop();
+    }
+    
+    // 移除事件监听器
+    try {
+      // 注意:uni-app不支持直接移除事件监听器
+      // 但我们可以确保组件销毁时停止所有活动
+      this.isRecording = false;
+      this.recordingStarted = false;
+    } catch (error) {
+      console.error('清理资源时出错:', error);
+    }
   }
 }
 </script>
@@ -101,51 +608,98 @@ export default {
   border-radius: 20rpx;
   padding: 40rpx;
   text-align: center;
+  box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.2);
 }
 
 .modal-title {
-  font-size: 32rpx;
+  font-size: 36rpx;
   font-weight: bold;
   margin-bottom: 20rpx;
+  color: #0039b3;
 }
 
 .modal-text {
-  font-size: 28rpx;
+  font-size: 30rpx;
   color: #666;
   margin-bottom: 30rpx;
 }
 
 .wave-container {
-  height: 160rpx;
+  height: 200rpx;
   display: flex;
   align-items: center;
   justify-content: center;
-  gap: 6rpx;
+  gap: 4rpx; /* 减小波形条之间的间距 */
   margin: 30rpx 0;
+  background: #f8f9fa;
+  border-radius: 15rpx;
+  position: relative;
+  overflow: hidden;
+  border: 1rpx solid #eee;
 }
 
 .wave-bar {
-  width: 8rpx;
-  background: #0039b3;
-  border-radius: 4rpx;
-  transition: height 0.1s ease;
+  width: 6rpx; /* 减小波形条的宽度 */
+  background: linear-gradient(to top, #1a73e8, #0039b3);
+  border-radius: 3rpx;
+  transition: height 0.1s ease-out; /* 加快过渡动画速度 */
+}
+
+.timer {
+  position: absolute;
+  top: 15rpx;
+  right: 15rpx;
+  background: rgba(0, 57, 179, 0.1);
+  color: #0039b3;
+  padding: 5rpx 15rpx;
+  border-radius: 20rpx;
+  font-size: 26rpx;
+  font-weight: 500;
 }
 
 .read-text {
-  background: #f5f5f5;
+  background: #f8f9fa;
   padding: 30rpx;
-  border-radius: 10rpx;
-  font-size: 30rpx;
+  border-radius: 15rpx;
+  font-size: 32rpx;
   margin: 30rpx 0;
+  color: #333;
+  line-height: 1.6;
+  border: 1rpx solid #eee;
 }
 
 .confirm-btn {
   width: 80%;
   height: 80rpx;
   line-height: 80rpx;
-  background: #0039b3;
+  background: linear-gradient(90deg, #0039b3, #1a73e8);
   color: #fff;
   border-radius: 40rpx;
   margin-top: 30rpx;
+  font-size: 32rpx;
+  font-weight: 500;
+  box-shadow: 0 5rpx 15rpx rgba(0, 57, 179, 0.3);
+}
+
+.status-message {
+  font-size: 28rpx;
+  color: #666;
+  margin-top: 30rpx;
+  height: 40rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+/* 添加状态消息的动画效果 */
+@keyframes blink {
+  0% { opacity: 1; }
+  50% { opacity: 0.5; }
+  100% { opacity: 1; }
+}
+
+.status-message.warning {
+  color: #ff9800;
+  animation: blink 1.5s infinite;
 }
 </style>

+ 11 - 8
pages/identity-verify/identity-verify.vue

@@ -356,7 +356,7 @@ export default {
       thinkingTimer: null, // 思考计时器
       questionRetryMap: {}, // 用于跟踪每个问题的重试次数
       maxQuestionRetries: 2, // 每个问题最大重试次数
-	   statusBarHeight: 0
+	    statusBarHeight: 0
     }
   },
    onLoad() {
@@ -388,14 +388,14 @@ export default {
       if (this.screenCaptureCount === 1) {
         uni.showModal({
           title: 'Warning',
-          content: 'Screen capture detected. If you capture the screen again, your interview will be invalid.',
+          content: '检测到屏幕截图。如果你再次捕捉屏幕,你的面试将无效.',
           showCancel: false,
           confirmText: 'OK'
         });
       } else if (this.screenCaptureCount >= 2) {
         uni.showModal({
           title: 'Interview Invalid',
-          content: 'Your interview has been invalidated due to multiple screen captures. Please contact the recruiter for assistance.',
+          content: '由于多次屏幕截图,您的面试已无效。请联系招聘人员寻求帮助.',
           showCancel: false,
           confirmText: 'OK',
           success: () => {
@@ -407,6 +407,7 @@ export default {
 
     const systemInfo = uni.getSystemInfoSync();
     const isMiniProgram = systemInfo.uniPlatform && systemInfo.uniPlatform.startsWith('mp-');
+    console.log('isMiniProgram', isMiniProgram);
     if (isMiniProgram) {
       this.initPersonDetectionWebSocket();
     }
@@ -4176,6 +4177,7 @@ export default {
         this.personDetectionSocket.onMessage((res) => {
           try {
             const data = JSON.parse(res.data);
+          /*   console.log('data1', data); */
             if (data.type === 'person_detection_result') {
               this.handlePersonDetectionResult(data);
             }
@@ -4265,6 +4267,7 @@ export default {
     },
 
     handlePersonDetectionResult(data) {
+      console.log('data', data);
       /* console.log(data.data.detection.has_person);
       console.log(data.data.identity.status);//identity_verified */
       
@@ -4290,10 +4293,10 @@ export default {
         }, 3000);
       }
       // 然后检查身份验证状态
-      else */ if (data.data.identity.status !== "identity_verified") {
+      else */ if (data.data.identity.status == "no_face") {
          this.showPageWarning = true;
-        uni.showToast({
-          title: data.data.identity.message,
+        uni.showToast({/* data.data.identity.message */
+          title: '请保持面部完整出现在镜头内',
           icon: 'none',
           duration: 3000
         });
@@ -4558,7 +4561,7 @@ export default {
       this.isVideoSwitching = false; // 重置切换状态锁
       
       // 延迟执行跳转,确保资源清理完成
-      setTimeout(() => {
+     /*  setTimeout(() => { */
         // 如果职位ID为9,跳转到interview-question页面
         if (jobId === 9) {
           uni.navigateTo({
@@ -4614,7 +4617,7 @@ export default {
             }
           });
         }
-      }, 200);
+      /* }, 200); */
     },
   },
   computed: {

+ 58 - 8
pages/index/index.vue

@@ -334,22 +334,72 @@ import { apiBaseUrl } from '@/common/config.js';
 						if (res.statusCode === 200) {
 							if (res.data.code === 2000) {
 								uni.setStorageSync('configData', JSON.stringify(res.data.data))
-								uni.navigateTo({
-									url: '/pages/Personal/Personal',
+								// 调用获取用户完整信息的接口
+								const openid = JSON.parse(uni.getStorageSync('userInfo')).openid;
+								uni.request({
+									url: `${apiBaseUrl}/api/wechat/user/get_full_info?tenant_id=1&openid=${openid}`,
+									method: 'GET',
+									success: (infoRes) => {
+										if (infoRes.statusCode === 200 && infoRes.data && infoRes.data.data && infoRes.data.data.profile) {
+											const resumeUrl = infoRes.data.data.profile.resume_url || '';
+											if (!resumeUrl) {
+												// resume_url 为空,跳转到其他页面
+												uni.navigateTo({
+													url: '/pages/uploadResume/uploadResume', // 假设跳转到上传简历页面
+													fail: (err) => {
+														console.error('页面跳转失败:', err);
+														uni.showToast({
+															title: '页面跳转失败',
+															icon: 'none'
+														});
+													}
+												});
+											} else {
+												// resume_url 不为空,跳转到 Personal 页面
+												uni.navigateTo({
+													url: '/pages/Personal/Personal',
+													fail: (err) => {
+														console.error('页面跳转失败:', err);
+														uni.showToast({
+															title: '页面跳转失败',
+															icon: 'none'
+														});
+													}
+												});
+											}
+										} else {
+											// 获取用户信息失败,直接跳转到 Personal 页面
+											uni.navigateTo({
+												url: '/pages/Personal/Personal',
+												fail: (err) => {
+													console.error('页面跳转失败:', err);
+													uni.showToast({
+														title: '页面跳转失败',
+														icon: 'none'
+													});
+												}
+											});
+										}
+									},
 									fail: (err) => {
-										console.error('页面跳转失败:', err);
-										uni.showToast({
-											title: '页面跳转失败',
-											icon: 'none'
+										console.error('获取用户信息失败:', err);
+										uni.navigateTo({
+											url: '/pages/Personal/Personal',
+											fail: (err) => {
+												console.error('页面跳转失败:', err);
+												uni.showToast({
+													title: '页面跳转失败',
+													icon: 'none'
+												});
+											}
 										});
 									}
 								});
 							} else {
-								
+								// 其他逻辑
 							}
 						} else {
 							uni.hideLoading();
-							
 						}
 					},
 					fail: (err) => {

+ 128 - 40
pages/interview/interview.vue

@@ -25,14 +25,13 @@
 				</view>
 			</view>
 		</view>
-		
-		<view class="controls">
+		<view class="controls" v-if="allPhotosCompleted">
 			<button class="camera-btn" @click="takePhoto">{{ photos[currentStep] ? '重新拍摄' : '拍摄照片' }}</button>
 			
-			<view class="navigation-btns">
-				<button class="nav-btn prev" :disabled="currentStep === 0" @click="prevStep">上一步</button>
+			<view class="navigation-btns" v-if="allPhotosCompleted">
+				<!-- <button class="nav-btn prev" :disabled="currentStep === 0" @click="prevStep">上一步</button> -->
 				<button class="nav-btn next" :disabled="!canGoNext" @click="nextStep">
-					{{ currentStep === 5 && photos[5] ? '完成' : '下一步' }}
+					{{ allPhotosCompleted ? '完成' : '下一步' }}
 				</button>
 			</view>
 		</view>
@@ -77,10 +76,9 @@
 			
 			<cover-view class="camera-controls">
 				<cover-view class="capture-btn" @tap="capturePhotoMP"></cover-view>
-				<cover-view class="camera-buttons">
-					<!-- <cover-view class="switch-camera-btn" @tap="switchCamera">切换摄像头</cover-view> -->
+				<!-- <cover-view class="camera-buttons" v-if="allPhotosCompleted">
 					<cover-view class="cancel-btn" @tap="cancelCamera">取消</cover-view>
-				</cover-view>
+				</cover-view> -->
 			</cover-view>
 		</view>
 		
@@ -177,6 +175,10 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 			}
 		},
 		computed: {
+			allPhotosCompleted() {
+				// 检查是否所有照片都已拍摄完成
+				return this.photos.every(photo => photo !== null);
+			},
 			canGoNext() {
 				// 如果是最后一步,检查所有照片是否都已完成
 				if (this.currentStep === 5) {
@@ -198,6 +200,7 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 				scope: 'scope.camera',
 				success: () => {
 					console.log('相机权限已授权');
+					this.takePhoto();
 				},
 				fail: () => {
 					uni.showModal({
@@ -219,10 +222,22 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 		},
 		methods: {
 			takePhoto() {
+				// 记录当前是否为重新拍摄
+				const isRetake = this.photos[this.currentStep] !== null;
+				
 				// 先重置当前步骤的状态
 				this.$set(this.photos, this.currentStep, null);
 				this.$set(this.photoLinks, this.currentStep, null);
 				
+				// 如果是重新拍摄,显示提示
+				if (isRetake) {
+					uni.showToast({
+						title: '重新拍摄第' + (this.currentStep + 1) + '张照片',
+						icon: 'none',
+						duration: 1500
+					});
+				}
+				
 				// 根据平台选择不同的拍照方法
 				if (this.isH5) {
 					this.startCamera();
@@ -252,6 +267,9 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 					title: '上传中...'
 				});
 				
+				// 获取当前步骤索引,确保上传到正确的位置
+				const currentIndex = this.currentStep;
+				
 				// 获取用户openid
 				const userInfo = uni.getStorageSync('userInfo');
 				const openid = userInfo ? JSON.parse(userInfo).openid || '' : '';
@@ -316,17 +334,33 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 						}
 						
 						uni.hideLoading();
-						uni.showToast({
-							title: '照片上传成功',
-							icon: 'success',
-							duration: 1500,
-							success: () => {
-								// 上传成功后自动进入下一步
-								setTimeout(() => {
-									this.autoNextStep();
-								}, 1500);
-							}
-						});
+								uni.showToast({
+									title: '照片上传成功',
+									icon: 'success',
+									duration: 1500,
+									success: () => {
+										// 判断是否是重新拍摄的照片
+										const isRetake = this.photos.filter(photo => photo !== null).length > currentIndex + 1;
+										
+										// 如果是重新拍摄,不自动进入下一步
+										if (!isRetake) {
+											// 判断是否是最后一步
+											if (currentIndex === 5) {
+												// 最后一步,显示提示但不自动进入下一步
+												uni.showToast({
+													title: '最后一张照片已完成,请点击完成按钮提交',
+													icon: 'success',
+													duration: 2000
+												});
+											} else {
+												// 不是最后一步,自动进入下一步
+												setTimeout(() => {
+													this.autoNextStep();
+												}, 1500);
+											}
+										}
+									}
+								});
 					})
 					.catch(error => {
 						console.error('照片上传失败:', error);
@@ -383,10 +417,26 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 								icon: 'success',
 								duration: 1500,
 								success: () => {
-									// 上传成功后自动进入下一步
-									setTimeout(() => {
-										this.autoNextStep();
-									}, 1500);
+									// 判断是否是重新拍摄的照片
+								const isRetake = this.photos.filter(photo => photo !== null).length > currentIndex + 1;
+								
+								// 如果是重新拍摄,不自动进入下一步
+								if (!isRetake) {
+									// 判断是否是最后一步
+									if (currentIndex === 5) {
+										// 最后一步,显示提示但不自动进入下一步
+										uni.showToast({
+											title: '最后一张照片已完成,请点击完成按钮提交',
+											icon: 'success',
+											duration: 2000
+										});
+									} else {
+										// 不是最后一步,自动进入下一步
+										setTimeout(() => {
+											this.autoNextStep();
+										}, 1500);
+									}
+								}
 								}
 							});
 						},
@@ -410,26 +460,53 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 				}
 			},
 			nextStep() {
+				// 如果所有照片都已拍摄完成,点击"完成"按钮时提交
+				if (this.allPhotosCompleted) {
+					// 所有照片都已拍摄完成,进行提交
+					this.submitAllPhotos();
+					return;
+				}
+				
+				// 否则进入下一步
 				if (this.currentStep < 5) {
-					return
 					this.currentStep++;
-				} else if (this.currentStep === 5 && this.photos[5]) {
-					// 所有照片都已拍摄完成,可以进行提交或跳转
-					this.submitAllPhotos();
 				}
 			},
 			previewPhoto(index) {
-				// 如果照片存在,则预览
+				// 如果照片存在,有两种操作:
+				// 1. 在预览区显示该照片
+				// 2. 切换到该步骤,允许重新拍摄
 				if (this.photos[index]) {
-					uni.previewImage({
-						urls: [this.photos[index]],
-						current: this.photos[index]
+					// 切换到对应的步骤
+					this.currentStep = index;
+					
+					// 显示操作选项
+					uni.showActionSheet({
+						itemList: ['在预览区查看', '重新拍摄此照片'],
+						success: (res) => {
+							if (res.tapIndex === 0) {
+								// 选择了预览,不需要额外操作,因为已经切换到对应步骤
+								// 如果需要全屏预览,可以使用以下代码
+								uni.previewImage({
+									urls: [this.photos[index]],
+									current: this.photos[index]
+								});
+							} else if (res.tapIndex === 1) {
+								// 选择了重新拍摄
+								this.takePhoto(); // 调用拍照方法重新拍摄当前步骤的照片
+							}
+						}
 					});
+				} else {
+					// 如果照片不存在,直接切换到该步骤
+					this.currentStep = index;
+					// 可以选择是否自动开始拍照
+					// this.takePhoto();
 				}
 			},
 			submitAllPhotos() {
 				// 检查是否所有照片都已拍摄
-				if (this.photos.every(photo => photo !== null)) {
+				if (this.allPhotosCompleted) {
 					uni.showLoading({
 						title: '处理中...'
 					});
@@ -743,16 +820,27 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 			
 			// 添加自动进入下一步的方法
 			autoNextStep() {
+				// 检查是否所有照片都已拍摄完成
+				const allPhotosCompleted = this.photos.every(photo => photo !== null);
+				
+				if (allPhotosCompleted) {
+					// 所有照片都已拍摄完成,显示提示,但不自动提交
+					// 让用户点击"完成"按钮来提交
+					uni.showToast({
+						title: '所有照片已完成,请点击完成按钮提交',
+						icon: 'success',
+						duration: 2000
+					});
+					return;
+				}
+				
+				// 如果当前不是最后一步,进入下一步
 				if (this.currentStep < 5) {
 					this.currentStep++;
-					// 如果不是最后一步,自动开始拍摄下一张照片
-					if (this.currentStep < 6) {
-						this.takePhoto();
-					}
-				} else if (this.currentStep === 5 && this.photos[5]) {
-					// 所有照片都已拍摄完成,进行提交
-					this.submitAllPhotos();
+					// 自动开始拍摄下一张照片
+					this.takePhoto();
 				}
+				// 移除自动提交逻辑,让用户点击"完成"按钮来提交
 			}
 		}
 	}
@@ -876,7 +964,7 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 }
 
 .nav-btn {
-	width: 48%;
+	width: 100%;
 	height: 80rpx;
 	line-height: 80rpx;
 	text-align: center;

+ 1 - 1
pages/job-detail/job-detail.vue

@@ -352,7 +352,7 @@ export default {
 
         // 使用微信小程序的地址解析接口
         uni.request({
-          url: `https://apis.map.qq.com/ws/geocoder/v1/?address=${encodeURIComponent(addressStr)}&key=WJLBZ-SMQYZ-3RNX5-7J4LI-XTZD6-7IBZR`, // 需要替换为实际的地图Key
+          url: `https://apis.map.qq.com/ws/geocoder/v1/?address=${encodeURIComponent(addressStr)}&key=ZS4BZ-NKAA7-4VLXR-PHVI4-HAGPH-Z4FJ3`, // 需要替换为实际的地图Key
           success: (res) => {
             if (res.data.status === 0) {
               const { lat, lng } = res.data.result.location;

+ 52 - 3
pages/uploadResume/uploadResume.vue

@@ -12,7 +12,9 @@
 		<button class="upload-btn" @click="chooseFile" :disabled="!!uploadedFile">
 			{{uploadedFile ? '已上传文件' : '选择并上传文件'}}
 		</button>
-
+		<!-- 跳过,直接进入下一环节 -->
+		<button class="skip-btn" @click="handleSkip" v-if="showSkipButton">跳过,直接进入下一环节</button>
+		
 		<!-- 文件信息显示区域 -->
 		<view class="file-info" v-if="uploadedFile">
 			<view class="file-info-header">
@@ -53,10 +55,34 @@
 			return {
 				fileType: ['pdf', 'doc', 'docx'],
 				maxSize: 30 * 1024 * 1024, // 30MB in bytes
-				uploadedFile: null
+				uploadedFile: null,
+				showSkipButton: false // 控制跳过按钮显示
+			}
+		},
+		onLoad(options) {
+			// 判断是否从index页面跳转来
+			const pages = getCurrentPages();
+			const prevPage = pages[pages.length - 2]; // 获取上一个页面
+			if (prevPage && prevPage.route === 'pages/index/index') {
+				this.showSkipButton = true;
 			}
 		},
 		methods: {
+			// 处理跳过按钮点击
+			handleSkip() {
+				uni.showModal({
+					title: '提示',
+					content: '确定要跳过上传简历环节吗?',
+					success: (res) => {
+						if (res.confirm) {
+							uni.navigateTo({
+								url: '/pages/Personal/Personal'
+							});
+						}
+					}
+				});
+			},
+			
 			// 调用简历解析接口
 			/* async parseResume(fileUrl) {
 				try {
@@ -248,7 +274,13 @@
 											uni.hideLoading();
 											uni.showToast({
 												title: '处理完成',
-												icon: 'success'
+												icon: 'success',
+												duration: 2000,
+												success: () => {
+													uni.navigateTo({
+														url: '/pages/Personal/Personal'
+													});
+												}
 											});
 										}
 									}, 1000);
@@ -416,5 +448,22 @@
 			color: #ffffff;
 		}
 	}
+	
+	.skip-btn {
+		width: 90%;
+		height: 88rpx;
+		line-height: 88rpx;
+		background-color: #ffffff;
+		color: #666666;
+		border-radius: 8rpx;
+		font-size: 32rpx;
+		margin: 20rpx auto;
+		text-align: center;
+		border: 1px solid #dddddd;
+		
+		&::after {
+			border: none;
+		}
+	}
 }
 </style>

+ 1 - 1
unpackage/dist/dev/mp-weixin/api/user.js

@@ -38,7 +38,7 @@ const logout = () => {
 const getJobList = (params = {}) => {
   const defaultParams = {
     page: 1,
-    pageSize: 10,
+    limit: 50,
     searchTerms: "",
     status: "",
     tenant_id: 1

+ 12 - 32
unpackage/dist/dev/mp-weixin/pages/camera/camera.js

@@ -201,7 +201,7 @@ const _sfc_main = {
             }
           });
         }
-      }, 5e3);
+      }, 3e3);
     },
     cleanupPersonDetectionWebSocket() {
       if (this.personDetectionInterval) {
@@ -372,7 +372,6 @@ const _sfc_main = {
               } else {
                 common_vendor.index.navigateTo({
                   url: "/pages/posture-guide/posture-guide"
-                  //'/pages/interview-question/interview-question'
                 });
               }
             }, 1500);
@@ -395,9 +394,8 @@ const _sfc_main = {
         });
         return;
       }
-      if (this.showResult) {
-        this.showResult = false;
-      }
+      if (this.showResult)
+        return;
       console.log("selectOption", index);
       if (this.currentQuestion.questionType === 2) {
         const optionIndex = this.selectedOptions.indexOf(index);
@@ -414,22 +412,12 @@ const _sfc_main = {
           this.saveAnswer();
           this.submitCurrentAnswer().then(() => {
             setTimeout(() => {
-              if (this.currentQuestionIndex < this.answers.length - 1) {
-                this.currentQuestionIndex++;
-                this.currentScrollId = "question-" + this.currentQuestionIndex;
-              } else {
-                this.goToNextQuestion();
-              }
+              this.goToNextQuestion();
             }, 800);
           }).catch((error) => {
             console.error("提交答案失败:", error);
             setTimeout(() => {
-              if (this.currentQuestionIndex < this.answers.length - 1) {
-                this.currentQuestionIndex++;
-                this.currentScrollId = "question-" + this.currentQuestionIndex;
-              } else {
-                this.goToNextQuestion();
-              }
+              this.goToNextQuestion();
             }, 1e3);
           });
         }, 500);
@@ -452,22 +440,12 @@ const _sfc_main = {
           this.saveAnswer();
           this.submitCurrentAnswer().then(() => {
             setTimeout(() => {
-              if (this.currentQuestionIndex < this.answers.length - 1) {
-                this.currentQuestionIndex++;
-                this.currentScrollId = "question-" + this.currentQuestionIndex;
-              } else {
-                this.goToNextQuestion();
-              }
+              this.goToNextQuestion();
             }, 800);
           }).catch((error) => {
             console.error("提交答案失败:", error);
             setTimeout(() => {
-              if (this.currentQuestionIndex < this.answers.length - 1) {
-                this.currentQuestionIndex++;
-                this.currentScrollId = "question-" + this.currentQuestionIndex;
-              } else {
-                this.goToNextQuestion();
-              }
+              this.goToNextQuestion();
             }, 1e3);
           });
         }, 500);
@@ -524,7 +502,6 @@ const _sfc_main = {
             } else {
               common_vendor.index.navigateTo({
                 url: "/pages/posture-guide/posture-guide"
-                //'/pages/interview-question/interview-question'
               });
             }
           } else {
@@ -543,7 +520,6 @@ const _sfc_main = {
             } else {
               common_vendor.index.navigateTo({
                 url: "/pages/posture-guide/posture-guide"
-                //'/pages/interview-question/interview-question'
               });
             }
           } else {
@@ -667,6 +643,11 @@ const _sfc_main = {
     // 修改 handleOptionClick 方法
     handleOptionClick(qIndex, index) {
       if (qIndex < this.currentQuestionIndex - 1) {
+        common_vendor.index.showToast({
+          title: "该题目已完成,无法修改",
+          icon: "none",
+          duration: 2e3
+        });
         return;
       }
       if (qIndex === this.questions.length - 1 && this.showContinueButton) {
@@ -740,7 +721,6 @@ const _sfc_main = {
       } else {
         common_vendor.index.navigateTo({
           url: "/pages/posture-guide/posture-guide"
-          //'/pages/interview-question/interview-question'
         });
       }
     },

+ 389 - 23
unpackage/dist/dev/mp-weixin/pages/components/voice-check-modal.js

@@ -12,45 +12,410 @@ const _sfc_main = {
   data() {
     return {
       isRecording: false,
-      waveData: Array(20).fill(20),
-      // 初始波形数据
-      waveTimer: null
+      waveData: Array(30).fill(20),
+      recordingTime: 0,
+      statusMessage: '点击"开始录制"按钮朗读文字',
+      silenceCounter: 0,
+      recordingTimer: null,
+      volumeLevel: 0,
+      animationTimer: null,
+      lastVolume: 0,
+      hasPermission: false,
+      // 添加录音权限状态
+      noSoundTimeout: null,
+      // 添加无声音超时计时器
+      recordingStarted: false,
+      // 添加录音开始标志
+      audioPath: null,
+      // 添加录音文件路径
+      platform: "",
+      // 添加平台标识
+      isRetrying: false
+      // 添加重试标记
     };
   },
   methods: {
-    // 开始录音
-    startRecording() {
+    // 检查录音权限 - 跨平台兼容版本
+    async checkPermission() {
+      const systemInfo = common_vendor.index.getSystemInfoSync();
+      const platform = systemInfo.platform;
+      try {
+        if (platform === "ios") {
+          const res = await new Promise((resolve, reject) => {
+            common_vendor.index.getSetting({
+              success: (result) => {
+                if (result.authSetting["scope.record"]) {
+                  resolve(true);
+                } else {
+                  common_vendor.index.authorize({
+                    scope: "scope.record",
+                    success: () => resolve(true),
+                    fail: (err) => reject(err)
+                  });
+                }
+              },
+              fail: (err) => reject(err)
+            });
+          });
+          this.hasPermission = true;
+          return true;
+        } else {
+          const res = await common_vendor.index.authorize({
+            scope: "scope.record"
+          });
+          this.hasPermission = true;
+          return true;
+        }
+      } catch (error) {
+        console.error("权限请求失败:", error);
+        common_vendor.index.showModal({
+          title: "提示",
+          content: "需要录音权限才能进行录音,请在设置中开启录音权限",
+          confirmText: "去设置",
+          success: (res) => {
+            if (res.confirm) {
+              common_vendor.index.openSetting();
+            }
+          }
+        });
+        this.hasPermission = false;
+        return false;
+      }
+    },
+    async startRecording() {
+      if (!await this.checkPermission()) {
+        return;
+      }
       this.isRecording = true;
-      recorderManager.start({
-        duration: 1e4,
-        sampleRate: 16e3,
-        numberOfChannels: 1,
-        encodeBitRate: 48e3,
-        format: "wav"
-      });
-      this.waveTimer = setInterval(() => {
-        this.waveData = this.waveData.map(() => Math.random() * 40 + 20);
-      }, 100);
+      this.recordingTime = 0;
+      this.silenceCounter = 0;
+      this.statusMessage = "正在检测声音...";
+      this.waveData = this.waveData.map(() => 20);
+      this.lastVolume = 0;
+      this.recordingStarted = true;
+      const systemInfo = common_vendor.index.getSystemInfoSync();
+      const platform = systemInfo.platform;
+      const recorderOptions = {
+        duration: 6e4,
+        // 最长录音时间
+        format: "mp3",
+        // 使用mp3格式提高兼容性
+        frameSize: 5
+      };
+      if (platform === "ios") {
+        recorderOptions.sampleRate = 44100;
+        recorderOptions.numberOfChannels = 1;
+        recorderOptions.encodeBitRate = 96e3;
+      } else {
+        recorderOptions.sampleRate = 16e3;
+        recorderOptions.numberOfChannels = 1;
+        recorderOptions.encodeBitRate = 48e3;
+      }
+      recorderManager.start(recorderOptions);
+      this.recordingTimer = setInterval(() => {
+        this.recordingTime++;
+      }, 1e3);
+      this.animationTimer = setInterval(() => {
+        if (this.isRecording) {
+          this.updateWaveform();
+        }
+      }, 50);
+      this.noSoundTimeout = setTimeout(() => {
+        if (this.silenceCounter > 20) {
+          this.showNoSoundTip();
+        }
+      }, 3e3);
     },
-    // 停止录音
     stopRecording() {
       this.isRecording = false;
+      this.recordingStarted = false;
       recorderManager.stop();
-      clearInterval(this.waveTimer);
-      this.waveData = Array(20).fill(20);
+      clearInterval(this.recordingTimer);
+      clearInterval(this.animationTimer);
+      clearTimeout(this.noSoundTimeout);
+      this.statusMessage = "录制完成!";
+      setTimeout(() => {
+        this.waveData = Array(30).fill(20);
+      }, 500);
+      setTimeout(() => {
+        this.processAudioFile();
+        this.$emit("complete", {
+          success: true,
+          audioPath: this.audioPath,
+          platform: this.platform
+        });
+      }, 300);
+    },
+    // 添加音频文件处理方法
+    processAudioFile() {
+      if (!this.audioPath)
+        return;
+      const systemInfo = common_vendor.index.getSystemInfoSync();
+      this.platform = systemInfo.platform;
+      console.log("处理音频文件:", this.audioPath, "平台:", this.platform);
+      if (this.platform === "ios") {
+        if (!this.audioPath.startsWith("file://")) {
+          this.audioPath = "file://" + this.audioPath;
+        }
+      } else if (this.platform === "android")
+        ;
+      else if (this.platform === "harmony")
+        ;
+      common_vendor.index.getFileInfo({
+        filePath: this.audioPath,
+        success: (res) => {
+          console.log("音频文件信息:", res);
+          if (res.size === 0) {
+            console.warn("警告: 音频文件大小为0");
+          }
+        },
+        fail: (err) => {
+          console.error("获取音频文件信息失败:", err);
+        }
+      });
+    },
+    // 显示无声音提示
+    showNoSoundTip() {
+      if (!this.isRecording)
+        return;
+      common_vendor.index.showModal({
+        title: "未检测到声音",
+        content: "请检查以下问题:\n1. 麦克风是否正常工作\n2. 是否允许使用麦克风\n3. 是否靠近麦克风说话\n4. 说话音量是否足够",
+        showCancel: true,
+        cancelText: "重新录制",
+        confirmText: "继续录制",
+        success: (res) => {
+          if (res.cancel) {
+            this.restartRecording();
+          } else {
+            this.continueRecording();
+          }
+        }
+      });
+    },
+    // 重新录制方法
+    restartRecording() {
+      recorderManager.stop();
+      clearInterval(this.recordingTimer);
+      clearInterval(this.animationTimer);
+      clearTimeout(this.noSoundTimeout);
+      this.isRecording = false;
+      this.recordingStarted = false;
+      this.recordingTime = 0;
+      this.silenceCounter = 0;
+      this.volumeLevel = 0;
+      this.lastVolume = 0;
+      this.waveData = Array(30).fill(20);
+      setTimeout(() => {
+        this.startRecording();
+      }, 500);
+    },
+    // 继续录制方法
+    continueRecording() {
+      this.silenceCounter = 0;
+      this.statusMessage = "正在检测声音...";
+      clearTimeout(this.noSoundTimeout);
+      this.noSoundTimeout = setTimeout(() => {
+        if (this.silenceCounter > 20) {
+          this.showNoSoundTip();
+        }
+      }, 3e3);
+    },
+    detectAudio(int16Array) {
+      const systemInfo = common_vendor.index.getSystemInfoSync();
+      const platform = systemInfo.platform;
+      let sum = 0;
+      let peak = 0;
+      const blockSize = 128;
+      for (let i = 0; i < int16Array.length; i += blockSize) {
+        const end = Math.min(i + blockSize, int16Array.length);
+        for (let j = i; j < end; j++) {
+          const absValue = Math.abs(int16Array[j]);
+          sum += absValue;
+          peak = Math.max(peak, absValue);
+        }
+      }
+      const avg = sum / int16Array.length;
+      let SPEAK_THRESHOLD = 500;
+      let volumeScale = 2e3;
+      if (platform === "ios") {
+        SPEAK_THRESHOLD = 300;
+        volumeScale = 1500;
+      } else if (platform === "android") {
+        SPEAK_THRESHOLD = 500;
+        volumeScale = 2e3;
+      } else {
+        SPEAK_THRESHOLD = 400;
+        volumeScale = 1800;
+      }
+      const combinedLevel = (avg * 0.7 + peak * 0.3) / volumeScale;
+      this.volumeLevel = this.smoothVolume(combinedLevel);
+      if (avg > SPEAK_THRESHOLD || peak > SPEAK_THRESHOLD * 3) {
+        this.statusMessage = "检测到声音:录音中...";
+        this.silenceCounter = 0;
+        clearTimeout(this.noSoundTimeout);
+        this.updateWaveform(false);
+      } else {
+        this.silenceCounter++;
+        if (this.silenceCounter > 10) {
+          this.statusMessage = "未检测到声音,请靠近麦克风并说话...";
+          if (this.silenceCounter === 30 && this.isRecording) {
+            this.showNoSoundTip();
+          }
+          this.updateWaveform(true);
+        }
+      }
+    },
+    // 添加音量平滑处理函数
+    smoothVolume(newVolume) {
+      const smoothFactor = 0.3;
+      const smoothedVolume = this.lastVolume + (newVolume - this.lastVolume) * smoothFactor;
+      this.lastVolume = smoothedVolume;
+      return Math.min(1, Math.max(0, smoothedVolume));
+    },
+    updateWaveform(isSilent = false) {
+      const systemInfo = common_vendor.index.getSystemInfoSync();
+      const platform = systemInfo.platform;
+      let decayFactor = 0.95;
+      let animationScale = 1;
+      if (platform === "ios") {
+        decayFactor = 0.97;
+        animationScale = 0.9;
+      } else if (platform === "android") {
+        decayFactor = 0.95;
+        animationScale = 1;
+      } else {
+        decayFactor = 0.96;
+        animationScale = 0.95;
+      }
+      if (isSilent) {
+        this.waveData = this.waveData.map((height) => Math.max(20, height * decayFactor));
+      } else {
+        const now = Date.now() * 2e-3;
+        const volumeEffect = this.volumeLevel * animationScale;
+        const baseHeight = 20 + volumeEffect * 60;
+        const waveAmplitude = volumeEffect * 40;
+        this.waveData = this.waveData.map((currentHeight, i) => {
+          const phase = i * 0.2 + now;
+          const wave1 = Math.cos(phase) * 0.5;
+          const wave2 = Math.sin(phase * 1.5) * 0.3;
+          const randomFactor = Math.random() * 0.1;
+          const combinedWave = (wave1 + wave2 + randomFactor) * volumeEffect;
+          const targetHeight = baseHeight + combinedWave * waveAmplitude;
+          const smoothFactor = platform === "ios" ? 0.3 : 0.4;
+          const newHeight = currentHeight + (targetHeight - currentHeight) * smoothFactor;
+          return Math.max(20, Math.min(120, newHeight));
+        });
+      }
     },
-    // 处理确认按钮点击
     handleConfirm() {
       if (!this.isRecording) {
         this.startRecording();
       } else {
         this.stopRecording();
-        this.$emit("complete");
       }
+    },
+    formatTime(seconds) {
+      const mins = Math.floor(seconds / 60);
+      const secs = seconds % 60;
+      return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
     }
   },
+  mounted() {
+    const systemInfo = common_vendor.index.getSystemInfoSync();
+    const platform = systemInfo.platform;
+    recorderManager.onError((error) => {
+      console.error("录音错误:", error);
+      let errorMessage = "录音出现错误,请检查麦克风权限或重试";
+      if (platform === "ios") {
+        if (error.errMsg && error.errMsg.includes("authorize")) {
+          errorMessage = "iOS需要麦克风权限,请在设置中允许访问麦克风";
+        } else if (error.errMsg && error.errMsg.includes("busy")) {
+          errorMessage = "麦克风正被其他应用使用,请关闭其他使用麦克风的应用";
+        }
+      } else if (platform === "android") {
+        if (error.errMsg && error.errMsg.includes("permission")) {
+          errorMessage = "安卓系统需要麦克风权限,请在设置中允许访问麦克风";
+        }
+      } else if (platform === "harmony") {
+        errorMessage = "请确保已授予麦克风权限并重试";
+      }
+      common_vendor.index.showModal({
+        title: "录音错误",
+        content: errorMessage,
+        showCancel: false,
+        success: () => {
+          this.stopRecording();
+        }
+      });
+    });
+    recorderManager.onStart(() => {
+      console.log("录音开始");
+      this.recordingStarted = true;
+      if (platform === "ios") {
+        setTimeout(() => {
+          if (this.silenceCounter > 5) {
+            this.updateWaveform(false);
+          }
+        }, 500);
+      }
+    });
+    recorderManager.onFrameRecorded((res) => {
+      if (!res.frameBuffer || !this.recordingStarted)
+        return;
+      try {
+        const int16Array = new Int16Array(res.frameBuffer);
+        this.detectAudio(int16Array);
+      } catch (error) {
+        console.error("处理音频帧数据错误:", error);
+        this.updateWaveform(false);
+      }
+    });
+    recorderManager.onStop((res) => {
+      this.audioPath = res.tempFilePath;
+      this.isRecording = false;
+      this.recordingStarted = false;
+      clearInterval(this.recordingTimer);
+      clearInterval(this.animationTimer);
+      clearTimeout(this.noSoundTimeout);
+      this.statusMessage = "录制完成!";
+      if (res.tempFilePath) {
+        console.log("录音文件路径:", res.tempFilePath);
+        if (platform === "ios" && !res.tempFilePath.startsWith("file://")) {
+          this.audioPath = "file://" + res.tempFilePath;
+        } else {
+          this.audioPath = res.tempFilePath;
+        }
+      } else {
+        console.error("未获取到录音文件路径");
+        common_vendor.index.showToast({
+          title: "录音保存失败",
+          icon: "none"
+        });
+      }
+    });
+    recorderManager.onInterruptionBegin && recorderManager.onInterruptionBegin(() => {
+      console.log("录音被中断");
+      this.statusMessage = "录音被中断,请重试";
+      this.stopRecording();
+    });
+    recorderManager.onInterruptionEnd && recorderManager.onInterruptionEnd(() => {
+      console.log("录音中断结束");
+    });
+  },
   beforeDestroy() {
-    clearInterval(this.waveTimer);
+    clearInterval(this.recordingTimer);
+    clearInterval(this.animationTimer);
+    clearTimeout(this.noSoundTimeout);
+    if (this.isRecording) {
+      recorderManager.stop();
+    }
+    try {
+      this.isRecording = false;
+      this.recordingStarted = false;
+    } catch (error) {
+      console.error("清理资源时出错:", error);
+    }
   }
 };
 function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
@@ -66,8 +431,9 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
       };
     })
   } : {}, {
-    d: common_vendor.t($data.isRecording ? "读完了" : "开始"),
-    e: common_vendor.o((...args) => $options.handleConfirm && $options.handleConfirm(...args))
+    d: common_vendor.t($data.isRecording ? "停止录制" : "开始录制"),
+    e: common_vendor.o((...args) => $options.handleConfirm && $options.handleConfirm(...args)),
+    f: common_vendor.t($data.statusMessage)
   }) : {});
 }
 const Component = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-ce199c44"]]);

+ 1 - 1
unpackage/dist/dev/mp-weixin/pages/components/voice-check-modal.wxml

@@ -1 +1 @@
-<view wx:if="{{a}}" class="modal data-v-ce199c44"><view class="modal-content data-v-ce199c44"><view class="modal-title data-v-ce199c44">麦克风检测</view><view class="modal-text data-v-ce199c44">请朗读下面的文字</view><view wx:if="{{b}}" class="wave-container data-v-ce199c44"><view wx:for="{{c}}" wx:for-item="item" wx:key="a" class="wave-bar data-v-ce199c44" style="{{'height:' + item.b}}"></view></view><view class="read-text data-v-ce199c44">成为奇才,就是现在,我们开始吧。</view><button class="confirm-btn data-v-ce199c44" bindtap="{{e}}">{{d}}</button></view></view>
+<view wx:if="{{a}}" class="modal data-v-ce199c44"><view class="modal-content data-v-ce199c44"><view class="modal-title data-v-ce199c44">麦克风检测</view><view class="modal-text data-v-ce199c44">请朗读下面的文字</view><view wx:if="{{b}}" class="wave-container data-v-ce199c44"><view wx:for="{{c}}" wx:for-item="item" wx:key="a" class="wave-bar data-v-ce199c44" style="{{'height:' + item.b}}"></view></view><view class="read-text data-v-ce199c44">成为奇才,就是现在,我们开始吧。</view><button class="confirm-btn data-v-ce199c44" bindtap="{{e}}">{{d}}</button><view class="status-message data-v-ce199c44">{{f}}</view></view></view>

+ 59 - 12
unpackage/dist/dev/mp-weixin/pages/components/voice-check-modal.wxss

@@ -17,44 +17,91 @@
   border-radius: 20rpx;
   padding: 40rpx;
   text-align: center;
+  box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.2);
 }
 .modal-title.data-v-ce199c44 {
-  font-size: 32rpx;
+  font-size: 36rpx;
   font-weight: bold;
   margin-bottom: 20rpx;
+  color: #0039b3;
 }
 .modal-text.data-v-ce199c44 {
-  font-size: 28rpx;
+  font-size: 30rpx;
   color: #666;
   margin-bottom: 30rpx;
 }
 .wave-container.data-v-ce199c44 {
-  height: 160rpx;
+  height: 200rpx;
   display: flex;
   align-items: center;
   justify-content: center;
-  gap: 6rpx;
+  gap: 4rpx; /* 减小波形条之间的间距 */
   margin: 30rpx 0;
+  background: #f8f9fa;
+  border-radius: 15rpx;
+  position: relative;
+  overflow: hidden;
+  border: 1rpx solid #eee;
 }
 .wave-bar.data-v-ce199c44 {
-  width: 8rpx;
-  background: #0039b3;
-  border-radius: 4rpx;
-  transition: height 0.1s ease;
+  width: 6rpx; /* 减小波形条的宽度 */
+  background: linear-gradient(to top, #1a73e8, #0039b3);
+  border-radius: 3rpx;
+  transition: height 0.1s ease-out; /* 加快过渡动画速度 */
+}
+.timer.data-v-ce199c44 {
+  position: absolute;
+  top: 15rpx;
+  right: 15rpx;
+  background: rgba(0, 57, 179, 0.1);
+  color: #0039b3;
+  padding: 5rpx 15rpx;
+  border-radius: 20rpx;
+  font-size: 26rpx;
+  font-weight: 500;
 }
 .read-text.data-v-ce199c44 {
-  background: #f5f5f5;
+  background: #f8f9fa;
   padding: 30rpx;
-  border-radius: 10rpx;
-  font-size: 30rpx;
+  border-radius: 15rpx;
+  font-size: 32rpx;
   margin: 30rpx 0;
+  color: #333;
+  line-height: 1.6;
+  border: 1rpx solid #eee;
 }
 .confirm-btn.data-v-ce199c44 {
   width: 80%;
   height: 80rpx;
   line-height: 80rpx;
-  background: #0039b3;
+  background: linear-gradient(90deg, #0039b3, #1a73e8);
   color: #fff;
   border-radius: 40rpx;
   margin-top: 30rpx;
+  font-size: 32rpx;
+  font-weight: 500;
+  box-shadow: 0 5rpx 15rpx rgba(0, 57, 179, 0.3);
+}
+.status-message.data-v-ce199c44 {
+  font-size: 28rpx;
+  color: #666;
+  margin-top: 30rpx;
+  height: 40rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+/* 添加状态消息的动画效果 */
+@keyframes blink-ce199c44 {
+0% { opacity: 1;
+}
+50% { opacity: 0.5;
+}
+100% { opacity: 1;
+}
+}
+.status-message.warning.data-v-ce199c44 {
+  color: #ff9800;
+  animation: blink-ce199c44 1.5s infinite;
 }

+ 52 - 51
unpackage/dist/dev/mp-weixin/pages/identity-verify/identity-verify.js

@@ -260,14 +260,14 @@ const _sfc_main = {
       if (this.screenCaptureCount === 1) {
         common_vendor.index.showModal({
           title: "Warning",
-          content: "Screen capture detected. If you capture the screen again, your interview will be invalid.",
+          content: "检测到屏幕截图。如果你再次捕捉屏幕,你的面试将无效.",
           showCancel: false,
           confirmText: "OK"
         });
       } else if (this.screenCaptureCount >= 2) {
         common_vendor.index.showModal({
           title: "Interview Invalid",
-          content: "Your interview has been invalidated due to multiple screen captures. Please contact the recruiter for assistance.",
+          content: "由于多次屏幕截图,您的面试已无效。请联系招聘人员寻求帮助.",
           showCancel: false,
           confirmText: "OK",
           success: () => {
@@ -278,6 +278,7 @@ const _sfc_main = {
     });
     const systemInfo = common_vendor.index.getSystemInfoSync();
     const isMiniProgram = systemInfo.uniPlatform && systemInfo.uniPlatform.startsWith("mp-");
+    console.log("isMiniProgram", isMiniProgram);
     if (isMiniProgram) {
       this.initPersonDetectionWebSocket();
     }
@@ -3013,10 +3014,12 @@ const _sfc_main = {
       }
     },
     handlePersonDetectionResult(data) {
-      if (data.data.identity.status !== "identity_verified") {
+      console.log("data", data);
+      if (data.data.identity.status == "no_face") {
         this.showPageWarning = true;
         common_vendor.index.showToast({
-          title: data.data.identity.message,
+          /* data.data.identity.message */
+          title: "请保持面部完整出现在镜头内",
           icon: "none",
           duration: 3e3
         });
@@ -3213,53 +3216,51 @@ const _sfc_main = {
     // 处理视频完成
     handleVideoCompletion(jobId) {
       this.isVideoSwitching = false;
-      setTimeout(() => {
-        if (jobId === 9) {
-          common_vendor.index.navigateTo({
-            url: "/pages/interview-question/interview-question",
-            success: () => {
-              console.log("成功跳转到interview-question页面");
-            },
-            fail: (err) => {
-              console.error("跳转到interview-question页面失败:", err);
-              common_vendor.index.redirectTo({
-                url: "/pages/interview-question/interview-question",
-                fail: (redirectErr) => {
-                  console.error("重定向到interview-question页面也失败:", redirectErr);
-                  common_vendor.index.navigateBack({
-                    delta: 1
-                  });
-                }
-              });
-            }
-          });
-        } else {
-          common_vendor.index.navigateTo({
-            url: "/pages/camera/camera",
-            success: () => {
-              console.log("成功跳转到camera页面");
-            },
-            fail: (err) => {
-              console.error("跳转到camera页面失败:", err);
-              common_vendor.index.redirectTo({
-                url: "/pages/camera/camera",
-                fail: (redirectErr) => {
-                  console.error("重定向到camera页面也失败:", redirectErr);
-                  common_vendor.index.switchTab({
-                    url: "/pages/camera/camera",
-                    fail: (switchErr) => {
-                      console.error("所有跳转方式都失败:", switchErr);
-                      common_vendor.index.navigateBack({
-                        delta: 1
-                      });
-                    }
-                  });
-                }
-              });
-            }
-          });
-        }
-      }, 200);
+      if (jobId === 9) {
+        common_vendor.index.navigateTo({
+          url: "/pages/interview-question/interview-question",
+          success: () => {
+            console.log("成功跳转到interview-question页面");
+          },
+          fail: (err) => {
+            console.error("跳转到interview-question页面失败:", err);
+            common_vendor.index.redirectTo({
+              url: "/pages/interview-question/interview-question",
+              fail: (redirectErr) => {
+                console.error("重定向到interview-question页面也失败:", redirectErr);
+                common_vendor.index.navigateBack({
+                  delta: 1
+                });
+              }
+            });
+          }
+        });
+      } else {
+        common_vendor.index.navigateTo({
+          url: "/pages/camera/camera",
+          success: () => {
+            console.log("成功跳转到camera页面");
+          },
+          fail: (err) => {
+            console.error("跳转到camera页面失败:", err);
+            common_vendor.index.redirectTo({
+              url: "/pages/camera/camera",
+              fail: (redirectErr) => {
+                console.error("重定向到camera页面也失败:", redirectErr);
+                common_vendor.index.switchTab({
+                  url: "/pages/camera/camera",
+                  fail: (switchErr) => {
+                    console.error("所有跳转方式都失败:", switchErr);
+                    common_vendor.index.navigateBack({
+                      delta: 1
+                    });
+                  }
+                });
+              }
+            });
+          }
+        });
+      }
     }
   },
   computed: {

+ 54 - 6
unpackage/dist/dev/mp-weixin/pages/index/index.js

@@ -210,13 +210,61 @@ const _sfc_main = {
           if (res.statusCode === 200) {
             if (res.data.code === 2e3) {
               common_vendor.index.setStorageSync("configData", JSON.stringify(res.data.data));
-              common_vendor.index.navigateTo({
-                url: "/pages/Personal/Personal",
+              const openid = JSON.parse(common_vendor.index.getStorageSync("userInfo")).openid;
+              common_vendor.index.request({
+                url: `${common_config.apiBaseUrl}/api/wechat/user/get_full_info?tenant_id=1&openid=${openid}`,
+                method: "GET",
+                success: (infoRes) => {
+                  if (infoRes.statusCode === 200 && infoRes.data && infoRes.data.data && infoRes.data.data.profile) {
+                    const resumeUrl = infoRes.data.data.profile.resume_url || "";
+                    if (!resumeUrl) {
+                      common_vendor.index.navigateTo({
+                        url: "/pages/uploadResume/uploadResume",
+                        // 假设跳转到上传简历页面
+                        fail: (err) => {
+                          console.error("页面跳转失败:", err);
+                          common_vendor.index.showToast({
+                            title: "页面跳转失败",
+                            icon: "none"
+                          });
+                        }
+                      });
+                    } else {
+                      common_vendor.index.navigateTo({
+                        url: "/pages/Personal/Personal",
+                        fail: (err) => {
+                          console.error("页面跳转失败:", err);
+                          common_vendor.index.showToast({
+                            title: "页面跳转失败",
+                            icon: "none"
+                          });
+                        }
+                      });
+                    }
+                  } else {
+                    common_vendor.index.navigateTo({
+                      url: "/pages/Personal/Personal",
+                      fail: (err) => {
+                        console.error("页面跳转失败:", err);
+                        common_vendor.index.showToast({
+                          title: "页面跳转失败",
+                          icon: "none"
+                        });
+                      }
+                    });
+                  }
+                },
                 fail: (err) => {
-                  console.error("页面跳转失败:", err);
-                  common_vendor.index.showToast({
-                    title: "页面跳转失败",
-                    icon: "none"
+                  console.error("获取用户信息失败:", err);
+                  common_vendor.index.navigateTo({
+                    url: "/pages/Personal/Personal",
+                    fail: (err2) => {
+                      console.error("页面跳转失败:", err2);
+                      common_vendor.index.showToast({
+                        title: "页面跳转失败",
+                        icon: "none"
+                      });
+                    }
                   });
                 }
               });

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

@@ -67,6 +67,9 @@ const _sfc_main = {
     };
   },
   computed: {
+    allPhotosCompleted() {
+      return this.photos.every((photo) => photo !== null);
+    },
     canGoNext() {
       if (this.currentStep === 5) {
         return this.photos.every((photo) => photo !== null);
@@ -79,6 +82,7 @@ const _sfc_main = {
       scope: "scope.camera",
       success: () => {
         console.log("相机权限已授权");
+        this.takePhoto();
       },
       fail: () => {
         common_vendor.index.showModal({
@@ -98,8 +102,16 @@ const _sfc_main = {
   },
   methods: {
     takePhoto() {
+      const isRetake = this.photos[this.currentStep] !== null;
       this.$set(this.photos, this.currentStep, null);
       this.$set(this.photoLinks, this.currentStep, null);
+      if (isRetake) {
+        common_vendor.index.showToast({
+          title: "重新拍摄第" + (this.currentStep + 1) + "张照片",
+          icon: "none",
+          duration: 1500
+        });
+      }
       if (this.isH5) {
         this.startCamera();
       } else {
@@ -121,6 +133,7 @@ const _sfc_main = {
       common_vendor.index.showLoading({
         title: "上传中..."
       });
+      const currentIndex = this.currentStep;
       const userInfo = common_vendor.index.getStorageSync("userInfo");
       userInfo ? JSON.parse(userInfo).openid || "" : "";
       const tenant_id = common_vendor.index.getStorageSync("tenant_id") || "1";
@@ -170,9 +183,20 @@ const _sfc_main = {
             icon: "success",
             duration: 1500,
             success: () => {
-              setTimeout(() => {
-                this.autoNextStep();
-              }, 1500);
+              const isRetake = this.photos.filter((photo) => photo !== null).length > currentIndex + 1;
+              if (!isRetake) {
+                if (currentIndex === 5) {
+                  common_vendor.index.showToast({
+                    title: "最后一张照片已完成,请点击完成按钮提交",
+                    icon: "success",
+                    duration: 2e3
+                  });
+                } else {
+                  setTimeout(() => {
+                    this.autoNextStep();
+                  }, 1500);
+                }
+              }
             }
           });
         }).catch((error) => {
@@ -225,9 +249,20 @@ const _sfc_main = {
               icon: "success",
               duration: 1500,
               success: () => {
-                setTimeout(() => {
-                  this.autoNextStep();
-                }, 1500);
+                const isRetake = this.photos.filter((photo) => photo !== null).length > currentIndex + 1;
+                if (!isRetake) {
+                  if (currentIndex === 5) {
+                    common_vendor.index.showToast({
+                      title: "最后一张照片已完成,请点击完成按钮提交",
+                      icon: "success",
+                      duration: 2e3
+                    });
+                  } else {
+                    setTimeout(() => {
+                      this.autoNextStep();
+                    }, 1500);
+                  }
+                }
               }
             });
           },
@@ -251,22 +286,36 @@ const _sfc_main = {
       }
     },
     nextStep() {
-      if (this.currentStep < 5) {
-        return;
-      } else if (this.currentStep === 5 && this.photos[5]) {
+      if (this.allPhotosCompleted) {
         this.submitAllPhotos();
+        return;
+      }
+      if (this.currentStep < 5) {
+        this.currentStep++;
       }
     },
     previewPhoto(index) {
       if (this.photos[index]) {
-        common_vendor.index.previewImage({
-          urls: [this.photos[index]],
-          current: this.photos[index]
+        this.currentStep = index;
+        common_vendor.index.showActionSheet({
+          itemList: ["在预览区查看", "重新拍摄此照片"],
+          success: (res) => {
+            if (res.tapIndex === 0) {
+              common_vendor.index.previewImage({
+                urls: [this.photos[index]],
+                current: this.photos[index]
+              });
+            } else if (res.tapIndex === 1) {
+              this.takePhoto();
+            }
+          }
         });
+      } else {
+        this.currentStep = index;
       }
     },
     submitAllPhotos() {
-      if (this.photos.every((photo) => photo !== null)) {
+      if (this.allPhotosCompleted) {
         common_vendor.index.showLoading({
           title: "处理中..."
         });
@@ -507,13 +556,18 @@ const _sfc_main = {
     },
     // 添加自动进入下一步的方法
     autoNextStep() {
+      const allPhotosCompleted = this.photos.every((photo) => photo !== null);
+      if (allPhotosCompleted) {
+        common_vendor.index.showToast({
+          title: "所有照片已完成,请点击完成按钮提交",
+          icon: "success",
+          duration: 2e3
+        });
+        return;
+      }
       if (this.currentStep < 5) {
         this.currentStep++;
-        if (this.currentStep < 6) {
-          this.takePhoto();
-        }
-      } else if (this.currentStep === 5 && this.photos[5]) {
-        this.submitAllPhotos();
+        this.takePhoto();
       }
     }
   }
@@ -541,13 +595,16 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
         g: photo ? 1 : ""
       });
     }),
-    f: common_vendor.t($data.photos[$data.currentStep] ? "重新拍摄" : "拍摄照片"),
-    g: common_vendor.o((...args) => $options.takePhoto && $options.takePhoto(...args)),
-    h: $data.currentStep === 0,
-    i: common_vendor.o((...args) => $options.prevStep && $options.prevStep(...args)),
-    j: common_vendor.t($data.currentStep === 5 && $data.photos[5] ? "完成" : "下一步"),
+    f: $options.allPhotosCompleted
+  }, $options.allPhotosCompleted ? common_vendor.e({
+    g: common_vendor.t($data.photos[$data.currentStep] ? "重新拍摄" : "拍摄照片"),
+    h: common_vendor.o((...args) => $options.takePhoto && $options.takePhoto(...args)),
+    i: $options.allPhotosCompleted
+  }, $options.allPhotosCompleted ? {
+    j: common_vendor.t($options.allPhotosCompleted ? "完成" : "下一步"),
     k: !$options.canGoNext,
-    l: common_vendor.o((...args) => $options.nextStep && $options.nextStep(...args)),
+    l: common_vendor.o((...args) => $options.nextStep && $options.nextStep(...args))
+  } : {}) : {}, {
     m: !$data.isH5 && $data.showCamera
   }, !$data.isH5 && $data.showCamera ? {
     n: common_vendor.t($data.gestureHints[$data.currentStep]),
@@ -557,19 +614,18 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
     r: $data.guideIcon[$data.currentStep],
     s: common_vendor.s($data.currentStep >= 3 ? "left:30rpx" : "right:30rpx"),
     t: $data.gestureOverlays[$data.currentStep],
-    v: common_vendor.o((...args) => $options.capturePhotoMP && $options.capturePhotoMP(...args)),
-    w: common_vendor.o((...args) => $options.cancelCamera && $options.cancelCamera(...args))
+    v: common_vendor.o((...args) => $options.capturePhotoMP && $options.capturePhotoMP(...args))
   } : {}, {
-    x: $data.isH5
+    w: $data.isH5
   }, $data.isH5 ? {
-    y: $data.guideIcon[$data.currentStep],
-    z: common_vendor.s($data.currentStep >= 3 ? "left:30rpx" : "right:30rpx"),
-    A: $data.gestureOverlays[$data.currentStep],
-    B: common_vendor.t($data.gestureHints[$data.currentStep]),
-    C: common_vendor.o((...args) => $options.capturePhoto && $options.capturePhoto(...args)),
-    D: common_vendor.o((...args) => $options.switchCamera && $options.switchCamera(...args)),
-    E: common_vendor.o((...args) => $options.cancelCamera && $options.cancelCamera(...args)),
-    F: $data.showCamera
+    x: $data.guideIcon[$data.currentStep],
+    y: common_vendor.s($data.currentStep >= 3 ? "left:30rpx" : "right:30rpx"),
+    z: $data.gestureOverlays[$data.currentStep],
+    A: common_vendor.t($data.gestureHints[$data.currentStep]),
+    B: common_vendor.o((...args) => $options.capturePhoto && $options.capturePhoto(...args)),
+    C: common_vendor.o((...args) => $options.switchCamera && $options.switchCamera(...args)),
+    D: common_vendor.o((...args) => $options.cancelCamera && $options.cancelCamera(...args)),
+    E: $data.showCamera
   } : {});
 }
 const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);

Fișier diff suprimat deoarece este prea mare
+ 0 - 0
unpackage/dist/dev/mp-weixin/pages/interview/interview.wxml


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

@@ -99,7 +99,7 @@
 	justify-content: space-between;
 }
 .nav-btn {
-	width: 48%;
+	width: 100%;
 	height: 80rpx;
 	line-height: 80rpx;
 	text-align: center;

+ 1 - 1
unpackage/dist/dev/mp-weixin/pages/job-detail/job-detail.js

@@ -241,7 +241,7 @@ const _sfc_main = {
           addressStr = location;
         }
         common_vendor.index.request({
-          url: `https://apis.map.qq.com/ws/geocoder/v1/?address=${encodeURIComponent(addressStr)}&key=WJLBZ-SMQYZ-3RNX5-7J4LI-XTZD6-7IBZR`,
+          url: `https://apis.map.qq.com/ws/geocoder/v1/?address=${encodeURIComponent(addressStr)}&key=ZS4BZ-NKAA7-4VLXR-PHVI4-HAGPH-Z4FJ3`,
           // 需要替换为实际的地图Key
           success: (res) => {
             if (res.data.status === 0) {

+ 40 - 7
unpackage/dist/dev/mp-weixin/pages/uploadResume/uploadResume.js

@@ -7,10 +7,33 @@ const _sfc_main = {
       fileType: ["pdf", "doc", "docx"],
       maxSize: 30 * 1024 * 1024,
       // 30MB in bytes
-      uploadedFile: null
+      uploadedFile: null,
+      showSkipButton: false
+      // 控制跳过按钮显示
     };
   },
+  onLoad(options) {
+    const pages = getCurrentPages();
+    const prevPage = pages[pages.length - 2];
+    if (prevPage && prevPage.route === "pages/index/index") {
+      this.showSkipButton = true;
+    }
+  },
   methods: {
+    // 处理跳过按钮点击
+    handleSkip() {
+      common_vendor.index.showModal({
+        title: "提示",
+        content: "确定要跳过上传简历环节吗?",
+        success: (res) => {
+          if (res.confirm) {
+            common_vendor.index.navigateTo({
+              url: "/pages/Personal/Personal"
+            });
+          }
+        }
+      });
+    },
     // 调用简历解析接口
     /* async parseResume(fileUrl) {
     	try {
@@ -166,7 +189,13 @@ const _sfc_main = {
                     common_vendor.index.hideLoading();
                     common_vendor.index.showToast({
                       title: "处理完成",
-                      icon: "success"
+                      icon: "success",
+                      duration: 2e3,
+                      success: () => {
+                        common_vendor.index.navigateTo({
+                          url: "/pages/Personal/Personal"
+                        });
+                      }
                     });
                   }
                 }, 1e3);
@@ -211,12 +240,16 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
     a: common_vendor.t($data.uploadedFile ? "已上传文件" : "选择并上传文件"),
     b: common_vendor.o((...args) => $options.chooseFile && $options.chooseFile(...args)),
     c: !!$data.uploadedFile,
-    d: $data.uploadedFile
+    d: $data.showSkipButton
+  }, $data.showSkipButton ? {
+    e: common_vendor.o((...args) => $options.handleSkip && $options.handleSkip(...args))
+  } : {}, {
+    f: $data.uploadedFile
   }, $data.uploadedFile ? {
-    e: common_vendor.o((...args) => $options.clearFile && $options.clearFile(...args)),
-    f: common_vendor.t($data.uploadedFile.name),
-    g: common_vendor.t($options.formatFileSize($data.uploadedFile.size)),
-    h: common_vendor.t($data.uploadedFile.uploadTime)
+    g: common_vendor.o((...args) => $options.clearFile && $options.clearFile(...args)),
+    h: common_vendor.t($data.uploadedFile.name),
+    i: common_vendor.t($options.formatFileSize($data.uploadedFile.size)),
+    j: common_vendor.t($data.uploadedFile.uploadTime)
   } : {});
 }
 const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);

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

@@ -1 +1 @@
-<view class="upload-container"><view class="header-notice"><image style="width:30rpx;height:30rpx" src="https://data.qicai321.com/minlong/57d03f34-47de-43fe-8730-b764ed8bdda2.png" mode="aspectFit"></image>请上传简历文件,格式支持pdf及doc格式 </view><view class="illustration"><image class="img" src="https://data.qicai321.com/minlong/09d4daa6-6507-4133-81ff-68e47b0259e5.png" mode="aspectFit"></image></view><button class="upload-btn" bindtap="{{b}}" disabled="{{c}}">{{a}}</button><view wx:if="{{d}}" class="file-info"><view class="file-info-header"><view class="file-info-title">已上传文件</view><view class="file-delete" bindtap="{{e}}"><image class="delete-icon" src="https://data.qicai321.com/minlong/delete.png" mode="aspectFit"></image></view></view><view class="file-info-content"><view class="file-name">{{f}}</view><view class="file-size">{{g}}</view><view class="file-time">上传时间:{{h}}</view></view></view><view class="instructions"><view class="title">使用说明及注意事项</view><view class="instruction-list"><view class="instruction-item">1. 点击【选择并上传文件】按钮,上传简历;</view><view class="instruction-item">2. 请在本地选择简历文件,文件小于30mb;</view><view class="instruction-item">3. 支持pdf及doc格式。</view></view><view class="privacy-notice"> * 我们将对您的隐私保护,所内容仅用于评估岗位的匹配度。 </view></view></view>
+<view class="upload-container"><view class="header-notice"><image style="width:30rpx;height:30rpx" src="https://data.qicai321.com/minlong/57d03f34-47de-43fe-8730-b764ed8bdda2.png" mode="aspectFit"></image>请上传简历文件,格式支持pdf及doc格式 </view><view class="illustration"><image class="img" src="https://data.qicai321.com/minlong/09d4daa6-6507-4133-81ff-68e47b0259e5.png" mode="aspectFit"></image></view><button class="upload-btn" bindtap="{{b}}" disabled="{{c}}">{{a}}</button><button wx:if="{{d}}" class="skip-btn" bindtap="{{e}}">跳过,直接进入下一环节</button><view wx:if="{{f}}" class="file-info"><view class="file-info-header"><view class="file-info-title">已上传文件</view><view class="file-delete" bindtap="{{g}}"><image class="delete-icon" src="https://data.qicai321.com/minlong/delete.png" mode="aspectFit"></image></view></view><view class="file-info-content"><view class="file-name">{{h}}</view><view class="file-size">{{i}}</view><view class="file-time">上传时间:{{j}}</view></view></view><view class="instructions"><view class="title">使用说明及注意事项</view><view class="instruction-list"><view class="instruction-item">1. 点击【选择并上传文件】按钮,上传简历;</view><view class="instruction-item">2. 请在本地选择简历文件,文件小于30mb;</view><view class="instruction-item">3. 支持pdf及doc格式。</view></view><view class="privacy-notice"> * 我们将对您的隐私保护,所内容仅用于评估岗位的匹配度。 </view></view></view>

+ 15 - 0
unpackage/dist/dev/mp-weixin/pages/uploadResume/uploadResume.wxss

@@ -125,4 +125,19 @@
 .upload-container .upload-btn[disabled] {
   background-color: #cccccc;
   color: #ffffff;
+}
+.upload-container .skip-btn {
+  width: 90%;
+  height: 88rpx;
+  line-height: 88rpx;
+  background-color: #ffffff;
+  color: #666666;
+  border-radius: 8rpx;
+  font-size: 32rpx;
+  margin: 20rpx auto;
+  text-align: center;
+  border: 1px solid #dddddd;
+}
+.upload-container .skip-btn::after {
+  border: none;
 }

+ 2 - 2
unpackage/dist/dev/mp-weixin/project.config.json

@@ -8,7 +8,7 @@
     "urlCheck": false,
     "es6": true,
     "postcss": false,
-    "minified": true,
+    "minified": false,
     "newFeature": true,
     "bigPackageSizeSupport": true,
     "babelSetting": {
@@ -18,7 +18,7 @@
     }
   },
   "compileType": "miniprogram",
-  "libVersion": "3.8.8",
+  "libVersion": "3.8.11",
   "appid": "wxc9655eeaa3223b75",
   "projectname": "interview_uni",
   "condition": {

+ 7 - 0
unpackage/dist/dev/mp-weixin/project.private.config.json

@@ -7,6 +7,13 @@
   "condition": {
     "miniprogram": {
       "list": [
+        {
+          "name": "pages/interview-notice",
+          "pathName": "pages/interview-notice/interview-notice",
+          "query": "",
+          "launchMode": "default",
+          "scene": null
+        },
         {
           "name": "pages/preview/preview",
           "pathName": "pages/preview/preview",

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff