From 9ba0b8eb8aeafb502fd426cbb85828e036c9fcb2 Mon Sep 17 00:00:00 2001 From: Ultrame <1019265060@qq.com> Date: Fri, 26 Sep 2025 00:01:27 +0800 Subject: [PATCH] =?UTF-8?q?=E6=97=A5=E6=9C=9F=E8=8C=83=E5=9B=B4=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E5=99=A8=E3=80=81=E8=B4=A6=E5=8D=95=E7=AD=9B=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CalendarDialog/DialogCalendarCard.tsx | 28 ++-- .../Picker/CalendarUI/CalendarUI.tsx | 6 +- src/user_pages/wallet/index.scss | 99 +++++++++----- src/user_pages/wallet/index.tsx | 125 +++++++++++++++++- 4 files changed, 205 insertions(+), 53 deletions(-) diff --git a/src/components/Picker/CalendarDialog/DialogCalendarCard.tsx b/src/components/Picker/CalendarDialog/DialogCalendarCard.tsx index 449b28c..daff3c1 100644 --- a/src/components/Picker/CalendarDialog/DialogCalendarCard.tsx +++ b/src/components/Picker/CalendarDialog/DialogCalendarCard.tsx @@ -28,6 +28,8 @@ const DialogCalendarCard: React.FC = ({ const [selectedBackup, setSelectedBackup] = useState( Array.isArray(value) ? [...(value as Date[])] : [value as Date] ); + const [current, setCurrent] = useState(new Date()); + const [delta, setDelta] = useState(0); const calendarRef = useRef(null); const [type, setType] = useState<"year" | "month" | "time">("year"); const [selectedHour, setSelectedHour] = useState(8); @@ -56,11 +58,11 @@ const DialogCalendarCard: React.FC = ({ setPendingJump({ year, month }); setType("year"); if (searchType === "range") { - const delta = calculateMonthDifference( - selected as Date, + calculateMonthDifference( + current, new Date(year, month - 1, 1) ); - calendarRef.current?.gotoMonth(delta); + return; } setSelected(new Date(year, month - 1, 1)); @@ -77,10 +79,10 @@ const DialogCalendarCard: React.FC = ({ const minutes = minute.toString().padStart(2, "0"); const finalDate = new Date( dayjs(selected as Date).format("YYYY-MM-DD") + - " " + - hours + - ":" + - minutes + " " + + hours + + ":" + + minutes ); if (onChange) onChange(finalDate); } @@ -92,12 +94,11 @@ const DialogCalendarCard: React.FC = ({ if (!(date1 instanceof Date) || !(date2 instanceof Date)) { throw new Error("Both arguments must be Date objects"); } - + setCurrent(date1) let months = (date2.getFullYear() - date1.getFullYear()) * 12; months -= date1.getMonth(); months += date2.getMonth(); - - return months; + setDelta(months); }; const handleChange = (d: Date | Date[]) => { @@ -106,9 +107,8 @@ const DialogCalendarCard: React.FC = ({ if (d.length === 2) { return; } else if (d.length === 1) { - debugger; if (selectedBackup.length === 0 || selectedBackup.length === 2) { - setSelected([]); + setSelected([...d]); setSelectedBackup([...d]); } else { setSelected( @@ -152,6 +152,10 @@ const DialogCalendarCard: React.FC = ({ onClose(); } }; + useEffect(() => { + calendarRef.current?.gotoMonth(delta); + }, [delta]) + useEffect(() => { if (visible && value) { setSelected(value || new Date()); diff --git a/src/components/Picker/CalendarUI/CalendarUI.tsx b/src/components/Picker/CalendarUI/CalendarUI.tsx index 08f2d4a..f58f469 100644 --- a/src/components/Picker/CalendarUI/CalendarUI.tsx +++ b/src/components/Picker/CalendarUI/CalendarUI.tsx @@ -83,13 +83,11 @@ const NutUICalendar = React.forwardRef( jumpTo: (year: number, month: number) => { calendarRef.current?.jumpTo(year, month); }, - gotoMonth: (delta: number) => { - gotoMonth(delta); - }, + gotoMonth, })); const handleDateChange = (newValue: any) => { - console.log("aaaaaaaaaaaaaaaaaaaaaa", newValue); + if (type === "range") return; setSelectedValue(newValue); onChange?.(newValue as any); }; diff --git a/src/user_pages/wallet/index.scss b/src/user_pages/wallet/index.scss index 3025e31..51aad1b 100644 --- a/src/user_pages/wallet/index.scss +++ b/src/user_pages/wallet/index.scss @@ -4,26 +4,26 @@ min-height: 100vh; background-color: #f5f5f5; padding: 5px; - + .wallet_main_card { background: #000; border-radius: 20px; padding: 12px 20px 32px; margin-bottom: 8px; color: #fff; - + .card_header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 32px; - + .header_title { font-size: 12px; font-weight: 400; line-height: 1.5; } - + .modify_password { font-size: 12px; font-weight: 400; @@ -31,37 +31,37 @@ 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: 32px; font-weight: 400; 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; line-height: 0.75; } - + .decimal_amount { font-family: 'DingTalk JinBuTi', sans-serif; font-size: 32px; @@ -70,7 +70,7 @@ } } } - + .withdraw_btn { background: #fff; border: none; @@ -86,7 +86,7 @@ align-items: center; } } - + .available_amount { font-size: 12px; font-weight: 400; @@ -95,13 +95,13 @@ } } } - + .function_buttons { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; - + .function_item { background: white; border: 0.5px solid #EBEBEB; @@ -114,19 +114,19 @@ box-shadow: 0px 4px 36px 0px rgba(0, 0, 0, 0.05); height: 24px; flex: 1; - + .function_icon { width: 16px; height: 16px; display: flex; align-items: center; justify-content: center; - + .icon_text { font-size: 8px; } } - + .function_text { font-size: 12px; font-weight: 600; @@ -134,7 +134,7 @@ line-height: 1.33; white-space: nowrap; } - + &:active { background: #f8f8f8; } @@ -169,7 +169,8 @@ position: relative; padding-right: 16px; - &::before, &::after { + &::before, + &::after { content: ''; display: block; width: 2px; @@ -195,6 +196,7 @@ } .transaction_list { + .loading_state, .empty_state { padding: 40px 20px; @@ -277,21 +279,21 @@ .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%; @@ -301,17 +303,17 @@ padding: 0 10px; font-size: 14px; color: #333; - + &:focus { border-color: #667eea; box-shadow: none; } - + &::placeholder { color: #bbb; } } - + .currency_symbol { position: absolute; left: 10px; @@ -321,24 +323,24 @@ 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; @@ -349,3 +351,40 @@ } } } + +// 过滤弹窗 +.filter_popup { + padding: 20px; + + .popup_content { + .form_section { + .form_item { + margin-bottom: 20px; + .form_label { + display: inline-block; + font-family: PingFang SC; + font-weight: 600; + font-style: Semibold; + font-size: 16px; + margin-bottom: 20px; + } + + .options_wrapper { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px; + .option_item { + background-color: #0000000D; + text-align: center; + padding: 8px; + border-radius: 4px; + &.active { + background-color: #000000; + color: #fff; + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/user_pages/wallet/index.tsx b/src/user_pages/wallet/index.tsx index dc7f218..3429f4c 100644 --- a/src/user_pages/wallet/index.tsx +++ b/src/user_pages/wallet/index.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { View, Text, Input, Button, Image } from "@tarojs/components"; import Taro, { useDidShow } from "@tarojs/taro"; import "./index.scss"; @@ -28,6 +28,71 @@ interface WalletInfo { total_withdraw?: number; } +enum TransactionType { + All = '', + Income = 'income', + Expense = 'expense', +} + +enum TransactionSubType { + All = '', + GameActivity = 'game_activity', + Withdrawal = 'withdrawal', + Refund = 'refund', + Compensation = 'compensation', +} + +interface TransactionLoadParams { + page: number; + limit: number; + type: TransactionType; + transaction_sub_type: TransactionSubType; + keyword?: string; + date?: string; +} +interface Option { + label: string; + value: T; +} + +const income_expense_options: Option[] = [ + { + label: '全部', + value: TransactionType.All + }, + { + label: '支出', + value: TransactionType.Expense + }, + { + label: '收入', + value: TransactionType.Income + }, +]; + +const transaction_type_options: Option[] = [ + { + label: '全部', + value: TransactionSubType.All + }, + { + label: '组织活动', + value: TransactionSubType.GameActivity + }, + { + label: '提现', + value: TransactionSubType.Withdrawal + }, + { + label: '退款', + value: TransactionSubType.Refund + }, + { + label: '企业赔付', + value: TransactionSubType.Compensation + }, +]; + const WalletPage: React.FC = () => { // 钱包信息状态 @@ -44,6 +109,18 @@ const WalletPage: React.FC = () => { const [transactions, set_transactions] = useState([]); const [loading_transactions, set_loading_transactions] = useState(false); + // 交易记录过滤状态 + const [showFilterPopup, setShowFilterPopup] = useState(false); + + const [load_transactions_params, set_load_transactions_params] = useState({ + page: 1, + limit: 20, + type: TransactionType.All, + transaction_sub_type: TransactionSubType.All, + keyword: "", + date: "" + }); + // 页面显示时加载数据 useDidShow(() => { load_wallet_data(); @@ -82,14 +159,13 @@ const WalletPage: React.FC = () => { // 加载交易记录 const load_transactions = async () => { + setShowFilterPopup(false); + set_load_transactions_params({ ...load_transactions_params, page: 1 }); try { set_loading_transactions(true); console.log("开始加载交易记录..."); - const response = await httpService.post("/wallet/transactions", { - page: 1, - limit: 20 - }); + const response = await httpService.post("/wallet/transactions", { ...load_transactions_params }); console.log("交易记录响应:", response); @@ -165,7 +241,7 @@ const WalletPage: React.FC = () => { }); // 根据后端返回的数据结构解析参数 - const { mch_id, app_id, package_info, open_id } = response.data; + const { mch_id, app_id, package_info, open_id } = response.data; console.log("/wallet/withdraw:", response.data); @@ -255,6 +331,10 @@ const WalletPage: React.FC = () => { return `${prefix}${format_amount(transaction.amount)}`; }; + const show_filter_popup = () => { + setShowFilterPopup(true) + } + return ( {/* 钱包主卡片 */} @@ -285,7 +365,7 @@ const WalletPage: React.FC = () => { {/* 功能按钮区域 */} - + 全部账单 @@ -392,6 +472,37 @@ const WalletPage: React.FC = () => { + + {/* 筛选账单弹窗 */} + setShowFilterPopup(false)} + onConfirm={load_transactions} + title="选择筛选项" + className="filter_popup" + > + + + + 收支类型 + + {income_expense_options.map((option: Option) => ( + { set_load_transactions_params({ ...load_transactions_params, type: option.value }) }}>{option.label} + ))} + + + + 交易类型 + + {transaction_type_options.map((option: Option) => ( + { set_load_transactions_params({ ...load_transactions_params, transaction_sub_type: option.value }) }}>{option.label} + ))} + + + + + + ); };