Initial project commit
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { onPullDownRefresh, onReachBottom, onShow } from "@dcloudio/uni-app";
|
||||
import { getMyPoints, getMyPointRecords } from "@/api/point";
|
||||
import { checkLogin } from "@/utils/auth";
|
||||
|
||||
onShow(() => { checkLogin(); });
|
||||
|
||||
const balance = ref(0);
|
||||
const records = ref([]);
|
||||
const page = ref(1);
|
||||
const pageSize = 15;
|
||||
const hasMore = ref(true);
|
||||
const loading = ref(false);
|
||||
|
||||
const fetchBalance = async () => {
|
||||
try {
|
||||
const res = await getMyPoints();
|
||||
balance.value = res.balance ?? res.points ?? 0;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchRecords = async (reset = false) => {
|
||||
if (loading.value) return;
|
||||
if (!reset && !hasMore.value) return;
|
||||
|
||||
loading.value = true;
|
||||
if (reset) {
|
||||
page.value = 1;
|
||||
hasMore.value = true;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await getMyPointRecords({ page: page.value, page_size: pageSize });
|
||||
const list = res.items || res.data || res || [];
|
||||
|
||||
if (reset) {
|
||||
records.value = list;
|
||||
} else {
|
||||
records.value.push(...list);
|
||||
}
|
||||
|
||||
if (list.length < pageSize) {
|
||||
hasMore.value = false;
|
||||
} else {
|
||||
page.value++;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const formatDate = (dateStr) => {
|
||||
if (!dateStr) return "";
|
||||
const d = new Date(dateStr);
|
||||
const y = d.getFullYear();
|
||||
const m = String(d.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(d.getDate()).padStart(2, "0");
|
||||
const h = String(d.getHours()).padStart(2, "0");
|
||||
const min = String(d.getMinutes()).padStart(2, "0");
|
||||
return `${y}-${m}-${day} ${h}:${min}`;
|
||||
};
|
||||
|
||||
const formatChange = (val) => {
|
||||
return val > 0 ? `+${val}` : `${val}`;
|
||||
};
|
||||
|
||||
onPullDownRefresh(async () => {
|
||||
await Promise.all([fetchBalance(), fetchRecords(true)]);
|
||||
uni.stopPullDownRefresh();
|
||||
});
|
||||
|
||||
onReachBottom(() => {
|
||||
fetchRecords();
|
||||
});
|
||||
|
||||
fetchBalance();
|
||||
fetchRecords(true);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="points-page">
|
||||
<view class="balance-card">
|
||||
<text class="balance-label">当前积分</text>
|
||||
<text class="balance-value">{{ balance }}</text>
|
||||
</view>
|
||||
|
||||
<view class="section-header">
|
||||
<text class="section-title">积分记录</text>
|
||||
</view>
|
||||
|
||||
<view class="records-list">
|
||||
<view
|
||||
v-for="item in records"
|
||||
:key="item.id"
|
||||
class="record-item"
|
||||
>
|
||||
<view class="record-left">
|
||||
<text class="record-reason">{{ item.reason || "积分变动" }}</text>
|
||||
<text class="record-date">{{ formatDate(item.created_at) }}</text>
|
||||
</view>
|
||||
<text
|
||||
class="record-change"
|
||||
:class="item.change > 0 ? 'positive' : 'negative'"
|
||||
>
|
||||
{{ formatChange(item.change) }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<view v-if="loading" class="status-tip">
|
||||
<text>加载中...</text>
|
||||
</view>
|
||||
<view v-else-if="!hasMore && records.length > 0" class="status-tip">
|
||||
<text>没有更多了</text>
|
||||
</view>
|
||||
<view v-else-if="!loading && records.length === 0" class="empty-state">
|
||||
<uni-icons type="star-filled" size="48" color="#6366f1" class="empty-icon" />
|
||||
<text class="empty-title">暂无积分记录</text>
|
||||
<text class="empty-desc">投稿取景地、获得收藏可以赚取积分</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.points-page {
|
||||
min-height: 100vh;
|
||||
background: #f5f6fa;
|
||||
}
|
||||
|
||||
.balance-card {
|
||||
margin: 24rpx 32rpx;
|
||||
background: linear-gradient(135deg, #6366f1, #818cf8);
|
||||
border-radius: 24rpx;
|
||||
padding: 48rpx 40rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
box-shadow: 0 8rpx 32rpx rgba(99, 102, 241, 0.25);
|
||||
}
|
||||
|
||||
.balance-label {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.balance-value {
|
||||
font-size: 80rpx;
|
||||
font-weight: 800;
|
||||
color: #ffffff;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
padding: 24rpx 32rpx 16rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.records-list {
|
||||
padding: 0 32rpx 32rpx;
|
||||
}
|
||||
|
||||
.record-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #ffffff;
|
||||
padding: 28rpx 28rpx;
|
||||
border-radius: 14rpx;
|
||||
margin-bottom: 16rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
|
||||
}
|
||||
|
||||
.record-left {
|
||||
flex: 1;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.record-reason {
|
||||
font-size: 28rpx;
|
||||
color: #1e293b;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.record-date {
|
||||
font-size: 24rpx;
|
||||
color: #94a3b8;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.record-change {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.record-change.positive {
|
||||
color: #22c55e;
|
||||
}
|
||||
|
||||
.record-change.negative {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.status-tip {
|
||||
text-align: center;
|
||||
padding: 40rpx 0;
|
||||
color: #94a3b8;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 120rpx 0;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 96rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.empty-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.empty-desc {
|
||||
font-size: 26rpx;
|
||||
color: #94a3b8;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user