Ver código fonte

修改内容

yangg 3 dias atrás
pai
commit
6b1ef14bb9

+ 1 - 1
pages.json

@@ -91,7 +91,7 @@
 		{
 			"path": "pages/agreement/agreement",
 			"style": {
-				"navigationBarTitleText": "用户协议"
+				"navigationBarTitleText": "隐私协议"
 			}
 		},
 		{

+ 123 - 1
pages/agreement/agreement.vue

@@ -23,7 +23,7 @@
       </view>
       
       <!-- 使用v-else和v-html动态渲染协议内容 -->
-      <rich-text  :nodes="agreementContent"></rich-text>
+      <view v-else class="markdown-content" v-html="parsedContent"></view>
     </scroll-view>
   </view>
 </template>
@@ -41,10 +41,52 @@ export default {
       errorMsg: '加载失败,请重试'
     }
   },
+  computed: {
+    parsedContent() {
+      if (!this.agreementContent) return '';
+      return this.parseMarkdown(this.agreementContent);
+    }
+  },
   onLoad() {
     this.fetchAgreement();
   },
   methods: {
+    // 简单的 markdown 解析函数
+    parseMarkdown(markdown) {
+      if (!markdown) return '';
+      
+      let html = markdown
+        // 标题解析
+        .replace(/^### (.*$)/gim, '<h3>$1</h3>')
+        .replace(/^## (.*$)/gim, '<h2>$1</h2>')
+        .replace(/^# (.*$)/gim, '<h1>$1</h1>')
+        // 粗体
+        .replace(/\*\*(.*)\*\*/gim, '<strong>$1</strong>')
+        // 斜体
+        .replace(/\*(.*)\*/gim, '<em>$1</em>')
+        // 链接
+        .replace(/\[([^\]]*)\]\(([^\)]*)\)/gim, '<a href="$2">$1</a>')
+        // 代码块
+        .replace(/```(.*?)```/gims, '<pre><code>$1</code></pre>')
+        // 行内代码
+        .replace(/`([^`]*)`/gim, '<code>$1</code>')
+        // 列表项
+        .replace(/^\* (.*$)/gim, '<li>$1</li>')
+        .replace(/^\d+\. (.*$)/gim, '<li>$1</li>')
+        // 换行处理
+        .replace(/\n\n/gim, '</p><p>')
+        .replace(/\n/gim, '<br>');
+      
+      // 包装列表
+      html = html.replace(/(<li>.*<\/li>)/gim, '<ul>$1</ul>');
+      
+      // 包装段落
+      if (html && !html.startsWith('<h') && !html.startsWith('<ul') && !html.startsWith('<ol')) {
+        html = '<p>' + html + '</p>';
+      }
+      
+      return html;
+    },
     goBack() {
       uni.navigateBack();
     },
@@ -184,4 +226,84 @@ export default {
   border-radius: 8rpx;
   font-size: 28rpx;
 }
+
+/* Markdown 样式 */
+.markdown-content {
+  font-size: 28rpx;
+  line-height: 1.6;
+  color: #333;
+}
+
+.markdown-content h1 {
+  font-size: 36rpx;
+  font-weight: bold;
+  margin: 30rpx 0 20rpx 0;
+  color: #333;
+}
+
+.markdown-content h2 {
+  font-size: 32rpx;
+  font-weight: bold;
+  margin: 25rpx 0 15rpx 0;
+  color: #333;
+}
+
+.markdown-content h3 {
+  font-size: 30rpx;
+  font-weight: bold;
+  margin: 20rpx 0 10rpx 0;
+  color: #333;
+}
+
+.markdown-content p {
+  margin-bottom: 20rpx;
+  text-align: justify;
+}
+
+.markdown-content strong {
+  font-weight: bold;
+  color: #333;
+}
+
+.markdown-content em {
+  font-style: italic;
+}
+
+.markdown-content a {
+  color: #1890ff;
+  text-decoration: underline;
+}
+
+.markdown-content code {
+  background-color: #f5f5f5;
+  padding: 2rpx 8rpx;
+  border-radius: 4rpx;
+  font-family: 'Courier New', monospace;
+  font-size: 24rpx;
+}
+
+.markdown-content pre {
+  background-color: #f5f5f5;
+  padding: 20rpx;
+  border-radius: 8rpx;
+  margin: 20rpx 0;
+  overflow-x: auto;
+}
+
+.markdown-content pre code {
+  background: none;
+  padding: 0;
+  font-size: 24rpx;
+  line-height: 1.4;
+}
+
+.markdown-content ul {
+  margin: 20rpx 0;
+  padding-left: 40rpx;
+}
+
+.markdown-content li {
+  margin-bottom: 10rpx;
+  list-style-type: disc;
+}
 </style> 

+ 115 - 129
pages/demo/demo.vue

@@ -320,18 +320,17 @@ export default {
       needPlayLowScoreVideo: false, // 是否需要播放低分视频
       finalRecordingDuration: 0, // 最终录制时长
       historyTime: 0, // 添加历史时间记录
-      followUpQuestions: [], // 存储追问问题数据
-      currentParentQuestionId: null, // 当前父问题ID
+      followUpQuestions: [], // 存储追问问题数据(预加载的静态追问)
       hasPlayedFollowUp: {}, // 记录已播放的追问,格式: {questionId: true}
       isFollowUpQuestion: false, // 标记当前是否正在播放追问问题
       currentFollowUpQuestion: null, // 添加当前追问问题的完整信息
       lastQuestionWasFollowUp: false,
       lastFollowUpQuestionId: null,
-      // 追问次数控制
-      defaultFollowUpLimit: 1, // 默认每个主题目最多2次追问
-      followUpLimitMap: {}, // { parentJobPositionQuestionId: limit }
-      followUpAskedCountMap: {}, // { parentJobPositionQuestionId: askedCount }
-      followUpRequestInFlight: {}, // { parentJobPositionQuestionId: boolean }
+      // 追问次数控制 - 统一使用job_position_question_id作为key
+      defaultFollowUpLimit: 1, // 默认每个主题目最多1次追问
+      followUpLimitMap: {}, // { job_position_question_id: limit }
+      followUpAskedCountMap: {}, // { job_position_question_id: askedCount }
+      followUpRequestInFlight: {}, // { job_position_question_id: boolean }
       lastUpdateTime: Date.now(), // 添加最后更新时间戳
       subtitleMap: {}, // 用于存储字幕和翻译的映射
       progressPercent: 0, // 录制进度百分比
@@ -425,43 +424,52 @@ export default {
   },
       methods: {
       // ===== 追问次数控制:工具方法 =====
-      getFollowUpLimit(parentId) {
-        if (!parentId) return this.defaultFollowUpLimit;
-        return this.followUpLimitMap[parentId] || this.defaultFollowUpLimit;
+      getFollowUpLimit(jobPositionQuestionId) {
+        if (!jobPositionQuestionId) return this.defaultFollowUpLimit;
+        return this.followUpLimitMap[jobPositionQuestionId] || this.defaultFollowUpLimit;
       },
-      setFollowUpLimit(parentId, limit) {
-        if (!parentId) return;
+      setFollowUpLimit(jobPositionQuestionId, limit) {
+        if (!jobPositionQuestionId) return;
         const parsed = parseInt(limit);
-        this.followUpLimitMap[parentId] = Number.isFinite(parsed) && parsed > 0 ? parsed : this.defaultFollowUpLimit;
+        this.followUpLimitMap[jobPositionQuestionId] = Number.isFinite(parsed) && parsed > 0 ? parsed : this.defaultFollowUpLimit;
+        console.log(`设置问题 ${jobPositionQuestionId} 的追问上限为: ${this.followUpLimitMap[jobPositionQuestionId]}`);
       },
-      resetFollowUpAskedCount(parentId) {
-        if (!parentId) return;
-        this.followUpAskedCountMap[parentId] = 0;
+      resetFollowUpAskedCount(jobPositionQuestionId) {
+        if (!jobPositionQuestionId) return;
+        this.followUpAskedCountMap[jobPositionQuestionId] = 0;
+        console.log(`重置问题 ${jobPositionQuestionId} 的追问次数为: 0`);
       },
-      incrementFollowUpCount(parentId) {
-        if (!parentId) return;
-        const current = this.followUpAskedCountMap[parentId] || 0;
-        this.followUpAskedCountMap[parentId] = current + 1;
+      incrementFollowUpCount(jobPositionQuestionId) {
+        if (!jobPositionQuestionId) return;
+        const current = this.followUpAskedCountMap[jobPositionQuestionId] || 0;
+        this.followUpAskedCountMap[jobPositionQuestionId] = current + 1;
+        console.log(`问题 ${jobPositionQuestionId} 的追问次数增加到: ${this.followUpAskedCountMap[jobPositionQuestionId]}`);
       },
-      canAskMoreFollowUps(parentId) {
-        if (!parentId) return false;
-        const asked = this.followUpAskedCountMap[parentId] || 0;
-        const limit = this.getFollowUpLimit(parentId);
-        return asked < limit;
+      canAskMoreFollowUps(jobPositionQuestionId) {
+        if (!jobPositionQuestionId) return false;
+        const asked = this.followUpAskedCountMap[jobPositionQuestionId] || 0;
+        const limit = this.getFollowUpLimit(jobPositionQuestionId);
+        const canAsk = asked < limit;
+        console.log(`检查问题 ${jobPositionQuestionId} 是否可以继续追问: ${canAsk} (已问${asked}次/上限${limit}次)`);
+        return canAsk;
       },
-      async maybeRequestNextFollowUp(parentId) {
-        if (!parentId) {
+      async maybeRequestNextFollowUp(jobPositionQuestionId) {
+        if (!jobPositionQuestionId) {
+          console.log('缺少job_position_question_id,直接进入下一题');
           this.proceedToNextQuestion();
           return;
         }
-        if (this.canAskMoreFollowUps(parentId)) {
+        
+        console.log(`检查是否需要继续追问,问题ID: ${jobPositionQuestionId}`);
+        
+        if (this.canAskMoreFollowUps(jobPositionQuestionId)) {
           // 避免重复触发并发请求
-          if (this.followUpRequestInFlight[parentId]) {
+          if (this.followUpRequestInFlight[jobPositionQuestionId]) {
             console.log('已有追问请求进行中,跳过重复触发');
             return;
           }
           // 交由 callInterviewInteraction 统一处理并发标记
-          const ok = await this.callInterviewInteraction(parentId);
+          const ok = await this.callInterviewInteraction(jobPositionQuestionId);
           if (!ok) {
             console.log('未获取到下一条追问或调用失败,进入下一题');
             this.isFollowUpMode = false;
@@ -471,6 +479,7 @@ export default {
           }
         } else {
           // 已达上限,进入下一题
+          console.log(`问题 ${jobPositionQuestionId} 追问次数已达上限,进入下一题`);
           this.isFollowUpMode = false;
           this.isFollowUpQuestion = false;
           this.currentFollowUpQuestion = null;
@@ -616,14 +625,14 @@ export default {
         this.handleAudioEnd();
       },
       // 调用面试互动接口
-      async callInterviewInteraction(questionId, retryCount = 0, maxRetries = 3) {
+      async callInterviewInteraction(jobPositionQuestionId, retryCount = 0, maxRetries = 3) {
         // 防重入:同一父问题并发保护
-        if (questionId && this.followUpRequestInFlight[questionId]) {
+        if (jobPositionQuestionId && this.followUpRequestInFlight[jobPositionQuestionId]) {
           console.log('追问请求已在进行中,跳过本次调用');
           return false;
         }
-        if (questionId) {
-          this.followUpRequestInFlight[questionId] = true;
+        if (jobPositionQuestionId) {
+          this.followUpRequestInFlight[jobPositionQuestionId] = true;
         }
         const userInfo = JSON.parse(uni.getStorageSync('userInfo'));
         const appId = uni.getStorageSync('appId');
@@ -632,13 +641,13 @@ export default {
         try {
           // 显示思考中loading
           this.showThinkingLoading();
-          console.log('开始调用面试互动接口', { questionId, appId });
+          console.log('开始调用面试互动接口', { jobPositionQuestionId, appId });
           const res = await uni.request({
             url: `${apiBaseUrl}/api/voice_interview_interaction/`,
             method: 'POST',
             data: {
               tenant_id: userInfo.tenant_id || 1,
-              question_id: questionId,
+              question_id: jobPositionQuestionId,
               position_config_id: positionConfigId,
               application_id: appId
             },
@@ -654,7 +663,7 @@ export default {
               console.log(`视频转写未完成,${retryCount + 1}次重试中...`);
               // 等待3秒后重试
               await new Promise(resolve => setTimeout(resolve, 3000));
-              return this.callInterviewInteraction(questionId, retryCount + 1, maxRetries);
+              return this.callInterviewInteraction(jobPositionQuestionId, retryCount + 1, maxRetries);
             } else {
               console.log('达到最大重试次数,视频转写仍未完成');
               uni.showToast({
@@ -667,9 +676,9 @@ export default {
           }
           
           if (res.data.success) {
-            // 成功获取到一条追问,计数 +1(表示“已请求”这条追问)
-            if (questionId) {
-              this.incrementFollowUpCount(questionId);
+            // 成功获取到一条追问,计数 +1(表示"已请求"这条追问)
+            if (jobPositionQuestionId) {
+              this.incrementFollowUpCount(jobPositionQuestionId);
             }
             // 处理本次追问(即便已达到上限,也应该播放本次追问)
             await this.handleFollowUpQuestion(res.data);
@@ -685,8 +694,8 @@ export default {
           this.hideThinkingLoading();
           return false;
         } finally {
-          if (questionId) {
-            this.followUpRequestInFlight[questionId] = false;
+          if (jobPositionQuestionId) {
+            this.followUpRequestInFlight[jobPositionQuestionId] = false;
           }
         }
       },
@@ -2246,7 +2255,7 @@ export default {
      
       
       console.log('准备上传视频:', typeof fileOrPath === 'string' ? fileOrPath : fileOrPath.name);
-      console.log('当前问题ID:', this.currentParentQuestionId);
+      console.log('当前父问题ID:', this.parentJobPositionQuestionId);
       console.log('当前问题类型:', this.isFollowUpQuestion);
       console.log('当前追问问题:', this.currentFollowUpQuestion);
       
@@ -2299,14 +2308,11 @@ export default {
         questionForm: questionForm,
         attempts: 0,
         maxAttempts: 3,
-        parentQuestionId: this.currentParentQuestionId,
+        parentQuestionId: this.parentJobPositionQuestionId,
         videoDuration: this.currentRecordingDuration || 0 // 添加视频时长
       };
       
-      // 如果不是追问问题,记录当前问题ID作为父问题ID
-      if (!isFollowUpQuestionUpload) {
-        this.currentParentQuestionId = questionId;
-      }
+      // 如果不是追问问题,当前问题ID会在提交成功后成为父问题ID
       
       // 添加到上传队列
       this.uploadQueue.push(uploadTask);
@@ -2681,18 +2687,24 @@ export default {
           // 若未达到上限则继续请求下一条追问,否则进入下一题
           this.maybeRequestNextFollowUp(this.parentJobPositionQuestionId);
         } else {
-          // 如果是常规问题,记录父问题ID并检查追问问题
-          this.currentParentQuestionId = task.questionId;
-          console.log('检查常规问题的追问,父问题ID:', this.currentParentQuestionId);
-          // 从题目读取追问上限(若后端提供),否则使用默认
-          const currentQuestion = this.getCurrentQuestionByIndex(this.currentVideoIndex);
-          if (currentQuestion && typeof currentQuestion.follow_up_limit !== 'undefined') {
-            this.setFollowUpLimit(this.currentParentQuestionId, currentQuestion.follow_up_limit);
-          } else if (typeof this.defaultFollowUpLimit !== 'undefined') {
-            this.setFollowUpLimit(this.currentParentQuestionId, this.defaultFollowUpLimit);
+          // 如果是常规问题,检查追问问题
+          console.log('常规问题回答完成,准备检查追问,父问题ID:', this.parentJobPositionQuestionId);
+          // 注意:这里应该使用从后端返回的job_position_question_id,而不是前端的questionId
+          // parentJobPositionQuestionId应该在提交视频成功后就已经设置好了
+          if (this.parentJobPositionQuestionId) {
+            // 从题目读取追问上限(若后端提供),否则使用默认
+            const currentQuestion = this.getCurrentQuestionByIndex(this.currentVideoIndex);
+            if (currentQuestion && typeof currentQuestion.follow_up_limit !== 'undefined') {
+              this.setFollowUpLimit(this.parentJobPositionQuestionId, currentQuestion.follow_up_limit);
+            } else if (typeof this.defaultFollowUpLimit !== 'undefined') {
+              this.setFollowUpLimit(this.parentJobPositionQuestionId, this.defaultFollowUpLimit);
+            }
+            this.resetFollowUpAskedCount(this.parentJobPositionQuestionId);
+            this.checkAndPlayFollowUpQuestion(this.parentJobPositionQuestionId);
+          } else {
+            console.error('缺少parentJobPositionQuestionId,无法检查追问,直接进入下一题');
+            this.proceedToNextQuestion();
           }
-          this.resetFollowUpAskedCount(this.currentParentQuestionId);
-          this.checkAndPlayFollowUpQuestion(this.currentParentQuestionId);
         }
       }
     },
@@ -3275,8 +3287,8 @@ export default {
       }
       
       // 清理当前父题的追问中的并发锁
-      if (this.currentParentQuestionId) {
-        this.followUpRequestInFlight[this.currentParentQuestionId] = false;
+      if (this.parentJobPositionQuestionId) {
+        this.followUpRequestInFlight[this.parentJobPositionQuestionId] = false;
       }
 
       // 增加当前视频索引,切换到下一个视频
@@ -4549,9 +4561,9 @@ export default {
       this.hasPlayedFollowUp = {};
     },
 
-    // 修改检查并播放追问问题的方法
-    checkAndPlayFollowUpQuestion(parentQuestionId) {
-      console.log('检查是否有追问问题,父问题ID:', parentQuestionId);
+    // 修改检查并播放追问问题的方法 - 使用动态追问API
+    async checkAndPlayFollowUpQuestion(jobPositionQuestionId) {
+      console.log('检查是否有追问问题,父问题ID:', jobPositionQuestionId);
       
       // 如果正在等待用户回答,不进行任何操作
       if (this.isWaitingForAnswer) {
@@ -4559,80 +4571,54 @@ export default {
         return;
       }
       
-      // 超过追问上限则直接进入下一题
-      if (!this.canAskMoreFollowUps(parentQuestionId)) {
-        console.log('追问次数已达上限,进入下一题');
+      if (!jobPositionQuestionId) {
+        console.warn('没有父问题ID,无法检查追问问题');
         this.proceedToNextQuestion();
         return;
       }
-
-      if (!parentQuestionId) {
-        console.warn('没有父问题ID,无法检查追问问题');
-        this.proceedToNextQuestion();
+      
+      // 检查录制时长,如果时长太短则播放低分提示视频而非追问
+      const recordingDuration = this.getRecordingDuration();
+      const lowScoreDuration = 7; // 低分阈值时长(秒),少于7秒视为低分
+      const minDuration = 3; // 最小录制时长阈值(秒)
+      
+      // 如果录制时间少于最小时长或者在最小时长和低分阈值之间,播放"未听清楚"提示视频
+      if (recordingDuration < lowScoreDuration) {
+        let message = recordingDuration < minDuration ? 
+          `录制时间 ${recordingDuration} 秒,少于最小时长 ${minDuration} 秒` :
+          `录制时间 ${recordingDuration} 秒,少于标准时长 ${lowScoreDuration} 秒`;
+        console.log(message + ',将播放低分提示视频');
+        
+        // 增加重试次数
+        this.retryCount++;
+        
+        // 设置需要播放低分提示视频
+        this.needPlayLowScoreVideo = true;
+        
+        // 播放低分提示视频
+        this.playLowScoreVideo();
         return;
       }
-
-      // 如果已经播放过这个问题的追问,则直接进入下一个问题
-      if (this.hasPlayedFollowUp[parentQuestionId]) {
-        console.log('已经播放过此问题的追问,直接进入下一个问题');
-        this.retryCount = 0;
+      
+      // 如果录制时长足够,重置重试次数
+      this.retryCount = 0;
+      
+      // 检查是否可以继续追问
+      if (!this.canAskMoreFollowUps(jobPositionQuestionId)) {
+        console.log('追问次数已达上限,进入下一题');
         this.proceedToNextQuestion();
         return;
       }
-
-      // 查找对应的追问问题
-      const followUpQuestion = this.followUpQuestions.find(q => 
-        q.parent_question_id === parentQuestionId && 
-        q.digital_human_video_url && 
-        q.question_form === 5
-      );
-
-      if (followUpQuestion) {
-        console.log('找到追问问题:', followUpQuestion);
-        
-        // 检查录制时长
-        const recordingDuration = this.getRecordingDuration();
-        const lowScoreDuration = 7; // 低分阈值时长(秒),少于7秒视为低分
-        const minDuration = 3; // 最小录制时长阈值(秒)
-        
-        // 如果录制时间少于最小时长或者在最小时长和低分阈值之间,播放"未听清楚"提示视频
-        if (recordingDuration < lowScoreDuration) {
-          let message = recordingDuration < minDuration ? 
-            `录制时间 ${recordingDuration} 秒,少于最小时长 ${minDuration} 秒` :
-            `录制时间 ${recordingDuration} 秒,少于标准时长 ${lowScoreDuration} 秒`;
-          console.log(message + ',将播放低分提示视频');
-          
-          // 增加重试次数
-          this.retryCount++;
-          
-          // 设置需要播放低分提示视频
-          this.needPlayLowScoreVideo = true;
-          
-          // 播放低分提示视频
-          this.playLowScoreVideo();
-          return;
+      
+      // 调用追问API
+      try {
+        const success = await this.callInterviewInteraction(jobPositionQuestionId);
+        if (!success) {
+          console.log('获取追问失败,进入下一题');
+          this.proceedToNextQuestion();
         }
-        
-        // 如果录制时长足够,重置重试次数
-        this.retryCount = 0;
-        
-        // 标记此追问已播放
-        this.hasPlayedFollowUp[parentQuestionId] = true;
-        
-        // 标记当前是追问问题
-        this.isFollowUpQuestion = true;
-        
-        // 记录当前父问题ID
-        this.currentParentQuestionId = parentQuestionId;
-        
-        // 保存当前追问问题的完整信息
-        this.currentFollowUpQuestion = followUpQuestion;
-        
-        // 播放追问问题视频
-        this.playFollowUpQuestionVideo(followUpQuestion);
-      } else {
-        console.log('没有找到对应的追问问题,继续下一个问题');
-        this.retryCount = 0;
+      } catch (error) {
+        console.error('调用追问接口失败:', error);
         this.proceedToNextQuestion();
       }
     },

+ 1 - 1
pages/login/login.vue

@@ -7,7 +7,7 @@
       <checkbox :checked="isAgreed" @tap="toggleAgreement" color="#0039b3" />
       <text class="agreement-text">
         我已阅读并同意
-        <text class="agreement-link" @tap="navigateToAgreement">《用户协议》</text>
+        <text class="agreement-link" @tap="navigateToAgreement">《隐私协议》</text>
        <!--  和
         <text class="agreement-link" @tap="navigateToPrivacy">《隐私政策》</text> -->
       </text>

+ 22 - 3
unpackage/dist/dev/mp-weixin/pages/agreement/agreement.js

@@ -11,10 +11,28 @@ const _sfc_main = {
       errorMsg: "加载失败,请重试"
     };
   },
+  computed: {
+    parsedContent() {
+      if (!this.agreementContent)
+        return "";
+      return this.parseMarkdown(this.agreementContent);
+    }
+  },
   onLoad() {
     this.fetchAgreement();
   },
   methods: {
+    // 简单的 markdown 解析函数
+    parseMarkdown(markdown) {
+      if (!markdown)
+        return "";
+      let html = markdown.replace(/^### (.*$)/gim, "<h3>$1</h3>").replace(/^## (.*$)/gim, "<h2>$1</h2>").replace(/^# (.*$)/gim, "<h1>$1</h1>").replace(/\*\*(.*)\*\*/gim, "<strong>$1</strong>").replace(/\*(.*)\*/gim, "<em>$1</em>").replace(/\[([^\]]*)\]\(([^\)]*)\)/gim, '<a href="$2">$1</a>').replace(/```(.*?)```/gims, "<pre><code>$1</code></pre>").replace(/`([^`]*)`/gim, "<code>$1</code>").replace(/^\* (.*$)/gim, "<li>$1</li>").replace(/^\d+\. (.*$)/gim, "<li>$1</li>").replace(/\n\n/gim, "</p><p>").replace(/\n/gim, "<br>");
+      html = html.replace(/(<li>.*<\/li>)/gim, "<ul>$1</ul>");
+      if (html && !html.startsWith("<h") && !html.startsWith("<ul") && !html.startsWith("<ol")) {
+        html = "<p>" + html + "</p>";
+      }
+      return html;
+    },
     goBack() {
       common_vendor.index.navigateBack();
     },
@@ -39,9 +57,10 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
   }, $data.loading ? {} : $data.error ? {
     c: common_vendor.t($data.errorMsg),
     d: common_vendor.o((...args) => $options.fetchAgreement && $options.fetchAgreement(...args))
-  } : {}, {
-    b: $data.error,
-    e: $data.agreementContent
+  } : {
+    e: $options.parsedContent
+  }, {
+    b: $data.error
   });
 }
 const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);

