1.18升级
This commit is contained in:
parent
8b66e7d100
commit
38b6e873c8
@ -66,6 +66,8 @@
|
|||||||
"cmdk": "^1.1.1",
|
"cmdk": "^1.1.1",
|
||||||
"dagre": "^0.8.5",
|
"dagre": "^0.8.5",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
|
"echarts": "^6.0.0",
|
||||||
|
"echarts-for-react": "^3.0.5",
|
||||||
"form-render": "^2.5.6",
|
"form-render": "^2.5.6",
|
||||||
"less": "^4.2.1",
|
"less": "^4.2.1",
|
||||||
"monaco-editor": "^0.52.2",
|
"monaco-editor": "^0.52.2",
|
||||||
|
|||||||
@ -173,6 +173,12 @@ importers:
|
|||||||
dayjs:
|
dayjs:
|
||||||
specifier: ^1.11.13
|
specifier: ^1.11.13
|
||||||
version: 1.11.13
|
version: 1.11.13
|
||||||
|
echarts:
|
||||||
|
specifier: ^6.0.0
|
||||||
|
version: 6.0.0
|
||||||
|
echarts-for-react:
|
||||||
|
specifier: ^3.0.5
|
||||||
|
version: 3.0.5(echarts@6.0.0)(react@18.3.1)
|
||||||
form-render:
|
form-render:
|
||||||
specifier: ^2.5.6
|
specifier: ^2.5.6
|
||||||
version: 2.5.6(@types/react@18.3.18)(antd@5.27.5(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 2.5.6(@types/react@18.3.18)(antd@5.27.5(date-fns@2.30.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
@ -2752,6 +2758,15 @@ packages:
|
|||||||
eastasianwidth@0.2.0:
|
eastasianwidth@0.2.0:
|
||||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||||
|
|
||||||
|
echarts-for-react@3.0.5:
|
||||||
|
resolution: {integrity: sha512-YpEI5Ty7O/2nvCfQ7ybNa+S90DwE8KYZWacGvJW4luUqywP7qStQ+pxDlYOmr4jGDu10mhEkiAuMKcUlT4W5vg==}
|
||||||
|
peerDependencies:
|
||||||
|
echarts: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0
|
||||||
|
react: ^15.0.0 || >=16.0.0
|
||||||
|
|
||||||
|
echarts@6.0.0:
|
||||||
|
resolution: {integrity: sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==}
|
||||||
|
|
||||||
electron-to-chromium@1.5.73:
|
electron-to-chromium@1.5.73:
|
||||||
resolution: {integrity: sha512-8wGNxG9tAG5KhGd3eeA0o6ixhiNdgr0DcHWm85XPCphwZgD1lIEoi6t3VERayWao7SF7AAZTw6oARGJeVjH8Kg==}
|
resolution: {integrity: sha512-8wGNxG9tAG5KhGd3eeA0o6ixhiNdgr0DcHWm85XPCphwZgD1lIEoi6t3VERayWao7SF7AAZTw6oARGJeVjH8Kg==}
|
||||||
|
|
||||||
@ -4106,6 +4121,9 @@ packages:
|
|||||||
simple-swizzle@0.2.4:
|
simple-swizzle@0.2.4:
|
||||||
resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==}
|
resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==}
|
||||||
|
|
||||||
|
size-sensor@1.0.2:
|
||||||
|
resolution: {integrity: sha512-2NCmWxY7A9pYKGXNBfteo4hy14gWu47rg5692peVMst6lQLPKrVjhY+UTEsPI5ceFRJSl3gVgMYaUi/hKuaiKw==}
|
||||||
|
|
||||||
slash@3.0.0:
|
slash@3.0.0:
|
||||||
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@ -4242,6 +4260,9 @@ packages:
|
|||||||
ts-interface-checker@0.1.13:
|
ts-interface-checker@0.1.13:
|
||||||
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
||||||
|
|
||||||
|
tslib@2.3.0:
|
||||||
|
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
|
||||||
|
|
||||||
tslib@2.8.1:
|
tslib@2.8.1:
|
||||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||||
|
|
||||||
@ -4411,6 +4432,9 @@ packages:
|
|||||||
zod@3.24.1:
|
zod@3.24.1:
|
||||||
resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==}
|
resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==}
|
||||||
|
|
||||||
|
zrender@6.0.0:
|
||||||
|
resolution: {integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==}
|
||||||
|
|
||||||
zustand@4.5.5:
|
zustand@4.5.5:
|
||||||
resolution: {integrity: sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==}
|
resolution: {integrity: sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==}
|
||||||
engines: {node: '>=12.7.0'}
|
engines: {node: '>=12.7.0'}
|
||||||
@ -7272,6 +7296,18 @@ snapshots:
|
|||||||
|
|
||||||
eastasianwidth@0.2.0: {}
|
eastasianwidth@0.2.0: {}
|
||||||
|
|
||||||
|
echarts-for-react@3.0.5(echarts@6.0.0)(react@18.3.1):
|
||||||
|
dependencies:
|
||||||
|
echarts: 6.0.0
|
||||||
|
fast-deep-equal: 3.1.3
|
||||||
|
react: 18.3.1
|
||||||
|
size-sensor: 1.0.2
|
||||||
|
|
||||||
|
echarts@6.0.0:
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.3.0
|
||||||
|
zrender: 6.0.0
|
||||||
|
|
||||||
electron-to-chromium@1.5.73: {}
|
electron-to-chromium@1.5.73: {}
|
||||||
|
|
||||||
emoji-regex@8.0.0: {}
|
emoji-regex@8.0.0: {}
|
||||||
@ -8777,6 +8813,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-arrayish: 0.3.4
|
is-arrayish: 0.3.4
|
||||||
|
|
||||||
|
size-sensor@1.0.2: {}
|
||||||
|
|
||||||
slash@3.0.0: {}
|
slash@3.0.0: {}
|
||||||
|
|
||||||
source-map-js@1.2.1: {}
|
source-map-js@1.2.1: {}
|
||||||
@ -8923,6 +8961,8 @@ snapshots:
|
|||||||
|
|
||||||
ts-interface-checker@0.1.13: {}
|
ts-interface-checker@0.1.13: {}
|
||||||
|
|
||||||
|
tslib@2.3.0: {}
|
||||||
|
|
||||||
tslib@2.8.1: {}
|
tslib@2.8.1: {}
|
||||||
|
|
||||||
type-check@0.4.0:
|
type-check@0.4.0:
|
||||||
@ -9052,6 +9092,10 @@ snapshots:
|
|||||||
|
|
||||||
zod@3.24.1: {}
|
zod@3.24.1: {}
|
||||||
|
|
||||||
|
zrender@6.0.0:
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.3.0
|
||||||
|
|
||||||
zustand@4.5.5(@types/react@18.3.18)(immer@10.1.1)(react@18.3.1):
|
zustand@4.5.5(@types/react@18.3.18)(immer@10.1.1)(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
use-sync-external-store: 1.2.2(react@18.3.1)
|
use-sync-external-store: 1.2.2(react@18.3.1)
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import {
|
|||||||
Tag,
|
Tag,
|
||||||
FileText,
|
FileText,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
|
BarChart3,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
@ -38,12 +39,13 @@ interface ServerCardProps {
|
|||||||
onDelete: (server: ServerResponse) => void;
|
onDelete: (server: ServerResponse) => void;
|
||||||
onSSHConnect: (server: ServerResponse) => void;
|
onSSHConnect: (server: ServerResponse) => void;
|
||||||
onCollectHardware: (server: ServerResponse) => void;
|
onCollectHardware: (server: ServerResponse) => void;
|
||||||
|
onMonitor: (server: ServerResponse) => void;
|
||||||
isTesting?: boolean;
|
isTesting?: boolean;
|
||||||
isCollecting?: boolean;
|
isCollecting?: boolean;
|
||||||
getOsIcon: (osType?: string) => React.ReactNode;
|
getOsIcon: (osType?: string) => React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ServerCard: React.FC<ServerCardProps> = ({ server, onTest, onEdit, onDelete, onSSHConnect, onCollectHardware, isTesting, isCollecting, getOsIcon }) => {
|
export const ServerCard: React.FC<ServerCardProps> = ({ server, onTest, onEdit, onDelete, onSSHConnect, onCollectHardware, onMonitor, isTesting, isCollecting, getOsIcon }) => {
|
||||||
const [expanded, setExpanded] = React.useState(false);
|
const [expanded, setExpanded] = React.useState(false);
|
||||||
|
|
||||||
const formatTime = (time?: string) => {
|
const formatTime = (time?: string) => {
|
||||||
@ -73,6 +75,11 @@ export const ServerCard: React.FC<ServerCardProps> = ({ server, onTest, onEdit,
|
|||||||
<Card className={`group relative flex h-full flex-col justify-between overflow-visible border transition-all duration-200 hover:border-primary/40 hover:shadow-lg ${
|
<Card className={`group relative flex h-full flex-col justify-between overflow-visible border transition-all duration-200 hover:border-primary/40 hover:shadow-lg ${
|
||||||
expanded ? 'rounded-b-none border-b-0' : ''
|
expanded ? 'rounded-b-none border-b-0' : ''
|
||||||
}`}>
|
}`}>
|
||||||
|
{/* ID Badge - 悬浮在左上角,类似通知样式 */}
|
||||||
|
<div className="absolute -left-2 -top-2 z-10 flex h-6 w-6 items-center justify-center rounded-full bg-primary text-[10px] font-bold text-primary-foreground shadow-md">
|
||||||
|
{server.id}
|
||||||
|
</div>
|
||||||
|
|
||||||
<CardContent className="flex flex-1 flex-col gap-3 p-3">
|
<CardContent className="flex flex-1 flex-col gap-3 p-3">
|
||||||
{/* 基础信息 */}
|
{/* 基础信息 */}
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
@ -233,6 +240,20 @@ export const ServerCard: React.FC<ServerCardProps> = ({ server, onTest, onEdit,
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="h-7 w-7 text-primary"
|
||||||
|
onClick={() => onMonitor(server)}
|
||||||
|
>
|
||||||
|
<BarChart3 className="h-3.5 w-3.5" />
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>监控数据</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import {
|
|||||||
HelpCircle,
|
HelpCircle,
|
||||||
Terminal,
|
Terminal,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
|
BarChart3,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
@ -38,6 +39,7 @@ interface ServerTableProps {
|
|||||||
onDelete: (server: ServerResponse) => void;
|
onDelete: (server: ServerResponse) => void;
|
||||||
onSSHConnect: (server: ServerResponse) => void;
|
onSSHConnect: (server: ServerResponse) => void;
|
||||||
onCollectHardware: (server: ServerResponse) => void;
|
onCollectHardware: (server: ServerResponse) => void;
|
||||||
|
onMonitor: (server: ServerResponse) => void;
|
||||||
isTesting?: (serverId: number) => boolean;
|
isTesting?: (serverId: number) => boolean;
|
||||||
isCollecting?: (serverId: number) => boolean;
|
isCollecting?: (serverId: number) => boolean;
|
||||||
getOsIcon: (osType?: string) => React.ReactNode;
|
getOsIcon: (osType?: string) => React.ReactNode;
|
||||||
@ -50,6 +52,7 @@ export const ServerTable: React.FC<ServerTableProps> = ({
|
|||||||
onDelete,
|
onDelete,
|
||||||
onSSHConnect,
|
onSSHConnect,
|
||||||
onCollectHardware,
|
onCollectHardware,
|
||||||
|
onMonitor,
|
||||||
isTesting,
|
isTesting,
|
||||||
isCollecting,
|
isCollecting,
|
||||||
getOsIcon,
|
getOsIcon,
|
||||||
@ -74,6 +77,7 @@ export const ServerTable: React.FC<ServerTableProps> = ({
|
|||||||
<Table minWidth="1200px">
|
<Table minWidth="1200px">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
<TableHead width="80px">ID</TableHead>
|
||||||
<TableHead width="180px">服务器名称</TableHead>
|
<TableHead width="180px">服务器名称</TableHead>
|
||||||
<TableHead width="150px">IP地址</TableHead>
|
<TableHead width="150px">IP地址</TableHead>
|
||||||
<TableHead width="120px">状态</TableHead>
|
<TableHead width="120px">状态</TableHead>
|
||||||
@ -89,13 +93,16 @@ export const ServerTable: React.FC<ServerTableProps> = ({
|
|||||||
<TableBody>
|
<TableBody>
|
||||||
{servers.length === 0 ? (
|
{servers.length === 0 ? (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={10} className="text-center py-8">
|
<TableCell colSpan={11} className="text-center py-8">
|
||||||
<span className="text-muted-foreground">暂无服务器数据</span>
|
<span className="text-muted-foreground">暂无服务器数据</span>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
) : (
|
) : (
|
||||||
servers.map((server) => (
|
servers.map((server) => (
|
||||||
<TableRow key={server.id}>
|
<TableRow key={server.id}>
|
||||||
|
<TableCell>
|
||||||
|
<span className="font-mono text-sm text-muted-foreground">{server.id}</span>
|
||||||
|
</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="font-medium text-foreground">{server.serverName}</span>
|
<span className="font-medium text-foreground">{server.serverName}</span>
|
||||||
@ -141,6 +148,19 @@ export const ServerTable: React.FC<ServerTableProps> = ({
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell sticky className="text-right">
|
<TableCell sticky className="text-right">
|
||||||
<div className="flex items-center justify-end gap-1">
|
<div className="flex items-center justify-end gap-1">
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => onMonitor(server)}
|
||||||
|
className="h-8 w-8 p-0"
|
||||||
|
>
|
||||||
|
<BarChart3 className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>监控数据</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@ -44,6 +44,7 @@ import { ServerCard } from './components/ServerCard';
|
|||||||
import { ServerTable } from './components/ServerTable';
|
import { ServerTable } from './components/ServerTable';
|
||||||
import { SSHWindowManager } from './components/SSHWindowManager';
|
import { SSHWindowManager } from './components/SSHWindowManager';
|
||||||
import { FileManagerWindowManager } from '@/components/FileManager';
|
import { FileManagerWindowManager } from '@/components/FileManager';
|
||||||
|
import { ServerMonitorDialog } from './components/ServerMonitorDialog';
|
||||||
|
|
||||||
const ServerList: React.FC = () => {
|
const ServerList: React.FC = () => {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
@ -81,6 +82,8 @@ const ServerList: React.FC = () => {
|
|||||||
const [serverToDelete, setServerToDelete] = useState<ServerResponse | null>(null);
|
const [serverToDelete, setServerToDelete] = useState<ServerResponse | null>(null);
|
||||||
const [testingServerId, setTestingServerId] = useState<number | null>(null);
|
const [testingServerId, setTestingServerId] = useState<number | null>(null);
|
||||||
const [collectingServerId, setCollectingServerId] = useState<number | null>(null);
|
const [collectingServerId, setCollectingServerId] = useState<number | null>(null);
|
||||||
|
const [monitorDialogOpen, setMonitorDialogOpen] = useState(false);
|
||||||
|
const [monitoringServer, setMonitoringServer] = useState<ServerResponse | null>(null);
|
||||||
const [statsData, setStatsData] = useState<{
|
const [statsData, setStatsData] = useState<{
|
||||||
total: number;
|
total: number;
|
||||||
online: number;
|
online: number;
|
||||||
@ -103,10 +106,10 @@ const ServerList: React.FC = () => {
|
|||||||
try {
|
try {
|
||||||
// 并行请求各个状态的数量
|
// 并行请求各个状态的数量
|
||||||
const [totalResult, onlineResult, offlineResult, unknownResult] = await Promise.all([
|
const [totalResult, onlineResult, offlineResult, unknownResult] = await Promise.all([
|
||||||
getServers({ categoryId: selectedCategoryId, osType: selectedOsType, pageNum: 0, size: 1 }),
|
getServers({ categoryId: selectedCategoryId, osType: selectedOsType, pageNum: 0, pageSize: 1 }),
|
||||||
getServers({ categoryId: selectedCategoryId, status: 'ONLINE', osType: selectedOsType, pageNum: 0, size: 1 }),
|
getServers({ categoryId: selectedCategoryId, status: 'ONLINE', osType: selectedOsType, pageNum: 0, pageSize: 1 }),
|
||||||
getServers({ categoryId: selectedCategoryId, status: 'OFFLINE', osType: selectedOsType, pageNum: 0, size: 1 }),
|
getServers({ categoryId: selectedCategoryId, status: 'OFFLINE', osType: selectedOsType, pageNum: 0, pageSize: 1 }),
|
||||||
getServers({ categoryId: selectedCategoryId, status: 'UNKNOWN', osType: selectedOsType, pageNum: 0, size: 1 }),
|
getServers({ categoryId: selectedCategoryId, status: 'UNKNOWN', osType: selectedOsType, pageNum: 0, pageSize: 1 }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setStatsData({
|
setStatsData({
|
||||||
@ -173,7 +176,7 @@ const ServerList: React.FC = () => {
|
|||||||
const result = await getServers({
|
const result = await getServers({
|
||||||
...params,
|
...params,
|
||||||
pageNum: pageIndex,
|
pageNum: pageIndex,
|
||||||
size: pageSize,
|
pageSize: pageSize,
|
||||||
});
|
});
|
||||||
if (result) {
|
if (result) {
|
||||||
setServers(result.content || []);
|
setServers(result.content || []);
|
||||||
@ -280,6 +283,12 @@ const ServerList: React.FC = () => {
|
|||||||
setDeleteDialogOpen(true);
|
setDeleteDialogOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 监控数据
|
||||||
|
const handleMonitor = (server: ServerResponse) => {
|
||||||
|
setMonitoringServer(server);
|
||||||
|
setMonitorDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
// SSH连接 - 使用多窗口系统
|
// SSH连接 - 使用多窗口系统
|
||||||
const handleSSHConnect = (server: ServerResponse) => {
|
const handleSSHConnect = (server: ServerResponse) => {
|
||||||
// 调用全局方法打开新的SSH窗口
|
// 调用全局方法打开新的SSH窗口
|
||||||
@ -345,15 +354,12 @@ const ServerList: React.FC = () => {
|
|||||||
// 监听筛选条件和视图模式变化(使用全局loading)
|
// 监听筛选条件和视图模式变化(使用全局loading)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadServers(false, false); // 筛选加载,使用全局loading
|
loadServers(false, false); // 筛选加载,使用全局loading
|
||||||
// 筛选条件变化时重置分页到第一页
|
|
||||||
setPageIndex(0);
|
|
||||||
}, [pageSize, selectedCategoryId, selectedStatus, selectedOsType, viewMode]);
|
}, [pageSize, selectedCategoryId, selectedStatus, selectedOsType, viewMode]);
|
||||||
|
|
||||||
// 监听分页变化(单独处理,使用pageLoading)
|
// 监听分页变化(单独处理,使用pageLoading)
|
||||||
// 注意:这个useEffect只处理用户主动翻页的情况
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (viewMode === 'table' && pageIndex > 0) {
|
if (viewMode === 'table') {
|
||||||
// pageIndex > 0 说明是用户翻页操作,使用pageLoading
|
// 表格模式下,分页变化时加载数据
|
||||||
loadServers(false, true);
|
loadServers(false, true);
|
||||||
}
|
}
|
||||||
}, [pageIndex]);
|
}, [pageIndex]);
|
||||||
@ -615,6 +621,7 @@ const ServerList: React.FC = () => {
|
|||||||
onDelete={handleDelete}
|
onDelete={handleDelete}
|
||||||
onSSHConnect={handleSSHConnect}
|
onSSHConnect={handleSSHConnect}
|
||||||
onCollectHardware={handleCollectHardware}
|
onCollectHardware={handleCollectHardware}
|
||||||
|
onMonitor={handleMonitor}
|
||||||
isTesting={testingServerId === server.id}
|
isTesting={testingServerId === server.id}
|
||||||
isCollecting={collectingServerId === server.id}
|
isCollecting={collectingServerId === server.id}
|
||||||
getOsIcon={getOsIcon}
|
getOsIcon={getOsIcon}
|
||||||
@ -642,6 +649,7 @@ const ServerList: React.FC = () => {
|
|||||||
onDelete={handleDelete}
|
onDelete={handleDelete}
|
||||||
onSSHConnect={handleSSHConnect}
|
onSSHConnect={handleSSHConnect}
|
||||||
onCollectHardware={handleCollectHardware}
|
onCollectHardware={handleCollectHardware}
|
||||||
|
onMonitor={handleMonitor}
|
||||||
isTesting={(serverId) => testingServerId === serverId}
|
isTesting={(serverId) => testingServerId === serverId}
|
||||||
isCollecting={(serverId) => collectingServerId === serverId}
|
isCollecting={(serverId) => collectingServerId === serverId}
|
||||||
getOsIcon={getOsIcon}
|
getOsIcon={getOsIcon}
|
||||||
@ -726,6 +734,13 @@ const ServerList: React.FC = () => {
|
|||||||
confirmText="确定"
|
confirmText="确定"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* 监控数据对话框 */}
|
||||||
|
<ServerMonitorDialog
|
||||||
|
open={monitorDialogOpen}
|
||||||
|
onOpenChange={setMonitorDialogOpen}
|
||||||
|
server={monitoringServer}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* SSH多窗口管理器 */}
|
{/* SSH多窗口管理器 */}
|
||||||
<SSHWindowManager />
|
<SSHWindowManager />
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,9 @@ import type {
|
|||||||
AlertRuleResponse,
|
AlertRuleResponse,
|
||||||
AlertRuleQuery,
|
AlertRuleQuery,
|
||||||
AlertRuleRequest,
|
AlertRuleRequest,
|
||||||
|
ServerMonitorResponse,
|
||||||
|
TimeRange,
|
||||||
|
MetricType,
|
||||||
} from './types';
|
} from './types';
|
||||||
import type { ServerFormValues } from './schema';
|
import type { ServerFormValues } from './schema';
|
||||||
import type { Page } from '@/types/base';
|
import type { Page } from '@/types/base';
|
||||||
@ -18,6 +21,7 @@ import type { Page } from '@/types/base';
|
|||||||
const CATEGORY_URL = '/api/v1/server-category';
|
const CATEGORY_URL = '/api/v1/server-category';
|
||||||
const SERVER_URL = '/api/v1/server';
|
const SERVER_URL = '/api/v1/server';
|
||||||
const ALERT_RULE_URL = '/api/v1/server/alert-rule';
|
const ALERT_RULE_URL = '/api/v1/server/alert-rule';
|
||||||
|
const MONITOR_URL = '/api/v1/server/monitor';
|
||||||
|
|
||||||
// ==================== 服务器分类 ====================
|
// ==================== 服务器分类 ====================
|
||||||
|
|
||||||
@ -78,22 +82,14 @@ export const getServerList = (params?: {
|
|||||||
/**
|
/**
|
||||||
* 获取服务器分页列表
|
* 获取服务器分页列表
|
||||||
*/
|
*/
|
||||||
export const getServers = (params: {
|
export const getServers = (params?: {
|
||||||
categoryId?: number;
|
categoryId?: number;
|
||||||
status?: string;
|
status?: string;
|
||||||
osType?: string;
|
osType?: string;
|
||||||
pageNum?: number;
|
pageNum?: number;
|
||||||
size?: number;
|
pageSize?: number;
|
||||||
}) =>
|
}) =>
|
||||||
request.get<Page<ServerResponse>>(`${SERVER_URL}/page`, {
|
request.get<Page<ServerResponse>>(`${SERVER_URL}/page`, { params });
|
||||||
params: {
|
|
||||||
pageNum: params.pageNum ?? 0,
|
|
||||||
size: params.size ?? 20,
|
|
||||||
categoryId: params.categoryId,
|
|
||||||
status: params.status,
|
|
||||||
osType: params.osType,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取服务器详情
|
* 获取服务器详情
|
||||||
@ -177,3 +173,22 @@ export const updateAlertRule = (id: number, data: AlertRuleRequest) =>
|
|||||||
export const deleteAlertRule = (id: number) =>
|
export const deleteAlertRule = (id: number) =>
|
||||||
request.delete<void>(`${ALERT_RULE_URL}/${id}`);
|
request.delete<void>(`${ALERT_RULE_URL}/${id}`);
|
||||||
|
|
||||||
|
// ==================== 服务器监控 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取服务器监控数据
|
||||||
|
* @param serverId 服务器ID
|
||||||
|
* @param timeRange 时间范围
|
||||||
|
* @param metrics 指标类型列表(可选,为空则查询所有)
|
||||||
|
*/
|
||||||
|
export const getServerMonitorMetrics = (
|
||||||
|
serverId: number,
|
||||||
|
timeRange: TimeRange,
|
||||||
|
metrics?: MetricType[]
|
||||||
|
) =>
|
||||||
|
request.get<ServerMonitorResponse>(`${MONITOR_URL}/${serverId}/metrics`, {
|
||||||
|
params: {
|
||||||
|
timeRange,
|
||||||
|
metrics: metrics?.join(','),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@ -371,3 +371,233 @@ export const AlertTypeLabels: Record<AlertType, { label: string; unit: string; d
|
|||||||
[AlertType.SERVER_STATUS]: { label: '服务器状态', unit: '次', description: '服务器连接状态告警' },
|
[AlertType.SERVER_STATUS]: { label: '服务器状态', unit: '次', description: '服务器连接状态告警' },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ==================== 服务器监控 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间范围枚举
|
||||||
|
*/
|
||||||
|
export enum TimeRange {
|
||||||
|
/** 最近1小时 */
|
||||||
|
LAST_1_HOUR = 'LAST_1_HOUR',
|
||||||
|
/** 最近6小时 */
|
||||||
|
LAST_6_HOURS = 'LAST_6_HOURS',
|
||||||
|
/** 最近24小时 */
|
||||||
|
LAST_24_HOURS = 'LAST_24_HOURS',
|
||||||
|
/** 最近7天 */
|
||||||
|
LAST_7_DAYS = 'LAST_7_DAYS',
|
||||||
|
/** 最近30天 */
|
||||||
|
LAST_30_DAYS = 'LAST_30_DAYS',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监控指标类型枚举
|
||||||
|
*/
|
||||||
|
export enum MetricType {
|
||||||
|
/** CPU */
|
||||||
|
CPU = 'CPU',
|
||||||
|
/** 内存 */
|
||||||
|
MEMORY = 'MEMORY',
|
||||||
|
/** 磁盘 */
|
||||||
|
DISK = 'DISK',
|
||||||
|
/** 网络 */
|
||||||
|
NETWORK = 'NETWORK',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间范围标签映射
|
||||||
|
*/
|
||||||
|
export const TimeRangeLabels: Record<TimeRange, { label: string; interval: string; description: string }> = {
|
||||||
|
[TimeRange.LAST_1_HOUR]: { label: '最近1小时', interval: '5分钟', description: '最近1小时的监控数据' },
|
||||||
|
[TimeRange.LAST_6_HOURS]: { label: '最近6小时', interval: '5分钟', description: '最近6小时的监控数据' },
|
||||||
|
[TimeRange.LAST_24_HOURS]: { label: '最近24小时', interval: '15分钟', description: '最近24小时的监控数据' },
|
||||||
|
[TimeRange.LAST_7_DAYS]: { label: '最近7天', interval: '1小时', description: '最近7天的监控数据' },
|
||||||
|
[TimeRange.LAST_30_DAYS]: { label: '最近30天', interval: '4小时', description: '最近30天的监控数据' },
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指标类型标签映射
|
||||||
|
*/
|
||||||
|
export const MetricTypeLabels: Record<MetricType, { label: string; unit: string; color: string }> = {
|
||||||
|
[MetricType.CPU]: { label: 'CPU使用率', unit: '%', color: '#3b82f6' },
|
||||||
|
[MetricType.MEMORY]: { label: '内存使用率', unit: '%', color: '#10b981' },
|
||||||
|
[MetricType.DISK]: { label: '磁盘使用率', unit: '%', color: '#f59e0b' },
|
||||||
|
[MetricType.NETWORK]: { label: '网络流量', unit: 'MB/s', color: '#8b5cf6' },
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CPU监控数据点
|
||||||
|
*/
|
||||||
|
export interface CpuMetricData {
|
||||||
|
/** 时间点 */
|
||||||
|
time: string;
|
||||||
|
/** CPU使用率(%) */
|
||||||
|
value: number;
|
||||||
|
/** 采集状态 */
|
||||||
|
status: 'SUCCESS' | 'FAILURE';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内存监控数据点
|
||||||
|
*/
|
||||||
|
export interface MemoryMetricData {
|
||||||
|
/** 时间点 */
|
||||||
|
time: string;
|
||||||
|
/** 内存使用率(%) */
|
||||||
|
usagePercent: number;
|
||||||
|
/** 已用内存(GB) */
|
||||||
|
usedGB: number;
|
||||||
|
/** 采集状态 */
|
||||||
|
status: 'SUCCESS' | 'FAILURE';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络监控数据点
|
||||||
|
*/
|
||||||
|
export interface NetworkMetricData {
|
||||||
|
/** 时间点 */
|
||||||
|
time: string;
|
||||||
|
/** 接收字节数(累计) */
|
||||||
|
rxBytes: number;
|
||||||
|
/** 发送字节数(累计) */
|
||||||
|
txBytes: number;
|
||||||
|
/** 接收速率(MB/s) */
|
||||||
|
rxMBps: number;
|
||||||
|
/** 发送速率(MB/s) */
|
||||||
|
txMBps: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 磁盘分区信息
|
||||||
|
*/
|
||||||
|
export interface DiskPartitionData {
|
||||||
|
/** 挂载点 */
|
||||||
|
mountPoint: string;
|
||||||
|
/** 文件系统 */
|
||||||
|
fileSystem: string;
|
||||||
|
/** 总容量(GB) */
|
||||||
|
totalSizeGB: number;
|
||||||
|
/** 已用容量(GB) */
|
||||||
|
usedSizeGB: number;
|
||||||
|
/** 使用率(%) */
|
||||||
|
usagePercent: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 磁盘监控数据
|
||||||
|
*/
|
||||||
|
export interface DiskMetricData {
|
||||||
|
/** 最新采集时间 */
|
||||||
|
latestTime: string;
|
||||||
|
/** 分区信息列表 */
|
||||||
|
partitions: DiskPartitionData[];
|
||||||
|
/** 时间范围内最大使用率(%) */
|
||||||
|
maxUsagePercent: number;
|
||||||
|
/** 最大使用率的分区 */
|
||||||
|
maxUsagePartition: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CPU统计信息
|
||||||
|
*/
|
||||||
|
export interface CpuStatistics {
|
||||||
|
/** 平均值(%) */
|
||||||
|
avg: number;
|
||||||
|
/** 最大值(%) */
|
||||||
|
max: number;
|
||||||
|
/** 最小值(%) */
|
||||||
|
min: number;
|
||||||
|
/** 峰值时间 */
|
||||||
|
maxTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内存统计信息
|
||||||
|
*/
|
||||||
|
export interface MemoryStatistics {
|
||||||
|
/** 平均使用率(%) */
|
||||||
|
avgPercent: number;
|
||||||
|
/** 最大使用率(%) */
|
||||||
|
maxPercent: number;
|
||||||
|
/** 最小使用率(%) */
|
||||||
|
minPercent: number;
|
||||||
|
/** 峰值时间 */
|
||||||
|
maxTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网络统计信息
|
||||||
|
*/
|
||||||
|
export interface NetworkStatistics {
|
||||||
|
/** 总接收字节数 */
|
||||||
|
totalRxBytes: number;
|
||||||
|
/** 总发送字节数 */
|
||||||
|
totalTxBytes: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务器信息
|
||||||
|
*/
|
||||||
|
export interface MonitorServerInfo {
|
||||||
|
/** 服务器ID */
|
||||||
|
serverId: number;
|
||||||
|
/** 服务器名称 */
|
||||||
|
serverName: string;
|
||||||
|
/** 主机IP */
|
||||||
|
hostIp: string;
|
||||||
|
/** 服务器状态 */
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间范围信息
|
||||||
|
*/
|
||||||
|
export interface MonitorTimeRangeInfo {
|
||||||
|
/** 开始时间 */
|
||||||
|
startTime: string;
|
||||||
|
/** 结束时间 */
|
||||||
|
endTime: string;
|
||||||
|
/** 数据聚合间隔 */
|
||||||
|
interval: string;
|
||||||
|
/** 实际返回的数据点数量 */
|
||||||
|
dataPoints: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监控指标数据
|
||||||
|
*/
|
||||||
|
export interface MonitorMetrics {
|
||||||
|
/** CPU数据 */
|
||||||
|
cpu: CpuMetricData[] | null;
|
||||||
|
/** 内存数据 */
|
||||||
|
memory: MemoryMetricData[] | null;
|
||||||
|
/** 网络数据 */
|
||||||
|
network: NetworkMetricData[] | null;
|
||||||
|
/** 磁盘数据 */
|
||||||
|
disk: DiskMetricData | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监控统计信息
|
||||||
|
*/
|
||||||
|
export interface MonitorStatistics {
|
||||||
|
/** CPU统计 */
|
||||||
|
cpu: CpuStatistics | null;
|
||||||
|
/** 内存统计 */
|
||||||
|
memory: MemoryStatistics | null;
|
||||||
|
/** 网络统计 */
|
||||||
|
network: NetworkStatistics | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务器监控数据响应
|
||||||
|
*/
|
||||||
|
export interface ServerMonitorResponse {
|
||||||
|
/** 服务器信息 */
|
||||||
|
server: MonitorServerInfo;
|
||||||
|
/** 时间范围信息 */
|
||||||
|
timeRange: MonitorTimeRangeInfo;
|
||||||
|
/** 指标数据 */
|
||||||
|
metrics: MonitorMetrics;
|
||||||
|
/** 统计信息 */
|
||||||
|
statistics: MonitorStatistics;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user