peixue-dev/peidu/uniapp/user-package/pages/wallet/transaction.vue

324 lines
7.1 KiB
Vue

<template>
<view class="transaction-page">
<!-- 筛选栏 -->
<view class="filter-bar">
<view class="filter-tabs">
<view
class="tab-item"
:class="{ active: currentType === item.value }"
v-for="item in typeList"
:key="item.value"
@click="changeType(item.value)"
>
{{ item.label }}
</view>
</view>
</view>
<!-- 交易列表 -->
<view class="transaction-list">
<view class="transaction-item" v-for="item in list" :key="item.id" @click="viewDetail(item)">
<view class="item-left">
<view class="item-icon" :class="'icon-' + item.type">
<text class="iconfont" :class="'icon-' + item.type + '-text'"></text>
</view>
<view class="item-info">
<view class="item-title">{{ item.title }}</view>
<view class="item-time">{{ item.createTime }}</view>
</view>
</view>
<view class="item-right">
<view class="item-amount" :class="item.type === 'income' ? 'income' : 'expense'">
{{ item.type === 'income' ? '+' : '-' }}¥{{ item.amount }}
</view>
<view class="item-status" :class="'status-' + item.status">
{{ getStatusText(item.status) }}
</view>
</view>
</view>
<!-- 加载更多 -->
<view class="load-more" v-if="hasMore">
<text v-if="loading">加载中...</text>
<text v-else @click="loadMore">加载更多</text>
</view>
<!-- 没有更多 -->
<view class="no-more" v-if="!hasMore && list.length > 0">
<text>没有更多了</text>
</view>
<!-- 空状态 -->
<view class="empty" v-if="list.length === 0 && !loading">
<image class="empty-img" src="/static/images/empty-transaction.png" mode="aspectFit"></image>
<text class="empty-text">暂无交易记录</text>
</view>
</view>
</view>
</template>
<script>
import { walletApi } from '@/api/index.js'
export default {
data() {
return {
currentType: 'all',
typeList: [
{ label: '全部', value: 'all' },
{ label: '收入', value: 'income' },
{ label: '支出', value: 'expense' }
],
list: [],
page: 1,
pageSize: 20,
hasMore: true,
loading: false
}
},
onLoad() {
this.loadList()
},
onReachBottom() {
if (this.hasMore && !this.loading) {
this.loadMore()
}
},
onPullDownRefresh() {
this.page = 1
this.list = []
this.hasMore = true
this.loadList().then(() => {
uni.stopPullDownRefresh()
})
},
methods: {
async loadList() {
if (this.loading) return
this.loading = true
try {
const res = await walletApi.getTransactionList({
type: this.currentType === 'all' ? '' : this.currentType,
page: this.page,
pageSize: this.pageSize
})
if (this.page === 1) {
this.list = res.data.records || []
} else {
this.list = [...this.list, ...(res.data.records || [])]
}
this.hasMore = this.list.length < res.data.total
} catch (e) {
console.error('加载交易记录失败', e)
uni.showToast({ title: '加载失败', icon: 'none' })
} finally {
this.loading = false
}
},
changeType(type) {
if (this.currentType === type) return
this.currentType = type
this.page = 1
this.list = []
this.hasMore = true
this.loadList()
},
loadMore() {
this.page++
this.loadList()
},
getIconClass(type) {
const iconMap = {
'recharge': 'icon-recharge',
'withdraw': 'icon-withdraw',
'payment': 'icon-payment',
'refund': 'icon-refund',
'commission': 'icon-commission',
'reward': 'icon-reward'
}
return iconMap[type] || 'icon-transaction'
},
getStatusText(status) {
const statusMap = {
'pending': '处理中',
'success': '成功',
'failed': '失败',
'cancelled': '已取消'
}
return statusMap[status] || status
},
viewDetail(item) {
uni.navigateTo({
url: `/pages/wallet/transaction-detail?id=${item.id}`
})
}
}
}
</script>
<style lang="scss" scoped>
.transaction-page {
min-height: 100vh;
background: #f0f9f7;
}
.filter-bar {
background: #fff;
padding: 20rpx;
.filter-tabs {
display: flex;
justify-content: space-around;
.tab-item {
flex: 1;
text-align: center;
padding: 20rpx 0;
font-size: 28rpx;
color: #666;
position: relative;
&.active {
color: #5fc9ba;
font-weight: bold;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 60rpx;
height: 4rpx;
background: #5fc9ba;
border-radius: 2rpx;
}
}
}
}
}
.transaction-list {
padding: 20rpx;
.transaction-item {
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
.item-left {
display: flex;
align-items: center;
flex: 1;
.item-icon {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
&.icon-income {
background: #e8f5e9;
color: #4caf50;
}
&.icon-expense {
background: #ffebee;
color: #f44336;
}
.iconfont {
font-size: 40rpx;
}
}
.item-info {
flex: 1;
.item-title {
font-size: 28rpx;
color: #333;
margin-bottom: 10rpx;
}
.item-time {
font-size: 24rpx;
color: #999;
}
}
}
.item-right {
text-align: right;
.item-amount {
font-size: 32rpx;
font-weight: bold;
margin-bottom: 10rpx;
&.income {
color: #4caf50;
}
&.expense {
color: #f44336;
}
}
.item-status {
font-size: 24rpx;
&.status-pending {
color: #ff9800;
}
&.status-success {
color: #4caf50;
}
&.status-failed {
color: #f44336;
}
&.status-cancelled {
color: #999;
}
}
}
}
.load-more, .no-more {
text-align: center;
padding: 40rpx 0;
font-size: 24rpx;
color: #999;
}
.empty {
text-align: center;
padding: 200rpx 0;
.empty-img {
width: 300rpx;
height: 300rpx;
margin-bottom: 40rpx;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
}
}
</style>