+ 1 - 1
unpackage/dist/dev/mp-weixin/pages/agreement/agreement.json

@@ -1,4 +1,4 @@
 {
-  "navigationBarTitleText": "用户协议",
+  "navigationBarTitleText": "隐私协议",
   "usingComponents": {}
 }

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

@@ -1 +1 @@
-<view class="agreement-container"><scroll-view scroll-y class="agreement-content"><view wx:if="{{a}}" class="loading"><text>加载中...</text></view><view wx:elif="{{b}}" class="error"><text>{{c}}</text><button class="retry-btn" bindtap="{{d}}">重新加载</button></view><rich-text nodes="{{e}}"></rich-text></scroll-view></view>
+<view class="agreement-container"><scroll-view scroll-y class="agreement-content"><view wx:if="{{a}}" class="loading"><text>加载中...</text></view><view wx:elif="{{b}}" class="error"><text>{{c}}</text><button class="retry-btn" bindtap="{{d}}">重新加载</button></view><view wx:else class="markdown-content"><rich-text nodes="{{e}}"/></view></scroll-view></view>

+ 68 - 0
unpackage/dist/dev/mp-weixin/pages/agreement/agreement.wxss

@@ -86,3 +86,71 @@
   border-radius: 8rpx;
   font-size: 28rpx;
 }
