Files
qnc-webview-v3/src/views/SubordinateDetail.vue
2026-05-13 14:43:38 +08:00

301 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import { ref } from 'vue'
import { useRoute } from 'vue-router'
import useApiFetch from '@/composables/useApiFetch'
import EmptyState from '@/components/EmptyState.vue'
const route = useRoute()
const loading = ref(false)
const refreshing = ref(false)
const finished = ref(false)
const page = ref(1)
const pageSize = 8
const activeTab = ref('order') // 'order' 或 'invite'
// 数据
const userInfo = ref({})
const orderStats = ref({})
const rebateStats = ref({})
const inviteStats = ref({})
const orderList = ref([])
const inviteList = ref([])
const orderListTotal = ref(0)
const inviteListTotal = ref(0)
// 获取详情数据
const fetchDetail = async () => {
loading.value = true
const tabType = activeTab.value
const { data, error } = await useApiFetch(
`/agent/subordinate/contribution/detail?subordinate_id=${route.params.id}&page=${page.value}&page_size=${pageSize}&tab_type=${tabType}`
)
.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,
}
// 更新统计数据
orderStats.value = data.value.data.order_stats || {}
rebateStats.value = data.value.data.rebate_stats || {}
inviteStats.value = data.value.data.invite_stats || {}
// 清空列表
if (tabType === 'order') {
orderList.value = []
} else {
inviteList.value = []
}
}
// 处理列表数据
if (tabType === 'order') {
if (data.value.data.order_list) {
if (page.value === 1) {
orderList.value = data.value.data.order_list
} else {
orderList.value.push(...data.value.data.order_list)
}
orderListTotal.value = data.value.data.order_list_total || 0
finished.value = data.value.data.order_list.length < pageSize
if (!finished.value) {
page.value++
}
} else {
finished.value = true
}
} else {
if (data.value.data.invite_list) {
if (page.value === 1) {
inviteList.value = data.value.data.invite_list
} else {
inviteList.value.push(...data.value.data.invite_list)
}
inviteListTotal.value = data.value.data.invite_list_total || 0
finished.value = data.value.data.invite_list.length < pageSize
if (!finished.value) {
page.value++
}
} else {
finished.value = true
}
}
}
}
loading.value = false
}
// 切换标签页
const switchTab = (tab) => {
if (activeTab.value === tab) return
activeTab.value = tab
page.value = 1
finished.value = false
loading.value = false
// 延迟一下确保tab切换完成
setTimeout(() => {
fetchDetail()
}, 100)
}
// 下拉刷新
const onRefresh = () => {
finished.value = false
page.value = 1
fetchDetail().finally(() => {
refreshing.value = false
})
}
// 获取等级标签样式
const getLevelClass = (level) => {
const levelNum = typeof level === 'number' ? level : parseInt(level)
switch (levelNum) {
case 3:
return 'bg-purple-100 text-purple-600'
case 2:
return 'bg-yellow-100 text-yellow-600'
case 1:
default:
return 'bg-gray-100 text-gray-600'
}
}
// 获取等级显示名称
const getLevelName = (level) => {
const levelNum = typeof level === 'number' ? level : parseInt(level)
const levelMap = {
1: '普通',
2: '黄金',
3: '钻石'
}
return levelMap[levelNum] || '普通'
}
// 格式化时间
const formatTime = timeStr => {
if (!timeStr) return '-'
return timeStr.split(' ')[0]
}
// 格式化金额
const formatNumber = num => {
if (!num) return '0.00'
return Number(num).toFixed(2)
}
</script>
<template>
<div class="subordinate-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>
<!-- 订单统计卡片 -->
<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-3 gap-3">
<div class="text-center">
<div class="text-gray-500 text-sm mb-1">总订单量</div>
<div class="text-xl font-semibold text-blue-600">{{ orderStats.total_orders || 0 }}</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">{{ orderStats.month_orders || 0 }}</div>
</div>
<div class="text-center">
<div class="text-gray-500 text-sm mb-1">今日订单</div>
<div class="text-xl font-semibold text-orange-600">{{ orderStats.today_orders || 0 }}</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-3 gap-3">
<div class="text-center">
<div class="text-gray-500 text-sm mb-1">总返佣金额</div>
<div class="text-xl font-semibold text-blue-600">¥{{ formatNumber(rebateStats.total_rebate_amount) }}</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(rebateStats.month_rebate_amount) }}</div>
</div>
<div class="text-center">
<div class="text-gray-500 text-sm mb-1">今日返佣金额</div>
<div class="text-xl font-semibold text-orange-600">¥{{ formatNumber(rebateStats.today_rebate_amount) }}
</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-3 gap-3">
<div class="text-center">
<div class="text-gray-500 text-sm mb-1">总邀请</div>
<div class="text-xl font-semibold text-blue-600">{{ inviteStats.total_invites || 0 }}</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">{{ inviteStats.month_invites || 0 }}</div>
</div>
<div class="text-center">
<div class="text-gray-500 text-sm mb-1">今日邀请</div>
<div class="text-xl font-semibold text-orange-600">{{ inviteStats.today_invites || 0 }}</div>
</div>
</div>
</div>
<!-- Tab标签页 -->
<div class="bg-white rounded-xl shadow-sm p-4 mb-4">
<van-tabs v-model:active="activeTab" @change="switchTab">
<van-tab title="订单列表" name="order">
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
<van-list v-model:loading="loading" :finished="finished" :finished-text="orderList.length > 0 ? '没有更多了' : ''" @load="fetchDetail">
<div class="p-2">
<div v-for="item in orderList" :key="item.order_no"
class="order-item mb-3 border-b border-gray-200 pb-3">
<div class="flex items-center justify-between">
<div class="flex-1">
<div class="font-medium text-gray-800 mb-1">{{ item.product_name || '未知产品' }}</div>
<div class="text-xs text-gray-500 mb-1">订单号:{{ item.order_no }}</div>
<div class="text-xs text-gray-500">{{ formatTime(item.create_time) }}</div>
</div>
<div class="text-right ml-4">
<div class="text-sm text-gray-500 mb-1">订单金额</div>
<div class="text-base font-semibold text-blue-600 mb-2">¥{{ formatNumber(item.order_amount) }}
</div>
<div class="text-sm text-gray-500 mb-1">返佣金额</div>
<div class="text-base font-semibold text-green-600">¥{{ formatNumber(item.rebate_amount) }}
</div>
</div>
</div>
</div>
</div>
</van-list>
</van-pull-refresh>
<EmptyState v-if="!loading && orderList.length === 0" text="暂无订单记录" />
</van-tab>
<van-tab title="邀请列表" name="invite">
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
<van-list v-model:loading="loading" :finished="finished" :finished-text="inviteList.length > 0 ? '没有更多了' : ''" @load="fetchDetail">
<div class="p-2">
<div v-for="item in inviteList" :key="item.agent_id"
class="invite-item mb-3 border-b border-gray-200 pb-3">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-3 flex-1">
<div class="text-lg font-semibold text-gray-800">{{ item.mobile }}</div>
<span :class="['px-3 py-1 rounded-full text-sm font-medium', getLevelClass(item.level)]">
{{ item.level_name }}代理
</span>
</div>
<div class="text-xs text-gray-500">{{ formatTime(item.create_time) }}</div>
</div>
</div>
</div>
</van-list>
</van-pull-refresh>
<EmptyState v-if="!loading && inviteList.length === 0" text="暂无邀请记录" />
</van-tab>
</van-tabs>
</div>
</div>
</div>
</template>
<style scoped>
.subordinate-detail {
min-height: 100vh;
background-color: #f5f5f5;
}
.order-item,
.invite-item {
transition: transform 0.2s;
}
.order-item:active,
.invite-item:active {
transform: scale(0.98);
}
</style>