feat(invite): 添加邀请统计功能
- 新增邀请统计页面,包含统计概览、邀请记录和排行榜三个标签页 - 实现邀请统计数据的获取和展示,包括总邀请数、成功注册数、转化率等 - 添加邀请记录的查询和展示功能 - 实现邀请排行榜的查询和展示功能 - 新增生成小程序码和处理邀请场景值的接口
This commit is contained in:
@@ -1,60 +1,197 @@
|
||||
import React, { useState } from 'react'
|
||||
import React, { useState, useEffect, useCallback } from 'react'
|
||||
import { View, Text } from '@tarojs/components'
|
||||
import { Empty, Tabs, Tag, PullToRefresh } from '@nutui/nutui-react-taro'
|
||||
import { Empty, Tabs, Tag, PullToRefresh, Loading } from '@nutui/nutui-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { pageShopDealerOrder } from '@/api/shop/shopDealerOrder'
|
||||
import { useDealerUser } from '@/hooks/useDealerUser'
|
||||
import type { ShopDealerOrder } from '@/api/shop/shopDealerOrder/model'
|
||||
|
||||
interface OrderWithDetails extends ShopDealerOrder {
|
||||
orderNo?: string
|
||||
customerName?: string
|
||||
totalCommission?: string
|
||||
// 当前用户在此订单中的层级和佣金
|
||||
userLevel?: 1 | 2 | 3
|
||||
userCommission?: string
|
||||
}
|
||||
|
||||
const DealerOrders: React.FC = () => {
|
||||
const [activeTab, setActiveTab] = useState<string>('0')
|
||||
const [refreshing, setRefreshing] = useState<boolean>(false)
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [orders, setOrders] = useState<OrderWithDetails[]>([])
|
||||
const [statistics, setStatistics] = useState({
|
||||
totalOrders: 0,
|
||||
totalCommission: '0.00',
|
||||
pendingCommission: '0.00',
|
||||
// 分层统计
|
||||
level1: { orders: 0, commission: '0.00' },
|
||||
level2: { orders: 0, commission: '0.00' },
|
||||
level3: { orders: 0, commission: '0.00' }
|
||||
})
|
||||
|
||||
// 模拟订单数据
|
||||
const mockOrders = [
|
||||
{
|
||||
id: '1',
|
||||
orderNo: 'DD202412180001',
|
||||
customerName: '张三',
|
||||
amount: '299.00',
|
||||
commission: '29.90',
|
||||
status: 'completed',
|
||||
createTime: '2024-12-18 10:30:00'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
orderNo: 'DD202412180002',
|
||||
customerName: '李四',
|
||||
amount: '599.00',
|
||||
commission: '59.90',
|
||||
status: 'pending',
|
||||
createTime: '2024-12-18 14:20:00'
|
||||
const { dealerUser } = useDealerUser()
|
||||
|
||||
// 获取订单数据 - 查询当前用户作为各层级分销商的所有订单
|
||||
const fetchOrders = useCallback(async () => {
|
||||
if (!dealerUser?.userId) return
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
|
||||
// 并行查询三个层级的订单
|
||||
const [level1Result, level2Result, level3Result] = await Promise.all([
|
||||
// 一级分销商订单
|
||||
pageShopDealerOrder({
|
||||
page: 1,
|
||||
limit: 100,
|
||||
firstUserId: dealerUser.userId
|
||||
}),
|
||||
// 二级分销商订单
|
||||
pageShopDealerOrder({
|
||||
page: 1,
|
||||
limit: 100,
|
||||
secondUserId: dealerUser.userId
|
||||
}),
|
||||
// 三级分销商订单
|
||||
pageShopDealerOrder({
|
||||
page: 1,
|
||||
limit: 100,
|
||||
thirdUserId: dealerUser.userId
|
||||
})
|
||||
])
|
||||
|
||||
const allOrders: OrderWithDetails[] = []
|
||||
const stats = {
|
||||
totalOrders: 0,
|
||||
totalCommission: '0.00',
|
||||
pendingCommission: '0.00',
|
||||
level1: { orders: 0, commission: '0.00' },
|
||||
level2: { orders: 0, commission: '0.00' },
|
||||
level3: { orders: 0, commission: '0.00' }
|
||||
}
|
||||
|
||||
// 处理一级分销订单
|
||||
if (level1Result?.list) {
|
||||
const level1Orders = level1Result.list.map(order => ({
|
||||
...order,
|
||||
orderNo: `DD${order.orderId}`,
|
||||
customerName: `用户${order.userId}`,
|
||||
userLevel: 1 as const,
|
||||
userCommission: order.firstMoney || '0.00',
|
||||
totalCommission: (
|
||||
parseFloat(order.firstMoney || '0') +
|
||||
parseFloat(order.secondMoney || '0') +
|
||||
parseFloat(order.thirdMoney || '0')
|
||||
).toFixed(2)
|
||||
}))
|
||||
|
||||
allOrders.push(...level1Orders)
|
||||
stats.level1.orders = level1Orders.length
|
||||
stats.level1.commission = level1Orders.reduce((sum, order) =>
|
||||
sum + parseFloat(order.userCommission || '0'), 0
|
||||
).toFixed(2)
|
||||
}
|
||||
|
||||
// 处理二级分销订单
|
||||
if (level2Result?.list) {
|
||||
const level2Orders = level2Result.list.map(order => ({
|
||||
...order,
|
||||
orderNo: `DD${order.orderId}`,
|
||||
customerName: `用户${order.userId}`,
|
||||
userLevel: 2 as const,
|
||||
userCommission: order.secondMoney || '0.00',
|
||||
totalCommission: (
|
||||
parseFloat(order.firstMoney || '0') +
|
||||
parseFloat(order.secondMoney || '0') +
|
||||
parseFloat(order.thirdMoney || '0')
|
||||
).toFixed(2)
|
||||
}))
|
||||
|
||||
allOrders.push(...level2Orders)
|
||||
stats.level2.orders = level2Orders.length
|
||||
stats.level2.commission = level2Orders.reduce((sum, order) =>
|
||||
sum + parseFloat(order.userCommission || '0'), 0
|
||||
).toFixed(2)
|
||||
}
|
||||
|
||||
// 处理三级分销订单
|
||||
if (level3Result?.list) {
|
||||
const level3Orders = level3Result.list.map(order => ({
|
||||
...order,
|
||||
orderNo: `DD${order.orderId}`,
|
||||
customerName: `用户${order.userId}`,
|
||||
userLevel: 3 as const,
|
||||
userCommission: order.thirdMoney || '0.00',
|
||||
totalCommission: (
|
||||
parseFloat(order.firstMoney || '0') +
|
||||
parseFloat(order.secondMoney || '0') +
|
||||
parseFloat(order.thirdMoney || '0')
|
||||
).toFixed(2)
|
||||
}))
|
||||
|
||||
allOrders.push(...level3Orders)
|
||||
stats.level3.orders = level3Orders.length
|
||||
stats.level3.commission = level3Orders.reduce((sum, order) =>
|
||||
sum + parseFloat(order.userCommission || '0'), 0
|
||||
).toFixed(2)
|
||||
}
|
||||
|
||||
// 去重(同一个订单可能在多个层级中出现)
|
||||
const uniqueOrders = allOrders.filter((order, index, self) =>
|
||||
index === self.findIndex(o => o.orderId === order.orderId)
|
||||
)
|
||||
|
||||
// 计算总统计
|
||||
stats.totalOrders = uniqueOrders.length
|
||||
stats.totalCommission = (
|
||||
parseFloat(stats.level1.commission) +
|
||||
parseFloat(stats.level2.commission) +
|
||||
parseFloat(stats.level3.commission)
|
||||
).toFixed(2)
|
||||
stats.pendingCommission = allOrders
|
||||
.filter(order => order.isSettled === 0)
|
||||
.reduce((sum, order) => sum + parseFloat(order.userCommission || '0'), 0)
|
||||
.toFixed(2)
|
||||
|
||||
setOrders(uniqueOrders)
|
||||
setStatistics(stats)
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取分销订单失败:', error)
|
||||
Taro.showToast({
|
||||
title: '获取订单失败',
|
||||
icon: 'error'
|
||||
})
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
]
|
||||
|
||||
const getStatusText = (status: string) => {
|
||||
switch (status) {
|
||||
case 'completed': return '已完成'
|
||||
case 'pending': return '待结算'
|
||||
case 'cancelled': return '已取消'
|
||||
default: return '未知'
|
||||
}
|
||||
}
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case 'completed': return 'success'
|
||||
case 'pending': return 'warning'
|
||||
case 'cancelled': return 'danger'
|
||||
default: return 'default'
|
||||
}
|
||||
}
|
||||
}, [dealerUser?.userId])
|
||||
|
||||
// 刷新数据
|
||||
const handleRefresh = async () => {
|
||||
setRefreshing(true)
|
||||
// 模拟刷新
|
||||
setTimeout(() => {
|
||||
setRefreshing(false)
|
||||
}, 1000)
|
||||
await fetchOrders()
|
||||
}
|
||||
|
||||
const renderOrderItem = (order: any) => (
|
||||
// 初始化加载数据
|
||||
useEffect(() => {
|
||||
if (dealerUser?.userId) {
|
||||
fetchOrders().then()
|
||||
}
|
||||
}, [fetchOrders])
|
||||
|
||||
const getStatusText = (isSettled?: number, isInvalid?: number) => {
|
||||
if (isInvalid === 1) return '已失效'
|
||||
if (isSettled === 1) return '已结算'
|
||||
return '待结算'
|
||||
}
|
||||
|
||||
const getStatusColor = (isSettled?: number, isInvalid?: number) => {
|
||||
if (isInvalid === 1) return 'danger'
|
||||
if (isSettled === 1) return 'success'
|
||||
return 'warning'
|
||||
}
|
||||
|
||||
const renderOrderItem = (order: OrderWithDetails) => (
|
||||
<View key={order.id} className="bg-white rounded-lg p-4 mb-3 shadow-sm">
|
||||
<View className="flex justify-between items-start mb-3">
|
||||
<View>
|
||||
@@ -64,19 +201,28 @@ const DealerOrders: React.FC = () => {
|
||||
<Text className="text-sm text-gray-500">
|
||||
客户:{order.customerName}
|
||||
</Text>
|
||||
{/* 显示用户在此订单中的层级 */}
|
||||
<Text className="text-xs text-blue-500">
|
||||
{order.userLevel === 1 && '一级分销'}
|
||||
{order.userLevel === 2 && '二级分销'}
|
||||
{order.userLevel === 3 && '三级分销'}
|
||||
</Text>
|
||||
</View>
|
||||
<Tag type={getStatusColor(order.status)}>
|
||||
{getStatusText(order.status)}
|
||||
<Tag type={getStatusColor(order.isSettled, order.isInvalid)}>
|
||||
{getStatusText(order.isSettled, order.isInvalid)}
|
||||
</Tag>
|
||||
</View>
|
||||
|
||||
<View className="flex justify-between items-center">
|
||||
<View>
|
||||
<Text className="text-sm text-gray-600">
|
||||
订单金额:¥{order.amount}
|
||||
订单金额:¥{order.orderPrice || '0.00'}
|
||||
</Text>
|
||||
<Text className="text-sm text-orange-500 font-semibold">
|
||||
预计佣金:¥{order.commission}
|
||||
我的佣金:¥{order.userCommission}
|
||||
</Text>
|
||||
<Text className="text-xs text-gray-400">
|
||||
总佣金:¥{order.totalCommission}
|
||||
</Text>
|
||||
</View>
|
||||
<Text className="text-xs text-gray-400">
|
||||
@@ -86,37 +232,92 @@ const DealerOrders: React.FC = () => {
|
||||
</View>
|
||||
)
|
||||
|
||||
// 根据状态和层级过滤订单
|
||||
const getFilteredOrders = (filter: string) => {
|
||||
switch (filter) {
|
||||
case '1': // 一级分销
|
||||
return orders.filter(order => order.userLevel === 1)
|
||||
case '2': // 二级分销
|
||||
return orders.filter(order => order.userLevel === 2)
|
||||
case '3': // 三级分销
|
||||
return orders.filter(order => order.userLevel === 3)
|
||||
case '4': // 待结算
|
||||
return orders.filter(order => order.isSettled === 0 && order.isInvalid === 0)
|
||||
case '5': // 已结算
|
||||
return orders.filter(order => order.isSettled === 1)
|
||||
case '6': // 已失效
|
||||
return orders.filter(order => order.isInvalid === 1)
|
||||
default: // 全部
|
||||
return orders
|
||||
}
|
||||
}
|
||||
|
||||
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">
|
||||
{/* 统计卡片 */}
|
||||
<View className="bg-white p-4 mb-4">
|
||||
<View className="grid grid-cols-3 gap-4">
|
||||
{/* 总体统计 */}
|
||||
<View className="grid grid-cols-3 gap-4 mb-4">
|
||||
<View className="text-center">
|
||||
<Text className="text-lg font-bold text-blue-500">2</Text>
|
||||
<Text className="text-lg font-bold text-blue-500">{statistics.totalOrders}</Text>
|
||||
<Text className="text-xs text-gray-500">总订单</Text>
|
||||
</View>
|
||||
<View className="text-center">
|
||||
<Text className="text-lg font-bold text-green-500">¥89.80</Text>
|
||||
<Text className="text-lg font-bold text-green-500">¥{statistics.totalCommission}</Text>
|
||||
<Text className="text-xs text-gray-500">总佣金</Text>
|
||||
</View>
|
||||
<View className="text-center">
|
||||
<Text className="text-lg font-bold text-orange-500">¥29.90</Text>
|
||||
<Text className="text-lg font-bold text-orange-500">¥{statistics.pendingCommission}</Text>
|
||||
<Text className="text-xs text-gray-500">待结算</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 分层统计 */}
|
||||
<View className="border-t pt-3">
|
||||
<Text className="text-sm text-gray-600 mb-2">分层统计</Text>
|
||||
<View className="grid grid-cols-3 gap-2">
|
||||
<View className="text-center bg-gray-50 rounded p-2">
|
||||
<Text className="text-sm font-semibold text-red-500">{statistics.level1.orders}</Text>
|
||||
<Text className="text-xs text-gray-500">一级订单</Text>
|
||||
<Text className="text-xs text-red-500">¥{statistics.level1.commission}</Text>
|
||||
</View>
|
||||
<View className="text-center bg-gray-50 rounded p-2">
|
||||
<Text className="text-sm font-semibold text-blue-500">{statistics.level2.orders}</Text>
|
||||
<Text className="text-xs text-gray-500">二级订单</Text>
|
||||
<Text className="text-xs text-blue-500">¥{statistics.level2.commission}</Text>
|
||||
</View>
|
||||
<View className="text-center bg-gray-50 rounded p-2">
|
||||
<Text className="text-sm font-semibold text-purple-500">{statistics.level3.orders}</Text>
|
||||
<Text className="text-xs text-gray-500">三级订单</Text>
|
||||
<Text className="text-xs text-purple-500">¥{statistics.level3.commission}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 订单列表 */}
|
||||
<Tabs value={activeTab} onChange={() => setActiveTab}>
|
||||
<Tabs.TabPane title="全部" value="0">
|
||||
<PullToRefresh
|
||||
// @ts-ignore
|
||||
loading={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
>
|
||||
<View className="p-4">
|
||||
{mockOrders.length > 0 ? (
|
||||
mockOrders.map(renderOrderItem)
|
||||
{loading ? (
|
||||
<View className="text-center py-8">
|
||||
<Loading />
|
||||
<Text className="text-gray-500 mt-2">加载中...</Text>
|
||||
</View>
|
||||
) : getFilteredOrders('0').length > 0 ? (
|
||||
getFilteredOrders('0').map(renderOrderItem)
|
||||
) : (
|
||||
<Empty description="暂无分销订单" />
|
||||
)}
|
||||
@@ -124,15 +325,63 @@ const DealerOrders: React.FC = () => {
|
||||
</PullToRefresh>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane title="待结算" value="1">
|
||||
<Tabs.TabPane title="一级分销" value="1">
|
||||
<View className="p-4">
|
||||
{mockOrders.filter(o => o.status === 'pending').map(renderOrderItem)}
|
||||
{getFilteredOrders('1').length > 0 ? (
|
||||
getFilteredOrders('1').map(renderOrderItem)
|
||||
) : (
|
||||
<Empty description="暂无一级分销订单" />
|
||||
)}
|
||||
</View>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane title="已完成" value="2">
|
||||
<Tabs.TabPane title="二级分销" value="2">
|
||||
<View className="p-4">
|
||||
{mockOrders.filter(o => o.status === 'completed').map(renderOrderItem)}
|
||||
{getFilteredOrders('2').length > 0 ? (
|
||||
getFilteredOrders('2').map(renderOrderItem)
|
||||
) : (
|
||||
<Empty description="暂无二级分销订单" />
|
||||
)}
|
||||
</View>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane title="三级分销" value="3">
|
||||
<View className="p-4">
|
||||
{getFilteredOrders('3').length > 0 ? (
|
||||
getFilteredOrders('3').map(renderOrderItem)
|
||||
) : (
|
||||
<Empty description="暂无三级分销订单" />
|
||||
)}
|
||||
</View>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane title="待结算" value="4">
|
||||
<View className="p-4">
|
||||
{getFilteredOrders('4').length > 0 ? (
|
||||
getFilteredOrders('4').map(renderOrderItem)
|
||||
) : (
|
||||
<Empty description="暂无待结算订单" />
|
||||
)}
|
||||
</View>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane title="已结算" value="5">
|
||||
<View className="p-4">
|
||||
{getFilteredOrders('5').length > 0 ? (
|
||||
getFilteredOrders('5').map(renderOrderItem)
|
||||
) : (
|
||||
<Empty description="暂无已结算订单" />
|
||||
)}
|
||||
</View>
|
||||
</Tabs.TabPane>
|
||||
|
||||
<Tabs.TabPane title="已失效" value="6">
|
||||
<View className="p-4">
|
||||
{getFilteredOrders('6').length > 0 ? (
|
||||
getFilteredOrders('6').map(renderOrderItem)
|
||||
) : (
|
||||
<Empty description="暂无失效订单" />
|
||||
)}
|
||||
</View>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
|
||||
Reference in New Issue
Block a user