feat: 添加充值记录页面,支持显示充值记录和统计信息,集成支付方式和状态管理
This commit is contained in:
@@ -104,6 +104,11 @@ const routes: Array<RouteRecordRaw> = [
|
||||
component: () => import("@/views/recharge/index.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: "/recharge/records",
|
||||
component: () => import("@/views/recharge/records.vue"),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang='ts' setup>
|
||||
import { alertController, toastController } from "@ionic/vue";
|
||||
import { cardOutline, checkmarkCircleOutline, logoAlipay, logoWechat, walletOutline } from "ionicons/icons";
|
||||
import { cardOutline, checkmarkCircleOutline, documentTextOutline, logoAlipay, logoWechat, walletOutline } from "ionicons/icons";
|
||||
|
||||
const walletStore = useWalletStore();
|
||||
const { balanceWallet } = storeToRefs(walletStore);
|
||||
@@ -73,6 +73,8 @@ const paymentTypes = [
|
||||
{ type: "wechat" as const, name: "微信", icon: logoWechat, color: "#07C160" },
|
||||
];
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
function selectQuickAmount(amount: number) {
|
||||
rechargeAmount.value = amount.toString();
|
||||
}
|
||||
@@ -92,6 +94,10 @@ function selectPaymentMethod(id: number) {
|
||||
selectedPaymentId.value = id;
|
||||
}
|
||||
|
||||
function goToRecords() {
|
||||
router.push("/recharge/records");
|
||||
}
|
||||
|
||||
async function showToast(message: string, color: "success" | "danger" | "warning" = "success") {
|
||||
const toast = await toastController.create({
|
||||
message,
|
||||
@@ -163,6 +169,11 @@ walletStore.syncBalanceWallet();
|
||||
<back-button />
|
||||
</ion-buttons>
|
||||
<ion-title>充值</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button color="light" size="small" @click="goToRecords">
|
||||
<ion-icon slot="icon-only" :icon="documentTextOutline" />
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
|
||||
354
src/views/recharge/records.vue
Normal file
354
src/views/recharge/records.vue
Normal file
@@ -0,0 +1,354 @@
|
||||
<script lang='ts' setup>
|
||||
import { cardOutline, listOutline, logoAlipay, logoWechat } from "ionicons/icons";
|
||||
|
||||
interface RechargeRecord {
|
||||
id: number;
|
||||
amount: number;
|
||||
paymentType: "alipay" | "unionpay" | "wechat";
|
||||
paymentAccount: string;
|
||||
status: "success" | "pending" | "failed";
|
||||
time: string;
|
||||
orderNo: string;
|
||||
}
|
||||
|
||||
// 支付方式选项
|
||||
const paymentTypes = [
|
||||
{ type: "alipay" as const, name: "支付宝", icon: logoAlipay, color: "#1677FF" },
|
||||
{ type: "unionpay" as const, name: "银联", icon: cardOutline, color: "#E31937" },
|
||||
{ type: "wechat" as const, name: "微信", icon: logoWechat, color: "#07C160" },
|
||||
];
|
||||
|
||||
// 充值记录数据
|
||||
const rechargeRecords = ref<RechargeRecord[]>([
|
||||
{
|
||||
id: 1,
|
||||
amount: 1000.00,
|
||||
paymentType: "alipay",
|
||||
paymentAccount: "138****8000",
|
||||
status: "success",
|
||||
time: "2026-01-18 14:30:20",
|
||||
orderNo: "RCH202601181430001",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
amount: 500.00,
|
||||
paymentType: "wechat",
|
||||
paymentAccount: "wxid_abc123",
|
||||
status: "success",
|
||||
time: "2026-01-17 10:15:30",
|
||||
orderNo: "RCH202601171015002",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
amount: 2000.00,
|
||||
paymentType: "unionpay",
|
||||
paymentAccount: "6222 **** **** 1234",
|
||||
status: "pending",
|
||||
time: "2026-01-16 16:45:10",
|
||||
orderNo: "RCH202601161645003",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
amount: 100.00,
|
||||
paymentType: "alipay",
|
||||
paymentAccount: "159****6666",
|
||||
status: "success",
|
||||
time: "2026-01-15 09:20:00",
|
||||
orderNo: "RCH202601150920004",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
amount: 800.00,
|
||||
paymentType: "unionpay",
|
||||
paymentAccount: "6217 **** **** 5678",
|
||||
status: "failed",
|
||||
time: "2026-01-14 20:30:15",
|
||||
orderNo: "RCH202601142030005",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
amount: 300.00,
|
||||
paymentType: "alipay",
|
||||
paymentAccount: "138****8000",
|
||||
status: "success",
|
||||
time: "2026-01-13 11:45:30",
|
||||
orderNo: "RCH202601131145006",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
amount: 1500.00,
|
||||
paymentType: "wechat",
|
||||
paymentAccount: "wxid_abc123",
|
||||
status: "success",
|
||||
time: "2026-01-12 15:20:10",
|
||||
orderNo: "RCH202601121520007",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
amount: 600.00,
|
||||
paymentType: "unionpay",
|
||||
paymentAccount: "6222 **** **** 1234",
|
||||
status: "success",
|
||||
time: "2026-01-11 09:30:45",
|
||||
orderNo: "RCH202601110930008",
|
||||
},
|
||||
]);
|
||||
|
||||
function getPaymentTypeName(type: "alipay" | "unionpay" | "wechat") {
|
||||
return paymentTypes.find(pt => pt.type === type)?.name || type;
|
||||
}
|
||||
|
||||
function getPaymentTypeIcon(type: "alipay" | "unionpay" | "wechat") {
|
||||
return paymentTypes.find(pt => pt.type === type)?.icon || cardOutline;
|
||||
}
|
||||
|
||||
function getPaymentTypeColor(type: "alipay" | "unionpay" | "wechat") {
|
||||
return paymentTypes.find(pt => pt.type === type)?.color || "#999";
|
||||
}
|
||||
|
||||
function getStatusText(status: "success" | "pending" | "failed") {
|
||||
const statusMap = {
|
||||
success: "成功",
|
||||
pending: "处理中",
|
||||
failed: "失败",
|
||||
};
|
||||
return statusMap[status];
|
||||
}
|
||||
|
||||
function getStatusColor(status: "success" | "pending" | "failed") {
|
||||
const colorMap = {
|
||||
success: "#52c41a",
|
||||
pending: "#faad14",
|
||||
failed: "#f5222d",
|
||||
};
|
||||
return colorMap[status];
|
||||
}
|
||||
|
||||
// 统计数据
|
||||
const totalAmount = computed(() => {
|
||||
return rechargeRecords.value
|
||||
.filter(r => r.status === "success")
|
||||
.reduce((sum, r) => sum + r.amount, 0);
|
||||
});
|
||||
|
||||
const totalCount = computed(() => {
|
||||
return rechargeRecords.value.filter(r => r.status === "success").length;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ion-page>
|
||||
<ion-header class="ion-no-border">
|
||||
<ion-toolbar class="ion-toolbar">
|
||||
<ion-buttons slot="start">
|
||||
<back-button />
|
||||
</ion-buttons>
|
||||
<ion-title>充值记录</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<!-- 统计卡片 -->
|
||||
<div class="stats-card">
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">
|
||||
累计充值金额
|
||||
</div>
|
||||
<div class="stat-value">
|
||||
¥{{ totalAmount.toFixed(2) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-divider" />
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">
|
||||
充值成功次数
|
||||
</div>
|
||||
<div class="stat-value">
|
||||
{{ totalCount }}次
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 充值记录列表 -->
|
||||
<div class="records-section">
|
||||
<div v-if="rechargeRecords.length > 0" class="records-list">
|
||||
<div
|
||||
v-for="record in rechargeRecords"
|
||||
:key="record.id"
|
||||
class="record-item"
|
||||
>
|
||||
<div class="record-left">
|
||||
<ion-icon
|
||||
:icon="getPaymentTypeIcon(record.paymentType)"
|
||||
class="record-icon"
|
||||
:style="{ color: getPaymentTypeColor(record.paymentType) }"
|
||||
/>
|
||||
<div class="record-info">
|
||||
<div class="record-title">
|
||||
{{ getPaymentTypeName(record.paymentType) }}充值
|
||||
</div>
|
||||
<div class="record-account">
|
||||
{{ record.paymentAccount }}
|
||||
</div>
|
||||
<div class="record-time">
|
||||
{{ record.time }}
|
||||
</div>
|
||||
<div class="record-order">
|
||||
订单号:{{ record.orderNo }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="record-right">
|
||||
<div class="record-amount">
|
||||
+¥{{ record.amount.toFixed(2) }}
|
||||
</div>
|
||||
<div
|
||||
class="record-status"
|
||||
:style="{ color: getStatusColor(record.status) }"
|
||||
>
|
||||
{{ getStatusText(record.status) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="empty-records">
|
||||
<ion-icon :icon="listOutline" class="empty-icon" />
|
||||
<div class="empty-text">
|
||||
暂无充值记录
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-page>
|
||||
</template>
|
||||
|
||||
<style lang='css' scoped>
|
||||
.stats-card {
|
||||
background: linear-gradient(135deg, #c41e3a 0%, #8b1a2e 100%);
|
||||
padding: 24px 20px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-size: 13px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
color: white;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.stat-divider {
|
||||
width: 1px;
|
||||
height: 40px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.records-section {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
min-height: calc(100vh - 200px);
|
||||
}
|
||||
|
||||
.records-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.record-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
background: #fafafa;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.record-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.record-icon {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.record-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.record-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.record-account {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.record-time {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.record-order {
|
||||
font-size: 11px;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.record-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.record-amount {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #52c41a;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.record-status {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.empty-records {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 80px 20px;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 80px;
|
||||
color: #ddd;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user