410 lines
9.1 KiB
Vue
410 lines
9.1 KiB
Vue
<template>
|
|
<view class="points-page">
|
|
<!-- 积分余额卡片 -->
|
|
<view class="points-balance-card">
|
|
<view class="balance-info">
|
|
<text class="label">我的积分</text>
|
|
<text class="value">{{ pointsBalance }}</text>
|
|
</view>
|
|
|
|
<button class="btn-exchange" @click="goExchange">积分兑换</button>
|
|
</view>
|
|
|
|
<!-- 积分规则 -->
|
|
<view class="rules-card">
|
|
<view class="card-title">积分获取规则</view>
|
|
<view class="rule-list">
|
|
<view class="rule-item">
|
|
<text class="rule-icon">🎁</text>
|
|
<view class="rule-content">
|
|
<text class="rule-name">新用户注册</text>
|
|
<text class="rule-desc">赠送100积分</text>
|
|
</view>
|
|
<text class="rule-points">+100</text>
|
|
</view>
|
|
<view class="rule-item">
|
|
<text class="rule-icon">💰</text>
|
|
<view class="rule-content">
|
|
<text class="rule-name">订单消费</text>
|
|
<text class="rule-desc">每消费1元获得1积分</text>
|
|
</view>
|
|
<text class="rule-points">+1/元</text>
|
|
</view>
|
|
<view class="rule-item">
|
|
<text class="rule-icon">👥</text>
|
|
<view class="rule-content">
|
|
<text class="rule-name">邀请好友</text>
|
|
<text class="rule-desc">成功邀请赠送200积分</text>
|
|
</view>
|
|
<text class="rule-points">+200</text>
|
|
</view>
|
|
<view class="rule-item">
|
|
<text class="rule-icon">✅</text>
|
|
<view class="rule-content">
|
|
<text class="rule-name">完成订单</text>
|
|
<text class="rule-desc">每完成一单赠送50积分</text>
|
|
</view>
|
|
<text class="rule-points">+50</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 积分记录 -->
|
|
<view class="records-section">
|
|
<view class="section-header">
|
|
<text class="section-title">积分明细</text>
|
|
<picker :range="typeOptions" range-key="label" @change="onTypeChange">
|
|
<view class="filter-btn">
|
|
{{ currentType.label }}
|
|
<text class="arrow">▼</text>
|
|
</view>
|
|
</picker>
|
|
</view>
|
|
|
|
<view v-if="records.length === 0" class="empty-state">
|
|
<text class="empty-icon">📭</text>
|
|
<text class="empty-text">暂无积分记录</text>
|
|
</view>
|
|
|
|
<view v-else class="record-list">
|
|
<view v-for="item in records" :key="item.id" class="record-item">
|
|
<view class="item-left">
|
|
<text class="item-desc">{{ item.description }}</text>
|
|
<text class="item-time">{{ formatTime(item.createTime) }}</text>
|
|
<text class="item-expire" v-if="item.expireDate">
|
|
有效期至: {{ item.expireDate }}
|
|
</text>
|
|
</view>
|
|
<view class="item-right">
|
|
<text class="item-points" :class="item.points > 0 ? 'earn' : 'use'">
|
|
{{ item.points > 0 ? '+' : '' }}{{ item.points }}
|
|
</text>
|
|
<text class="item-balance">余额: {{ item.balance }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import { pointsApi } from '@/api/index.js'
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
pointsBalance: 0,
|
|
records: [],
|
|
typeOptions: [
|
|
{ label: '全部', value: '' },
|
|
{ label: '获得', value: 'earn' },
|
|
{ label: '使用', value: 'use' },
|
|
{ label: '过期', value: 'expire' }
|
|
],
|
|
currentType: { label: '全部', value: '' },
|
|
page: 1,
|
|
size: 20,
|
|
loading: false,
|
|
finished: false
|
|
}
|
|
},
|
|
|
|
onLoad() {
|
|
this.loadPointsBalance()
|
|
this.loadRecords()
|
|
},
|
|
|
|
onReachBottom() {
|
|
if (!this.loading && !this.finished) {
|
|
this.page++
|
|
this.loadRecords()
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
async loadPointsBalance() {
|
|
try {
|
|
uni.showLoading({ title: '加载中...' })
|
|
const res = await pointsApi.getBalance()
|
|
// request.js 响应拦截器直接返回 res.data
|
|
if (res) {
|
|
this.pointsBalance = res.balance || 0
|
|
}
|
|
} catch (e) {
|
|
console.error('加载积分余额失败', e)
|
|
this.pointsBalance = 0
|
|
} finally {
|
|
uni.hideLoading()
|
|
}
|
|
},
|
|
|
|
async loadRecords() {
|
|
if (this.loading) return
|
|
this.loading = true
|
|
|
|
try {
|
|
const params = {
|
|
page: this.page,
|
|
size: this.size
|
|
}
|
|
if (this.currentType.value) {
|
|
params.type = this.currentType.value
|
|
}
|
|
|
|
const res = await pointsApi.getRecords(params)
|
|
|
|
// request.js 响应拦截器直接返回 res.data
|
|
if (res) {
|
|
const records = res.records || []
|
|
|
|
if (this.page === 1) {
|
|
this.records = records
|
|
} else {
|
|
this.records = [...this.records, ...records]
|
|
}
|
|
|
|
if (records.length < this.size) {
|
|
this.finished = true
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error('加载积分记录失败', e)
|
|
this.records = []
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
},
|
|
|
|
onTypeChange(e) {
|
|
this.currentType = this.typeOptions[e.detail.value]
|
|
this.page = 1
|
|
this.finished = false
|
|
this.records = []
|
|
this.loadRecords()
|
|
},
|
|
|
|
formatTime(datetime) {
|
|
if (!datetime) return ''
|
|
return datetime.replace('T', ' ').substring(0, 16)
|
|
},
|
|
|
|
goExchange() {
|
|
uni.showToast({
|
|
title: '积分兑换功能开发中',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import '@/static/css/common.scss';
|
|
|
|
.points-page {
|
|
min-height: 100vh;
|
|
background: $bg-color;
|
|
padding-bottom: 40rpx;
|
|
}
|
|
|
|
.points-balance-card {
|
|
background: linear-gradient(135deg, #ff9500 0%, #ff6b00 100%);
|
|
padding: 60rpx 40rpx;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
|
|
.balance-info {
|
|
.label {
|
|
display: block;
|
|
font-size: 28rpx;
|
|
color: rgba(255, 255, 255, 0.9);
|
|
margin-bottom: 16rpx;
|
|
}
|
|
|
|
.value {
|
|
display: block;
|
|
font-size: 72rpx;
|
|
font-weight: bold;
|
|
color: #fff;
|
|
}
|
|
}
|
|
|
|
.btn-exchange {
|
|
width: 180rpx;
|
|
height: 72rpx;
|
|
line-height: 72rpx;
|
|
background: #fff;
|
|
color: #ff9500;
|
|
border-radius: 36rpx;
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
border: none;
|
|
}
|
|
}
|
|
|
|
.rules-card {
|
|
background: #fff;
|
|
margin: 20rpx 30rpx;
|
|
padding: 30rpx;
|
|
border-radius: 16rpx;
|
|
|
|
.card-title {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
margin-bottom: 30rpx;
|
|
}
|
|
|
|
.rule-list {
|
|
.rule-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 24rpx 0;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.rule-icon {
|
|
font-size: 48rpx;
|
|
margin-right: 20rpx;
|
|
}
|
|
|
|
.rule-content {
|
|
flex: 1;
|
|
|
|
.rule-name {
|
|
display: block;
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.rule-desc {
|
|
display: block;
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
}
|
|
}
|
|
|
|
.rule-points {
|
|
font-size: 28rpx;
|
|
color: #ff9500;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.records-section {
|
|
background: #fff;
|
|
margin: 20rpx 30rpx;
|
|
border-radius: 16rpx;
|
|
padding: 30rpx;
|
|
|
|
.section-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 30rpx;
|
|
|
|
.section-title {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.filter-btn {
|
|
font-size: 28rpx;
|
|
color: #666;
|
|
|
|
.arrow {
|
|
margin-left: 8rpx;
|
|
font-size: 20rpx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.empty-state {
|
|
text-align: center;
|
|
padding: 80rpx 0;
|
|
|
|
.empty-image {
|
|
width: 300rpx;
|
|
height: 300rpx;
|
|
margin-bottom: 40rpx;
|
|
}
|
|
|
|
.empty-text {
|
|
display: block;
|
|
font-size: 28rpx;
|
|
color: #999;
|
|
}
|
|
}
|
|
|
|
.record-list {
|
|
.record-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 24rpx 0;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.item-left {
|
|
flex: 1;
|
|
|
|
.item-desc {
|
|
display: block;
|
|
font-size: 30rpx;
|
|
color: #333;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.item-time {
|
|
display: block;
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
margin-bottom: 4rpx;
|
|
}
|
|
|
|
.item-expire {
|
|
display: block;
|
|
font-size: 24rpx;
|
|
color: #ff9500;
|
|
}
|
|
}
|
|
|
|
.item-right {
|
|
text-align: right;
|
|
|
|
.item-points {
|
|
display: block;
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
margin-bottom: 8rpx;
|
|
|
|
&.earn {
|
|
color: #ff9500;
|
|
}
|
|
|
|
&.use {
|
|
color: #52c41a;
|
|
}
|
|
}
|
|
|
|
.item-balance {
|
|
display: block;
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|