diff --git a/src/dealer/withdraw/__tests__/withdraw.test.tsx b/src/dealer/withdraw/__tests__/withdraw.test.tsx new file mode 100644 index 0000000..c3aeab9 --- /dev/null +++ b/src/dealer/withdraw/__tests__/withdraw.test.tsx @@ -0,0 +1,184 @@ +import React from 'react' +import { render, fireEvent, waitFor } from '@testing-library/react' +import DealerWithdraw from '../index' +import { useDealerUser } from '@/hooks/useDealerUser' +import * as withdrawAPI from '@/api/shop/shopDealerWithdraw' + +// Mock dependencies +jest.mock('@/hooks/useDealerUser') +jest.mock('@/api/shop/shopDealerWithdraw') +jest.mock('@tarojs/taro', () => ({ + showToast: jest.fn(), + getStorageSync: jest.fn(() => 123), +})) + +const mockUseDealerUser = useDealerUser as jest.MockedFunction +const mockAddShopDealerWithdraw = withdrawAPI.addShopDealerWithdraw as jest.MockedFunction +const mockPageShopDealerWithdraw = withdrawAPI.pageShopDealerWithdraw as jest.MockedFunction + +describe('DealerWithdraw', () => { + const mockDealerUser = { + userId: 123, + money: '10000.00', + realName: '测试用户', + mobile: '13800138000' + } + + beforeEach(() => { + mockUseDealerUser.mockReturnValue({ + dealerUser: mockDealerUser, + loading: false, + error: null, + refresh: jest.fn() + }) + + mockPageShopDealerWithdraw.mockResolvedValue({ + list: [], + count: 0 + }) + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + test('应该正确显示可提现余额', () => { + const { getByText } = render() + expect(getByText('10000.00')).toBeInTheDocument() + expect(getByText('可提现余额')).toBeInTheDocument() + }) + + test('应该验证最低提现金额', async () => { + mockAddShopDealerWithdraw.mockResolvedValue('success') + + const { getByPlaceholderText, getByText } = render() + + // 输入低于最低金额的数值 + const amountInput = getByPlaceholderText('请输入提现金额') + fireEvent.change(amountInput, { target: { value: '50' } }) + + // 选择提现方式 + const wechatRadio = getByText('微信钱包') + fireEvent.click(wechatRadio) + + // 提交表单 + const submitButton = getByText('申请提现') + fireEvent.click(submitButton) + + await waitFor(() => { + expect(require('@tarojs/taro').showToast).toHaveBeenCalledWith({ + title: '最低提现金额为100元', + icon: 'error' + }) + }) + }) + + test('应该验证提现金额不超过可用余额', async () => { + const { getByPlaceholderText, getByText } = render() + + // 输入超过可用余额的金额 + const amountInput = getByPlaceholderText('请输入提现金额') + fireEvent.change(amountInput, { target: { value: '20000' } }) + + // 选择提现方式 + const wechatRadio = getByText('微信钱包') + fireEvent.click(wechatRadio) + + // 提交表单 + const submitButton = getByText('申请提现') + fireEvent.click(submitButton) + + await waitFor(() => { + expect(require('@tarojs/taro').showToast).toHaveBeenCalledWith({ + title: '提现金额超过可用余额', + icon: 'error' + }) + }) + }) + + test('应该验证支付宝账户信息完整性', async () => { + const { getByPlaceholderText, getByText } = render() + + // 输入有效金额 + const amountInput = getByPlaceholderText('请输入提现金额') + fireEvent.change(amountInput, { target: { value: '1000' } }) + + // 选择支付宝提现 + const alipayRadio = getByText('支付宝') + fireEvent.click(alipayRadio) + + // 只填写账号,不填写姓名 + const accountInput = getByPlaceholderText('请输入支付宝账号') + fireEvent.change(accountInput, { target: { value: 'test@alipay.com' } }) + + // 提交表单 + const submitButton = getByText('申请提现') + fireEvent.click(submitButton) + + await waitFor(() => { + expect(require('@tarojs/taro').showToast).toHaveBeenCalledWith({ + title: '请填写完整的支付宝信息', + icon: 'error' + }) + }) + }) + + test('应该成功提交微信提现申请', async () => { + mockAddShopDealerWithdraw.mockResolvedValue('success') + + const { getByPlaceholderText, getByText } = render() + + // 输入有效金额 + const amountInput = getByPlaceholderText('请输入提现金额') + fireEvent.change(amountInput, { target: { value: '1000' } }) + + // 选择微信提现 + const wechatRadio = getByText('微信钱包') + fireEvent.click(wechatRadio) + + // 提交表单 + const submitButton = getByText('申请提现') + fireEvent.click(submitButton) + + await waitFor(() => { + expect(mockAddShopDealerWithdraw).toHaveBeenCalledWith({ + userId: 123, + money: '1000', + payType: 10, + applyStatus: 10, + platform: 'MiniProgram' + }) + }) + + await waitFor(() => { + expect(require('@tarojs/taro').showToast).toHaveBeenCalledWith({ + title: '提现申请已提交', + icon: 'success' + }) + }) + }) + + test('快捷金额按钮应该正常工作', () => { + const { getByText, getByPlaceholderText } = render() + + // 点击快捷金额按钮 + const quickAmountButton = getByText('500') + fireEvent.click(quickAmountButton) + + // 验证金额输入框的值 + const amountInput = getByPlaceholderText('请输入提现金额') as HTMLInputElement + expect(amountInput.value).toBe('500') + }) + + test('全部按钮应该设置为可用余额', () => { + const { getByText, getByPlaceholderText } = render() + + // 点击全部按钮 + const allButton = getByText('全部') + fireEvent.click(allButton) + + // 验证金额输入框的值 + const amountInput = getByPlaceholderText('请输入提现金额') as HTMLInputElement + expect(amountInput.value).toBe('10000.00') + }) +}) diff --git a/src/dealer/withdraw/debug.tsx b/src/dealer/withdraw/debug.tsx new file mode 100644 index 0000000..167d7ba --- /dev/null +++ b/src/dealer/withdraw/debug.tsx @@ -0,0 +1,80 @@ +import React, { useState } from 'react' +import { View, Text } from '@tarojs/components' +import { Tabs, Button } from '@nutui/nutui-react-taro' + +/** + * 提现功能调试组件 + * 用于测试 Tabs 组件的点击和切换功能 + */ +const WithdrawDebug: React.FC = () => { + const [activeTab, setActiveTab] = useState('0') + const [clickCount, setClickCount] = useState(0) + + // Tab 切换处理函数 + const handleTabChange = (value: string | number) => { + console.log('Tab切换:', { from: activeTab, to: value, type: typeof value }) + setActiveTab(value) + setClickCount(prev => prev + 1) + } + + // 手动切换测试 + const manualSwitch = (tab: string | number) => { + console.log('手动切换到:', tab) + setActiveTab(tab) + setClickCount(prev => prev + 1) + } + + return ( + + + 调试信息 + 当前Tab: {String(activeTab)} + 切换次数: {clickCount} + Tab类型: {typeof activeTab} + + + + 手动切换测试 + + + + + + + + + + + 申请提现页面内容 + + 当前Tab值: {String(activeTab)} + + + + + + + 提现记录页面内容 + + 当前Tab值: {String(activeTab)} + + + + + + + + 事件日志 + + 请查看控制台输出以获取详细的切换日志 + + + + ) +} + +export default WithdrawDebug diff --git a/src/dealer/withdraw/index.tsx b/src/dealer/withdraw/index.tsx index f6dddf3..7ab011f 100644 --- a/src/dealer/withdraw/index.tsx +++ b/src/dealer/withdraw/index.tsx @@ -1,5 +1,5 @@ -import React, { useState, useRef, useEffect, useCallback } from 'react' -import { View, Text } from '@tarojs/components' +import React, {useState, useRef, useEffect, useCallback} from 'react' +import {View, Text} from '@tarojs/components' import { Cell, Space, @@ -14,19 +14,19 @@ import { Loading, PullToRefresh } from '@nutui/nutui-react-taro' -import { Wallet } from '@nutui/icons-react-taro' -import { businessGradients } from '@/styles/gradients' +import {Wallet} from '@nutui/icons-react-taro' +import {businessGradients} from '@/styles/gradients' import Taro from '@tarojs/taro' -import { useDealerUser } from '@/hooks/useDealerUser' -import { pageShopDealerWithdraw, addShopDealerWithdraw } from '@/api/shop/shopDealerWithdraw' -import type { ShopDealerWithdraw } from '@/api/shop/shopDealerWithdraw/model' +import {useDealerUser} from '@/hooks/useDealerUser' +import {pageShopDealerWithdraw, addShopDealerWithdraw} from '@/api/shop/shopDealerWithdraw' +import type {ShopDealerWithdraw} from '@/api/shop/shopDealerWithdraw/model' interface WithdrawRecordWithDetails extends ShopDealerWithdraw { accountDisplay?: string } const DealerWithdraw: React.FC = () => { - const [activeTab, setActiveTab] = useState('0') + const [activeTab, setActiveTab] = useState('0') const [selectedAccount, setSelectedAccount] = useState('') const [loading, setLoading] = useState(false) const [refreshing, setRefreshing] = useState(false) @@ -35,17 +35,28 @@ const DealerWithdraw: React.FC = () => { const [withdrawRecords, setWithdrawRecords] = useState([]) const formRef = useRef(null) - const { dealerUser } = useDealerUser() + const {dealerUser} = useDealerUser() + + // Tab 切换处理函数 + const handleTabChange = (value: string | number) => { + console.log('Tab切换到:', value) + setActiveTab(value) + + // 如果切换到提现记录页面,刷新数据 + if (String(value) === '1') { + fetchWithdrawRecords() + } + } // 获取可提现余额 const fetchBalance = useCallback(async () => { - console.log(dealerUser,'dealerUser...') + console.log(dealerUser, 'dealerUser...') try { - setAvailableAmount(dealerUser?.money || '0.00') + setAvailableAmount(dealerUser?.money || '0.00') } catch (error) { console.error('获取余额失败:', error) } - }, []) + }, [dealerUser]) // 获取提现记录 const fetchWithdrawRecords = useCallback(async () => { @@ -106,21 +117,31 @@ const DealerWithdraw: React.FC = () => { const getStatusText = (status?: number) => { switch (status) { - case 40: return '已到账' - case 20: return '审核通过' - case 10: return '待审核' - case 30: return '已驳回' - default: return '未知' + case 40: + return '已到账' + case 20: + return '审核通过' + case 10: + return '待审核' + case 30: + return '已驳回' + default: + return '未知' } } const getStatusColor = (status?: number) => { switch (status) { - case 40: return 'success' - case 20: return 'success' - case 10: return 'warning' - case 30: return 'danger' - default: return 'default' + case 40: + return 'success' + case 20: + return 'success' + case 10: + return 'warning' + case 30: + return 'danger' + default: + return 'default' } } @@ -133,9 +154,25 @@ const DealerWithdraw: React.FC = () => { return } + if (!values.accountType) { + Taro.showToast({ + title: '请选择提现方式', + icon: 'error' + }) + return + } + // 验证提现金额 const amount = parseFloat(values.amount) - const available = parseFloat(availableAmount.replace(',', '')) + const available = parseFloat(availableAmount.replace(/,/g, '')) + + if (isNaN(amount) || amount <= 0) { + Taro.showToast({ + title: '请输入有效的提现金额', + icon: 'error' + }) + return + } if (amount < 100) { Taro.showToast({ @@ -153,6 +190,25 @@ const DealerWithdraw: React.FC = () => { return } + // 验证账户信息 + if (values.accountType === 'alipay') { + if (!values.account || !values.accountName) { + Taro.showToast({ + title: '请填写完整的支付宝信息', + icon: 'error' + }) + return + } + } else if (values.accountType === 'bank') { + if (!values.account || !values.accountName || !values.bankName) { + Taro.showToast({ + title: '请填写完整的银行卡信息', + icon: 'error' + }) + return + } + } + try { setSubmitting(true) @@ -160,7 +216,7 @@ const DealerWithdraw: React.FC = () => { userId: dealerUser.userId, money: values.amount, payType: values.accountType === 'wechat' ? 10 : - values.accountType === 'alipay' ? 20 : 30, + values.accountType === 'alipay' ? 20 : 30, applyStatus: 10, // 待审核 platform: 'MiniProgram' } @@ -206,11 +262,11 @@ const DealerWithdraw: React.FC = () => { const quickAmounts = ['100', '300', '500', '1000'] const setQuickAmount = (amount: string) => { - formRef.current?.setFieldsValue({ amount }) + formRef.current?.setFieldsValue({amount}) } const setAllAmount = () => { - formRef.current?.setFieldsValue({ amount: availableAmount.replace(',', '') }) + formRef.current?.setFieldsValue({amount: availableAmount.replace(/,/g, '')}) } // 格式化金额 @@ -240,7 +296,7 @@ const DealerWithdraw: React.FC = () => { - + { { + // 实时验证提现金额 + const amount = parseFloat(value) + const available = parseFloat(availableAmount.replace(/,/g, '')) + if (!isNaN(amount) && amount > available) { + // 可以在这里添加实时提示,但不阻止输入 + } + }} /> @@ -308,10 +372,10 @@ const DealerWithdraw: React.FC = () => { {selectedAccount === 'alipay' && ( <> - + - + )} @@ -319,13 +383,13 @@ const DealerWithdraw: React.FC = () => { {selectedAccount === 'bank' && ( <> - + - + - + )} @@ -354,60 +418,64 @@ const DealerWithdraw: React.FC = () => { ) - const renderWithdrawRecords = () => ( - - - {loading ? ( - - - 加载中... - - ) : withdrawRecords.length > 0 ? ( - withdrawRecords.map(record => ( - - - - - 提现金额:¥{record.money} - - - 提现账户:{record.accountDisplay} - + const renderWithdrawRecords = () => { + console.log('渲染提现记录:', {loading, recordsCount: withdrawRecords.length, dealerUserId: dealerUser?.userId}) + + return ( + + + {loading ? ( + + + 加载中... + + ) : withdrawRecords.length > 0 ? ( + withdrawRecords.map(record => ( + + + + + 提现金额:¥{record.money} + + + 提现账户:{record.accountDisplay} + + + + {getStatusText(record.applyStatus)} + - - {getStatusText(record.applyStatus)} - - - - 申请时间:{record.createTime} - {record.auditTime && ( - - 审核时间:{new Date(record.auditTime).toLocaleString()} - - )} - {record.rejectReason && ( - - 驳回原因:{record.rejectReason} - - )} + + 申请时间:{record.createTime} + {record.auditTime && ( + + 审核时间:{new Date(record.auditTime).toLocaleString()} + + )} + {record.rejectReason && ( + + 驳回原因:{record.rejectReason} + + )} + - - )) - ) : ( - - )} - - - ) + )) + ) : ( + + )} + + + ) + } if (!dealerUser) { return ( - + 加载中... ) @@ -415,12 +483,12 @@ const DealerWithdraw: React.FC = () => { return ( - setActiveTab}> + {renderWithdrawForm()} - + {renderWithdrawRecords()}