feat: enhance MainLayout with header height management using useLayoutEffect, improve AiProvidersPage by removing priority field, and update UsagePage with dynamic stats cards and sparkline charts for better data visualization

This commit is contained in:
Supra4E8C
2025-12-10 01:42:21 +08:00
parent d8f540cdb1
commit c71af9a8a5
9 changed files with 371 additions and 102 deletions

View File

@@ -1,4 +1,4 @@
import { ReactNode, SVGProps, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ReactNode, SVGProps, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { NavLink, Outlet } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Button } from '@/components/ui/Button';
@@ -144,12 +144,40 @@ export function MainLayout() {
const [checkingVersion, setCheckingVersion] = useState(false);
const [brandExpanded, setBrandExpanded] = useState(true);
const brandCollapseTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const headerRef = useRef<HTMLElement | null>(null);
const isLocal = useMemo(() => isLocalhost(window.location.hostname), []);
const fullBrandName = 'CLI Proxy API Management Center';
const abbrBrandName = t('title.abbr');
// 将顶栏高度写入 CSS 变量,确保侧栏/内容区计算一致,防止滚动时抖动
useLayoutEffect(() => {
const updateHeaderHeight = () => {
const height = headerRef.current?.offsetHeight;
if (height) {
document.documentElement.style.setProperty('--header-height', `${height}px`);
}
};
updateHeaderHeight();
const resizeObserver =
typeof ResizeObserver !== 'undefined' && headerRef.current ? new ResizeObserver(updateHeaderHeight) : null;
if (resizeObserver && headerRef.current) {
resizeObserver.observe(headerRef.current);
}
window.addEventListener('resize', updateHeaderHeight);
return () => {
if (resizeObserver) {
resizeObserver.disconnect();
}
window.removeEventListener('resize', updateHeaderHeight);
};
}, []);
// 5秒后自动收起品牌名称
useEffect(() => {
brandCollapseTimer.current = setTimeout(() => {
@@ -244,7 +272,7 @@ export function MainLayout() {
return (
<div className="app-shell">
<header className="main-header">
<header className="main-header" ref={headerRef}>
<div className="left">
<button
className="sidebar-toggle-header"