gates.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  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="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 }}积分</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="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 }}积分</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="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 }}积分</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. loadData: function() {
  109. var self = this
  110. self.loading = true
  111. getUnlockStatus().then(function(res) {
  112. if (res && res.data) {
  113. self.gates = res.data.gates || []
  114. }
  115. self.loading = false
  116. }).catch(function() {
  117. self.loading = false
  118. })
  119. },
  120. goGateAction: function(g) {
  121. var type = g.gateType
  122. var url = ''
  123. switch (type) {
  124. case 'SELF_CHECK':
  125. url = '/pages/family/self-check'
  126. break
  127. case 'INVITE_FAMILY':
  128. url = '/pages/family/address-edit'
  129. break
  130. case 'CREATE_FAMILY_MEMBER':
  131. url = '/pages/profile-extra/family-members'
  132. break
  133. case 'PURCHASE':
  134. url = '/pages/shop/index/index'
  135. break
  136. case 'MEMBERSHIP':
  137. url = '/pages/membership/index'
  138. break
  139. case 'REPORT_UPLOAD':
  140. url = '/pages/health/report-upload'
  141. break
  142. case 'MICRO_ACTION':
  143. url = '/pages/home-pages/parent-index'
  144. break
  145. case 'STREAK_DAYS':
  146. url = '/pages/health/daily-checkin'
  147. break
  148. }
  149. if (url) {
  150. uni.navigateTo({ url: url })
  151. }
  152. }
  153. }
  154. }
  155. </script>
  156. <style scoped>
  157. .gates-page {
  158. padding: 20rpx 0;
  159. min-height: 100vh;
  160. background: #f5f5f5;
  161. }
  162. .page-section {
  163. margin-bottom: 24rpx;
  164. }
  165. .section-title {
  166. font-size: 28rpx;
  167. font-weight: bold;
  168. color: #333;
  169. padding: 0 30rpx;
  170. margin-bottom: 16rpx;
  171. display: block;
  172. }
  173. .gate-list {
  174. padding: 0 24rpx;
  175. }
  176. .gate-card {
  177. display: flex;
  178. align-items: center;
  179. background: #fff;
  180. border-radius: 16rpx;
  181. padding: 24rpx;
  182. margin-bottom: 16rpx;
  183. box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
  184. }
  185. .gate-card--done {
  186. opacity: 0.7;
  187. }
  188. .gate-card--current {
  189. border: 2rpx solid #5B9BD5;
  190. }
  191. .gate-status-icon {
  192. width: 60rpx;
  193. font-size: 36rpx;
  194. text-align: center;
  195. flex-shrink: 0;
  196. }
  197. .gate-info {
  198. flex: 1;
  199. margin: 0 16rpx;
  200. }
  201. .gate-name {
  202. font-size: 28rpx;
  203. font-weight: bold;
  204. color: #333;
  205. display: block;
  206. }
  207. .gate-desc {
  208. font-size: 22rpx;
  209. color: #999;
  210. margin-top: 4rpx;
  211. display: block;
  212. }
  213. .gate-reward {
  214. display: flex;
  215. align-items: center;
  216. gap: 8rpx;
  217. margin-top: 8rpx;
  218. flex-wrap: wrap;
  219. }
  220. .reward-label {
  221. font-size: 20rpx;
  222. color: #999;
  223. }
  224. .reward-points, .reward-energy {
  225. font-size: 20rpx;
  226. padding: 2rpx 10rpx;
  227. border-radius: 12rpx;
  228. }
  229. .reward-points { color: #B8860B; background: rgba(255,215,0,0.15); }
  230. .reward-energy { color: #3A7CC2; background: rgba(66,165,245,0.15); }
  231. .gate-action {
  232. flex-shrink: 0;
  233. }
  234. .action-btn {
  235. font-size: 24rpx;
  236. color: #fff;
  237. background: #5B9BD5;
  238. padding: 8rpx 20rpx;
  239. border-radius: 20rpx;
  240. }
  241. .action-btn:active {
  242. opacity: 0.8;
  243. }
  244. .action-disabled {
  245. font-size: 22rpx;
  246. color: #ccc;
  247. }
  248. .action-done {
  249. font-size: 22rpx;
  250. color: #5B9BD5;
  251. }
  252. .empty-state {
  253. text-align: center;
  254. padding: 60rpx;
  255. color: #999;
  256. }
  257. </style>