下载账单、优化账单查询

This commit is contained in:
2025-09-28 22:45:30 +08:00
parent 8a3e41cef6
commit 883491f466
7 changed files with 180 additions and 57 deletions

View File

@@ -178,7 +178,27 @@ const DownloadBill: React.FC = () => {
const { transaction_sub_type } = load_transactions_params;
const { start, end } = dateRange;
const date_range = [start, end];
await httpService.post("/wallet/download_bill", {transaction_sub_type, date_range});
const res = await httpService.post("/wallet/download_bill", { transaction_sub_type, date_range });
const { fileUrl, fileName } = res.data;
// 调用下载文件接口
wx.downloadFile({
url: fileUrl, // 文件路径
success: function (res) {
// 只有200状态码表示下载成功
if (res.statusCode === 200) {
// 下载成功后可以使用res.tempFilePath访问临时文件路径
console.log('文件下载成功,临时路径为:', res.tempFilePath);
// 保存文件到本地
wx.openDocument({
filePath: res.tempFilePath,
showMenu: true // 显示保存菜单
});
}
},
fail: function (err) {
console.error('文件下载失败:', err);
}
});
} catch (error) {
console.error(error);
}
@@ -187,7 +207,7 @@ const DownloadBill: React.FC = () => {
<View className="download_bill_page">
<View className="hint_content">
<Text> </Text>
<Text className="button_text"></Text>
{/* <Text className="button_text">示例文件</Text> */}
</View>
<View className="form_container">
{/* <View className="form_item">
@@ -226,9 +246,8 @@ const DownloadBill: React.FC = () => {
</View>
<View
className={`option_button ${
dateType === "month" ? "active" : ""
}`}
className={`option_button ${dateType === "month" ? "active" : ""
}`}
onClick={() => {
selectDateRange("month");
}}
@@ -236,9 +255,8 @@ const DownloadBill: React.FC = () => {
</View>
<View
className={`option_button ${
dateType === "custom" ? "active" : ""
}`}
className={`option_button ${dateType === "custom" ? "active" : ""
}`}
onClick={() => {
selectDateRange("custom");
}}

View File

@@ -2,41 +2,71 @@ import React, { useState, useEffect } from "react";
import { View, Text } from "@tarojs/components";
import "./index.scss";
import httpService from "@/services/httpService";
import Taro from "@tarojs/taro";
interface BillRecord {
id: number;
file_name: string;
download_url: string;
file_size: number;
create_time: string;
expire_time: string;
bill_date_range_start: string;
bill_date_range_end: string;
bill_transaction_type: string;
bill_transaction_sub_type: string;
date_range_desc: string;
transaction_type_desc: string;
transaction_sub_type_desc: string;
}
const DownloadBillRecords: React.FC = () => {
const [records, setRecords] = useState<BillRecord[]>([]);
const [params, setParams] = useState({
page: 1,
limit: 20,
});
useEffect(() => {
fetchRecords();
}, []);
const fetchRecords = async () => {
try {
const response = await httpService.post<{ rows: BillRecord[] }>('/wallet/download_history', params);
setRecords(response.data.rows);
} catch (error) {
console.log(error);
Taro.showToast({
title: '获取账单记录失败',
icon: 'none',
duration: 2000,
});
}
};
return (
<View className="download-bill-records-page">
<View className="records-container">
<View className="record-item">
<View className="title-text"></View>
<View className="info-item">
<Text></Text>
<Text>2025912 19:03:06</Text>
</View>
<View className="info-item">
<Text></Text>
<Text>2025912 19:03:06 2025912 19:03:06</Text>
</View>
<View className="info-item">
<Text></Text>
<Text className="btn"></Text>
</View>
</View>
<View className="record-item">
<View className="title-text"></View>
<View className="info-item">
<Text></Text>
<Text>2025912 19:03:06</Text>
</View>
<View className="info-item">
<Text></Text>
<Text>2025912 19:03:06 2025912 19:03:06</Text>
</View>
<View className="info-item">
<Text></Text>
<Text className="btn"></Text>
</View>
</View>
{
records.map((record) => (
<View className="record-item" key={record.id}>
<View className="title-text">{record.file_name}</View>
<View className="info-item">
<Text></Text>
<Text>{record.create_time}</Text>
</View>
<View className="info-item">
<Text></Text>
<Text>{record.date_range_desc}</Text>
</View>
<View className="info-item">
<Text></Text>
<Text className="btn"></Text>
</View>
</View>
))
}
</View>
<View className="tips">7</View>
</View>

View File

@@ -23,8 +23,28 @@ const ValidPhone: React.FC = () => {
};
const handleConfirm = async () => {
// TODO: 校验验证码
Taro.navigateTo({ url: `/user_pages/setTransactionPassword/index?type=reset&phone=${formData.phone}&sms_code=${formData.sms_code}` });
const isValid = await validSMSCode();
if (isValid) {
Taro.navigateTo({ url: `/user_pages/setTransactionPassword/index?type=reset&phone=${formData.phone}&sms_code=${formData.sms_code}` });
}
};
const validSMSCode = async () => {
const { phone, sms_code } = formData;
try {
const res = await httpService.post("/wallet/verify_sms_code", { phone, sms_code, type: "reset_password" });
const { verified } = res.data;
if (verified) {
return true;
} else {
Taro.showToast({ title: "验证码校验失败", icon: "none" });
return false;
}
} catch (error) {
console.log(error);
Taro.showToast({ title: "验证码校验失败", icon: "none" });
return false;
}
};
const getSMSCode = async () => {

View File

@@ -146,7 +146,6 @@
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;
@@ -154,6 +153,9 @@
align-items: center;
padding: 12px 20px;
border-bottom: 0.5px solid rgba(120, 120, 128, 0.12);
position: sticky;
top: 0;
background-color: #fff;
.history_title {
font-size: 16px;

View File

@@ -5,6 +5,7 @@ import "./index.scss";
import { CommonPopup } from "@/components";
import httpService from "@/services/httpService";
import { withAuth } from "@/components";
import { PopupPicker } from "@/components/Picker/index";
// 交易记录类型
interface Transaction {
@@ -111,6 +112,16 @@ const WalletPage: React.FC = () => {
// 交易记录过滤状态
const [showFilterPopup, setShowFilterPopup] = useState(false);
const [showMonthPicker, setShowMonthPicker] = useState(false);
const [filterParams, setFilterParams] = useState({
type: TransactionType.All,
transaction_sub_type: TransactionSubType.All,
});
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, "0");
const [load_transactions_params, set_load_transactions_params] =
useState<TransactionLoadParams>({
@@ -119,16 +130,31 @@ const WalletPage: React.FC = () => {
type: TransactionType.All,
transaction_sub_type: TransactionSubType.All,
keyword: "",
date: "",
date: `${year}-${month}`
});
useEffect(() => {
load_transactions();
}, [load_transactions_params]);
// 页面显示时加载数据
useDidShow(() => {
load_wallet_data();
load_transactions();
check_password_status();
});
const modify_load_transactions_params = () => {
const { type, transaction_sub_type } = filterParams;
set_load_transactions_params((prev) => {
return {
...prev,
type,
transaction_sub_type,
}
})
};
const check_password_status = async () => {
try {
const res = await httpService.post("/wallet/check_password_status");
@@ -182,11 +208,10 @@ const WalletPage: React.FC = () => {
// 加载交易记录
const load_transactions = async () => {
setShowFilterPopup(false);
set_load_transactions_params({ ...load_transactions_params, page: 1 });
// set_load_transactions_params({ ...load_transactions_params, page: 1 });
try {
set_loading_transactions(true);
console.log("开始加载交易记录...");
const response = await httpService.post("/wallet/transactions", {
...load_transactions_params,
});
@@ -327,6 +352,7 @@ const WalletPage: React.FC = () => {
// 格式化时间显示
const format_time = (time: string) => {
time = time.replace(/-/g, "/");
const date = new Date(time);
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
@@ -380,6 +406,14 @@ const WalletPage: React.FC = () => {
setShowFilterPopup(true);
};
const handleFilterCancel = () => {
setShowFilterPopup(false);
setFilterParams({
type: load_transactions_params.type,
transaction_sub_type: load_transactions_params.transaction_sub_type,
});
};
return (
<View className="wallet_page">
{/* 钱包主卡片 */}
@@ -465,8 +499,8 @@ const WalletPage: React.FC = () => {
{/* 标题栏 */}
<View className="history_header">
<Text className="history_title"></Text>
<View className="month_selector">
<Text className="current_month">2025-09</Text>
<View className="month_selector" onClick={() => setShowMonthPicker(true)}>
<Text className="current_month">{load_transactions_params.date}</Text>
</View>
</View>
@@ -557,12 +591,30 @@ const WalletPage: React.FC = () => {
</View>
</View>
</CommonPopup>
{/* 选择月份弹窗 */}
{showMonthPicker && (
<PopupPicker
visible={showMonthPicker}
setvisible={setShowMonthPicker}
value={[
Number(load_transactions_params.date!.split("-")[0]),
Number(load_transactions_params.date!.split("-")[1])
]}
type="month"
onChange={(e) => {
const [year, month] = e;
set_load_transactions_params({
...load_transactions_params,
date: `${year}-${String(month).padStart(2, "0")}`,
});
}}
/>
)}
{/* 筛选账单弹窗 */}
<CommonPopup
visible={showFilterPopup}
onClose={() => setShowFilterPopup(false)}
onConfirm={load_transactions}
onClose={handleFilterCancel}
onConfirm={modify_load_transactions_params}
title="选择筛选项"
className="filter_popup"
>
@@ -575,14 +627,14 @@ const WalletPage: React.FC = () => {
(option: Option<TransactionType>) => (
<View
className={
load_transactions_params.type === option.value
filterParams.type === option.value
? "option_item active"
: "option_item"
}
key={option.value}
onClick={() => {
set_load_transactions_params({
...load_transactions_params,
setFilterParams({
...filterParams,
type: option.value,
});
}}
@@ -600,15 +652,15 @@ const WalletPage: React.FC = () => {
(option: Option<TransactionSubType>) => (
<View
className={
load_transactions_params.transaction_sub_type ===
filterParams.transaction_sub_type ===
option.value
? "option_item active"
: "option_item"
}
key={option.value}
onClick={() => {
set_load_transactions_params({
...load_transactions_params,
setFilterParams({
...filterParams,
transaction_sub_type: option.value,
});
}}

View File

@@ -95,7 +95,7 @@
}
.tips-container {
padding: 20px 20px 0;
padding: 20px 20px;
.title-text {
font-weight: 600;

View File

@@ -253,6 +253,7 @@ const Withdrawal: React.FC = () => {
<Text>4. 使</Text>
</View>
</View>
{/* 提现输入密码弹窗 */}
<CommonPopup
visible={show_withdraw_popup}