yangg 5 месяцев назад
Родитель
Сommit
2cc1a157ef
24 измененных файлов с 1613 добавлено и 148 удалено
  1. 7 0
      pages.json
  2. 1 1
      pages/followUp/followUp.vue
  3. 170 23
      pages/identity-verify/identity-verify.vue
  4. 2 2
      pages/interview-question/interview-question.vue
  5. 37 18
      pages/interview/interview.vue
  6. 423 0
      pages/interview_retake/interview_retake.vue
  7. 198 20
      pages/interview_success/interview_success.vue
  8. 1 0
      unpackage/dist/dev/mp-weixin/app.js
  9. 2 1
      unpackage/dist/dev/mp-weixin/app.json
  10. 1 1
      unpackage/dist/dev/mp-weixin/pages/followUp/followUp.js
  11. 50 28
      unpackage/dist/dev/mp-weixin/pages/identity-verify/identity-verify.js
  12. 0 0
      unpackage/dist/dev/mp-weixin/pages/identity-verify/identity-verify.wxml
  13. 124 15
      unpackage/dist/dev/mp-weixin/pages/identity-verify/identity-verify.wxss
  14. 3 3
      unpackage/dist/dev/mp-weixin/pages/interview-question/interview-question.js
  15. 30 15
      unpackage/dist/dev/mp-weixin/pages/interview/interview.js
  16. 221 0
      unpackage/dist/dev/mp-weixin/pages/interview_retake/interview_retake.js
  17. 5 0
      unpackage/dist/dev/mp-weixin/pages/interview_retake/interview_retake.json
  18. 1 0
      unpackage/dist/dev/mp-weixin/pages/interview_retake/interview_retake.wxml
  19. 147 0
      unpackage/dist/dev/mp-weixin/pages/interview_retake/interview_retake.wxss
  20. 110 19
      unpackage/dist/dev/mp-weixin/pages/interview_success/interview_success.js
  21. 1 1
      unpackage/dist/dev/mp-weixin/pages/interview_success/interview_success.wxml
  22. 71 0
      unpackage/dist/dev/mp-weixin/pages/interview_success/interview_success.wxss
  23. 1 1
      unpackage/dist/dev/mp-weixin/project.config.json
  24. 7 0
      unpackage/dist/dev/mp-weixin/project.private.config.json

+ 7 - 0
pages.json

@@ -134,6 +134,13 @@
 				"navigationBarTitleText": "体态评估指导",
 				"navigationStyle": "default"
 			}
+		},
+		{
+			"path": "pages/interview_retake/interview_retake",
+			"style": {
+				"navigationBarTitleText": "体态照片补拍",
+				"navigationStyle": "default"
+			}
 		}
 	],
 	

+ 1 - 1
pages/followUp/followUp.vue

