gates.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. <template>
  2. <view class="gates-page">
  3. <view class="page-section" v-if="mainGates.length > 0">
  4. <text class="section-title">主线关卡</text>
  5. <view class="gate-list">
  6. <view class="gate-card" v-for="(g, i) in mainGates" :key="getGateKey(g, i)"
  7. :class="'gate-card--' + (g.status === 'UNLOCKED' ? 'done' : (g.status === 'CURRENT' ? 'current' : 'locked'))">
  8. <view class="gate-status-icon">
  9. <text v-if="g.status === 'UNLOCKED'">✅</text>
  10. <text v-else-if="g.status === 'CURRENT'">🔓</text>
  11. <text v-else>🔒</text>
  12. </view>
  13. <view class="gate-info">
  14. <text class="gate-name">{{ g.name }}</text>
  15. <text class="gate-desc">{{ g.description }}</text>
  16. <view class="gate-reward" v-if="g.rewardPoints > 0 || g.rewardEnergy > 0">
  17. <text class="reward-label">奖励:</text>
  18. <text class="reward-points" v-if="g.rewardPoints > 0">+{{ g.rewardPoints }} CF值</text>
  19. <text class="reward-energy" v-if="g.rewardEnergy > 0">+{{ g.rewardEnergy }}能量</text>
  20. </view>
  21. </view>
  22. <view class="gate-action">
  23. <text v-if="g.status === 'CURRENT'" class="action-btn" @click="goGateAction(g)">去完成</text>
  24. <text v-else-if="g.status === 'LOCKED'" class="action-disabled">未解锁</text>
  25. <text v-else class="action-done">已解锁</text>
  26. </view>
  27. </view>
  28. </view>
  29. </view>
  30. <view class="page-section" v-if="softGates.length > 0">
  31. <text class="section-title">推荐挑战</text>
  32. <view class="gate-list">
  33. <view class="gate-card" :class="'gate-card--' + (g.status === 'UNLOCKED' ? 'done' : 'current')"
  34. v-for="(g, i) in softGates" :key="getGateKey(g, i)">
  35. <view class="gate-status-icon">
  36. <text v-if="g.status === 'UNLOCKED'">✅</text>
  37. <text v-else>🎯</text>
  38. </view>
  39. <view class="gate-info">
  40. <text class="gate-name">{{ g.name }}</text>
  41. <text class="gate-desc">{{ g.description }}</text>
  42. <view class="gate-reward" v-if="g.rewardPoints > 0 || g.rewardEnergy > 0">
  43. <text class="reward-label">奖励:</text>
  44. <text class="reward-points" v-if="g.rewardPoints > 0">+{{ g.rewardPoints }} CF值</text>
  45. <text class="reward-energy" v-if="g.rewardEnergy > 0">+{{ g.rewardEnergy }}能量</text>
  46. </view>
  47. </view>
  48. <view class="gate-action">
  49. <text v-if="g.status !== 'UNLOCKED'" class="action-btn" @click="goGateAction(g)">去挑战</text>
  50. <text v-else class="action-done">已完成</text>
  51. </view>
  52. </view>
  53. </view>
  54. </view>
  55. <view class="page-section" v-if="valueGates.length > 0">
  56. <text class="section-title">增值关卡</text>
  57. <view class="gate-list">
  58. <view class="gate-card gate-card--value" v-for="(g, i) in valueGates" :key="getGateKey(g, i)">
  59. <view class="gate-status-icon">
  60. <text v-if="g.status === 'UNLOCKED'">✅</text>
  61. <text v-else>💎</text>
  62. </view>
  63. <view class="gate-info">
  64. <text class="gate-name">{{ g.name }}</text>
  65. <text class="gate-desc">{{ g.description }}</text>
  66. <view class="gate-reward" v-if="g.rewardPoints > 0 || g.rewardEnergy > 0">
  67. <text class="reward-label">奖励:</text>
  68. <text class="reward-points" v-if="g.rewardPoints > 0">+{{ g.rewardPoints }} CF值</text>
  69. <text class="reward-energy" v-if="g.rewardEnergy > 0">+{{ g.rewardEnergy }}能量</text>
  70. </view>
  71. </view>
  72. <view class="gate-action">
  73. <text v-if="g.status !== 'UNLOCKED'" class="action-btn" @click="goGateAction(g)">前往</text>
  74. <text v-else class="action-done">已解锁</text>
  75. </view>
  76. </view>
  77. </view>
  78. </view>
  79. <view class="empty-state" v-if="loading">
  80. <text>加载中...</text>
  81. </view>
  82. </view>
  83. </template>
  84. <script>
  85. import { getUnlockStatus } from '../../utils/api'
  86. export default {
  87. data: function() {
  88. return {
  89. loading: true,
  90. gates: []
  91. }
  92. },
  93. computed: {
  94. mainGates: function() {
  95. return this.gates.filter(function(g) { return g.isRequired === 1 && g.isSoft === 0 })
  96. },
  97. softGates: function() {
  98. return this.gates.filter(function(g) { return g.isRequired === 1 && g.isSoft === 1 })
  99. },
  100. valueGates: function() {
  101. return this.gates.filter(function(g) { return g.isRequired === 0 })
  102. }
  103. },
  104. onShow: function() {
  105. this.loadData()
  106. },
  107. methods: {
  108. getGateKey: function(g, i) { return (g && g.id) ? g.id : i },
  109. loadData: function() {
  110. var self = this
  111. self.loading = true
  112. getUnlockStatus().then(function(res) {
  113. if (res && res.data) {
  114. self.gates = res.data.gates || []
  115. }
  116. self.loading = false
  117. }).catch(function() {
  118. self.loading = false
  119. })
  120. },
  121. goGateAction: function(g) {
  122. var type = g.gateType
  123. var url = ''
  124. switch (type) {
  125. case 'SELF_CHECK':
  126. url = '/pages/family/self-check'
  127. break
  128. case 'INVITE_FAMILY':
  129. url = '/pages/profile-extra/family-members'
  130. break
  131. case 'CREATE_FAMILY_MEMBER':
  132. url = '/pages/profile-extra/family-members'
  133. break
  134. case 'PURCHASE':
  135. url = '/pages/shop/index/index'
  136. break
  137. case 'MEMBERSHIP':
  138. url = '/pages/membership/index'
  139. break
  140. case 'REPORT_UPLOAD':
  141. url = '/pages/health/report-upload'
  142. break
  143. case 'MICRO_ACTION':
  144. url = '/pages/home-pages/parent-index'
  145. break
  146. case 'STREAK_DAYS':
  147. url = '/pages/health/daily-checkin'
  148. break
  149. }
  150. if (url) {
  151. uni.navigateTo({ url: url })
  152. }
  153. }
  154. }
  155. }
  156. </script>
  157. <style scoped>
  158. .gates-page {
  159. padding: 20rpx 0;
  160. min-height: 100vh;
  161. background: #f5f5f5;
  162. }
  163. .page-section {
  164. margin-bottom: 24rpx;
  165. }
  166. .section-title {
  167. font-size: 28rpx;
  168. font-weight: bold;
  169. color: #333;
  170. padding: 0 30rpx;
  171. margin-bottom: 16rpx;
  172. display: block;
  173. }
  174. .gate-list {
  175. padding: 0 24rpx;
  176. }
  177. .gate-card {
  178. display: flex;
  179. align-items: center;
  180. background: #fff;
  181. border-radius: 16rpx;
  182. padding: 24rpx;
  183. margin-bottom: 16rpx;
  184. box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
  185. }
  186. .gate-card--done {
  187. opacity: 0.7;
  188. }
  189. .gate-card--current {
  190. border: 2rpx solid #5B9BD5;
  191. }
  192. .gate-status-icon {
  193. width: 60rpx;
  194. font-size: 36rpx;
  195. text-align: center;
  196. flex-shrink: 0;
  197. }
  198. .gate-info {
  199. flex: 1;
  200. margin: 0 16rpx;
  201. }
  202. .gate-name {
  203. font-size: 28rpx;
  204. font-weight: bold;
  205. color: #333;
  206. display: block;
  207. }
  208. .gate-desc {
  209. font-size: 22rpx;
  210. color: #999;
  211. margin-top: 4rpx;
  212. display: block;
  213. }
  214. .gate-reward {
  215. display: flex;
  216. align-items: center;
  217. gap: 8rpx;
  218. margin-top: 8rpx;
  219. flex-wrap: wrap;
  220. }
  221. .reward-label {
  222. font-size: 20rpx;
  223. color: #999;
  224. }
  225. .reward-points, .reward-energy {
  226. font-size: 20rpx;
  227. padding: 2rpx 10rpx;
  228. border-radius: 12rpx;
  229. }
  230. .reward-points { color: #B8860B; background: rgba(255,215,0,0.15); }
  231. .reward-energy { color: #3A7CC2; background: rgba(66,165,245,0.15); }
  232. .gate-action {
  233. flex-shrink: 0;
  234. }
  235. .action-btn {
  236. font-size: 24rpx;
  237. color: #fff;
  238. background: #5B9BD5;
  239. padding: 8rpx 20rpx;
  240. border-radius: 20rpx;
  241. }
  242. .action-btn:active {
  243. opacity: 0.8;
  244. }
  245. .action-disabled {
  246. font-size: 22rpx;
  247. color: #ccc;
  248. }
  249. .action-done {
  250. font-size: 22rpx;
  251. color: #5B9BD5;
  252. }
  253. .empty-state {
  254. text-align: center;
  255. padding: 60rpx;
  256. color: #999;
  257. }
  258. </style>