dynamic.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. <template>
  2. <view class="sub-base-container">
  3. <view class="messgae-list" v-for="(item,index) in messageList" :key="index">
  4. <text class="date">{{item.date}}</text>
  5. <view class="message-item" v-for="(ele,idx) in item.list" :key="idx">
  6. <view class="messgae-info">
  7. <view class="info-text">
  8. <up-image class="avatar" :fade="false" :src="ele.icon" width="56rpx" height="56rpx"
  9. shape="circle"></up-image>
  10. <view><text class="my">{{ele.nickname}}</text> 使用 <text
  11. class="name">{{clockinTypeStr[ele.clockinType]}}</text> 能量增加 <text
  12. class="name">{{ele.energy}}分</text></view>
  13. <!-- <view><text class="my">励志人生</text> 向 <text class="name">茜茜荔</text> 留言 </view> -->
  14. </view>
  15. <!-- <text class="time">{{ele.createTime}}</text> -->
  16. </view>
  17. <view class="time">{{ele.createTime}}</view>
  18. <text class="message-text">{{ele.desc}}</text>
  19. </view>
  20. </view>
  21. <up-loadmore :status="loadStatus" />
  22. </view>
  23. </template>
  24. <script setup>
  25. import {
  26. ref,
  27. } from 'vue';
  28. import TREE from '@/api/tree.js'
  29. import {
  30. onLoad,
  31. onReachBottom,
  32. onPullDownRefresh
  33. } from '@dcloudio/uni-app'
  34. const messageList = ref([])
  35. const farmBuyIdData = ref('')
  36. const page = ref(1)
  37. const limit = 10
  38. const isLoading = ref(false)
  39. const hasMore = ref(true)
  40. const loadStatus = ref('loadmore') // loading | loadmore | nomore
  41. onLoad(({
  42. farmBuyId
  43. }) => {
  44. farmBuyIdData.value = farmBuyId
  45. fetchMessageList({ refresh: true })
  46. })
  47. const clockinTypeStr = {
  48. '1': '每日阳光',
  49. "2": "分享守护",
  50. "3": "每日祝福"
  51. }
  52. const fetchMessageList = ({ refresh = false } = {}) => {
  53. if (isLoading.value) return
  54. if (refresh) {
  55. page.value = 1
  56. hasMore.value = true
  57. }
  58. if (!hasMore.value) return
  59. isLoading.value = true
  60. loadStatus.value = 'loading'
  61. const params = {
  62. page: page.value,
  63. limit,
  64. farmBuyId: farmBuyIdData.value
  65. }
  66. TREE.getEnergyRecords(params)
  67. .then(res => {
  68. const list = (res?.data || []).map((it) => {
  69. const label = formatDateLabel(it)
  70. return { ...it, dateClass: label }
  71. })
  72. const grouped = groupByDateLabel(list)
  73. if (refresh) {
  74. messageList.value = grouped
  75. } else {
  76. mergeGroups(messageList.value, grouped)
  77. }
  78. if (list.length < limit) {
  79. hasMore.value = false
  80. loadStatus.value = 'nomore'
  81. } else {
  82. page.value += 1
  83. loadStatus.value = 'loadmore'
  84. }
  85. })
  86. .finally(() => {
  87. isLoading.value = false
  88. if (refresh) {
  89. uni.stopPullDownRefresh()
  90. }
  91. })
  92. }
  93. const groupByDateLabel = (records) => {
  94. const map = new Map()
  95. records.forEach((rec) => {
  96. const key = rec.dateClass
  97. if (!map.has(key)) map.set(key, [])
  98. map.get(key).push(rec)
  99. })
  100. return Array.from(map.entries()).map(([date, list]) => ({ date, list }))
  101. }
  102. const mergeGroups = (targetGroups, incomingGroups) => {
  103. incomingGroups.forEach((grp) => {
  104. const exist = targetGroups.find((g) => g.date === grp.date)
  105. if (exist) {
  106. exist.list = exist.list.concat(grp.list)
  107. } else {
  108. targetGroups.push({ date: grp.date, list: grp.list })
  109. }
  110. })
  111. }
  112. const formatDateLabel = (item) => {
  113. const src = item?.createTime || item?.dateClass || ''
  114. const d = parseToDate(src)
  115. if (!d) return (typeof item?.dateClass === 'string' && item.dateClass) ? item.dateClass : ''
  116. const today = new Date()
  117. const startOfToday = new Date(today.getFullYear(), today.getMonth(), today.getDate())
  118. const startOfThatDay = new Date(d.getFullYear(), d.getMonth(), d.getDate())
  119. const diffDays = Math.round((startOfToday - startOfThatDay) / 86400000)
  120. if (diffDays === 0) return '今天'
  121. if (diffDays === 1) return '昨天'
  122. return `${pad2(d.getMonth() + 1)}/${pad2(d.getDate())}`
  123. }
  124. const parseToDate = (str) => {
  125. if (!str || typeof str !== 'string') return null
  126. const normalized = str.replace(/-/g, '/').replace(/\.\d{3}.*/, '')
  127. const d = new Date(normalized)
  128. return isNaN(d.getTime()) ? null : d
  129. }
  130. const pad2 = (n) => (n < 10 ? `0${n}` : `${n}`)
  131. onReachBottom(() => {
  132. fetchMessageList()
  133. })
  134. onPullDownRefresh(() => {
  135. fetchMessageList({ refresh: true })
  136. })
  137. </script>
  138. <style lang="scss" scoped>
  139. .sub-base-container {
  140. .messgae-list {
  141. .date {
  142. font-size: 34rpx;
  143. }
  144. .message-item {
  145. margin-top: 24rpx;
  146. background: #fff;
  147. border-radius: 16rpx;
  148. padding: 24rpx 32rpx;
  149. .time {
  150. font-size: 28rpx;
  151. color: #999999;
  152. margin-left: 70rpx;
  153. }
  154. .messgae-info {
  155. display: flex;
  156. align-items: center;
  157. justify-content: space-between;
  158. margin-bottom: 16rpx;
  159. .info-text {
  160. display: flex;
  161. align-items: center;
  162. .my {
  163. font-weight: 500;
  164. }
  165. .name {
  166. color: #EAB400;
  167. }
  168. }
  169. .avatar {
  170. margin-right: 16rpx;
  171. }
  172. }
  173. .message-text {
  174. font-size: 28rpx;
  175. }
  176. }
  177. }
  178. .messgae-list+.messgae-list {
  179. margin-top: 24rpx;
  180. }
  181. }
  182. </style>