+
+/* Markdown 样式 */
+.markdown-content {
+  font-size: 28rpx;
+  line-height: 1.6;
+  color: #333;
+}
+.markdown-content h1 {
+  font-size: 36rpx;
+  font-weight: bold;
+  margin: 30rpx 0 20rpx 0;
+  color: #333;
+}
+.markdown-content h2 {
+  font-size: 32rpx;
+  font-weight: bold;
+  margin: 25rpx 0 15rpx 0;
+  color: #333;
+}
+.markdown-content h3 {
+  font-size: 30rpx;
+  font-weight: bold;
+  margin: 20rpx 0 10rpx 0;
+  color: #333;
+}
+.markdown-content p {
+  margin-bottom: 20rpx;
+  text-align: justify;
+}
+.markdown-content strong {
+  font-weight: bold;
+  color: #333;
+}
+.markdown-content em {
+  font-style: italic;
+}
+.markdown-content a {
+  color: #1890ff;
+  text-decoration: underline;
+}
+.markdown-content code {
+  background-color: #f5f5f5;
+  padding: 2rpx 8rpx;
+  border-radius: 4rpx;
+  font-family: 'Courier New', monospace;
+  font-size: 24rpx;
+}
+.markdown-content pre {
+  background-color: #f5f5f5;
+  padding: 20rpx;
+  border-radius: 8rpx;
+  margin: 20rpx 0;
+  overflow-x: auto;
+}
+.markdown-content pre code {
+  background: none;
+  padding: 0;
+  font-size: 24rpx;
+  line-height: 1.4;
+}
+.markdown-content ul {
+  margin: 20rpx 0;
+  padding-left: 40rpx;
+}
+.markdown-content li {
+  margin-bottom: 10rpx;
+  list-style-type: disc;
+}

