404 lines
9.0 KiB
Vue
404 lines
9.0 KiB
Vue
|
|
<template>
|
||
|
|
<view class="payment-record-page">
|
||
|
|
<!-- 自定义导航栏 -->
|
||
|
|
<view class="custom-nav safe-area-top">
|
||
|
|
<view class="nav-content">
|
||
|
|
<view class="nav-btn" @click="goBack">
|
||
|
|
<uni-icons type="left" size="22" color="#1E293B"></uni-icons>
|
||
|
|
</view>
|
||
|
|
<text class="nav-title">支付记录</text>
|
||
|
|
<view class="nav-btn" @click="refreshData">
|
||
|
|
<uni-icons type="refresh" size="20" color="#64748B"></uni-icons>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
|
||
|
|
<!-- 统计卡片 -->
|
||
|
|
<view class="stats-section">
|
||
|
|
<view class="stats-card">
|
||
|
|
<text class="stats-label">累计支付</text>
|
||
|
|
<text class="stats-value">¥{{totalAmount}}</text>
|
||
|
|
</view>
|
||
|
|
<view class="stats-card">
|
||
|
|
<text class="stats-label">支付次数</text>
|
||
|
|
<text class="stats-value">{{totalCount}}</text>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
|
||
|
|
<scroll-view
|
||
|
|
scroll-y
|
||
|
|
class="page-content"
|
||
|
|
@scrolltolower="loadMore"
|
||
|
|
refresher-enabled
|
||
|
|
:refresher-triggered="refreshing"
|
||
|
|
@refresherrefresh="onRefresh"
|
||
|
|
>
|
||
|
|
<!-- 记录列表 -->
|
||
|
|
<view class="record-list" v-if="payments.length > 0">
|
||
|
|
<view
|
||
|
|
v-for="(payment, index) in payments"
|
||
|
|
:key="payment.id"
|
||
|
|
class="record-card"
|
||
|
|
>
|
||
|
|
<view class="record-header">
|
||
|
|
<view class="payment-icon" :class="payment.paymentMethod">
|
||
|
|
<text>{{getPaymentMethodIcon(payment.paymentMethod)}}</text>
|
||
|
|
</view>
|
||
|
|
<view class="payment-info">
|
||
|
|
<text class="payment-title">{{getPaymentTitle(payment)}}</text>
|
||
|
|
<text class="payment-time">{{formatDateTime(payment.payTime)}}</text>
|
||
|
|
</view>
|
||
|
|
<view class="payment-amount">
|
||
|
|
<text class="amount">¥{{payment.amount}}</text>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
<view class="record-body">
|
||
|
|
<view class="record-item">
|
||
|
|
<text class="item-label">订单编号</text>
|
||
|
|
<text class="item-value">{{payment.orderNo}}</text>
|
||
|
|
</view>
|
||
|
|
<view class="record-item" v-if="payment.transactionId">
|
||
|
|
<text class="item-label">交易号</text>
|
||
|
|
<text class="item-value">{{payment.transactionId}}</text>
|
||
|
|
</view>
|
||
|
|
<view class="record-item">
|
||
|
|
<text class="item-label">支付方式</text>
|
||
|
|
<text class="item-value">{{getPaymentMethodText(payment.paymentMethod)}}</text>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
|
||
|
|
<!-- 加载更多 -->
|
||
|
|
<view class="load-more" v-if="hasMore">
|
||
|
|
<text v-if="loading">加载中...</text>
|
||
|
|
<text v-else @click="loadMore">加载更多</text>
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
|
||
|
|
<!-- 空状态 -->
|
||
|
|
<view class="empty-state" v-else>
|
||
|
|
<uni-icons type="wallet-filled" size="80" color="#E4E7ED"></uni-icons>
|
||
|
|
<text class="empty-title">暂无支付记录</text>
|
||
|
|
<text class="empty-desc">您还没有支付记录</text>
|
||
|
|
</view>
|
||
|
|
|
||
|
|
<view class="safe-area-bottom" style="height: 40rpx;"></view>
|
||
|
|
</scroll-view>
|
||
|
|
</view>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
import { billingApi } from '@/api/index.js'
|
||
|
|
|
||
|
|
export default {
|
||
|
|
data() {
|
||
|
|
return {
|
||
|
|
payments: [],
|
||
|
|
totalAmount: 0,
|
||
|
|
totalCount: 0,
|
||
|
|
page: 1,
|
||
|
|
pageSize: 10,
|
||
|
|
hasMore: true,
|
||
|
|
loading: false,
|
||
|
|
refreshing: false
|
||
|
|
}
|
||
|
|
},
|
||
|
|
onLoad() {
|
||
|
|
this.loadPayments()
|
||
|
|
},
|
||
|
|
onShow() {
|
||
|
|
this.refreshData()
|
||
|
|
},
|
||
|
|
methods: {
|
||
|
|
async loadPayments() {
|
||
|
|
if (this.loading || !this.hasMore) return
|
||
|
|
this.loading = true
|
||
|
|
|
||
|
|
try {
|
||
|
|
const res = await billingApi.getPayments({
|
||
|
|
page: this.page,
|
||
|
|
pageSize: this.pageSize
|
||
|
|
})
|
||
|
|
|
||
|
|
if (res.code === 200) {
|
||
|
|
const newPayments = res.data.list || []
|
||
|
|
if (this.page === 1) {
|
||
|
|
this.payments = newPayments
|
||
|
|
} else {
|
||
|
|
this.payments = [...this.payments, ...newPayments]
|
||
|
|
}
|
||
|
|
this.totalAmount = res.data.totalAmount || 0
|
||
|
|
this.totalCount = res.data.totalCount || 0
|
||
|
|
this.hasMore = newPayments.length >= this.pageSize
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('加载支付记录失败:', error)
|
||
|
|
uni.showToast({ title: '加载失败', icon: 'none' })
|
||
|
|
} finally {
|
||
|
|
this.loading = false
|
||
|
|
this.refreshing = false
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
onRefresh() {
|
||
|
|
this.refreshing = true
|
||
|
|
this.page = 1
|
||
|
|
this.hasMore = true
|
||
|
|
this.loadPayments()
|
||
|
|
},
|
||
|
|
|
||
|
|
refreshData() {
|
||
|
|
this.page = 1
|
||
|
|
this.hasMore = true
|
||
|
|
this.loadPayments()
|
||
|
|
},
|
||
|
|
|
||
|
|
loadMore() {
|
||
|
|
if (this.hasMore && !this.loading) {
|
||
|
|
this.page++
|
||
|
|
this.loadPayments()
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
getPaymentMethodIcon(method) {
|
||
|
|
const map = {
|
||
|
|
alipay: '支',
|
||
|
|
wechat: '微',
|
||
|
|
bank: '银'
|
||
|
|
}
|
||
|
|
return map[method] || '付'
|
||
|
|
},
|
||
|
|
|
||
|
|
getPaymentMethodText(method) {
|
||
|
|
const map = {
|
||
|
|
alipay: '支付宝',
|
||
|
|
wechat: '微信支付',
|
||
|
|
bank: '银行转账'
|
||
|
|
}
|
||
|
|
return map[method] || method || '-'
|
||
|
|
},
|
||
|
|
|
||
|
|
getPaymentTitle(payment) {
|
||
|
|
if (payment.subscriptionPlan) {
|
||
|
|
return payment.subscriptionPlan.name
|
||
|
|
}
|
||
|
|
return '套餐购买'
|
||
|
|
},
|
||
|
|
|
||
|
|
formatDateTime(dateTime) {
|
||
|
|
if (!dateTime) return '-'
|
||
|
|
const date = new Date(dateTime)
|
||
|
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
|
||
|
|
},
|
||
|
|
|
||
|
|
goBack() {
|
||
|
|
uni.navigateBack()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
.payment-record-page {
|
||
|
|
min-height: 100vh;
|
||
|
|
background: #F8FAFC;
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 导航栏 */
|
||
|
|
.custom-nav {
|
||
|
|
background: #FFFFFF;
|
||
|
|
border-bottom: 2rpx solid #F1F5F9;
|
||
|
|
}
|
||
|
|
|
||
|
|
.nav-content {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: space-between;
|
||
|
|
padding: 20rpx 32rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.nav-btn {
|
||
|
|
width: 72rpx;
|
||
|
|
height: 72rpx;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
border-radius: 50%;
|
||
|
|
}
|
||
|
|
|
||
|
|
.nav-title {
|
||
|
|
font-size: 34rpx;
|
||
|
|
font-weight: 600;
|
||
|
|
color: #1E293B;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 统计区域 */
|
||
|
|
.stats-section {
|
||
|
|
display: flex;
|
||
|
|
gap: 24rpx;
|
||
|
|
padding: 24rpx 32rpx;
|
||
|
|
background: #FFFFFF;
|
||
|
|
border-bottom: 2rpx solid #F1F5F9;
|
||
|
|
}
|
||
|
|
|
||
|
|
.stats-card {
|
||
|
|
flex: 1;
|
||
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
|
|
border-radius: 20rpx;
|
||
|
|
padding: 32rpx;
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
align-items: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.stats-label {
|
||
|
|
font-size: 26rpx;
|
||
|
|
color: rgba(255, 255, 255, 0.8);
|
||
|
|
margin-bottom: 12rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.stats-value {
|
||
|
|
font-size: 40rpx;
|
||
|
|
font-weight: 700;
|
||
|
|
color: #FFFFFF;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 页面内容 */
|
||
|
|
.page-content {
|
||
|
|
flex: 1;
|
||
|
|
padding: 24rpx 32rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 记录列表 */
|
||
|
|
.record-list {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
gap: 24rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.record-card {
|
||
|
|
background: #FFFFFF;
|
||
|
|
border-radius: 24rpx;
|
||
|
|
padding: 28rpx;
|
||
|
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
|
||
|
|
}
|
||
|
|
|
||
|
|
.record-header {
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 20rpx;
|
||
|
|
margin-bottom: 20rpx;
|
||
|
|
padding-bottom: 20rpx;
|
||
|
|
border-bottom: 2rpx solid #F1F5F9;
|
||
|
|
}
|
||
|
|
|
||
|
|
.payment-icon {
|
||
|
|
width: 72rpx;
|
||
|
|
height: 72rpx;
|
||
|
|
border-radius: 20rpx;
|
||
|
|
display: flex;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.payment-icon.alipay {
|
||
|
|
background: #1677FF;
|
||
|
|
}
|
||
|
|
|
||
|
|
.payment-icon.wechat {
|
||
|
|
background: #07C160;
|
||
|
|
}
|
||
|
|
|
||
|
|
.payment-icon.bank {
|
||
|
|
background: #FF6B6B;
|
||
|
|
}
|
||
|
|
|
||
|
|
.payment-icon text {
|
||
|
|
font-size: 32rpx;
|
||
|
|
color: #FFFFFF;
|
||
|
|
font-weight: 700;
|
||
|
|
}
|
||
|
|
|
||
|
|
.payment-info {
|
||
|
|
flex: 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
.payment-title {
|
||
|
|
display: block;
|
||
|
|
font-size: 30rpx;
|
||
|
|
font-weight: 600;
|
||
|
|
color: #1E293B;
|
||
|
|
margin-bottom: 8rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.payment-time {
|
||
|
|
display: block;
|
||
|
|
font-size: 24rpx;
|
||
|
|
color: #94A3B8;
|
||
|
|
}
|
||
|
|
|
||
|
|
.payment-amount {
|
||
|
|
text-align: right;
|
||
|
|
}
|
||
|
|
|
||
|
|
.payment-amount .amount {
|
||
|
|
font-size: 36rpx;
|
||
|
|
font-weight: 700;
|
||
|
|
color: #F56C6C;
|
||
|
|
}
|
||
|
|
|
||
|
|
.record-body {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
gap: 16rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.record-item {
|
||
|
|
display: flex;
|
||
|
|
justify-content: space-between;
|
||
|
|
align-items: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.item-label {
|
||
|
|
font-size: 26rpx;
|
||
|
|
color: #64748B;
|
||
|
|
}
|
||
|
|
|
||
|
|
.item-value {
|
||
|
|
font-size: 26rpx;
|
||
|
|
color: #1E293B;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 加载更多 */
|
||
|
|
.load-more {
|
||
|
|
text-align: center;
|
||
|
|
padding: 32rpx;
|
||
|
|
color: #64748B;
|
||
|
|
font-size: 26rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 空状态 */
|
||
|
|
.empty-state {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
align-items: center;
|
||
|
|
justify-content: center;
|
||
|
|
padding: 120rpx 32rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.empty-title {
|
||
|
|
font-size: 32rpx;
|
||
|
|
font-weight: 600;
|
||
|
|
color: #1E293B;
|
||
|
|
margin-top: 32rpx;
|
||
|
|
margin-bottom: 16rpx;
|
||
|
|
}
|
||
|
|
|
||
|
|
.empty-desc {
|
||
|
|
font-size: 26rpx;
|
||
|
|
color: #64748B;
|
||
|
|
}
|
||
|
|
</style>
|