// v1的时候就连统计信息也5秒获取一次,不妥,所以改成逻辑触发刷新 import React, { useState, useEffect, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import { makeStyles, tokens, Card, CardHeader, Text, Spinner, Badge, Button, useToastController, Toast, ToastTitle, Divider, } from '@fluentui/react-components'; import { CheckmarkCircle20Filled, DismissCircle20Filled, ArrowClockwise20Regular } from '@fluentui/react-icons'; import { useLayout } from '../context/LayoutContext'; import { testApiStatus, getStatics, getHotTopics } from '../api'; import type { StaticsData, HotTopicItem } from '../api'; const useStyles = makeStyles({ container: { display: 'flex', flexDirection: 'column', gap: tokens.spacingVerticalS, width: '100%', }, card: { width: '100%', }, statusContainer: { padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalM}`, paddingBottom: tokens.spacingVerticalM, }, statusText: { display: 'flex', alignItems: 'center', gap: tokens.spacingHorizontalSNudge, }, online: { color: tokens.colorStatusSuccessForeground1, fontSize: tokens.fontSizeBase200, }, offline: { color: tokens.colorStatusDangerForeground1, fontSize: tokens.fontSizeBase200, }, statsContainer: { padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalM}`, paddingBottom: tokens.spacingVerticalM, display: 'flex', flexDirection: 'column', gap: '4px', }, statRow: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', }, refreshButton: { minWidth: 'auto', padding: '2px', }, labelText: { fontSize: tokens.fontSizeBase200, }, headerText: { fontSize: tokens.fontSizeBase300, fontWeight: tokens.fontWeightSemibold, }, topicList: { padding: `${tokens.spacingVerticalS} ${tokens.spacingHorizontalM}`, display: 'flex', flexDirection: 'column', gap: tokens.spacingVerticalS, }, topicRow: { display: 'flex', flexDirection: 'column', gap: tokens.spacingVerticalXXS, padding: `${tokens.spacingVerticalS} 0`, }, topicName: { fontSize: tokens.fontSizeBase300, color: tokens.colorNeutralForeground1, }, topicCount: { fontSize: tokens.fontSizeBase200, color: tokens.colorNeutralForeground3, }, }); const Widgets: React.FC = () => { const styles = useStyles(); const { toasterId, refreshTrigger, staticsRefreshTrigger } = useLayout(); const { dispatchToast } = useToastController(toasterId); const navigate = useNavigate(); const [isApiOnline, setIsApiOnline] = useState(null); const [statics, setStatics] = useState(null); const [topics, setTopics] = useState([]); const [isTestLoading, setIsTestLoading] = useState(true); const [isStaticsLoading, setIsStaticsLoading] = useState(true); const [isTopicsLoading, setIsTopicsLoading] = useState(true); const checkStatus = useCallback(async () => { const online = await testApiStatus(); setIsApiOnline(online); setIsTestLoading(false); }, []); const refreshWidgets = useCallback(async (isManual: boolean = false) => { setIsStaticsLoading(true); setIsTopicsLoading(true); try { const [staticsData, topicsData] = await Promise.all([getStatics(), getHotTopics()]); setStatics(staticsData); setTopics(topicsData); if (isManual) { dispatchToast( 小组件数据已刷新 , { intent: 'success' } ); } } catch (error) { console.error('Failed to refresh statics:', error); setStatics(null); // 失败时重置数据 setTopics([]); } finally { setIsStaticsLoading(false); setIsTopicsLoading(false); } }, [dispatchToast]); useEffect(() => { // 初始加载 checkStatus(); refreshWidgets(false); // 每 10 秒测试一次后端 API 状态 const testInterval = setInterval(checkStatus, 10000); return () => { clearInterval(testInterval); }; }, [checkStatus, refreshWidgets]); // Listen for global refresh trigger useEffect(() => { if (refreshTrigger > 0) { refreshWidgets(false); } }, [refreshTrigger, refreshWidgets]); useEffect(() => { if (staticsRefreshTrigger > 0) { refreshWidgets(false); } }, [staticsRefreshTrigger, refreshWidgets]); return (
系统状态} />
{isTestLoading && isApiOnline === null ? ( ) : (
{isApiOnline ? ( <> 在线 ) : ( <> 离线 )}
)}
统计数据} />
{isStaticsLoading && !statics ? ( ) : statics ? ( <>
投稿数量: {statics.posts}
评论数量: {statics.comments}
图片数量: {statics.images}
) : ( 统计数据获取失败。 )}
# 趋势} action={
); }; export default Widgets;