detail.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. <template>
  2. <view class="container">
  3. <!-- 头部:维度信息 -->
  4. <view class="dim-header" v-if="dimInfo">
  5. <view class="dim-icon-wrap" :class="'dim-icon--' + elementColor">
  6. <text class="dim-icon">{{ dimInfo.icon }}</text>
  7. </view>
  8. <text class="dim-name">{{ dimInfo.name }}</text>
  9. <text class="dim-element">{{ dimInfo.element }} · {{ dimInfo.code }}</text>
  10. <view class="dim-energy">
  11. <text class="dim-energy-value">{{ dimEnergy }}</text>
  12. <text class="dim-energy-label">能量值</text>
  13. </view>
  14. </view>
  15. <!-- 加载状态 -->
  16. <BaseLoading :loading="loading" text="加载中..." />
  17. <!-- 流水列表 -->
  18. <view class="logs-section" v-if="hasFamily && !loading">
  19. <view class="section-header">
  20. <text class="section-title">📋 能量流水</text>
  21. </view>
  22. <view class="log-item" v-for="(log, idx) in logs" :key="idx" v-if="logs.length > 0">
  23. <view class="log-left">
  24. <text :class="log.amount > 0 ? 'log-amount earn' : 'log-amount spend'">
  25. {{ log.amount > 0 ? '+' : '' }}{{ log.amount }}
  26. </text>
  27. <text class="log-desc">{{ log.description || '能量变动' }}</text>
  28. </view>
  29. <view class="log-right">
  30. <text class="log-balance">余额 {{ log.balanceAfter }}</text>
  31. <text class="log-time">{{ formatTime(log.createdAt) }}</text>
  32. </view>
  33. </view>
  34. <BaseEmpty v-if="logs.length === 0" icon="💧" title="暂无能量流水" description="完成任务可以获取能量值" />
  35. <view class="load-more" v-if="hasMore" @click="loadMore">
  36. <text>加载更多...</text>
  37. </view>
  38. </view>
  39. <view v-else-if="!loading">
  40. <FamilyEmptyState type="list" />
  41. </view>
  42. </view>
  43. </template>
  44. <script>
  45. import { getEnergyLogs } from '../../utils/api.js'
  46. import BaseLoading from '../../components/BaseLoading.vue'
  47. import BaseEmpty from '../../components/BaseEmpty.vue'
  48. import { parseDate } from '../../utils/format.js'
  49. var ELEMENT_MAP = {
  50. 'mind': 'fire', 'body': 'earth', 'wisdom': 'metal',
  51. 'action': 'wood', 'wealth': 'water'
  52. }
  53. export default {
  54. components: { BaseLoading, BaseEmpty },
  55. data() {
  56. return {
  57. memberId: '',
  58. code: '',
  59. dimInfo: null,
  60. dimEnergy: 0,
  61. loading: true,
  62. logs: [],
  63. page: 1,
  64. pageSize: 20,
  65. hasMore: true,
  66. elementColor: 'fire'
  67. }
  68. },
  69. computed: {
  70. hasFamily: function() {
  71. return this.$store.getters.hasFamily
  72. }
  73. },
  74. onLoad(options) {
  75. if (options.memberId) this.memberId = options.memberId
  76. if (options.code) {
  77. this.code = options.code
  78. this.elementColor = ELEMENT_MAP[options.code] || 'fire'
  79. }
  80. // 如果有传入 dimData(URL编码的JSON),解析维度信息
  81. if (options.dimData) {
  82. try {
  83. var parsed = JSON.parse(decodeURIComponent(options.dimData))
  84. if (parsed) {
  85. this.dimInfo = parsed
  86. this.dimEnergy = parsed.energy || 0
  87. }
  88. } catch (e) { /* ignore */ }
  89. }
  90. this.loadLogs()
  91. },
  92. methods: {
  93. async loadLogs() {
  94. this.loading = true
  95. try {
  96. var res = await getEnergyLogs(this.memberId, this.code, this.page, this.pageSize)
  97. if (res && res.data) {
  98. var records = res.data.records || []
  99. if (this.page === 1) {
  100. this.logs = records
  101. } else {
  102. this.logs = this.logs.concat(records)
  103. }
  104. this.hasMore = records.length >= this.pageSize
  105. }
  106. } catch (e) {
  107. console.log('获取能量流水失败', e)
  108. } finally {
  109. this.loading = false
  110. }
  111. },
  112. loadMore() {
  113. this.page++
  114. this.loadLogs()
  115. },
  116. formatTime(dateStr) {
  117. if (!dateStr) return ''
  118. var d = parseDate(dateStr)
  119. if (!d) return ''
  120. var month = d.getMonth() + 1
  121. var day = d.getDate()
  122. var hour = d.getHours()
  123. var min = d.getMinutes()
  124. if (month < 10) month = '0' + month
  125. if (day < 10) day = '0' + day
  126. if (hour < 10) hour = '0' + hour
  127. if (min < 10) min = '0' + min
  128. return month + '-' + day + ' ' + hour + ':' + min
  129. }
  130. }
  131. }
  132. </script>
  133. <style scoped>
  134. .container {
  135. min-height: 100vh;
  136. background: #0F1B2D;
  137. padding-bottom: 40rpx;
  138. }
  139. /* ---- 头部 ---- */
  140. .dim-header {
  141. display: flex;
  142. flex-direction: column;
  143. align-items: center;
  144. padding: 60rpx 0 40rpx;
  145. position: relative;
  146. }
  147. .dim-header::after {
  148. content: '';
  149. position: absolute;
  150. bottom: 0;
  151. left: 10%;
  152. width: 80%;
  153. height: 1rpx;
  154. background: linear-gradient(90deg, transparent, rgba(143,197,232,0.2), transparent);
  155. }
  156. .dim-icon-wrap {
  157. width: 100rpx;
  158. height: 100rpx;
  159. border-radius: 50%;
  160. display: flex;
  161. align-items: center;
  162. justify-content: center;
  163. margin-bottom: 16rpx;
  164. box-shadow: 0 0 40rpx rgba(255,255,255,0.15);
  165. }
  166. .dim-icon--fire { background: linear-gradient(135deg, #FF6B35, #E65100); }
  167. .dim-icon--water { background: linear-gradient(135deg, #42A5F5, #1565C0); }
  168. .dim-icon--earth { background: linear-gradient(135deg, #8D6E63, #5D4037); }
  169. .dim-icon--metal { background: linear-gradient(135deg, #FFD700, #FF8F00); }
  170. .dim-icon--wood { background: linear-gradient(135deg, #4CAF50, #2E7D32); }
  171. .dim-icon { font-size: 44rpx; }
  172. .dim-name { font-size: 36rpx; font-weight: bold; color: #FFFFFF; }
  173. .dim-element { font-size: 22rpx; color: rgba(143,197,232,0.5); margin-top: 6rpx; }
  174. .dim-energy {
  175. display: flex;
  176. flex-direction: column;
  177. align-items: center;
  178. margin-top: 20rpx;
  179. }
  180. .dim-energy-value { font-size: 56rpx; font-weight: bold; color: #F97316; }
  181. .dim-energy-label { font-size: 20rpx; color: rgba(143,197,232,0.4); }
  182. /* ---- 流水 ---- */
  183. .logs-section {
  184. padding: 30rpx 30rpx 0;
  185. }
  186. .section-header { margin-bottom: 20rpx; }
  187. .section-title { font-size: 28rpx; font-weight: bold; color: #8FC5E8; }
  188. .log-item {
  189. display: flex;
  190. justify-content: space-between;
  191. align-items: center;
  192. padding: 24rpx 20rpx;
  193. background: rgba(255,255,255,0.04);
  194. border-radius: 16rpx;
  195. margin-bottom: 12rpx;
  196. }
  197. .log-left { display: flex; flex-direction: column; }
  198. .log-amount { font-size: 32rpx; font-weight: bold; }
  199. .log-amount.earn { color: #4CAF50; }
  200. .log-amount.spend { color: #FF6B35; }
  201. .log-desc { font-size: 22rpx; color: rgba(255,255,255,0.5); margin-top: 4rpx; }
  202. .log-right { display: flex; flex-direction: column; align-items: flex-end; }
  203. .log-balance { font-size: 22rpx; color: rgba(255,255,255,0.4); }
  204. .log-time { font-size: 20rpx; color: rgba(255,255,255,0.25); margin-top: 4rpx; }
  205. .load-more {
  206. text-align: center;
  207. padding: 24rpx;
  208. color: rgba(143,197,232,0.4);
  209. font-size: 24rpx;
  210. }
  211. </style>