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表示关闭