hookehuyr

feat(积分): 实现积分列表接口对接及页面展示

- 新增积分相关API接口文件及获取积分列表方法
- 修改积分页面,移除模拟数据,对接真实接口
- 添加积分余额显示及列表数据动态加载功能
- 实现分页加载、下拉刷新和不同积分类型筛选
/*
* @Date: 2025-12-24 12:26:27
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-12-24 12:37:23
* @FilePath: /mlaj/src/api/points.js
* @Description: 积分相关接口
*/
import { fn, fetch } from './fn';
const Api = {
POINTS_LIST: '/srv/?a=points&t=list',
}
/**
* @description 获取积分列表
* @param {*} direction add=已获得,subtract=已消耗
* @param {*} begin_date 开始日期
* @param {*} end_date 结束日期
* @param {*} keyword 搜索关键词
* @param {*} page 页码
* @param {*} limit 每页数量
* @returns data: {
* balance: 积分余额
* point_list: [{ id, event_title 标题, event_time 时间, change 积分变化, event_type_desc 事件类型 }]
* }
*/
export const getPointsListAPI = (params) => fn(fetch.get(Api.POINTS_LIST, params));
......@@ -4,7 +4,7 @@
<div class="header relative w-full h-48 bg-cover bg-center" :style="{ backgroundImage: `url(${headerBg})` }">
<div class="absolute top-12 left-6">
<div class="text-white text-sm opacity-90 mb-1">当前星球币</div>
<div class="text-[#FFDD01] text-4xl font-bold tracking-wider">15,800</div>
<div class="text-[#FFDD01] text-4xl font-bold tracking-wider">{{ balance }}</div>
</div>
<!-- 右侧瓶子 -->
......@@ -72,15 +72,16 @@
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
<div v-for="item in list" :key="item.id" class="bg-white rounded-xl p-4 mb-3 shadow-sm">
<div class="text-gray-900 text-sm font-medium leading-relaxed mb-3 line-clamp-2">
{{ item.title }}
{{ item.event_title }}
</div>
<div class="flex justify-between items-center">
<div class="flex flex-col">
<span class="text-gray-400 text-xs mb-1">{{ item.date }}</span>
<span class="text-gray-400 text-xs">{{ item.type }}</span>
<span class="text-gray-400 text-xs mb-1">{{ item.event_time }}</span>
<span class="text-gray-400 text-xs">{{ item.event_type_desc }}</span>
</div>
<div class="text-lg font-bold" :class="item.isIncome ? 'text-[#2E85FF]' : 'text-[#FF4D4F]'">
{{ item.isIncome ? '+' : '' }}{{ item.amount }}
<div class="text-lg font-bold"
:class="String(item.change).includes('-') ? 'text-[#FF4D4F]' : 'text-[#2E85FF]'">
{{ item.change }}
</div>
</div>
<!-- 分割线 -->
......@@ -104,11 +105,12 @@
</template>
<script setup>
import { ref, computed } from 'vue'
import { ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useTitle } from '@vueuse/core'
import { showToast } from 'vant'
import dayjs from 'dayjs'
import { getPointsListAPI } from '@/api/points'
// 导入图片
const headerBg = 'https://cdn.ipadbiz.cn/mlaj/recall/img/962@2x.png'
......@@ -139,6 +141,9 @@ const list = ref([])
const loading = ref(false)
const finished = ref(false)
const refreshing = ref(false)
const page = ref(0)
const limit = ref(10)
const balance = ref('0')
// 切换Tab
const handleTabChange = (index) => {
......@@ -169,74 +174,61 @@ const onConfirmEndDate = ({ selectedValues }) => {
onRefresh()
}
// 模拟数据
const mockData = [
{
id: 1,
title: '2025.11月3日-10日江苏东台养生营,邀您一起进入童话世界!',
date: '2025-11-03',
type: '活动参与奖励',
amount: 6400,
isIncome: true
},
{
id: 2,
title: '【自然的恩典】青少年成长营-贵阳百花湖3(小学初中专场)',
date: '2025-10-03',
type: '活动参与奖励',
amount: 7998,
isIncome: true
},
{
id: 3,
title: '2024年4月22-25日浙江义乌【中华智慧商业应用论坛】',
date: '2024-04-20',
type: '活动参与奖励',
amount: 3200,
isIncome: true
},
{
id: 4,
title: '2023.7.6-7.11【自然的恩典】“爱我中华”优秀传统文化夏令营-天津场',
date: '2023-07-01',
type: '活动参与奖励',
amount: 3990,
isIncome: true
},
{
id: 5,
title: '兑换活动优惠券',
date: '2023-07-01',
type: '兑换奖励',
amount: 200,
isIncome: false
}
]
// 加载列表
const onLoad = () => {
setTimeout(() => {
const onLoad = async () => {
const nextPage = page.value + 1
let direction = ''
if (activeTab.value === 1) direction = 'add'
if (activeTab.value === 2) direction = 'subtract'
try {
const res = await getPointsListAPI({
direction,
begin_date: startDate.value,
end_date: endDate.value,
keyword: searchKeyword.value,
page: nextPage,
limit: limit.value
})
if (refreshing.value) {
list.value = []
refreshing.value = false
}
// 模拟数据加载
const newData = mockData.map(item => ({ ...item, id: item.id + list.value.length }))
list.value.push(...newData)
if (res && res.data) {
if (res.data.balance !== undefined) {
balance.value = res.data.balance
}
loading.value = false
const newItems = res.data.point_list || []
list.value.push(...newItems)
// 模拟数据加载完毕
if (list.value.length >= 20) {
page.value = nextPage
if (newItems.length < limit.value) {
finished.value = true
}
} else {
finished.value = true
}
}, 1000)
} catch (error) {
console.error('Failed to load points:', error)
finished.value = true
if (refreshing.value) {
refreshing.value = false
}
} finally {
loading.value = false
}
}
const onRefresh = () => {
finished.value = false
loading.value = true
page.value = 0
refreshing.value = true
onLoad()
}
</script>
......