fix: App context for copy toast + remove polling + disk progress layout + process aliases
All checks were successful
Deploy Admin Frontend / build-and-deploy (push) Successful in 8s
All checks were successful
Deploy Admin Frontend / build-and-deploy (push) Successful in 8s
This commit is contained in:
parent
795ef9676d
commit
e297e2fac7
@ -33,7 +33,7 @@ export default function Dashboard() {
|
||||
const { data: serverData } = useQuery({
|
||||
queryKey: ['servers', 'metrics'],
|
||||
queryFn: getServerMetrics,
|
||||
refetchInterval: 15_000,
|
||||
staleTime: 30_000,
|
||||
})
|
||||
|
||||
const userTrendOption = useMemo(() => ({
|
||||
|
||||
@ -1,15 +1,19 @@
|
||||
import { useState } from 'react'
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { Card, Row, Col, Progress, Table, Tag, Typography, Button, Space, Tooltip, message } from 'antd'
|
||||
import { Card, Row, Col, Progress, Table, Tag, Typography, Button, Space, Tooltip, App } from 'antd'
|
||||
import { CloudServerOutlined, ReloadOutlined, CopyOutlined, GlobalOutlined } from '@ant-design/icons'
|
||||
import { getServerMetrics, type ServerInfo, type ProcessInfo } from '@/services/server-api'
|
||||
|
||||
const { Text, Title } = Typography
|
||||
|
||||
function CopyTag({ text, icon, color }: { text: string; icon?: React.ReactNode; color?: string }) {
|
||||
const { message } = App.useApp()
|
||||
return (
|
||||
<Tag color={color || 'default'} style={{ cursor: 'pointer' }}
|
||||
onClick={async () => { await navigator.clipboard.writeText(text); message.success(`已复制: ${text}`) }}>
|
||||
onClick={async () => {
|
||||
try { await navigator.clipboard.writeText(text); message.success(`已复制: ${text}`) }
|
||||
catch { message.error('复制失败') }
|
||||
}}>
|
||||
{icon}{text} <CopyOutlined style={{ fontSize: 10 }} />
|
||||
</Tag>
|
||||
)
|
||||
@ -21,7 +25,7 @@ function ServerCard({ server }: { server: ServerInfo }) {
|
||||
|
||||
return (
|
||||
<Card title={<Space><CloudServerOutlined />{server.name}<Tag color="blue">{server.role}</Tag></Space>} style={{ height: '100%' }}>
|
||||
<Row gutter={[12, 8]} style={{ marginBottom: 16 }}>
|
||||
<Row gutter={[8, 8]} style={{ marginBottom: 16 }}>
|
||||
<Col span={24}>
|
||||
<Space wrap size={[4, 4]}>
|
||||
<CopyTag text={server.network.publicIp} icon="🌐 " color="cyan" />
|
||||
@ -33,11 +37,10 @@ function ServerCard({ server }: { server: ServerInfo }) {
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={[16, 16]}>
|
||||
<Row gutter={[16, 12]}>
|
||||
<Col xs={12} sm={6}>
|
||||
<Text type="secondary" style={{ fontSize: 11 }}>CPU · {server.cpu.cores}核</Text>
|
||||
<Progress percent={server.cpu.usagePercent} strokeColor={cpuColor} size="small" format={p => `${p}%`} />
|
||||
<Text style={{ fontSize: 10 }} type="secondary">{server.cpu.model?.slice(0, 28)}</Text>
|
||||
</Col>
|
||||
<Col xs={12} sm={6}>
|
||||
<Text type="secondary" style={{ fontSize: 11 }}>内存</Text>
|
||||
@ -45,58 +48,54 @@ function ServerCard({ server }: { server: ServerInfo }) {
|
||||
<Text style={{ fontSize: 10 }} type="secondary">{server.memory.used}/{server.memory.total}</Text>
|
||||
</Col>
|
||||
<Col xs={24} sm={12}>
|
||||
<Text type="secondary" style={{ fontSize: 11, marginBottom: 2, display: 'block' }}>磁盘</Text>
|
||||
{server.disks.filter(d => d.total !== '-').length === 0 && <Text type="secondary" style={{ fontSize: 11 }}>暂无数据</Text>}
|
||||
{server.disks.filter(d => d.total !== '-').map(d => (
|
||||
<div key={d.mount} style={{ marginBottom: 3 }}>
|
||||
<Space size={4} style={{ width: '100%' }}>
|
||||
<Text type="secondary" style={{ fontSize: 11, marginBottom: 4, display: 'block' }}>磁盘</Text>
|
||||
{(server.disks || []).map(d => (
|
||||
<div key={d.mount} style={{ marginBottom: 4 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<Text style={{ fontSize: 10, minWidth: 36 }} type="secondary">{d.mount}</Text>
|
||||
<Progress percent={d.percent} size="small" strokeColor={d.percent > 80 ? '#ff4d4f' : '#1677ff'} style={{ flex: 1, margin: 0 }} />
|
||||
<Text style={{ fontSize: 10, whiteSpace: 'nowrap' }}>{d.used}/{d.total}</Text>
|
||||
</Space>
|
||||
<div style={{ flex: 1 }}><Progress percent={d.percent} size="small" strokeColor={d.percent > 80 ? '#ff4d4f' : '#1677ff'} style={{ margin: 0 }} /></div>
|
||||
<Text style={{ fontSize: 10, whiteSpace: 'nowrap', minWidth: 70, textAlign: 'right' }}>{d.used} / {d.total}</Text>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Text type="secondary" style={{ fontSize: 11 }}>运行时间</Text>
|
||||
<div><Text>{server.uptime}</Text></div>
|
||||
<Text type="secondary" style={{ fontSize: 11 }}>运行 {server.uptime}</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Table
|
||||
dataSource={server.processes}
|
||||
dataSource={server.processes || []}
|
||||
rowKey="pid" size="small" pagination={false}
|
||||
style={{ marginTop: 12 }}
|
||||
columns={[
|
||||
{ title: '进程', dataIndex: 'name', width: 140, ellipsis: true, render: (name: string, r: ProcessInfo) => (
|
||||
<Tooltip title={r.desc ? `${r.desc}\n命令: ${r.command}` : `命令: ${r.command}`}>
|
||||
<span>{name}</span>
|
||||
</Tooltip>
|
||||
{ title: '进程', dataIndex: 'name', width: 120, ellipsis: true, render: (name: string, r: ProcessInfo) => (
|
||||
<Tooltip title={r.desc ? `${r.desc}\n${r.command}` : r.command}><span>{name}</span></Tooltip>
|
||||
)},
|
||||
{ title: '说明', dataIndex: 'desc', width: 120, ellipsis: true, render: (d: string) => <Text type="secondary" style={{ fontSize: 12 }}>{d || '-'}</Text> },
|
||||
{ title: '说明', dataIndex: 'desc', width: 100, ellipsis: true, render: (d: string) => <Text type="secondary" style={{ fontSize: 12 }}>{d || '-'}</Text> },
|
||||
{ title: 'CPU', dataIndex: 'cpu', width: 55 },
|
||||
{ title: 'MEM', dataIndex: 'mem', width: 55 },
|
||||
]}
|
||||
locale={{ emptyText: '暂无进程' }}
|
||||
locale={{ emptyText: '暂无' }}
|
||||
/>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default function ServersPage() {
|
||||
function ServersContent() {
|
||||
const qc = useQueryClient()
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
|
||||
const { data } = useQuery({
|
||||
queryKey: ['servers', 'metrics'],
|
||||
queryFn: getServerMetrics,
|
||||
refetchInterval: 15_000,
|
||||
staleTime: 30_000,
|
||||
})
|
||||
|
||||
const handleRefresh = async () => {
|
||||
setRefreshing(true)
|
||||
await qc.invalidateQueries({ queryKey: ['servers', 'metrics'] })
|
||||
setTimeout(() => setRefreshing(false), 1000)
|
||||
setTimeout(() => setRefreshing(false), 800)
|
||||
}
|
||||
|
||||
return (
|
||||
@ -115,3 +114,7 @@ export default function ServersPage() {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function ServersPage() {
|
||||
return <App><ServersContent /></App>
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user