points-detail.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. <template>
  2. <view class="points-detail">
  3. <!-- 顶部导航栏 -->
  4. <view class="header">
  5. <view class="back-icon" @click="handleBack"></view>
  6. <text class="header-title">积分明细</text>
  7. <view class="header-right"></view>
  8. </view>
  9. <!-- 积分余额卡片 -->
  10. <view class="balance-card">
  11. <view class="balance-label">当前积分</view>
  12. <view class="balance-value">{{ balance }}</view>
  13. </view>
  14. <!-- 明细列表 -->
  15. <view class="records-section">
  16. <view class="section-title">积分记录</view>
  17. <view v-if="records.length === 0 && !loading" class="empty-tip">
  18. 暂无积分记录
  19. </view>
  20. <view class="record-list">
  21. <view class="record-item" v-for="(item, index) in records" :key="index">
  22. <view class="record-left">
  23. <view class="record-icon" :class="item.points > 0 ? 'income' : 'expense'">
  24. {{ item.points > 0 ? '+' : '-' }}
  25. </view>
  26. <view class="record-info">
  27. <view class="record-reason">{{ item.reason }}</view>
  28. <view class="record-time">{{ formatTime(item.createTime) }}</view>
  29. </view>
  30. </view>
  31. <view class="record-points" :class="item.points > 0 ? 'income' : 'expense'">
  32. {{ item.points > 0 ? '+' : '' }}{{ item.points }}
  33. </view>
  34. </view>
  35. </view>
  36. <!-- 加载更多 -->
  37. <view class="load-more" v-if="hasMore" @click="loadMore">
  38. {{ loading ? '加载中...' : '加载更多' }}
  39. </view>
  40. </view>
  41. </view>
  42. </template>
  43. <script>
  44. import api from '@/utils/api.js'
  45. export default {
  46. name: 'points-detail',
  47. data() {
  48. return {
  49. balance: 0,
  50. records: [],
  51. makerId: null,
  52. pageNum: 1,
  53. pageSize: 20,
  54. total: 0,
  55. loading: false
  56. }
  57. },
  58. computed: {
  59. hasMore() {
  60. return this.records.length < this.total
  61. }
  62. },
  63. onLoad() {
  64. this.initData()
  65. },
  66. methods: {
  67. async initData() {
  68. const userInfo = uni.getStorageSync('userInfo')
  69. if (userInfo && userInfo.matchmakerId) {
  70. this.makerId = userInfo.matchmakerId
  71. } else if (userInfo && userInfo.userId) {
  72. this.makerId = userInfo.userId
  73. }
  74. await this.loadRecords()
  75. },
  76. async loadRecords() {
  77. if (!this.makerId || this.loading) return
  78. this.loading = true
  79. try {
  80. const res = await api.pointsMall.getRecords(this.makerId, this.pageNum, this.pageSize)
  81. this.balance = res.balance || 0
  82. this.total = res.total || 0
  83. if (this.pageNum === 1) {
  84. this.records = res.list || []
  85. } else {
  86. this.records = [...this.records, ...(res.list || [])]
  87. }
  88. } catch (e) {
  89. console.error('获取积分明细失败:', e)
  90. uni.showToast({ title: '获取积分明细失败', icon: 'none' })
  91. } finally {
  92. this.loading = false
  93. }
  94. },
  95. loadMore() {
  96. if (this.hasMore && !this.loading) {
  97. this.pageNum++
  98. this.loadRecords()
  99. }
  100. },
  101. handleBack() {
  102. uni.navigateBack()
  103. },
  104. formatTime(timeStr) {
  105. if (!timeStr) return ''
  106. const date = new Date(timeStr)
  107. const year = date.getFullYear()
  108. const month = String(date.getMonth() + 1).padStart(2, '0')
  109. const day = String(date.getDate()).padStart(2, '0')
  110. const hour = String(date.getHours()).padStart(2, '0')
  111. const minute = String(date.getMinutes()).padStart(2, '0')
  112. return `${year}-${month}-${day} ${hour}:${minute}`
  113. }
  114. }
  115. }
  116. </script>
  117. <style lang="scss" scoped>
  118. .points-detail {
  119. min-height: 100vh;
  120. background: #F5F5F5;
  121. }
  122. .header {
  123. display: flex;
  124. align-items: center;
  125. justify-content: space-between;
  126. padding: 25rpx 30rpx;
  127. padding-top: calc(25rpx + env(safe-area-inset-top));
  128. background: #FFFFFF;
  129. .back-icon {
  130. width: 44rpx;
  131. height: 44rpx;
  132. display: flex;
  133. align-items: center;
  134. justify-content: center;
  135. font-size: 32rpx;
  136. color: #333;
  137. &::before { content: '‹'; }
  138. }
  139. .header-title {
  140. font-size: 36rpx;
  141. font-weight: bold;
  142. color: #333;
  143. }
  144. .header-right {
  145. width: 44rpx;
  146. }
  147. }
  148. .balance-card {
  149. background: linear-gradient(135deg, #9C27B0 0%, #E91E63 100%);
  150. margin: 30rpx;
  151. padding: 40rpx;
  152. border-radius: 20rpx;
  153. text-align: center;
  154. .balance-label {
  155. font-size: 28rpx;
  156. color: rgba(255, 255, 255, 0.8);
  157. margin-bottom: 15rpx;
  158. }
  159. .balance-value {
  160. font-size: 72rpx;
  161. font-weight: bold;
  162. color: #FFFFFF;
  163. }
  164. }
  165. .records-section {
  166. background: #FFFFFF;
  167. margin: 0 30rpx;
  168. border-radius: 20rpx;
  169. padding: 30rpx;
  170. .section-title {
  171. font-size: 30rpx;
  172. font-weight: bold;
  173. color: #333;
  174. margin-bottom: 30rpx;
  175. }
  176. .empty-tip {
  177. text-align: center;
  178. color: #999;
  179. font-size: 28rpx;
  180. padding: 60rpx 0;
  181. }
  182. }
  183. .record-list {
  184. .record-item {
  185. display: flex;
  186. align-items: center;
  187. justify-content: space-between;
  188. padding: 25rpx 0;
  189. border-bottom: 1rpx solid #F0F0F0;
  190. &:last-child {
  191. border-bottom: none;
  192. }
  193. .record-left {
  194. display: flex;
  195. align-items: center;
  196. .record-icon {
  197. width: 60rpx;
  198. height: 60rpx;
  199. border-radius: 50%;
  200. display: flex;
  201. align-items: center;
  202. justify-content: center;
  203. font-size: 32rpx;
  204. font-weight: bold;
  205. margin-right: 20rpx;
  206. &.income {
  207. background: #E8F5E9;
  208. color: #4CAF50;
  209. }
  210. &.expense {
  211. background: #FFEBEE;
  212. color: #F44336;
  213. }
  214. }
  215. .record-info {
  216. .record-reason {
  217. font-size: 28rpx;
  218. color: #333;
  219. margin-bottom: 8rpx;
  220. }
  221. .record-time {
  222. font-size: 24rpx;
  223. color: #999;
  224. }
  225. }
  226. }
  227. .record-points {
  228. font-size: 32rpx;
  229. font-weight: bold;
  230. &.income {
  231. color: #4CAF50;
  232. }
  233. &.expense {
  234. color: #F44336;
  235. }
  236. }
  237. }
  238. }
  239. .load-more {
  240. text-align: center;
  241. padding: 30rpx;
  242. color: #9C27B0;
  243. font-size: 26rpx;
  244. }
  245. </style>