You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

242 lines
5.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="lf-page records-page">
<view class="lf-nav">
<view>
<view class="lf-nav__title">{{ mode === 'redemptions' ? '兑换记录' : '积分流水' }}</view>
<view class="lf-nav__sub">分页查询,滚动加载更多</view>
</view>
<view class="lf-pill">{{ total }} 条</view>
</view>
<view class="lf-card filter-card">
<view class="mode-row">
<button :class="mode === 'transactions' && 'is-active'" @click="switchMode('transactions')">积分流水</button>
<button :class="mode === 'redemptions' && 'is-active'" @click="switchMode('redemptions')">兑换记录</button>
</view>
<view class="search-box">
<u-icon name="search" color="#6f7c78" size="22"></u-icon>
<input v-model="filters.keyword" placeholder="搜索事项、券码或权益名称" confirm-type="search" @confirm="refresh" />
</view>
<button class="query-button" @click="refresh">查询</button>
</view>
<view v-for="item in list" :key="item.id" class="lf-card record-card">
<template v-if="mode === 'transactions'">
<view>
<view class="record-card__name">{{ item.title }}</view>
<view class="record-card__meta">{{ item.createdAt }}</view>
</view>
<view class="record-card__right">
<strong :class="item.amount < 0 && 'is-minus'">{{ item.amount > 0 ? '+' : '' }}{{ item.amount }}</strong>
<text>{{ statusText(item.status) }}</text>
</view>
</template>
<template v-else>
<view>
<view class="record-card__name">{{ item.reward?.name || item.itemId }}</view>
<view class="record-card__meta">{{ item.createdAt }} · {{ item.costPoints }} 积分</view>
<view v-if="item.voucherCode" class="record-card__meta">券码 {{ item.voucherCode }}</view>
</view>
<view class="record-card__right">
<text>{{ item.statusLabel || item.status }}</text>
</view>
</template>
</view>
<view class="load-state">{{ loadText }}</view>
<u-toast ref="toast"></u-toast>
</view>
</template>
<script>
import { POINT_STATUS } from '../../config'
import { getPointRedemptions, getPointTransactions } from '../../services/educationApi'
import { getStoredAccount, refreshStoredAccount } from '../../services/accountSession'
export default {
data() {
return {
mode: 'transactions',
list: [],
total: 0,
loading: false,
finished: false,
filters: {
keyword: '',
status: 'all',
pageNum: 1,
pageSize: 10,
},
}
},
computed: {
loadText() {
if (this.loading) return '加载中...'
if (this.finished) return this.list.length ? '没有更多了' : '暂无记录'
return '上拉加载更多'
},
},
onLoad(query = {}) {
if (query.mode === 'redemptions') this.mode = 'redemptions'
},
async onShow() {
await refreshStoredAccount()
await this.refresh()
},
async onPullDownRefresh() {
await this.refresh()
uni.stopPullDownRefresh()
},
onReachBottom() {
this.loadMore()
},
methods: {
switchMode(mode) {
this.mode = mode
this.refresh()
},
statusText(status) {
return POINT_STATUS[status] || status
},
async refresh() {
this.filters.pageNum = 1
this.finished = false
await this.load(true)
},
async loadMore() {
if (this.loading || this.finished) return
this.filters.pageNum += 1
await this.load(false)
},
async load(reset) {
const user = getStoredAccount()
if (!user) {
uni.reLaunch({ url: '/pages/login/index' })
return
}
this.loading = true
try {
const loader = this.mode === 'redemptions' ? getPointRedemptions : getPointTransactions
const res = await loader(user.id, this.filters)
if (res.ok === false) {
this.$refs.toast.show({ message: res.message || '记录加载失败', type: 'error' })
return
}
const data = res.data || res
this.total = data.total || 0
this.list = reset ? (data.list || []) : this.list.concat(data.list || [])
this.finished = this.list.length >= this.total
} finally {
this.loading = false
}
},
},
}
</script>
<style lang="scss" scoped>
.filter-card,
.record-card {
margin-top: 18rpx;
padding: 28rpx;
}
.mode-row {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12rpx;
}
.mode-row button,
.query-button {
height: 64rpx;
border-radius: 16rpx;
font-size: 24rpx;
font-weight: 900;
}
.mode-row button {
color: #0b4f46;
background: #eef8f4;
}
.mode-row button.is-active,
.query-button {
color: #ffffff;
background: #0b4f46;
}
.search-box {
display: flex;
align-items: center;
gap: 12rpx;
height: 74rpx;
margin-top: 18rpx;
padding: 0 22rpx;
border-radius: 16rpx;
background: #f3f8ef;
}
.search-box input {
flex: 1;
color: #17312d;
font-size: 26rpx;
}
.query-button {
width: 100%;
margin-top: 18rpx;
}
.record-card {
display: flex;
justify-content: space-between;
gap: 18rpx;
}
.record-card__name {
color: #17312d;
font-size: 29rpx;
font-weight: 900;
}
.record-card__meta {
margin-top: 8rpx;
color: #6f7c78;
font-size: 22rpx;
line-height: 1.45;
}
.record-card__right {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 10rpx;
}
.record-card__right strong {
color: #0b4f46;
font-size: 30rpx;
}
.record-card__right strong.is-minus {
color: #ff7a59;
}
.record-card__right text {
padding: 8rpx 14rpx;
border-radius: 999rpx;
color: #0b4f46;
background: #dff8ee;
font-size: 20rpx;
font-weight: 900;
}
.load-state {
padding: 28rpx 0 80rpx;
color: #6f7c78;
font-size: 23rpx;
text-align: center;
}
</style>