Skip to content

Commit 8dce0da

Browse files
committed
feat: new tunnel details info layout
1 parent 0ca534a commit 8dce0da

File tree

5 files changed

+899
-0
lines changed

5 files changed

+899
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { Card, CardBody, CardHeader } from "@heroui/react";
2+
3+
interface ConnectionsData {
4+
pool?: number | null;
5+
tcps?: number | null;
6+
udps?: number | null;
7+
}
8+
9+
interface ConnectionsStatsCardProps {
10+
connectionsData: ConnectionsData;
11+
}
12+
13+
export const ConnectionsStatsCard = ({
14+
connectionsData,
15+
}: ConnectionsStatsCardProps) => {
16+
// 计算TCP和UDP连接数
17+
const tcpConnections = connectionsData.tcps || 0;
18+
const udpConnections = connectionsData.udps || 0;
19+
const totalConnections = tcpConnections + udpConnections;
20+
21+
// 计算百分比用于显示比例
22+
const tcpPercentage =
23+
totalConnections > 0 ? (tcpConnections / totalConnections) * 100 : 50;
24+
const udpPercentage =
25+
totalConnections > 0 ? (udpConnections / totalConnections) * 100 : 50;
26+
27+
return (
28+
<div
29+
className="col-span-1"
30+
style={{
31+
gridTemplateColumns: "repeat(auto-fit, minmax(120px, 1fr))",
32+
maxWidth: "100%",
33+
}}
34+
>
35+
<Card className="relative p-2 cursor-pointer transition-all duration-300 ">
36+
<CardHeader className="flex items-center pb-0">
37+
<svg
38+
className="text-blue-500 mr-1"
39+
height="20"
40+
viewBox="0 0 24 24"
41+
width="20"
42+
xmlns="http://www.w3.org/2000/svg"
43+
>
44+
<path
45+
d="M18 10h-4a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2ZM6 4h4a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2Z"
46+
fill="none"
47+
stroke="currentColor"
48+
strokeLinecap="round"
49+
strokeLinejoin="round"
50+
strokeWidth="2"
51+
/>
52+
</svg>
53+
<div className="flex items-center gap-2">
54+
<h3 className="text-base font-semibold">连接数量</h3>
55+
</div>
56+
</CardHeader>
57+
<CardBody>
58+
{/* 主要内容区域 */}
59+
<div className="flex flex-row items-center justify-between">
60+
<div className="flex rounded-lg overflow-hidden mt-2 w-full">
61+
{/* TCP连接部分 */}
62+
<div
63+
className="p-4 flex-1 flex flex-col items-center relative bg-purple-50 dark:bg-purple-950/30 "
64+
style={{
65+
flex: `${tcpPercentage}`,
66+
minWidth: "100px",
67+
}}
68+
>
69+
<div className="text-sm md:text-lg font-bold mb-1 text-purple-700 dark:text-purple-300">
70+
{tcpConnections}
71+
</div>
72+
<div className="text-xs font-medium opacity-90 text-purple-600 dark:text-purple-400">
73+
TCP连接数
74+
</div>
75+
</div>
76+
77+
{/* UDP连接部分 */}
78+
<div
79+
className="p-4 flex-1 flex flex-col items-center bg-orange-50 dark:bg-orange-950/30"
80+
style={{
81+
flex: `${udpPercentage}`,
82+
minWidth: "100px",
83+
}}
84+
>
85+
<div className="text-sm md:text-lg font-bold mb-1 text-orange-700 dark:text-orange-300">
86+
{udpConnections}
87+
</div>
88+
<div className="text-xs font-medium opacity-90 text-orange-600 dark:text-orange-400">
89+
UDP连接数
90+
</div>
91+
</div>
92+
</div>
93+
</div>
94+
</CardBody>
95+
</Card>
96+
</div>
97+
);
98+
};
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { Card, CardBody, CardHeader } from "@heroui/react";
2+
3+
interface NetworkQualityData {
4+
ping?: number | null;
5+
pool?: number | null;
6+
}
7+
8+
interface NetworkQualityCardProps {
9+
networkData: NetworkQualityData;
10+
}
11+
12+
export const NetworkQualityCard = ({
13+
networkData,
14+
}: NetworkQualityCardProps) => {
15+
const ping = networkData.ping || 0;
16+
const pool = networkData.pool || 0;
17+
18+
// 计算延迟质量等级 (越低越好)
19+
const getLatencyQuality = (latency: number) => {
20+
if (latency === 0) return { level: "未知", percentage: 0 };
21+
if (latency <= 50) return { level: "优秀", percentage: 90 };
22+
if (latency <= 100) return { level: "良好", percentage: 70 };
23+
if (latency <= 200) return { level: "一般", percentage: 50 };
24+
25+
return { level: "较差", percentage: 30 };
26+
};
27+
28+
// 计算连接池质量等级 (适中最好)
29+
const getPoolQuality = (poolCount: number) => {
30+
if (poolCount === 0) return { level: "空闲", percentage: 50 };
31+
if (poolCount <= 10) return { level: "轻负载", percentage: 80 };
32+
if (poolCount <= 50) return { level: "中负载", percentage: 90 };
33+
if (poolCount <= 100) return { level: "重负载", percentage: 70 };
34+
35+
return { level: "超负载", percentage: 40 };
36+
};
37+
38+
const latencyQuality = getLatencyQuality(ping);
39+
const poolQuality = getPoolQuality(pool);
40+
41+
// 使用百分比来显示质量比例
42+
const latencyPercentage = latencyQuality.percentage;
43+
const poolPercentage = poolQuality.percentage;
44+
45+
return (
46+
<div
47+
className="col-span-1"
48+
style={{
49+
gridTemplateColumns: "repeat(auto-fit, minmax(120px, 1fr))",
50+
maxWidth: "100%",
51+
}}
52+
>
53+
<Card className="relative p-2 cursor-pointer transition-all duration-300 ">
54+
<CardHeader className="flex items-center pb-0">
55+
<svg
56+
className="text-blue-500 mr-1"
57+
height="20"
58+
viewBox="0 0 24 24"
59+
width="20"
60+
xmlns="http://www.w3.org/2000/svg"
61+
>
62+
<path
63+
d="M2 12a10 10 0 1 0 20 0a10 10 0 1 0-20 0m10-6v6l4 2"
64+
fill="none"
65+
stroke="currentColor"
66+
strokeLinecap="round"
67+
strokeLinejoin="round"
68+
strokeWidth="2"
69+
/>
70+
</svg>
71+
<div className="flex items-center gap-2">
72+
<h3 className="text-base font-semibold">网络质量</h3>
73+
</div>
74+
</CardHeader>
75+
<CardBody>
76+
{/* 主要内容区域 */}
77+
<div className="flex flex-row items-center justify-between ">
78+
<div className="flex rounded-lg overflow-hidden mt-2 w-full">
79+
{/* 延迟质量部分 */}
80+
<div
81+
className="p-4 flex-1 flex flex-col items-center relative bg-pink-50 dark:bg-pink-950/30"
82+
style={{
83+
flex: `${latencyPercentage}`,
84+
minWidth: "100px",
85+
}}
86+
>
87+
<div className="text-sm md:text-lg font-bold mb-1 text-pink-700 dark:text-pink-300">
88+
{ping > 0 ? `${ping}ms` : "—"}
89+
</div>
90+
<div className="text-xs font-medium opacity-90 text-pink-600 dark:text-pink-400">
91+
延迟 {latencyQuality.level}
92+
</div>
93+
</div>
94+
95+
{/* 池连接质量部分 */}
96+
<div
97+
className="p-4 flex-1 flex flex-col items-center bg-cyan-50 dark:bg-cyan-950/30"
98+
style={{
99+
flex: `${poolPercentage}`,
100+
minWidth: "100px",
101+
}}
102+
>
103+
<div className="text-sm md:text-lg font-bold mb-1 text-cyan-700 dark:text-cyan-300">
104+
{pool}
105+
</div>
106+
<div className="text-xs font-medium opacity-90 text-cyan-600 dark:text-cyan-400">
107+
{poolQuality.level}
108+
</div>
109+
</div>
110+
</div>
111+
</div>
112+
</CardBody>
113+
</Card>
114+
</div>
115+
);
116+
};
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { Card, CardBody, CardHeader } from "@heroui/react";
2+
3+
interface TrafficData {
4+
tcpRx: number;
5+
tcpTx: number;
6+
udpRx: number;
7+
udpTx: number;
8+
ping?: number | null;
9+
}
10+
11+
interface TrafficStatsCardProps {
12+
trafficData: TrafficData;
13+
formatTrafficValue: (bytes: number) => { value: string; unit: string };
14+
}
15+
16+
export const TrafficStatsCard = ({
17+
trafficData,
18+
formatTrafficValue,
19+
}: TrafficStatsCardProps) => {
20+
// 计算TCP和UDP总和用于比例显示
21+
const tcpTotal = trafficData.tcpRx + trafficData.tcpTx;
22+
const udpTotal = trafficData.udpRx + trafficData.udpTx;
23+
const grandTotal = tcpTotal + udpTotal;
24+
25+
// 计算百分比用于显示比例
26+
const tcpPercentage = grandTotal > 0 ? (tcpTotal / grandTotal) * 100 : 50;
27+
const udpPercentage = grandTotal > 0 ? (udpTotal / grandTotal) * 100 : 50;
28+
29+
// 格式化各个数值
30+
const { value: tcpRxValue, unit: tcpRxUnit } = formatTrafficValue(
31+
trafficData.tcpRx,
32+
);
33+
const { value: tcpTxValue, unit: tcpTxUnit } = formatTrafficValue(
34+
trafficData.tcpTx,
35+
);
36+
const { value: udpRxValue, unit: udpRxUnit } = formatTrafficValue(
37+
trafficData.udpRx,
38+
);
39+
const { value: udpTxValue, unit: udpTxUnit } = formatTrafficValue(
40+
trafficData.udpTx,
41+
);
42+
43+
return (
44+
<div
45+
className="col-span-1"
46+
style={{
47+
gridTemplateColumns: "repeat(auto-fit, minmax(120px, 1fr))",
48+
maxWidth: "100%",
49+
}}
50+
>
51+
<Card className="relative p-2 cursor-pointer transition-all duration-300 ">
52+
<CardHeader className="flex items-center pb-0">
53+
<svg
54+
className="text-blue-500 mr-1"
55+
height="20"
56+
viewBox="0 0 24 24"
57+
width="20"
58+
xmlns="http://www.w3.org/2000/svg"
59+
>
60+
<path
61+
d="M12.748 3.572c.059-.503-.532-.777-.835-.388L4.111 13.197c-.258.33-.038.832.364.832h6.988c.285 0 .506.267.47.57l-.68 5.83c-.06.502.53.776.834.387l7.802-10.013c.258-.33.038-.832-.364-.832h-6.988c-.285 0-.506-.267-.47-.57z"
62+
fill="none"
63+
stroke="currentColor"
64+
strokeLinecap="round"
65+
strokeLinejoin="round"
66+
strokeWidth="1.5"
67+
/>
68+
</svg>
69+
<div className="flex items-center gap-2">
70+
<h3 className="text-base font-semibold">流量累计</h3>
71+
</div>
72+
</CardHeader>
73+
<CardBody>
74+
{/* 主要内容区域 */}
75+
<div className="flex flex-row items-center justify-between ">
76+
<div className="flex rounded-lg overflow-hidden mt-2 w-full">
77+
{/* TCP 流量部分 */}
78+
<div
79+
className="p-4 flex-1 flex flex-col items-center relative bg-blue-50 dark:bg-blue-950/30"
80+
style={{
81+
flex: `${tcpPercentage}`,
82+
minWidth: "100px",
83+
}}
84+
>
85+
<div className="text-sm md:text-lg font-bold mb-1 text-blue-700 dark:text-blue-300">
86+
{tcpRxValue}
87+
{tcpRxUnit} | ↑{tcpTxValue}
88+
{tcpTxUnit}
89+
</div>
90+
<div className="text-xs font-medium opacity-90 text-blue-600 dark:text-blue-400">
91+
TCP流量
92+
</div>
93+
</div>
94+
95+
{/* UDP 流量部分 */}
96+
<div
97+
className="p-4 flex-1 flex flex-col items-center bg-green-50 dark:bg-green-950/30 "
98+
style={{
99+
flex: `${udpPercentage}`,
100+
minWidth: "100px",
101+
}}
102+
>
103+
<div className="text-sm md:text-lg font-bold mb-1 text-green-700 dark:text-green-300">
104+
{udpRxValue}
105+
{udpRxUnit} | ↑{udpTxValue}
106+
{udpTxUnit}
107+
</div>
108+
<div className="text-xs font-medium opacity-90 text-green-600 dark:text-green-400">
109+
UDP流量
110+
</div>
111+
</div>
112+
</div>
113+
</div>
114+
</CardBody>
115+
</Card>
116+
</div>
117+
);
118+
};

0 commit comments

Comments
 (0)