|
|
@ -4,61 +4,66 @@ import {Button, Loading} from '@nutui/nutui-react-taro' |
|
|
|
import {Share, Download, Copy, QrCode} from '@nutui/icons-react-taro' |
|
|
|
import Taro from '@tarojs/taro' |
|
|
|
import {useDealerUser} from '@/hooks/useDealerUser' |
|
|
|
import {generateInviteCode, getInviteStats} from '@/api/invite' |
|
|
|
import type {InviteStats} from '@/api/invite' |
|
|
|
import {generateInviteCode} from '@/api/invite' |
|
|
|
// import type {InviteStats} from '@/api/invite'
|
|
|
|
import {businessGradients} from '@/styles/gradients' |
|
|
|
|
|
|
|
const DealerQrcode: React.FC = () => { |
|
|
|
const [miniProgramCodeUrl, setMiniProgramCodeUrl] = useState<string>('') |
|
|
|
const [loading, setLoading] = useState<boolean>(false) |
|
|
|
const [inviteStats, setInviteStats] = useState<InviteStats | null>(null) |
|
|
|
const [statsLoading, setStatsLoading] = useState<boolean>(false) |
|
|
|
// const [inviteStats, setInviteStats] = useState<InviteStats | null>(null)
|
|
|
|
// const [statsLoading, setStatsLoading] = useState<boolean>(false)
|
|
|
|
const {dealerUser} = useDealerUser() |
|
|
|
|
|
|
|
// 生成小程序码
|
|
|
|
const generateMiniProgramCode = async () => { |
|
|
|
if (!dealerUser?.userId) return |
|
|
|
if (!dealerUser?.userId) { |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
setLoading(true) |
|
|
|
|
|
|
|
// 生成邀请小程序码
|
|
|
|
const codeUrl = await generateInviteCode(dealerUser.userId) |
|
|
|
console.log('小程序码生成成功:', codeUrl) |
|
|
|
|
|
|
|
if (codeUrl) { |
|
|
|
setMiniProgramCodeUrl(codeUrl) |
|
|
|
} else { |
|
|
|
throw new Error('返回的小程序码URL为空') |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('生成小程序码失败:', error) |
|
|
|
} catch (error: any) { |
|
|
|
Taro.showToast({ |
|
|
|
title: '生成小程序码失败', |
|
|
|
title: error.message || '生成小程序码失败', |
|
|
|
icon: 'error' |
|
|
|
}) |
|
|
|
// 清空之前的二维码
|
|
|
|
setMiniProgramCodeUrl('') |
|
|
|
} finally { |
|
|
|
setLoading(false) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 获取邀请统计数据
|
|
|
|
const fetchInviteStats = async () => { |
|
|
|
if (!dealerUser?.userId) return |
|
|
|
|
|
|
|
try { |
|
|
|
setStatsLoading(true) |
|
|
|
const stats = await getInviteStats(dealerUser.userId) |
|
|
|
stats && setInviteStats(stats) |
|
|
|
} catch (error) { |
|
|
|
console.error('获取邀请统计失败:', error) |
|
|
|
} finally { |
|
|
|
setStatsLoading(false) |
|
|
|
} |
|
|
|
} |
|
|
|
// const fetchInviteStats = async () => {
|
|
|
|
// if (!dealerUser?.userId) return
|
|
|
|
//
|
|
|
|
// try {
|
|
|
|
// setStatsLoading(true)
|
|
|
|
// const stats = await getInviteStats(dealerUser.userId)
|
|
|
|
// stats && setInviteStats(stats)
|
|
|
|
// } catch (error) {
|
|
|
|
// // 静默处理错误,不影响用户体验
|
|
|
|
// } finally {
|
|
|
|
// setStatsLoading(false)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// 初始化生成小程序码和获取统计数据
|
|
|
|
useEffect(() => { |
|
|
|
if (dealerUser?.userId) { |
|
|
|
generateMiniProgramCode() |
|
|
|
fetchInviteStats() |
|
|
|
// fetchInviteStats()
|
|
|
|
} |
|
|
|
}, [dealerUser?.userId]) |
|
|
|
|
|
|
@ -189,11 +194,6 @@ const DealerQrcode: React.FC = () => { |
|
|
|
|
|
|
|
<View className="px-4"> |
|
|
|
{/* 小程序码展示区 */} |
|
|
|
{/*<Image*/} |
|
|
|
{/* src={'http://127.0.0.1:9200/api/wx-login/getOrderQRCodeUnlimited/33103'}*/} |
|
|
|
{/* className="w-full h-full"*/} |
|
|
|
{/* mode="aspectFit"*/} |
|
|
|
{/*/>*/} |
|
|
|
<View className="bg-white rounded-2xl p-6 mb-6 shadow-sm"> |
|
|
|
<View className="text-center"> |
|
|
|
{loading ? ( |
|
|
@ -207,6 +207,20 @@ const DealerQrcode: React.FC = () => { |
|
|
|
src={miniProgramCodeUrl} |
|
|
|
className="w-full h-full" |
|
|
|
mode="aspectFit" |
|
|
|
onError={() => { |
|
|
|
Taro.showModal({ |
|
|
|
title: '二维码加载失败', |
|
|
|
content: '请检查网络连接或联系管理员', |
|
|
|
showCancel: true, |
|
|
|
confirmText: '重新生成', |
|
|
|
success: (res) => { |
|
|
|
if (res.confirm) { |
|
|
|
generateMiniProgramCode(); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
}} |
|
|
|
|
|
|
|
/> |
|
|
|
</View> |
|
|
|
) : ( |
|
|
@ -227,9 +241,11 @@ const DealerQrcode: React.FC = () => { |
|
|
|
<View className="text-lg font-semibold text-gray-800 mb-2"> |
|
|
|
扫码加入我的团队 |
|
|
|
</View> |
|
|
|
<View className="text-sm text-gray-500 mb-6"> |
|
|
|
<View className="text-sm text-gray-500 mb-4"> |
|
|
|
好友扫描小程序码即可直接进入小程序并建立邀请关系 |
|
|
|
</View> |
|
|
|
|
|
|
|
|
|
|
|
</View> |
|
|
|
</View> |
|
|
|
|
|
|
@ -273,7 +289,7 @@ const DealerQrcode: React.FC = () => { |
|
|
|
</View> |
|
|
|
|
|
|
|
{/* 推广说明 */} |
|
|
|
<View className="bg-white rounded-2xl p-4 mt-6"> |
|
|
|
<View className="bg-white rounded-2xl p-4 mt-6 hidden"> |
|
|
|
<Text className="font-semibold text-gray-800 mb-3">推广说明</Text> |
|
|
|
<View className="space-y-2"> |
|
|
|
<View className="flex items-start"> |
|
|
@ -298,82 +314,82 @@ const DealerQrcode: React.FC = () => { |
|
|
|
</View> |
|
|
|
|
|
|
|
{/* 邀请统计数据 */} |
|
|
|
<View className="bg-white rounded-2xl p-4 mt-4 mb-6"> |
|
|
|
<Text className="font-semibold text-gray-800 mb-3">我的邀请数据</Text> |
|
|
|
{statsLoading ? ( |
|
|
|
<View className="flex items-center justify-center py-8"> |
|
|
|
<Loading/> |
|
|
|
<Text className="text-gray-500 mt-2">加载中...</Text> |
|
|
|
</View> |
|
|
|
) : inviteStats ? ( |
|
|
|
<View className="space-y-4"> |
|
|
|
<View className="grid grid-cols-2 gap-4"> |
|
|
|
<View className="text-center"> |
|
|
|
<Text className="text-2xl font-bold text-blue-500"> |
|
|
|
{inviteStats.totalInvites || 0} |
|
|
|
</Text> |
|
|
|
<Text className="text-sm text-gray-500">总邀请数</Text> |
|
|
|
</View> |
|
|
|
<View className="text-center"> |
|
|
|
<Text className="text-2xl font-bold text-green-500"> |
|
|
|
{inviteStats.successfulRegistrations || 0} |
|
|
|
</Text> |
|
|
|
<Text className="text-sm text-gray-500">成功注册</Text> |
|
|
|
</View> |
|
|
|
</View> |
|
|
|
{/*<View className="bg-white rounded-2xl p-4 mt-4 mb-6">*/} |
|
|
|
{/* <Text className="font-semibold text-gray-800 mb-3">我的邀请数据</Text>*/} |
|
|
|
{/* {statsLoading ? (*/} |
|
|
|
{/* <View className="flex items-center justify-center py-8">*/} |
|
|
|
{/* <Loading/>*/} |
|
|
|
{/* <Text className="text-gray-500 mt-2">加载中...</Text>*/} |
|
|
|
{/* </View>*/} |
|
|
|
{/* ) : inviteStats ? (*/} |
|
|
|
{/* <View className="space-y-4">*/} |
|
|
|
{/* <View className="grid grid-cols-2 gap-4">*/} |
|
|
|
{/* <View className="text-center">*/} |
|
|
|
{/* <Text className="text-2xl font-bold text-blue-500">*/} |
|
|
|
{/* {inviteStats.totalInvites || 0}*/} |
|
|
|
{/* </Text>*/} |
|
|
|
{/* <Text className="text-sm text-gray-500">总邀请数</Text>*/} |
|
|
|
{/* </View>*/} |
|
|
|
{/* <View className="text-center">*/} |
|
|
|
{/* <Text className="text-2xl font-bold text-green-500">*/} |
|
|
|
{/* {inviteStats.successfulRegistrations || 0}*/} |
|
|
|
{/* </Text>*/} |
|
|
|
{/* <Text className="text-sm text-gray-500">成功注册</Text>*/} |
|
|
|
{/* </View>*/} |
|
|
|
{/* </View>*/} |
|
|
|
|
|
|
|
<View className="grid grid-cols-2 gap-4"> |
|
|
|
<View className="text-center"> |
|
|
|
<Text className="text-2xl font-bold text-purple-500"> |
|
|
|
{inviteStats.conversionRate ? `${(inviteStats.conversionRate * 100).toFixed(1)}%` : '0%'} |
|
|
|
</Text> |
|
|
|
<Text className="text-sm text-gray-500">转化率</Text> |
|
|
|
</View> |
|
|
|
<View className="text-center"> |
|
|
|
<Text className="text-2xl font-bold text-orange-500"> |
|
|
|
{inviteStats.todayInvites || 0} |
|
|
|
</Text> |
|
|
|
<Text className="text-sm text-gray-500">今日邀请</Text> |
|
|
|
</View> |
|
|
|
</View> |
|
|
|
{/* <View className="grid grid-cols-2 gap-4">*/} |
|
|
|
{/* <View className="text-center">*/} |
|
|
|
{/* <Text className="text-2xl font-bold text-purple-500">*/} |
|
|
|
{/* {inviteStats.conversionRate ? `${(inviteStats.conversionRate * 100).toFixed(1)}%` : '0%'}*/} |
|
|
|
{/* </Text>*/} |
|
|
|
{/* <Text className="text-sm text-gray-500">转化率</Text>*/} |
|
|
|
{/* </View>*/} |
|
|
|
{/* <View className="text-center">*/} |
|
|
|
{/* <Text className="text-2xl font-bold text-orange-500">*/} |
|
|
|
{/* {inviteStats.todayInvites || 0}*/} |
|
|
|
{/* </Text>*/} |
|
|
|
{/* <Text className="text-sm text-gray-500">今日邀请</Text>*/} |
|
|
|
{/* </View>*/} |
|
|
|
{/* </View>*/} |
|
|
|
|
|
|
|
{/* 邀请来源统计 */} |
|
|
|
{inviteStats.sourceStats && inviteStats.sourceStats.length > 0 && ( |
|
|
|
<View className="mt-4"> |
|
|
|
<Text className="text-sm font-medium text-gray-700 mb-2">邀请来源分布</Text> |
|
|
|
<View className="space-y-2"> |
|
|
|
{inviteStats.sourceStats.map((source, index) => ( |
|
|
|
<View key={index} className="flex items-center justify-between py-2 px-3 bg-gray-50 rounded-lg"> |
|
|
|
<View className="flex items-center"> |
|
|
|
<View className="w-3 h-3 rounded-full bg-blue-500 mr-2"></View> |
|
|
|
<Text className="text-sm text-gray-700">{source.source}</Text> |
|
|
|
</View> |
|
|
|
<View className="text-right"> |
|
|
|
<Text className="text-sm font-medium text-gray-800">{source.count}</Text> |
|
|
|
<Text className="text-xs text-gray-500"> |
|
|
|
{source.conversionRate ? `${(source.conversionRate * 100).toFixed(1)}%` : '0%'} |
|
|
|
</Text> |
|
|
|
</View> |
|
|
|
</View> |
|
|
|
))} |
|
|
|
</View> |
|
|
|
</View> |
|
|
|
)} |
|
|
|
</View> |
|
|
|
) : ( |
|
|
|
<View className="text-center py-8"> |
|
|
|
<View className="text-gray-500">暂无邀请数据</View> |
|
|
|
<Button |
|
|
|
size="small" |
|
|
|
type="primary" |
|
|
|
className="mt-2" |
|
|
|
onClick={fetchInviteStats} |
|
|
|
> |
|
|
|
刷新数据 |
|
|
|
</Button> |
|
|
|
</View> |
|
|
|
)} |
|
|
|
</View> |
|
|
|
{/* /!* 邀请来源统计 *!/*/} |
|
|
|
{/* {inviteStats.sourceStats && inviteStats.sourceStats.length > 0 && (*/} |
|
|
|
{/* <View className="mt-4">*/} |
|
|
|
{/* <Text className="text-sm font-medium text-gray-700 mb-2">邀请来源分布</Text>*/} |
|
|
|
{/* <View className="space-y-2">*/} |
|
|
|
{/* {inviteStats.sourceStats.map((source, index) => (*/} |
|
|
|
{/* <View key={index} className="flex items-center justify-between py-2 px-3 bg-gray-50 rounded-lg">*/} |
|
|
|
{/* <View className="flex items-center">*/} |
|
|
|
{/* <View className="w-3 h-3 rounded-full bg-blue-500 mr-2"></View>*/} |
|
|
|
{/* <Text className="text-sm text-gray-700">{source.source}</Text>*/} |
|
|
|
{/* </View>*/} |
|
|
|
{/* <View className="text-right">*/} |
|
|
|
{/* <Text className="text-sm font-medium text-gray-800">{source.count}</Text>*/} |
|
|
|
{/* <Text className="text-xs text-gray-500">*/} |
|
|
|
{/* {source.conversionRate ? `${(source.conversionRate * 100).toFixed(1)}%` : '0%'}*/} |
|
|
|
{/* </Text>*/} |
|
|
|
{/* </View>*/} |
|
|
|
{/* </View>*/} |
|
|
|
{/* ))}*/} |
|
|
|
{/* </View>*/} |
|
|
|
{/* </View>*/} |
|
|
|
{/* )}*/} |
|
|
|
{/* </View>*/} |
|
|
|
{/* ) : (*/} |
|
|
|
{/* <View className="text-center py-8">*/} |
|
|
|
{/* <View className="text-gray-500">暂无邀请数据</View>*/} |
|
|
|
{/* <Button*/} |
|
|
|
{/* size="small"*/} |
|
|
|
{/* type="primary"*/} |
|
|
|
{/* className="mt-2"*/} |
|
|
|
{/* onClick={fetchInviteStats}*/} |
|
|
|
{/* >*/} |
|
|
|
{/* 刷新数据*/} |
|
|
|
{/* </Button>*/} |
|
|
|
{/* </View>*/} |
|
|
|
{/* )}*/} |
|
|
|
{/*</View>*/} |
|
|
|
</View> |
|
|
|
</View> |
|
|
|
) |
|
|
|