352 lines
12 KiB
Vue
352 lines
12 KiB
Vue
|
|
<script setup>
|
|||
|
|
import { ref, onMounted } from 'vue'
|
|||
|
|
import { useRoute } from 'vue-router'
|
|||
|
|
import useApiFetch from '@/composables/useApiFetch'
|
|||
|
|
|
|||
|
|
const route = useRoute()
|
|||
|
|
const loading = ref(false)
|
|||
|
|
const refreshing = ref(false)
|
|||
|
|
const finished = ref(false)
|
|||
|
|
const page = ref(1)
|
|||
|
|
const pageSize = 8
|
|||
|
|
|
|||
|
|
// 获取收益列表
|
|||
|
|
const fetchRewardDetails = async () => {
|
|||
|
|
if (loading.value || finished.value) return
|
|||
|
|
|
|||
|
|
loading.value = true
|
|||
|
|
const { data, error } = await useApiFetch(
|
|||
|
|
`/agent/subordinate/contribution/detail?subordinate_id=${route.params.id}&page=${page.value}&page_size=${pageSize}`
|
|||
|
|
)
|
|||
|
|
.get()
|
|||
|
|
.json()
|
|||
|
|
|
|||
|
|
if (data.value && !error.value) {
|
|||
|
|
if (data.value.code === 200) {
|
|||
|
|
if (page.value === 1) {
|
|||
|
|
// 更新用户信息
|
|||
|
|
userInfo.value = {
|
|||
|
|
createTime: data.value.data.create_time,
|
|||
|
|
level: data.value.data.level_name || '普通',
|
|||
|
|
mobile: data.value.data.mobile,
|
|||
|
|
}
|
|||
|
|
// 更新汇总数据
|
|||
|
|
summary.value = {
|
|||
|
|
totalReward: data.value.data.total_earnings,
|
|||
|
|
totalContribution: data.value.data.total_contribution,
|
|||
|
|
totalOrders: data.value.data.total_orders,
|
|||
|
|
}
|
|||
|
|
// 设置默认的统计类型
|
|||
|
|
statistics.value = [
|
|||
|
|
{
|
|||
|
|
type: 'descendant_promotion',
|
|||
|
|
amount: 0,
|
|||
|
|
count: 0,
|
|||
|
|
description: '推广奖励',
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
type: 'cost',
|
|||
|
|
amount: 0,
|
|||
|
|
count: 0,
|
|||
|
|
description: '成本贡献',
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
type: 'pricing',
|
|||
|
|
amount: 0,
|
|||
|
|
count: 0,
|
|||
|
|
description: '定价贡献',
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
type: 'descendant_withdraw',
|
|||
|
|
amount: 0,
|
|||
|
|
count: 0,
|
|||
|
|
description: '提现收益',
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
type: 'descendant_upgrade_vip',
|
|||
|
|
amount: 0,
|
|||
|
|
count: 0,
|
|||
|
|
description: '转化VIP奖励',
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
type: 'descendant_upgrade_svip',
|
|||
|
|
amount: 0,
|
|||
|
|
count: 0,
|
|||
|
|
description: '转化SVIP奖励',
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
type: 'descendant_new_active',
|
|||
|
|
amount: 0,
|
|||
|
|
count: 0,
|
|||
|
|
description: '新增活跃奖励',
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
type: 'descendant_stay_active',
|
|||
|
|
amount: 0,
|
|||
|
|
count: 0,
|
|||
|
|
description: '月度活跃奖励',
|
|||
|
|
},
|
|||
|
|
]
|
|||
|
|
// 如果有统计数据,更新对应的值
|
|||
|
|
if (data.value.data.stats) {
|
|||
|
|
const stats = data.value.data.stats
|
|||
|
|
// 更新推广奖励
|
|||
|
|
const platformStat = statistics.value.find(s => s.type === 'descendant_promotion')
|
|||
|
|
if (platformStat) {
|
|||
|
|
platformStat.amount = stats.descendant_promotion_amount || 0
|
|||
|
|
platformStat.count = stats.descendant_promotion_count || 0
|
|||
|
|
}
|
|||
|
|
// 更新成本贡献
|
|||
|
|
const costStat = statistics.value.find(s => s.type === 'cost')
|
|||
|
|
if (costStat) {
|
|||
|
|
costStat.amount = stats.cost_amount || 0
|
|||
|
|
costStat.count = stats.cost_count || 0
|
|||
|
|
}
|
|||
|
|
// 更新定价贡献
|
|||
|
|
const pricingStat = statistics.value.find(s => s.type === 'pricing')
|
|||
|
|
if (pricingStat) {
|
|||
|
|
pricingStat.amount = stats.pricing_amount || 0
|
|||
|
|
pricingStat.count = stats.pricing_count || 0
|
|||
|
|
}
|
|||
|
|
// 更新提现收益
|
|||
|
|
const withdrawStat = statistics.value.find(s => s.type === 'descendant_withdraw')
|
|||
|
|
if (withdrawStat) {
|
|||
|
|
withdrawStat.amount = stats.descendant_withdraw_amount || 0
|
|||
|
|
withdrawStat.count = stats.descendant_withdraw_count || 0
|
|||
|
|
}
|
|||
|
|
// 更新转化VIP奖励
|
|||
|
|
const conversionVipStat = statistics.value.find(s => s.type === 'descendant_upgrade_vip')
|
|||
|
|
if (conversionVipStat) {
|
|||
|
|
conversionVipStat.amount = stats.descendant_upgrade_vip_amount || 0
|
|||
|
|
conversionVipStat.count = stats.descendant_upgrade_vip_count || 0
|
|||
|
|
}
|
|||
|
|
// 更新转化SVIP奖励
|
|||
|
|
const conversionSvipStat = statistics.value.find(s => s.type === 'descendant_upgrade_svip')
|
|||
|
|
if (conversionSvipStat) {
|
|||
|
|
conversionSvipStat.amount = stats.descendant_upgrade_svip_amount || 0
|
|||
|
|
conversionSvipStat.count = stats.descendant_upgrade_svip_count || 0
|
|||
|
|
}
|
|||
|
|
// 更新活跃奖励
|
|||
|
|
const activeStat = statistics.value.find(s => s.type === 'descendant_new_active')
|
|||
|
|
if (activeStat) {
|
|||
|
|
activeStat.amount = stats.descendant_new_active_amount || 0
|
|||
|
|
activeStat.count = stats.descendant_new_active_count || 0
|
|||
|
|
}
|
|||
|
|
// 更新月度活跃奖励
|
|||
|
|
const monthlyActiveStat = statistics.value.find(s => s.type === 'descendant_stay_active')
|
|||
|
|
if (monthlyActiveStat) {
|
|||
|
|
monthlyActiveStat.amount = stats.descendant_stay_active_amount || 0
|
|||
|
|
monthlyActiveStat.count = stats.descendant_stay_active_count || 0
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
rewardDetails.value = []
|
|||
|
|
}
|
|||
|
|
// 处理列表数据
|
|||
|
|
if (data.value.data.list) {
|
|||
|
|
if (page.value === 1) {
|
|||
|
|
rewardDetails.value = data.value.data.list
|
|||
|
|
} else {
|
|||
|
|
rewardDetails.value.push(...data.value.data.list)
|
|||
|
|
}
|
|||
|
|
finished.value = data.value.data.list.length < pageSize
|
|||
|
|
} else {
|
|||
|
|
finished.value = true
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
loading.value = false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 下拉刷新
|
|||
|
|
const onRefresh = () => {
|
|||
|
|
finished.value = false
|
|||
|
|
page.value = 1
|
|||
|
|
fetchRewardDetails().finally(() => {
|
|||
|
|
refreshing.value = false
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const rewardDetails = ref([])
|
|||
|
|
const userInfo = ref({})
|
|||
|
|
const summary = ref({})
|
|||
|
|
const statistics = ref([])
|
|||
|
|
|
|||
|
|
onMounted(() => {
|
|||
|
|
fetchRewardDetails()
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 获取收益类型样式
|
|||
|
|
const getRewardTypeClass = type => {
|
|||
|
|
const typeMap = {
|
|||
|
|
descendant_promotion: 'bg-blue-100 text-blue-600',
|
|||
|
|
cost: 'bg-green-100 text-green-600',
|
|||
|
|
pricing: 'bg-purple-100 text-purple-600',
|
|||
|
|
descendant_withdraw: 'bg-yellow-100 text-yellow-600',
|
|||
|
|
descendant_upgrade_vip: 'bg-red-100 text-red-600',
|
|||
|
|
descendant_upgrade_svip: 'bg-orange-100 text-orange-600',
|
|||
|
|
descendant_new_active: 'bg-indigo-100 text-indigo-600',
|
|||
|
|
descendant_stay_active: 'bg-pink-100 text-pink-600',
|
|||
|
|
}
|
|||
|
|
return typeMap[type] || 'bg-gray-100 text-gray-600'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取收益类型图标
|
|||
|
|
const getRewardTypeIcon = type => {
|
|||
|
|
const iconMap = {
|
|||
|
|
descendant_promotion: 'gift',
|
|||
|
|
cost: 'gold-coin',
|
|||
|
|
pricing: 'balance-pay',
|
|||
|
|
descendant_withdraw: 'cash-back-record',
|
|||
|
|
descendant_upgrade_vip: 'fire',
|
|||
|
|
descendant_upgrade_svip: 'fire',
|
|||
|
|
descendant_new_active: 'medal',
|
|||
|
|
descendant_stay_active: 'medal',
|
|||
|
|
}
|
|||
|
|
return iconMap[type] || 'balance-o'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取收益类型描述
|
|||
|
|
const getRewardTypeDescription = type => {
|
|||
|
|
const descriptionMap = {
|
|||
|
|
descendant_promotion: '推广奖励',
|
|||
|
|
cost: '成本贡献',
|
|||
|
|
pricing: '定价贡献',
|
|||
|
|
descendant_withdraw: '提现收益',
|
|||
|
|
descendant_upgrade_vip: '转化VIP奖励',
|
|||
|
|
descendant_upgrade_svip: '转化SVIP奖励',
|
|||
|
|
descendant_new_active: '新增活跃奖励',
|
|||
|
|
descendant_stay_active: '月度活跃奖励',
|
|||
|
|
}
|
|||
|
|
return descriptionMap[type] || '未知类型'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 格式化时间
|
|||
|
|
const formatTime = timeStr => {
|
|||
|
|
if (!timeStr) return '-'
|
|||
|
|
const date = new Date(timeStr)
|
|||
|
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 格式化金额
|
|||
|
|
const formatNumber = num => {
|
|||
|
|
if (!num) return '0.00'
|
|||
|
|
return Number(num).toFixed(2)
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<template>
|
|||
|
|
<div class="reward-detail">
|
|||
|
|
<!-- 用户信息卡片 -->
|
|||
|
|
<div class="p-4">
|
|||
|
|
<div class="bg-white rounded-xl shadow-sm p-5 mb-4">
|
|||
|
|
<div class="flex items-center justify-between mb-4">
|
|||
|
|
<div class="flex items-center space-x-3">
|
|||
|
|
<div class="text-xl font-semibold text-gray-800">{{ userInfo.mobile }}</div>
|
|||
|
|
<span class="px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-600">
|
|||
|
|
{{ userInfo.level }}代理
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="text-sm text-gray-500 mb-4">成为下级代理时间:{{ formatTime(userInfo.createTime) }}</div>
|
|||
|
|
<div class="grid grid-cols-3 gap-4">
|
|||
|
|
<div class="text-center">
|
|||
|
|
<div class="text-gray-500 text-sm mb-1">总推广单量</div>
|
|||
|
|
<div class="text-xl font-semibold text-blue-600">{{ summary.totalOrders }}</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="text-center">
|
|||
|
|
<div class="text-gray-500 text-sm mb-1">总收益</div>
|
|||
|
|
<div class="text-xl font-semibold text-green-600">¥{{ formatNumber(summary.totalReward) }}</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="text-center">
|
|||
|
|
<div class="text-gray-500 text-sm mb-1">总贡献</div>
|
|||
|
|
<div class="text-xl font-semibold text-purple-600">¥{{ formatNumber(summary.totalContribution) }}</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 贡献统计卡片 -->
|
|||
|
|
<div class="bg-white rounded-xl shadow-sm p-4 mb-4">
|
|||
|
|
<div class="text-base font-medium text-gray-800 mb-3">贡献统计</div>
|
|||
|
|
<div class="grid grid-cols-2 gap-3">
|
|||
|
|
<div
|
|||
|
|
v-for="item in statistics"
|
|||
|
|
:key="item.type"
|
|||
|
|
class="flex items-center p-2 rounded-lg"
|
|||
|
|
:class="getRewardTypeClass(item.type).split(' ')[0]"
|
|||
|
|
>
|
|||
|
|
<van-icon
|
|||
|
|
:name="getRewardTypeIcon(item.type)"
|
|||
|
|
class="text-lg mr-2"
|
|||
|
|
:class="getRewardTypeClass(item.type).split(' ')[1]"
|
|||
|
|
/>
|
|||
|
|
<div class="flex-1">
|
|||
|
|
<div class="text-sm font-medium" :class="getRewardTypeClass(item.type).split(' ')[1]">
|
|||
|
|
{{ item.description }}
|
|||
|
|
</div>
|
|||
|
|
<div class="flex justify-between items-center mt-1">
|
|||
|
|
<div class="text-xs text-gray-500">{{ item.count }} 次</div>
|
|||
|
|
<div class="text-sm font-medium" :class="getRewardTypeClass(item.type).split(' ')[1]">
|
|||
|
|
¥{{ formatNumber(item.amount) }}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="bg-white rounded-xl shadow-sm p-4 mb-4">
|
|||
|
|
<!-- 贡献记录列表 -->
|
|||
|
|
<div class="text-base font-medium text-gray-800">贡献记录</div>
|
|||
|
|
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
|
|||
|
|
<van-list
|
|||
|
|
v-model:loading="loading"
|
|||
|
|
:finished="finished"
|
|||
|
|
finished-text="没有更多了"
|
|||
|
|
@load="fetchRewardDetails"
|
|||
|
|
>
|
|||
|
|
<div class="p-4">
|
|||
|
|
<div v-if="rewardDetails.length === 0" class="text-center text-gray-500 py-8">暂无贡献记录</div>
|
|||
|
|
<div v-else v-for="item in rewardDetails" :key="item.id" class="reward-item">
|
|||
|
|
<div class="mb-3 border-b border-gray-200 pb-3">
|
|||
|
|
<div class="flex items-center justify-between">
|
|||
|
|
<div class="flex items-center space-x-3">
|
|||
|
|
<van-icon
|
|||
|
|
:name="getRewardTypeIcon(item.type)"
|
|||
|
|
class="text-lg"
|
|||
|
|
:class="getRewardTypeClass(item.type).split(' ')[1]"
|
|||
|
|
/>
|
|||
|
|
<div>
|
|||
|
|
<div class="font-medium text-gray-800">{{ getRewardTypeDescription(item.type) }}</div>
|
|||
|
|
<div class="text-xs text-gray-500">{{ formatTime(item.create_time) }}</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="text-right">
|
|||
|
|
<div class="text-base font-semibold" :class="getRewardTypeClass(item.type).split(' ')[1]">
|
|||
|
|
¥{{ formatNumber(item.amount) }}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</van-list>
|
|||
|
|
</van-pull-refresh>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.reward-detail {
|
|||
|
|
min-height: 100vh;
|
|||
|
|
background-color: #f5f5f5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.reward-item {
|
|||
|
|
transition: transform 0.2s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.reward-item:active {
|
|||
|
|
transform: scale(0.98);
|
|||
|
|
}
|
|||
|
|
</style>
|