From 1a0d4062841f192b67831437a8d2d36e8f20aac9 Mon Sep 17 00:00:00 2001 From: dushixiang Date: Wed, 26 Nov 2025 22:09:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=85=AC=E5=91=8A=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/components/PublicHeader.tsx | 5 ++- web/src/pages/Public/MonitorDetail.tsx | 8 +--- web/src/pages/Public/MonitorList.tsx | 55 +++++++++----------------- web/src/pages/Public/ServerDetail.tsx | 8 +--- web/src/pages/Public/ServerList.tsx | 47 ++++++++++------------ web/src/pages/PublicLayout.tsx | 38 ++++++++++++++++++ web/src/router/index.tsx | 34 +++++++++------- 7 files changed, 104 insertions(+), 91 deletions(-) create mode 100644 web/src/pages/PublicLayout.tsx diff --git a/web/src/components/PublicHeader.tsx b/web/src/components/PublicHeader.tsx index 62ac17c..99d5cc7 100644 --- a/web/src/components/PublicHeader.tsx +++ b/web/src/components/PublicHeader.tsx @@ -1,7 +1,7 @@ import {useEffect, useState} from 'react'; import {Activity, LayoutGrid, List, LogIn, Server, Settings} from 'lucide-react'; import {getCurrentUser} from '../api/auth'; -import {Link} from "react-router-dom"; +import {Link, useLocation} from "react-router-dom"; interface PublicHeaderProps { viewMode?: 'grid' | 'list'; @@ -15,7 +15,7 @@ const PublicHeader = ({ showViewToggle = false }: PublicHeaderProps) => { const [isLoggedIn, setIsLoggedIn] = useState(false); - const currentPath = window.location.pathname; + let location = useLocation(); useEffect(() => { // 检查本地是否有 token @@ -41,6 +41,7 @@ const PublicHeader = ({ }, []); // 判断导航是否激活 + const currentPath = location.pathname; const isDeviceActive = currentPath === '/'; const isMonitorActive = currentPath === '/monitors'; diff --git a/web/src/pages/Public/MonitorDetail.tsx b/web/src/pages/Public/MonitorDetail.tsx index 377e920..f187010 100644 --- a/web/src/pages/Public/MonitorDetail.tsx +++ b/web/src/pages/Public/MonitorDetail.tsx @@ -363,8 +363,8 @@ const MonitorDetail = () => { ]; return ( -
-
+
+
{/* Hero Section */}
@@ -654,10 +654,6 @@ const MonitorDetail = () => {
- -
- © {new Date().getFullYear()} Pika Monitor · 保持洞察,稳定运行。 -
); diff --git a/web/src/pages/Public/MonitorList.tsx b/web/src/pages/Public/MonitorList.tsx index 4e3665b..f87ff5e 100644 --- a/web/src/pages/Public/MonitorList.tsx +++ b/web/src/pages/Public/MonitorList.tsx @@ -1,11 +1,10 @@ -import {useState} from 'react'; +import {useEffect} from 'react'; import {useNavigate} from 'react-router-dom'; import {useQuery} from '@tanstack/react-query'; import {AlertCircle, CheckCircle2, Clock, Loader2, Shield} from 'lucide-react'; import {getPublicMonitors} from '../../api/monitor'; import type {PublicMonitor} from '../../types'; -import PublicHeader from '../../components/PublicHeader'; -import PublicFooter from '../../components/PublicFooter'; +import {usePublicLayout} from '../PublicLayout'; type ViewMode = 'grid' | 'list'; @@ -84,7 +83,13 @@ const EmptyState = () => ( const MonitorList = () => { const navigate = useNavigate(); - const [viewMode, setViewMode] = useState('grid'); + const {viewMode, setShowViewToggle} = usePublicLayout(); + + // 挂载时启用视图切换,卸载时禁用 + useEffect(() => { + setShowViewToggle(true); + return () => setShowViewToggle(false); + }, [setShowViewToggle]); const {data: monitors = [], isLoading, dataUpdatedAt} = useQuery({ queryKey: ['publicMonitors'], @@ -330,43 +335,21 @@ const MonitorList = () => { if (isLoading) { return ( -
- -
-
- -
-
- +
+
); } return ( -
- - -
-
- {monitorSummaries.length === 0 ? ( - - ) : viewMode === 'grid' ? ( - renderGridView() - ) : ( - renderListView() - )} -
-
- - +
+ {monitorSummaries.length === 0 ? ( + + ) : viewMode === 'grid' ? ( + renderGridView() + ) : ( + renderListView() + )}
); }; diff --git a/web/src/pages/Public/ServerDetail.tsx b/web/src/pages/Public/ServerDetail.tsx index 3ede118..7fb4a7c 100644 --- a/web/src/pages/Public/ServerDetail.tsx +++ b/web/src/pages/Public/ServerDetail.tsx @@ -795,8 +795,8 @@ const ServerDetail = () => { } return ( -
-
+
+
{ )} - -
- © {new Date().getFullYear()} Pika Monitor · 保持洞察,稳定运行。 -
); diff --git a/web/src/pages/Public/ServerList.tsx b/web/src/pages/Public/ServerList.tsx index 5acefbf..afc0358 100644 --- a/web/src/pages/Public/ServerList.tsx +++ b/web/src/pages/Public/ServerList.tsx @@ -1,11 +1,10 @@ -import {type ReactNode, useState} from 'react'; +import {type ReactNode, useEffect} from 'react'; import {Link, useNavigate} from 'react-router-dom'; import {useQuery} from '@tanstack/react-query'; import {Cpu, EthernetPortIcon, HardDrive, Loader2, MemoryStick, Network} from 'lucide-react'; import {listAgents} from '../../api/agent'; import type {Agent, LatestMetrics} from '../../types'; -import PublicHeader from '../../components/PublicHeader'; -import PublicFooter from '../../components/PublicFooter'; +import {usePublicLayout} from '../PublicLayout'; interface AgentWithMetrics extends Agent { metrics?: LatestMetrics; @@ -116,7 +115,13 @@ const getProgressColor = (percent: number) => { const ServerList = () => { const navigate = useNavigate(); - const [viewMode, setViewMode] = useState('grid'); + const {viewMode, setShowViewToggle} = usePublicLayout(); + + // 挂载时启用视图切换,卸载时禁用 + useEffect(() => { + setShowViewToggle(true); + return () => setShowViewToggle(false); + }, [setShowViewToggle]); const {data: agents = [], isLoading, dataUpdatedAt} = useQuery({ queryKey: ['agents', 'online'], @@ -437,29 +442,17 @@ const ServerList = () => { } return ( -
- - -
-
- {filteredAgents.length === 0 ? ( - - ) : viewMode === 'grid' ? ( - renderGridView() - ) : ( - renderListView() - )} -
-
- - +
+ {filteredAgents.length === 0 ? ( + + ) : viewMode === 'grid' ? ( + renderGridView() + ) : ( + renderListView() + )}
); }; diff --git a/web/src/pages/PublicLayout.tsx b/web/src/pages/PublicLayout.tsx new file mode 100644 index 0000000..3e893b1 --- /dev/null +++ b/web/src/pages/PublicLayout.tsx @@ -0,0 +1,38 @@ +import {Outlet, useOutletContext} from 'react-router-dom'; +import {useState} from 'react'; +import PublicHeader from '../components/PublicHeader'; +import PublicFooter from '../components/PublicFooter'; + +type ViewMode = 'grid' | 'list'; + +interface PublicLayoutContextType { + viewMode: ViewMode; + setViewMode: (mode: ViewMode) => void; + showViewToggle: boolean; + setShowViewToggle: (show: boolean) => void; +} + +const PublicLayout = () => { + const [viewMode, setViewMode] = useState('grid'); + const [showViewToggle, setShowViewToggle] = useState(false); + + return ( +
+ +
+ +
+ +
+ ); +}; + +export function usePublicLayout() { + return useOutletContext(); +} + +export default PublicLayout; diff --git a/web/src/router/index.tsx b/web/src/router/index.tsx index 909b256..41435a7 100644 --- a/web/src/router/index.tsx +++ b/web/src/router/index.tsx @@ -5,6 +5,7 @@ import PrivateRoute from '../components/PrivateRoute'; const LoginPage = lazy(() => import('../pages/Login')); const GitHubCallbackPage = lazy(() => import('../pages/Login/GitHubCallback')); const OIDCCallbackPage = lazy(() => import('../pages/Login/OIDCCallback')); +const PublicLayout = lazy(() => import('../pages/PublicLayout')); const AdminLayout = lazy(() => import('../pages/AdminLayout')); const AgentListPage = lazy(() => import('../pages/Agents/AgentList')); const AgentDetailPage = lazy(() => import('../pages/Agents/AgentDetail')); @@ -45,20 +46,25 @@ const router = createBrowserRouter([ }, // 公开页面 - 不需要登录 { - path: '/', - element: lazyLoad(ServerListPage), - }, - { - path: '/servers/:id', - element: lazyLoad(ServerDetailPage), - }, - { - path: '/monitors', - element: lazyLoad(PublicMonitorListPage), - }, - { - path: '/monitors/:id', - element: lazyLoad(PublicMonitorDetailPage), + element: lazyLoad(PublicLayout), + children: [ + { + path: '/', + element: lazyLoad(ServerListPage), + }, + { + path: '/servers/:id', + element: lazyLoad(ServerDetailPage), + }, + { + path: '/monitors', + element: lazyLoad(PublicMonitorListPage), + }, + { + path: '/monitors/:id', + element: lazyLoad(PublicMonitorDetailPage), + }, + ], }, // 管理员页面 - 需要登录 {