+ 90 - 94
unpackage/dist/dev/mp-weixin/pages/demo/demo.js

@@ -165,9 +165,7 @@ const _sfc_main = {
       historyTime: 0,
       // 添加历史时间记录
       followUpQuestions: [],
-      // 存储追问问题数据
-      currentParentQuestionId: null,
-      // 当前父问题ID
+      // 存储追问问题数据(预加载的静态追问)
       hasPlayedFollowUp: {},
       // 记录已播放的追问,格式: {questionId: true}
       isFollowUpQuestion: false,
@@ -176,15 +174,15 @@ const _sfc_main = {
       // 添加当前追问问题的完整信息
       lastQuestionWasFollowUp: false,
       lastFollowUpQuestionId: null,
-      // 追问次数控制
+      // 追问次数控制 - 统一使用job_position_question_id作为key
       defaultFollowUpLimit: 1,
-      // 默认每个主题目最多2次追问
+      // 默认每个主题目最多1次追问
       followUpLimitMap: {},
-      // { parentJobPositionQuestionId: limit }
+      // { job_position_question_id: limit }
       followUpAskedCountMap: {},
-      // { parentJobPositionQuestionId: askedCount }
+      // { job_position_question_id: askedCount }
       followUpRequestInFlight: {},
-      // { parentJobPositionQuestionId: boolean }
+      // { job_position_question_id: boolean }
       lastUpdateTime: Date.now(),
       // 添加最后更新时间戳
       subtitleMap: {},
@@ -300,46 +298,53 @@ const _sfc_main = {
   },
   methods: {
     // ===== 追问次数控制:工具方法 =====
-    getFollowUpLimit(parentId) {
-      if (!parentId)
+    getFollowUpLimit(jobPositionQuestionId) {
+      if (!jobPositionQuestionId)
         return this.defaultFollowUpLimit;
-      return this.followUpLimitMap[parentId] || this.defaultFollowUpLimit;
+      return this.followUpLimitMap[jobPositionQuestionId] || this.defaultFollowUpLimit;
     },
-    setFollowUpLimit(parentId, limit) {
-      if (!parentId)
+    setFollowUpLimit(jobPositionQuestionId, limit) {
+      if (!jobPositionQuestionId)
         return;
       const parsed = parseInt(limit);
-      this.followUpLimitMap[parentId] = Number.isFinite(parsed) && parsed > 0 ? parsed : this.defaultFollowUpLimit;
+      this.followUpLimitMap[jobPositionQuestionId] = Number.isFinite(parsed) && parsed > 0 ? parsed : this.defaultFollowUpLimit;
+      console.log(`设置问题 ${jobPositionQuestionId} 的追问上限为: ${this.followUpLimitMap[jobPositionQuestionId]}`);
     },
-    resetFollowUpAskedCount(parentId) {
-      if (!parentId)
+    resetFollowUpAskedCount(jobPositionQuestionId) {
+      if (!jobPositionQuestionId)
         return;
-      this.followUpAskedCountMap[parentId] = 0;
+      this.followUpAskedCountMap[jobPositionQuestionId] = 0;
+      console.log(`重置问题 ${jobPositionQuestionId} 的追问次数为: 0`);
     },
-    incrementFollowUpCount(parentId) {
-      if (!parentId)
+    incrementFollowUpCount(jobPositionQuestionId) {
+      if (!jobPositionQuestionId)
         return;
-      const current = this.followUpAskedCountMap[parentId] || 0;
-      this.followUpAskedCountMap[parentId] = current + 1;
+      const current = this.followUpAskedCountMap[jobPositionQuestionId] || 0;
+      this.followUpAskedCountMap[jobPositionQuestionId] = current + 1;
+      console.log(`问题 ${jobPositionQuestionId} 的追问次数增加到: ${this.followUpAskedCountMap[jobPositionQuestionId]}`);
     },
-    canAskMoreFollowUps(parentId) {
-      if (!parentId)
+    canAskMoreFollowUps(jobPositionQuestionId) {
+      if (!jobPositionQuestionId)
         return false;
-      const asked = this.followUpAskedCountMap[parentId] || 0;
-      const limit = this.getFollowUpLimit(parentId);
-      return asked < limit;
-    },
-    async maybeRequestNextFollowUp(parentId) {
-      if (!parentId) {
+      const asked = this.followUpAskedCountMap[jobPositionQuestionId] || 0;
+      const limit = this.getFollowUpLimit(jobPositionQuestionId);
+      const canAsk = asked < limit;
+      console.log(`检查问题 ${jobPositionQuestionId} 是否可以继续追问: ${canAsk} (已问${asked}次/上限${limit}次)`);
+      return canAsk;
+    },
+    async maybeRequestNextFollowUp(jobPositionQuestionId) {
+      if (!jobPositionQuestionId) {
+        console.log("缺少job_position_question_id,直接进入下一题");
         this.proceedToNextQuestion();
         return;
       }
-      if (this.canAskMoreFollowUps(parentId)) {
-        if (this.followUpRequestInFlight[parentId]) {
+      console.log(`检查是否需要继续追问,问题ID: ${jobPositionQuestionId}`);
+      if (this.canAskMoreFollowUps(jobPositionQuestionId)) {
+        if (this.followUpRequestInFlight[jobPositionQuestionId]) {
           console.log("已有追问请求进行中,跳过重复触发");
           return;
         }
-        const ok = await this.callInterviewInteraction(parentId);
+        const ok = await this.callInterviewInteraction(jobPositionQuestionId);
         if (!ok) {
           console.log("未获取到下一条追问或调用失败,进入下一题");
           this.isFollowUpMode = false;
@@ -348,6 +353,7 @@ const _sfc_main = {
           this.proceedToNextQuestion();
         }
       } else {
+        console.log(`问题 ${jobPositionQuestionId} 追问次数已达上限,进入下一题`);
         this.isFollowUpMode = false;
         this.isFollowUpQuestion = false;
         this.currentFollowUpQuestion = null;
@@ -447,26 +453,26 @@ const _sfc_main = {
       this.handleAudioEnd();
     },
     // 调用面试互动接口
-    async callInterviewInteraction(questionId, retryCount = 0, maxRetries = 3) {
-      if (questionId && this.followUpRequestInFlight[questionId]) {
+    async callInterviewInteraction(jobPositionQuestionId, retryCount = 0, maxRetries = 3) {
+      if (jobPositionQuestionId && this.followUpRequestInFlight[jobPositionQuestionId]) {
         console.log("追问请求已在进行中,跳过本次调用");
         return false;
       }
-      if (questionId) {
-        this.followUpRequestInFlight[questionId] = true;
+      if (jobPositionQuestionId) {
+        this.followUpRequestInFlight[jobPositionQuestionId] = true;
       }
       const userInfo = JSON.parse(common_vendor.index.getStorageSync("userInfo"));
       const appId = common_vendor.index.getStorageSync("appId");
       const positionConfigId = JSON.parse(common_vendor.index.getStorageSync("configData")).id;
       try {
         this.showThinkingLoading();
-        console.log("开始调用面试互动接口", { questionId, appId });
+        console.log("开始调用面试互动接口", { jobPositionQuestionId, appId });
         const res = await common_vendor.index.request({
           url: `${common_config.apiBaseUrl}/api/voice_interview_interaction/`,
           method: "POST",
           data: {
             tenant_id: userInfo.tenant_id || 1,
-            question_id: questionId,
+            question_id: jobPositionQuestionId,
             position_config_id: positionConfigId,
             application_id: appId
           },
@@ -479,7 +485,7 @@ const _sfc_main = {
           if (retryCount < maxRetries) {
             console.log(`视频转写未完成,${retryCount + 1}次重试中...`);
             await new Promise((resolve) => setTimeout(resolve, 3e3));
-            return this.callInterviewInteraction(questionId, retryCount + 1, maxRetries);
+            return this.callInterviewInteraction(jobPositionQuestionId, retryCount + 1, maxRetries);
           } else {
             console.log("达到最大重试次数,视频转写仍未完成");
             common_vendor.index.showToast({
@@ -491,8 +497,8 @@ const _sfc_main = {
           }
         }
         if (res.data.success) {
-          if (questionId) {
-            this.incrementFollowUpCount(questionId);
+          if (jobPositionQuestionId) {
+            this.incrementFollowUpCount(jobPositionQuestionId);
           }
           await this.handleFollowUpQuestion(res.data);
           this.hideThinkingLoading();
@@ -507,8 +513,8 @@ const _sfc_main = {
         this.hideThinkingLoading();
         return false;
       } finally {
-        if (questionId) {
-          this.followUpRequestInFlight[questionId] = false;
+        if (jobPositionQuestionId) {
+          this.followUpRequestInFlight[jobPositionQuestionId] = false;
         }
       }
     },
@@ -1585,7 +1591,7 @@ const _sfc_main = {
     // 修改 uploadRecordedVideo 方法,添加记录父问题ID的逻辑
     uploadRecordedVideo(fileOrPath) {
       console.log("准备上传视频:", typeof fileOrPath === "string" ? fileOrPath : fileOrPath.name);
-      console.log("当前问题ID:", this.currentParentQuestionId);
+      console.log("当前父问题ID:", this.parentJobPositionQuestionId);
       console.log("当前问题类型:", this.isFollowUpQuestion);
       console.log("当前追问问题:", this.currentFollowUpQuestion);
       let questionId;
@@ -1628,13 +1634,10 @@ const _sfc_main = {
         questionForm,
         attempts: 0,
         maxAttempts: 3,
-        parentQuestionId: this.currentParentQuestionId,
+        parentQuestionId: this.parentJobPositionQuestionId,
         videoDuration: this.currentRecordingDuration || 0
         // 添加视频时长
       };
-      if (!isFollowUpQuestionUpload) {
-        this.currentParentQuestionId = questionId;
-      }
       this.uploadQueue.push(uploadTask);
       this.uploadProgress[uploadTask.id] = 0;
       this.uploadStatus[uploadTask.id] = "pending";
@@ -1891,16 +1894,20 @@ const _sfc_main = {
           this.videoUrl = "";
           this.maybeRequestNextFollowUp(this.parentJobPositionQuestionId);
         } else {
-          this.currentParentQuestionId = task.questionId;
-          console.log("检查常规问题的追问,父问题ID:", this.currentParentQuestionId);
-          const currentQuestion = this.getCurrentQuestionByIndex(this.currentVideoIndex);
-          if (currentQuestion && typeof currentQuestion.follow_up_limit !== "undefined") {
-            this.setFollowUpLimit(this.currentParentQuestionId, currentQuestion.follow_up_limit);
-          } else if (typeof this.defaultFollowUpLimit !== "undefined") {
-            this.setFollowUpLimit(this.currentParentQuestionId, this.defaultFollowUpLimit);
+          console.log("常规问题回答完成,准备检查追问,父问题ID:", this.parentJobPositionQuestionId);
+          if (this.parentJobPositionQuestionId) {
+            const currentQuestion = this.getCurrentQuestionByIndex(this.currentVideoIndex);
+            if (currentQuestion && typeof currentQuestion.follow_up_limit !== "undefined") {
+              this.setFollowUpLimit(this.parentJobPositionQuestionId, currentQuestion.follow_up_limit);
+            } else if (typeof this.defaultFollowUpLimit !== "undefined") {
+              this.setFollowUpLimit(this.parentJobPositionQuestionId, this.defaultFollowUpLimit);
+            }
+            this.resetFollowUpAskedCount(this.parentJobPositionQuestionId);
+            this.checkAndPlayFollowUpQuestion(this.parentJobPositionQuestionId);
+          } else {
+            console.error("缺少parentJobPositionQuestionId,无法检查追问,直接进入下一题");
+            this.proceedToNextQuestion();
           }
-          this.resetFollowUpAskedCount(this.currentParentQuestionId);
-          this.checkAndPlayFollowUpQuestion(this.currentParentQuestionId);
         }
       }
     },
@@ -2323,8 +2330,8 @@ const _sfc_main = {
         this.handleVideoCompletion(jobId);
         return;
       }
-      if (this.currentParentQuestionId) {
-        this.followUpRequestInFlight[this.currentParentQuestionId] = false;
+      if (this.parentJobPositionQuestionId) {
+        this.followUpRequestInFlight[this.parentJobPositionQuestionId] = false;
       }
       this.currentVideoIndex++;
       this.historyTime = 0;
@@ -3242,54 +3249,43 @@ const _sfc_main = {
       console.log("有追问的父问题ID:", Array.from(parentQuestionIds));
       this.hasPlayedFollowUp = {};
     },
-    // 修改检查并播放追问问题的方法
-    checkAndPlayFollowUpQuestion(parentQuestionId) {
-      console.log("检查是否有追问问题,父问题ID:", parentQuestionId);
+    // 修改检查并播放追问问题的方法 - 使用动态追问API
+    async checkAndPlayFollowUpQuestion(jobPositionQuestionId) {
+      console.log("检查是否有追问问题,父问题ID:", jobPositionQuestionId);
       if (this.isWaitingForAnswer) {
         console.log("正在等待用户回答,暂不处理追问");
         return;
       }
-      if (!this.canAskMoreFollowUps(parentQuestionId)) {
-        console.log("追问次数已达上限,进入下一题");
+      if (!jobPositionQuestionId) {
+        console.warn("没有父问题ID,无法检查追问问题");
         this.proceedToNextQuestion();
         return;
       }
-      if (!parentQuestionId) {
-        console.warn("没有父问题ID,无法检查追问问题");
-        this.proceedToNextQuestion();
+      const recordingDuration = this.getRecordingDuration();
+      const lowScoreDuration = 7;
+      const minDuration = 3;
+      if (recordingDuration < lowScoreDuration) {
+        let message = recordingDuration < minDuration ? `录制时间 ${recordingDuration} 秒,少于最小时长 ${minDuration} 秒` : `录制时间 ${recordingDuration} 秒,少于标准时长 ${lowScoreDuration} 秒`;
+        console.log(message + ",将播放低分提示视频");
+        this.retryCount++;
+        this.needPlayLowScoreVideo = true;
+        this.playLowScoreVideo();
         return;
       }
-      if (this.hasPlayedFollowUp[parentQuestionId]) {
-        console.log("已经播放过此问题的追问,直接进入下一个问题");
-        this.retryCount = 0;
+      this.retryCount = 0;
+      if (!this.canAskMoreFollowUps(jobPositionQuestionId)) {
+        console.log("追问次数已达上限,进入下一题");
         this.proceedToNextQuestion();
         return;
       }
-      const followUpQuestion = this.followUpQuestions.find(
-        (q) => q.parent_question_id === parentQuestionId && q.digital_human_video_url && q.question_form === 5
-      );
-      if (followUpQuestion) {
-        console.log("找到追问问题:", followUpQuestion);
-        const recordingDuration = this.getRecordingDuration();
-        const lowScoreDuration = 7;
-        const minDuration = 3;
-        if (recordingDuration < lowScoreDuration) {
-          let message = recordingDuration < minDuration ? `录制时间 ${recordingDuration} 秒,少于最小时长 ${minDuration} 秒` : `录制时间 ${recordingDuration} 秒,少于标准时长 ${lowScoreDuration} 秒`;
-          console.log(message + ",将播放低分提示视频");
-          this.retryCount++;
-          this.needPlayLowScoreVideo = true;
-          this.playLowScoreVideo();
-          return;
+      try {
+        const success = await this.callInterviewInteraction(jobPositionQuestionId);
+        if (!success) {
+          console.log("获取追问失败,进入下一题");
+          this.proceedToNextQuestion();
         }
-        this.retryCount = 0;
-        this.hasPlayedFollowUp[parentQuestionId] = true;
-        this.isFollowUpQuestion = true;
-        this.currentParentQuestionId = parentQuestionId;
-        this.currentFollowUpQuestion = followUpQuestion;
-        this.playFollowUpQuestionVideo(followUpQuestion);
-      } else {
-        console.log("没有找到对应的追问问题,继续下一个问题");
-        this.retryCount = 0;
+      } catch (error) {
+        console.error("调用追问接口失败:", error);
         this.proceedToNextQuestion();
       }
     },

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

@@ -1 +1 @@
-<view class="login-container"><image class="login-image" src="https://data.qicai321.com/minlong/8baae825-6aa1-4783-9875-44d699f297cb.jpg" mode="aspectFit"></image><view class="login-title">奇才,只选对的</view><button class="login-btn" bindtap="{{a}}">微信登录</button><view class="agreement"><checkbox checked="{{b}}" bindtap="{{c}}" color="#0039b3"/><text class="agreement-text"> 我已阅读并同意 <text class="agreement-link" bindtap="{{d}}">《用户协议》</text></text></view></view>
+<view class="login-container"><image class="login-image" src="https://data.qicai321.com/minlong/8baae825-6aa1-4783-9875-44d699f297cb.jpg" mode="aspectFit"></image><view class="login-title">奇才,只选对的</view><button class="login-btn" bindtap="{{a}}">微信登录</button><view class="agreement"><checkbox checked="{{b}}" bindtap="{{c}}" color="#0039b3"/><text class="agreement-text"> 我已阅读并同意 <text class="agreement-link" bindtap="{{d}}">《隐私协议》</text></text></view></view>

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

@@ -8,7 +8,7 @@
     "urlCheck": false,
     "es6": true,
     "postcss": false,
-    "minified": false,
+    "minified": true,
     "newFeature": true,
     "bigPackageSizeSupport": true,
     "babelSetting": {