下载账单、优化账单查询
This commit is contained in:
@@ -178,7 +178,27 @@ const DownloadBill: React.FC = () => {
|
|||||||
const { transaction_sub_type } = load_transactions_params;
|
const { transaction_sub_type } = load_transactions_params;
|
||||||
const { start, end } = dateRange;
|
const { start, end } = dateRange;
|
||||||
const date_range = [start, end];
|
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) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
@@ -187,7 +207,7 @@ const DownloadBill: React.FC = () => {
|
|||||||
<View className="download_bill_page">
|
<View className="download_bill_page">
|
||||||
<View className="hint_content">
|
<View className="hint_content">
|
||||||
<Text>最长可导出三个月的账单 </Text>
|
<Text>最长可导出三个月的账单 </Text>
|
||||||
<Text className="button_text">示例文件</Text>
|
{/* <Text className="button_text">示例文件</Text> */}
|
||||||
</View>
|
</View>
|
||||||
<View className="form_container">
|
<View className="form_container">
|
||||||
{/* <View className="form_item">
|
{/* <View className="form_item">
|
||||||
@@ -226,9 +246,8 @@ const DownloadBill: React.FC = () => {
|
|||||||
近一周
|
近一周
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View
|
||||||
className={`option_button ${
|
className={`option_button ${dateType === "month" ? "active" : ""
|
||||||
dateType === "month" ? "active" : ""
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
selectDateRange("month");
|
selectDateRange("month");
|
||||||
}}
|
}}
|
||||||
@@ -236,9 +255,8 @@ const DownloadBill: React.FC = () => {
|
|||||||
近一月
|
近一月
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View
|
||||||
className={`option_button ${
|
className={`option_button ${dateType === "custom" ? "active" : ""
|
||||||
dateType === "custom" ? "active" : ""
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
selectDateRange("custom");
|
selectDateRange("custom");
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -2,41 +2,71 @@ import React, { useState, useEffect } from "react";
|
|||||||
import { View, Text } from "@tarojs/components";
|
import { View, Text } from "@tarojs/components";
|
||||||
|
|
||||||
import "./index.scss";
|
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 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 (
|
return (
|
||||||
<View className="download-bill-records-page">
|
<View className="download-bill-records-page">
|
||||||
<View className="records-container">
|
<View className="records-container">
|
||||||
<View className="record-item">
|
{
|
||||||
<View className="title-text">账单流水文件</View>
|
records.map((record) => (
|
||||||
<View className="info-item">
|
<View className="record-item" key={record.id}>
|
||||||
<Text>申请时间</Text>
|
<View className="title-text">{record.file_name}</View>
|
||||||
<Text>2025年9月12日 19:03:06</Text>
|
<View className="info-item">
|
||||||
</View>
|
<Text>申请时间</Text>
|
||||||
<View className="info-item">
|
<Text>{record.create_time}</Text>
|
||||||
<Text>账单范围</Text>
|
</View>
|
||||||
<Text>2025年9月12日 19:03:06 至 2025年9月12日 19:03:06</Text>
|
<View className="info-item">
|
||||||
</View>
|
<Text>账单范围</Text>
|
||||||
<View className="info-item">
|
<Text>{record.date_range_desc}</Text>
|
||||||
<Text></Text>
|
</View>
|
||||||
<Text className="btn">查看材料</Text>
|
<View className="info-item">
|
||||||
</View>
|
<Text></Text>
|
||||||
</View>
|
<Text className="btn">查看材料</Text>
|
||||||
<View className="record-item">
|
</View>
|
||||||
<View className="title-text">账单流水文件</View>
|
</View>
|
||||||
<View className="info-item">
|
))
|
||||||
<Text>申请时间</Text>
|
}
|
||||||
<Text>2025年9月12日 19:03:06</Text>
|
|
||||||
</View>
|
|
||||||
<View className="info-item">
|
|
||||||
<Text>账单范围</Text>
|
|
||||||
<Text>2025年9月12日 19:03:06 至 2025年9月12日 19:03:06</Text>
|
|
||||||
</View>
|
|
||||||
<View className="info-item">
|
|
||||||
<Text></Text>
|
|
||||||
<Text className="btn">查看材料</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
<View className="tips">出于信息安全考虑,仅保留并展示7天内的账单下载记录</View>
|
<View className="tips">出于信息安全考虑,仅保留并展示7天内的账单下载记录</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -23,8 +23,28 @@ const ValidPhone: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleConfirm = async () => {
|
const handleConfirm = async () => {
|
||||||
// TODO: 校验验证码
|
const isValid = await validSMSCode();
|
||||||
Taro.navigateTo({ url: `/user_pages/setTransactionPassword/index?type=reset&phone=${formData.phone}&sms_code=${formData.sms_code}` });
|
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 () => {
|
const getSMSCode = async () => {
|
||||||
|
|||||||
@@ -146,7 +146,6 @@
|
|||||||
border: 0.5px solid #EBEBEB;
|
border: 0.5px solid #EBEBEB;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
box-shadow: 0px 0px 36px 0px rgba(0, 0, 0, 0.1);
|
box-shadow: 0px 0px 36px 0px rgba(0, 0, 0, 0.1);
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.history_header {
|
.history_header {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -154,6 +153,9 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 12px 20px;
|
padding: 12px 20px;
|
||||||
border-bottom: 0.5px solid rgba(120, 120, 128, 0.12);
|
border-bottom: 0.5px solid rgba(120, 120, 128, 0.12);
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
|
||||||
.history_title {
|
.history_title {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import "./index.scss";
|
|||||||
import { CommonPopup } from "@/components";
|
import { CommonPopup } from "@/components";
|
||||||
import httpService from "@/services/httpService";
|
import httpService from "@/services/httpService";
|
||||||
import { withAuth } from "@/components";
|
import { withAuth } from "@/components";
|
||||||
|
import { PopupPicker } from "@/components/Picker/index";
|
||||||
|
|
||||||
// 交易记录类型
|
// 交易记录类型
|
||||||
interface Transaction {
|
interface Transaction {
|
||||||
@@ -111,6 +112,16 @@ const WalletPage: React.FC = () => {
|
|||||||
|
|
||||||
// 交易记录过滤状态
|
// 交易记录过滤状态
|
||||||
const [showFilterPopup, setShowFilterPopup] = useState(false);
|
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] =
|
const [load_transactions_params, set_load_transactions_params] =
|
||||||
useState<TransactionLoadParams>({
|
useState<TransactionLoadParams>({
|
||||||
@@ -119,16 +130,31 @@ const WalletPage: React.FC = () => {
|
|||||||
type: TransactionType.All,
|
type: TransactionType.All,
|
||||||
transaction_sub_type: TransactionSubType.All,
|
transaction_sub_type: TransactionSubType.All,
|
||||||
keyword: "",
|
keyword: "",
|
||||||
date: "",
|
date: `${year}-${month}`
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
load_transactions();
|
||||||
|
}, [load_transactions_params]);
|
||||||
// 页面显示时加载数据
|
// 页面显示时加载数据
|
||||||
useDidShow(() => {
|
useDidShow(() => {
|
||||||
load_wallet_data();
|
load_wallet_data();
|
||||||
load_transactions();
|
|
||||||
check_password_status();
|
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 () => {
|
const check_password_status = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await httpService.post("/wallet/check_password_status");
|
const res = await httpService.post("/wallet/check_password_status");
|
||||||
@@ -182,11 +208,10 @@ const WalletPage: React.FC = () => {
|
|||||||
// 加载交易记录
|
// 加载交易记录
|
||||||
const load_transactions = async () => {
|
const load_transactions = async () => {
|
||||||
setShowFilterPopup(false);
|
setShowFilterPopup(false);
|
||||||
set_load_transactions_params({ ...load_transactions_params, page: 1 });
|
// set_load_transactions_params({ ...load_transactions_params, page: 1 });
|
||||||
try {
|
try {
|
||||||
set_loading_transactions(true);
|
set_loading_transactions(true);
|
||||||
console.log("开始加载交易记录...");
|
console.log("开始加载交易记录...");
|
||||||
|
|
||||||
const response = await httpService.post("/wallet/transactions", {
|
const response = await httpService.post("/wallet/transactions", {
|
||||||
...load_transactions_params,
|
...load_transactions_params,
|
||||||
});
|
});
|
||||||
@@ -327,6 +352,7 @@ const WalletPage: React.FC = () => {
|
|||||||
|
|
||||||
// 格式化时间显示
|
// 格式化时间显示
|
||||||
const format_time = (time: string) => {
|
const format_time = (time: string) => {
|
||||||
|
time = time.replace(/-/g, "/");
|
||||||
const date = new Date(time);
|
const date = new Date(time);
|
||||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||||
const day = String(date.getDate()).padStart(2, "0");
|
const day = String(date.getDate()).padStart(2, "0");
|
||||||
@@ -380,6 +406,14 @@ const WalletPage: React.FC = () => {
|
|||||||
setShowFilterPopup(true);
|
setShowFilterPopup(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleFilterCancel = () => {
|
||||||
|
setShowFilterPopup(false);
|
||||||
|
setFilterParams({
|
||||||
|
type: load_transactions_params.type,
|
||||||
|
transaction_sub_type: load_transactions_params.transaction_sub_type,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="wallet_page">
|
<View className="wallet_page">
|
||||||
{/* 钱包主卡片 */}
|
{/* 钱包主卡片 */}
|
||||||
@@ -465,8 +499,8 @@ const WalletPage: React.FC = () => {
|
|||||||
{/* 标题栏 */}
|
{/* 标题栏 */}
|
||||||
<View className="history_header">
|
<View className="history_header">
|
||||||
<Text className="history_title">现金明细</Text>
|
<Text className="history_title">现金明细</Text>
|
||||||
<View className="month_selector">
|
<View className="month_selector" onClick={() => setShowMonthPicker(true)}>
|
||||||
<Text className="current_month">2025-09</Text>
|
<Text className="current_month">{load_transactions_params.date}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -557,12 +591,30 @@ const WalletPage: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</CommonPopup>
|
</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
|
<CommonPopup
|
||||||
visible={showFilterPopup}
|
visible={showFilterPopup}
|
||||||
onClose={() => setShowFilterPopup(false)}
|
onClose={handleFilterCancel}
|
||||||
onConfirm={load_transactions}
|
onConfirm={modify_load_transactions_params}
|
||||||
title="选择筛选项"
|
title="选择筛选项"
|
||||||
className="filter_popup"
|
className="filter_popup"
|
||||||
>
|
>
|
||||||
@@ -575,14 +627,14 @@ const WalletPage: React.FC = () => {
|
|||||||
(option: Option<TransactionType>) => (
|
(option: Option<TransactionType>) => (
|
||||||
<View
|
<View
|
||||||
className={
|
className={
|
||||||
load_transactions_params.type === option.value
|
filterParams.type === option.value
|
||||||
? "option_item active"
|
? "option_item active"
|
||||||
: "option_item"
|
: "option_item"
|
||||||
}
|
}
|
||||||
key={option.value}
|
key={option.value}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
set_load_transactions_params({
|
setFilterParams({
|
||||||
...load_transactions_params,
|
...filterParams,
|
||||||
type: option.value,
|
type: option.value,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
@@ -600,15 +652,15 @@ const WalletPage: React.FC = () => {
|
|||||||
(option: Option<TransactionSubType>) => (
|
(option: Option<TransactionSubType>) => (
|
||||||
<View
|
<View
|
||||||
className={
|
className={
|
||||||
load_transactions_params.transaction_sub_type ===
|
filterParams.transaction_sub_type ===
|
||||||
option.value
|
option.value
|
||||||
? "option_item active"
|
? "option_item active"
|
||||||
: "option_item"
|
: "option_item"
|
||||||
}
|
}
|
||||||
key={option.value}
|
key={option.value}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
set_load_transactions_params({
|
setFilterParams({
|
||||||
...load_transactions_params,
|
...filterParams,
|
||||||
transaction_sub_type: option.value,
|
transaction_sub_type: option.value,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tips-container {
|
.tips-container {
|
||||||
padding: 20px 20px 0;
|
padding: 20px 20px;
|
||||||
|
|
||||||
.title-text {
|
.title-text {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|||||||
@@ -253,6 +253,7 @@ const Withdrawal: React.FC = () => {
|
|||||||
<Text>4. 用户在使用提现服务前,应充分了解并同意上述规则。</Text>
|
<Text>4. 用户在使用提现服务前,应充分了解并同意上述规则。</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 提现输入密码弹窗 */}
|
{/* 提现输入密码弹窗 */}
|
||||||
<CommonPopup
|
<CommonPopup
|
||||||
visible={show_withdraw_popup}
|
visible={show_withdraw_popup}
|
||||||
|
|||||||
Reference in New Issue
Block a user