From 16c790a48f2cae0a694e297b76e2493cbd7b9440 Mon Sep 17 00:00:00 2001 From: Montia37 Date: Tue, 9 Sep 2025 17:47:59 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=B5=81=E9=87=8F?= =?UTF-8?q?=E8=BF=9B=E5=BA=A6=E6=9D=A1=E6=A0=B7=E5=BC=8F=E9=80=89=E9=A1=B9?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E7=9B=B8=E5=85=B3=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +- komari-theme.json | 8 ++ src/components/sections/NodeCard.tsx | 101 ++++++++++++++------- src/components/sections/NodeListHeader.tsx | 2 +- src/components/sections/NodeListItem.tsx | 94 +++++++++++++------ src/config/default.ts | 2 + src/config/hooks.ts | 12 ++- src/pages/Home.tsx | 3 + src/pages/instance/index.tsx | 5 +- 9 files changed, 163 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index 8901252..796cc03 100644 --- a/README.md +++ b/README.md @@ -63,8 +63,8 @@ | 名称 | 配置项 | 类型 | 默认值 | 说明 | | :--- | :--- | :--- | :--- | :--- | | 背景图片链接 | `backgroundImage` | `string` | `/assets/Moonlit-Scenery.webp` | 目前仅支持单张背景图片(eg: https://test.com/1.png ) | -| 启用视频背景 | `enableVedioBackground` | `switch` | `false` | 启用后将使用视频作为背景 | -| 视频背景链接 | `vedioBackgroundUrl` | `string` | `/assets/LanternRivers_1080p15fps2Mbps3s.mp4` | 视频背景链接(eg: https://test.com/1.mp4 ),建议使用无声视频,且视频文件较大时可能会影响加载速度 | +| 启用视频背景 | `enableVideoBackground` | `switch` | `false` | 启用后将使用视频作为背景 | +| 视频背景链接 | `videoBackgroundUrl` | `string` | `/assets/LanternRivers_1080p15fps2Mbps3s.mp4` | 视频背景链接(eg: https://test.com/1.mp4 ),建议使用无声视频,且视频文件较大时可能会影响加载速度 | | 启用磨砂玻璃效果 | `enableBlur` | `switch` | `true` | 启用后将使主要容器拥有磨砂玻璃效果 | | 磨砂玻璃模糊值 | `blurValue` | `number` | `10` | 调整模糊值大小,数值越大模糊效果越明显,建议值为 5-20,为 0 则表示不启用模糊效果 | | 磨砂玻璃背景色 | `blurBackgroundColor` | `string` | `rgba(255, 255, 255, 0.5)\|rgba(0, 0, 0, 0.5)` | 调整模糊背景色,推荐 rgba 颜色值,使用“\|”分隔亮色模式和暗色模式的颜色值(eg: rgba(255, 255, 255, 0.5)\|rgba(0, 0, 0, 0.5)) | @@ -93,6 +93,7 @@ | 启用统计栏 | `enableStatsBar` | `switch` | `true` | 启用后默认显示统计栏 | | 启用分组栏 | `enableGroupedBar` | `switch` | `true` | 启用后默认显示分组栏 | | 启用 SWAP 显示 | `enableSwap` | `switch` | `true` | 启用后默认显示 SWAP 信息 | +| 流量进度条样式 | `selectTrafficProgressStyle` | `select` | `circular` | 设置流量进度条样式为 circular(环形)或 linear(线形) | | 启用列表视图进度条 | `enableListItemProgressBar` | `switch` | `true` | 启用后列表视图中将会显示进度条来表示使用率 | #### Instance 设置 diff --git a/komari-theme.json b/komari-theme.json index 4ad451f..8b3262d 100644 --- a/komari-theme.json +++ b/komari-theme.json @@ -172,6 +172,14 @@ "default": true, "help": "启用后默认显示 SWAP 信息" }, + { + "key": "selectTrafficProgressStyle", + "name": "流量进度条样式", + "type": "select", + "options": "circular,linear", + "default": "circular", + "help": "设置流量进度条样式为 circular(环形)或 linear(线形)" + }, { "key": "enableListItemProgressBar", "name": "启用列表视图进度条", diff --git a/src/components/sections/NodeCard.tsx b/src/components/sections/NodeCard.tsx index fb18811..b960829 100644 --- a/src/components/sections/NodeCard.tsx +++ b/src/components/sections/NodeCard.tsx @@ -16,10 +16,15 @@ import { CircleProgress } from "../ui/progress-circle"; interface NodeCardProps { node: NodeWithStatus; - enableSwap: boolean | undefined; + enableSwap: boolean; + selectTrafficProgressStyle: "circular" | "linear"; } -export const NodeCard = ({ node, enableSwap }: NodeCardProps) => { +export const NodeCard = ({ + node, + enableSwap, + selectTrafficProgressStyle, +}: NodeCardProps) => { const { stats, isOnline, @@ -113,6 +118,36 @@ export const NodeCard = ({ node, enableSwap }: NodeCardProps) => { {diskUsage.toFixed(0)}% + {selectTrafficProgressStyle === "linear" && isOnline && stats && ( +
+
+ 流量 +
+ + + {node.traffic_limit !== 0 + ? `${trafficPercentage.toFixed(0)}%` + : "OFF"} + +
+
+
+ + {formatTrafficLimit( + node.traffic_limit, + node.traffic_limit_type + )} + + + {stats + ? `↑ ${formatBytes(stats.network.totalUp)} ↓ ${formatBytes( + stats.network.totalDown + )}` + : "N/A"} + +
+
+ )}
网络: @@ -123,40 +158,42 @@ export const NodeCard = ({ node, enableSwap }: NodeCardProps) => {
-
- 流量 -
-
- {node.traffic_limit !== 0 && isOnline && stats && ( - - )} -
-
-
- - ↑ {stats ? formatBytes(stats.network.totalUp) : "N/A"} - - - ↓ {stats ? formatBytes(stats.network.totalDown) : "N/A"} - + {selectTrafficProgressStyle === "circular" && isOnline && stats && ( +
+ 流量 +
+
+ {node.traffic_limit !== 0 && ( + + )}
- {node.traffic_limit !== 0 && isOnline && stats && ( -
- {formatTrafficLimit( - node.traffic_limit, - node.traffic_limit_type - )} +
+
+ + ↑ {stats ? formatBytes(stats.network.totalUp) : "N/A"} + + + ↓ {stats ? formatBytes(stats.network.totalDown) : "N/A"} +
- )} + {node.traffic_limit !== 0 && isOnline && stats && ( +
+ {formatTrafficLimit( + node.traffic_limit, + node.traffic_limit_type + )} +
+ )} +
-
+ )}
负载 {load} diff --git a/src/components/sections/NodeListHeader.tsx b/src/components/sections/NodeListHeader.tsx index 43d091d..4d01f2c 100644 --- a/src/components/sections/NodeListHeader.tsx +++ b/src/components/sections/NodeListHeader.tsx @@ -1,5 +1,5 @@ interface NodeListHeaderProps { - enableSwap: boolean | undefined; + enableSwap: boolean; } export const NodeListHeader = ({ enableSwap }: NodeListHeaderProps) => { diff --git a/src/components/sections/NodeListItem.tsx b/src/components/sections/NodeListItem.tsx index a9c29df..e225f1d 100644 --- a/src/components/sections/NodeListItem.tsx +++ b/src/components/sections/NodeListItem.tsx @@ -10,14 +10,16 @@ import { ProgressBar } from "../ui/progress-bar"; interface NodeListItemProps { node: NodeWithStatus; - enableSwap: boolean | undefined; - enableListItemProgressBar: boolean | undefined; + enableSwap: boolean; + enableListItemProgressBar: boolean; + selectTrafficProgressStyle: "circular" | "linear"; } export const NodeListItem = ({ node, enableSwap, enableListItemProgressBar, + selectTrafficProgressStyle, }: NodeListItemProps) => { const { stats, @@ -137,36 +139,72 @@ export const NodeListItem = ({
↓ {stats ? formatBytes(stats.network.down, true) : "N/A"}
-
- {node.traffic_limit !== 0 && isOnline && stats && ( -
- -
- )} -
-
-
↑ {stats ? formatBytes(stats.network.totalUp) : "N/A"}
-
- ↓ {stats ? formatBytes(stats.network.totalDown) : "N/A"} -
+ {selectTrafficProgressStyle === "linear" && isOnline && stats ? ( +
+
+ + {stats + ? `↑ ${formatBytes(stats.network.totalUp)} ↓ ${formatBytes( + stats.network.totalDown + )}` + : "N/A"} +
{node.traffic_limit !== 0 && isOnline && stats && ( -
- {formatTrafficLimit( - node.traffic_limit, - node.traffic_limit_type - )} -
+ <> +
+ + + {node.traffic_limit !== 0 + ? `${trafficPercentage.toFixed(0)}%` + : "OFF"} + +
+
+ {formatTrafficLimit( + node.traffic_limit, + node.traffic_limit_type + )} +
+ )}
-
+ ) : ( +
+ {node.traffic_limit !== 0 && isOnline && stats && ( +
+ +
+ )} +
+
+
+ ↑ {stats ? formatBytes(stats.network.totalUp) : "N/A"} +
+
+ ↓ {stats ? formatBytes(stats.network.totalDown) : "N/A"} +
+
+ {node.traffic_limit !== 0 && isOnline && stats && ( +
+ {formatTrafficLimit( + node.traffic_limit, + node.traffic_limit_type + )} +
+ )} +
+
+ )}
{load.split("|").map((item, index) => ( diff --git a/src/config/default.ts b/src/config/default.ts index c76f8e1..409be10 100644 --- a/src/config/default.ts +++ b/src/config/default.ts @@ -24,6 +24,7 @@ export interface ConfigOptions { enableConnectBreaks?: boolean; // 是否启用连接断点 pingChartMaxPoints?: number; // 延迟图表最大点数 enableSwap?: boolean; // 是否启用SWAP显示 + selectTrafficProgressStyle?: "circular" | "linear"; // 流量进度条样式 enableListItemProgressBar?: boolean; // 是否启用列表视图进度条 } @@ -54,5 +55,6 @@ export const DEFAULT_CONFIG: ConfigOptions = { enableConnectBreaks: false, pingChartMaxPoints: 0, enableSwap: true, + selectTrafficProgressStyle: "linear", enableListItemProgressBar: true, }; diff --git a/src/config/hooks.ts b/src/config/hooks.ts index 9a6a76a..3492d0b 100644 --- a/src/config/hooks.ts +++ b/src/config/hooks.ts @@ -5,10 +5,11 @@ import { DEFAULT_CONFIG } from "./default"; /** * 使用全局配置 Hook,用于获取当前应用配置 - * @returns 配置对象 + * @returns 配置对象(合并了默认配置,确保所有属性都有值) */ -export function useAppConfig(): ConfigOptions { - return useContext(ConfigContext); +export function useAppConfig(): Required { + const config = useContext(ConfigContext); + return { ...DEFAULT_CONFIG, ...config } as Required; } /** @@ -26,3 +27,8 @@ export function useConfigItem( // 导出配置类型 export type { ConfigOptions } from "./default"; + +/** + * 应用配置类型 + */ +export type AppConfig = Required; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index aa1101b..6a34464 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -43,6 +43,7 @@ const HomePage: React.FC = ({ enableStatsBar, enableSwap, enableListItemProgressBar, + selectTrafficProgressStyle, } = useAppConfig(); const [displayOptions, setDisplayOptions] = useState({ time: true, @@ -184,6 +185,7 @@ const HomePage: React.FC = ({ key={node.uuid} node={node} enableSwap={enableSwap} + selectTrafficProgressStyle={selectTrafficProgressStyle} /> ) : ( = ({ node={node} enableSwap={enableSwap} enableListItemProgressBar={enableListItemProgressBar} + selectTrafficProgressStyle={selectTrafficProgressStyle} /> ) )} diff --git a/src/pages/instance/index.tsx b/src/pages/instance/index.tsx index bca3944..8c8d366 100644 --- a/src/pages/instance/index.tsx +++ b/src/pages/instance/index.tsx @@ -10,7 +10,7 @@ const LoadCharts = lazy(() => import("./LoadCharts")); const PingChart = lazy(() => import("./PingChart")); import Loading from "@/components/loading"; import Flag from "@/components/sections/Flag"; -import { useConfigItem } from "@/config"; +import { useAppConfig } from "@/config"; import { useIsMobile } from "@/hooks/useMobile"; const InstancePage = () => { @@ -27,8 +27,7 @@ const InstancePage = () => { const [chartType, setChartType] = useState<"load" | "ping">("load"); const [loadHours, setLoadHours] = useState(0); const [pingHours, setPingHours] = useState(1); // 默认1小时 - const enableInstanceDetail = useConfigItem("enableInstanceDetail"); - const enablePingChart = useConfigItem("enablePingChart"); + const { enableInstanceDetail, enablePingChart } = useAppConfig(); const isMobile = useIsMobile(); const maxRecordPreserveTime = publicSettings?.record_preserve_time || 0; // 默认0表示关闭