yangg před 1 dnem
rodič
revize
9f03176dab

+ 65 - 42
pages/Personal/Personal.vue

@@ -984,6 +984,16 @@ import { apiBaseUrl } from '@/common/config.js';
 				const fields = ['start_date', 'end_date', 'school_name', 'major', 'degree'];
 				return fields.some(field => this.safeEducationFieldsConfig[field]?.visible !== false);
 			},
+			// 判断是否显示家庭成员步骤
+			shouldShowFamilyStep() {
+				// 如果没有配置数据,默认显示
+				if (Object.keys(this.safeFamilyFieldsConfig).length === 0) {
+					return true;
+				}
+				// 检查所有家庭成员字段是否都被隐藏
+				const fields = ['relation', 'name', 'workplace', 'position', 'phone'];
+				return fields.some(field => this.safeFamilyFieldsConfig[field]?.visible !== false);
+			},
 		},
 		methods: {
 			 // 输入时限制小数位数
@@ -1817,29 +1827,35 @@ import { apiBaseUrl } from '@/common/config.js';
 			prevStep() {
 				const prevIndex = this.currentStepIndex - 1;
 				if (prevIndex >= 0) {
-					// 如果上一步是专业技能步骤且不需要显示
-					if (this.steps[prevIndex].id === 6 && !this.shouldShowSkillsStep) {
-						// 检查上上步是否是教育经历步骤
-						const skipIndex = prevIndex - 1;
-						if (skipIndex >= 0) {
-							// 如果上上步是教育经历步骤且也不需要显示,则继续跳过
-							if (this.steps[skipIndex].id === 5 && !this.shouldShowEducationStep) {
-								const skipIndex2 = skipIndex - 1;
-								if (skipIndex2 >= 0) {
-									this.currentStep = this.steps[skipIndex2].id;
-								}
-							} else {
-								this.currentStep = this.steps[skipIndex].id;
-							}
+					let targetIndex = prevIndex;
+					
+					// 检查上一步是否需要跳过
+					while (targetIndex >= 0) {
+						const stepId = this.steps[targetIndex].id;
+						
+						// 如果是专业技能步骤且不需要显示,则跳过
+						if (stepId === 6 && !this.shouldShowSkillsStep) {
+							targetIndex--;
+							continue;
+						}
+						// 如果是教育经历步骤且不需要显示,则跳过
+						if (stepId === 5 && !this.shouldShowEducationStep) {
+							targetIndex--;
+							continue;
 						}
-					} else if (this.steps[prevIndex].id === 5 && !this.shouldShowEducationStep) {
-						// 如果上一步是教育经历步骤且不需要显示,则跳过
-						const skipIndex = prevIndex - 1;
-						if (skipIndex >= 0) {
-							this.currentStep = this.steps[skipIndex].id;
+						// 如果是家庭成员步骤且不需要显示,则跳过
+						if (stepId === 3 && !this.shouldShowFamilyStep) {
+							targetIndex--;
+							continue;
 						}
-					} else {
-						this.currentStep = this.steps[prevIndex].id;
+						
+						// 找到有效步骤,跳出循环
+						break;
+					}
+					
+					// 如果找到有效步骤,跳转到该步骤
+					if (targetIndex >= 0) {
+						this.currentStep = this.steps[targetIndex].id;
 					}
 					
 					// 滚动到页面顶部
@@ -1863,30 +1879,37 @@ import { apiBaseUrl } from '@/common/config.js';
 				
 				const nextIndex = this.currentStepIndex + 1;
 				if (nextIndex < this.steps.length) {
-					// 如果下一步是教育经历步骤(currentStep === 5),且不需要显示,则跳过
-					if (this.steps[nextIndex].id === 5 && !this.shouldShowEducationStep) {
-						// 跳到下下一步
-						const skipIndex = nextIndex + 1;
-						if (skipIndex < this.steps.length) {
-							// 如果下下步是专业技能步骤且也不需要显示,则继续跳过
-							if (this.steps[skipIndex].id === 6 && !this.shouldShowSkillsStep) {
-								const skipIndex2 = skipIndex + 1;
-								if (skipIndex2 < this.steps.length) {
-									this.currentStep = this.steps[skipIndex2].id;
-								}
-							} else {
-								this.currentStep = this.steps[skipIndex].id;
-							}
+					let targetIndex = nextIndex;
+					
+					// 检查下一步是否需要跳过
+					while (targetIndex < this.steps.length) {
+						const stepId = this.steps[targetIndex].id;
+						
+						// 如果是家庭成员步骤且不需要显示,则跳过
+						if (stepId === 3 && !this.shouldShowFamilyStep) {
+							targetIndex++;
+							continue;
+						}
+						// 如果是教育经历步骤且不需要显示,则跳过
+						if (stepId === 5 && !this.shouldShowEducationStep) {
+							targetIndex++;
+							continue;
 						}
-					} else if (this.steps[nextIndex].id === 6 && !this.shouldShowSkillsStep) {
-						// 跳到下下一步
-						const skipIndex = nextIndex + 1;
-						if (skipIndex < this.steps.length) {
-							this.currentStep = this.steps[skipIndex].id;
+						// 如果是专业技能步骤且不需要显示,则跳过
+						if (stepId === 6 && !this.shouldShowSkillsStep) {
+							targetIndex++;
+							continue;
 						}
-					} else {
-						this.currentStep = this.steps[nextIndex].id;
+						
+						// 找到有效步骤,跳出循环
+						break;
 					}
+					
+					// 如果找到有效步骤,跳转到该步骤
+					if (targetIndex < this.steps.length) {
+						this.currentStep = this.steps[targetIndex].id;
+					}
+					
 					// 滚动到页面顶部
 					uni.pageScrollTo({
 						scrollTop: 0,

+ 62 - 12
pages/identity-verify/identity-verify.vue

@@ -3524,17 +3524,23 @@ export default {
         if (configStr) {
           try {
             const configData = JSON.parse(configStr);
-            if (configData && configData.digital_human_opening_speech && Array.isArray(configData.digital_human_opening_speech)) {
-              // 将配置数据转换为字幕格式
-              openingSpeech = configData.digital_human_opening_speech.map((item, index, arr) => {
-                const startTime = index === 0 ? 0 : arr[index - 1].end_time || index * 5;
-                const endTime = item.end_time || (index + 1) * 5;
-                return {
-                  startTime,
-                  endTime,
-                  text: item.content
-                };
-              });
+            if (configData && configData.digital_human_opening_speech) {
+              // 检查是否为数组格式
+              if (Array.isArray(configData.digital_human_opening_speech)) {
+                // 将配置数据转换为字幕格式
+                openingSpeech = configData.digital_human_opening_speech.map((item, index, arr) => {
+                  const startTime = index === 0 ? 0 : arr[index - 1].end_time || index * 5;
+                  const endTime = item.end_time || (index + 1) * 5;
+                  return {
+                    startTime,
+                    endTime,
+                    text: item.content
+                  };
+                });
+              } else if (typeof configData.digital_human_opening_speech === 'string') {
+                // 使用新的方法处理字符串类型的开场白
+                openingSpeech = this.convertStringToSubtitles(configData.digital_human_opening_speech);
+              }
             }
           } catch (error) {
             console.error('解析configData失败:', error);
@@ -3559,7 +3565,7 @@ export default {
           text: '以免影响本次面试的结果。如果你在面试过程中遇到问题,请与我们的招聘人员联系。'
         }
       ];
-      
+      console.log('处理后的字幕数据:', this.subtitles);
       // 按照sequence_number排序问题
       const sortedQuestions = [...this.questions].sort((a, b) => a.sequence_number - b.sequence_number);
       
@@ -3595,6 +3601,50 @@ export default {
       }
     },
 
+    // 将字符串类型的文本转换为字幕格式
+    convertStringToSubtitles(text) {
+      if (!text || typeof text !== 'string') {
+        return [];
+      }
+      
+      // 按照标点符号分割文本,保留标点符号
+      const sentences = text.split(/([。!?!?])/)
+        .filter(part => part.trim() !== '')
+        .reduce((acc, part, index, arr) => {
+          if (index % 2 === 0) {
+            // 文本部分
+            const nextPart = arr[index + 1] || '';
+            acc.push(part + nextPart);
+          }
+          return acc;
+        }, [])
+        .filter(sentence => sentence.trim().length > 0);
+      
+      // 如果没有明显的分句标点,按长度分割
+      if (sentences.length <= 1) {
+        const maxLength = 30; // 每段最大字符数
+        const textParts = [];
+        for (let i = 0; i < text.length; i += maxLength) {
+          textParts.push(text.substring(i, i + maxLength));
+        }
+        sentences.splice(0, sentences.length, ...textParts);
+      }
+      
+      // 为每个句子分配时间戳
+      let currentStartTime = 0;
+      return sentences.map((sentence) => {
+        const duration = Math.max(3, Math.ceil(sentence.length / 6)); // 根据文本长度计算时长
+        const startTime = currentStartTime;
+        const endTime = startTime + duration;
+        currentStartTime = endTime;
+        return {
+          startTime,
+          endTime,
+          text: sentence.trim()
+        };
+      });
+    },
+
     // 使用默认视频和字幕(作为备用)
     useDefaultVideosAndSubtitles() {
       console.log('使用默认视频和字幕');

+ 34 - 13
pages/interview-question/interview-question.vue

@@ -3302,25 +3302,46 @@ export default {
     playIOSMiniProgramAudio() {
       return new Promise((resolve, reject) => {
         try {
-        const audioContext = wx.createInnerAudioContext();
-          audioContext.src = this.aiVoiceUrl;
-          audioContext.autoplay = true;
-          
-        audioContext.onPlay(() => {
-          console.log('iOS小程序音频开始播放');
+          const bgAudio = uni.getBackgroundAudioManager();
+          // iOS 小程序需要设置 title 等元信息,否则可能不触发播放
+          bgAudio.title = '语音播报';
+          bgAudio.epname = 'AI Voice';
+          bgAudio.singer = 'Assistant';
+          // 可选封面,按需提供可访问的图片地址
+          // bgAudio.coverImgUrl = this.coverImgUrl || '';
+
+          // 先绑定事件再设置 src,防止错过 canplay 回调
+          bgAudio.onCanplay(() => {
+            try {
+              bgAudio.play();
+            } catch (e) {
+              console.warn('调用 play 失败,稍后重试', e);
+              setTimeout(() => {
+                try { bgAudio.play(); } catch (_) {}
+              }, 50);
+            }
           });
-          
-        audioContext.onEnded(() => {
+
+          bgAudio.onPlay(() => {
+            console.log('iOS小程序音频开始播放');
+          });
+
+          bgAudio.onEnded(() => {
             console.log('iOS小程序音频播放完成');
-            audioContext.destroy();
+            // BackgroundAudioManager 无 destroy 方法,使用 stop 即可
+            try { bgAudio.stop(); } catch (_) {}
+            this.navigateToNextPage()
             resolve();
           });
-          
-        audioContext.onError((err) => {
-          console.error('iOS小程序音频播放错误:', err);
-            audioContext.destroy();
+
+          bgAudio.onError((err) => {
+            console.error('iOS小程序音频播放错误:', err);
+            try { bgAudio.stop(); } catch (_) {}
             reject(err);
           });
+
+          bgAudio.autoplay = true; // 某些版本不生效,仍在 onCanplay 主动 play
+          bgAudio.src = this.aiVoiceUrl;
         } catch (error) {
           reject(error);
         }

+ 60 - 0
test_subtitle_conversion.js

@@ -0,0 +1,60 @@
+// 测试字符串转字幕功能
+function convertStringToSubtitles(text) {
+  if (!text || typeof text !== 'string') {
+    return [];
+  }
+  
+  // 按照标点符号分割文本,保留标点符号
+  const sentences = text.split(/([。!?!?])/)
+    .filter(part => part.trim() !== '')
+    .reduce((acc, part, index, arr) => {
+      if (index % 2 === 0) {
+        // 文本部分
+        const nextPart = arr[index + 1] || '';
+        acc.push(part + nextPart);
+      }
+      return acc;
+    }, [])
+    .filter(sentence => sentence.trim().length > 0);
+  
+  // 如果没有明显的分句标点,按长度分割
+  if (sentences.length <= 1) {
+    const maxLength = 30; // 每段最大字符数
+    const textParts = [];
+    for (let i = 0; i < text.length; i += maxLength) {
+      textParts.push(text.substring(i, i + maxLength));
+    }
+    sentences.splice(0, sentences.length, ...textParts);
+  }
+  
+  // 为每个句子分配时间戳
+  let currentStartTime = 0;
+  return sentences.map((sentence) => {
+    const duration = Math.max(3, Math.ceil(sentence.length / 8)); // 根据文本长度计算时长
+    const startTime = currentStartTime;
+    const endTime = startTime + duration;
+    currentStartTime = endTime;
+    return {
+      startTime,
+      endTime,
+      text: sentence.trim()
+    };
+  });
+}
+
+// 测试数据
+const testString = "你好1123,我是本次面试的面试官,欢迎参加本公司的线上面试!面试预计需要15分钟,请你提前安排在网络良好、光线亮度合适、且相对安静的环境参加这次面试以免影响本次面试的结果。如果你在面试过程中遇到问题,请与我们的招聘人员联系。";
+
+console.log('原始字符串:');
+console.log(testString);
+console.log('\n转换后的字幕格式:');
+
+const result = convertStringToSubtitles(testString);
+result.forEach((item, index) => {
+  console.log(`${index + 1}. 时间: ${item.startTime}s - ${item.endTime}s, 内容: "${item.text}"`);
+});
+
+console.log('\n期望的格式类似:');
+console.log('1. 时间: 0s - 5s, 内容: "你好1123,我是本次面试的面试官,欢迎参加本公司的线上面试!"');
+console.log('2. 时间: 5s - 13s, 内容: "面试预计需要15分钟,请你提前安排在网络良好、光线亮度合适、且相对安静的环境参加这次面试以免影响本次面试的结果。"');
+console.log('3. 时间: 13s - 20s, 内容: "如果你在面试过程中遇到问题,请与我们的招聘人员联系。"');

+ 45 - 34
unpackage/dist/dev/mp-weixin/pages/Personal/Personal.js

@@ -378,6 +378,17 @@ const _sfc_main = {
         var _a;
         return ((_a = this.safeEducationFieldsConfig[field]) == null ? void 0 : _a.visible) !== false;
       });
+    },
+    // 判断是否显示家庭成员步骤
+    shouldShowFamilyStep() {
+      if (Object.keys(this.safeFamilyFieldsConfig).length === 0) {
+        return true;
+      }
+      const fields = ["relation", "name", "workplace", "position", "phone"];
+      return fields.some((field) => {
+        var _a;
+        return ((_a = this.safeFamilyFieldsConfig[field]) == null ? void 0 : _a.visible) !== false;
+      });
     }
   },
   methods: {
@@ -1096,25 +1107,25 @@ const _sfc_main = {
     prevStep() {
       const prevIndex = this.currentStepIndex - 1;
       if (prevIndex >= 0) {
-        if (this.steps[prevIndex].id === 6 && !this.shouldShowSkillsStep) {
-          const skipIndex = prevIndex - 1;
-          if (skipIndex >= 0) {
-            if (this.steps[skipIndex].id === 5 && !this.shouldShowEducationStep) {
-              const skipIndex2 = skipIndex - 1;
-              if (skipIndex2 >= 0) {
-                this.currentStep = this.steps[skipIndex2].id;
-              }
-            } else {
-              this.currentStep = this.steps[skipIndex].id;
-            }
+        let targetIndex = prevIndex;
+        while (targetIndex >= 0) {
+          const stepId = this.steps[targetIndex].id;
+          if (stepId === 6 && !this.shouldShowSkillsStep) {
+            targetIndex--;
+            continue;
           }
-        } else if (this.steps[prevIndex].id === 5 && !this.shouldShowEducationStep) {
-          const skipIndex = prevIndex - 1;
-          if (skipIndex >= 0) {
-            this.currentStep = this.steps[skipIndex].id;
+          if (stepId === 5 && !this.shouldShowEducationStep) {
+            targetIndex--;
+            continue;
           }
-        } else {
-          this.currentStep = this.steps[prevIndex].id;
+          if (stepId === 3 && !this.shouldShowFamilyStep) {
+            targetIndex--;
+            continue;
+          }
+          break;
+        }
+        if (targetIndex >= 0) {
+          this.currentStep = this.steps[targetIndex].id;
         }
         common_vendor.index.pageScrollTo({
           scrollTop: 0,
@@ -1132,25 +1143,25 @@ const _sfc_main = {
       }
       const nextIndex = this.currentStepIndex + 1;
       if (nextIndex < this.steps.length) {
-        if (this.steps[nextIndex].id === 5 && !this.shouldShowEducationStep) {
-          const skipIndex = nextIndex + 1;
-          if (skipIndex < this.steps.length) {
-            if (this.steps[skipIndex].id === 6 && !this.shouldShowSkillsStep) {
-              const skipIndex2 = skipIndex + 1;
-              if (skipIndex2 < this.steps.length) {
-                this.currentStep = this.steps[skipIndex2].id;
-              }
-            } else {
-              this.currentStep = this.steps[skipIndex].id;
-            }
+        let targetIndex = nextIndex;
+        while (targetIndex < this.steps.length) {
+          const stepId = this.steps[targetIndex].id;
+          if (stepId === 3 && !this.shouldShowFamilyStep) {
+            targetIndex++;
+            continue;
+          }
+          if (stepId === 5 && !this.shouldShowEducationStep) {
+            targetIndex++;
+            continue;
           }
-        } else if (this.steps[nextIndex].id === 6 && !this.shouldShowSkillsStep) {
-          const skipIndex = nextIndex + 1;
-          if (skipIndex < this.steps.length) {
-            this.currentStep = this.steps[skipIndex].id;
+          if (stepId === 6 && !this.shouldShowSkillsStep) {
+            targetIndex++;
+            continue;
           }
-        } else {
-          this.currentStep = this.steps[nextIndex].id;
+          break;
+        }
+        if (targetIndex < this.steps.length) {
+          this.currentStep = this.steps[targetIndex].id;
         }
         common_vendor.index.pageScrollTo({
           scrollTop: 0,

+ 48 - 10
unpackage/dist/dev/mp-weixin/pages/identity-verify/identity-verify.js

@@ -2480,16 +2480,20 @@ const _sfc_main = {
       if (configStr) {
         try {
           const configData = JSON.parse(configStr);
-          if (configData && configData.digital_human_opening_speech && Array.isArray(configData.digital_human_opening_speech)) {
-            openingSpeech = configData.digital_human_opening_speech.map((item, index, arr) => {
-              const startTime = index === 0 ? 0 : arr[index - 1].end_time || index * 5;
-              const endTime = item.end_time || (index + 1) * 5;
-              return {
-                startTime,
-                endTime,
-                text: item.content
-              };
-            });
+          if (configData && configData.digital_human_opening_speech) {
+            if (Array.isArray(configData.digital_human_opening_speech)) {
+              openingSpeech = configData.digital_human_opening_speech.map((item, index, arr) => {
+                const startTime = index === 0 ? 0 : arr[index - 1].end_time || index * 5;
+                const endTime = item.end_time || (index + 1) * 5;
+                return {
+                  startTime,
+                  endTime,
+                  text: item.content
+                };
+              });
+            } else if (typeof configData.digital_human_opening_speech === "string") {
+              openingSpeech = this.convertStringToSubtitles(configData.digital_human_opening_speech);
+            }
           }
         } catch (error) {
           console.error("解析configData失败:", error);
@@ -2512,6 +2516,7 @@ const _sfc_main = {
           text: "以免影响本次面试的结果。如果你在面试过程中遇到问题,请与我们的招聘人员联系。"
         }
       ];
+      console.log("处理后的字幕数据:", this.subtitles);
       const sortedQuestions = [...this.questions].sort((a, b) => a.sequence_number - b.sequence_number);
       const questionsToUse = sortedQuestions.slice(0, 5);
       questionsToUse.forEach((question, index) => {
@@ -2535,6 +2540,39 @@ const _sfc_main = {
         console.log("处理后的字幕:", this.subtitles);
       }
     },
+    // 将字符串类型的文本转换为字幕格式
+    convertStringToSubtitles(text) {
+      if (!text || typeof text !== "string") {
+        return [];
+      }
+      const sentences = text.split(/([。!?!?])/).filter((part) => part.trim() !== "").reduce((acc, part, index, arr) => {
+        if (index % 2 === 0) {
+          const nextPart = arr[index + 1] || "";
+          acc.push(part + nextPart);
+        }
+        return acc;
+      }, []).filter((sentence) => sentence.trim().length > 0);
+      if (sentences.length <= 1) {
+        const maxLength = 30;
+        const textParts = [];
+        for (let i = 0; i < text.length; i += maxLength) {
+          textParts.push(text.substring(i, i + maxLength));
+        }
+        sentences.splice(0, sentences.length, ...textParts);
+      }
+      let currentStartTime = 0;
+      return sentences.map((sentence) => {
+        const duration = Math.max(3, Math.ceil(sentence.length / 6));
+        const startTime = currentStartTime;
+        const endTime = startTime + duration;
+        currentStartTime = endTime;
+        return {
+          startTime,
+          endTime,
+          text: sentence.trim()
+        };
+      });
+    },
     // 使用默认视频和字幕(作为备用)
     useDefaultVideosAndSubtitles() {
       console.log("使用默认视频和字幕");

+ 31 - 8
unpackage/dist/dev/mp-weixin/pages/interview-question/interview-question.js

@@ -2256,22 +2256,45 @@ const _sfc_main = {
     playIOSMiniProgramAudio() {
       return new Promise((resolve, reject) => {
         try {
-          const audioContext = common_vendor.wx$1.createInnerAudioContext();
-          audioContext.src = this.aiVoiceUrl;
-          audioContext.autoplay = true;
-          audioContext.onPlay(() => {
+          const bgAudio = common_vendor.index.getBackgroundAudioManager();
+          bgAudio.title = "语音播报";
+          bgAudio.epname = "AI Voice";
+          bgAudio.singer = "Assistant";
+          bgAudio.onCanplay(() => {
+            try {
+              bgAudio.play();
+            } catch (e) {
+              console.warn("调用 play 失败,稍后重试", e);
+              setTimeout(() => {
+                try {
+                  bgAudio.play();
+                } catch (_) {
+                }
+              }, 50);
+            }
+          });
+          bgAudio.onPlay(() => {
             console.log("iOS小程序音频开始播放");
           });
-          audioContext.onEnded(() => {
+          bgAudio.onEnded(() => {
             console.log("iOS小程序音频播放完成");
-            audioContext.destroy();
+            try {
+              bgAudio.stop();
+            } catch (_) {
+            }
+            this.navigateToNextPage();
             resolve();
           });
-          audioContext.onError((err) => {
+          bgAudio.onError((err) => {
             console.error("iOS小程序音频播放错误:", err);
-            audioContext.destroy();
+            try {
+              bgAudio.stop();
+            } catch (_) {
+            }
             reject(err);
           });
+          bgAudio.autoplay = true;
+          bgAudio.src = this.aiVoiceUrl;
         } catch (error) {
           reject(error);
         }