mirror of
https://github.com/fankes/komari-theme-purcarte.git
synced 2025-10-19 11:59:21 +08:00
feat: 添加流量进度条样式选项,优化相关组件
This commit is contained in:
@@ -63,8 +63,8 @@
|
|||||||
| 名称 | 配置项 | 类型 | 默认值 | 说明 |
|
| 名称 | 配置项 | 类型 | 默认值 | 说明 |
|
||||||
| :--- | :--- | :--- | :--- | :--- |
|
| :--- | :--- | :--- | :--- | :--- |
|
||||||
| 背景图片链接 | `backgroundImage` | `string` | `/assets/Moonlit-Scenery.webp` | 目前仅支持单张背景图片(eg: https://test.com/1.png ) |
|
| 背景图片链接 | `backgroundImage` | `string` | `/assets/Moonlit-Scenery.webp` | 目前仅支持单张背景图片(eg: https://test.com/1.png ) |
|
||||||
| 启用视频背景 | `enableVedioBackground` | `switch` | `false` | 启用后将使用视频作为背景 |
|
| 启用视频背景 | `enableVideoBackground` | `switch` | `false` | 启用后将使用视频作为背景 |
|
||||||
| 视频背景链接 | `vedioBackgroundUrl` | `string` | `/assets/LanternRivers_1080p15fps2Mbps3s.mp4` | 视频背景链接(eg: https://test.com/1.mp4 ),建议使用无声视频,且视频文件较大时可能会影响加载速度 |
|
| 视频背景链接 | `videoBackgroundUrl` | `string` | `/assets/LanternRivers_1080p15fps2Mbps3s.mp4` | 视频背景链接(eg: https://test.com/1.mp4 ),建议使用无声视频,且视频文件较大时可能会影响加载速度 |
|
||||||
| 启用磨砂玻璃效果 | `enableBlur` | `switch` | `true` | 启用后将使主要容器拥有磨砂玻璃效果 |
|
| 启用磨砂玻璃效果 | `enableBlur` | `switch` | `true` | 启用后将使主要容器拥有磨砂玻璃效果 |
|
||||||
| 磨砂玻璃模糊值 | `blurValue` | `number` | `10` | 调整模糊值大小,数值越大模糊效果越明显,建议值为 5-20,为 0 则表示不启用模糊效果 |
|
| 磨砂玻璃模糊值 | `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)) |
|
| 磨砂玻璃背景色 | `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` | 启用后默认显示统计栏 |
|
| 启用统计栏 | `enableStatsBar` | `switch` | `true` | 启用后默认显示统计栏 |
|
||||||
| 启用分组栏 | `enableGroupedBar` | `switch` | `true` | 启用后默认显示分组栏 |
|
| 启用分组栏 | `enableGroupedBar` | `switch` | `true` | 启用后默认显示分组栏 |
|
||||||
| 启用 SWAP 显示 | `enableSwap` | `switch` | `true` | 启用后默认显示 SWAP 信息 |
|
| 启用 SWAP 显示 | `enableSwap` | `switch` | `true` | 启用后默认显示 SWAP 信息 |
|
||||||
|
| 流量进度条样式 | `selectTrafficProgressStyle` | `select` | `circular` | 设置流量进度条样式为 circular(环形)或 linear(线形) |
|
||||||
| 启用列表视图进度条 | `enableListItemProgressBar` | `switch` | `true` | 启用后列表视图中将会显示进度条来表示使用率 |
|
| 启用列表视图进度条 | `enableListItemProgressBar` | `switch` | `true` | 启用后列表视图中将会显示进度条来表示使用率 |
|
||||||
|
|
||||||
#### Instance 设置
|
#### Instance 设置
|
||||||
|
@@ -172,6 +172,14 @@
|
|||||||
"default": true,
|
"default": true,
|
||||||
"help": "启用后默认显示 SWAP 信息"
|
"help": "启用后默认显示 SWAP 信息"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "selectTrafficProgressStyle",
|
||||||
|
"name": "流量进度条样式",
|
||||||
|
"type": "select",
|
||||||
|
"options": "circular,linear",
|
||||||
|
"default": "circular",
|
||||||
|
"help": "设置流量进度条样式为 circular(环形)或 linear(线形)"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "enableListItemProgressBar",
|
"key": "enableListItemProgressBar",
|
||||||
"name": "启用列表视图进度条",
|
"name": "启用列表视图进度条",
|
||||||
|
@@ -16,10 +16,15 @@ import { CircleProgress } from "../ui/progress-circle";
|
|||||||
|
|
||||||
interface NodeCardProps {
|
interface NodeCardProps {
|
||||||
node: NodeWithStatus;
|
node: NodeWithStatus;
|
||||||
enableSwap: boolean | undefined;
|
enableSwap: boolean;
|
||||||
|
selectTrafficProgressStyle: "circular" | "linear";
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NodeCard = ({ node, enableSwap }: NodeCardProps) => {
|
export const NodeCard = ({
|
||||||
|
node,
|
||||||
|
enableSwap,
|
||||||
|
selectTrafficProgressStyle,
|
||||||
|
}: NodeCardProps) => {
|
||||||
const {
|
const {
|
||||||
stats,
|
stats,
|
||||||
isOnline,
|
isOnline,
|
||||||
@@ -113,6 +118,36 @@ export const NodeCard = ({ node, enableSwap }: NodeCardProps) => {
|
|||||||
<span className="w-12 text-right">{diskUsage.toFixed(0)}%</span>
|
<span className="w-12 text-right">{diskUsage.toFixed(0)}%</span>
|
||||||
</div>
|
</div>
|
||||||
</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="border-t border-(--accent-4)/50 my-2"></div>
|
||||||
<div className="flex justify-between text-xs">
|
<div className="flex justify-between text-xs">
|
||||||
<span className="text-secondary-foreground">网络:</span>
|
<span className="text-secondary-foreground">网络:</span>
|
||||||
@@ -123,11 +158,12 @@ export const NodeCard = ({ node, enableSwap }: NodeCardProps) => {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{selectTrafficProgressStyle === "circular" && isOnline && stats && (
|
||||||
<div className="flex items-center justify-between text-xs">
|
<div className="flex items-center justify-between text-xs">
|
||||||
<span className="text-secondary-foreground w-1/5">流量</span>
|
<span className="text-secondary-foreground w-1/5">流量</span>
|
||||||
<div className="flex items-center justify-between w-4/5">
|
<div className="flex items-center justify-between w-4/5">
|
||||||
<div className="flex items-center w-1/4">
|
<div className="flex items-center w-1/4">
|
||||||
{node.traffic_limit !== 0 && isOnline && stats && (
|
{node.traffic_limit !== 0 && (
|
||||||
<CircleProgress
|
<CircleProgress
|
||||||
value={trafficPercentage}
|
value={trafficPercentage}
|
||||||
maxValue={100}
|
maxValue={100}
|
||||||
@@ -157,6 +193,7 @@ export const NodeCard = ({ node, enableSwap }: NodeCardProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<div className="flex justify-between text-xs">
|
<div className="flex justify-between text-xs">
|
||||||
<span className="text-secondary-foreground">负载</span>
|
<span className="text-secondary-foreground">负载</span>
|
||||||
<span>{load}</span>
|
<span>{load}</span>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
interface NodeListHeaderProps {
|
interface NodeListHeaderProps {
|
||||||
enableSwap: boolean | undefined;
|
enableSwap: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NodeListHeader = ({ enableSwap }: NodeListHeaderProps) => {
|
export const NodeListHeader = ({ enableSwap }: NodeListHeaderProps) => {
|
||||||
|
@@ -10,14 +10,16 @@ import { ProgressBar } from "../ui/progress-bar";
|
|||||||
|
|
||||||
interface NodeListItemProps {
|
interface NodeListItemProps {
|
||||||
node: NodeWithStatus;
|
node: NodeWithStatus;
|
||||||
enableSwap: boolean | undefined;
|
enableSwap: boolean;
|
||||||
enableListItemProgressBar: boolean | undefined;
|
enableListItemProgressBar: boolean;
|
||||||
|
selectTrafficProgressStyle: "circular" | "linear";
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NodeListItem = ({
|
export const NodeListItem = ({
|
||||||
node,
|
node,
|
||||||
enableSwap,
|
enableSwap,
|
||||||
enableListItemProgressBar,
|
enableListItemProgressBar,
|
||||||
|
selectTrafficProgressStyle,
|
||||||
}: NodeListItemProps) => {
|
}: NodeListItemProps) => {
|
||||||
const {
|
const {
|
||||||
stats,
|
stats,
|
||||||
@@ -137,6 +139,37 @@ export const NodeListItem = ({
|
|||||||
<div>↓ {stats ? formatBytes(stats.network.down, true) : "N/A"}</div>
|
<div>↓ {stats ? formatBytes(stats.network.down, true) : "N/A"}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-2">
|
<div className="col-span-2">
|
||||||
|
{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 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 className="flex items-center justify-around">
|
<div className="flex items-center justify-around">
|
||||||
{node.traffic_limit !== 0 && isOnline && stats && (
|
{node.traffic_limit !== 0 && isOnline && stats && (
|
||||||
<div className="flex items-center justify-center w-1/3">
|
<div className="flex items-center justify-center w-1/3">
|
||||||
@@ -150,9 +183,13 @@ export const NodeListItem = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className={node.traffic_limit !== 0 ? "w-2/3 text-left" : "w-full"}>
|
className={
|
||||||
|
node.traffic_limit !== 0 ? "w-2/3 text-left" : "w-full"
|
||||||
|
}>
|
||||||
<div>
|
<div>
|
||||||
<div>↑ {stats ? formatBytes(stats.network.totalUp) : "N/A"}</div>
|
<div>
|
||||||
|
↑ {stats ? formatBytes(stats.network.totalUp) : "N/A"}
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
↓ {stats ? formatBytes(stats.network.totalDown) : "N/A"}
|
↓ {stats ? formatBytes(stats.network.totalDown) : "N/A"}
|
||||||
</div>
|
</div>
|
||||||
@@ -167,6 +204,7 @@ export const NodeListItem = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-1">
|
<div className="col-span-1">
|
||||||
{load.split("|").map((item, index) => (
|
{load.split("|").map((item, index) => (
|
||||||
|
@@ -24,6 +24,7 @@ export interface ConfigOptions {
|
|||||||
enableConnectBreaks?: boolean; // 是否启用连接断点
|
enableConnectBreaks?: boolean; // 是否启用连接断点
|
||||||
pingChartMaxPoints?: number; // 延迟图表最大点数
|
pingChartMaxPoints?: number; // 延迟图表最大点数
|
||||||
enableSwap?: boolean; // 是否启用SWAP显示
|
enableSwap?: boolean; // 是否启用SWAP显示
|
||||||
|
selectTrafficProgressStyle?: "circular" | "linear"; // 流量进度条样式
|
||||||
enableListItemProgressBar?: boolean; // 是否启用列表视图进度条
|
enableListItemProgressBar?: boolean; // 是否启用列表视图进度条
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,5 +55,6 @@ export const DEFAULT_CONFIG: ConfigOptions = {
|
|||||||
enableConnectBreaks: false,
|
enableConnectBreaks: false,
|
||||||
pingChartMaxPoints: 0,
|
pingChartMaxPoints: 0,
|
||||||
enableSwap: true,
|
enableSwap: true,
|
||||||
|
selectTrafficProgressStyle: "linear",
|
||||||
enableListItemProgressBar: true,
|
enableListItemProgressBar: true,
|
||||||
};
|
};
|
||||||
|
@@ -5,10 +5,11 @@ import { DEFAULT_CONFIG } from "./default";
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用全局配置 Hook,用于获取当前应用配置
|
* 使用全局配置 Hook,用于获取当前应用配置
|
||||||
* @returns 配置对象
|
* @returns 配置对象(合并了默认配置,确保所有属性都有值)
|
||||||
*/
|
*/
|
||||||
export function useAppConfig(): ConfigOptions {
|
export function useAppConfig(): Required<ConfigOptions> {
|
||||||
return useContext(ConfigContext);
|
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 { ConfigOptions } from "./default";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用配置类型
|
||||||
|
*/
|
||||||
|
export type AppConfig = Required<ConfigOptions>;
|
||||||
|
@@ -43,6 +43,7 @@ const HomePage: React.FC<HomePageProps> = ({
|
|||||||
enableStatsBar,
|
enableStatsBar,
|
||||||
enableSwap,
|
enableSwap,
|
||||||
enableListItemProgressBar,
|
enableListItemProgressBar,
|
||||||
|
selectTrafficProgressStyle,
|
||||||
} = useAppConfig();
|
} = useAppConfig();
|
||||||
const [displayOptions, setDisplayOptions] = useState({
|
const [displayOptions, setDisplayOptions] = useState({
|
||||||
time: true,
|
time: true,
|
||||||
@@ -184,6 +185,7 @@ const HomePage: React.FC<HomePageProps> = ({
|
|||||||
key={node.uuid}
|
key={node.uuid}
|
||||||
node={node}
|
node={node}
|
||||||
enableSwap={enableSwap}
|
enableSwap={enableSwap}
|
||||||
|
selectTrafficProgressStyle={selectTrafficProgressStyle}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<NodeListItem
|
<NodeListItem
|
||||||
@@ -191,6 +193,7 @@ const HomePage: React.FC<HomePageProps> = ({
|
|||||||
node={node}
|
node={node}
|
||||||
enableSwap={enableSwap}
|
enableSwap={enableSwap}
|
||||||
enableListItemProgressBar={enableListItemProgressBar}
|
enableListItemProgressBar={enableListItemProgressBar}
|
||||||
|
selectTrafficProgressStyle={selectTrafficProgressStyle}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
@@ -10,7 +10,7 @@ const LoadCharts = lazy(() => import("./LoadCharts"));
|
|||||||
const PingChart = lazy(() => import("./PingChart"));
|
const PingChart = lazy(() => import("./PingChart"));
|
||||||
import Loading from "@/components/loading";
|
import Loading from "@/components/loading";
|
||||||
import Flag from "@/components/sections/Flag";
|
import Flag from "@/components/sections/Flag";
|
||||||
import { useConfigItem } from "@/config";
|
import { useAppConfig } from "@/config";
|
||||||
import { useIsMobile } from "@/hooks/useMobile";
|
import { useIsMobile } from "@/hooks/useMobile";
|
||||||
|
|
||||||
const InstancePage = () => {
|
const InstancePage = () => {
|
||||||
@@ -27,8 +27,7 @@ const InstancePage = () => {
|
|||||||
const [chartType, setChartType] = useState<"load" | "ping">("load");
|
const [chartType, setChartType] = useState<"load" | "ping">("load");
|
||||||
const [loadHours, setLoadHours] = useState<number>(0);
|
const [loadHours, setLoadHours] = useState<number>(0);
|
||||||
const [pingHours, setPingHours] = useState<number>(1); // 默认1小时
|
const [pingHours, setPingHours] = useState<number>(1); // 默认1小时
|
||||||
const enableInstanceDetail = useConfigItem("enableInstanceDetail");
|
const { enableInstanceDetail, enablePingChart } = useAppConfig();
|
||||||
const enablePingChart = useConfigItem("enablePingChart");
|
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
const maxRecordPreserveTime = publicSettings?.record_preserve_time || 0; // 默认0表示关闭
|
const maxRecordPreserveTime = publicSettings?.record_preserve_time || 0; // 默认0表示关闭
|
||||||
|
Reference in New Issue
Block a user