2025-04-07 16:56:13 +08:00

209 lines
6.2 KiB
Vue

<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { statsAPI } from '../services/api'
import { Bar, Pie, Doughnut } from 'vue-chartjs'
import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, ArcElement } from 'chart.js'
// Register ChartJS components
ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale, ArcElement)
// Stats data
const stats = ref({
user_count: 0,
prizes_awarded: 0,
prizes_shipped: 0,
cards_collected: 0,
prize_distribution: [] as { name: string, count: number }[]
})
// Load stats
const loadStats = async () => {
try {
const data = await statsAPI.getStats()
stats.value = data
} catch (error) {
console.error('Failed to load stats:', error)
}
}
// Chart data
const prizeDistributionData = computed(() => {
return {
labels: stats.value.prize_distribution.map(item => item.name),
datasets: [
{
label: '奖品分布',
backgroundColor: [
'#6A5ACD', // primary
'#FF6B6B', // secondary
'#4ECDC4', // accent
'#2ECC71', // success
'#FFD700', // warning
],
data: stats.value.prize_distribution.map(item => item.count),
}
]
}
})
const prizesStatusData = computed(() => {
return {
labels: ['已发放', '待发放'],
datasets: [
{
backgroundColor: ['#2ECC71', '#FFD700'],
data: [stats.value.prizes_shipped, stats.value.prizes_awarded - stats.value.prizes_shipped],
}
]
}
})
const userActivityData = computed(() => {
return {
labels: ['用户数', '收集卡片数'],
datasets: [
{
label: '用户活动统计',
backgroundColor: ['#6A5ACD', '#4ECDC4'],
data: [stats.value.user_count, stats.value.cards_collected],
}
]
}
})
// Chart options
const chartOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: '抽奖系统数据分析'
}
}
}
// Initialize
onMounted(() => {
loadStats()
})
</script>
<template>
<div class="dashboard">
<h1 class="text-2xl font-bold text-primary mb-6">系统数据看板</h1>
<!-- Stat cards -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
<div class="card bg-primary/10">
<div class="flex items-center">
<div class="rounded-full bg-primary w-12 h-12 flex items-center justify-center text-white">
<i class="fas fa-users"></i>
</div>
<div class="ml-4">
<div class="text-sm text-gray-500">用户总数</div>
<div class="text-2xl font-bold">{{ stats.user_count }}</div>
</div>
</div>
</div>
<div class="card bg-secondary/10">
<div class="flex items-center">
<div class="rounded-full bg-secondary w-12 h-12 flex items-center justify-center text-white">
<i class="fas fa-gift"></i>
</div>
<div class="ml-4">
<div class="text-sm text-gray-500">奖品发放</div>
<div class="text-2xl font-bold">{{ stats.prizes_awarded }}</div>
</div>
</div>
</div>
<div class="card bg-success/10">
<div class="flex items-center">
<div class="rounded-full bg-success w-12 h-12 flex items-center justify-center text-white">
<i class="fas fa-shipping-fast"></i>
</div>
<div class="ml-4">
<div class="text-sm text-gray-500">已发货</div>
<div class="text-2xl font-bold">{{ stats.prizes_shipped }}</div>
</div>
</div>
</div>
<div class="card bg-accent/10">
<div class="flex items-center">
<div class="rounded-full bg-accent w-12 h-12 flex items-center justify-center text-white">
<i class="fas fa-layer-group"></i>
</div>
<div class="ml-4">
<div class="text-sm text-gray-500">卡片收集</div>
<div class="text-2xl font-bold">{{ stats.cards_collected }}</div>
</div>
</div>
</div>
</div>
<!-- Charts -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
<div class="card">
<h2 class="text-lg font-bold mb-4">奖品分布情况</h2>
<div class="h-80">
<Pie
:data="prizeDistributionData"
:options="chartOptions"
/>
</div>
</div>
<div class="card">
<h2 class="text-lg font-bold mb-4">奖品发放状态</h2>
<div class="h-80">
<Doughnut
:data="prizesStatusData"
:options="chartOptions"
/>
</div>
</div>
<div class="card">
<h2 class="text-lg font-bold mb-4">用户活动统计</h2>
<div class="h-80">
<Bar
:data="userActivityData"
:options="chartOptions"
/>
</div>
</div>
</div>
<!-- Prize distribution table -->
<div class="card">
<h2 class="text-lg font-bold mb-4">奖品发放明细</h2>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">奖品名称</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">发放数量</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">百分比</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr v-for="item in stats.prize_distribution" :key="item.name">
<td class="px-6 py-4 whitespace-nowrap">{{ item.name }}</td>
<td class="px-6 py-4 whitespace-nowrap">{{ item.count }}</td>
<td class="px-6 py-4 whitespace-nowrap">
{{ stats.prizes_awarded > 0 ? Math.round((item.count / stats.prizes_awarded) * 100) : 0 }}%
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>