import React, {useState, useRef, useEffect, useCallback} from 'react' import {View, Text} from '@tarojs/components' import { Space, Button, Form, Input, CellGroup, 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, receiveShopDealerWithdraw, receiveSuccessShopDealerWithdraw } from '@/api/shop/shopDealerWithdraw' import type {ShopDealerWithdraw} from '@/api/shop/shopDealerWithdraw/model' interface WithdrawRecordWithDetails extends ShopDealerWithdraw { accountDisplay?: string // Backend may include these fields for WeChat "confirm receipt" flow after approval. package_info?: string packageInfo?: string package?: string } const extractPackageInfo = (result: unknown): string | null => { if (typeof result === 'string') return result 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 [loading, setLoading] = useState(false) const [refreshing, setRefreshing] = useState(false) const [submitting, setSubmitting] = useState(false) const [claimingId, setClaimingId] = useState(null) 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 'info' 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 } // 验证提现金额 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 } try { setSubmitting(true) const withdrawData: ShopDealerWithdraw = { userId: dealerUser.userId, money: values.amount, // Only support WeChat wallet withdrawals. payType: 10, applyStatus: 10, // 待审核 platform: 'MiniProgram' } // Security flow: // 1) user submits => applyStatus=10 (待审核) // 2) backend审核通过 => applyStatus=20 (待领取) // 3) user goes to records to "领取" => applyStatus=40 (已到账) await addShopDealerWithdraw(withdrawData) Taro.showToast({title: '提现申请已提交,等待审核', icon: 'success'}) // 重置表单 formRef.current?.resetFields() // 刷新数据 await handleRefresh() // 切换到提现记录页面 setActiveTab('1') } catch (error: any) { console.error('提现申请失败:', error) Taro.showToast({ title: error.message || '提现申请失败', icon: 'error' }) } finally { setSubmitting(false) } } const handleClaim = async (record: WithdrawRecordWithDetails) => { if (!record?.id) { Taro.showToast({title: '记录不存在', icon: 'error'}) return } if (record.applyStatus !== 20) { Taro.showToast({title: '当前状态不可领取', icon: 'none'}) return } if (record.payType !== 10) { Taro.showToast({title: '仅支持微信提现领取', icon: 'none'}) return } if (claimingId !== null) return try { setClaimingId(record.id) if (!canRequestMerchantTransferConfirm()) { throw new Error('当前环境不支持微信收款确认,请在微信小程序内操作') } const receiveResult = await receiveShopDealerWithdraw(record.id) const packageInfo = extractPackageInfo(receiveResult) if (!packageInfo) { throw new Error('后台未返回 package_info,无法领取,请联系管理员') } try { await requestMerchantTransferConfirm(packageInfo) } catch (e: any) { const msg = String(e?.errMsg || e?.message || '') if (/cancel/i.test(msg)) { Taro.showToast({title: '已取消领取', icon: 'none'}) return } throw new Error(msg || '领取失败,请稍后重试') } try { await receiveSuccessShopDealerWithdraw(record.id) Taro.showToast({title: '领取成功', icon: 'success'}) } catch (e: any) { console.warn('领取成功,但状态同步失败:', e) Taro.showToast({title: '已收款,状态更新失败,请稍后刷新', icon: 'none'}) } finally { await handleRefresh() } } catch (e: any) { console.error('领取失败:', e) Taro.showToast({title: e?.message || '领取失败', icon: 'error'}) } finally { setClaimingId(null) } } const quickAmounts = ['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)} 可提现余额 手续费:免费
{/* 快捷金额 */} 快捷金额 {quickAmounts.map(amount => ( ))} 提现方式:微信钱包(提交后进入“待审核”,审核通过后请到“提现记录”点击“领取到微信零钱”完成收款确认)
) 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.applyStatus === 20 && record.payType === 10 && ( )} 创建时间:{record.createTime} {record.auditTime && ( 审核时间:{record.auditTime} )} {record.rejectReason && ( 驳回原因:{record.rejectReason} )} )) ) : ( )} ) } if (!dealerUser) { return ( 加载中... ) } return ( {renderWithdrawForm()} {renderWithdrawRecords()} ) } export default DealerWithdraw