index.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <template>
  2. <view class="badge-page">
  3. <!-- 顶部段位横幅 -->
  4. <view class="grade-banner" :style="{ backgroundColor: gradeColor }">
  5. <view class="grade-info">
  6. <text class="grade-title">{{ gradeText }}</text>
  7. <text class="grade-sub">{{ gradeSubText }}</text>
  8. </view>
  9. <view class="grade-progress">
  10. <text class="progress-text">已获得 {{ earnedCount }}/32</text>
  11. </view>
  12. </view>
  13. <!-- 徽章分区 -->
  14. <view class="badge-section" v-for="(group, gIdx) in badgeGroups" :key="gIdx">
  15. <view class="section-header">
  16. <text class="section-title">{{ group.title }}</text>
  17. </view>
  18. <view class="badge-grid">
  19. <view
  20. class="badge-card"
  21. v-for="(badge, bIdx) in group.list"
  22. :key="bIdx"
  23. @click="handleBadgeClick(badge)"
  24. :style="badge.earned ? 'background-color:' + badge.color : ''"
  25. >
  26. <view class="badge-content">
  27. <text class="badge-icon" :class="badge.earned ? 'icon-earned' : ''">{{ badge.earned ? '✓' : '🔒' }}</text>
  28. <text class="badge-name" :class="badge.earned ? 'name-earned' : ''">{{ badge.name }}</text>
  29. </view>
  30. </view>
  31. </view>
  32. </view>
  33. <view class="footer-space"></view>
  34. </view>
  35. </template>
  36. <script>
  37. import { getMyBadges } from '@/utils/api.js'
  38. export default {
  39. data() {
  40. return {
  41. loading: true,
  42. grade: null,
  43. earnedCount: 0,
  44. badges: [],
  45. gradeColorMap: {
  46. novice: '#94A3B8',
  47. starter: '#7C3AED',
  48. expert: '#F59E0B',
  49. system: '#14B8A6',
  50. lecturer: '#F97316'
  51. },
  52. categoryMap: {
  53. module: '模块徽章',
  54. course: '课程徽章',
  55. skill: '技能徽章',
  56. milestone: '里程碑',
  57. lecturer: '讲师荣誉'
  58. }
  59. }
  60. },
  61. computed: {
  62. gradeColor() {
  63. if (!this.grade || !this.grade.key) return '#CBD5E1'
  64. return this.gradeColorMap[this.grade.key] || '#CBD5E1'
  65. },
  66. gradeText() {
  67. if (!this.grade || !this.grade.name) return '未入门'
  68. return this.grade.name
  69. },
  70. gradeSubText() {
  71. if (!this.grade) return '参加沙龙开启成长之旅'
  72. return '持续探索,攀登更高峰'
  73. },
  74. badgeGroups() {
  75. var groups = []
  76. var cats = ['module', 'course', 'skill', 'milestone', 'lecturer']
  77. for (var i = 0; i < cats.length; i++) {
  78. var cat = cats[i]
  79. var list = this.badges.filter(function(b) {
  80. return b.category === cat
  81. })
  82. if (list.length > 0) {
  83. groups.push({
  84. title: this.categoryMap[cat] || cat,
  85. list: list
  86. })
  87. }
  88. }
  89. return groups
  90. }
  91. },
  92. onLoad() {
  93. this.fetchBadges()
  94. },
  95. methods: {
  96. fetchBadges() {
  97. var self = this
  98. self.loading = true
  99. getMyBadges().then(function(resp) {
  100. var data = resp.data || {}
  101. self.grade = data.grade || null
  102. self.earnedCount = data.earnedCount || 0
  103. self.badges = data.badges || []
  104. }).catch(function(err) {
  105. uni.showToast({
  106. title: (err && err.message) || '加载失败',
  107. icon: 'none'
  108. })
  109. }).finally(function() {
  110. self.loading = false
  111. })
  112. },
  113. handleBadgeClick(badge) {
  114. if (badge.earned) {
  115. uni.showToast({
  116. title: '已获得',
  117. icon: 'success'
  118. })
  119. } else {
  120. uni.showModal({
  121. title: '获得条件',
  122. content: badge.condition || '暂无条件说明',
  123. showCancel: false
  124. })
  125. }
  126. }
  127. }
  128. }
  129. </script>
  130. <style scoped>
  131. .badge-page {
  132. min-height: 100vh;
  133. background: #F8FAFC;
  134. padding: 32rpx;
  135. }
  136. .grade-banner {
  137. border-radius: 24rpx;
  138. padding: 48rpx 32rpx;
  139. display: flex;
  140. justify-content: space-between;
  141. align-items: center;
  142. color: #FFF;
  143. margin-bottom: 40rpx;
  144. box-shadow: 0 8rpx 24rpx rgba(0,0,0,0.1);
  145. }
  146. .grade-info {
  147. display: flex;
  148. flex-direction: column;
  149. }
  150. .grade-title {
  151. font-size: 44rpx;
  152. font-weight: 700;
  153. margin-bottom: 8rpx;
  154. }
  155. .grade-sub {
  156. font-size: 26rpx;
  157. opacity: 0.9;
  158. }
  159. .grade-progress {
  160. background: rgba(255,255,255,0.2);
  161. padding: 12rpx 24rpx;
  162. border-radius: 32rpx;
  163. backdrop-filter: blur(4px);
  164. }
  165. .progress-text {
  166. font-size: 24rpx;
  167. font-weight: 600;
  168. }
  169. .badge-section {
  170. margin-bottom: 40rpx;
  171. }
  172. .section-header {
  173. margin-bottom: 24rpx;
  174. padding-left: 16rpx;
  175. border-left: 8rpx solid #CBD5E1;
  176. }
  177. .section-title {
  178. font-size: 32rpx;
  179. font-weight: 700;
  180. color: #1E293B;
  181. }
  182. .badge-grid {
  183. display: flex;
  184. flex-wrap: wrap;
  185. justify-content: flex-start;
  186. }
  187. .badge-card {
  188. width: 180rpx;
  189. height: 180rpx;
  190. margin: 12rpx;
  191. border-radius: 20rpx;
  192. display: flex;
  193. align-items: center;
  194. justify-content: center;
  195. transition: transform 0.1s;
  196. background: #F1F5F9; /* Default locked color */
  197. border: 2rpx solid #E2E8F0;
  198. }
  199. .badge-card:active {
  200. transform: scale(0.95);
  201. }
  202. .badge-content {
  203. display: flex;
  204. flex-direction: column;
  205. align-items: center;
  206. text-align: center;
  207. }
  208. .badge-icon {
  209. font-size: 40rpx;
  210. margin-bottom: 8rpx;
  211. color: #94A3B8;
  212. }
  213. .badge-name {
  214. font-size: 24rpx;
  215. color: #64748B;
  216. width: 140rpx;
  217. overflow: hidden;
  218. text-overflow: ellipsis;
  219. white-space: pre-wrap;
  220. line-height: 1.4;
  221. }
  222. /* Earned state overrides */
  223. .icon-earned {
  224. color: #FFF;
  225. }
  226. .name-earned {
  227. color: rgba(255,255,255,0.9);
  228. }
  229. .footer-space {
  230. height: 60rpx;
  231. }
  232. </style>