mirror of
https://github.com/fankes/komari-theme-purcarte.git
synced 2025-10-18 11:29:22 +08:00
perf: 优化样式和跳转返回页面状态
This commit is contained in:
@@ -84,10 +84,9 @@ const Flag = React.memo(({ flag, size }: FlagProps) => {
|
||||
return (
|
||||
<Box
|
||||
as="span"
|
||||
className={`self-center flex-shrink-0 ${
|
||||
className={`self-center flex-shrink-0 inline-flex items-center ${
|
||||
size ? `w-${size} h-${size}` : "w-6 h-6"
|
||||
}`}
|
||||
style={{ display: "inline-flex", alignItems: "center" }}
|
||||
aria-label={altText}>
|
||||
<img
|
||||
src={imgSrc}
|
||||
|
@@ -62,7 +62,7 @@ export const StatsBar = ({
|
||||
return (
|
||||
displayOptions.time && (
|
||||
<div className="w-full py-1" key="time">
|
||||
<div className="rt-Flex rt-r-fd-column rt-r-gap-2">
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-secondary-foreground text-sm">
|
||||
当前时间
|
||||
</label>
|
||||
@@ -77,7 +77,7 @@ export const StatsBar = ({
|
||||
return (
|
||||
displayOptions.online && (
|
||||
<div className="w-full py-1" key="online">
|
||||
<div className="rt-Flex rt-r-fd-column rt-r-gap-2">
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-secondary-foreground text-sm">
|
||||
当前在线
|
||||
</label>
|
||||
@@ -94,7 +94,7 @@ export const StatsBar = ({
|
||||
return (
|
||||
displayOptions.regions && (
|
||||
<div className="w-full py-1" key="regions">
|
||||
<div className="rt-Flex rt-r-fd-column rt-r-gap-2">
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-secondary-foreground text-sm">
|
||||
点亮地区
|
||||
</label>
|
||||
@@ -109,7 +109,7 @@ export const StatsBar = ({
|
||||
return (
|
||||
displayOptions.traffic && (
|
||||
<div className="w-full py-1" key="traffic">
|
||||
<div className="rt-Flex rt-r-fd-column rt-r-gap-2">
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-secondary-foreground text-sm">
|
||||
流量概览
|
||||
</label>
|
||||
@@ -131,7 +131,7 @@ export const StatsBar = ({
|
||||
return (
|
||||
displayOptions.speed && (
|
||||
<div className="w-full py-1" key="speed">
|
||||
<div className="rt-Flex rt-r-fd-column rt-r-gap-2">
|
||||
<div className="flex flex-col gap-2">
|
||||
<label className="text-secondary-foreground text-sm">
|
||||
网络速率
|
||||
</label>
|
||||
|
@@ -10,7 +10,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-secondary-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"flex w-full text-sm rounded-md bg-card px-3 py-2 placeholder:text-secondary-foreground disabled:cursor-not-allowed disabled:opacity-50 outline-none focus-visible:ring-0",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { useState, useMemo } from "react";
|
||||
import { useState, useMemo, useEffect, useRef } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { StatsBar } from "@/components/sections/StatsBar";
|
||||
import { NodeCard } from "@/components/sections/NodeCard";
|
||||
@@ -15,10 +15,17 @@ interface HomePageProps {
|
||||
searchTerm: string;
|
||||
}
|
||||
|
||||
const homeStateCache = {
|
||||
selectedGroup: "所有",
|
||||
scrollPosition: 0,
|
||||
};
|
||||
|
||||
const HomePage: React.FC<HomePageProps> = ({ viewMode, searchTerm }) => {
|
||||
const { nodes: staticNodes, loading, getGroups } = useNodeData();
|
||||
const { liveData } = useLiveData();
|
||||
const [selectedGroup, setSelectedGroup] = useState("所有");
|
||||
const [selectedGroup, setSelectedGroup] = useState(
|
||||
homeStateCache.selectedGroup
|
||||
);
|
||||
const { enableGroupedBar, enableStatsBar, enableSwap } = useAppConfig();
|
||||
const [displayOptions, setDisplayOptions] = useState({
|
||||
time: true,
|
||||
@@ -79,8 +86,37 @@ const HomePage: React.FC<HomePageProps> = ({ viewMode, searchTerm }) => {
|
||||
};
|
||||
}, [filteredNodes]);
|
||||
|
||||
const mainContentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
if (mainContentRef.current) {
|
||||
homeStateCache.scrollPosition = mainContentRef.current.scrollTop;
|
||||
}
|
||||
};
|
||||
|
||||
const mainContentElement = mainContentRef.current;
|
||||
mainContentElement?.addEventListener("scroll", handleScroll);
|
||||
|
||||
return () => {
|
||||
mainContentElement?.removeEventListener("scroll", handleScroll);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (mainContentRef.current) {
|
||||
mainContentRef.current.scrollTop = homeStateCache.scrollPosition;
|
||||
}
|
||||
}, [loading]);
|
||||
|
||||
useEffect(() => {
|
||||
homeStateCache.selectedGroup = selectedGroup;
|
||||
}, [selectedGroup]);
|
||||
|
||||
return (
|
||||
<div className="w-[90%] max-w-screen-2xl mx-auto flex-1 flex flex-col pb-10">
|
||||
<div
|
||||
ref={mainContentRef}
|
||||
className="w-[90%] max-w-screen-2xl mx-auto flex-1 flex flex-col pb-10 overflow-y-auto">
|
||||
{enableStatsBar && (
|
||||
<StatsBar
|
||||
displayOptions={displayOptions}
|
||||
@@ -145,9 +181,7 @@ const HomePage: React.FC<HomePageProps> = ({ viewMode, searchTerm }) => {
|
||||
) : (
|
||||
<div className="text-center py-12">
|
||||
<p className="text-lg font-bold">没有结果</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
请尝试更改筛选条件
|
||||
</p>
|
||||
<p className="text-muted-foreground">请尝试更改筛选条件</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@@ -50,28 +50,28 @@ const Instance = memo(({ node }: InstanceProps) => {
|
||||
</CardHeader>
|
||||
<CardContent className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-3">
|
||||
<div className="md:col-span-2">
|
||||
<p className="text-muted-foreground text-sm">CPU</p>
|
||||
<p className="text-sm">{`${node.cpu_name} (x${node.cpu_cores})`}</p>
|
||||
<p className="text-muted-foreground">CPU</p>
|
||||
<p>{`${node.cpu_name} (x${node.cpu_cores})`}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm">架构</p>
|
||||
<p className="text-sm">{node.arch}</p>
|
||||
<p className="text-muted-foreground">架构</p>
|
||||
<p>{node.arch}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm">虚拟化</p>
|
||||
<p className="text-sm">{node.virtualization}</p>
|
||||
<p className="text-muted-foreground">虚拟化</p>
|
||||
<p>{node.virtualization}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm">GPU</p>
|
||||
<p className="text-sm">{node.gpu_name || "N/A"}</p>
|
||||
<p className="text-muted-foreground">GPU</p>
|
||||
<p>{node.gpu_name || "N/A"}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm">操作系统</p>
|
||||
<p className="text-sm">{node.os}</p>
|
||||
<p className="text-muted-foreground">操作系统</p>
|
||||
<p>{node.os}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm">内存</p>
|
||||
<p className="text-sm">
|
||||
<p className="text-muted-foreground">内存</p>
|
||||
<p>
|
||||
{stats && isOnline
|
||||
? `${formatBytes(stats.ram.used)} / ${formatBytes(
|
||||
node.mem_total
|
||||
@@ -80,8 +80,8 @@ const Instance = memo(({ node }: InstanceProps) => {
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm">交换</p>
|
||||
<p className="text-sm">
|
||||
<p className="text-muted-foreground">交换</p>
|
||||
<p>
|
||||
{stats && isOnline
|
||||
? `${formatBytes(stats.swap.used)} / ${formatBytes(
|
||||
node.swap_total
|
||||
@@ -90,8 +90,8 @@ const Instance = memo(({ node }: InstanceProps) => {
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm">磁盘</p>
|
||||
<p className="text-sm">
|
||||
<p className="text-muted-foreground">磁盘</p>
|
||||
<p>
|
||||
{stats && isOnline
|
||||
? `${formatBytes(stats.disk.used)} / ${formatBytes(
|
||||
node.disk_total
|
||||
@@ -100,12 +100,12 @@ const Instance = memo(({ node }: InstanceProps) => {
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm">运行时间</p>
|
||||
<p className="text-sm">{formatUptime(stats?.uptime || 0)}</p>
|
||||
<p className="text-muted-foreground">运行时间</p>
|
||||
<p>{formatUptime(stats?.uptime || 0)}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm">实时网络</p>
|
||||
<p className="text-sm">
|
||||
<p className="text-muted-foreground">实时网络</p>
|
||||
<p>
|
||||
{stats && isOnline
|
||||
? `↑ ${formatBytes(stats.network.up, true)} ↓ ${formatBytes(
|
||||
stats.network.down,
|
||||
@@ -115,7 +115,7 @@ const Instance = memo(({ node }: InstanceProps) => {
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm">总流量</p>
|
||||
<p className="text-muted-foreground">总流量</p>
|
||||
<div className="flex items-center gap-2">
|
||||
{node.traffic_limit !== 0 && isOnline && stats && (
|
||||
<CircleProgress
|
||||
@@ -127,14 +127,14 @@ const Instance = memo(({ node }: InstanceProps) => {
|
||||
/>
|
||||
)}
|
||||
<div>
|
||||
<p className="text-sm">
|
||||
<p>
|
||||
{stats && isOnline
|
||||
? `↑ ${formatBytes(stats.network.totalUp)} ↓ ${formatBytes(
|
||||
stats.network.totalDown
|
||||
)}`
|
||||
: "N/A"}
|
||||
</p>
|
||||
<p className="text-sm">
|
||||
<p>
|
||||
{formatTrafficLimit(
|
||||
node.traffic_limit,
|
||||
node.traffic_limit_type
|
||||
@@ -144,8 +144,8 @@ const Instance = memo(({ node }: InstanceProps) => {
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm">负载</p>
|
||||
<p className="text-sm">
|
||||
<p className="text-muted-foreground">负载</p>
|
||||
<p>
|
||||
{stats && isOnline
|
||||
? `${stats.load.load1.toFixed(2)} | ${stats.load.load5.toFixed(
|
||||
2
|
||||
@@ -154,8 +154,8 @@ const Instance = memo(({ node }: InstanceProps) => {
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm">最后上报</p>
|
||||
<p className="text-sm">
|
||||
<p className="text-muted-foreground">最后上报</p>
|
||||
<p>
|
||||
{stats && isOnline
|
||||
? new Date(stats.updated_at).toLocaleString()
|
||||
: "N/A"}
|
||||
|
@@ -91,7 +91,7 @@ const LoadCharts = memo(({ node, hours, liveData }: LoadChartsProps) => {
|
||||
title: "内存",
|
||||
type: "area",
|
||||
value: (
|
||||
<Flex gap="0" direction="column" align="end" className="text-sm">
|
||||
<Flex gap="0" direction="column" align="end">
|
||||
<label>
|
||||
{liveData?.ram?.used
|
||||
? `${formatBytes(liveData.ram.used)} / ${formatBytes(
|
||||
@@ -153,7 +153,7 @@ const LoadCharts = memo(({ node, hours, liveData }: LoadChartsProps) => {
|
||||
type: "line",
|
||||
value: (
|
||||
<>
|
||||
<Flex gap="0" align="end" direction="column" className="text-sm">
|
||||
<Flex gap="0" align="end" direction="column">
|
||||
<span>↑ {formatBytes(liveData?.network.up || 0)}/s</span>
|
||||
<span>↓ {formatBytes(liveData?.network.down || 0)}/s</span>
|
||||
</Flex>
|
||||
@@ -182,7 +182,7 @@ const LoadCharts = memo(({ node, hours, liveData }: LoadChartsProps) => {
|
||||
title: "连接数",
|
||||
type: "line",
|
||||
value: (
|
||||
<Flex gap="0" align="end" direction="column" className="text-sm">
|
||||
<Flex gap="0" align="end" direction="column">
|
||||
<span>TCP: {liveData?.connections.tcp}</span>
|
||||
<span>UDP: {liveData?.connections.udp}</span>
|
||||
</Flex>
|
||||
|
Reference in New Issue
Block a user