diff --git a/frontend/admin-web/src/app/(dashboard)/dashboard/dashboard.module.scss b/frontend/admin-web/src/app/(dashboard)/dashboard/dashboard.module.scss
index 322e91d8..ce93a4f8 100644
--- a/frontend/admin-web/src/app/(dashboard)/dashboard/dashboard.module.scss
+++ b/frontend/admin-web/src/app/(dashboard)/dashboard/dashboard.module.scss
@@ -20,6 +20,10 @@
}
}
+ &__statsError {
+ grid-column: 1 / -1;
+ }
+
&__charts {
display: grid;
grid-template-columns: 1fr 360px;
@@ -47,4 +51,89 @@
&__regionChart {
min-height: 300px;
}
+
+ // 骨架屏样式
+ &__skeleton {
+ background: $color-bg-secondary;
+ border-radius: $border-radius-lg;
+ padding: $spacing-lg;
+ display: flex;
+ flex-direction: column;
+ gap: $spacing-md;
+ }
+
+ &__skeletonTitle {
+ width: 60%;
+ height: 16px;
+ background: linear-gradient(90deg, $color-border 25%, $color-bg-tertiary 50%, $color-border 75%);
+ background-size: 200% 100%;
+ animation: shimmer 1.5s infinite;
+ border-radius: $border-radius-sm;
+ }
+
+ &__skeletonValue {
+ width: 80%;
+ height: 32px;
+ background: linear-gradient(90deg, $color-border 25%, $color-bg-tertiary 50%, $color-border 75%);
+ background-size: 200% 100%;
+ animation: shimmer 1.5s infinite;
+ border-radius: $border-radius-sm;
+ }
+
+ &__skeletonChange {
+ width: 40%;
+ height: 14px;
+ background: linear-gradient(90deg, $color-border 25%, $color-bg-tertiary 50%, $color-border 75%);
+ background-size: 200% 100%;
+ animation: shimmer 1.5s infinite;
+ border-radius: $border-radius-sm;
+ }
+
+ // 错误提示样式
+ &__error {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: $spacing-md;
+ padding: $spacing-xl;
+ background: $color-bg-secondary;
+ border-radius: $border-radius-lg;
+ color: $color-text-secondary;
+ min-height: 120px;
+ }
+
+ // 空数据提示样式
+ &__empty {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: $spacing-xl;
+ background: $color-bg-secondary;
+ border-radius: $border-radius-lg;
+ color: $color-text-tertiary;
+ grid-column: 1 / -1;
+ min-height: 120px;
+ }
+
+ // 加载中提示
+ &__activitiesLoading,
+ &__regionLoading {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: $color-bg-secondary;
+ border-radius: $border-radius-lg;
+ color: $color-text-secondary;
+ min-height: 100%;
+ }
+}
+
+@keyframes shimmer {
+ 0% {
+ background-position: 200% 0;
+ }
+ 100% {
+ background-position: -200% 0;
+ }
}
diff --git a/frontend/admin-web/src/app/(dashboard)/dashboard/page.tsx b/frontend/admin-web/src/app/(dashboard)/dashboard/page.tsx
index 28c1a5ec..ffef9a2d 100644
--- a/frontend/admin-web/src/app/(dashboard)/dashboard/page.tsx
+++ b/frontend/admin-web/src/app/(dashboard)/dashboard/page.tsx
@@ -1,110 +1,81 @@
'use client';
+import { useState } from 'react';
import { Button } from '@/components/common';
import { PageContainer } from '@/components/layout';
import { StatCard } from '@/components/features/dashboard/StatCard';
import { TrendChart } from '@/components/features/dashboard/TrendChart';
import { RegionDistribution } from '@/components/features/dashboard/RegionDistribution';
import { RecentActivity } from '@/components/features/dashboard/RecentActivity';
+import {
+ useDashboardStats,
+ useDashboardTrend,
+ useDashboardRegion,
+ useDashboardActivities,
+} from '@/hooks';
+import type { DashboardPeriod } from '@/types';
import styles from './dashboard.module.scss';
-// 模拟统计数据
-const statsData = [
- {
- title: '总认种量',
- value: 12580,
- suffix: '棵',
- change: { value: 5.6, trend: 'up' as const },
- color: '#1565C0',
- },
- {
- title: '活跃用户',
- value: 3240,
- suffix: '人',
- change: { value: 3.2, trend: 'up' as const },
- color: '#4CAF50',
- },
- {
- title: '省级公司',
- value: 28,
- suffix: '家',
- change: { value: 2.1, trend: 'up' as const },
- color: '#F5A623',
- },
- {
- title: '市级公司',
- value: 156,
- suffix: '家',
- change: { value: 4.8, trend: 'up' as const },
- color: '#9C27B0',
- },
-];
+// 骨架屏组件
+const StatCardSkeleton = () => (
+
+);
-// 模拟趋势数据
-const trendData = [
- { date: '03-19', value: 150 },
- { date: '03-20', value: 280 },
- { date: '03-21', value: 320 },
- { date: '03-22', value: 250 },
- { date: '03-23', value: 380 },
- { date: '03-24', value: 420 },
- { date: '03-25', value: 390 },
-];
+// 错误提示组件
+const ErrorMessage = ({ message, onRetry }: { message: string; onRetry?: () => void }) => (
+
+ {message}
+ {onRetry && (
+
+ )}
+
+);
-// 模拟区域分布数据
-const regionData = [
- { region: '华东地区', percentage: 35, color: '#1565C0' },
- { region: '华南地区', percentage: 25, color: '#4CAF50' },
- { region: '华北地区', percentage: 20, color: '#F5A623' },
- { region: '华中地区', percentage: 12, color: '#9C27B0' },
- { region: '其他地区', percentage: 8, color: '#607D8B' },
-];
-
-// 模拟最近活动数据
-const activityData = [
- {
- id: '1',
- type: 'user_register' as const,
- icon: '👤',
- title: '新用户注册',
- description: '用户 张三 完成注册',
- timestamp: '5分钟前',
- },
- {
- id: '2',
- type: 'company_activity' as const,
- icon: '🏢',
- title: '公司授权',
- description: '广东省公司完成授权',
- timestamp: '15分钟前',
- },
- {
- id: '3',
- type: 'system_update' as const,
- icon: '⚙️',
- title: '系统更新',
- description: '龙虎榜规则已更新',
- timestamp: '1小时前',
- },
- {
- id: '4',
- type: 'report_generated' as const,
- icon: '📊',
- title: '报表生成',
- description: '3月份运营报表已生成',
- timestamp: '2小时前',
- },
- {
- id: '5',
- type: 'user_register' as const,
- icon: '👤',
- title: '新用户注册',
- description: '用户 李四 完成注册',
- timestamp: '3小时前',
- },
-];
+// 空数据提示
+const EmptyData = ({ message }: { message: string }) => (
+
+ {message}
+
+);
export default function DashboardPage() {
+ const [trendPeriod, setTrendPeriod] = useState('7d');
+
+ // 使用 React Query hooks 获取数据
+ const {
+ data: statsData,
+ isLoading: statsLoading,
+ error: statsError,
+ refetch: refetchStats,
+ } = useDashboardStats();
+
+ const {
+ data: trendData,
+ isLoading: trendLoading,
+ error: trendError,
+ refetch: refetchTrend,
+ } = useDashboardTrend(trendPeriod);
+
+ const {
+ data: regionData,
+ isLoading: regionLoading,
+ error: regionError,
+ refetch: refetchRegion,
+ } = useDashboardRegion();
+
+ const {
+ data: activitiesData,
+ isLoading: activitiesLoading,
+ error: activitiesError,
+ refetch: refetchActivities,
+ } = useDashboardActivities(5);
+
const headerActions = (
<>