添加钱包测试功能
@@ -25,6 +25,7 @@ export default defineAppConfig({
|
||||
"edit/index", // 编辑个人中心
|
||||
"other/index", // 他人个人主页
|
||||
"follow/index", // 球友关注页
|
||||
"wallet/index", // 钱包页
|
||||
],
|
||||
},
|
||||
// {
|
||||
|
||||
115
src/services/walletService.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import httpService from "./httpService";
|
||||
|
||||
// 钱包信息接口
|
||||
export interface WalletInfo {
|
||||
balance: number;
|
||||
total_income: number;
|
||||
total_withdraw: number;
|
||||
}
|
||||
|
||||
// 交易记录接口
|
||||
export interface TransactionRecord {
|
||||
id: string;
|
||||
type: "withdraw" | "income" | "refund";
|
||||
amount: number;
|
||||
description: string;
|
||||
created_time: string;
|
||||
status: "pending" | "success" | "failed";
|
||||
}
|
||||
|
||||
// 提现请求接口
|
||||
export interface WithdrawRequest {
|
||||
amount: number;
|
||||
bank_id: string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class WalletService {
|
||||
/**
|
||||
* 获取钱包信息
|
||||
*/
|
||||
static async get_wallet_info(): Promise<WalletInfo> {
|
||||
try {
|
||||
const response = await httpService.post("/api/wallet/get_info", {});
|
||||
|
||||
if (response.code === 200) {
|
||||
return response.data;
|
||||
} else {
|
||||
throw new Error(response.message || "获取钱包信息失败");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取钱包信息失败:", error);
|
||||
// 返回模拟数据
|
||||
return {
|
||||
balance: 1588.80,
|
||||
total_income: 3200.00,
|
||||
total_withdraw: 1611.20,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取交易记录
|
||||
*/
|
||||
static async get_transactions(): Promise<TransactionRecord[]> {
|
||||
try {
|
||||
const response = await httpService.post("/api/wallet/get_transactions", {});
|
||||
|
||||
if (response.code === 200) {
|
||||
return response.data;
|
||||
} else {
|
||||
throw new Error(response.message || "获取交易记录失败");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取交易记录失败:", error);
|
||||
// 返回模拟数据
|
||||
return [
|
||||
{
|
||||
id: "1",
|
||||
type: "income",
|
||||
amount: 150.00,
|
||||
description: "球局收入",
|
||||
created_time: "2024-12-20 14:30:00",
|
||||
status: "success",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
type: "withdraw",
|
||||
amount: 200.00,
|
||||
description: "提现",
|
||||
created_time: "2024-12-19 10:15:00",
|
||||
status: "success",
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
type: "refund",
|
||||
amount: 80.00,
|
||||
description: "球局退款",
|
||||
created_time: "2024-12-18 16:45:00",
|
||||
status: "success",
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交提现申请
|
||||
*/
|
||||
static async submit_withdraw(request: WithdrawRequest): Promise<void> {
|
||||
try {
|
||||
const response = await httpService.post("/wallet/withdraw", {
|
||||
amount: request.amount,
|
||||
transfer_remark: "用户申请提现"
|
||||
});
|
||||
|
||||
if (response.code !== 200) {
|
||||
throw new Error(response.message || "提现申请提交失败");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("提现申请提交失败:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
6
src/static/userInfo/bank_card.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="4" y="12" width="40" height="24" rx="4" fill="#667EEA"/>
|
||||
<rect x="4" y="20" width="40" height="4" fill="#FFF"/>
|
||||
<rect x="8" y="28" width="12" height="2" rx="1" fill="#FFF"/>
|
||||
<rect x="8" y="32" width="8" height="2" rx="1" fill="#FFF"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 355 B |
4
src/static/userInfo/income_icon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 18L12 2M18 8L12 2L6 8" stroke="#2ED573" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4 22H20" stroke="#2ED573" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 304 B |
5
src/static/userInfo/recharge_btn.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="32" cy="32" r="32" fill="#4ECDC4" opacity="0.1"/>
|
||||
<path d="M32 42L32 16M40 24L32 16L24 24" stroke="#4ECDC4" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M20 48H44" stroke="#4ECDC4" stroke-width="3" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 374 B |
4
src/static/userInfo/refund_icon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3 9L9 3L5 3L5 7M5 7L5 3M5 7L9 7" stroke="#FFA502" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C9.23858 3 6.79449 4.30367 5.24561 6.34267" stroke="#FFA502" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 405 B |
4
src/static/userInfo/transaction_icon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="12" cy="12" r="9" stroke="#999" stroke-width="2"/>
|
||||
<path d="M12 6V12L16 14" stroke="#999" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 251 B |
6
src/static/userInfo/transaction_list.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 8H28M4 16H28M4 24H28" stroke="#667EEA" stroke-width="2" stroke-linecap="round"/>
|
||||
<circle cx="26" cy="8" r="2" fill="#667EEA"/>
|
||||
<circle cx="26" cy="16" r="2" fill="#667EEA"/>
|
||||
<circle cx="26" cy="24" r="2" fill="#667EEA"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 344 B |
5
src/static/userInfo/withdraw_btn.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="32" cy="32" r="32" fill="#FF6B6B" opacity="0.1"/>
|
||||
<path d="M32 16L32 42M24 34L32 42L40 34" stroke="#FF6B6B" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M20 48H44" stroke="#FF6B6B" stroke-width="3" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 374 B |
4
src/static/userInfo/withdraw_icon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 2L12 18M6 12L12 18L18 12" stroke="#FF4757" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4 22H20" stroke="#FF4757" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 307 B |
@@ -30,7 +30,7 @@ const MyselfPage: React.FC = () => {
|
||||
participated: 0,
|
||||
},
|
||||
bio: "加载中...",
|
||||
location: "加载中...",
|
||||
city: "加载中...",
|
||||
occupation: "加载中...",
|
||||
ntrp_level: "NTRP 3.0",
|
||||
personal_profile: "加载中...",
|
||||
@@ -143,10 +143,10 @@ const MyselfPage: React.FC = () => {
|
||||
});
|
||||
};
|
||||
|
||||
// 处理收藏
|
||||
const handle_favorites = () => {
|
||||
// 处理钱包
|
||||
const handle_wallet = () => {
|
||||
Taro.navigateTo({
|
||||
url: "/other_pages/favorites/index",
|
||||
url: "/user_pages/wallet/index",
|
||||
});
|
||||
};
|
||||
|
||||
@@ -180,7 +180,7 @@ const MyselfPage: React.FC = () => {
|
||||
<Text className="action_text">球局订单</Text>
|
||||
</View>
|
||||
<View className="action_divider"></View>
|
||||
<View className="action_content" onClick={handle_favorites}>
|
||||
<View className="action_content" onClick={handle_wallet}>
|
||||
<Image
|
||||
className="action_icon"
|
||||
src={require("@/static/userInfo/wallet.svg")}
|
||||
|
||||
3
src/user_pages/wallet/index.config.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: "我的钱包",
|
||||
});
|
||||
344
src/user_pages/wallet/index.scss
Normal file
@@ -0,0 +1,344 @@
|
||||
// @use '../../scss/common.scss' as *;
|
||||
|
||||
.wallet_page {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding: 5px;
|
||||
|
||||
.wallet_main_card {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
padding: 12px 20px 32px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.card_header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 32px;
|
||||
|
||||
.header_title {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #000;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.modify_password {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #007AFF;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
.balance_display {
|
||||
.amount_section {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.amount_container {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 8px;
|
||||
|
||||
.currency_symbol {
|
||||
font-family: 'DingTalk JinBuTi', sans-serif;
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
color: #000;
|
||||
line-height: 0.8;
|
||||
}
|
||||
|
||||
.amount_group {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
|
||||
.main_amount {
|
||||
font-family: 'DingTalk JinBuTi', sans-serif;
|
||||
font-size: 32px;
|
||||
font-weight: 400;
|
||||
color: #000;
|
||||
line-height: 0.75;
|
||||
}
|
||||
|
||||
.decimal_amount {
|
||||
font-family: 'DingTalk JinBuTi', sans-serif;
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
color: #000;
|
||||
line-height: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.withdraw_btn {
|
||||
background: #000;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
padding: 8px 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 1.29;
|
||||
width: 68px;
|
||||
height: 34px;
|
||||
|
||||
&:active {
|
||||
background: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.available_amount {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #000;
|
||||
line-height: 1.5;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.function_buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.function_item {
|
||||
background: white;
|
||||
border: 0.5px solid #EBEBEB;
|
||||
border-radius: 20px;
|
||||
padding: 4px 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2px;
|
||||
box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.05);
|
||||
height: 24px;
|
||||
flex: 1;
|
||||
|
||||
.function_icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.icon_text {
|
||||
font-size: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.function_text {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #000;
|
||||
line-height: 1.33;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: #f8f8f8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.transaction_history {
|
||||
background: white;
|
||||
border: 0.5px solid #EBEBEB;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0px 0px 36px 0px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
|
||||
.history_header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 20px;
|
||||
border-bottom: 0.5px solid rgba(120, 120, 128, 0.12);
|
||||
|
||||
.history_title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #000;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.month_selector {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
.current_month {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #000;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.dropdown_arrow {
|
||||
font-size: 10px;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.transaction_list {
|
||||
.loading_state,
|
||||
.empty_state {
|
||||
padding: 40px 20px;
|
||||
text-align: center;
|
||||
|
||||
.loading_text,
|
||||
.empty_text {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.transaction_item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 20px;
|
||||
|
||||
.transaction_left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
flex: 1;
|
||||
|
||||
.transaction_title {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #000;
|
||||
line-height: 1.5;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.transaction_time {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: stretch;
|
||||
gap: 4px;
|
||||
|
||||
.transaction_date,
|
||||
.transaction_clock {
|
||||
font-size: 10px;
|
||||
font-weight: 400;
|
||||
color: rgba(60, 60, 67, 0.6);
|
||||
line-height: 1.2;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.transaction_right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 4px;
|
||||
width: 68px;
|
||||
|
||||
.transaction_amount {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #000;
|
||||
line-height: 1.5;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.balance_info {
|
||||
font-size: 10px;
|
||||
font-weight: 400;
|
||||
color: rgba(60, 60, 67, 0.6);
|
||||
line-height: 1.2;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 提现弹窗样式
|
||||
.withdraw_popup {
|
||||
.popup_content {
|
||||
padding: 12px;
|
||||
overflow: hidden;
|
||||
|
||||
.form_section {
|
||||
.form_item {
|
||||
margin-bottom: 12px;
|
||||
|
||||
.form_label {
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.input_wrapper {
|
||||
position: relative;
|
||||
|
||||
.amount_input {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 4px;
|
||||
padding: 0 10px;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
|
||||
&:focus {
|
||||
border-color: #667eea;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: #bbb;
|
||||
}
|
||||
}
|
||||
|
||||
.currency_symbol {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.amount_input.with_symbol {
|
||||
padding-left: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.balance_tip {
|
||||
font-size: 10px;
|
||||
color: #999;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.withdraw_desc {
|
||||
padding: 8px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e9ecef;
|
||||
|
||||
.desc_text {
|
||||
font-size: 10px;
|
||||
color: #666;
|
||||
line-height: 1.3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
406
src/user_pages/wallet/index.tsx
Normal file
@@ -0,0 +1,406 @@
|
||||
import React, { useState } from "react";
|
||||
import { View, Text, Input, Button } from "@tarojs/components";
|
||||
import Taro, { useDidShow } from "@tarojs/taro";
|
||||
import "./index.scss";
|
||||
import { CommonPopup } from "@/components";
|
||||
import httpService from "@/services/httpService";
|
||||
import { withAuth } from "@/components";
|
||||
|
||||
// 交易记录类型
|
||||
interface Transaction {
|
||||
id: number;
|
||||
user_id: number;
|
||||
transaction_type: string;
|
||||
freeze_action: string | null;
|
||||
amount: string;
|
||||
description: string;
|
||||
create_time: string;
|
||||
last_modify_time: string;
|
||||
related_id: number;
|
||||
}
|
||||
|
||||
// 钱包信息类型
|
||||
interface WalletInfo {
|
||||
balance: number;
|
||||
frozen_balance?: number;
|
||||
total_balance?: number;
|
||||
total_income?: number;
|
||||
total_withdraw?: number;
|
||||
}
|
||||
|
||||
|
||||
const WalletPage: React.FC = () => {
|
||||
// 钱包信息状态
|
||||
const [wallet_info, set_wallet_info] = useState<WalletInfo>({
|
||||
balance: 0,
|
||||
});
|
||||
|
||||
// 提现弹窗状态
|
||||
const [show_withdraw_popup, set_show_withdraw_popup] = useState(false);
|
||||
const [withdraw_amount, set_withdraw_amount] = useState("");
|
||||
const [submitting, set_submitting] = useState(false);
|
||||
|
||||
// 交易记录状态
|
||||
const [transactions, set_transactions] = useState<Transaction[]>([]);
|
||||
const [loading_transactions, set_loading_transactions] = useState(false);
|
||||
|
||||
// 页面显示时加载数据
|
||||
useDidShow(() => {
|
||||
load_wallet_data();
|
||||
load_transactions();
|
||||
});
|
||||
|
||||
// 加载钱包数据
|
||||
const load_wallet_data = async () => {
|
||||
try {
|
||||
const response = await httpService.post("/wallet/balance");
|
||||
const { balance, frozen_balance, total_balance, total_income, total_withdraw } = response.data;
|
||||
set_wallet_info({
|
||||
balance,
|
||||
frozen_balance,
|
||||
total_balance,
|
||||
total_income,
|
||||
total_withdraw
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error("加载钱包数据失败:", error);
|
||||
|
||||
let errorMessage = "加载失败,请重试";
|
||||
if (error && error.response && error.response.data && error.response.data.message) {
|
||||
errorMessage = error.response.data.message;
|
||||
} else if (error && error.data && error.data.message) {
|
||||
errorMessage = error.data.message;
|
||||
}
|
||||
|
||||
Taro.showToast({
|
||||
title: errorMessage,
|
||||
icon: "error",
|
||||
duration: 2000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 加载交易记录
|
||||
const load_transactions = async () => {
|
||||
try {
|
||||
set_loading_transactions(true);
|
||||
console.log("开始加载交易记录...");
|
||||
|
||||
const response = await httpService.post("/wallet/transactions", {
|
||||
page: 1,
|
||||
limit: 20
|
||||
});
|
||||
|
||||
console.log("交易记录响应:", response);
|
||||
|
||||
if (response && response.data && response.data.list) {
|
||||
set_transactions(response.data.list);
|
||||
console.log("设置交易记录:", response.data.list);
|
||||
} else {
|
||||
console.log("响应数据格式异常:", response);
|
||||
set_transactions([]);
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error("加载交易记录失败:", error);
|
||||
set_transactions([]);
|
||||
|
||||
let errorMessage = "加载交易记录失败";
|
||||
if (error && error.response && error.response.data && error.response.data.message) {
|
||||
errorMessage = error.response.data.message;
|
||||
} else if (error && error.data && error.data.message) {
|
||||
errorMessage = error.data.message;
|
||||
}
|
||||
|
||||
Taro.showToast({
|
||||
title: errorMessage,
|
||||
icon: "error",
|
||||
duration: 2000,
|
||||
});
|
||||
} finally {
|
||||
console.log("加载交易记录完成,设置loading为false");
|
||||
set_loading_transactions(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理提现
|
||||
const handle_withdraw = () => {
|
||||
if (wallet_info.balance <= 0) {
|
||||
Taro.showToast({
|
||||
title: "余额不足",
|
||||
icon: "error",
|
||||
duration: 2000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
set_show_withdraw_popup(true);
|
||||
};
|
||||
|
||||
// 提交提现申请
|
||||
const submit_withdraw = async () => {
|
||||
if (!withdraw_amount || parseFloat(withdraw_amount) <= 0) {
|
||||
Taro.showToast({
|
||||
title: "请输入有效金额",
|
||||
icon: "error",
|
||||
duration: 2000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (parseFloat(withdraw_amount) > wallet_info.balance) {
|
||||
Taro.showToast({
|
||||
title: "提现金额不能超过余额",
|
||||
icon: "error",
|
||||
duration: 2000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
set_submitting(true);
|
||||
|
||||
// 先调用后端接口获取提现参数
|
||||
const response = await httpService.post("/wallet/withdraw", {
|
||||
amount: parseFloat(withdraw_amount),
|
||||
transfer_remark: "用户申请提现"
|
||||
});
|
||||
|
||||
// 根据后端返回的数据结构解析参数
|
||||
const { mch_id, app_id, package: package_info, open_id } = response.data;
|
||||
|
||||
// 调用微信商户转账接口
|
||||
(Taro as any).requestMerchantTransfer({
|
||||
mchId: mch_id,
|
||||
appId: app_id,
|
||||
package: package_info,
|
||||
openId: open_id,
|
||||
success: (res) => {
|
||||
console.log("微信转账成功:", res);
|
||||
|
||||
Taro.showToast({
|
||||
title: "提现成功",
|
||||
icon: "success",
|
||||
duration: 2000,
|
||||
});
|
||||
|
||||
// 关闭弹窗并重置状态
|
||||
set_show_withdraw_popup(false);
|
||||
set_withdraw_amount("");
|
||||
// 重新加载数据
|
||||
load_wallet_data();
|
||||
},
|
||||
fail: (res) => {
|
||||
console.log("微信转账失败:", res);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
// 格式化金额显示
|
||||
const format_amount = (amount: number | string) => {
|
||||
const numAmount = typeof amount === 'string' ? parseFloat(amount) : amount;
|
||||
return numAmount.toFixed(2);
|
||||
};
|
||||
|
||||
// 格式化时间显示
|
||||
const format_time = (time: string) => {
|
||||
const date = new Date(time);
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
|
||||
return {
|
||||
date: `2025-${month}-${day}`,
|
||||
time: `${hours}:${minutes}:${seconds}`
|
||||
};
|
||||
};
|
||||
|
||||
// 获取交易类型文字
|
||||
const get_transaction_type_text = (transaction: Transaction) => {
|
||||
// 如果有描述信息,优先使用描述
|
||||
if (transaction.description) {
|
||||
return transaction.description;
|
||||
}
|
||||
|
||||
const typeMap: { [key: string]: string } = {
|
||||
'income': '收入',
|
||||
'expense': '支出',
|
||||
'freeze': '冻结',
|
||||
'unfreeze': '解冻'
|
||||
};
|
||||
|
||||
let typeText = typeMap[transaction.transaction_type] || transaction.transaction_type;
|
||||
|
||||
// 如果有冻结操作,添加到类型文字中
|
||||
if (transaction.freeze_action) {
|
||||
const freezeMap: { [key: string]: string } = {
|
||||
'freeze': '冻结',
|
||||
'unfreeze': '解冻'
|
||||
};
|
||||
typeText += `(${freezeMap[transaction.freeze_action]})`;
|
||||
}
|
||||
|
||||
return typeText;
|
||||
};
|
||||
|
||||
// 获取金额显示(带符号)
|
||||
const get_amount_display = (transaction: Transaction) => {
|
||||
const isPositive = transaction.transaction_type === 'income';
|
||||
const prefix = isPositive ? '+' : '-';
|
||||
return `${prefix}${format_amount(transaction.amount)}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<View className="wallet_page">
|
||||
{/* 钱包主卡片 */}
|
||||
<View className="wallet_main_card">
|
||||
{/* 头部信息 */}
|
||||
<View className="card_header">
|
||||
<Text className="header_title">我的现金</Text>
|
||||
<Text className="modify_password">修改交易密码</Text>
|
||||
</View>
|
||||
|
||||
{/* 余额显示 */}
|
||||
<View className="balance_display">
|
||||
<View className="amount_section">
|
||||
<View className="amount_container">
|
||||
<Text className="currency_symbol">¥</Text>
|
||||
<View className="amount_group">
|
||||
<Text className="main_amount">{Math.floor(wallet_info.balance)}</Text>
|
||||
<Text className="decimal_amount">.{((wallet_info.balance % 1) * 100).toFixed(0).padStart(2, '0')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Button className="withdraw_btn" onClick={handle_withdraw}>
|
||||
提现
|
||||
</Button>
|
||||
</View>
|
||||
<Text className="available_amount">可提现金额:¥{format_amount(wallet_info.balance)}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 功能按钮区域 */}
|
||||
<View className="function_buttons">
|
||||
<View className="function_item">
|
||||
<View className="function_icon">
|
||||
<Text className="icon_text">📄</Text>
|
||||
</View>
|
||||
<Text className="function_text">全部账单</Text>
|
||||
</View>
|
||||
<View className="function_item">
|
||||
<View className="function_icon">
|
||||
<Text className="icon_text">🔍</Text>
|
||||
</View>
|
||||
<Text className="function_text">查询交易</Text>
|
||||
</View>
|
||||
<View className="function_item">
|
||||
<View className="function_icon">
|
||||
<Text className="icon_text">⬇️</Text>
|
||||
</View>
|
||||
<Text className="function_text">下载账单</Text>
|
||||
</View>
|
||||
<View className="function_item">
|
||||
<View className="function_icon">
|
||||
<Text className="icon_text">💬</Text>
|
||||
</View>
|
||||
<Text className="function_text">客服中心</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 现金明细 */}
|
||||
<View className="transaction_history">
|
||||
{/* 标题栏 */}
|
||||
<View className="history_header">
|
||||
<Text className="history_title">现金明细</Text>
|
||||
<View className="month_selector">
|
||||
<Text className="current_month">2025-09</Text>
|
||||
<Text className="dropdown_arrow">⌄</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 交易记录列表 */}
|
||||
<View className="transaction_list">
|
||||
{loading_transactions ? (
|
||||
<View className="loading_state">
|
||||
<Text className="loading_text">加载中...</Text>
|
||||
</View>
|
||||
) : transactions.length > 0 ? (
|
||||
transactions.map((transaction) => {
|
||||
const timeInfo = format_time(transaction.create_time);
|
||||
return (
|
||||
<View key={transaction.id} className="transaction_item">
|
||||
<View className="transaction_left">
|
||||
<Text className="transaction_title">
|
||||
{get_transaction_type_text(transaction)}
|
||||
</Text>
|
||||
<View className="transaction_time">
|
||||
<Text className="transaction_date">{timeInfo.date}</Text>
|
||||
<Text className="transaction_clock">{timeInfo.time}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View className="transaction_right">
|
||||
<Text className="transaction_amount">
|
||||
{get_amount_display(transaction)}
|
||||
</Text>
|
||||
<Text className="balance_info">
|
||||
余额 ¥{format_amount(wallet_info.balance)}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<View className="empty_state">
|
||||
<Text className="empty_text">暂无交易记录</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 提现弹窗 */}
|
||||
<CommonPopup
|
||||
visible={show_withdraw_popup}
|
||||
onClose={() => set_show_withdraw_popup(false)}
|
||||
onConfirm={submit_withdraw}
|
||||
title="申请提现"
|
||||
className="withdraw_popup"
|
||||
>
|
||||
<View className="popup_content">
|
||||
<View className="form_section">
|
||||
{/* 提现金额 */}
|
||||
<View className="form_item">
|
||||
<Text className="form_label">提现金额</Text>
|
||||
<View className="input_wrapper">
|
||||
<Text className="currency_symbol">¥</Text>
|
||||
<Input
|
||||
className="amount_input with_symbol"
|
||||
type="digit"
|
||||
placeholder="请输入提现金额"
|
||||
value={withdraw_amount}
|
||||
onInput={(e) => set_withdraw_amount(e.detail.value)}
|
||||
/>
|
||||
</View>
|
||||
<Text className="balance_tip">
|
||||
可提现余额:¥{format_amount(wallet_info.balance)}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* 提现说明 */}
|
||||
<View className="form_item">
|
||||
<Text className="form_label">提现说明</Text>
|
||||
<View className="withdraw_desc">
|
||||
<Text className="desc_text">
|
||||
提现申请提交后,我们将在1-3个工作日内处理并转账到您的账户
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</CommonPopup>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default withAuth(WalletPage);
|
||||