index.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. <!-- 学时任务列表 -->
  2. <template>
  3. <view class="safetyEducationExamination-studyHourTask">
  4. <!-- 顶部类型标签 -->
  5. <scroll-view scroll-x class="tag-scroll">
  6. <view class="tag-scroll-inner">
  7. <text
  8. v-for="(tag, index) in typeTags"
  9. :key="index"
  10. class="filter-tag"
  11. :class="{ 'filter-tag-active': activeTagIndex === index }"
  12. @click="onTagChange(index)"
  13. >{{ tag.label }}</text>
  14. </view>
  15. </scroll-view>
  16. <!-- 课程列表 -->
  17. <scroll-view scroll-y class="scroll-content">
  18. <view class="panel">
  19. <view
  20. v-for="(item, index) in courseList"
  21. :key="index"
  22. class="course-card"
  23. @click="goCourseDetail(item)"
  24. >
  25. <view class="course-cover" :style="'background:' + item.coverGradient">
  26. <text class="cover-icon">{{ item.coverIcon || '📖' }}</text>
  27. </view>
  28. <view class="course-info">
  29. <view class="course-info-top">
  30. <text class="course-name">{{ item.courseName }}</text>
  31. <text class="status-tag" :class="item.statusClass">{{ item.statusName }}</text>
  32. </view>
  33. <text class="course-meta">{{ item.courseDuration }}分钟 | {{ item.studyHours }}学时 | {{ item.points }}积分</text>
  34. </view>
  35. </view>
  36. <view v-if="!courseList[0] && !loading" class="empty-tip">暂无学时任务</view>
  37. <view v-if="loading" class="loading-tip">加载中...</view>
  38. </view>
  39. </scroll-view>
  40. </view>
  41. </template>
  42. <script>
  43. import {
  44. examUserCourseLearningList,
  45. examUserExamTypeSelectExamType,
  46. } from '@/pages_safetyEducationExamination/api/index.js'
  47. // 封面渐变色池
  48. const COVER_GRADIENTS = [
  49. 'linear-gradient(135deg,#ffd666,#fa8c16)',
  50. 'linear-gradient(135deg,#b37feb,#722ed1)',
  51. 'linear-gradient(135deg,#ff9c6e,#f5222d)',
  52. 'linear-gradient(135deg,#5cdbd3,#13c2c2)',
  53. 'linear-gradient(135deg,#1857d4,#5b8af0)',
  54. 'linear-gradient(135deg,#10a37f,#34d399)',
  55. 'linear-gradient(135deg,#6c63ff,#a78bfa)',
  56. 'linear-gradient(135deg,#95de64,#237804)',
  57. ];
  58. // 学习状态映射
  59. function getStatusInfo(status) {
  60. const map = {
  61. 0: { name: '未开始', cls: 'tag-gray' },
  62. 1: { name: '学习中', cls: 'tag-orange' },
  63. 2: { name: '已完成', cls: 'tag-green' },
  64. };
  65. return map[status] || map[0];
  66. }
  67. export default {
  68. data() {
  69. return {
  70. typeTags: [{ label: '全部', value: null }],
  71. activeTagIndex: 0,
  72. courseList: [],
  73. loading: false,
  74. }
  75. },
  76. created() {
  77. this.loadTypeTags();
  78. },
  79. onShow() {
  80. this.loadCourseList();
  81. },
  82. methods: {
  83. // 加载考试类型标签
  84. async loadTypeTags() {
  85. try {
  86. const { data } = await examUserExamTypeSelectExamType({});
  87. if (data.code === 200 && data.data) {
  88. const tags = (data.data || []).map(item => ({
  89. label: item.examTypeName,
  90. value: item.examTypeId,
  91. }));
  92. this.$set(this, 'typeTags', [{ label: '全部', value: null }, ...tags]);
  93. }
  94. } catch (e) {
  95. console.error('获取考试类型失败', e);
  96. }
  97. },
  98. onTagChange(index) {
  99. this.activeTagIndex = index;
  100. this.loadCourseList();
  101. },
  102. // 学时任务列表(courseType=1)
  103. async loadCourseList() {
  104. if (this.loading) return;
  105. this.loading = true;
  106. try {
  107. const tag = this.typeTags[this.activeTagIndex];
  108. const { data } = await examUserCourseLearningList({
  109. page: 1,
  110. pageSize: 50,
  111. courseType: 1,
  112. examTypeId: tag.value || undefined,
  113. });
  114. if (data.code === 200) {
  115. const list = (data.data.records || []).map((item, i) => {
  116. const s = getStatusInfo(item.learnStatus);
  117. return {
  118. ...item,
  119. coverGradient: COVER_GRADIENTS[i % COVER_GRADIENTS.length],
  120. statusName: s.name,
  121. statusClass: s.cls,
  122. };
  123. });
  124. this.$set(this, 'courseList', list);
  125. }
  126. } catch (e) {
  127. console.error('获取学时任务失败', e);
  128. } finally {
  129. this.loading = false;
  130. }
  131. },
  132. goCourseDetail(item) {
  133. // 跳转课程详情,传递 courseId
  134. },
  135. },
  136. }
  137. </script>
  138. <style lang="stylus" scoped>
  139. .safetyEducationExamination-studyHourTask
  140. height 100%
  141. display flex
  142. flex-direction column
  143. overflow hidden
  144. background-color #f0f4fb
  145. // 类型标签横滚
  146. .tag-scroll
  147. background #fff
  148. white-space nowrap
  149. padding 16rpx 24rpx
  150. border-bottom 1rpx solid #e8edf3
  151. flex-shrink 0
  152. .tag-scroll-inner
  153. display inline-flex
  154. gap 16rpx
  155. .filter-tag
  156. display inline-block
  157. font-size 22rpx
  158. padding 8rpx 20rpx
  159. border-radius 30rpx
  160. background #eef1f6
  161. color #666
  162. border 1rpx solid transparent
  163. white-space nowrap
  164. .filter-tag-active
  165. background #e6f0ff
  166. color #1677ff
  167. border-color #91caff
  168. font-weight 600
  169. // 滚动区
  170. .scroll-content
  171. flex 1
  172. overflow hidden
  173. .panel
  174. padding 24rpx
  175. // 课程卡片
  176. .course-card
  177. background #fff
  178. border-radius 20rpx
  179. margin-bottom 20rpx
  180. overflow hidden
  181. box-shadow 0 2rpx 12rpx rgba(0,0,0,0.04)
  182. border 1rpx solid #dde3ed
  183. .course-cover
  184. width 100%
  185. height 180rpx
  186. display flex
  187. align-items center
  188. justify-content center
  189. .cover-icon
  190. font-size 64rpx
  191. .course-info
  192. padding 20rpx 24rpx
  193. .course-info-top
  194. display flex
  195. justify-content space-between
  196. align-items center
  197. margin-bottom 8rpx
  198. .course-name
  199. flex 1
  200. font-size 28rpx
  201. font-weight 600
  202. color #222
  203. margin-right 12rpx
  204. .course-meta
  205. font-size 22rpx
  206. color #999
  207. // 状态标签
  208. .status-tag
  209. font-size 20rpx
  210. padding 4rpx 12rpx
  211. border-radius 8rpx
  212. border 1rpx solid transparent
  213. flex-shrink 0
  214. .tag-gray
  215. background #f5f5f5
  216. color #999
  217. border-color #e8e8e8
  218. .tag-orange
  219. background #fff7e6
  220. color #d46b08
  221. border-color #ffd591
  222. .tag-green
  223. background #f6ffed
  224. color #389e0d
  225. border-color #b7eb8f
  226. // 提示
  227. .empty-tip
  228. text-align center
  229. line-height 80rpx
  230. color #aaa
  231. font-size 24rpx
  232. .loading-tip
  233. text-align center
  234. line-height 60rpx
  235. color #aaa
  236. font-size 22rpx
  237. </style>