scoreDetail.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. <!-- 答题详情 -->
  2. <template>
  3. <view class="safetyEducationExamination-scoreDetail">
  4. <scroll-view scroll-y class="scroll-content" @scrolltolower="onScrollToLower">
  5. <!-- 考试概览 -->
  6. <view class="card overview-card">
  7. <view class="row-between mb8">
  8. <text class="tag tag-blue text-xs">{{ detail.examTypeName }}</text>
  9. <text :class="detail.isPass ? 'score-result-pass' : 'score-result-fail'">
  10. {{ detail.isPass ? '通过' : '未通过' }}
  11. </text>
  12. </view>
  13. <view class="exam-name">{{ detail.examName }}</view>
  14. <view class="overview-grid">
  15. <view><text class="text-gray">考试时间:</text>{{ detail.examTime }}</view>
  16. <view><text class="text-gray">用时:</text>{{ detail.usedTime }}</view>
  17. <view><text class="text-gray">满分/及格:</text>{{ detail.totalScore }} / {{ detail.passScore }}</view>
  18. <view><text class="text-gray">考次:</text>{{ detail.attemptCount }} / {{ detail.attemptLimit === 0 ? '不限' : detail.attemptLimit }}</view>
  19. <view>
  20. <text class="text-gray">最高成绩:</text>
  21. <text class="text-blue fw500">{{ detail.bestScore }} 分</text>
  22. </view>
  23. <view>
  24. <text class="text-gray">考试结果:</text>
  25. <text class="fw500" :class="detail.isPass ? 'text-green' : 'text-red'">
  26. {{ detail.isPass ? '通过' : '未通过' }}
  27. </text>
  28. </view>
  29. </view>
  30. <!-- 答题统计 -->
  31. <view class="stat-box">
  32. <view class="text-sm fw500 mb8">答题统计</view>
  33. <view class="stat-grid">
  34. <view class="stat-item">
  35. <view class="stat-num text-blue">{{ detail.totalQuestionNum || 0 }}</view>
  36. <view class="text-xs text-gray">总题数</view>
  37. </view>
  38. <view class="stat-item">
  39. <view class="stat-num text-green">{{ detail.correctCount || 0 }}</view>
  40. <view class="text-xs text-gray">正确</view>
  41. </view>
  42. <view class="stat-item">
  43. <view class="stat-num text-red">{{ detail.wrongCount || 0 }}</view>
  44. <view class="text-xs text-gray">错误</view>
  45. </view>
  46. <view class="stat-item">
  47. <view class="stat-num text-blue">{{ detail.accuracy }}</view>
  48. <view class="text-xs text-gray">正确率</view>
  49. </view>
  50. </view>
  51. </view>
  52. </view>
  53. <!-- 答题详情 -->
  54. <view class="detail-panel">
  55. <view class="detail-title">
  56. 答题详情
  57. <text class="text-gray detail-title-sub">(共{{ detail.totalQuestionNum || 0 }}题)</text>
  58. </view>
  59. <view v-for="(item, index) in answerList" :key="index" class="question-card">
  60. <view class="row-between mb8">
  61. <view class="q-head">
  62. <text class="q-type-badge">{{ questionTypeName(item.questionType) }}</text>
  63. <text class="text-xs text-gray q-no">第 {{ questionNo(index) }} 题</text>
  64. <text class="diff-badge" :class="difficultyClass(item.difficulty)">{{ difficultyName(item.difficulty) }}</text>
  65. </view>
  66. <text class="tag text-xs" :class="isCorrect(item) ? 'tag-green' : 'tag-orange'">
  67. {{ isCorrect(item) ? '答对' : '答错' }}
  68. </text>
  69. </view>
  70. <view class="q-content">{{ item.questionContent }}</view>
  71. <view class="answer-box" :class="isCorrect(item) ? 'answer-box-correct' : 'answer-box-wrong'">
  72. <view class="mb4">
  73. <text class="text-xs text-gray">我的答案:</text>
  74. <text class="text-xs fw500" :class="isCorrect(item) ? 'text-green' : 'text-red'">
  75. {{ item.userAnswer }}{{ answerSuffix(item) }}
  76. </text>
  77. </view>
  78. <view>
  79. <text class="text-xs text-gray">正确答案:</text>
  80. <text class="text-xs text-green fw500">{{ item.correctAnswer }}</text>
  81. </view>
  82. </view>
  83. <view v-if="item.analysis" class="analysis-box">解析:{{ item.analysis }}</view>
  84. </view>
  85. <view v-if="!answerList[0] && !listLoading" class="empty-tip">暂无答题记录</view>
  86. <view v-if="listLoading" class="loading-tip">加载中...</view>
  87. <view v-if="noMore && answerList[0]" class="loading-tip">没有更多了</view>
  88. </view>
  89. </scroll-view>
  90. </view>
  91. </template>
  92. <script>
  93. // 题型映射:1-单选题,2-多选题,3-判断题
  94. const QUESTION_TYPE_MAP = {
  95. 1: '单选题',
  96. 2: '多选题',
  97. 3: '判断题',
  98. };
  99. // 难度映射:1-简单,2-一般,3-较难,4-极难
  100. const DIFFICULTY_MAP = {
  101. 1: { name: '简单', cls: 'diff-easy' },
  102. 2: { name: '一般', cls: 'diff-normal' },
  103. 3: { name: '较难', cls: 'diff-hard' },
  104. 4: { name: '极难', cls: 'diff-extreme' },
  105. };
  106. export default {
  107. data() {
  108. return {
  109. attemptId: '',
  110. examId: '',
  111. // 上部分:考试概况
  112. detail: {
  113. examTypeName: '',
  114. examName: '',
  115. isPass: false,
  116. examTime: '',
  117. usedTime: '',
  118. totalScore: 0,
  119. passScore: 0,
  120. attemptCount: 0,
  121. attemptLimit: 0,
  122. bestScore: 0,
  123. totalQuestionNum: 0,
  124. correctCount: 0,
  125. wrongCount: 0,
  126. accuracy: '-',
  127. },
  128. // 下部分:答题详情列表
  129. answerList: [],
  130. listLoading: false,
  131. page: 1,
  132. pageSize: 10,
  133. total: 0,
  134. noMore: false,
  135. }
  136. },
  137. onLoad(options) {
  138. this.attemptId = options.attemptId || '';
  139. this.examId = options.examId || '';
  140. if (!this.attemptId) {
  141. uni.showToast({ title: '考试信息无效', icon: 'none' });
  142. setTimeout(() => uni.navigateBack(), 1500);
  143. return;
  144. }
  145. this.loadExamDetail();
  146. this.loadAnswerList();
  147. },
  148. methods: {
  149. questionTypeName(type) {
  150. return QUESTION_TYPE_MAP[type] || '';
  151. },
  152. difficultyName(difficulty) {
  153. const d = DIFFICULTY_MAP[difficulty];
  154. return d ? d.name : '';
  155. },
  156. difficultyClass(difficulty) {
  157. const d = DIFFICULTY_MAP[difficulty];
  158. return d ? d.cls : '';
  159. },
  160. isCorrect(item) {
  161. return item.isCorrect === 1 || item.isCorrect === true;
  162. },
  163. answerSuffix(item) {
  164. if (item.answerStatusText) return `(${item.answerStatusText})`;
  165. if (item.isPartial) return '(部分正确)';
  166. return this.isCorrect(item) ? '(正确)' : '(错误)';
  167. },
  168. questionNo(index) {
  169. const item = this.answerList[index];
  170. if (item && item.questionNo) return item.questionNo;
  171. if (item && item.sortNo) return item.sortNo;
  172. return index + 1;
  173. },
  174. // 上部分:考试概况(独立详情接口)
  175. async loadExamDetail() {
  176. try {
  177. // TODO 后台接口未提供,先占位;接口就绪后放开以下代码
  178. // const { data } = await examElExamAttemptAnswerDetail({ attemptId: this.attemptId });
  179. // if (data.code === 200 && data.data) {
  180. // const d = data.data;
  181. // this.detail = {
  182. // examTypeName: d.examTypeName || '',
  183. // examName: d.examName || '',
  184. // isPass: d.examStatus === 1,
  185. // examTime: parseTime(d.startTime, '{y}-{m}-{d}'),
  186. // usedTime: d.myDuration || '-',
  187. // totalScore: d.totalScore || 0,
  188. // passScore: d.passScore || 0,
  189. // attemptCount: d.attemptCount || 0,
  190. // attemptLimit: d.attemptLimit || 0,
  191. // bestScore: d.bestScore || 0,
  192. // totalQuestionNum: d.totalQuestionNum || 0,
  193. // correctCount: d.correctCount || 0,
  194. // wrongCount: d.wrongCount || 0,
  195. // accuracy: d.accuracy || '-',
  196. // };
  197. // }
  198. } catch (e) {
  199. console.error('获取考试详情失败', e);
  200. }
  201. },
  202. resetList() {
  203. this.page = 1;
  204. this.noMore = false;
  205. this.$set(this, 'answerList', []);
  206. this.loadAnswerList();
  207. },
  208. // 下部分:答题详情列表(分页滚动加载)
  209. async loadAnswerList() {
  210. if (this.listLoading || this.noMore) return;
  211. this.listLoading = true;
  212. try {
  213. // TODO 后台接口未提供,先占位;接口就绪后放开以下代码
  214. // const { data } = await examElExamAttemptAnswerAttemptAnswer({
  215. // page: this.page,
  216. // pageSize: this.pageSize,
  217. // attemptId: this.attemptId,
  218. // });
  219. // if (data.code === 200) {
  220. // const records = data.data.records || [];
  221. // const newList = this.page === 1 ? records : [...this.answerList, ...records];
  222. // this.$set(this, 'answerList', newList);
  223. // this.total = data.data.total;
  224. // if (newList.length >= this.total) {
  225. // this.noMore = true;
  226. // } else {
  227. // this.page++;
  228. // }
  229. // }
  230. } catch (e) {
  231. console.error('获取答题详情失败', e);
  232. } finally {
  233. this.listLoading = false;
  234. }
  235. },
  236. onScrollToLower() {
  237. this.loadAnswerList();
  238. },
  239. },
  240. }
  241. </script>
  242. <style lang="stylus" scoped>
  243. .safetyEducationExamination-scoreDetail
  244. height 100%
  245. display flex
  246. flex-direction column
  247. overflow hidden
  248. background-color #f0f4fb
  249. .scroll-content
  250. flex 1
  251. overflow hidden
  252. .card
  253. background #fff
  254. border 1rpx solid #dde3ed
  255. border-radius 24rpx
  256. padding 32rpx
  257. margin 24rpx 24rpx 24rpx
  258. box-shadow 0 4rpx 24rpx rgba(24,87,212,0.08)
  259. .exam-name
  260. font-size 32rpx
  261. font-weight 700
  262. color #1a2233
  263. margin-bottom 20rpx
  264. .overview-grid
  265. display grid
  266. grid-template-columns 1fr 1fr
  267. gap 12rpx
  268. font-size 26rpx
  269. color #1a2233
  270. margin-bottom 28rpx
  271. // 答题统计
  272. .stat-box
  273. background #f4f6fa
  274. border-radius 16rpx
  275. padding 24rpx
  276. margin-bottom 28rpx
  277. .stat-grid
  278. display grid
  279. grid-template-columns 1fr 1fr 1fr 1fr
  280. gap 16rpx
  281. text-align center
  282. .stat-num
  283. font-size 40rpx
  284. font-weight 700
  285. // 答题详情区
  286. .detail-panel
  287. padding 0 24rpx 24rpx
  288. .detail-title
  289. font-size 24rpx
  290. font-weight 500
  291. padding 28rpx 0 20rpx
  292. .detail-title-sub
  293. font-weight 400
  294. // 题目卡片
  295. .question-card
  296. background #fff
  297. border 1rpx solid #dde3ed
  298. border-radius 20rpx
  299. padding 28rpx
  300. margin-bottom 20rpx
  301. .row-between
  302. display flex
  303. align-items center
  304. justify-content space-between
  305. .q-head
  306. display flex
  307. align-items center
  308. .q-type-badge
  309. background #1857d4
  310. color #fff
  311. font-size 24rpx
  312. padding 4rpx 16rpx
  313. border-radius 8rpx
  314. .q-no
  315. margin-left 12rpx
  316. .diff-badge
  317. margin-left 12rpx
  318. font-size 20rpx
  319. padding 2rpx 12rpx
  320. border-radius 8rpx
  321. border 1rpx solid transparent
  322. .diff-easy
  323. background #f6ffed
  324. color #52c41a
  325. border-color #b7eb8f
  326. .diff-normal
  327. background #e6f4ff
  328. color #1677ff
  329. border-color #91caff
  330. .diff-hard
  331. background #fff7e6
  332. color #fa8c16
  333. border-color #ffd591
  334. .diff-extreme
  335. background #f9f0ff
  336. color #722ed1
  337. border-color #d3adf7
  338. .q-content
  339. font-size 24rpx
  340. line-height 1.7
  341. margin-bottom 20rpx
  342. // 答案区
  343. .answer-box
  344. border-radius 16rpx
  345. padding 20rpx
  346. margin-bottom 20rpx
  347. .answer-box-correct
  348. background #f6ffed
  349. border 1rpx solid #b7eb8f
  350. .answer-box-wrong
  351. background #f4f6fa
  352. // 解析区
  353. .analysis-box
  354. background #f6ffed
  355. border-radius 16rpx
  356. padding 20rpx
  357. font-size 26rpx
  358. color #4a5568
  359. line-height 1.7
  360. // 标签
  361. .tag
  362. display inline-flex
  363. align-items center
  364. padding 4rpx 16rpx
  365. border-radius 40rpx
  366. font-weight 500
  367. .tag-blue
  368. background #eef3fd
  369. color #1857d4
  370. .tag-green
  371. background #edfaf6
  372. color #0d8f6f
  373. .tag-orange
  374. background #fef6ec
  375. color #e67e22
  376. .score-result-pass
  377. color #10a37f
  378. font-weight 600
  379. .score-result-fail
  380. color #e53e3e
  381. font-weight 600
  382. .text-xs
  383. font-size 22rpx
  384. .text-sm
  385. font-size 24rpx
  386. .text-gray
  387. color #8896a7
  388. .text-blue
  389. color #1857d4
  390. .text-green
  391. color #10a37f
  392. .text-red
  393. color #e53e3e
  394. .fw500
  395. font-weight 500
  396. .mb4
  397. margin-bottom 8rpx
  398. .mb8
  399. margin-bottom 16rpx
  400. // 提示
  401. .empty-tip
  402. text-align center
  403. line-height 80rpx
  404. color #aaa
  405. font-size 24rpx
  406. .loading-tip
  407. text-align center
  408. line-height 60rpx
  409. color #aaa
  410. font-size 22rpx
  411. </style>