@@ -195,7 +195,7 @@ export default {
       videoList: [], // 修改为空数组,将通过API获取
       // 其他属性保持不变...
       questions: [], // 添加新属性存储API返回的问题数据
-      introVideoUrl: 'http://121.36.251.245:9000/minlong/tenant_1/general_uploads/880a117c8fcc4196b0d8c0750608d373.mp4', // 保留介绍视频
+      introVideoUrl: 'https://data.qicai321.com/minlong/ee4d9cce-c3d5-4350-8c6e-684283827897.mp4', // 保留介绍视频
       isRecording: false,
       recordingTimer: null,
       showStopRecordingButton: false,

+ 170 - 23
pages/identity-verify/identity-verify.vue

@@ -1,6 +1,11 @@
 <template>
   <div class="identity-verify-container">
     <div class="digital-human-container">
+      <!-- 添加题号显示 -->
+     <!--  <div class="question-counter" v-if="currentVideoIndex > 0">
+        <span class="question-number">{{ currentVideoIndex }}/{{ totalQuestions }}</span>
+      </div> -->
+      
       <!-- AI数字人视频/图像显示区域 -->
       <div class="digital-human-video">
         <!-- 添加GIF图片显示区域 -->
@@ -44,9 +49,24 @@
           </button>
         </div>
         <!-- 添加字幕覆盖层 :data-time="formatTime(recordingTimerCount) || '03:30'"-->
-        <div class="subtitle-overlay" v-if="currentSubtitle" >
-          <div class="subtitle-main">{{ currentSubtitle }}</div>
-         <!--  <div class="subtitle-translation" v-if="currentTranslation">{{ currentTranslation }}</div> -->
+        <div class="subtitle-overlay" v-if="currentSubtitle">
+          <!-- 添加题号显示 -->
+          <div class="subtitle-header" v-if="currentVideoIndex > 0">
+            <span class="question-tag">{{ currentVideoIndex }}/{{ totalQuestions }}</span>
+          </div>
+          <!-- 显示父问题 -->
+          <div class="subtitle-main" v-if="!isFollowUpQuestion">{{ currentSubtitle }}</div>
+          <!-- 当是追问时显示父问题和追问问题 -->
+          <template v-else>
+            <div class="parent-question">{{ parentQuestion }}</div>
+            <div class="followup-container">
+              <!-- <div class="followup-divider"></div> -->
+              <div class="followup-content">
+                <span class="followup-label">追问:</span>
+                <span class="followup-text">{{ currentSubtitle }}</span>
+              </div>
+            </div>
+          </template>
         </div>
         
         <!-- 添加答题按钮 -->
@@ -217,7 +237,7 @@ export default {
       videoList: [], // 修改为空数组,将通过API获取
       // 其他属性保持不变...
       questions: [], // 添加新属性存储API返回的问题数据
-      introVideoUrl: 'http://121.36.251.245:9000/minlong/tenant_1/general_uploads/880a117c8fcc4196b0d8c0750608d373.mp4', // 保留介绍视频
+      introVideoUrl: 'https://data.qicai321.com/minlong/ee4d9cce-c3d5-4350-8c6e-684283827897.mp4', // 保留介绍视频
       isRecording: false,
       recordingTimer: null,
       showStopRecordingButton: false,
@@ -275,6 +295,7 @@ export default {
       progressPercent: 0, // 录制进度百分比
       progressColor: '#05dc8b', // 进度条颜色
       progressBgColor: 'rgba(0, 0, 0,0.3)', // 进度条背景色
+      parentQuestion: '', // 添加父问题存储
     }
   },
   mounted() {
@@ -917,8 +938,9 @@ export default {
       if (this.isFollowUpQuestion) {
         console.log('追问问题视频播放完成');
         
-        // 重置追问标记
-        this.isFollowUpQuestion = false;
+        // 不重置追问状态,保持显示
+        // this.isFollowUpQuestion = false;
+        // this.currentFollowUpQuestion = null;
         
         // 显示"开始回答"按钮,准备回答追问
         this.showStartRecordingButton = true;
@@ -937,6 +959,12 @@ export default {
         // 对于介绍视频(第一个视频),显示"开始面试"按钮
         this.showAnswerButton = true;
       }
+      
+      // 重置追问状态
+      if (this.isFollowUpQuestion) {
+        this.isFollowUpQuestion = false;
+        this.currentFollowUpQuestion = null;
+      }
     },
     
     // 添加新方法:开始倒计时
@@ -3190,12 +3218,17 @@ export default {
       }
     },
 
-    // 添加新方法:播放追问问题视频
+    // 修改播放追问问题视频的方法
     playFollowUpQuestionVideo(followUpQuestion) {
       console.log('播放追问问题视频:', followUpQuestion.digital_human_video_url);
       
+      // 保存父问题内容
+      this.parentQuestion = this.currentSubtitle;
+      
       this.videoUrl = followUpQuestion.digital_human_video_url;
       this.videoPlaying = true;
+      this.isFollowUpQuestion = true;
+      this.currentFollowUpQuestion = followUpQuestion;
       
       // 设置追问问题的字幕
       const followUpSubtitles = [{
@@ -3212,7 +3245,7 @@ export default {
         if (videoContext) {
           videoContext.play();
           
-          // 立即显示字幕
+          // 更新当前字幕为追问内容
           this.currentSubtitle = followUpSubtitles[0].text;
         }
       });
@@ -3247,6 +3280,11 @@ export default {
     progressPercent() {
       // 将时间进度转换为百分比(0-100)
       return Math.min(Math.round((this.recordingTimerCount / this.maxRecordingTime) * 100), 100);
+    },
+    // 添加总题数计算属性
+    totalQuestions() {
+      // 获取问题总数,不包括介绍视频
+      return this.questions.length || 5; // 如果没有问题数据,默认显示5
     }
   }
 }
@@ -3354,9 +3392,9 @@ video::-webkit-media-controls-fullscreen-button {
 .subtitle-overlay {
   position: absolute;
   bottom: 180px;
-  left: 5%;
+  left: 10%;
   width: 80%;
-  padding: 15px 20px;
+  padding: 0; /* 移除内边距,由内部元素控制 */
   border-radius: 15px;
   background-color: rgba(255, 255, 255, 0.9);
   color: #333;
@@ -3368,27 +3406,31 @@ video::-webkit-media-controls-fullscreen-button {
   box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
   display: flex;
   flex-direction: column;
-  gap: 8px;
+  overflow: hidden; /* 确保圆角效果 */
 }
 
-.subtitle-main {
-  font-weight: 500;
+/* 添加题号header样式 */
+.subtitle-header {
+/*   background-color: rgba(0, 0, 0, 0.05); */
+  padding: 8px 20px;
+  /* border-bottom: 1px solid rgba(0, 0, 0, 0.05); */
 }
 
-.subtitle-translation {
+.question-tag {
   font-size: 14px;
   color: #666;
-  font-style: italic;
+  font-weight: 500;
 }
 
-/* 添加计时器样式,右侧显示橙色圆点和时间 */
-.subtitle-overlay::after {
-  content: attr(data-time);
-  position: absolute;
-  right: 20px;
-  bottom: 15px;
-  color: #333;
-  font-size: 14px;
+/* 调整主字幕样式 */
+.subtitle-main {
+  padding: 15px 20px ;
+  font-weight: 500;
+}
+
+/* 移除之前的题号显示器 */
+.question-counter {
+  display: none;
 }
 
 .control-panel {
@@ -3758,4 +3800,109 @@ video::-webkit-media-controls-fullscreen-button {
   font-weight: bold;
    color: #05dc8b;
 }
+
+/* 添加题号显示 */
+.question-counter {
+  position: absolute;
+  top: 20px;
+  right: 130px; /* 调整位置,避免与用户摄像头重叠 */
+  background-color: rgba(255, 255, 255, 0.9);
+  padding: 6px 12px;
+  border-radius: 15px;
+  z-index: 20;
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
+  display: flex;
+  align-items: center;
+}
+
+.question-number {
+  color: #333;
+  font-size: 14px;
+  font-weight: 500;
+}
+
+/* 追问问题容器样式 */
+.followup-container {
+  width: 100%;
+}
+
+/* 追问分隔线 */
+.followup-divider {
+  height: 1px;
+  background-color: rgba(0, 0, 0, 0.05);
+  margin: 0;
+}
+
+/* 追问内容样式 */
+.followup-content {
+  padding: 12px 20px;
+  display: flex;
+  align-items: flex-start;
+  gap: 8px;
+}
+
+/* 追问标签样式 */
+.followup-label {
+  color: #fb752f;
+  font-size: 14px;
+  font-weight: 500;
+}
+
+/* 追问文本样式 */
+.followup-text {
+  color: #333;
+  font-size: 14px;
+  line-height: 1.5;
+  flex: 1;
+}
+
+/* 调整主字幕样式,确保有足够空间 */
+.subtitle-main {
+  padding:15px 20px ;
+  font-weight: 500;
+  border-bottom: none; /* 移除底部边框,由追问分隔线处理 */
+}
+
+/* 父问题样式 */
+.parent-question {
+  padding: 15px 20px;
+  font-weight: 500;
+  color: #333;
+}
+
+/* 追问容器样式调整 */
+.followup-container {
+  width: 100%;
+ /*  background-color: rgba(0, 0, 0, 0.02);  *//* 轻微的背景色区分 */
+}
+
+/* 分隔线样式 */
+.followup-divider {
+  height: 1px;
+  background-color: rgba(0, 0, 0, 0.05);
+  margin: 0;
+}
+
+/* 追问内容样式 */
+.followup-content {
+  padding: 12px 20px;
+  display: flex;
+  align-items: flex-start;
+  gap: 8px;
+}
+
+/* 追问标签样式 */
+.followup-label {
+  color: #fb752f;
+  font-size: 14px;
+  font-weight: 500;
+}
+
+/* 追问文本样式 */
+.followup-text {
+  color: #333;
+  font-size: 14px;
+  line-height: 1.5;
+  flex: 1;
+}
 </style>

+ 2 - 2
pages/interview-question/interview-question.vue

@@ -203,7 +203,7 @@ export default {
       audioTranscript: '',
       videoPlaying: false,
       showDebugInfo: false, // 设置为true可以显示调试信息
-      videoUrl: 'http://121.36.251.245:9000/minlong/tenant_1/general_uploads/abdaa6fda8494e3a8613304743ed0433.mp4', // 用于存储AI数字人视频URL
+      videoUrl: 'https://data.qicai321.com/minlong/1809457d-30c5-40bb-b6d8-a8c2e8e1e9e2.mp4', //'http://121.36.251.245:9000/minlong/tenant_1/general_uploads/abdaa6fda8494e3a8613304743ed0433.mp4', // 用于存储AI数字人视频URL
       showReplayButton: false,
       cameraStream: null, // 存储摄像头流
       cameraError: null, // 存储摄像头错误信息
@@ -255,7 +255,7 @@ export default {
       showAnswerButton: false, // 控制答题按钮显示
       currentVideoIndex: 0, // 当前播放的视频索引
       videoList: [
-        'http://121.36.251.245:9000/minlong/tenant_1/general_uploads/abdaa6fda8494e3a8613304743ed0433.mp4'//结束
+        'https://data.qicai321.com/minlong/1809457d-30c5-40bb-b6d8-a8c2e8e1e9e2.mp4'//结束
       ],
       isRecording: false,
       recordingTimer: null,

+ 37 - 18
pages/interview/interview.vue

@@ -425,24 +425,43 @@ import { apiBaseUrl } from '@/common/config.js'; // Import the base URL from a c
 						title: '处理中...'
 					});
 					
-					// 所有照片已经单独上传,这里只需要处理跳转
-					setTimeout(() => {
-						uni.hideLoading();
-						uni.showToast({
-							title: '手部照片采集完成',
-							icon: 'success',
-							duration: 2000,
-							success: () => {
-								// 延迟跳转,让用户看到成功提示
-								setTimeout(() => {
-									// 使用redirectTo替代navigateTo,避免页面栈溢出
-									uni.redirectTo({
-										url: '/pages/interview_success/interview_success'
-									});
-								}, 100);
-							}
-						});
-					}, 500);
+					// 获取必要参数
+					const application_id = uni.getStorageSync('appId');
+					const tenant_id = uni.getStorageSync('tenant_id') || '1';
+					
+					// 调用视觉分析接口
+					uni.request({
+						url: `${apiBaseUrl}/api/system/job/trigger_visual_analysis?application_id=${application_id}&tenant_id=${tenant_id}`,
+						method: 'POST',
+						success: (res) => {
+							console.log('视觉分析触发成功:', res);
+							
+							// 继续处理跳转逻辑
+							uni.hideLoading();
+							uni.showToast({
+								title: '手部照片采集完成',
+								icon: 'success',
+								duration: 2000,
+								success: () => {
+									// 延迟跳转,让用户看到成功提示
+									setTimeout(() => {
+										// 使用redirectTo替代navigateTo,避免页面栈溢出
+										uni.redirectTo({
+											url: '/pages/interview_success/interview_success'
+										});
+									}, 100);
+								}
+							});
+						},
+						fail: (err) => {
+							console.error('视觉分析触发失败:', err);
+							uni.hideLoading();
+							uni.showToast({
+								title: '处理失败,请重试',
+								icon: 'none'
+							});
+						}
+					});
 				} else {
 					uni.showToast({
 						title: '请完成所有照片拍摄',

+ 423 - 0
pages/interview_retake/interview_retake.vue

@@ -0,0 +1,423 @@
+<template>
+	<view class="interview-container">
+		<view class="header">
+			<view class="title">补拍检测</view>
+			<view class="subtitle">请按提示重新拍摄失败项</view>
+		</view>
+		
+		<view class="camera-container" v-if="showCamera">
+			<camera class="camera" device-position="front" flash="off" @error="handleError">
+				<view class="gesture-overlay">
+					<image class="gesture-image" :src="currentStep.tipImage"></image>
+					<cover-view class="tip-text">{{currentStep.tipText}}</cover-view>
+				</view>
+				<view class="camera-tip" :style="currentStep.type.includes('right_')?'left:30rpx':'right:30rpx'">
+					<view class="guide-icon">
+						<video 
+							:src="currentStep.guideVideo"
+							id="mpVideo"
+							autoplay 
+							class="mp-video-player"
+							:controls="false"
+							loop
+							muted
+							object-fit="contain"
+						></video>
+					</view>
+				</view>
+			</camera>
+			<view class="capture-btn" @tap="takePhoto"></view>
+		</view>
+		
+		<view class="preview-container" v-else>
+			<image :src="tempImagePath" mode="aspectFit"></image>
+			<view class="preview-buttons">
+				<button class="btn-retake" @tap="retakePhoto">重拍</button>
+				<button class="btn-confirm" @tap="confirmPhoto">确认</button>
+			</view>
+		</view>
+		
+		<view class="progress-bar">
+			<view class="step-item" v-for="(item, index) in failedItems" :key="index"
+				:class="{'active': currentIndex === index, 'completed': index < currentIndex}">
+				{{item.name}}
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import { apiBaseUrl } from '@/common/config.js'; // 导入基础URL配置
+
+export default {
+	data() {
+		return {
+			failedItems: [],
+			currentIndex: 0,
+			showCamera: true,
+			tempImagePath: '',
+			uploadedImages: {},
+			isH5: false,
+			cameraPosition: 'front',
+			cameraMode: 'normal',
+			cameraContext: null,
+			cameraInitRetries: 0,
+			// 添加手势引导图片
+			gestureOverlays: {
+				left_palm: 'http://data.qicai321.com/minlong/18590378-6792-4a26-be18-ad9f2bcc4159.png',
+				left_back: 'http://data.qicai321.com/minlong/e08cff2b-3b1d-478f-a62b-766b38445a16.png',
+				left_fist: 'http://data.qicai321.com/minlong/d30ccab7-9cfe-4386-bf0e-ac1f0045bd76.png',
+				right_palm: 'http://data.qicai321.com/minlong/8068a698-ac40-4a5d-a737-54dba47a668d.png',
+				right_back: 'http://data.qicai321.com/minlong/f04a1f7e-4f79-49c7-9a02-bbad296672ea.png',
+				right_fist: 'http://data.qicai321.com/minlong/1d9bbc36-2fb8-4489-bff6-c36e7a31bd37.png'
+			},
+			// 添加引导视频
+			guideVideos: {
+				left_palm: 'http://data.qicai321.com/minlong/fd080e3b-67be-4ce7-87a3-58d047cfbbb1.mp4',
+				left_back: 'http://data.qicai321.com/minlong/394a858c-672a-44bd-8206-450913863900.mp4',
+				left_fist: 'http://data.qicai321.com/minlong/37153e68-a8f0-4a93-b677-5e5678379243.mp4',
+				right_palm: 'http://data.qicai321.com/minlong/ebf6e43f-ffdf-49bb-8b00-adeff9ce1b56.mp4',
+				right_back: 'http://data.qicai321.com/minlong/e0ca45e2-af3e-42a0-9dd1-4de545e9c720.mp4',
+				right_fist: 'http://data.qicai321.com/minlong/77028dd6-22bb-41b4-8879-19699e8b1dbb.mp4'
+			}
+		}
+	},
+	computed: {
+		currentStep() {
+			if (!this.failedItems.length) return null
+			const current = this.failedItems[this.currentIndex]
+			return {
+				...current,
+				tipText: `${current.name}`,
+				tipImage: this.gestureOverlays[current.type],
+				guideVideo: this.guideVideos[current.type]
+			}
+		}
+	},
+	onLoad(options) {
+		if (options.failedItems) {
+			const items = JSON.parse(options.failedItems)
+			this.failedItems = items.map(item => ({
+				name: this.getNameByType(item.type),
+				type: item.type
+			}))
+		}
+	},
+	methods: {
+		getNameByType(type) {
+			const nameMap = {
+				left_palm: '展示完整的左手手掌',
+				left_back: '展示完整的左手手背',
+				left_fist: '展示完整的左手握拳',
+				right_palm: '展示完整的右手手掌',
+				right_back: '展示完整的右手手背',
+				right_fist: '展示完整的右手握拳'
+			}
+			return nameMap[type] || ''
+		},
+		takePhoto() {
+			const camera = uni.createCameraContext()
+			camera.takePhoto({
+				quality: 'high',
+				success: (res) => {
+					this.tempImagePath = res.tempImagePath
+					this.showCamera = false
+				},
+				fail: (err) => {
+					uni.showToast({
+						title: '拍照失败',
+						icon: 'none'
+					})
+				}
+			})
+		},
+		retakePhoto() {
+			this.showCamera = true
+			this.tempImagePath = ''
+		},
+		async confirmPhoto() {
+			try {
+				const uploadResult = await this.uploadImage(this.tempImagePath)
+				this.uploadedImages[this.currentStep.type] = uploadResult.url
+				
+				if (this.currentIndex < this.failedItems.length - 1) {
+					this.currentIndex++
+					this.showCamera = true
+					this.tempImagePath = ''
+				} else {
+					// 所有补拍完成,返回结果
+					this.completeRetake()
+				}
+			} catch (err) {
+				uni.showToast({
+					title: '上传失败,请重试',
+					icon: 'none'
+				})
+			}
+		},
+		async uploadImage(tempFilePath) {
+			const tenant_id = uni.getStorageSync('tenant_id') || '1';
+			const description = `补拍-${this.currentStep.name}`;
+
+			return new Promise((resolve, reject) => {
+				uni.uploadFile({
+					url: `${apiBaseUrl}/job/upload_posture_photo`,
+					filePath: tempFilePath,
+					name: 'photo',
+					formData: {
+						'application_id': uni.getStorageSync('appId'),
+						'tenant_id': tenant_id,
+						'description': description
+					},
+					success: (uploadRes) => {
+						try {
+							const result = typeof uploadRes.data === 'string' ? 
+								JSON.parse(uploadRes.data) : uploadRes.data;
+							resolve(result);
+						} catch (e) {
+							reject(new Error('解析上传结果失败'));
+						}
+					},
+					fail: reject
+				});
+			});
+		},
+		completeRetake() {
+			// 返回检测结果页面并传递补拍结果
+			const pages = getCurrentPages()
+			const prevPage = pages[pages.length - 2]
+			
+			// 更新上一页的检测结果
+			if (prevPage && prevPage.$vm) {
+				prevPage.$vm.updateCheckResults(this.uploadedImages)
+			}
+			
+			// 显示成功提示
+			uni.showToast({
+				title: '补拍完成',
+				icon: 'success',
+				duration: 1500,
+				success: () => {
+					// 延迟跳转到成功页面
+					setTimeout(() => {
+						uni.redirectTo({
+							url: '/pages/success/success?type=retake&message=手部照片补拍完成,请等待系统审核'
+						});
+					}, 1500);
+				}
+			});
+		},
+		handleError(e) {
+			console.error('相机错误:', e.detail);
+			uni.showToast({
+				title: '相机启动失败,将使用系统相机',
+				icon: 'none'
+			});
+			this.fallbackToChooseImage();
+		},
+		fallbackToChooseImage() {
+			this.showCamera = false;
+			uni.chooseImage({
+				count: 1,
+				sourceType: ['camera'],
+				success: async (res) => {
+					try {
+						const uploadResult = await this.uploadImage(res.tempFilePaths[0]);
+						this.uploadedImages[this.currentStep.type] = uploadResult.data.url;
+						this.handlePhotoSuccess();
+					} catch (err) {
+						this.handlePhotoError();
+					}
+				},
+				fail: () => this.handlePhotoError()
+			});
+		},
+		handlePhotoSuccess() {
+			if (this.currentIndex < this.failedItems.length - 1) {
+				this.currentIndex++;
+				this.showCamera = true;
+				this.tempImagePath = '';
+			} else {
+				this.completeRetake();
+			}
+		},
+		handlePhotoError() {
+			uni.showToast({
+				title: '拍照失败,请重试',
+				icon: 'none'
+			});
+		}
+	}
+}
+</script>
+
+<style>
+.interview-container {
+	height: 100vh;
+	display: flex;
+	flex-direction: column;
+}
+
+.header {
+	padding: 40rpx;
+	text-align: center;
+}
+
+.title {
+	font-size: 36rpx;
+	font-weight: bold;
+}
+
+.subtitle {
+	font-size: 28rpx;
+	color: #666;
+	margin-top: 20rpx;
+}
+
+.camera-container {
+	position: fixed;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	background-color: #000;
+	z-index: 999;
+}
+
+.camera {
+	width: 100%;
+	height: 100%;
+}
+
+.gesture-overlay {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+	pointer-events: none;
+	z-index: 10;
+}
+
+.gesture-image {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 90%;
+	opacity: 0.5;
+	/* 设置图片混合模式,使绿色更突出 */
+	mix-blend-mode: screen;
+}
+
+.camera-tip {
+	position: absolute;
+	top: 20rpx;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	z-index: 11;
+}
+
+.guide-icon {
+	width:160rpx;
+	height: 200rpx;
+	background-color: rgba(0, 0, 0, 0.3);
+	border-radius: 20rpx;
+	overflow: hidden;
+}
+
+.mp-video-player {
+	width: 100%;
+	height: 100%;
+	object-fit: cover;
+}
+
+.tip-text {
+	position: absolute;
+	top:20rpx;
+	left: 30%;
+	color: #fff;
+	font-size: 28rpx;
+	margin-top: 16rpx;
+	background-color: rgba(0, 0, 0, 0.3);
+	padding: 8rpx 20rpx;
+	border-radius: 30rpx;
+}
+
+.capture-btn {
+	position: absolute;
+	bottom: 100rpx;
+	left: 50%;
+	transform: translateX(-50%);
+	width: 140rpx;
+	height: 140rpx;
+	border-radius: 50%;
+	background: #fff;
+	border: 10rpx solid #0039b3;
+	z-index: 999;
+}
+
+.preview-container {
+	flex: 1;
+	display: flex;
+	flex-direction: column;
+}
+
+.preview-container image {
+	flex: 1;
+	width: 100%;
+}
+
+.preview-buttons {
+	display: flex;
+	padding: 20rpx;
+	justify-content: space-around;
+}
+
+.btn-retake, .btn-confirm {
+	width: 45%;
+	height: 80rpx;
+	line-height: 80rpx;
+	text-align: center;
+	border-radius: 8rpx;
+}
+
+.btn-retake {
+	background: #f5f5f5;
+	color: #333;
+}
+
+.btn-confirm {
+	background: #003399;
+	color: #fff;
+}
+
+.progress-bar {
+	display: flex;
+	padding: 20rpx;
+	background: #fff;
+}
+
+.step-item {
+	flex: 1;
+	text-align: center;
+	font-size: 24rpx;
+	color: #999;
+	position: relative;
+	padding: 20rpx 0;
+}
+
+.step-item.active {
+	color: #003399;
+	font-weight: bold;
+}
+
+.step-item.completed {
+	color: #67c23a;
+}
+</style> 

+ 198 - 20
pages/interview_success/interview_success.vue

@@ -1,49 +1,151 @@
 <template>
 	<view class="success-container">
 		<view class="success-icon">
-			<image src="http://121.36.251.245:9000/minlong/ze-certificate%201%401x.png" mode="aspectFit"></image>
+			<image src="https://data.qicai321.com/minlong/dbf3202a-de17-4d1d-8b8c-d0d708c55552.png" mode="aspectFit"></image>
+		</view>
+		<view class="success-title">
+			<block v-if="isAnalyzing">照片解析中...</block>
+			<block v-else>恭喜完成全部检测</block>
+		</view>
+		
+		<view v-if="isAnalyzing" class="loading-container">
+			<view class="loading-spinner"></view>
+			<view class="loading-text">正在分析照片,请稍候...</view>
 		</view>
-		<view class="success-title">恭喜完成全部检测</view>
 		
-		<view class="check-list">
+		<view v-else class="check-list">
 			<view class="check-item" v-for="(item, index) in checkItems" :key="index">
 				<view class="check-index">{{index + 1}}</view>
 				<view class="check-name">{{item.name}}</view>
-				<view class="check-status">{{item.status}}</view>
+				<view class="check-status" :class="{'status-fail': !item.success}">
+					{{item.success ? '动作检测成功' : '动作检测失败'}}
+				</view>
 			</view>
 		</view>
 		
-		<view class="button-group">
-			<button class="btn-retake" @click="retake">重拍</button>
-			<button class="btn-next" @click="goNext">下一步</button>
+		<view class="button-group" v-if="!isAnalyzing">
+			<button class="btn-retake" @click="retake" :disabled="isAnalyzing">重新拍摄</button>
+			<button class="btn-next" @click="goNext" :disabled="isAnalyzing">
+				{{hasFailedItems ? '补拍' : '下一步'}}
+			</button>
 		</view>
 	</view>
 </template>
 
 <script>
+import { apiBaseUrl } from '@/common/config.js'; // 导入基础URL配置
 	export default {
 		data() {
 			return {
 				checkItems: [
-					{ name: '左手手心', status: '动作检测成功' },
-					{ name: '左手手背', status: '动作检测成功' },
-					{ name: '左手握拳', status: '动作检测成功' },
-					{ name: '右手手心', status: '动作检测成功' },
-					{ name: '右手手背', status: '动作检测成功' },
-					{ name: '右手握拳', status: '动作检测成功' }
-				]
+					{ name: '左手手心', success: true, type: 'left_palm', description: '展示完整的左手手掌' },
+					{ name: '左手手背', success: true, type: 'left_back', description: '展示完整的左手手背' },
+					{ name: '左手握拳', success: true, type: 'left_fist', description: '展示完整的左手握拳' },
+					{ name: '右手手心', success: true, type: 'right_palm', description: '展示完整的右手手掌' },
+					{ name: '右手手背', success: true, type: 'right_back', description: '展示完整的右手手背' },
+					{ name: '右手握拳', success: true, type: 'right_fist', description: '展示完整的右手握拳' }
+				],
+				maxRetries: 3,
+				currentRetry: 0,
+				retryInterval: 3000, // 3秒
+				applicationId: null,
+				isAnalyzing: false, // 添加分析状态标志
 			}
 		},
+		computed: {
+			hasFailedItems() {
+				return this.checkItems.some(item => !item.success)
+			},
+			failedItems() {
+				return this.checkItems.filter(item => !item.success)
+			}
+		},
+		onLoad(options) {
+			this.applicationId = uni.getStorageSync('appId')
+			this.fetchAnalysisResults()
+		},
 		methods: {
+			async fetchAnalysisResults() {
+				try {
+					this.isAnalyzing = true // 开始分析时设置状态
+					const response = await uni.request({
+						url: `${apiBaseUrl}/api/system/job/get_visual_analysis_results?application_id=${this.applicationId}`,
+					})
+					console.log(response)
+					
+					// 检查响应状态码
+					if (response.data.code === 2001) {
+						// 状态码2001表示正在处理中,继续轮询
+						setTimeout(() => {
+							this.fetchAnalysisResults()
+						}, this.retryInterval)
+						return
+					}
+					
+					// 处理成功响应
+					if (response.data.code === 2000 && response.data.data && response.data.data.photo_results && response.data.data.photo_results.length > 0) {
+						this.isAnalyzing = false // 分析完成
+						this.processResults(response.data.data.photo_results)
+					} else if (this.currentRetry < this.maxRetries) {
+						// 如果没有结果且未达到最大重试次数,则重试
+						this.currentRetry++
+						setTimeout(() => {
+							this.fetchAnalysisResults()
+						}, this.retryInterval)
+					} else {
+						this.isAnalyzing = false // 重试结束后也要关闭分析状态
+					}
+				} catch (error) {
+					console.error('获取分析结果失败:', error)
+					this.isAnalyzing = false // 发生错误时关闭分析状态
+				}
+			},
+			
+			processResults(photoResults) {
+				const results = {}
+				
+				photoResults.forEach(photo => {
+					// 根据description匹配对应的检测项
+					const checkItem = this.checkItems.find(item => 
+						photo.description === item.description
+					)
+					console.log(checkItem, photo)
+					if (checkItem) {
+						results[checkItem.type] = photo.part_verification.is_correct_part
+					}
+				})
+				
+				this.updateCheckResults(results)
+			},
+			updateCheckResults(results) {
+				this.checkItems = this.checkItems.map(item => ({
+					...item,
+					success: results[item.type] || false
+				}))
+			},
 			retake() {
-				// 返回到拍照页面
-				uni.navigateBack();
+				if (this.hasFailedItems) {
+					// 携带失败项信息跳转到拍照页面
+					uni.navigateTo({
+						url: '/pages/interview/interview?retake=true&failedItems=' + 
+							 JSON.stringify(this.failedItems.map(item => item.type))
+					})
+				} else {
+					uni.navigateBack()
+				}
 			},
 			goNext() {
-				// 关闭所有页面,打开新页面
-				uni.reLaunch({
-					url: '/pages/success/success'
-				});
+				if (!this.hasFailedItems) {
+					uni.reLaunch({
+						url: '/pages/success/success'
+					})
+				} else {
+					// 使用 redirectTo 替代 navigateTo
+					uni.reLaunch({
+						url: '/pages/interview_retake/interview_retake?failedItems=' + 
+							 JSON.stringify(this.failedItems)
+					})
+				}
 			}
 		}
 	}
@@ -113,6 +215,10 @@
 	color: #67c23a;
 }
 
+.status-fail {
+	color: #f56c6c;
+}
+
 .button-group {
 	display: flex;
 	width: 100%;
@@ -138,4 +244,76 @@
 	background-color: #003399;
 	color: #fff;
 }
+
+.btn-next:disabled {
+	background-color: #999;
+	opacity: 0.6;
+}
+
+.loading-container {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	background-color: rgba(255, 255, 255, 0.8);
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+}
+
+.loading-spinner {
+	border: 4px solid #f3f3f3;
+	border-top: 4px solid #003399;
+	border-radius: 50%;
+	width: 40rpx;
+	height: 40rpx;
+	animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+	0% { transform: rotate(0deg); }
+	100% { transform: rotate(360deg); }
+}
+
+.loading-text {
+	margin-top: 20rpx;
+	font-size: 28rpx;
+	color: #333;
+}
+
+/* 添加 loading 相关样式 */
+.loading-container {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	margin: 40rpx 0;
+}
+
+.loading-spinner {
+	width: 60rpx;
+	height: 60rpx;
+	border: 4rpx solid #f3f3f3;
+	border-top: 4rpx solid #003399;
+	border-radius: 50%;
+	animation: spin 1s linear infinite;
+	margin-bottom: 20rpx;
+}
+
+.loading-text {
+	font-size: 28rpx;
+	color: #666;
+}
+
+@keyframes spin {
+	0% { transform: rotate(0deg); }
+	100% { transform: rotate(360deg); }
+}
+
+/* 修改按钮禁用状态样式 */
+.btn-retake:disabled, .btn-next:disabled {
+	opacity: 0.5;
+	cursor: not-allowed;
+}
 </style>

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

@@ -23,6 +23,7 @@ if (!Math) {
   "./pages/followUp/followUp.js";
   "./pages/uploadResume/uploadResume.js";
   "./pages/posture-guide/posture-guide.js";
+  "./pages/interview_retake/interview_retake.js";
 }
 const _sfc_main = {
   onLaunch: function() {

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

@@ -19,7 +19,8 @@
     "pages/job-detail/job-detail",
     "pages/followUp/followUp",
     "pages/uploadResume/uploadResume",
-    "pages/posture-guide/posture-guide"
+    "pages/posture-guide/posture-guide",
+    "pages/interview_retake/interview_retake"
   ],
   "window": {
     "navigationBarTextStyle": "black",

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

@@ -40,7 +40,7 @@ const _sfc_main = {
       // 其他属性保持不变...
       questions: [],
       // 添加新属性存储API返回的问题数据
-      introVideoUrl: "http://121.36.251.245:9000/minlong/tenant_1/general_uploads/880a117c8fcc4196b0d8c0750608d373.mp4",
+      introVideoUrl: "https://data.qicai321.com/minlong/ee4d9cce-c3d5-4350-8c6e-684283827897.mp4",
       // 保留介绍视频
       isRecording: false,
       recordingTimer: null,

+ 50 - 28
unpackage/dist/dev/mp-weixin/pages/identity-verify/identity-verify.js

@@ -42,7 +42,7 @@ const _sfc_main = {
       // 其他属性保持不变...
       questions: [],
       // 添加新属性存储API返回的问题数据
-      introVideoUrl: "http://121.36.251.245:9000/minlong/tenant_1/general_uploads/880a117c8fcc4196b0d8c0750608d373.mp4",
+      introVideoUrl: "https://data.qicai321.com/minlong/ee4d9cce-c3d5-4350-8c6e-684283827897.mp4",
       // 保留介绍视频
       isRecording: false,
       recordingTimer: null,
@@ -137,8 +137,10 @@ const _sfc_main = {
       // 录制进度百分比
       progressColor: "#05dc8b",
       // 进度条颜色
-      progressBgColor: "rgba(0, 0, 0,0.3)"
+      progressBgColor: "rgba(0, 0, 0,0.3)",
       // 进度条背景色
+      parentQuestion: ""
+      // 添加父问题存储
     };
   },
   mounted() {
@@ -602,7 +604,6 @@ const _sfc_main = {
       }
       if (this.isFollowUpQuestion) {
         console.log("追问问题视频播放完成");
-        this.isFollowUpQuestion = false;
         this.showStartRecordingButton = true;
         this.startCountdown();
         return;
@@ -613,6 +614,10 @@ const _sfc_main = {
       } else {
         this.showAnswerButton = true;
       }
+      if (this.isFollowUpQuestion) {
+        this.isFollowUpQuestion = false;
+        this.currentFollowUpQuestion = null;
+      }
     },
     // 添加新方法:开始倒计时
     startCountdown() {
@@ -2208,11 +2213,14 @@ const _sfc_main = {
         this.proceedToNextQuestion();
       }
     },
-    // 添加新方法:播放追问问题视频
+    // 修改播放追问问题视频的方法
     playFollowUpQuestionVideo(followUpQuestion) {
       console.log("播放追问问题视频:", followUpQuestion.digital_human_video_url);
+      this.parentQuestion = this.currentSubtitle;
       this.videoUrl = followUpQuestion.digital_human_video_url;
       this.videoPlaying = true;
+      this.isFollowUpQuestion = true;
+      this.currentFollowUpQuestion = followUpQuestion;
       const followUpSubtitles = [{
         startTime: 0,
         endTime: 60,
@@ -2254,6 +2262,10 @@ const _sfc_main = {
     // 修改进度百分比的计算方式
     progressPercent() {
       return Math.min(Math.round(this.recordingTimerCount / this.maxRecordingTime * 100), 100);
+    },
+    // 添加总题数计算属性
+    totalQuestions() {
+      return this.questions.length || 5;
     }
   }
 };
@@ -2270,26 +2282,36 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
     h: common_vendor.o((...args) => $options.handleAnswerButtonClick && $options.handleAnswerButtonClick(...args))
   } : {}, {
     i: $data.currentSubtitle
-  }, $data.currentSubtitle ? {
-    j: common_vendor.t($data.currentSubtitle)
+  }, $data.currentSubtitle ? common_vendor.e({
+    j: $data.currentVideoIndex > 0
+  }, $data.currentVideoIndex > 0 ? {
+    k: common_vendor.t($data.currentVideoIndex),
+    l: common_vendor.t($options.totalQuestions)
   } : {}, {
-    k: $data.useMiniProgramCameraComponent
+    m: !$data.isFollowUpQuestion
+  }, !$data.isFollowUpQuestion ? {
+    n: common_vendor.t($data.currentSubtitle)
+  } : {
+    o: common_vendor.t($data.parentQuestion),
+    p: common_vendor.t($data.currentSubtitle)
+  }) : {}, {
+    q: $data.useMiniProgramCameraComponent
   }, $data.useMiniProgramCameraComponent ? {
-    l: common_vendor.o((...args) => $options.handleCameraError && $options.handleCameraError(...args))
+    r: common_vendor.o((...args) => $options.handleCameraError && $options.handleCameraError(...args))
   } : {}, {
-    m: $data.loading
+    s: $data.loading
   }, $data.loading ? {} : {}, {
-    n: $data.showDebugInfo
+    t: $data.showDebugInfo
   }, $data.showDebugInfo ? common_vendor.e({
-    o: $data.assistantResponse
+    v: $data.assistantResponse
   }, $data.assistantResponse ? {
-    p: common_vendor.t($data.assistantResponse)
+    w: common_vendor.t($data.assistantResponse)
   } : {}, {
-    q: $data.audioTranscript
+    x: $data.audioTranscript
   }, $data.audioTranscript ? {
-    r: common_vendor.t($data.audioTranscript)
+    y: common_vendor.t($data.audioTranscript)
   } : {}, {
-    s: common_vendor.f($data.processedResponses, (item, index, i0) => {
+    z: common_vendor.f($data.processedResponses, (item, index, i0) => {
       return common_vendor.e({
         a: item.role
       }, item.role ? {
@@ -2303,30 +2325,30 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
       });
     })
   }) : {}, {
-    t: $data.showStopRecordingButton
+    A: $data.showStopRecordingButton
   }, $data.showStopRecordingButton ? {
-    v: common_vendor.o((...args) => $options.stopRecordingAnswer && $options.stopRecordingAnswer(...args))
+    B: common_vendor.o((...args) => $options.stopRecordingAnswer && $options.stopRecordingAnswer(...args))
   } : {}, {
-    w: $data.isRecording
+    C: $data.isRecording
   }, $data.isRecording ? {
-    x: common_vendor.t($data.recordingTimeDisplay),
-    y: `conic-gradient(${$data.progressColor} ${$options.progressPercent}%, ${$data.progressBgColor} 0%)`
+    D: common_vendor.t($data.recordingTimeDisplay),
+    E: `conic-gradient(${$data.progressColor} ${$options.progressPercent}%, ${$data.progressBgColor} 0%)`
   } : {}, {
-    z: $data.showStartRecordingButton
+    F: $data.showStartRecordingButton
   }, $data.showStartRecordingButton ? {
-    A: common_vendor.o((...args) => $options.handleStartRecordingClick && $options.handleStartRecordingClick(...args))
+    G: common_vendor.o((...args) => $options.handleStartRecordingClick && $options.handleStartRecordingClick(...args))
   } : {}, {
-    B: $data.showRetryButton
+    H: $data.showRetryButton
   }, $data.showRetryButton ? {
-    C: common_vendor.o((...args) => $options.retryVideoUpload && $options.retryVideoUpload(...args))
+    I: common_vendor.o((...args) => $options.retryVideoUpload && $options.retryVideoUpload(...args))
   } : {}, {
-    D: $data.showCountdown
+    J: $data.showCountdown
   }, $data.showCountdown ? {
-    E: common_vendor.t($data.countdownValue)
+    K: common_vendor.t($data.countdownValue)
   } : {}, {
-    F: $data.showRerecordButton
+    L: $data.showRerecordButton
   }, $data.showRerecordButton ? {
-    G: common_vendor.o((...args) => $options.handleRerecordButtonClick && $options.handleRerecordButtonClick(...args))
+    M: common_vendor.o((...args) => $options.handleRerecordButtonClick && $options.handleRerecordButtonClick(...args))
   } : {});
 }
 const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-464e78c6"]]);

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
unpackage/dist/dev/mp-weixin/pages/identity-verify/identity-verify.wxml


+ 124 - 15
unpackage/dist/dev/mp-weixin/pages/identity-verify/identity-verify.wxss

@@ -87,9 +87,9 @@ video.data-v-464e78c6::-webkit-media-controls-fullscreen-button {
 .subtitle-overlay.data-v-464e78c6 {
   position: absolute;
   bottom: 180px;
-  left: 5%;
+  left: 10%;
   width: 80%;
-  padding: 15px 20px;
+  padding: 0; /* 移除内边距,由内部元素控制 */
   border-radius: 15px;
   background-color: rgba(255, 255, 255, 0.9);
   color: #333;
@@ -101,25 +101,30 @@ video.data-v-464e78c6::-webkit-media-controls-fullscreen-button {
   box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
   display: flex;
   flex-direction: column;
-  gap: 8px;
+  overflow: hidden; /* 确保圆角效果 */
 }
-.subtitle-main.data-v-464e78c6 {
-  font-weight: 500;
+
+/* 添加题号header样式 */
+.subtitle-header.data-v-464e78c6 {
+/*   background-color: rgba(0, 0, 0, 0.05); */
+  padding: 8px 20px;
+  /* border-bottom: 1px solid rgba(0, 0, 0, 0.05); */
 }
-.subtitle-translation.data-v-464e78c6 {
+.question-tag.data-v-464e78c6 {
   font-size: 14px;
   color: #666;
-  font-style: italic;
+  font-weight: 500;
 }
 
-/* 添加计时器样式,右侧显示橙色圆点和时间 */
-.subtitle-overlay.data-v-464e78c6::after {
-  content: attr(data-time);
-  position: absolute;
-  right: 20px;
-  bottom: 15px;
-  color: #333;
-  font-size: 14px;
+/* 调整主字幕样式 */
+.subtitle-main.data-v-464e78c6 {
+  padding: 15px 20px ;
+  font-weight: 500;
+}
+
+/* 移除之前的题号显示器 */
+.question-counter.data-v-464e78c6 {
+  display: none;
 }
 .control-panel.data-v-464e78c6 {
   padding: 15px;
@@ -456,3 +461,107 @@ video.data-v-464e78c6::-webkit-media-controls-fullscreen-button {
   font-weight: bold;
    color: #05dc8b;
 }
+
+/* 添加题号显示 */
+.question-counter.data-v-464e78c6 {
+  position: absolute;
+  top: 20px;
+  right: 130px; /* 调整位置,避免与用户摄像头重叠 */
+  background-color: rgba(255, 255, 255, 0.9);
+  padding: 6px 12px;
+  border-radius: 15px;
+  z-index: 20;
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
+  display: flex;
+  align-items: center;
+}
+.question-number.data-v-464e78c6 {
+  color: #333;
+  font-size: 14px;
+  font-weight: 500;
+}
+
+/* 追问问题容器样式 */
+.followup-container.data-v-464e78c6 {
+  width: 100%;
+}
+
+/* 追问分隔线 */
+.followup-divider.data-v-464e78c6 {
+  height: 1px;
+  background-color: rgba(0, 0, 0, 0.05);
+  margin: 0;
+}
+
+/* 追问内容样式 */
+.followup-content.data-v-464e78c6 {
+  padding: 12px 20px;
+  display: flex;
+  align-items: flex-start;
+  gap: 8px;
+}
+
+/* 追问标签样式 */
+.followup-label.data-v-464e78c6 {
+  color: #fb752f;
+  font-size: 14px;
+  font-weight: 500;
+}
+
+/* 追问文本样式 */
+.followup-text.data-v-464e78c6 {
+  color: #333;
+  font-size: 14px;
+  line-height: 1.5;
+  flex: 1;
+}
+
+/* 调整主字幕样式,确保有足够空间 */
+.subtitle-main.data-v-464e78c6 {
+  padding:15px 20px ;
+  font-weight: 500;
+  border-bottom: none; /* 移除底部边框,由追问分隔线处理 */
+}
+
+/* 父问题样式 */
+.parent-question.data-v-464e78c6 {
+  padding: 15px 20px;
+  font-weight: 500;
+  color: #333;
+}
+
+/* 追问容器样式调整 */
+.followup-container.data-v-464e78c6 {
+  width: 100%;
+ /*  background-color: rgba(0, 0, 0, 0.02);  *//* 轻微的背景色区分 */
+}
+
+/* 分隔线样式 */
+.followup-divider.data-v-464e78c6 {
+  height: 1px;
+  background-color: rgba(0, 0, 0, 0.05);
+  margin: 0;
+}
+
+/* 追问内容样式 */
+.followup-content.data-v-464e78c6 {
+  padding: 12px 20px;
+  display: flex;
+  align-items: flex-start;
+  gap: 8px;
+}
+
+/* 追问标签样式 */
+.followup-label.data-v-464e78c6 {
+  color: #fb752f;
+  font-size: 14px;
+  font-weight: 500;
+}
+
+/* 追问文本样式 */
+.followup-text.data-v-464e78c6 {
+  color: #333;
+  font-size: 14px;
+  line-height: 1.5;
+  flex: 1;
+}

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

@@ -13,8 +13,8 @@ const _sfc_main = {
       videoPlaying: false,
       showDebugInfo: false,
       // 设置为true可以显示调试信息
-      videoUrl: "http://121.36.251.245:9000/minlong/tenant_1/general_uploads/abdaa6fda8494e3a8613304743ed0433.mp4",
-      // 用于存储AI数字人视频URL
+      videoUrl: "https://data.qicai321.com/minlong/1809457d-30c5-40bb-b6d8-a8c2e8e1e9e2.mp4",
+      //'http://121.36.251.245:9000/minlong/tenant_1/general_uploads/abdaa6fda8494e3a8613304743ed0433.mp4', // 用于存储AI数字人视频URL
       showReplayButton: false,
       cameraStream: null,
       // 存储摄像头流
@@ -74,7 +74,7 @@ const _sfc_main = {
       currentVideoIndex: 0,
       // 当前播放的视频索引
       videoList: [
-        "http://121.36.251.245:9000/minlong/tenant_1/general_uploads/abdaa6fda8494e3a8613304743ed0433.mp4"
+        "https://data.qicai321.com/minlong/1809457d-30c5-40bb-b6d8-a8c2e8e1e9e2.mp4"
         //结束
       ],
       isRecording: false,

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

@@ -265,21 +265,36 @@ const _sfc_main = {
         common_vendor.index.showLoading({
           title: "处理中..."
         });
-        setTimeout(() => {
-          common_vendor.index.hideLoading();
-          common_vendor.index.showToast({
-            title: "手部照片采集完成",
-            icon: "success",
-            duration: 2e3,
-            success: () => {
-              setTimeout(() => {
-                common_vendor.index.redirectTo({
-                  url: "/pages/interview_success/interview_success"
-                });
-              }, 100);
-            }
-          });
-        }, 500);
+        const application_id = common_vendor.index.getStorageSync("appId");
+        const tenant_id = common_vendor.index.getStorageSync("tenant_id") || "1";
+        common_vendor.index.request({
+          url: `${common_config.apiBaseUrl}/api/system/job/trigger_visual_analysis?application_id=${application_id}&tenant_id=${tenant_id}`,
+          method: "POST",
+          success: (res) => {
+            console.log("视觉分析触发成功:", res);
+            common_vendor.index.hideLoading();
+            common_vendor.index.showToast({
+              title: "手部照片采集完成",
+              icon: "success",
+              duration: 2e3,
+              success: () => {
+                setTimeout(() => {
+                  common_vendor.index.redirectTo({
+                    url: "/pages/interview_success/interview_success"
+                  });
+                }, 100);
+              }
+            });
+          },
+          fail: (err) => {
+            console.error("视觉分析触发失败:", err);
+            common_vendor.index.hideLoading();
+            common_vendor.index.showToast({
+              title: "处理失败,请重试",
+              icon: "none"
+            });
+          }
+        });
       } else {
         common_vendor.index.showToast({
           title: "请完成所有照片拍摄",

+ 221 - 0
unpackage/dist/dev/mp-weixin/pages/interview_retake/interview_retake.js

@@ -0,0 +1,221 @@
+"use strict";
+const common_vendor = require("../../common/vendor.js");
+const common_config = require("../../common/config.js");
+const _sfc_main = {
+  data() {
+    return {
+      failedItems: [],
+      currentIndex: 0,
+      showCamera: true,
+      tempImagePath: "",
+      uploadedImages: {},
+      isH5: false,
+      cameraPosition: "front",
+      cameraMode: "normal",
+      cameraContext: null,
+      cameraInitRetries: 0,
+      // 添加手势引导图片
+      gestureOverlays: {
+        left_palm: "http://data.qicai321.com/minlong/18590378-6792-4a26-be18-ad9f2bcc4159.png",
+        left_back: "http://data.qicai321.com/minlong/e08cff2b-3b1d-478f-a62b-766b38445a16.png",
+        left_fist: "http://data.qicai321.com/minlong/d30ccab7-9cfe-4386-bf0e-ac1f0045bd76.png",
+        right_palm: "http://data.qicai321.com/minlong/8068a698-ac40-4a5d-a737-54dba47a668d.png",
+        right_back: "http://data.qicai321.com/minlong/f04a1f7e-4f79-49c7-9a02-bbad296672ea.png",
+        right_fist: "http://data.qicai321.com/minlong/1d9bbc36-2fb8-4489-bff6-c36e7a31bd37.png"
+      },
+      // 添加引导视频
+      guideVideos: {
+        left_palm: "http://data.qicai321.com/minlong/fd080e3b-67be-4ce7-87a3-58d047cfbbb1.mp4",
+        left_back: "http://data.qicai321.com/minlong/394a858c-672a-44bd-8206-450913863900.mp4",
+        left_fist: "http://data.qicai321.com/minlong/37153e68-a8f0-4a93-b677-5e5678379243.mp4",
+        right_palm: "http://data.qicai321.com/minlong/ebf6e43f-ffdf-49bb-8b00-adeff9ce1b56.mp4",
+        right_back: "http://data.qicai321.com/minlong/e0ca45e2-af3e-42a0-9dd1-4de545e9c720.mp4",
+        right_fist: "http://data.qicai321.com/minlong/77028dd6-22bb-41b4-8879-19699e8b1dbb.mp4"
+      }
+    };
+  },
+  computed: {
+    currentStep() {
+      if (!this.failedItems.length)
+        return null;
+      const current = this.failedItems[this.currentIndex];
+      return {
+        ...current,
+        tipText: `${current.name}`,
+        tipImage: this.gestureOverlays[current.type],
+        guideVideo: this.guideVideos[current.type]
+      };
+    }
+  },
+  onLoad(options) {
+    if (options.failedItems) {
+      const items = JSON.parse(options.failedItems);
+      this.failedItems = items.map((item) => ({
+        name: this.getNameByType(item.type),
+        type: item.type
+      }));
+    }
+  },
+  methods: {
+    getNameByType(type) {
+      const nameMap = {
+        left_palm: "展示完整的左手手掌",
+        left_back: "展示完整的左手手背",
+        left_fist: "展示完整的左手握拳",
+        right_palm: "展示完整的右手手掌",
+        right_back: "展示完整的右手手背",
+        right_fist: "展示完整的右手握拳"
+      };
+      return nameMap[type] || "";
+    },
+    takePhoto() {
+      const camera = common_vendor.index.createCameraContext();
+      camera.takePhoto({
+        quality: "high",
+        success: (res) => {
+          this.tempImagePath = res.tempImagePath;
+          this.showCamera = false;
+        },
+        fail: (err) => {
+          common_vendor.index.showToast({
+            title: "拍照失败",
+            icon: "none"
+          });
+        }
+      });
+    },
+    retakePhoto() {
+      this.showCamera = true;
+      this.tempImagePath = "";
+    },
+    async confirmPhoto() {
+      try {
+        const uploadResult = await this.uploadImage(this.tempImagePath);
+        this.uploadedImages[this.currentStep.type] = uploadResult.url;
+        if (this.currentIndex < this.failedItems.length - 1) {
+          this.currentIndex++;
+          this.showCamera = true;
+          this.tempImagePath = "";
+        } else {
+          this.completeRetake();
+        }
+      } catch (err) {
+        common_vendor.index.showToast({
+          title: "上传失败,请重试",
+          icon: "none"
+        });
+      }
+    },
+    async uploadImage(tempFilePath) {
+      const tenant_id = common_vendor.index.getStorageSync("tenant_id") || "1";
+      const description = `补拍-${this.currentStep.name}`;
+      return new Promise((resolve, reject) => {
+        common_vendor.index.uploadFile({
+          url: `${common_config.apiBaseUrl}/job/upload_posture_photo`,
+          filePath: tempFilePath,
+          name: "photo",
+          formData: {
+            "application_id": common_vendor.index.getStorageSync("appId"),
+            "tenant_id": tenant_id,
+            "description": description
+          },
+          success: (uploadRes) => {
+            try {
+              const result = typeof uploadRes.data === "string" ? JSON.parse(uploadRes.data) : uploadRes.data;
+              resolve(result);
+            } catch (e) {
+              reject(new Error("解析上传结果失败"));
+            }
+          },
+          fail: reject
+        });
+      });
+    },
+    completeRetake() {
+      const pages = getCurrentPages();
+      const prevPage = pages[pages.length - 2];
+      if (prevPage && prevPage.$vm) {
+        prevPage.$vm.updateCheckResults(this.uploadedImages);
+      }
+      common_vendor.index.showToast({
+        title: "补拍完成",
+        icon: "success",
+        duration: 1500,
+        success: () => {
+          setTimeout(() => {
+            common_vendor.index.redirectTo({
+              url: "/pages/success/success?type=retake&message=手部照片补拍完成,请等待系统审核"
+            });
+          }, 1500);
+        }
+      });
+    },
+    handleError(e) {
+      console.error("相机错误:", e.detail);
+      common_vendor.index.showToast({
+        title: "相机启动失败,将使用系统相机",
+        icon: "none"
+      });
+      this.fallbackToChooseImage();
+    },
+    fallbackToChooseImage() {
+      this.showCamera = false;
+      common_vendor.index.chooseImage({
+        count: 1,
+        sourceType: ["camera"],
+        success: async (res) => {
+          try {
+            const uploadResult = await this.uploadImage(res.tempFilePaths[0]);
+            this.uploadedImages[this.currentStep.type] = uploadResult.data.url;
+            this.handlePhotoSuccess();
+          } catch (err) {
+            this.handlePhotoError();
+          }
+        },
+        fail: () => this.handlePhotoError()
+      });
+    },
+    handlePhotoSuccess() {
+      if (this.currentIndex < this.failedItems.length - 1) {
+        this.currentIndex++;
+        this.showCamera = true;
+        this.tempImagePath = "";
+      } else {
+        this.completeRetake();
+      }
+    },
+    handlePhotoError() {
+      common_vendor.index.showToast({
+        title: "拍照失败,请重试",
+        icon: "none"
+      });
+    }
+  }
+};
+function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
+  return common_vendor.e({
+    a: $data.showCamera
+  }, $data.showCamera ? {
+    b: $options.currentStep.tipImage,
+    c: common_vendor.t($options.currentStep.tipText),
+    d: $options.currentStep.guideVideo,
+    e: common_vendor.s($options.currentStep.type.includes("right_") ? "left:30rpx" : "right:30rpx"),
+    f: common_vendor.o((...args) => $options.handleError && $options.handleError(...args)),
+    g: common_vendor.o((...args) => $options.takePhoto && $options.takePhoto(...args))
+  } : {
+    h: $data.tempImagePath,
+    i: common_vendor.o((...args) => $options.retakePhoto && $options.retakePhoto(...args)),
+    j: common_vendor.o((...args) => $options.confirmPhoto && $options.confirmPhoto(...args))
+  }, {
+    k: common_vendor.f($data.failedItems, (item, index, i0) => {
+      return {
+        a: common_vendor.t(item.name),
+        b: index,
+        c: $data.currentIndex === index ? 1 : "",
+        d: index < $data.currentIndex ? 1 : ""
+      };
+    })
+  });
+}
+const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
+wx.createPage(MiniProgramPage);

+ 5 - 0
unpackage/dist/dev/mp-weixin/pages/interview_retake/interview_retake.json

@@ -0,0 +1,5 @@
+{
+  "navigationBarTitleText": "体态照片补拍",
+  "navigationStyle": "default",
+  "usingComponents": {}
+}

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

@@ -0,0 +1 @@
+<view class="interview-container"><view class="header"><view class="title">补拍检测</view><view class="subtitle">请按提示重新拍摄失败项</view></view><view wx:if="{{a}}" class="camera-container"><camera class="camera" device-position="front" flash="off" binderror="{{f}}"><view class="gesture-overlay"><image class="gesture-image" src="{{b}}"></image><cover-view class="tip-text">{{c}}</cover-view></view><view class="camera-tip" style="{{e}}"><view class="guide-icon"><video src="{{d}}" id="mpVideo" autoplay class="mp-video-player" controls="{{false}}" loop muted object-fit="contain"></video></view></view></camera><view class="capture-btn" bindtap="{{g}}"></view></view><view wx:else class="preview-container"><image src="{{h}}" mode="aspectFit"></image><view class="preview-buttons"><button class="btn-retake" bindtap="{{i}}">重拍</button><button class="btn-confirm" bindtap="{{j}}">确认</button></view></view><view class="progress-bar"><view wx:for="{{k}}" wx:for-item="item" wx:key="b" class="{{['step-item', item.c && 'active', item.d && 'completed']}}">{{item.a}}</view></view></view>

+ 147 - 0
unpackage/dist/dev/mp-weixin/pages/interview_retake/interview_retake.wxss

@@ -0,0 +1,147 @@
+
+.interview-container {
+	height: 100vh;
+	display: flex;
+	flex-direction: column;
+}
+.header {
+	padding: 40rpx;
+	text-align: center;
+}
+.title {
+	font-size: 36rpx;
+	font-weight: bold;
+}
+.subtitle {
+	font-size: 28rpx;
+	color: #666;
+	margin-top: 20rpx;
+}
+.camera-container {
+	position: fixed;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	background-color: #000;
+	z-index: 999;
+}
+.camera {
+	width: 100%;
+	height: 100%;
+}
+.gesture-overlay {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+	pointer-events: none;
+	z-index: 10;
+}
+.gesture-image {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 90%;
+	opacity: 0.5;
+	/* 设置图片混合模式,使绿色更突出 */
+	mix-blend-mode: screen;
+}
+.camera-tip {
+	position: absolute;
+	top: 20rpx;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	z-index: 11;
+}
+.guide-icon {
+	width:160rpx;
+	height: 200rpx;
+	background-color: rgba(0, 0, 0, 0.3);
+	border-radius: 20rpx;
+	overflow: hidden;
+}
+.mp-video-player {
+	width: 100%;
+	height: 100%;
+	object-fit: cover;
+}
+.tip-text {
+	position: absolute;
+	top:20rpx;
+	left: 30%;
+	color: #fff;
+	font-size: 28rpx;
+	margin-top: 16rpx;
+	background-color: rgba(0, 0, 0, 0.3);
+	padding: 8rpx 20rpx;
+	border-radius: 30rpx;
+}
+.capture-btn {
+	position: absolute;
+	bottom: 100rpx;
+	left: 50%;
+	transform: translateX(-50%);
+	width: 140rpx;
+	height: 140rpx;
+	border-radius: 50%;
+	background: #fff;
+	border: 10rpx solid #0039b3;
+	z-index: 999;
+}
+.preview-container {
+	flex: 1;
+	display: flex;
+	flex-direction: column;
+}
+.preview-container image {
+	flex: 1;
+	width: 100%;
+}
+.preview-buttons {
+	display: flex;
+	padding: 20rpx;
+	justify-content: space-around;
+}
+.btn-retake, .btn-confirm {
+	width: 45%;
+	height: 80rpx;
+	line-height: 80rpx;
+	text-align: center;
+	border-radius: 8rpx;
+}
+.btn-retake {
+	background: #f5f5f5;
+	color: #333;
+}
+.btn-confirm {
+	background: #003399;
+	color: #fff;
+}
+.progress-bar {
+	display: flex;
+	padding: 20rpx;
+	background: #fff;
+}
+.step-item {
+	flex: 1;
+	text-align: center;
+	font-size: 24rpx;
+	color: #999;
+	position: relative;
+	padding: 20rpx 0;
+}
+.step-item.active {
+	color: #003399;
+	font-weight: bold;
+}
+.step-item.completed {
+	color: #67c23a;
+}

+ 110 - 19
unpackage/dist/dev/mp-weixin/pages/interview_success/interview_success.js

@@ -1,42 +1,133 @@
 "use strict";
 const common_vendor = require("../../common/vendor.js");
+const common_config = require("../../common/config.js");
 const _sfc_main = {
   data() {
     return {
       checkItems: [
-        { name: "左手手心", status: "动作检测成功" },
-        { name: "左手手背", status: "动作检测成功" },
-        { name: "左手握拳", status: "动作检测成功" },
-        { name: "右手手心", status: "动作检测成功" },
-        { name: "右手手背", status: "动作检测成功" },
-        { name: "右手握拳", status: "动作检测成功" }
-      ]
+        { name: "左手手心", success: true, type: "left_palm", description: "展示完整的左手手掌" },
+        { name: "左手手背", success: true, type: "left_back", description: "展示完整的左手手背" },
+        { name: "左手握拳", success: true, type: "left_fist", description: "展示完整的左手握拳" },
+        { name: "右手手心", success: true, type: "right_palm", description: "展示完整的右手手掌" },
+        { name: "右手手背", success: true, type: "right_back", description: "展示完整的右手手背" },
+        { name: "右手握拳", success: true, type: "right_fist", description: "展示完整的右手握拳" }
+      ],
+      maxRetries: 3,
+      currentRetry: 0,
+      retryInterval: 3e3,
+      // 3秒
+      applicationId: null,
+      isAnalyzing: false
+      // 添加分析状态标志
     };
   },
+  computed: {
+    hasFailedItems() {
+      return this.checkItems.some((item) => !item.success);
+    },
+    failedItems() {
+      return this.checkItems.filter((item) => !item.success);
+    }
+  },
+  onLoad(options) {
+    this.applicationId = common_vendor.index.getStorageSync("appId");
+    this.fetchAnalysisResults();
+  },
   methods: {
+    async fetchAnalysisResults() {
+      try {
+        this.isAnalyzing = true;
+        const response = await common_vendor.index.request({
+          url: `${common_config.apiBaseUrl}/api/system/job/get_visual_analysis_results?application_id=${this.applicationId}`
+        });
+        console.log(response);
+        if (response.data.code === 2001) {
+          setTimeout(() => {
+            this.fetchAnalysisResults();
+          }, this.retryInterval);
+          return;
+        }
+        if (response.data.code === 2e3 && response.data.data && response.data.data.photo_results && response.data.data.photo_results.length > 0) {
+          this.isAnalyzing = false;
+          this.processResults(response.data.data.photo_results);
+        } else if (this.currentRetry < this.maxRetries) {
+          this.currentRetry++;
+          setTimeout(() => {
+            this.fetchAnalysisResults();
+          }, this.retryInterval);
+        } else {
+          this.isAnalyzing = false;
+        }
+      } catch (error) {
+        console.error("获取分析结果失败:", error);
+        this.isAnalyzing = false;
+      }
+    },
+    processResults(photoResults) {
+      const results = {};
+      photoResults.forEach((photo) => {
+        const checkItem = this.checkItems.find(
+          (item) => photo.description === item.description
+        );
+        console.log(checkItem, photo);
+        if (checkItem) {
+          results[checkItem.type] = photo.part_verification.is_correct_part;
+        }
+      });
+      this.updateCheckResults(results);
+    },
+    updateCheckResults(results) {
+      this.checkItems = this.checkItems.map((item) => ({
+        ...item,
+        success: results[item.type] || false
+      }));
+    },
     retake() {
-      common_vendor.index.navigateBack();
+      if (this.hasFailedItems) {
+        common_vendor.index.navigateTo({
+          url: "/pages/interview/interview?retake=true&failedItems=" + JSON.stringify(this.failedItems.map((item) => item.type))
+        });
+      } else {
+        common_vendor.index.navigateBack();
+      }
     },
     goNext() {
-      common_vendor.index.reLaunch({
-        url: "/pages/success/success"
-      });
+      if (!this.hasFailedItems) {
+        common_vendor.index.reLaunch({
+          url: "/pages/success/success"
+        });
+      } else {
+        common_vendor.index.reLaunch({
+          url: "/pages/interview_retake/interview_retake?failedItems=" + JSON.stringify(this.failedItems)
+        });
+      }
     }
   }
 };
 function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
-  return {
-    a: common_vendor.f($data.checkItems, (item, index, i0) => {
+  return common_vendor.e({
+    a: $data.isAnalyzing
+  }, $data.isAnalyzing ? {} : {}, {
+    b: $data.isAnalyzing
+  }, $data.isAnalyzing ? {} : {
+    c: common_vendor.f($data.checkItems, (item, index, i0) => {
       return {
         a: common_vendor.t(index + 1),
         b: common_vendor.t(item.name),
-        c: common_vendor.t(item.status),
-        d: index
+        c: common_vendor.t(item.success ? "动作检测成功" : "动作检测失败"),
+        d: !item.success ? 1 : "",
+        e: index
       };
-    }),
-    b: common_vendor.o((...args) => $options.retake && $options.retake(...args)),
-    c: common_vendor.o((...args) => $options.goNext && $options.goNext(...args))
-  };
+    })
+  }, {
+    d: !$data.isAnalyzing
+  }, !$data.isAnalyzing ? {
+    e: common_vendor.o((...args) => $options.retake && $options.retake(...args)),
+    f: $data.isAnalyzing,
+    g: common_vendor.t($options.hasFailedItems ? "补拍" : "下一步"),
+    h: common_vendor.o((...args) => $options.goNext && $options.goNext(...args)),
+    i: $data.isAnalyzing
+  } : {});
 }
 const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render]]);
 wx.createPage(MiniProgramPage);

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

@@ -1 +1 @@
-<view class="success-container"><view class="success-icon"><image src="http://121.36.251.245:9000/minlong/ze-certificate%201%401x.png" mode="aspectFit"></image></view><view class="success-title">恭喜完成全部检测</view><view class="check-list"><view wx:for="{{a}}" wx:for-item="item" wx:key="d" class="check-item"><view class="check-index">{{item.a}}</view><view class="check-name">{{item.b}}</view><view class="check-status">{{item.c}}</view></view></view><view class="button-group"><button class="btn-retake" bindtap="{{b}}">重拍</button><button class="btn-next" bindtap="{{c}}">下一步</button></view></view>
+<view class="success-container"><view class="success-icon"><image src="https://data.qicai321.com/minlong/dbf3202a-de17-4d1d-8b8c-d0d708c55552.png" mode="aspectFit"></image></view><view class="success-title"><block wx:if="{{a}}">照片解析中...</block><block wx:else>恭喜完成全部检测</block></view><view wx:if="{{b}}" class="loading-container"><view class="loading-spinner"></view><view class="loading-text">正在分析照片,请稍候...</view></view><view wx:else class="check-list"><view wx:for="{{c}}" wx:for-item="item" wx:key="e" class="check-item"><view class="check-index">{{item.a}}</view><view class="check-name">{{item.b}}</view><view class="{{['check-status', item.d && 'status-fail']}}">{{item.c}}</view></view></view><view wx:if="{{d}}" class="button-group"><button class="btn-retake" bindtap="{{e}}" disabled="{{f}}">重新拍摄</button><button class="btn-next" bindtap="{{h}}" disabled="{{i}}">{{g}}</button></view></view>

+ 71 - 0
unpackage/dist/dev/mp-weixin/pages/interview_success/interview_success.wxss

@@ -53,6 +53,9 @@
 	font-size: 28rpx;
 	color: #67c23a;
 }
+.status-fail {
+	color: #f56c6c;
+}
 .button-group {
 	display: flex;
 	width: 100%;
@@ -75,3 +78,71 @@
 	background-color: #003399;
 	color: #fff;
 }
+.btn-next:disabled {
+	background-color: #999;
+	opacity: 0.6;
+}
+.loading-container {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	background-color: rgba(255, 255, 255, 0.8);
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+}
+.loading-spinner {
+	border: 4px solid #f3f3f3;
+	border-top: 4px solid #003399;
+	border-radius: 50%;
+	width: 40rpx;
+	height: 40rpx;
+	animation: spin 1s linear infinite;
+}
+@keyframes spin {
+0% { transform: rotate(0deg);
+}
+100% { transform: rotate(360deg);
+}
+}
+.loading-text {
+	margin-top: 20rpx;
+	font-size: 28rpx;
+	color: #333;
+}
+
+/* 添加 loading 相关样式 */
+.loading-container {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	margin: 40rpx 0;
+}
+.loading-spinner {
+	width: 60rpx;
+	height: 60rpx;
+	border: 4rpx solid #f3f3f3;
+	border-top: 4rpx solid #003399;
+	border-radius: 50%;
+	animation: spin 1s linear infinite;
+	margin-bottom: 20rpx;
+}
+.loading-text {
+	font-size: 28rpx;
+	color: #666;
+}
+@keyframes spin {
+0% { transform: rotate(0deg);
+}
+100% { transform: rotate(360deg);
+}
+}
+
+/* 修改按钮禁用状态样式 */
+.btn-retake:disabled, .btn-next:disabled {
+	opacity: 0.5;
+	cursor: not-allowed;
+}

+ 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": {

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

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

Некоторые файлы не были показаны из-за большого количества измененных файлов