优化公告页面布局

This commit is contained in:
dushixiang committed 2025-11-26 22:09:30 +08:00
1 parent ab24de67f5
commit 1a0d406284
7 files changed
+104 -91

No files matched your search

+3 -2
View File
@@ -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';
+2 -6
View File
@@ -363,8 +363,8 @@ const MonitorDetail = () => {
];
return (
<div className="min-h-screen bg-slate-50 text-slate-900">
<div className="mx-auto flex min-h-screen max-w-7xl flex-col px-4 pb-10 pt-6 sm:px-6 lg:px-8">
<div className="bg-slate-50">
<div className="mx-auto flex max-w-7xl flex-col px-4 pb-10 pt-6 sm:px-6 lg:px-8">
{/* Hero Section */}
<section className="relative overflow-hidden rounded-3xl bg-gradient-to-r from-slate-900 via-slate-800 to-blue-900 p-6 text-white shadow-xl">
<div className="absolute inset-0 opacity-30 [background-image:radial-gradient(circle_at_top,rgba(255,255,255,0.35),transparent_55%)]"/>
@@ -654,10 +654,6 @@ const MonitorDetail = () => {
</div>
</Card>
</main>
<footer className="border-t border-slate-200 py-4 text-center text-xs text-slate-400">
© {new Date().getFullYear()} Pika Monitor · 保持洞察,稳定运行。
</footer>
</div>
</div>
);
+19 -36
View File
@@ -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<ViewMode>('grid');
const {viewMode, setShowViewToggle} = usePublicLayout();
// 挂载时启用视图切换,卸载时禁用
useEffect(() => {
setShowViewToggle(true);
return () => setShowViewToggle(false);
}, [setShowViewToggle]);
const {data: monitors = [], isLoading, dataUpdatedAt} = useQuery<PublicMonitor[]>({
queryKey: ['publicMonitors'],
@@ -330,43 +335,21 @@ const MonitorList = () => {
if (isLoading) {
return (
<div className="min-h-screen bg-white text-slate-900 flex flex-col">
<PublicHeader
viewMode={viewMode}
onViewModeChange={setViewMode}
showViewToggle={true}
/>
<main className="flex-1 bg-white">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-8">
<LoadingSpinner/>
</div>
</main>
<PublicFooter/>
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-8">
<LoadingSpinner/>
</div>
);
}
return (
<div className="min-h-screen bg-white text-slate-900 flex flex-col">
<PublicHeader
viewMode={viewMode}
onViewModeChange={setViewMode}
showViewToggle={true}
/>
<main className="flex-1 bg-white">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-8 space-y-6">
{monitorSummaries.length === 0 ? (
<EmptyState/>
) : viewMode === 'grid' ? (
renderGridView()
) : (
renderListView()
)}
</div>
</main>
<PublicFooter/>
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-8 space-y-6">
{monitorSummaries.length === 0 ? (
<EmptyState/>
) : viewMode === 'grid' ? (
renderGridView()
) : (
renderListView()
)}
</div>
);
};
+2 -6
View File
@@ -795,8 +795,8 @@ const ServerDetail = () => {
}
return (
<div className="min-h-screen bg-slate-50 text-slate-900">
<div className="mx-auto flex min-h-screen max-w-7xl flex-col px-4 pb-10 pt-6 sm:px-6 lg:px-8">
<div className="bg-slate-50">
<div className="mx-auto flex max-w-7xl flex-col px-4 pb-10 pt-6 sm:px-6 lg:px-8">
<section
className="relative overflow-hidden rounded-3xl bg-gradient-to-r from-slate-900 via-slate-800 to-blue-900 p-6 text-white shadow-xl">
<div
@@ -1296,10 +1296,6 @@ const ServerDetail = () => {
</Card>
)}
</main>
<footer className="border-t border-slate-200 py-4 text-center text-xs text-slate-400">
© {new Date().getFullYear()} Pika Monitor · 保持洞察,稳定运行。
</footer>
</div>
</div>
);
+20 -27
View File
@@ -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<ViewMode>('grid');
const {viewMode, setShowViewToggle} = usePublicLayout();
// 挂载时启用视图切换,卸载时禁用
useEffect(() => {
setShowViewToggle(true);
return () => setShowViewToggle(false);
}, [setShowViewToggle]);
const {data: agents = [], isLoading, dataUpdatedAt} = useQuery<AgentWithMetrics[]>({
queryKey: ['agents', 'online'],
@@ -437,29 +442,17 @@ const ServerList = () => {
}
return (
<div className="min-h-screen bg-white text-slate-900 flex flex-col">
<PublicHeader
viewMode={viewMode}
onViewModeChange={setViewMode}
showViewToggle={true}
/>
<main className="flex-1 bg-white">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-8 space-y-6">
{filteredAgents.length === 0 ? (
<EmptyState
title='暂无在线服务器'
description='当前没有任何探针在线,请稍后再试。'
/>
) : viewMode === 'grid' ? (
renderGridView()
) : (
renderListView()
)}
</div>
</main>
<PublicFooter/>
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-8 space-y-6">
{filteredAgents.length === 0 ? (
<EmptyState
title='暂无在线服务器'
description='当前没有任何探针在线,请稍后再试。'
/>
) : viewMode === 'grid' ? (
renderGridView()
) : (
renderListView()
)}
</div>
);
};
+38
View File
@@ -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<ViewMode>('grid');
const [showViewToggle, setShowViewToggle] = useState(false);
return (
<div className="min-h-screen bg-white text-slate-900 flex flex-col">
<PublicHeader
viewMode={viewMode}
onViewModeChange={setViewMode}
showViewToggle={showViewToggle}
/>
<main className="flex-1">
<Outlet context={{viewMode, setViewMode, showViewToggle, setShowViewToggle}}/>
</main>
<PublicFooter/>
</div>
);
};
export function usePublicLayout() {
return useOutletContext<PublicLayoutContextType>();
}
export default PublicLayout;
+20 -14
View File
@@ -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),
},
],
},
// 管理员页面 - 需要登录
{