2025-11-23 15:21:50 +08:00

346 lines
6.4 KiB
Vue

<template>
<view class="points-log-page">
<!-- 积分流水列表 -->
<scroll-view
scroll-y
class="log-scroll"
@scrolltolower="loadMore"
:refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="refresh"
>
<view class="log-list">
<view
class="log-item"
v-for="log in logList"
:key="log.id"
>
<view class="log-left">
<view class="log-icon" :class="getSourceTypeClass(log.source_type)">
{{ getSourceTypeIcon(log.source_type) }}
</view>
<view class="log-info">
<text class="log-type">{{ getSourceTypeText(log.source_type) }}</text>
<text class="log-time">{{ formatDate(log.created_at) }}</text>
</view>
</view>
<view class="log-right">
<text class="log-points" :class="getPointsClass(log.points)">
{{ formatPoints(log.points) }}
</text>
</view>
</view>
<!-- 空状态 -->
<view v-if="!loading && logList.length === 0" class="empty-state">
<text class="empty-icon">📋</text>
<text class="empty-text">暂无积分流水记录</text>
</view>
<!-- 加载更多 -->
<view v-if="loading && logList.length > 0" class="loading-more">
<text class="loading-text">加载中...</text>
</view>
<!-- 没有更多 -->
<view v-if="!hasMore && logList.length > 0" class="no-more">
<text class="no-more-text">没有更多记录了</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
import request from '@/api/request.js';
export default {
data() {
return {
logList: [],
loading: false,
refreshing: false,
page: 1,
pageSize: 10,
hasMore: true
};
},
onLoad() {
this.loadLogList();
},
methods: {
// 格式化日期时间
formatDate(dateStr) {
if (!dateStr) return '';
const date = new Date(dateStr);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hour = String(date.getHours()).padStart(2, '0');
const minute = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hour}:${minute}`;
},
// 格式化积分显示
formatPoints(points) {
if (!points && points !== 0) return '0';
const num = parseFloat(points);
if (num > 0) {
return `+${num}`;
}
return String(num);
},
// 获取积分样式类(正数绿色,负数红色)
getPointsClass(points) {
const num = parseFloat(points);
return num > 0 ? 'positive' : 'negative';
},
// 获取积分来源类型文本
getSourceTypeText(sourceType) {
const typeMap = {
1: '签到',
2: '邀请',
3: '消费',
4: '兑换扣除',
5: '系统发放',
6: '消费',
7: '回退'
};
return typeMap[sourceType] || '未知';
},
// 获取积分来源类型图标
getSourceTypeIcon(sourceType) {
const iconMap = {
1: '📅',
2: '👥',
3: '🛍️',
4: '💸',
5: '🎁',
6: '🛒',
7: '↩️'
};
return iconMap[sourceType] || '📝';
},
// 获取积分来源类型样式类
getSourceTypeClass(sourceType) {
const classMap = {
1: 'type-sign',
2: 'type-invite',
3: 'type-consume',
4: 'type-exchange',
5: 'type-system',
6: 'type-consume',
7: 'type-refund'
};
return classMap[sourceType] || '';
},
// 加载积分流水列表
async loadLogList() {
if (this.loading || !this.hasMore) return;
this.loading = true;
try {
const params = {
page: this.page,
page_size: this.pageSize
};
const response = await request('xcx/user_points_logs', 'GET', params);
// 处理返回数据
let logs = [];
if (Array.isArray(response)) {
logs = response;
} else if (response.list && Array.isArray(response.list)) {
logs = response.list;
} else if (response.data && Array.isArray(response.data)) {
logs = response.data;
}
if (logs.length < this.pageSize) {
this.hasMore = false;
}
if (this.page === 1) {
this.logList = logs;
} else {
this.logList = [...this.logList, ...logs];
}
this.page++;
} catch (error) {
console.error('加载积分流水失败:', error);
// 如果是第一页且没有数据,显示空状态
if (this.page === 1) {
this.logList = [];
}
} finally {
this.loading = false;
this.refreshing = false;
}
},
// 刷新列表
refresh() {
this.refreshing = true;
this.page = 1;
this.hasMore = true;
this.logList = [];
this.loadLogList();
},
// 加载更多
loadMore() {
if (!this.loading && this.hasMore) {
this.loadLogList();
}
}
}
};
</script>
<style scoped lang="scss">
.points-log-page {
min-height: 100vh;
background-color: #f5f5f5;
display: flex;
flex-direction: column;
}
.log-scroll {
flex: 1;
height: calc(100vh);
}
.log-list {
padding: 24rpx;
display: flex;
flex-direction: column;
gap: 16rpx;
}
.log-item {
background-color: #fff;
border-radius: 24rpx;
padding: 32rpx;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}
.log-left {
display: flex;
align-items: center;
gap: 24rpx;
flex: 1;
}
.log-icon {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 40rpx;
flex-shrink: 0;
}
.log-icon.type-sign {
background-color: #fff3e0;
}
.log-icon.type-invite {
background-color: #e3f2fd;
}
.log-icon.type-consume {
background-color: #fce4ec;
}
.log-icon.type-exchange {
background-color: #ffebee;
}
.log-icon.type-system {
background-color: #e8f5e9;
}
.log-icon.type-refund {
background-color: #f3e5f5;
}
.log-info {
display: flex;
flex-direction: column;
gap: 8rpx;
flex: 1;
}
.log-type {
font-size: 30rpx;
color: #333;
font-weight: 500;
}
.log-time {
font-size: 24rpx;
color: #999;
}
.log-right {
display: flex;
align-items: center;
}
.log-points {
font-size: 32rpx;
font-weight: bold;
}
.log-points.positive {
color: #26b95a;
}
.log-points.negative {
color: #e7000b;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 0;
}
.empty-icon {
font-size: 120rpx;
margin-bottom: 24rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
.loading-more,
.no-more {
text-align: center;
padding: 40rpx 0;
}
.loading-text,
.no-more-text {
font-size: 24rpx;
color: #999;
}
</style>