feat(积分): 实现积分列表接口对接及页面展示
- 新增积分相关API接口文件及获取积分列表方法 - 修改积分页面,移除模拟数据,对接真实接口 - 添加积分余额显示及列表数据动态加载功能 - 实现分页加载、下拉刷新和不同积分类型筛选
Showing
2 changed files
with
79 additions
and
60 deletions
src/api/points.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2025-12-24 12:26:27 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-12-24 12:37:23 | ||
| 5 | + * @FilePath: /mlaj/src/api/points.js | ||
| 6 | + * @Description: 积分相关接口 | ||
| 7 | + */ | ||
| 8 | +import { fn, fetch } from './fn'; | ||
| 9 | + | ||
| 10 | +const Api = { | ||
| 11 | + POINTS_LIST: '/srv/?a=points&t=list', | ||
| 12 | +} | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * @description 获取积分列表 | ||
| 16 | + * @param {*} direction add=已获得,subtract=已消耗 | ||
| 17 | + * @param {*} begin_date 开始日期 | ||
| 18 | + * @param {*} end_date 结束日期 | ||
| 19 | + * @param {*} keyword 搜索关键词 | ||
| 20 | + * @param {*} page 页码 | ||
| 21 | + * @param {*} limit 每页数量 | ||
| 22 | + * @returns data: { | ||
| 23 | + * balance: 积分余额 | ||
| 24 | + * point_list: [{ id, event_title 标题, event_time 时间, change 积分变化, event_type_desc 事件类型 }] | ||
| 25 | + * } | ||
| 26 | + */ | ||
| 27 | +export const getPointsListAPI = (params) => fn(fetch.get(Api.POINTS_LIST, params)); |
| ... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
| 4 | <div class="header relative w-full h-48 bg-cover bg-center" :style="{ backgroundImage: `url(${headerBg})` }"> | 4 | <div class="header relative w-full h-48 bg-cover bg-center" :style="{ backgroundImage: `url(${headerBg})` }"> |
| 5 | <div class="absolute top-12 left-6"> | 5 | <div class="absolute top-12 left-6"> |
| 6 | <div class="text-white text-sm opacity-90 mb-1">当前星球币</div> | 6 | <div class="text-white text-sm opacity-90 mb-1">当前星球币</div> |
| 7 | - <div class="text-[#FFDD01] text-4xl font-bold tracking-wider">15,800</div> | 7 | + <div class="text-[#FFDD01] text-4xl font-bold tracking-wider">{{ balance }}</div> |
| 8 | </div> | 8 | </div> |
| 9 | 9 | ||
| 10 | <!-- 右侧瓶子 --> | 10 | <!-- 右侧瓶子 --> |
| ... | @@ -72,15 +72,16 @@ | ... | @@ -72,15 +72,16 @@ |
| 72 | <van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"> | 72 | <van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"> |
| 73 | <div v-for="item in list" :key="item.id" class="bg-white rounded-xl p-4 mb-3 shadow-sm"> | 73 | <div v-for="item in list" :key="item.id" class="bg-white rounded-xl p-4 mb-3 shadow-sm"> |
| 74 | <div class="text-gray-900 text-sm font-medium leading-relaxed mb-3 line-clamp-2"> | 74 | <div class="text-gray-900 text-sm font-medium leading-relaxed mb-3 line-clamp-2"> |
| 75 | - {{ item.title }} | 75 | + {{ item.event_title }} |
| 76 | </div> | 76 | </div> |
| 77 | <div class="flex justify-between items-center"> | 77 | <div class="flex justify-between items-center"> |
| 78 | <div class="flex flex-col"> | 78 | <div class="flex flex-col"> |
| 79 | - <span class="text-gray-400 text-xs mb-1">{{ item.date }}</span> | 79 | + <span class="text-gray-400 text-xs mb-1">{{ item.event_time }}</span> |
| 80 | - <span class="text-gray-400 text-xs">{{ item.type }}</span> | 80 | + <span class="text-gray-400 text-xs">{{ item.event_type_desc }}</span> |
| 81 | </div> | 81 | </div> |
| 82 | - <div class="text-lg font-bold" :class="item.isIncome ? 'text-[#2E85FF]' : 'text-[#FF4D4F]'"> | 82 | + <div class="text-lg font-bold" |
| 83 | - {{ item.isIncome ? '+' : '' }}{{ item.amount }} | 83 | + :class="String(item.change).includes('-') ? 'text-[#FF4D4F]' : 'text-[#2E85FF]'"> |
| 84 | + {{ item.change }} | ||
| 84 | </div> | 85 | </div> |
| 85 | </div> | 86 | </div> |
| 86 | <!-- 分割线 --> | 87 | <!-- 分割线 --> |
| ... | @@ -104,11 +105,12 @@ | ... | @@ -104,11 +105,12 @@ |
| 104 | </template> | 105 | </template> |
| 105 | 106 | ||
| 106 | <script setup> | 107 | <script setup> |
| 107 | -import { ref, computed } from 'vue' | 108 | +import { ref } from 'vue' |
| 108 | import { useRoute, useRouter } from 'vue-router' | 109 | import { useRoute, useRouter } from 'vue-router' |
| 109 | import { useTitle } from '@vueuse/core' | 110 | import { useTitle } from '@vueuse/core' |
| 110 | import { showToast } from 'vant' | 111 | import { showToast } from 'vant' |
| 111 | import dayjs from 'dayjs' | 112 | import dayjs from 'dayjs' |
| 113 | +import { getPointsListAPI } from '@/api/points' | ||
| 112 | 114 | ||
| 113 | // 导入图片 | 115 | // 导入图片 |
| 114 | const headerBg = 'https://cdn.ipadbiz.cn/mlaj/recall/img/962@2x.png' | 116 | const headerBg = 'https://cdn.ipadbiz.cn/mlaj/recall/img/962@2x.png' |
| ... | @@ -139,6 +141,9 @@ const list = ref([]) | ... | @@ -139,6 +141,9 @@ const list = ref([]) |
| 139 | const loading = ref(false) | 141 | const loading = ref(false) |
| 140 | const finished = ref(false) | 142 | const finished = ref(false) |
| 141 | const refreshing = ref(false) | 143 | const refreshing = ref(false) |
| 144 | +const page = ref(0) | ||
| 145 | +const limit = ref(10) | ||
| 146 | +const balance = ref('0') | ||
| 142 | 147 | ||
| 143 | // 切换Tab | 148 | // 切换Tab |
| 144 | const handleTabChange = (index) => { | 149 | const handleTabChange = (index) => { |
| ... | @@ -169,74 +174,61 @@ const onConfirmEndDate = ({ selectedValues }) => { | ... | @@ -169,74 +174,61 @@ const onConfirmEndDate = ({ selectedValues }) => { |
| 169 | onRefresh() | 174 | onRefresh() |
| 170 | } | 175 | } |
| 171 | 176 | ||
| 172 | -// 模拟数据 | ||
| 173 | -const mockData = [ | ||
| 174 | - { | ||
| 175 | - id: 1, | ||
| 176 | - title: '2025.11月3日-10日江苏东台养生营,邀您一起进入童话世界!', | ||
| 177 | - date: '2025-11-03', | ||
| 178 | - type: '活动参与奖励', | ||
| 179 | - amount: 6400, | ||
| 180 | - isIncome: true | ||
| 181 | - }, | ||
| 182 | - { | ||
| 183 | - id: 2, | ||
| 184 | - title: '【自然的恩典】青少年成长营-贵阳百花湖3(小学初中专场)', | ||
| 185 | - date: '2025-10-03', | ||
| 186 | - type: '活动参与奖励', | ||
| 187 | - amount: 7998, | ||
| 188 | - isIncome: true | ||
| 189 | - }, | ||
| 190 | - { | ||
| 191 | - id: 3, | ||
| 192 | - title: '2024年4月22-25日浙江义乌【中华智慧商业应用论坛】', | ||
| 193 | - date: '2024-04-20', | ||
| 194 | - type: '活动参与奖励', | ||
| 195 | - amount: 3200, | ||
| 196 | - isIncome: true | ||
| 197 | - }, | ||
| 198 | - { | ||
| 199 | - id: 4, | ||
| 200 | - title: '2023.7.6-7.11【自然的恩典】“爱我中华”优秀传统文化夏令营-天津场', | ||
| 201 | - date: '2023-07-01', | ||
| 202 | - type: '活动参与奖励', | ||
| 203 | - amount: 3990, | ||
| 204 | - isIncome: true | ||
| 205 | - }, | ||
| 206 | - { | ||
| 207 | - id: 5, | ||
| 208 | - title: '兑换活动优惠券', | ||
| 209 | - date: '2023-07-01', | ||
| 210 | - type: '兑换奖励', | ||
| 211 | - amount: 200, | ||
| 212 | - isIncome: false | ||
| 213 | - } | ||
| 214 | -] | ||
| 215 | - | ||
| 216 | // 加载列表 | 177 | // 加载列表 |
| 217 | -const onLoad = () => { | 178 | +const onLoad = async () => { |
| 218 | - setTimeout(() => { | 179 | + const nextPage = page.value + 1 |
| 180 | + | ||
| 181 | + let direction = '' | ||
| 182 | + if (activeTab.value === 1) direction = 'add' | ||
| 183 | + if (activeTab.value === 2) direction = 'subtract' | ||
| 184 | + | ||
| 185 | + try { | ||
| 186 | + const res = await getPointsListAPI({ | ||
| 187 | + direction, | ||
| 188 | + begin_date: startDate.value, | ||
| 189 | + end_date: endDate.value, | ||
| 190 | + keyword: searchKeyword.value, | ||
| 191 | + page: nextPage, | ||
| 192 | + limit: limit.value | ||
| 193 | + }) | ||
| 194 | + | ||
| 219 | if (refreshing.value) { | 195 | if (refreshing.value) { |
| 220 | list.value = [] | 196 | list.value = [] |
| 221 | refreshing.value = false | 197 | refreshing.value = false |
| 222 | } | 198 | } |
| 223 | 199 | ||
| 224 | - // 模拟数据加载 | 200 | + if (res && res.data) { |
| 225 | - const newData = mockData.map(item => ({ ...item, id: item.id + list.value.length })) | 201 | + if (res.data.balance !== undefined) { |
| 226 | - list.value.push(...newData) | 202 | + balance.value = res.data.balance |
| 203 | + } | ||
| 227 | 204 | ||
| 228 | - loading.value = false | 205 | + const newItems = res.data.point_list || [] |
| 206 | + list.value.push(...newItems) | ||
| 229 | 207 | ||
| 230 | - // 模拟数据加载完毕 | 208 | + page.value = nextPage |
| 231 | - if (list.value.length >= 20) { | 209 | + |
| 210 | + if (newItems.length < limit.value) { | ||
| 211 | + finished.value = true | ||
| 212 | + } | ||
| 213 | + } else { | ||
| 232 | finished.value = true | 214 | finished.value = true |
| 233 | } | 215 | } |
| 234 | - }, 1000) | 216 | + } catch (error) { |
| 217 | + console.error('Failed to load points:', error) | ||
| 218 | + finished.value = true | ||
| 219 | + if (refreshing.value) { | ||
| 220 | + refreshing.value = false | ||
| 221 | + } | ||
| 222 | + } finally { | ||
| 223 | + loading.value = false | ||
| 224 | + } | ||
| 235 | } | 225 | } |
| 236 | 226 | ||
| 237 | const onRefresh = () => { | 227 | const onRefresh = () => { |
| 238 | finished.value = false | 228 | finished.value = false |
| 239 | loading.value = true | 229 | loading.value = true |
| 230 | + page.value = 0 | ||
| 231 | + refreshing.value = true | ||
| 240 | onLoad() | 232 | onLoad() |
| 241 | } | 233 | } |
| 242 | </script> | 234 | </script> | ... | ... |
-
Please register or login to post a comment