feat: 添加流量进度条样式选项,优化相关组件

This commit is contained in:
Montia37
2025-09-09 17:47:59 +08:00
parent 8b3e7b8f40
commit 16c790a48f
9 changed files with 163 additions and 69 deletions

View File

@@ -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) => {
<span className="w-12 text-right">{diskUsage.toFixed(0)}%</span>
</div>
</div>
{selectTrafficProgressStyle === "linear" && isOnline && stats && (
<div>
<div className="flex items-center justify-between">
<span className="text-secondary-foreground"></span>
<div className="w-3/4 flex items-center gap-2">
<ProgressBar value={trafficPercentage} />
<span className="w-12 text-right">
{node.traffic_limit !== 0
? `${trafficPercentage.toFixed(0)}%`
: "OFF"}
</span>
</div>
</div>
<div className="flex text-xs items-center justify-between text-secondary-foreground">
<span>
{formatTrafficLimit(
node.traffic_limit,
node.traffic_limit_type
)}
</span>
<span>
{stats
? `${formatBytes(stats.network.totalUp)}${formatBytes(
stats.network.totalDown
)}`
: "N/A"}
</span>
</div>
</div>
)}
<div className="border-t border-(--accent-4)/50 my-2"></div>
<div className="flex justify-between text-xs">
<span className="text-secondary-foreground"></span>
@@ -123,40 +158,42 @@ export const NodeCard = ({ node, enableSwap }: NodeCardProps) => {
</span>
</div>
</div>
<div className="flex items-center justify-between text-xs">
<span className="text-secondary-foreground w-1/5"></span>
<div className="flex items-center justify-between w-4/5">
<div className="flex items-center w-1/4">
{node.traffic_limit !== 0 && isOnline && stats && (
<CircleProgress
value={trafficPercentage}
maxValue={100}
size={32}
strokeWidth={4}
showPercentage={true}
/>
)}
</div>
<div className="w-3/4 text-right">
<div>
<span>
{stats ? formatBytes(stats.network.totalUp) : "N/A"}
</span>
<span className="ml-2">
{stats ? formatBytes(stats.network.totalDown) : "N/A"}
</span>
{selectTrafficProgressStyle === "circular" && isOnline && stats && (
<div className="flex items-center justify-between text-xs">
<span className="text-secondary-foreground w-1/5"></span>
<div className="flex items-center justify-between w-4/5">
<div className="flex items-center w-1/4">
{node.traffic_limit !== 0 && (
<CircleProgress
value={trafficPercentage}
maxValue={100}
size={32}
strokeWidth={4}
showPercentage={true}
/>
)}
</div>
{node.traffic_limit !== 0 && isOnline && stats && (
<div className="text-right">
{formatTrafficLimit(
node.traffic_limit,
node.traffic_limit_type
)}
<div className="w-3/4 text-right">
<div>
<span>
{stats ? formatBytes(stats.network.totalUp) : "N/A"}
</span>
<span className="ml-2">
{stats ? formatBytes(stats.network.totalDown) : "N/A"}
</span>
</div>
)}
{node.traffic_limit !== 0 && isOnline && stats && (
<div className="text-right">
{formatTrafficLimit(
node.traffic_limit,
node.traffic_limit_type
)}
</div>
)}
</div>
</div>
</div>
</div>
)}
<div className="flex justify-between text-xs">
<span className="text-secondary-foreground"></span>
<span>{load}</span>

View File

@@ -1,5 +1,5 @@
interface NodeListHeaderProps {
enableSwap: boolean | undefined;
enableSwap: boolean;
}
export const NodeListHeader = ({ enableSwap }: NodeListHeaderProps) => {

View File

@@ -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 = ({
<div> {stats ? formatBytes(stats.network.down, true) : "N/A"}</div>
</div>
<div className="col-span-2">
<div className="flex items-center justify-around">
{node.traffic_limit !== 0 && isOnline && stats && (
<div className="flex items-center justify-center w-1/3">
<CircleProgress
value={trafficPercentage}
maxValue={100}
size={32}
strokeWidth={4}
showPercentage={true}
/>
</div>
)}
<div
className={node.traffic_limit !== 0 ? "w-2/3 text-left" : "w-full"}>
<div>
<div> {stats ? formatBytes(stats.network.totalUp) : "N/A"}</div>
<div>
{stats ? formatBytes(stats.network.totalDown) : "N/A"}
</div>
{selectTrafficProgressStyle === "linear" && isOnline && stats ? (
<div className="flex flex-col items-center">
<div className="w-full flex justify-center items-center">
<span>
{stats
? `${formatBytes(stats.network.totalUp)}${formatBytes(
stats.network.totalDown
)}`
: "N/A"}
</span>
</div>
{node.traffic_limit !== 0 && isOnline && stats && (
<div>
{formatTrafficLimit(
node.traffic_limit,
node.traffic_limit_type
)}
</div>
<>
<div className="w-[80%] flex items-center gap-1 mt-1">
<ProgressBar value={trafficPercentage} h="h-2" />
<span className="text-right text-xs">
{node.traffic_limit !== 0
? `${trafficPercentage.toFixed(0)}%`
: "OFF"}
</span>
</div>
<div className="text-xs text-secondary-foreground mt-1">
{formatTrafficLimit(
node.traffic_limit,
node.traffic_limit_type
)}
</div>
</>
)}
</div>
</div>
) : (
<div className="flex items-center justify-around">
{node.traffic_limit !== 0 && isOnline && stats && (
<div className="flex items-center justify-center w-1/3">
<CircleProgress
value={trafficPercentage}
maxValue={100}
size={32}
strokeWidth={4}
showPercentage={true}
/>
</div>
)}
<div
className={
node.traffic_limit !== 0 ? "w-2/3 text-left" : "w-full"
}>
<div>
<div>
{stats ? formatBytes(stats.network.totalUp) : "N/A"}
</div>
<div>
{stats ? formatBytes(stats.network.totalDown) : "N/A"}
</div>
</div>
{node.traffic_limit !== 0 && isOnline && stats && (
<div>
{formatTrafficLimit(
node.traffic_limit,
node.traffic_limit_type
)}
</div>
)}
</div>
</div>
)}
</div>
<div className="col-span-1">
{load.split("|").map((item, index) => (

View File

@@ -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,
};

View File

@@ -5,10 +5,11 @@ import { DEFAULT_CONFIG } from "./default";
/**
* 使用全局配置 Hook用于获取当前应用配置
* @returns 配置对象
* @returns 配置对象(合并了默认配置,确保所有属性都有值)
*/
export function useAppConfig(): ConfigOptions {
return useContext(ConfigContext);
export function useAppConfig(): Required<ConfigOptions> {
const config = useContext(ConfigContext);
return { ...DEFAULT_CONFIG, ...config } as Required<ConfigOptions>;
}
/**
@@ -26,3 +27,8 @@ export function useConfigItem<K extends keyof ConfigOptions>(
// 导出配置类型
export type { ConfigOptions } from "./default";
/**
* 应用配置类型
*/
export type AppConfig = Required<ConfigOptions>;

View File

@@ -43,6 +43,7 @@ const HomePage: React.FC<HomePageProps> = ({
enableStatsBar,
enableSwap,
enableListItemProgressBar,
selectTrafficProgressStyle,
} = useAppConfig();
const [displayOptions, setDisplayOptions] = useState({
time: true,
@@ -184,6 +185,7 @@ const HomePage: React.FC<HomePageProps> = ({
key={node.uuid}
node={node}
enableSwap={enableSwap}
selectTrafficProgressStyle={selectTrafficProgressStyle}
/>
) : (
<NodeListItem
@@ -191,6 +193,7 @@ const HomePage: React.FC<HomePageProps> = ({
node={node}
enableSwap={enableSwap}
enableListItemProgressBar={enableListItemProgressBar}
selectTrafficProgressStyle={selectTrafficProgressStyle}
/>
)
)}

View File

@@ -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<number>(0);
const [pingHours, setPingHours] = useState<number>(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表示关闭