index.vue 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. <!-- 刷题练习列表 -->
  2. <template>
  3. <view class="safetyEducationExamination-practice">
  4. <!-- 顶部类型标签 -->
  5. <view class="tag-bar" v-if="!hideTagBar">
  6. <scroll-view scroll-x class="tag-scroll">
  7. <view class="tag-scroll-inner">
  8. <text
  9. v-for="(tag, index) in typeTags"
  10. :key="index"
  11. class="tag-item"
  12. :class="{ 'tag-item-active': activeTagIndex === index }"
  13. @click="onTagChange(index)"
  14. >{{ tag.label }}</text>
  15. </view>
  16. </scroll-view>
  17. </view>
  18. <!-- 列表 -->
  19. <scroll-view scroll-y class="scroll-content">
  20. <view class="panel">
  21. <view
  22. v-for="(item, index) in practiceList"
  23. :key="index"
  24. class="practice-card"
  25. @click="goToPractice(item)"
  26. >
  27. <view class="card-top">
  28. <text class="tag" :class="item.tagClass">{{ item.knowledgePointName }}</text>
  29. <text class="total-count" :style="'color:' + item.progressColor">{{ item.totalCount }} 题</text>
  30. </view>
  31. <text class="card-title">{{ item.knowledgePointName }}</text>
  32. <text class="card-sub" :class="item.practiceStatus > 0 ? '' : 'text-gray'">
  33. {{ item.practiceStatus > 0 ? '已答 ' + item.answeredCount + ' 题 · 正确率 ' + item.correctRate + '%' : '未开始' }}
  34. </text>
  35. <view class="progress-row">
  36. <view class="progress-wrap">
  37. <view class="progress-bar" :style="'width:' + (item.progress || 0) + '%;background:' + item.progressColor"></view>
  38. </view>
  39. <text class="progress-text" :style="'color:' + item.progressColor">{{ item.progress || 0 }}%</text>
  40. </view>
  41. </view>
  42. <view v-if="!practiceList[0] && !loading" class="empty-tip">暂无题库数据</view>
  43. <view v-if="loading" class="loading-tip">加载中...</view>
  44. </view>
  45. </scroll-view>
  46. </view>
  47. </template>
  48. <script>
  49. import {
  50. examUserPracticeList,
  51. examElKnowledgePointTreeList,
  52. } from '@/pages_safetyEducationExamination/api/index.js'
  53. // 根据索引返回标签样式类和进度条颜色
  54. const TAG_COLORS = [
  55. { tagClass: 'tag-blue', color: '#1677ff' },
  56. { tagClass: 'tag-green', color: '#52c41a' },
  57. { tagClass: 'tag-orange', color: '#fa8c16' },
  58. { tagClass: 'tag-purple', color: '#722ed1' },
  59. { tagClass: 'tag-cyan', color: '#13c2c2' },
  60. ];
  61. export default {
  62. data() {
  63. return {
  64. typeTags: [{ label: '全部', value: null }],
  65. activeTagIndex: 0,
  66. practiceList: [],
  67. loading: false,
  68. // 跳转参数
  69. hideTagBar: false,
  70. disciplineTypeId: null,
  71. knowledgePointId: null,
  72. }
  73. },
  74. onLoad(options) {
  75. // 接收跳转参数
  76. if (options.disciplineTypeId || options.knowledgePointId) {
  77. this.disciplineTypeId = options.disciplineTypeId || null;
  78. this.knowledgePointId = options.knowledgePointId || null;
  79. this.hideTagBar = true;
  80. }
  81. },
  82. created() {
  83. this.loadTypeTags();
  84. },
  85. onShow() {
  86. this.loadPracticeList();
  87. },
  88. methods: {
  89. // 加载知识点类型标签(dataScope=2)
  90. async loadTypeTags() {
  91. try {
  92. const { data } = await examElKnowledgePointTreeList({ dataScope: 2 });
  93. if (data.code === 200 && data.data) {
  94. const tags = (data.data || []).map(item => ({
  95. label: item.knowledgePointName,
  96. value: item.id,
  97. }));
  98. this.$set(this, 'typeTags', [{ label: '全部', value: null }, ...tags]);
  99. }
  100. } catch (e) {
  101. console.error('获取知识点类型失败', e);
  102. }
  103. },
  104. // 加载题库分组列表
  105. async loadPracticeList() {
  106. if (this.loading) return;
  107. this.loading = true;
  108. try {
  109. const tag = this.typeTags[this.activeTagIndex];
  110. const obj = {
  111. page: 1,
  112. pageSize: 50,
  113. knowledgePointId: this.knowledgePointId || tag.value || undefined,
  114. disciplineTypeId: this.disciplineTypeId || undefined,
  115. };
  116. const { data } = await examUserPracticeList(obj);
  117. if (data.code === 200) {
  118. const records = (data.data.records || []).map((item, index) => {
  119. const colorInfo = TAG_COLORS[index % TAG_COLORS.length];
  120. return {
  121. ...item,
  122. tagClass: colorInfo.tagClass,
  123. progressColor: item.practiceStatus > 0 ? colorInfo.color : '#bbb',
  124. };
  125. });
  126. this.$set(this, 'practiceList', records);
  127. }
  128. } catch (e) {
  129. console.error('获取题库列表失败', e);
  130. } finally {
  131. this.loading = false;
  132. }
  133. },
  134. onTagChange(index) {
  135. this.activeTagIndex = index;
  136. this.loadPracticeList();
  137. },
  138. goToPractice(item) {
  139. uni.navigateTo({
  140. url: `/pages_safetyEducationExamination/views/practice/practicePlay?knowledgePointId=${item.knowledgePointId || ''}&attemptId=${item.attemptId || ''}&examId=${item.examId || ''}`,
  141. });
  142. },
  143. },
  144. }
  145. </script>
  146. <style lang="stylus" scoped>
  147. .safetyEducationExamination-practice
  148. height 100%
  149. display flex
  150. flex-direction column
  151. overflow hidden
  152. background-color #f0f4fb
  153. // 顶部标签栏
  154. .tag-bar
  155. background #fff
  156. border-bottom 1rpx solid #e8edf3
  157. flex-shrink 0
  158. padding 0 24rpx
  159. .tag-scroll
  160. white-space nowrap
  161. .tag-scroll-inner
  162. display inline-flex
  163. gap 16rpx
  164. padding 16rpx 0
  165. .tag-item
  166. display inline-block
  167. font-size 24rpx
  168. padding 10rpx 24rpx
  169. border-radius 30rpx
  170. background #eef1f6
  171. color #666
  172. border 1rpx solid transparent
  173. white-space nowrap
  174. .tag-item-active
  175. background #e6f0ff
  176. color #1677ff
  177. border-color #91caff
  178. font-weight 600
  179. // 内容滚动区
  180. .scroll-content
  181. flex 1
  182. overflow hidden
  183. .panel
  184. padding 24rpx
  185. // 题库卡片
  186. .practice-card
  187. background #fff
  188. border-radius 20rpx
  189. padding 24rpx 28rpx
  190. margin-bottom 20rpx
  191. box-shadow 0 2rpx 12rpx rgba(0,0,0,0.04)
  192. border 1rpx solid #dde3ed
  193. .card-top
  194. display flex
  195. justify-content space-between
  196. align-items center
  197. margin-bottom 16rpx
  198. // 标签
  199. .tag
  200. font-size 20rpx
  201. padding 4rpx 12rpx
  202. border-radius 8rpx
  203. border 1rpx solid transparent
  204. .tag-blue
  205. background #e6f4ff
  206. color #1677ff
  207. border-color #91caff
  208. .tag-green
  209. background #f6ffed
  210. color #389e0d
  211. border-color #b7eb8f
  212. .tag-orange
  213. background #fff7e6
  214. color #d46b08
  215. border-color #ffd591
  216. .tag-purple
  217. background #f9f0ff
  218. color #722ed1
  219. border-color #d3adf7
  220. .tag-cyan
  221. background #e6fffb
  222. color #08979c
  223. border-color #87e8de
  224. .total-count
  225. font-size 22rpx
  226. color #1677ff
  227. font-weight 600
  228. .text-hint
  229. color #bbb
  230. // 卡片文字
  231. .card-title
  232. display block
  233. font-size 28rpx
  234. font-weight 600
  235. color #222
  236. margin-bottom 8rpx
  237. .card-sub
  238. display block
  239. font-size 22rpx
  240. color #666
  241. margin-bottom 16rpx
  242. .text-gray
  243. color #999
  244. // 进度条
  245. .progress-row
  246. display flex
  247. align-items center
  248. gap 12rpx
  249. .progress-wrap
  250. flex 1
  251. height 10rpx
  252. background #eef1f6
  253. border-radius 10rpx
  254. overflow hidden
  255. .progress-bar
  256. height 100%
  257. border-radius 10rpx
  258. background #1677ff
  259. transition width 0.3s
  260. .progress-text
  261. font-size 22rpx
  262. font-weight 700
  263. width 64rpx
  264. text-align right
  265. // 提示
  266. .empty-tip
  267. text-align center
  268. line-height 80rpx
  269. color #aaa
  270. font-size 24rpx
  271. .loading-tip
  272. text-align center
  273. line-height 60rpx
  274. color #aaa
  275. font-size 22rpx
  276. </style>