import React, {useState, useRef, useEffect, useCallback} from 'react' import {View, Text} from '@tarojs/components' import { Cell, Space, Button, Form, Input, CellGroup, Radio, Tabs, Tag, Empty, Loading, PullToRefresh } from '@nutui/nutui-react-taro' 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' interface WithdrawRecordWithDetails extends ShopDealerWithdraw { accountDisplay?: string } const extractPackageInfo = (result: unknown): string | null => { if (!result || typeof result !== 'object') return null const r = result as any return ( r.package_info ?? r.packageInfo ?? r.package ?? null ) } const canRequestMerchantTransferConfirm = (): boolean => { try { if (typeof (Taro as any).getEnv === 'function' && (Taro as any).ENV_TYPE) { const env = (Taro as any).getEnv() if (env !== (Taro as any).ENV_TYPE.WEAPP) return false } const api = (globalThis as any).wx?.requestMerchantTransfer || (Taro as any).requestMerchantTransfer return typeof api === 'function' } catch { return false } } const requestMerchantTransferConfirm = (packageInfo: string): Promise => { if (!canRequestMerchantTransferConfirm()) { return Promise.reject(new Error('请在微信小程序内完成收款确认')) } // Backend may wrap/format base64 with newlines; WeChat API requires a clean string. const cleanPackageInfo = String(packageInfo).replace(/\s+/g, '') const api = (globalThis as any).wx?.requestMerchantTransfer || (Taro as any).requestMerchantTransfer if (typeof api !== 'function') { return Promise.reject(new Error('当前环境不支持商家转账收款确认(缺少 requestMerchantTransfer)')) } return new Promise((resolve, reject) => { api({ // WeChat API uses `package`, backend returns `package_info`. package: cleanPackageInfo, mchId: '1737910695', appId: 'wxad831ba00ad6a026', success: (res: any) => resolve(res), fail: (err: any) => reject(err) }) }) } // Some backends may return money fields as number; keep internal usage always as string. const normalizeMoneyString = (money: unknown) => { if (money === null || money === undefined || money === '') return '0.00' return typeof money === 'string' ? money : String(money) } const DealerWithdraw: React.FC = () => { const [activeTab, setActiveTab] = useState('0') const [selectedAccount, setSelectedAccount] = useState('') const [loading, setLoading] = useState(false) const [refreshing, setRefreshing] = useState(false) const [submitting, setSubmitting] = useState(false) const [availableAmount, setAvailableAmount] = useState('0.00') const [withdrawRecords, setWithdrawRecords] = useState([]) const formRef = useRef(null) 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...') try { setAvailableAmount(normalizeMoneyString(dealerUser?.money)) } catch (error) { console.error('获取余额失败:', error) } }, [dealerUser]) // 获取提现记录 const fetchWithdrawRecords = useCallback(async () => { if (!dealerUser?.userId) return try { setLoading(true) const result = await pageShopDealerWithdraw({ page: 1, limit: 100, userId: dealerUser.userId }) if (result?.list) { const processedRecords = result.list.map(record => ({ ...record, accountDisplay: getAccountDisplay(record) })) setWithdrawRecords(processedRecords) } } catch (error) { console.error('获取提现记录失败:', error) Taro.showToast({ title: '获取提现记录失败', icon: 'error' }) } finally { setLoading(false) } }, [dealerUser?.userId]) // 格式化账户显示 const getAccountDisplay = (record: ShopDealerWithdraw) => { if (record.payType === 10) { return '微信钱包' } else if (record.payType === 20 && record.alipayAccount) { return `支付宝(${record.alipayAccount.slice(-4)})` } else if (record.payType === 30 && record.bankCard) { return `${record.bankName || '银行卡'}(尾号${record.bankCard.slice(-4)})` } return '未知账户' } // 刷新数据 const handleRefresh = async () => { setRefreshing(true) await Promise.all([fetchBalance(), fetchWithdrawRecords()]) setRefreshing(false) } // 初始化加载数据 useEffect(() => { if (dealerUser?.userId) { fetchBalance().then() fetchWithdrawRecords().then() } }, [fetchBalance, fetchWithdrawRecords]) const getStatusText = (status?: number) => { switch (status) { 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' } } const handleSubmit = async (values: any) => { if (!dealerUser?.userId) { Taro.showToast({ title: '用户信息获取失败', icon: 'error' }) return } if (!values.accountType) { Taro.showToast({ title: '请选择提现方式', icon: 'error' }) return } // 验证提现金额 const amount = parseFloat(String(values.amount)) const available = parseFloat(normalizeMoneyString(availableAmount).replace(/,/g, '')) if (isNaN(amount) || amount <= 0) { Taro.showToast({ title: '请输入有效的提现金额', icon: 'error' }) return } if (amount < 100) { // Taro.showToast({ // title: '最低提现金额为100元', // icon: 'error' // }) // return } if (amount > available) { Taro.showToast({ title: '提现金额超过可用余额', icon: 'error' }) 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) const withdrawData: ShopDealerWithdraw = { userId: dealerUser.userId, money: values.amount, payType: values.accountType === 'wechat' ? 10 : values.accountType === 'alipay' ? 20 : 30, applyStatus: 10, // 待审核 platform: 'MiniProgram' } // 根据提现方式设置账户信息 if (values.accountType === 'alipay') { withdrawData.alipayAccount = values.account withdrawData.alipayName = values.accountName } else if (values.accountType === 'bank') { withdrawData.bankCard = values.account withdrawData.bankAccount = values.accountName withdrawData.bankName = values.bankName || '银行卡' } // WeChat wallet: backend should return `package_info`, frontend opens the "confirm receipt" page // for user to click "确认收款". if (values.accountType === 'wechat') { if (!canRequestMerchantTransferConfirm()) { throw new Error('当前环境不支持微信收款确认,请在微信小程序内操作') } const createResult = await addShopDealerWithdraw(withdrawData) const packageInfo = extractPackageInfo(createResult) if (!packageInfo) { throw new Error('后台未返回 package_info,无法调起微信收款确认页') } try { await requestMerchantTransferConfirm(packageInfo) Taro.showToast({ title: '已调起收款确认页', icon: 'success' }) } catch (e: any) { const msg = String(e?.errMsg || e?.message || '') if (/cancel/i.test(msg)) { Taro.showToast({title: '已取消收款确认', icon: 'none'}) } else { // Keep the original WeChat error for troubleshooting (e.g. "商户号错误"). throw new Error(msg || '调起收款确认页失败,请稍后重试') } } } else { await addShopDealerWithdraw(withdrawData) Taro.showToast({ title: '提现申请已提交', icon: 'success' }) } // 重置表单 formRef.current?.resetFields() setSelectedAccount('') // 刷新数据 await handleRefresh() // 切换到提现记录页面 setActiveTab('1') } catch (error: any) { console.error('提现申请失败:', error) Taro.showToast({ title: error.message || '提现申请失败', icon: 'error' }) } finally { setSubmitting(false) } } const quickAmounts = ['1','100', '300', '500', '1000'] const setQuickAmount = (amount: string) => { formRef.current?.setFieldsValue({amount}) } const setAllAmount = () => { formRef.current?.setFieldsValue({amount: normalizeMoneyString(availableAmount).replace(/,/g, '')}) } // 格式化金额 const formatMoney = (money?: unknown) => { const n = parseFloat(normalizeMoneyString(money).replace(/,/g, '')) return Number.isFinite(n) ? n.toFixed(2) : '0.00' } const renderWithdrawForm = () => ( {/* 余额卡片 */} {/* 装饰背景 - 小程序兼容版本 */} {formatMoney(dealerUser?.money)} 可提现余额 最低提现金额:¥100 | 手续费:免费
{ // 实时验证提现金额 const amount = parseFloat(String(value)) const available = parseFloat(normalizeMoneyString(availableAmount).replace(/,/g, '')) if (!isNaN(amount) && amount > available) { // 可以在这里添加实时提示,但不阻止输入 } }} /> {/* 快捷金额 */} 快捷金额 {quickAmounts.map(amount => ( ))} { const next = String(value) setSelectedAccount(next) // Ensure Form gets the field value even when Radio.Group is controlled. formRef.current?.setFieldsValue({accountType: next}) }} > 微信钱包 支付宝 银行卡 {selectedAccount === 'alipay' && ( <> )} {selectedAccount === 'bank' && ( <> )} {selectedAccount === 'wechat' && ( 提交后将拉起微信收款确认页,需要您点击“确认收款”后才会完成转账 )}
) 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)} 申请时间:{record.createTime} {record.auditTime && ( 审核时间:{new Date(record.auditTime).toLocaleString()} )} {record.rejectReason && ( 驳回原因:{record.rejectReason} )} )) ) : ( )} ) } if (!dealerUser) { return ( 加载中... ) } return ( {renderWithdrawForm()} {renderWithdrawRecords()} ) } export default DealerWithdraw