forked from gxwebsoft/mp-10550
- 新增邀请统计页面,包含统计概览、邀请记录和排行榜三个标签页 - 实现邀请统计数据的获取和展示,包括总邀请数、成功注册数、转化率等 - 添加邀请记录的查询和展示功能 - 实现邀请排行榜的查询和展示功能 - 新增生成小程序码和处理邀请场景值的接口
425 lines
13 KiB
TypeScript
425 lines
13 KiB
TypeScript
import React, { useState, useRef, useEffect, useCallback } from 'react'
|
||
import { View, Text } from '@tarojs/components'
|
||
import {
|
||
Cell,
|
||
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 DealerWithdraw: React.FC = () => {
|
||
const [activeTab, setActiveTab] = useState('0')
|
||
const [selectedAccount, setSelectedAccount] = useState('')
|
||
const [loading, setLoading] = useState<boolean>(false)
|
||
const [refreshing, setRefreshing] = useState<boolean>(false)
|
||
const [submitting, setSubmitting] = useState<boolean>(false)
|
||
const [availableAmount, setAvailableAmount] = useState<string>('0.00')
|
||
const [withdrawRecords, setWithdrawRecords] = useState<WithdrawRecordWithDetails[]>([])
|
||
const formRef = useRef<any>(null)
|
||
|
||
const { dealerUser } = useDealerUser()
|
||
|
||
// 获取可提现余额
|
||
const fetchBalance = useCallback(async () => {
|
||
try {
|
||
setAvailableAmount(dealerUser?.money || '0.00')
|
||
} catch (error) {
|
||
console.error('获取余额失败:', error)
|
||
}
|
||
}, [])
|
||
|
||
// 获取提现记录
|
||
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
|
||
}
|
||
|
||
// 验证提现金额
|
||
const amount = parseFloat(values.amount)
|
||
const available = parseFloat(availableAmount.replace(',', ''))
|
||
|
||
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,
|
||
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 || '银行卡'
|
||
}
|
||
|
||
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 = ['100', '300', '500', '1000']
|
||
|
||
const setQuickAmount = (amount: string) => {
|
||
formRef.current?.setFieldsValue({ amount })
|
||
}
|
||
|
||
const setAllAmount = () => {
|
||
formRef.current?.setFieldsValue({ amount: availableAmount.replace(',', '') })
|
||
}
|
||
|
||
const renderWithdrawForm = () => (
|
||
<View className="p-4">
|
||
{/* 余额卡片 */}
|
||
<View className="rounded-xl p-6 mb-6 text-white relative overflow-hidden" style={{
|
||
background: businessGradients.dealer.header
|
||
}}>
|
||
{/* 装饰背景 - 小程序兼容版本 */}
|
||
<View className="absolute top-0 right-0 w-24 h-24 rounded-full" style={{
|
||
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||
right: '-12px',
|
||
top: '-12px'
|
||
}}></View>
|
||
|
||
<View className="flex items-center justify-between relative z-10">
|
||
<View>
|
||
<Text className="text-white text-opacity-80 text-sm mb-1">可提现余额</Text>
|
||
<Text className="text-2xl font-bold text-white">¥{availableAmount}</Text>
|
||
</View>
|
||
<View className="p-3 rounded-full" style={{
|
||
background: 'rgba(255, 255, 255, 0.2)'
|
||
}}>
|
||
<Wallet color="white" size="32" />
|
||
</View>
|
||
</View>
|
||
<View className="mt-4 pt-4 relative z-10" style={{
|
||
borderTop: '1px solid rgba(255, 255, 255, 0.3)'
|
||
}}>
|
||
<Text className="text-white text-opacity-80 text-xs">
|
||
最低提现金额:¥100 | 手续费:免费
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
|
||
<Form
|
||
ref={formRef}
|
||
onFinish={handleSubmit}
|
||
labelPosition="top"
|
||
>
|
||
<CellGroup>
|
||
<Form.Item name="amount" label="提现金额" required>
|
||
<Input
|
||
placeholder="请输入提现金额"
|
||
type="number"
|
||
clearable
|
||
/>
|
||
</Form.Item>
|
||
|
||
{/* 快捷金额 */}
|
||
<View className="px-4 py-2">
|
||
<Text className="text-sm text-gray-600 mb-2">快捷金额</Text>
|
||
<View className="flex flex-wrap gap-2">
|
||
{quickAmounts.map(amount => (
|
||
<Button
|
||
key={amount}
|
||
size="small"
|
||
fill="outline"
|
||
onClick={() => setQuickAmount(amount)}
|
||
>
|
||
{amount}
|
||
</Button>
|
||
))}
|
||
<Button
|
||
size="small"
|
||
fill="outline"
|
||
onClick={setAllAmount}
|
||
>
|
||
全部
|
||
</Button>
|
||
</View>
|
||
</View>
|
||
|
||
<Form.Item name="accountType" label="提现方式" required>
|
||
<Radio.Group value={selectedAccount} onChange={() => setSelectedAccount}>
|
||
<Cell.Group>
|
||
<Cell>
|
||
<Radio value="wechat">微信钱包</Radio>
|
||
</Cell>
|
||
<Cell>
|
||
<Radio value="alipay">支付宝</Radio>
|
||
</Cell>
|
||
<Cell>
|
||
<Radio value="bank">银行卡</Radio>
|
||
</Cell>
|
||
</Cell.Group>
|
||
</Radio.Group>
|
||
</Form.Item>
|
||
|
||
{selectedAccount === 'alipay' && (
|
||
<>
|
||
<Form.Item name="account" label="支付宝账号" required>
|
||
<Input placeholder="请输入支付宝账号" />
|
||
</Form.Item>
|
||
<Form.Item name="accountName" label="支付宝姓名" required>
|
||
<Input placeholder="请输入支付宝实名姓名" />
|
||
</Form.Item>
|
||
</>
|
||
)}
|
||
|
||
{selectedAccount === 'bank' && (
|
||
<>
|
||
<Form.Item name="bankName" label="开户银行" required>
|
||
<Input placeholder="请输入开户银行名称" />
|
||
</Form.Item>
|
||
<Form.Item name="account" label="银行卡号" required>
|
||
<Input placeholder="请输入银行卡号" />
|
||
</Form.Item>
|
||
<Form.Item name="accountName" label="开户姓名" required>
|
||
<Input placeholder="请输入银行卡开户姓名" />
|
||
</Form.Item>
|
||
</>
|
||
)}
|
||
|
||
{selectedAccount === 'wechat' && (
|
||
<View className="px-4 py-2">
|
||
<Text className="text-sm text-gray-500">
|
||
微信钱包提现将直接转入您的微信零钱
|
||
</Text>
|
||
</View>
|
||
)}
|
||
</CellGroup>
|
||
|
||
<View className="mt-6 px-4">
|
||
<Button
|
||
block
|
||
type="primary"
|
||
nativeType="submit"
|
||
loading={submitting}
|
||
disabled={submitting || !selectedAccount}
|
||
>
|
||
{submitting ? '提交中...' : '申请提现'}
|
||
</Button>
|
||
</View>
|
||
</Form>
|
||
</View>
|
||
)
|
||
|
||
const renderWithdrawRecords = () => (
|
||
<PullToRefresh
|
||
loading={refreshing}
|
||
onRefresh={handleRefresh}
|
||
>
|
||
<View className="p-4">
|
||
{loading ? (
|
||
<View className="text-center py-8">
|
||
<Loading />
|
||
<Text className="text-gray-500 mt-2">加载中...</Text>
|
||
</View>
|
||
) : withdrawRecords.length > 0 ? (
|
||
withdrawRecords.map(record => (
|
||
<View key={record.id} className="bg-white rounded-lg p-4 mb-3 shadow-sm">
|
||
<View className="flex justify-between items-start mb-3">
|
||
<View>
|
||
<Text className="font-semibold text-gray-800 mb-1">
|
||
提现金额:¥{record.money}
|
||
</Text>
|
||
<Text className="text-sm text-gray-500">
|
||
提现账户:{record.accountDisplay}
|
||
</Text>
|
||
</View>
|
||
<Tag type={getStatusColor(record.applyStatus)}>
|
||
{getStatusText(record.applyStatus)}
|
||
</Tag>
|
||
</View>
|
||
|
||
<View className="text-xs text-gray-400">
|
||
<Text>申请时间:{record.createTime}</Text>
|
||
{record.auditTime && (
|
||
<Text className="block mt-1">
|
||
审核时间:{new Date(record.auditTime).toLocaleString()}
|
||
</Text>
|
||
)}
|
||
{record.rejectReason && (
|
||
<Text className="block mt-1 text-red-500">
|
||
驳回原因:{record.rejectReason}
|
||
</Text>
|
||
)}
|
||
</View>
|
||
</View>
|
||
))
|
||
) : (
|
||
<Empty description="暂无提现记录" />
|
||
)}
|
||
</View>
|
||
</PullToRefresh>
|
||
)
|
||
|
||
if (!dealerUser) {
|
||
return (
|
||
<View className="bg-gray-50 min-h-screen flex items-center justify-center">
|
||
<Loading />
|
||
<Text className="text-gray-500 mt-2">加载中...</Text>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<View className="bg-gray-50 min-h-screen">
|
||
<Tabs value={activeTab} onChange={() => setActiveTab}>
|
||
<Tabs.TabPane title="申请提现" value="0">
|
||
{renderWithdrawForm()}
|
||
</Tabs.TabPane>
|
||
|
||
<Tabs.TabPane title="提现记录" value="1">
|
||
{renderWithdrawRecords()}
|
||
</Tabs.TabPane>
|
||
</Tabs>
|
||
</View>
|
||
)
|
||
}
|
||
|
||
export default DealerWithdraw
|