- 新建 RefreshCountdown 组件,内部持有 timer,消除 App 每秒重渲染 - TargetBoard 分组逻辑 useMemo 化,避免 targets 引用不变时重复计算 - TargetGroup 加 React.memo,阻断无效渲染 - TrendChart 加 React.memo + chartData useMemo,避免 recharts 不必要重绘 - OverviewTab 统计项去掉 Card 包裹,改用纯 CSS 实现视觉效果 - 同步更新 refresh-control 和 target-detail-drawer spec 性能提升:消除每秒全组件树重渲染,减少 DOM 节点数
53 lines
1.4 KiB
TypeScript
53 lines
1.4 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { RefreshIcon } from "tdesign-icons-react";
|
|
import { Button, Typography } from "tdesign-react";
|
|
|
|
import { formatCountdown } from "../utils/time";
|
|
|
|
interface RefreshCountdownProps {
|
|
dashboardUpdatedAt: number;
|
|
isFetching: boolean;
|
|
isManualRefresh: boolean;
|
|
onRefresh: () => void;
|
|
refreshInterval: number;
|
|
}
|
|
|
|
export function RefreshCountdown({
|
|
dashboardUpdatedAt,
|
|
isFetching,
|
|
isManualRefresh,
|
|
onRefresh,
|
|
refreshInterval,
|
|
}: RefreshCountdownProps) {
|
|
const [now, setNow] = useState(() => new Date());
|
|
|
|
useEffect(() => {
|
|
const timer = window.setInterval(() => setNow(new Date()), 1000);
|
|
return () => window.clearInterval(timer);
|
|
}, []);
|
|
|
|
const nextRefreshSeconds =
|
|
dashboardUpdatedAt > 0 && !isManualRefresh
|
|
? Math.max(0, Math.ceil((dashboardUpdatedAt + refreshInterval - now.getTime()) / 1000))
|
|
: null;
|
|
|
|
if (isManualRefresh) {
|
|
return (
|
|
<Button
|
|
aria-label="刷新 Dashboard"
|
|
disabled={isFetching}
|
|
icon={<RefreshIcon />}
|
|
loading={isFetching}
|
|
onClick={() => void onRefresh()}
|
|
shape="circle"
|
|
variant="outline"
|
|
/>
|
|
);
|
|
}
|
|
|
|
const refreshText =
|
|
dashboardUpdatedAt > 0 ? (isFetching ? "刷新中..." : formatCountdown(nextRefreshSeconds ?? 0)) : "等待首次刷新";
|
|
|
|
return <Typography.Text theme="secondary">{refreshText}</Typography.Text>;
|
|
}
|