interview_success.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  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. uni.reLaunch({
  131. url: '/pages/success/success'
  132. })
  133. } else {
  134. // 使用 redirectTo 替代 navigateTo
  135. uni.reLaunch({
  136. url: '/pages/interview_retake/interview_retake?failedItems=' +
  137. JSON.stringify(this.failedItems)
  138. })
  139. }
  140. }
  141. }
  142. }
  143. </script>
  144. <style>
  145. .success-container {
  146. display: flex;
  147. flex-direction: column;
  148. align-items: center;
  149. padding: 40rpx 30rpx;
  150. }
  151. .success-icon {
  152. margin-top: 162rpx;
  153. width: 160rpx;
  154. height: 160rpx;
  155. }
  156. .success-icon image {
  157. width: 100%;
  158. height: 100%;
  159. }
  160. .success-title {
  161. font-size: 36rpx;
  162. font-weight: bold;
  163. margin: 30rpx 0 50rpx;
  164. }
  165. .check-list {
  166. width: 100%;
  167. margin-bottom: 60rpx;
  168. display: flex;
  169. flex-direction: column;
  170. align-content: center;
  171. flex-wrap: wrap;
  172. }
  173. .check-item {
  174. display: flex;
  175. align-items: center;
  176. margin-bottom: 20rpx;
  177. }
  178. .check-index {
  179. width: 40rpx;
  180. height: 40rpx;
  181. border-radius: 50%;
  182. background-color: #f0f0f0;
  183. color: #999;
  184. display: flex;
  185. justify-content: center;
  186. align-items: center;
  187. font-size: 24rpx;
  188. margin-right: 20rpx;
  189. }
  190. .check-name {
  191. margin-right: 20px;
  192. font-size: 28rpx;
  193. color: #333;
  194. }
  195. .check-status {
  196. font-size: 28rpx;
  197. color: #003399;
  198. }
  199. .status-fail {
  200. color: #f56c6c;
  201. }
  202. .button-group {
  203. display: flex;
  204. width: 100%;
  205. justify-content: space-between;
  206. margin-top: 240rpx;
  207. }
  208. .btn-retake, .btn-next {
  209. width: 45%;
  210. height: 80rpx;
  211. line-height: 80rpx;
  212. text-align: center;
  213. border-radius: 8rpx;
  214. }
  215. .btn-retake {
  216. background-color: #f5f5f5;
  217. color: #333;
  218. border: 1px solid #ddd;
  219. }
  220. .btn-next {
  221. background-color: #003399;
  222. color: #fff;
  223. }
  224. .btn-next:disabled {
  225. background-color: #999;
  226. opacity: 0.6;
  227. }
  228. .loading-container {
  229. position: absolute;
  230. top: 0;
  231. left: 0;
  232. width: 100%;
  233. height: 100%;
  234. background-color: rgba(255, 255, 255, 0.8);
  235. display: flex;
  236. flex-direction: column;
  237. align-items: center;
  238. justify-content: center;
  239. }
  240. .loading-spinner {
  241. border: 4px solid #f3f3f3;
  242. border-top: 4px solid #003399;
  243. border-radius: 50%;
  244. width: 40rpx;
  245. height: 40rpx;
  246. animation: spin 1s linear infinite;
  247. }
  248. @keyframes spin {
  249. 0% { transform: rotate(0deg); }
  250. 100% { transform: rotate(360deg); }
  251. }
  252. .loading-text {
  253. margin-top: 20rpx;
  254. font-size: 28rpx;
  255. color: #333;
  256. }
  257. /* 添加 loading 相关样式 */
  258. .loading-container {
  259. display: flex;
  260. flex-direction: column;
  261. align-items: center;
  262. margin: 40rpx 0;
  263. }
  264. .loading-spinner {
  265. width: 60rpx;
  266. height: 60rpx;
  267. border: 4rpx solid #f3f3f3;
  268. border-top: 4rpx solid #003399;
  269. border-radius: 50%;
  270. animation: spin 1s linear infinite;
  271. margin-bottom: 20rpx;
  272. }
  273. .loading-text {
  274. font-size: 28rpx;
  275. color: #666;
  276. }
  277. @keyframes spin {
  278. 0% { transform: rotate(0deg); }
  279. 100% { transform: rotate(360deg); }
  280. }
  281. /* 修改按钮禁用状态样式 */
  282. .btn-retake:disabled, .btn-next:disabled {
  283. opacity: 0.5;
  284. cursor: not-allowed;
  285. }
  286. </style>