feat(pages): 添加多个页面配置和功能模块
- 新增 .editorconfig、.eslintrc、.gitignore 配置文件 - 添加管理员文章管理页面配置和功能实现 - 添加经销商申请注册页面配置和功能实现 - 添加经销商银行卡管理页面配置和功能实现 - 添加经销商客户管理页面配置和功能实现 - 添加用户地址管理页面配置和功能实现 - 添加用户聊天消息页面配置和功能实现 - 添加用户礼品管理页面配置和功能实现
This commit is contained in:
3
src/dealer/withdraw/admin.config.ts
Normal file
3
src/dealer/withdraw/admin.config.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '提现审核'
|
||||
})
|
||||
498
src/dealer/withdraw/admin.tsx
Normal file
498
src/dealer/withdraw/admin.tsx
Normal file
@@ -0,0 +1,498 @@
|
||||
import React, {useState, useEffect, useCallback} from 'react'
|
||||
import {View, Text} from '@tarojs/components'
|
||||
import {
|
||||
Space,
|
||||
Tabs,
|
||||
Tag,
|
||||
Empty,
|
||||
ActionSheet,
|
||||
Loading,
|
||||
PullToRefresh,
|
||||
Button,
|
||||
Dialog,
|
||||
Image,
|
||||
TextArea
|
||||
} from '@nutui/nutui-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {useDealerUser} from '@/hooks/useDealerUser'
|
||||
import {pageShopDealerWithdraw, updateShopDealerWithdraw} from '@/api/shop/shopDealerWithdraw'
|
||||
import type {ShopDealerWithdraw} from '@/api/shop/shopDealerWithdraw/model'
|
||||
import {ShopDealerBank} from "@/api/shop/shopDealerBank/model";
|
||||
import {listShopDealerBank} from "@/api/shop/shopDealerBank";
|
||||
import {pushNoticeOfWithdrawalToAccount} from "@/api/sdy/sdyTemplateMessage";
|
||||
import {uploadFile} from "@/api/system/file";
|
||||
|
||||
interface WithdrawRecordWithDetails extends ShopDealerWithdraw {
|
||||
accountDisplay?: string
|
||||
}
|
||||
|
||||
const DealerWithdraw: React.FC = () => {
|
||||
const [activeTab, setActiveTab] = useState<string | number>('10')
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [refreshing, setRefreshing] = useState<boolean>(false)
|
||||
const [banks, setBanks] = useState<any[]>([])
|
||||
const [isVisible, setIsVisible] = useState<boolean>(false)
|
||||
const [withdrawRecords, setWithdrawRecords] = useState<WithdrawRecordWithDetails[]>([])
|
||||
const [rejectDialogVisible, setRejectDialogVisible] = useState<boolean>(false)
|
||||
const [rejectReason, setRejectReason] = useState<string>('')
|
||||
const [currentRecord, setCurrentRecord] = useState<ShopDealerWithdraw | null>(null)
|
||||
const [payDialogVisible, setPayDialogVisible] = useState<boolean>(false)
|
||||
const [paymentImages, setPaymentImages] = useState<string[]>([])
|
||||
|
||||
const {dealerUser} = useDealerUser()
|
||||
|
||||
// Tab 切换处理函数
|
||||
const handleTabChange = (value: string | number) => {
|
||||
console.log('Tab切换到:', value)
|
||||
setActiveTab(value)
|
||||
// activeTab变化会自动触发useEffect重新获取数据,无需手动调用
|
||||
}
|
||||
|
||||
// 获取提现记录
|
||||
const fetchWithdrawRecords = useCallback(async () => {
|
||||
if (!dealerUser?.userId) return
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
const currentStatus = Number(activeTab)
|
||||
const result = await pageShopDealerWithdraw({
|
||||
page: 1,
|
||||
limit: 100,
|
||||
applyStatus: currentStatus // 后端筛选,提高性能
|
||||
})
|
||||
|
||||
if (result?.list) {
|
||||
const processedRecords = result.list.map(record => ({
|
||||
...record,
|
||||
accountDisplay: getAccountDisplay(record)
|
||||
}))
|
||||
setWithdrawRecords(processedRecords)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取提现记录失败:', error)
|
||||
Taro.showToast({
|
||||
title: '获取提现记录失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [dealerUser?.userId, activeTab])
|
||||
|
||||
function fetchBanks() {
|
||||
listShopDealerBank({}).then(data => {
|
||||
const list = data.map(d => {
|
||||
d.name = d.bankName;
|
||||
d.type = d.bankName;
|
||||
return d;
|
||||
})
|
||||
setBanks(list.concat({
|
||||
name: '管理银行卡',
|
||||
type: 'add'
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
// 格式化账户显示
|
||||
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([fetchWithdrawRecords()])
|
||||
setRefreshing(false)
|
||||
}
|
||||
|
||||
const handleSelect = (item: ShopDealerBank) => {
|
||||
if(item.type === 'add'){
|
||||
return Taro.navigateTo({
|
||||
url: '/dealer/bank/index'
|
||||
})
|
||||
}
|
||||
setIsVisible(false)
|
||||
}
|
||||
|
||||
// 审核通过
|
||||
const handleApprove = async (record: ShopDealerWithdraw) => {
|
||||
try {
|
||||
await updateShopDealerWithdraw({
|
||||
...record,
|
||||
applyStatus: 20, // 审核通过
|
||||
})
|
||||
|
||||
Taro.showToast({
|
||||
title: '审核通过',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
await fetchWithdrawRecords()
|
||||
} catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('审核通过失败:', error)
|
||||
Taro.showToast({
|
||||
title: error.message || '操作失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 驳回申请
|
||||
const handleReject = (record: ShopDealerWithdraw) => {
|
||||
setCurrentRecord(record)
|
||||
setRejectReason('')
|
||||
setRejectDialogVisible(true)
|
||||
}
|
||||
|
||||
// 确认驳回
|
||||
const confirmReject = async () => {
|
||||
if (!rejectReason.trim()) {
|
||||
Taro.showToast({
|
||||
title: '请输入驳回原因',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await updateShopDealerWithdraw({
|
||||
...currentRecord!,
|
||||
applyStatus: 30, // 驳回
|
||||
rejectReason: rejectReason.trim()
|
||||
})
|
||||
|
||||
Taro.showToast({
|
||||
title: '已驳回',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
setRejectDialogVisible(false)
|
||||
setCurrentRecord(null)
|
||||
setRejectReason('')
|
||||
await fetchWithdrawRecords()
|
||||
} catch (error: any) {
|
||||
console.error('驳回失败:', error)
|
||||
Taro.showToast({
|
||||
title: error.message || '操作失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 确认打款 - 打开打款对话框
|
||||
const handleConfirmPay = (record: ShopDealerWithdraw) => {
|
||||
setCurrentRecord(record)
|
||||
setPaymentImages([])
|
||||
setPayDialogVisible(true)
|
||||
}
|
||||
|
||||
// 上传打款凭证
|
||||
const handleUploadPaymentImage = async () => {
|
||||
try {
|
||||
// 直接调用uploadFile,它内部会处理图片选择和上传
|
||||
const data = await uploadFile();
|
||||
console.log(data.url, 'uploaded image url');
|
||||
|
||||
// 确保url存在再添加到状态中
|
||||
if (data.url) {
|
||||
// 将返回的图片URL添加到状态中
|
||||
const newImages = [...paymentImages, data.url];
|
||||
setPaymentImages(newImages.slice(0, 3));
|
||||
} else {
|
||||
Taro.showToast({
|
||||
title: '图片上传失败:未返回有效URL',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('上传图片失败:', error);
|
||||
Taro.showToast({
|
||||
title: '上传失败: ' + (error instanceof Error ? error.message : '未知错误'),
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 删除打款凭证
|
||||
const handleRemovePaymentImage = (index: number) => {
|
||||
const newImages = paymentImages.filter((_, i) => i !== index)
|
||||
setPaymentImages(newImages)
|
||||
}
|
||||
|
||||
// 确认提交打款
|
||||
const confirmPayment = async () => {
|
||||
try {
|
||||
await updateShopDealerWithdraw({
|
||||
...currentRecord!,
|
||||
applyStatus: 40, // 已打款
|
||||
image: paymentImages.length > 0 ? JSON.stringify(paymentImages) : undefined
|
||||
})
|
||||
|
||||
Taro.showToast({
|
||||
title: '打款确认成功',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
if(currentRecord){
|
||||
await pushNoticeOfWithdrawalToAccount(currentRecord).then()
|
||||
}
|
||||
|
||||
setPayDialogVisible(false)
|
||||
setCurrentRecord(null)
|
||||
setPaymentImages([])
|
||||
await fetchWithdrawRecords()
|
||||
} catch (error: any) {
|
||||
console.error('确认打款失败:', error)
|
||||
Taro.showToast({
|
||||
title: error.message || '操作失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化加载数据
|
||||
useEffect(() => {
|
||||
if (dealerUser?.userId) {
|
||||
fetchWithdrawRecords().then()
|
||||
fetchBanks()
|
||||
}
|
||||
}, [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 renderWithdrawRecords = () => {
|
||||
console.log('渲染提现记录:', {loading, recordsCount: withdrawRecords.length, dealerUserId: dealerUser?.userId})
|
||||
|
||||
return (
|
||||
<PullToRefresh
|
||||
disabled={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
>
|
||||
<View>
|
||||
{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="rounded-lg bg-gray-50 p-4 mb-3 shadow-sm">
|
||||
<View className="flex justify-between items-start mb-3">
|
||||
<Space direction={'vertical'}>
|
||||
<Text className="font-semibold text-gray-800 mb-1">
|
||||
提现金额:¥{Number(record.money).toFixed(2)}
|
||||
</Text>
|
||||
<Text className="text-sm text-gray-500">
|
||||
{record.comments}
|
||||
</Text>
|
||||
<Text className={'text-sm text-gray-500'}>
|
||||
申请人:{record.bankAccount} {record.phone}
|
||||
</Text>
|
||||
</Space>
|
||||
<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>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
{record.applyStatus === 10 && (
|
||||
<View className="flex gap-2 mt-3">
|
||||
<Button
|
||||
type="success"
|
||||
size="small"
|
||||
className="flex-1"
|
||||
onClick={() => handleApprove(record)}
|
||||
>
|
||||
审核通过
|
||||
</Button>
|
||||
<Button
|
||||
type="danger"
|
||||
size="small"
|
||||
className="flex-1"
|
||||
onClick={() => handleReject(record)}
|
||||
>
|
||||
驳回
|
||||
</Button>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{record.applyStatus === 20 && (
|
||||
<View className="mt-3">
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
block
|
||||
onClick={() => handleConfirmPay(record)}
|
||||
>
|
||||
确认打款
|
||||
</Button>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
))
|
||||
) : (
|
||||
<Empty description="暂无提现记录"/>
|
||||
)}
|
||||
</View>
|
||||
</PullToRefresh>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View className="bg-gray-50 min-h-screen">
|
||||
<Tabs value={activeTab} onChange={handleTabChange}>
|
||||
<Tabs.TabPane title="待审核" value="10">
|
||||
{renderWithdrawRecords()}
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane title="已通过" value="20">
|
||||
{renderWithdrawRecords()}
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane title="已打款" value="40">
|
||||
{renderWithdrawRecords()}
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane title="已驳回" value="30">
|
||||
{renderWithdrawRecords()}
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
<ActionSheet
|
||||
visible={isVisible}
|
||||
options={banks}
|
||||
onSelect={handleSelect}
|
||||
onCancel={() => setIsVisible(false)}
|
||||
/>
|
||||
|
||||
{/* 驳回原因对话框 */}
|
||||
<Dialog
|
||||
visible={rejectDialogVisible}
|
||||
title="驳回原因"
|
||||
onCancel={() => {
|
||||
setRejectDialogVisible(false)
|
||||
setCurrentRecord(null)
|
||||
setRejectReason('')
|
||||
}}
|
||||
onConfirm={confirmReject}
|
||||
>
|
||||
<View className="p-4">
|
||||
<TextArea
|
||||
placeholder="请输入驳回原因"
|
||||
value={rejectReason}
|
||||
onChange={(value) => setRejectReason(value)}
|
||||
maxLength={200}
|
||||
rows={4}
|
||||
/>
|
||||
</View>
|
||||
</Dialog>
|
||||
|
||||
{/* 打款凭证对话框 */}
|
||||
<Dialog
|
||||
visible={payDialogVisible}
|
||||
title="确认打款"
|
||||
onCancel={() => {
|
||||
setPayDialogVisible(false)
|
||||
setCurrentRecord(null)
|
||||
setPaymentImages([])
|
||||
}}
|
||||
onConfirm={confirmPayment}
|
||||
>
|
||||
<View className="p-4">
|
||||
<View className="mb-3 flex flex-col">
|
||||
<Text className="text-sm text-gray-600">
|
||||
打款金额:¥{(Number(currentRecord?.money) - 3).toFixed(2)}
|
||||
</Text>
|
||||
<Text className="text-sm">
|
||||
开户行:{currentRecord?.bankName}
|
||||
</Text>
|
||||
<Text className="text-sm">
|
||||
户名:{currentRecord?.bankAccount}
|
||||
</Text>
|
||||
<Text className="text-sm">
|
||||
卡号:{currentRecord?.bankCard}
|
||||
</Text>
|
||||
</View>
|
||||
<View className="mb-3">
|
||||
<Text className="text-sm text-gray-600 mb-2 block">
|
||||
上传打款凭证(选填,最多3张)
|
||||
</Text>
|
||||
<View className="flex flex-wrap gap-2">
|
||||
{paymentImages.map((img, index) => (
|
||||
<View key={index} className="relative w-20 h-20">
|
||||
<Image src={img} className="w-full h-full object-cover rounded" />
|
||||
<View
|
||||
className="absolute top-0 right-0 bg-red-500 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs"
|
||||
onClick={() => handleRemovePaymentImage(index)}
|
||||
>
|
||||
×
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
{paymentImages.length < 3 && (
|
||||
<View
|
||||
className="w-20 h-20 border-2 border-dashed border-gray-300 rounded flex items-center justify-center"
|
||||
onClick={handleUploadPaymentImage}
|
||||
>
|
||||
<Text className="text-2xl text-gray-400">+</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Dialog>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default DealerWithdraw
|
||||
80
src/dealer/withdraw/debug.tsx
Normal file
80
src/dealer/withdraw/debug.tsx
Normal file
@@ -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<string | number>('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 (
|
||||
<View className="bg-gray-50 min-h-screen p-4">
|
||||
<View className="bg-white rounded-lg p-4 mb-4">
|
||||
<Text className="text-lg font-bold mb-2">调试信息</Text>
|
||||
<Text className="block mb-1">当前Tab: {String(activeTab)}</Text>
|
||||
<Text className="block mb-1">切换次数: {clickCount}</Text>
|
||||
<Text className="block mb-1">Tab类型: {typeof activeTab}</Text>
|
||||
</View>
|
||||
|
||||
<View className="bg-white rounded-lg p-4 mb-4">
|
||||
<Text className="text-lg font-bold mb-2">手动切换测试</Text>
|
||||
<View className="flex gap-2">
|
||||
<Button size="small" onClick={() => manualSwitch('0')}>
|
||||
切换到申请提现
|
||||
</Button>
|
||||
<Button size="small" onClick={() => manualSwitch('1')}>
|
||||
切换到提现记录
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className="bg-white rounded-lg">
|
||||
<Tabs value={activeTab} onChange={handleTabChange}>
|
||||
<Tabs.TabPane title="申请提现" value="0">
|
||||
<View className="p-4">
|
||||
<Text className="text-center text-gray-600">申请提现页面内容</Text>
|
||||
<Text className="text-center text-sm text-gray-400 mt-2">
|
||||
当前Tab值: {String(activeTab)}
|
||||
</Text>
|
||||
</View>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane title="提现记录" value="1">
|
||||
<View className="p-4">
|
||||
<Text className="text-center text-gray-600">提现记录页面内容</Text>
|
||||
<Text className="text-center text-sm text-gray-400 mt-2">
|
||||
当前Tab值: {String(activeTab)}
|
||||
</Text>
|
||||
</View>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</View>
|
||||
|
||||
<View className="bg-white rounded-lg p-4 mt-4">
|
||||
<Text className="text-lg font-bold mb-2">事件日志</Text>
|
||||
<Text className="text-sm text-gray-500">
|
||||
请查看控制台输出以获取详细的切换日志
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default WithdrawDebug
|
||||
3
src/dealer/withdraw/index.config.ts
Normal file
3
src/dealer/withdraw/index.config.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '提现申请'
|
||||
})
|
||||
480
src/dealer/withdraw/index.tsx
Normal file
480
src/dealer/withdraw/index.tsx
Normal file
@@ -0,0 +1,480 @@
|
||||
import React, {useState, useEffect, useCallback} from 'react'
|
||||
import {View, Text} from '@tarojs/components'
|
||||
import {
|
||||
Cell,
|
||||
Space,
|
||||
Button,
|
||||
Input,
|
||||
CellGroup,
|
||||
Tabs,
|
||||
Tag,
|
||||
Empty,
|
||||
ActionSheet,
|
||||
Loading,
|
||||
PullToRefresh
|
||||
} from '@nutui/nutui-react-taro'
|
||||
import {Wallet, ArrowRight} 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 {ShopDealerBank} from "@/api/shop/shopDealerBank/model";
|
||||
import {listShopDealerBank} from "@/api/shop/shopDealerBank";
|
||||
import {listCmsWebsiteField} from "@/api/cms/cmsWebsiteField";
|
||||
import {myUserVerify} from "@/api/system/userVerify";
|
||||
import navTo from "@/utils/common";
|
||||
import {pushWithdrawalReviewReminder} from "@/api/sdy/sdyTemplateMessage";
|
||||
|
||||
interface WithdrawRecordWithDetails extends ShopDealerWithdraw {
|
||||
accountDisplay?: string
|
||||
}
|
||||
|
||||
const DealerWithdraw: React.FC = () => {
|
||||
const [activeTab, setActiveTab] = useState<string | number>('0')
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [refreshing, setRefreshing] = useState<boolean>(false)
|
||||
const [submitting, setSubmitting] = useState<boolean>(false)
|
||||
const [banks, setBanks] = useState<any[]>([])
|
||||
const [bank, setBank] = useState<ShopDealerBank>()
|
||||
const [isVisible, setIsVisible] = useState<boolean>(false)
|
||||
const [availableAmount, setAvailableAmount] = useState<string>('0.00')
|
||||
const [withdrawRecords, setWithdrawRecords] = useState<WithdrawRecordWithDetails[]>([])
|
||||
const [withdrawAmount, setWithdrawAmount] = useState<string>('')
|
||||
const [withdrawValue, setWithdrawValue] = useState<string>('')
|
||||
|
||||
const {dealerUser} = useDealerUser()
|
||||
|
||||
// Tab 切换处理函数
|
||||
const handleTabChange = (value: string | number) => {
|
||||
console.log('Tab切换到:', value)
|
||||
setActiveTab(value)
|
||||
|
||||
// 如果切换到提现记录页面,刷新数据
|
||||
if (String(value) === '1') {
|
||||
fetchWithdrawRecords().then()
|
||||
}
|
||||
}
|
||||
|
||||
// 获取可提现余额
|
||||
const fetchBalance = useCallback(async () => {
|
||||
console.log(dealerUser, 'dealerUser...')
|
||||
try {
|
||||
setAvailableAmount(String(dealerUser?.money || '0.00'))
|
||||
} 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: 'none'
|
||||
})
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [dealerUser?.userId])
|
||||
|
||||
function fetchBanks() {
|
||||
listShopDealerBank({}).then(data => {
|
||||
const list = data.map(d => {
|
||||
d.name = d.bankName;
|
||||
d.type = d.bankName;
|
||||
return d;
|
||||
})
|
||||
setBanks(list.concat({
|
||||
name: '管理银行卡',
|
||||
type: 'add'
|
||||
}))
|
||||
setBank(data[0])
|
||||
})
|
||||
}
|
||||
|
||||
// 格式化账户显示
|
||||
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)
|
||||
}
|
||||
|
||||
const handleSelect = (item: ShopDealerBank) => {
|
||||
if(item.type === 'add'){
|
||||
return Taro.navigateTo({
|
||||
url: '/dealer/bank/index'
|
||||
})
|
||||
}
|
||||
setBank(item)
|
||||
setIsVisible(false)
|
||||
}
|
||||
|
||||
function fetchCmsField() {
|
||||
listCmsWebsiteField({ name: 'WithdrawValue'}).then(res => {
|
||||
if(res && res.length > 0){
|
||||
const text = res[0].value;
|
||||
setWithdrawValue(text || '')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 初始化加载数据
|
||||
useEffect(() => {
|
||||
if (dealerUser?.userId) {
|
||||
fetchBalance().then()
|
||||
fetchWithdrawRecords().then()
|
||||
fetchBanks()
|
||||
fetchCmsField()
|
||||
}
|
||||
}, [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 () => {
|
||||
if (!dealerUser?.userId) {
|
||||
Taro.showToast({
|
||||
title: '用户信息获取失败',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!bank) {
|
||||
Taro.showToast({
|
||||
title: '请选择提现银行卡',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 验证提现金额
|
||||
const amount = parseFloat(withdrawAmount)
|
||||
const availableStr = String(availableAmount || '0')
|
||||
const available = parseFloat(availableStr.replace(/,/g, ''))
|
||||
|
||||
if (isNaN(amount) || amount <= 0) {
|
||||
Taro.showToast({
|
||||
title: '请输入有效的提现金额',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (amount < 100) {
|
||||
Taro.showToast({
|
||||
title: '最低提现金额为100元',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (amount > available) {
|
||||
Taro.showToast({
|
||||
title: '提现金额超过可用余额',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 验证银行卡信息
|
||||
if (!bank.bankCard || !bank.bankAccount || !bank.bankName) {
|
||||
Taro.showToast({
|
||||
title: '银行卡信息不完整',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 验证实名认证
|
||||
const isChecked = await myUserVerify({})
|
||||
if(!isChecked){
|
||||
console.log(isChecked,'isCheckedisCheckedisCheckedisChecked');
|
||||
return navTo(`/user/userVerify/index`,true)
|
||||
}
|
||||
if(isChecked.status === 0){
|
||||
Taro.showToast({
|
||||
title: '实名认证还在审核中...',
|
||||
icon: 'none'
|
||||
})
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
setSubmitting(true)
|
||||
|
||||
const withdrawData: ShopDealerWithdraw = {
|
||||
userId: dealerUser.userId,
|
||||
money: withdrawAmount,
|
||||
payType: 30, // 银行卡提现
|
||||
applyStatus: 10, // 待审核
|
||||
platform: 'MiniProgram',
|
||||
bankCard: bank.bankCard,
|
||||
bankAccount: bank.bankAccount,
|
||||
bankName: bank.bankName,
|
||||
comments: `提现费3元/笔,预计到账金额¥${calculateExpectedAmount(withdrawAmount)}`,
|
||||
}
|
||||
|
||||
await addShopDealerWithdraw(withdrawData)
|
||||
|
||||
Taro.showToast({
|
||||
title: '提现申请已提交',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
await pushWithdrawalReviewReminder(withdrawData)
|
||||
|
||||
// 重置表单
|
||||
setWithdrawAmount('')
|
||||
|
||||
// 刷新数据
|
||||
await handleRefresh()
|
||||
|
||||
// 切换到提现记录页面
|
||||
setActiveTab('1')
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('提现申请失败:', error)
|
||||
Taro.showToast({
|
||||
title: error.message || '提现申请失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
setSubmitting(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化金额
|
||||
const formatMoney = (money?: string) => {
|
||||
if (!money) return '0.00'
|
||||
return parseFloat(money).toFixed(2)
|
||||
}
|
||||
|
||||
// 计算预计到账金额
|
||||
const calculateExpectedAmount = (amount: string) => {
|
||||
if (!amount || isNaN(parseFloat(amount))) return '0.00'
|
||||
const withdrawAmount = parseFloat(amount)
|
||||
// 提现费率 16% + 3元
|
||||
const feeRate = 0
|
||||
const fixedFee = 3
|
||||
const totalFee = withdrawAmount * feeRate + fixedFee
|
||||
const expectedAmount = withdrawAmount - totalFee
|
||||
return Math.max(0, expectedAmount).toFixed(2)
|
||||
}
|
||||
|
||||
const renderWithdrawForm = () => (
|
||||
<View>
|
||||
{/* 余额卡片 */}
|
||||
<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 className={'flex flex-col'}>
|
||||
<Text className="text-2xl font-bold text-white">{formatMoney(dealerUser?.money)}</Text>
|
||||
<Text className="text-white text-opacity-80 text-sm mb-1">可提现余额</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>
|
||||
|
||||
<CellGroup>
|
||||
<Cell style={{
|
||||
padding: '36px 12px'
|
||||
}} title={
|
||||
<View className="flex items-center justify-between">
|
||||
<Text className={'text-xl'}>¥</Text>
|
||||
<Input
|
||||
placeholder="提现金额"
|
||||
type="number"
|
||||
maxLength={7}
|
||||
value={withdrawAmount}
|
||||
onChange={(value) => setWithdrawAmount(value)}
|
||||
style={{
|
||||
padding: '0 10px',
|
||||
fontSize: '20px'
|
||||
}}
|
||||
/>
|
||||
<Button fill="none" size={'small'} onClick={() => setWithdrawAmount(dealerUser?.money || '0')}><Text className={'text-blue-500'}>全部提现</Text></Button>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
<Cell title={'提现到'} onClick={() => setIsVisible(true)} extra={
|
||||
<View className="flex items-center justify-between gap-1">
|
||||
{bank ? <Text className={'text-gray-800'}>{bank.bankName}</Text> : <Text className={'text-gray-400'}>请选择</Text>}
|
||||
<ArrowRight className={'text-gray-300'} size={15}/>
|
||||
</View>
|
||||
}/>
|
||||
<Cell title={'预计到账金额'} extra={
|
||||
<View className="flex items-center justify-between gap-1">
|
||||
<Text className={'text-orange-500 px-2 text-lg'}>¥{calculateExpectedAmount(withdrawAmount)}</Text>
|
||||
</View>
|
||||
}/>
|
||||
<Cell title={<Text className={'text-gray-400'}>说明:{withdrawValue}</Text>}/>
|
||||
</CellGroup>
|
||||
|
||||
<View className="mt-6 px-4">
|
||||
<Button
|
||||
block
|
||||
type="primary"
|
||||
nativeType="submit"
|
||||
loading={submitting}
|
||||
disabled={submitting || !withdrawAmount || !bank}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
{submitting ? '提交中...' : '申请提现'}
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
||||
const renderWithdrawRecords = () => {
|
||||
console.log('渲染提现记录:', {loading, recordsCount: withdrawRecords.length, dealerUserId: dealerUser?.userId})
|
||||
|
||||
return (
|
||||
<PullToRefresh
|
||||
disabled={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
>
|
||||
<View>
|
||||
{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="rounded-lg bg-gray-50 p-4 mb-3 shadow-sm">
|
||||
<View className="flex justify-between items-start mb-3">
|
||||
<Space direction={'vertical'}>
|
||||
<Text className="font-semibold text-gray-800 mb-1">
|
||||
提现金额:¥{Number(record.money).toFixed(2)}
|
||||
</Text>
|
||||
<Text className="text-sm text-gray-500">
|
||||
{record.comments}
|
||||
</Text>
|
||||
</Space>
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View className="bg-gray-50 min-h-screen">
|
||||
<Tabs value={activeTab} onChange={handleTabChange}>
|
||||
<Tabs.TabPane title="申请提现" value="0">
|
||||
{renderWithdrawForm()}
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane title="提现记录" value="1">
|
||||
{renderWithdrawRecords()}
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
<ActionSheet
|
||||
visible={isVisible}
|
||||
options={banks}
|
||||
onSelect={handleSelect}
|
||||
onCancel={() => setIsVisible(false)}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default DealerWithdraw
|
||||
Reference in New Issue
Block a user