interview_success.vue 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. <template>
  2. <view class="success-container">
  3. <view class="success-icon">
  4. <image src="https://data.qicai321.com/minlong/dbf3202a-de17-4d1d-8b8c-d0d708c55552.png" mode="aspectFit"></image>
  5. </view>
  6. <view class="success-title">
  7. <block v-if="isAnalyzing">照片解析中...</block>
  8. <block v-else>恭喜完成全部检测</block>
  9. </view>
  10. <view v-if="isAnalyzing" class="loading-container">
  11. <view class="loading-spinner"></view>
  12. <view class="loading-text">正在分析照片,请稍候...</view>
  13. </view>
  14. <view v-else class="check-list">
  15. <view class="check-item" v-for="(item, index) in checkItems" :key="index">
  16. <view class="check-index">{{index + 1}}</view>
  17. <view class="check-name">{{item.name}}</view>
  18. <view class="check-status" :class="{'status-fail': !item.success}">
  19. {{item.success ? '动作检测成功' : '动作检测失败'}}
  20. </view>
  21. </view>
  22. </view>
  23. <view class="button-group" v-if="!isAnalyzing">
  24. <button class="btn-retake" @click="retake" :disabled="isAnalyzing">重新拍摄</button>
  25. <button class="btn-next" @click="goNext" :disabled="isAnalyzing">
  26. {{hasFailedItems ? '补拍' : '下一步'}}
  27. </button>
  28. </view>
  29. </view>
  30. </template>
  31. <script>
  32. import { apiBaseUrl } from '@/common/config.js'; // 导入基础URL配置
  33. export default {
  34. data() {
  35. return {
  36. checkItems: [
  37. { name: '左手手心', success: true, type: 'left_palm', description: '展示完整的左手手掌' },
  38. { name: '左手手背', success: true, type: 'left_back', description: '展示完整的左手手背' },
  39. { name: '左手握拳', success: true, type: 'left_fist', description: '展示完整的左手握拳' },
  40. { name: '右手手心', success: true, type: 'right_palm', description: '展示完整的右手手掌' },
  41. { name: '右手手背', success: true, type: 'right_back', description: '展示完整的右手手背' },
  42. { name: '右手握拳', success: true, type: 'right_fist', description: '展示完整的右手握拳' }
  43. ],
  44. maxRetries: 3,
  45. currentRetry: 0,
  46. retryInterval: 3000, // 3秒
  47. applicationId: null,
  48. isAnalyzing: false, // 添加分析状态标志
  49. }
  50. },
  51. computed: {
  52. hasFailedItems() {
  53. return this.checkItems.some(item => !item.success)
  54. },
  55. failedItems() {
  56. return this.checkItems.filter(item => !item.success)
  57. }
  58. },
  59. onLoad(options) {
  60. this.applicationId = uni.getStorageSync('appId')
  61. this.fetchAnalysisResults()
  62. },
  63. methods: {
  64. async fetchAnalysisResults() {
  65. try {
  66. this.isAnalyzing = true // 开始分析时设置状态
  67. const response = await uni.request({
  68. url: `${apiBaseUrl}/api/system/job/get_visual_analysis_results?application_id=${this.applicationId}`,
  69. })
  70. console.log(response)
  71. // 检查响应状态码
  72. if (response.data.code === 2001) {
  73. // 状态码2001表示正在处理中,继续轮询
  74. setTimeout(() => {
  75. this.fetchAnalysisResults()
  76. }, this.retryInterval)
  77. return
  78. }
  79. // 处理成功响应
  80. if (response.data.code === 2000 && response.data.data && response.data.data.photo_results && response.data.data.photo_results.length > 0) {
  81. this.isAnalyzing = false // 分析完成
  82. this.processResults(response.data.data.photo_results)
  83. } else if (this.currentRetry < this.maxRetries) {
  84. // 如果没有结果且未达到最大重试次数,则重试
  85. this.currentRetry++
  86. setTimeout(() => {
  87. this.fetchAnalysisResults()
  88. }, this.retryInterval)
  89. } else {
  90. this.isAnalyzing = false // 重试结束后也要关闭分析状态
  91. }
  92. } catch (error) {
  93. console.error('获取分析结果失败:', error)
  94. this.isAnalyzing = false // 发生错误时关闭分析状态
  95. }
  96. },
  97. processResults(photoResults) {
  98. const results = {}
  99. photoResults.forEach(photo => {
  100. // 根据description匹配对应的检测项
  101. const checkItem = this.checkItems.find(item =>
  102. photo.description === item.description
  103. )
  104. console.log(checkItem, photo)
  105. if (checkItem) {
  106. results[checkItem.type] = photo.part_verification.is_correct_part
  107. }
  108. })
  109. this.updateCheckResults(results)
  110. },
  111. updateCheckResults(results) {
  112. this.checkItems = this.checkItems.map(item => ({
  113. ...item,
  114. success: results[item.type] || false
  115. }))
  116. },
  117. retake() {
  118. if (this.hasFailedItems) {
  119. // 携带失败项信息跳转到拍照页面
  120. uni.navigateTo({
  121. url: '/pages/interview/interview?retake=true&failedItems=' +
  122. JSON.stringify(this.failedItems.map(item => item.type))
  123. })
  124. } else {
  125. uni.navigateBack()
  126. }
  127. },
  128. goNext() {
  129. if (!this.hasFailedItems) {
  130. // 根据配置决定跳转到下一个页面
  131. this.navigateToNextPage();
  132. } else {
  133. // 使用 redirectTo 替代 navigateTo
  134. uni.reLaunch({
  135. url: '/pages/interview_retake/interview_retake?failedItems=' +
  136. JSON.stringify(this.failedItems)
  137. })
  138. }
  139. },
  140. // 根据配置决定跳转到下一个页面
  141. navigateToNextPage() {
  142. // 获取本地存储的配置数据
  143. let configData = {};
  144. try {
  145. const configStr = uni.getStorageSync('configData');
  146. if (configStr && configStr.trim()) {
  147. configData = JSON.parse(configStr);
  148. }
  149. } catch (error) {
  150. console.error('解析configData失败:', error);
  151. configData = {};
  152. }
  153. console.log('interview_success页面获取到的配置数据:', configData);
  154. // 获取问题形式开关配置
  155. const questionFormSwitches = configData?.question_form_switches || {};
  156. // 检查是否启用了选择题相关功能
  157. const hasChoiceQuestions = questionFormSwitches.enable_fill_blank ||
  158. questionFormSwitches.enable_image_choice ||
  159. questionFormSwitches.enable_multiple_choice ||
  160. questionFormSwitches.enable_single_choice;
  161. // 肢体检测配置
  162. const hasPostureDetection = configData?.enable_posture_check;
  163. // 候选人提问配置
  164. const hasCandidateQuestions = questionFormSwitches.enable_candidate_questions;
  165. let targetUrl = '/pages/success/success'; // 默认跳转到成功页面
  166. let pageName = 'success页面';
  167. console.log('interview_success页面配置检查:', {
  168. hasChoiceQuestions,
  169. hasPostureDetection,
  170. hasCandidateQuestions,
  171. questionFormSwitches
  172. });
  173. if (hasCandidateQuestions) {
  174. // 如果启用了候选人提问,跳转到interview-question页面
  175. targetUrl = '/pages/interview-question/interview-question';
  176. pageName = 'interview-question页面';
  177. }else{
  178. targetUrl = '/pages/success/success';
  179. pageName = 'success页面';
  180. }
  181. console.log('interview_success页面根据配置跳转到:', targetUrl);
  182. // 执行页面跳转
  183. uni.reLaunch({
  184. url: targetUrl,
  185. success: () => {
  186. console.log(`成功跳转到${pageName}`);
  187. },
  188. fail: (err) => {
  189. console.error(`跳转到${pageName}失败:`, err);
  190. // 如果跳转失败,尝试使用navigateTo
  191. uni.navigateTo({
  192. url: targetUrl,
  193. fail: (navigateErr) => {
  194. console.error(`导航到${pageName}也失败:`, navigateErr);
  195. // 最后尝试使用redirectTo
  196. uni.redirectTo({
  197. url: targetUrl,
  198. fail: (redirectErr) => {
  199. console.error('所有跳转方式都失败:', redirectErr);
  200. // 如果所有跳转方式都失败,跳转到默认成功页面
  201. uni.reLaunch({
  202. url: '/pages/success/success'
  203. });
  204. }
  205. });
  206. }
  207. });
  208. }
  209. });
  210. }
  211. }
  212. }
  213. </script>
  214. <style>
  215. .success-container {
  216. display: flex;
  217. flex-direction: column;
  218. align-items: center;
  219. padding: 40rpx 30rpx;
  220. }
  221. .success-icon {
  222. margin-top: 162rpx;
  223. width: 160rpx;
  224. height: 160rpx;
  225. }
  226. .success-icon image {
  227. width: 100%;
  228. height: 100%;
  229. }
  230. .success-title {
  231. font-size: 36rpx;
  232. font-weight: bold;
  233. margin: 30rpx 0 50rpx;
  234. }
  235. .check-list {
  236. width: 100%;
  237. margin-bottom: 60rpx;
  238. display: flex;
  239. flex-direction: column;
  240. align-content: center;
  241. flex-wrap: wrap;
  242. }
  243. .check-item {
  244. display: flex;
  245. align-items: center;
  246. margin-bottom: 20rpx;
  247. }
  248. .check-index {
  249. width: 40rpx;
  250. height: 40rpx;
  251. border-radius: 50%;
  252. background-color: #f0f0f0;
  253. color: #999;
  254. display: flex;
  255. justify-content: center;
  256. align-items: center;
  257. font-size: 24rpx;
  258. margin-right: 20rpx;
  259. }
  260. .check-name {
  261. margin-right: 20px;
  262. font-size: 28rpx;
  263. color: #333;
  264. }
  265. .check-status {
  266. font-size: 28rpx;
  267. color: #003399;
  268. }
  269. .status-fail {
  270. color: #f56c6c;
  271. }
  272. .button-group {
  273. display: flex;
  274. width: 100%;
  275. justify-content: space-between;
  276. margin-top: 240rpx;
  277. }
  278. .btn-retake, .btn-next {
  279. width: 45%;
  280. height: 80rpx;
  281. line-height: 80rpx;
  282. text-align: center;
  283. border-radius: 8rpx;
  284. }
  285. .btn-retake {
  286. background-color: #f5f5f5;
  287. color: #333;
  288. border: 1px solid #ddd;
  289. }
  290. .btn-next {
  291. background-color: #003399;
  292. color: #fff;
  293. }
  294. .btn-next:disabled {
  295. background-color: #999;
  296. opacity: 0.6;
  297. }
  298. .loading-container {
  299. position: absolute;
  300. top: 0;
  301. left: 0;
  302. width: 100%;
  303. height: 100%;
  304. background-color: rgba(255, 255, 255, 0.8);
  305. display: flex;
  306. flex-direction: column;
  307. align-items: center;
  308. justify-content: center;
  309. }
  310. .loading-spinner {
  311. border: 4px solid #f3f3f3;
  312. border-top: 4px solid #003399;
  313. border-radius: 50%;
  314. width: 40rpx;
  315. height: 40rpx;
  316. animation: spin 1s linear infinite;
  317. }
  318. @keyframes spin {
  319. 0% { transform: rotate(0deg); }
  320. 100% { transform: rotate(360deg); }
  321. }
  322. .loading-text {
  323. margin-top: 20rpx;
  324. font-size: 28rpx;
  325. color: #333;
  326. }
  327. /* 添加 loading 相关样式 */
  328. .loading-container {
  329. display: flex;
  330. flex-direction: column;
  331. align-items: center;
  332. margin: 40rpx 0;
  333. }
  334. .loading-spinner {
  335. width: 60rpx;
  336. height: 60rpx;
  337. border: 4rpx solid #f3f3f3;
  338. border-top: 4rpx solid #003399;
  339. border-radius: 50%;
  340. animation: spin 1s linear infinite;
  341. margin-bottom: 20rpx;
  342. }
  343. .loading-text {
  344. font-size: 28rpx;
  345. color: #666;
  346. }
  347. @keyframes spin {
  348. 0% { transform: rotate(0deg); }
  349. 100% { transform: rotate(360deg); }
  350. }
  351. /* 修改按钮禁用状态样式 */
  352. .btn-retake:disabled, .btn-next:disabled {
  353. opacity: 0.5;
  354. cursor: not-allowed;
  355. }
  356. </style>