refactor: restructure navigation and module layout
Reorganize navigation and module structure based on new requirements. Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
This commit is contained in:
3
app/monitoring/alerts/loading.tsx
Normal file
3
app/monitoring/alerts/loading.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Loading() {
|
||||
return null
|
||||
}
|
||||
383
app/monitoring/alerts/page.tsx
Normal file
383
app/monitoring/alerts/page.tsx
Normal file
@@ -0,0 +1,383 @@
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import {
|
||||
Bell,
|
||||
ArrowLeft,
|
||||
Plus,
|
||||
Search,
|
||||
Filter,
|
||||
AlertTriangle,
|
||||
CheckCircle2,
|
||||
Clock,
|
||||
Settings,
|
||||
Trash2,
|
||||
XCircle,
|
||||
} from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { CreateAlertRuleDialog } from "@/components/dialogs/create-alert-rule-dialog"
|
||||
import { AlertRuleSettingsDialog } from "@/components/dialogs/alert-rule-settings-dialog"
|
||||
|
||||
const alertRules = [
|
||||
{
|
||||
id: 1,
|
||||
name: "CPU使用率告警",
|
||||
condition: "CPU > 80%",
|
||||
duration: "5分钟",
|
||||
severity: "warning",
|
||||
status: "enabled",
|
||||
triggers: 3,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "内存使用率告警",
|
||||
condition: "Memory > 85%",
|
||||
duration: "5分钟",
|
||||
severity: "warning",
|
||||
status: "enabled",
|
||||
triggers: 1,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "API延迟告警",
|
||||
condition: "P99 > 200ms",
|
||||
duration: "3分钟",
|
||||
severity: "critical",
|
||||
status: "enabled",
|
||||
triggers: 5,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "磁盘空间告警",
|
||||
condition: "Disk > 90%",
|
||||
duration: "10分钟",
|
||||
severity: "critical",
|
||||
status: "enabled",
|
||||
triggers: 0,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "服务宕机告警",
|
||||
condition: "Service Down",
|
||||
duration: "1分钟",
|
||||
severity: "critical",
|
||||
status: "enabled",
|
||||
triggers: 0,
|
||||
},
|
||||
]
|
||||
|
||||
const alertHistory = [
|
||||
{
|
||||
id: 1,
|
||||
rule: "CPU使用率告警",
|
||||
message: "node-04 CPU使用率达到 85%",
|
||||
severity: "warning",
|
||||
status: "active",
|
||||
time: "10分钟前",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
rule: "磁盘空间告警",
|
||||
message: "磁盘使用率达到 80%,接近阈值",
|
||||
severity: "warning",
|
||||
status: "active",
|
||||
time: "2小时前",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
rule: "API延迟告警",
|
||||
message: "API网关P99延迟达到 250ms",
|
||||
severity: "critical",
|
||||
status: "resolved",
|
||||
time: "1小时前",
|
||||
resolvedAt: "45分钟前",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
rule: "内存使用率告警",
|
||||
message: "node-02 内存使用率达到 88%",
|
||||
severity: "warning",
|
||||
status: "resolved",
|
||||
time: "3小时前",
|
||||
resolvedAt: "2小时前",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
rule: "服务宕机告警",
|
||||
message: "Redis服务短暂不可用",
|
||||
severity: "critical",
|
||||
status: "resolved",
|
||||
time: "1天前",
|
||||
resolvedAt: "1天前",
|
||||
},
|
||||
]
|
||||
|
||||
export default function AlertsPage() {
|
||||
const [searchTerm, setSearchTerm] = useState("")
|
||||
const [activeTab, setActiveTab] = useState<"rules" | "history">("history")
|
||||
const [showCreateDialog, setShowCreateDialog] = useState(false)
|
||||
const [showSettingsDialog, setShowSettingsDialog] = useState(false)
|
||||
const [selectedRule, setSelectedRule] = useState<(typeof alertRules)[0] | null>(null)
|
||||
|
||||
const activeAlerts = alertHistory.filter((a) => a.status === "active")
|
||||
|
||||
const handleRuleSettings = (rule: (typeof alertRules)[0]) => {
|
||||
setSelectedRule(rule)
|
||||
setShowSettingsDialog(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-red-50/30 p-6">
|
||||
<div className="max-w-7xl mx-auto space-y-6">
|
||||
{/* 页面标题 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<Link href="/monitoring">
|
||||
<Button variant="ghost" size="icon" className="rounded-full">
|
||||
<ArrowLeft className="w-5 h-5" />
|
||||
</Button>
|
||||
</Link>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-slate-800">告警中心</h1>
|
||||
<p className="text-slate-500 mt-1">告警规则配置与历史记录</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
className="bg-gradient-to-r from-red-500 to-orange-600 hover:from-red-600 hover:to-orange-700"
|
||||
onClick={() => setShowCreateDialog(true)}
|
||||
>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
新建告警规则
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 告警统计 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs text-slate-500">活跃告警</p>
|
||||
<p className="text-2xl font-bold text-red-600">{activeAlerts.length}</p>
|
||||
</div>
|
||||
<div className="w-10 h-10 rounded-lg bg-red-100 flex items-center justify-center">
|
||||
<AlertTriangle className="w-5 h-5 text-red-600" />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs text-slate-500">今日已解决</p>
|
||||
<p className="text-2xl font-bold text-emerald-600">8</p>
|
||||
</div>
|
||||
<div className="w-10 h-10 rounded-lg bg-emerald-100 flex items-center justify-center">
|
||||
<CheckCircle2 className="w-5 h-5 text-emerald-600" />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs text-slate-500">告警规则数</p>
|
||||
<p className="text-2xl font-bold text-slate-800">{alertRules.length}</p>
|
||||
</div>
|
||||
<div className="w-10 h-10 rounded-lg bg-blue-100 flex items-center justify-center">
|
||||
<Bell className="w-5 h-5 text-blue-600" />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs text-slate-500">平均响应时间</p>
|
||||
<p className="text-2xl font-bold text-slate-800">15分钟</p>
|
||||
</div>
|
||||
<div className="w-10 h-10 rounded-lg bg-amber-100 flex items-center justify-center">
|
||||
<Clock className="w-5 h-5 text-amber-600" />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 标签页切换 */}
|
||||
<div className="flex items-center gap-2 border-b border-slate-200">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className={`rounded-none border-b-2 ${activeTab === "history" ? "border-slate-800 text-slate-800" : "border-transparent text-slate-500"}`}
|
||||
onClick={() => setActiveTab("history")}
|
||||
>
|
||||
告警历史
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className={`rounded-none border-b-2 ${activeTab === "rules" ? "border-slate-800 text-slate-800" : "border-transparent text-slate-500"}`}
|
||||
onClick={() => setActiveTab("rules")}
|
||||
>
|
||||
告警规则
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{activeTab === "history" ? (
|
||||
<>
|
||||
{/* 筛选工具栏 */}
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400" />
|
||||
<Input
|
||||
placeholder="搜索告警..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-9 bg-white/70"
|
||||
/>
|
||||
</div>
|
||||
<Button variant="outline">
|
||||
<Filter className="w-4 h-4 mr-2" />
|
||||
筛选
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 告警历史列表 */}
|
||||
<div className="space-y-3">
|
||||
{alertHistory.map((alert) => (
|
||||
<Card
|
||||
key={alert.id}
|
||||
className={`bg-white/70 backdrop-blur border-slate-200/60 ${alert.status === "active" ? "border-l-4 border-l-red-500" : ""}`}
|
||||
>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
{alert.status === "active" ? (
|
||||
<div className="w-10 h-10 rounded-lg bg-red-100 flex items-center justify-center">
|
||||
<AlertTriangle className="w-5 h-5 text-red-600" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-10 h-10 rounded-lg bg-slate-100 flex items-center justify-center">
|
||||
<CheckCircle2 className="w-5 h-5 text-slate-400" />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<h4 className="font-medium text-slate-800">{alert.rule}</h4>
|
||||
<Badge
|
||||
className={
|
||||
alert.severity === "critical"
|
||||
? "bg-red-100 text-red-700"
|
||||
: "bg-amber-100 text-amber-700"
|
||||
}
|
||||
>
|
||||
{alert.severity === "critical" ? "严重" : "警告"}
|
||||
</Badge>
|
||||
{alert.status === "active" ? (
|
||||
<Badge className="bg-red-100 text-red-700">活跃</Badge>
|
||||
) : (
|
||||
<Badge className="bg-emerald-100 text-emerald-700">已解决</Badge>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-sm text-slate-500 mt-1">{alert.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="text-right">
|
||||
<p className="text-sm text-slate-600">{alert.time}</p>
|
||||
{alert.resolvedAt && <p className="text-xs text-slate-400">解决于 {alert.resolvedAt}</p>}
|
||||
</div>
|
||||
{alert.status === "active" && (
|
||||
<Button variant="outline" size="sm">
|
||||
<XCircle className="w-4 h-4 mr-1" />
|
||||
标记解决
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
/* 告警规则列表 */
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardContent className="p-0">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr className="border-b border-slate-200">
|
||||
<th className="text-left py-4 px-6 text-xs font-medium text-slate-500">规则名称</th>
|
||||
<th className="text-left py-4 px-6 text-xs font-medium text-slate-500">触发条件</th>
|
||||
<th className="text-left py-4 px-6 text-xs font-medium text-slate-500">持续时间</th>
|
||||
<th className="text-left py-4 px-6 text-xs font-medium text-slate-500">严重程度</th>
|
||||
<th className="text-left py-4 px-6 text-xs font-medium text-slate-500">触发次数</th>
|
||||
<th className="text-left py-4 px-6 text-xs font-medium text-slate-500">状态</th>
|
||||
<th className="text-left py-4 px-6 text-xs font-medium text-slate-500">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{alertRules.map((rule) => (
|
||||
<tr key={rule.id} className="border-b border-slate-100 hover:bg-slate-50/50">
|
||||
<td className="py-4 px-6 text-sm font-medium text-slate-800">{rule.name}</td>
|
||||
<td className="py-4 px-6">
|
||||
<code className="text-xs bg-slate-100 px-2 py-1 rounded">{rule.condition}</code>
|
||||
</td>
|
||||
<td className="py-4 px-6 text-sm text-slate-600">{rule.duration}</td>
|
||||
<td className="py-4 px-6">
|
||||
<Badge
|
||||
className={
|
||||
rule.severity === "critical" ? "bg-red-100 text-red-700" : "bg-amber-100 text-amber-700"
|
||||
}
|
||||
>
|
||||
{rule.severity === "critical" ? "严重" : "警告"}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="py-4 px-6 text-sm text-slate-600">{rule.triggers}</td>
|
||||
<td className="py-4 px-6">
|
||||
<Badge className="bg-emerald-100 text-emerald-700">启用</Badge>
|
||||
</td>
|
||||
<td className="py-4 px-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="ghost" size="sm" onClick={() => handleRuleSettings(rule)}>
|
||||
<Settings className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm">
|
||||
<Trash2 className="w-4 h-4 text-red-500" />
|
||||
</Button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
<CreateAlertRuleDialog open={showCreateDialog} onOpenChange={setShowCreateDialog} />
|
||||
<AlertRuleSettingsDialog
|
||||
open={showSettingsDialog}
|
||||
onOpenChange={setShowSettingsDialog}
|
||||
rule={
|
||||
selectedRule
|
||||
? {
|
||||
name: selectedRule.name,
|
||||
condition: selectedRule.condition,
|
||||
severity: selectedRule.severity,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
3
app/monitoring/business/loading.tsx
Normal file
3
app/monitoring/business/loading.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Loading() {
|
||||
return null
|
||||
}
|
||||
299
app/monitoring/business/page.tsx
Normal file
299
app/monitoring/business/page.tsx
Normal file
@@ -0,0 +1,299 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import {
|
||||
TrendingUp,
|
||||
ArrowLeft,
|
||||
Users,
|
||||
Target,
|
||||
DollarSign,
|
||||
Activity,
|
||||
RefreshCw,
|
||||
ArrowUpRight,
|
||||
ArrowDownRight,
|
||||
} from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import {
|
||||
LineChart,
|
||||
Line,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
ResponsiveContainer,
|
||||
PieChart,
|
||||
Pie,
|
||||
Cell,
|
||||
} from "recharts"
|
||||
|
||||
const kpiData = [
|
||||
{ name: "数据接入成功率", value: 99.2, target: 99, unit: "%", trend: "up", change: 0.3 },
|
||||
{ name: "标签覆盖率", value: 87.5, target: 85, unit: "%", trend: "up", change: 2.1 },
|
||||
{ name: "价值预测准确率", value: 91.8, target: 90, unit: "%", trend: "up", change: 1.2 },
|
||||
{ name: "API调用增长", value: 15.2, target: 10, unit: "%", trend: "up", change: 3.5 },
|
||||
]
|
||||
|
||||
const userTrend = [
|
||||
{ date: "12-06", total: 4020000, active: 1250000, new: 12500 },
|
||||
{ date: "12-07", total: 4025000, active: 1280000, new: 13200 },
|
||||
{ date: "12-08", total: 4030000, active: 1260000, new: 11800 },
|
||||
{ date: "12-09", total: 4038000, active: 1320000, new: 14500 },
|
||||
{ date: "12-10", total: 4045000, active: 1350000, new: 15200 },
|
||||
{ date: "12-11", total: 4052000, active: 1380000, new: 16800 },
|
||||
{ date: "12-12", total: 4062000, active: 1420000, new: 18500 },
|
||||
]
|
||||
|
||||
const valueDistribution = [
|
||||
{ name: "S级", value: 328000, percent: 8.1, color: "#f59e0b" },
|
||||
{ name: "A级", value: 650000, percent: 16.0, color: "#3b82f6" },
|
||||
{ name: "B级", value: 1220000, percent: 30.0, color: "#10b981" },
|
||||
{ name: "C级", value: 1180000, percent: 29.1, color: "#8b5cf6" },
|
||||
{ name: "D级", value: 684000, percent: 16.8, color: "#6b7280" },
|
||||
]
|
||||
|
||||
const projectRanking = [
|
||||
{ rank: 1, name: "美妆护肤项目", users: 580000, value: 28.5, growth: 12.3 },
|
||||
{ rank: 2, name: "母婴亲子项目", users: 420000, value: 22.1, growth: 8.5 },
|
||||
{ rank: 3, name: "运动健康项目", users: 380000, value: 18.6, growth: 15.2 },
|
||||
{ rank: 4, name: "家居生活项目", users: 320000, value: 15.2, growth: 6.8 },
|
||||
{ rank: 5, name: "数码科技项目", users: 280000, value: 12.8, growth: 10.5 },
|
||||
]
|
||||
|
||||
export default function BusinessPage() {
|
||||
const [totalValue, setTotalValue] = useState(125.8)
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setTotalValue((prev) => prev + Math.random() * 0.01)
|
||||
}, 5000)
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-blue-50/30 p-6">
|
||||
<div className="max-w-7xl mx-auto space-y-6">
|
||||
{/* 页面标题 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<Link href="/monitoring">
|
||||
<Button variant="ghost" size="icon" className="rounded-full">
|
||||
<ArrowLeft className="w-5 h-5" />
|
||||
</Button>
|
||||
</Link>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-slate-800">业务指标看板</h1>
|
||||
<p className="text-slate-500 mt-1">核心业务KPI监控与分析</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button variant="outline">
|
||||
<RefreshCw className="w-4 h-4 mr-2" />
|
||||
刷新数据
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 核心KPI */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
{kpiData.map((kpi, i) => (
|
||||
<Card key={i} className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<p className="text-sm text-slate-500">{kpi.name}</p>
|
||||
<Badge
|
||||
className={
|
||||
kpi.value >= kpi.target ? "bg-emerald-100 text-emerald-700" : "bg-amber-100 text-amber-700"
|
||||
}
|
||||
>
|
||||
目标: {kpi.target}
|
||||
{kpi.unit}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="flex items-end justify-between">
|
||||
<p className="text-3xl font-bold text-slate-800">
|
||||
{kpi.value}
|
||||
<span className="text-lg text-slate-500">{kpi.unit}</span>
|
||||
</p>
|
||||
<div
|
||||
className={`flex items-center gap-1 ${kpi.trend === "up" ? "text-emerald-600" : "text-red-600"}`}
|
||||
>
|
||||
{kpi.trend === "up" ? <ArrowUpRight className="w-4 h-4" /> : <ArrowDownRight className="w-4 h-4" />}
|
||||
<span className="text-sm font-medium">{kpi.change}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 资产总价值 */}
|
||||
<Card className="bg-gradient-to-r from-blue-500 to-indigo-600 border-0">
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="text-white">
|
||||
<p className="text-sm opacity-80">数据资产总价值</p>
|
||||
<p className="text-5xl font-bold mt-2">{totalValue.toFixed(2)} 亿</p>
|
||||
<div className="flex items-center gap-4 mt-4">
|
||||
<div>
|
||||
<p className="text-xs opacity-60">总用户数</p>
|
||||
<p className="text-lg font-semibold">40.62 亿</p>
|
||||
</div>
|
||||
<div className="w-px h-10 bg-white/20" />
|
||||
<div>
|
||||
<p className="text-xs opacity-60">覆盖项目</p>
|
||||
<p className="text-lg font-semibold">156 个</p>
|
||||
</div>
|
||||
<div className="w-px h-10 bg-white/20" />
|
||||
<div>
|
||||
<p className="text-xs opacity-60">合作企业</p>
|
||||
<p className="text-lg font-semibold">89 家</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-32 h-32 rounded-full bg-white/20 flex items-center justify-center">
|
||||
<DollarSign className="w-16 h-16 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{/* 用户增长趋势 */}
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-base flex items-center gap-2">
|
||||
<Users className="w-4 h-4 text-slate-500" />
|
||||
用户增长趋势 (近7天)
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="h-64">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<LineChart data={userTrend}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#e2e8f0" />
|
||||
<XAxis dataKey="date" tick={{ fontSize: 12 }} stroke="#94a3b8" />
|
||||
<YAxis
|
||||
tick={{ fontSize: 12 }}
|
||||
stroke="#94a3b8"
|
||||
tickFormatter={(v) => `${(v / 10000).toFixed(0)}万`}
|
||||
/>
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: "white",
|
||||
border: "1px solid #e2e8f0",
|
||||
borderRadius: "8px",
|
||||
}}
|
||||
formatter={(value: number) => [`${(value / 10000).toFixed(1)}万`]}
|
||||
/>
|
||||
<Line type="monotone" dataKey="active" stroke="#3b82f6" strokeWidth={2} name="活跃用户" />
|
||||
<Line type="monotone" dataKey="new" stroke="#10b981" strokeWidth={2} name="新增用户" />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 价值等级分布 */}
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-base flex items-center gap-2">
|
||||
<Target className="w-4 h-4 text-slate-500" />
|
||||
用户价值分布
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="h-64 flex-1">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={valueDistribution}
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
innerRadius={60}
|
||||
outerRadius={90}
|
||||
dataKey="value"
|
||||
label={({ name, percent }) => `${name} ${percent}%`}
|
||||
>
|
||||
{valueDistribution.map((entry, index) => (
|
||||
<Cell key={`cell-${index}`} fill={entry.color} />
|
||||
))}
|
||||
</Pie>
|
||||
<Tooltip formatter={(value: number) => [`${(value / 10000).toFixed(1)}万`, "用户数"]} />
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{valueDistribution.map((item, i) => (
|
||||
<div key={i} className="flex items-center gap-2">
|
||||
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: item.color }} />
|
||||
<span className="text-sm text-slate-600">
|
||||
{item.name}: {(item.value / 10000).toFixed(1)}万
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 项目价值排名 */}
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-base flex items-center gap-2">
|
||||
<Activity className="w-4 h-4 text-slate-500" />
|
||||
项目价值排名 Top 5
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr className="border-b border-slate-200">
|
||||
<th className="text-left py-3 px-4 text-xs font-medium text-slate-500">排名</th>
|
||||
<th className="text-left py-3 px-4 text-xs font-medium text-slate-500">项目名称</th>
|
||||
<th className="text-left py-3 px-4 text-xs font-medium text-slate-500">用户数</th>
|
||||
<th className="text-left py-3 px-4 text-xs font-medium text-slate-500">资产价值</th>
|
||||
<th className="text-left py-3 px-4 text-xs font-medium text-slate-500">环比增长</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{projectRanking.map((project) => (
|
||||
<tr key={project.rank} className="border-b border-slate-100 hover:bg-slate-50/50">
|
||||
<td className="py-3 px-4">
|
||||
<div
|
||||
className={`w-6 h-6 rounded-full flex items-center justify-center text-xs font-bold ${
|
||||
project.rank === 1
|
||||
? "bg-amber-100 text-amber-700"
|
||||
: project.rank === 2
|
||||
? "bg-slate-200 text-slate-700"
|
||||
: project.rank === 3
|
||||
? "bg-orange-100 text-orange-700"
|
||||
: "bg-slate-100 text-slate-600"
|
||||
}`}
|
||||
>
|
||||
{project.rank}
|
||||
</div>
|
||||
</td>
|
||||
<td className="py-3 px-4 text-sm font-medium text-slate-800">{project.name}</td>
|
||||
<td className="py-3 px-4 text-sm text-slate-600">{(project.users / 10000).toFixed(1)}万</td>
|
||||
<td className="py-3 px-4 text-sm font-medium text-slate-800">{project.value}亿</td>
|
||||
<td className="py-3 px-4">
|
||||
<div className="flex items-center gap-1 text-emerald-600">
|
||||
<TrendingUp className="w-4 h-4" />
|
||||
<span className="text-sm font-medium">{project.growth}%</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
3
app/monitoring/health/loading.tsx
Normal file
3
app/monitoring/health/loading.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Loading() {
|
||||
return null
|
||||
}
|
||||
220
app/monitoring/health/page.tsx
Normal file
220
app/monitoring/health/page.tsx
Normal file
@@ -0,0 +1,220 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Progress } from "@/components/ui/progress"
|
||||
import { ArrowLeft, Server, CheckCircle2, AlertTriangle, RefreshCw, Clock } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from "recharts"
|
||||
|
||||
const performanceData = [
|
||||
{ time: "5m", p50: 32, p95: 85, p99: 120 },
|
||||
{ time: "10m", p50: 35, p95: 88, p99: 125 },
|
||||
{ time: "15m", p50: 30, p95: 82, p99: 115 },
|
||||
{ time: "20m", p50: 38, p95: 92, p99: 135 },
|
||||
{ time: "25m", p50: 33, p95: 86, p99: 122 },
|
||||
{ time: "30m", p50: 31, p95: 84, p99: 118 },
|
||||
]
|
||||
|
||||
const healthChecks = [
|
||||
{ name: "数据库连接池", status: "healthy", value: "48/50", description: "活跃连接数" },
|
||||
{ name: "Redis缓存", status: "healthy", value: "99.9%", description: "命中率" },
|
||||
{ name: "消息队列", status: "healthy", value: "0", description: "积压消息" },
|
||||
{ name: "定时任务", status: "healthy", value: "12/12", description: "运行中任务" },
|
||||
{ name: "外部API", status: "degraded", value: "95.2%", description: "成功率" },
|
||||
{ name: "文件存储", status: "healthy", value: "1.6TB", description: "已使用" },
|
||||
]
|
||||
|
||||
const nodeStatus = [
|
||||
{ id: 1, name: "node-01", role: "Master", cpu: 62, memory: 68, status: "healthy" },
|
||||
{ id: 2, name: "node-02", role: "Worker", cpu: 55, memory: 72, status: "healthy" },
|
||||
{ id: 3, name: "node-03", role: "Worker", cpu: 48, memory: 65, status: "healthy" },
|
||||
{ id: 4, name: "node-04", role: "Worker", cpu: 71, memory: 78, status: "degraded" },
|
||||
]
|
||||
|
||||
export default function HealthPage() {
|
||||
const [overallHealth, setOverallHealth] = useState(98.5)
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setOverallHealth((prev) => Math.min(100, Math.max(95, prev + (Math.random() - 0.5) * 1)))
|
||||
}, 5000)
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-emerald-50/30 p-6">
|
||||
<div className="max-w-7xl mx-auto space-y-6">
|
||||
{/* 页面标题 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<Link href="/monitoring">
|
||||
<Button variant="ghost" size="icon" className="rounded-full">
|
||||
<ArrowLeft className="w-5 h-5" />
|
||||
</Button>
|
||||
</Link>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-slate-800">系统健康度</h1>
|
||||
<p className="text-slate-500 mt-1">服务状态、性能指标与集群监控</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button variant="outline">
|
||||
<RefreshCw className="w-4 h-4 mr-2" />
|
||||
刷新
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 整体健康度 */}
|
||||
<Card className="bg-gradient-to-r from-emerald-500 to-teal-600 border-0">
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="text-white">
|
||||
<p className="text-sm opacity-80">系统整体健康度</p>
|
||||
<p className="text-5xl font-bold mt-2">{overallHealth.toFixed(1)}%</p>
|
||||
<p className="text-sm opacity-80 mt-2">所有核心服务运行正常</p>
|
||||
</div>
|
||||
<div className="w-32 h-32 rounded-full bg-white/20 flex items-center justify-center">
|
||||
<CheckCircle2 className="w-16 h-16 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 健康检查项 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{healthChecks.map((check, i) => (
|
||||
<Card key={i} className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-slate-700">{check.name}</p>
|
||||
<p className="text-2xl font-bold text-slate-800 mt-1">{check.value}</p>
|
||||
<p className="text-xs text-slate-500">{check.description}</p>
|
||||
</div>
|
||||
{check.status === "healthy" ? (
|
||||
<CheckCircle2 className="w-8 h-8 text-emerald-500" />
|
||||
) : (
|
||||
<AlertTriangle className="w-8 h-8 text-amber-500" />
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 响应时间分布 */}
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-base flex items-center gap-2">
|
||||
<Clock className="w-4 h-4 text-slate-500" />
|
||||
API响应时间分布 (最近30分钟)
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="h-64">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<LineChart data={performanceData}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#e2e8f0" />
|
||||
<XAxis dataKey="time" tick={{ fontSize: 12 }} stroke="#94a3b8" />
|
||||
<YAxis tick={{ fontSize: 12 }} stroke="#94a3b8" tickFormatter={(v) => `${v}ms`} />
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: "white",
|
||||
border: "1px solid #e2e8f0",
|
||||
borderRadius: "8px",
|
||||
}}
|
||||
formatter={(value: number) => [`${value}ms`]}
|
||||
/>
|
||||
<Line type="monotone" dataKey="p50" stroke="#10b981" strokeWidth={2} name="P50" />
|
||||
<Line type="monotone" dataKey="p95" stroke="#f59e0b" strokeWidth={2} name="P95" />
|
||||
<Line type="monotone" dataKey="p99" stroke="#ef4444" strokeWidth={2} name="P99" />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-6 mt-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-3 h-3 rounded-full bg-emerald-500" />
|
||||
<span className="text-xs text-slate-600">P50: 32ms</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-3 h-3 rounded-full bg-amber-500" />
|
||||
<span className="text-xs text-slate-600">P95: 85ms</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-3 h-3 rounded-full bg-red-500" />
|
||||
<span className="text-xs text-slate-600">P99: 120ms</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 集群节点状态 */}
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-base flex items-center gap-2">
|
||||
<Server className="w-4 h-4 text-slate-500" />
|
||||
集群节点状态
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr className="border-b border-slate-200">
|
||||
<th className="text-left py-3 px-4 text-xs font-medium text-slate-500">节点</th>
|
||||
<th className="text-left py-3 px-4 text-xs font-medium text-slate-500">角色</th>
|
||||
<th className="text-left py-3 px-4 text-xs font-medium text-slate-500">CPU</th>
|
||||
<th className="text-left py-3 px-4 text-xs font-medium text-slate-500">内存</th>
|
||||
<th className="text-left py-3 px-4 text-xs font-medium text-slate-500">状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{nodeStatus.map((node) => (
|
||||
<tr key={node.id} className="border-b border-slate-100 hover:bg-slate-50/50">
|
||||
<td className="py-3 px-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Server className="w-4 h-4 text-slate-400" />
|
||||
<span className="text-sm font-medium text-slate-800">{node.name}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="py-3 px-4">
|
||||
<Badge variant="outline">{node.role}</Badge>
|
||||
</td>
|
||||
<td className="py-3 px-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Progress value={node.cpu} className="w-20 h-2" />
|
||||
<span className="text-sm text-slate-600">{node.cpu}%</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="py-3 px-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<Progress value={node.memory} className="w-20 h-2" />
|
||||
<span className="text-sm text-slate-600">{node.memory}%</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="py-3 px-4">
|
||||
{node.status === "healthy" ? (
|
||||
<Badge className="bg-emerald-100 text-emerald-700">
|
||||
<CheckCircle2 className="w-3 h-3 mr-1" />
|
||||
正常
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge className="bg-amber-100 text-amber-700">
|
||||
<AlertTriangle className="w-3 h-3 mr-1" />
|
||||
降级
|
||||
</Badge>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
17
app/monitoring/loading.tsx
Normal file
17
app/monitoring/loading.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Skeleton } from "@/components/ui/skeleton"
|
||||
|
||||
export default function Loading() {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-emerald-50/30 p-6">
|
||||
<div className="max-w-7xl mx-auto space-y-6">
|
||||
<Skeleton className="h-10 w-64" />
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
{[...Array(4)].map((_, i) => (
|
||||
<Skeleton key={i} className="h-28 rounded-xl" />
|
||||
))}
|
||||
</div>
|
||||
<Skeleton className="h-64 rounded-xl" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
381
app/monitoring/page.tsx
Normal file
381
app/monitoring/page.tsx
Normal file
@@ -0,0 +1,381 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Progress } from "@/components/ui/progress"
|
||||
import {
|
||||
Activity,
|
||||
Server,
|
||||
Cpu,
|
||||
HardDrive,
|
||||
Wifi,
|
||||
AlertTriangle,
|
||||
CheckCircle2,
|
||||
Bell,
|
||||
RefreshCw,
|
||||
ArrowRight,
|
||||
TrendingUp,
|
||||
} from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, AreaChart, Area } from "recharts"
|
||||
|
||||
// 模拟系统资源数据
|
||||
const systemMetrics = [
|
||||
{ time: "00:00", cpu: 45, memory: 62, disk: 78, network: 120 },
|
||||
{ time: "04:00", cpu: 32, memory: 58, disk: 78, network: 80 },
|
||||
{ time: "08:00", cpu: 58, memory: 65, disk: 79, network: 180 },
|
||||
{ time: "12:00", cpu: 72, memory: 71, disk: 79, network: 320 },
|
||||
{ time: "16:00", cpu: 68, memory: 69, disk: 80, network: 280 },
|
||||
{ time: "20:00", cpu: 55, memory: 64, disk: 80, network: 200 },
|
||||
]
|
||||
|
||||
const recentAlerts = [
|
||||
{ id: 1, type: "warning", message: "数据同步任务延迟超过阈值", time: "10分钟前", status: "active" },
|
||||
{ id: 2, type: "info", message: "模型训练任务已完成", time: "30分钟前", status: "resolved" },
|
||||
{ id: 3, type: "error", message: "API网关响应超时", time: "1小时前", status: "resolved" },
|
||||
{ id: 4, type: "warning", message: "磁盘使用率超过80%", time: "2小时前", status: "active" },
|
||||
]
|
||||
|
||||
const serviceStatus = [
|
||||
{ name: "数据接入服务", status: "healthy", uptime: "99.99%", latency: "12ms" },
|
||||
{ name: "标签计算引擎", status: "healthy", uptime: "99.95%", latency: "45ms" },
|
||||
{ name: "AI推理服务", status: "healthy", uptime: "99.90%", latency: "120ms" },
|
||||
{ name: "API网关", status: "degraded", uptime: "99.85%", latency: "85ms" },
|
||||
{ name: "数据库集群", status: "healthy", uptime: "99.99%", latency: "8ms" },
|
||||
{ name: "缓存服务", status: "healthy", uptime: "99.99%", latency: "2ms" },
|
||||
]
|
||||
|
||||
export default function MonitoringPage() {
|
||||
const [cpuUsage, setCpuUsage] = useState(65)
|
||||
const [memoryUsage, setMemoryUsage] = useState(68)
|
||||
const [diskUsage, setDiskUsage] = useState(80)
|
||||
const [networkIO, setNetworkIO] = useState(256)
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setCpuUsage((prev) => Math.min(95, Math.max(30, prev + (Math.random() - 0.5) * 10)))
|
||||
setMemoryUsage((prev) => Math.min(90, Math.max(50, prev + (Math.random() - 0.5) * 5)))
|
||||
setNetworkIO((prev) => Math.min(500, Math.max(100, prev + (Math.random() - 0.5) * 50)))
|
||||
}, 3000)
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-emerald-50/30 p-6">
|
||||
<div className="max-w-7xl mx-auto space-y-6">
|
||||
{/* 页面标题 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-slate-800">系统监控与运营看板</h1>
|
||||
<p className="text-slate-500 mt-1">实时监控系统健康状态、服务运行和告警信息</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="outline">
|
||||
<Bell className="w-4 h-4 mr-2" />
|
||||
告警设置
|
||||
</Button>
|
||||
<Button variant="outline">
|
||||
<RefreshCw className="w-4 h-4 mr-2" />
|
||||
刷新
|
||||
</Button>
|
||||
<Link href="/monitoring/alerts">
|
||||
<Button className="bg-gradient-to-r from-emerald-500 to-teal-600 hover:from-emerald-600 hover:to-teal-700">
|
||||
<Activity className="w-4 h-4 mr-2" />
|
||||
告警中心
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 系统资源概览 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-blue-500 to-cyan-600 flex items-center justify-center">
|
||||
<Cpu className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<span className="text-sm font-medium text-slate-700">CPU使用率</span>
|
||||
</div>
|
||||
<span
|
||||
className={`text-xl font-bold ${cpuUsage > 80 ? "text-red-600" : cpuUsage > 60 ? "text-amber-600" : "text-emerald-600"}`}
|
||||
>
|
||||
{cpuUsage.toFixed(1)}%
|
||||
</span>
|
||||
</div>
|
||||
<Progress value={cpuUsage} className="h-2" />
|
||||
<p className="text-xs text-slate-500 mt-2">8核 / 16线程</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-violet-500 to-purple-600 flex items-center justify-center">
|
||||
<Server className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<span className="text-sm font-medium text-slate-700">内存使用</span>
|
||||
</div>
|
||||
<span
|
||||
className={`text-xl font-bold ${memoryUsage > 85 ? "text-red-600" : memoryUsage > 70 ? "text-amber-600" : "text-emerald-600"}`}
|
||||
>
|
||||
{memoryUsage.toFixed(1)}%
|
||||
</span>
|
||||
</div>
|
||||
<Progress value={memoryUsage} className="h-2" />
|
||||
<p className="text-xs text-slate-500 mt-2">54.4GB / 80GB</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-amber-500 to-orange-600 flex items-center justify-center">
|
||||
<HardDrive className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<span className="text-sm font-medium text-slate-700">磁盘使用</span>
|
||||
</div>
|
||||
<span className={`text-xl font-bold ${diskUsage > 85 ? "text-red-600" : "text-amber-600"}`}>
|
||||
{diskUsage}%
|
||||
</span>
|
||||
</div>
|
||||
<Progress value={diskUsage} className="h-2" />
|
||||
<p className="text-xs text-slate-500 mt-2">1.6TB / 2TB</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-emerald-500 to-teal-600 flex items-center justify-center">
|
||||
<Wifi className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<span className="text-sm font-medium text-slate-700">网络IO</span>
|
||||
</div>
|
||||
<span className="text-xl font-bold text-emerald-600">{networkIO} MB/s</span>
|
||||
</div>
|
||||
<Progress value={(networkIO / 500) * 100} className="h-2" />
|
||||
<p className="text-xs text-slate-500 mt-2">入站: 180MB/s | 出站: 76MB/s</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 功能入口 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<Link href="/monitoring/health">
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60 hover:shadow-lg hover:border-slate-300 transition-all cursor-pointer group h-full">
|
||||
<CardContent className="p-5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-emerald-500 to-teal-600 flex items-center justify-center">
|
||||
<Activity className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-slate-800">系统健康度</h3>
|
||||
<p className="text-sm text-slate-500">服务状态与性能指标</p>
|
||||
</div>
|
||||
</div>
|
||||
<ArrowRight className="w-5 h-5 text-slate-400 group-hover:text-slate-600 group-hover:translate-x-1 transition-all" />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
|
||||
<Link href="/monitoring/alerts">
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60 hover:shadow-lg hover:border-slate-300 transition-all cursor-pointer group h-full">
|
||||
<CardContent className="p-5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-red-500 to-orange-600 flex items-center justify-center">
|
||||
<Bell className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-slate-800">告警中心</h3>
|
||||
<p className="text-sm text-slate-500">告警规则与通知管理</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge className="bg-red-100 text-red-700">2</Badge>
|
||||
<ArrowRight className="w-5 h-5 text-slate-400 group-hover:text-slate-600 group-hover:translate-x-1 transition-all" />
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
|
||||
<Link href="/monitoring/business">
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60 hover:shadow-lg hover:border-slate-300 transition-all cursor-pointer group h-full">
|
||||
<CardContent className="p-5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-blue-500 to-indigo-600 flex items-center justify-center">
|
||||
<TrendingUp className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-slate-800">业务指标</h3>
|
||||
<p className="text-sm text-slate-500">核心业务KPI监控</p>
|
||||
</div>
|
||||
</div>
|
||||
<ArrowRight className="w-5 h-5 text-slate-400 group-hover:text-slate-600 group-hover:translate-x-1 transition-all" />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
{/* 系统资源趋势 */}
|
||||
<Card className="lg:col-span-2 bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-base">系统资源趋势 (24小时)</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="h-64">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<AreaChart data={systemMetrics}>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#e2e8f0" />
|
||||
<XAxis dataKey="time" tick={{ fontSize: 12 }} stroke="#94a3b8" />
|
||||
<YAxis tick={{ fontSize: 12 }} stroke="#94a3b8" />
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
backgroundColor: "white",
|
||||
border: "1px solid #e2e8f0",
|
||||
borderRadius: "8px",
|
||||
}}
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient id="colorCpu" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="5%" stopColor="#3b82f6" stopOpacity={0.3} />
|
||||
<stop offset="95%" stopColor="#3b82f6" stopOpacity={0} />
|
||||
</linearGradient>
|
||||
<linearGradient id="colorMemory" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="5%" stopColor="#8b5cf6" stopOpacity={0.3} />
|
||||
<stop offset="95%" stopColor="#8b5cf6" stopOpacity={0} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<Area type="monotone" dataKey="cpu" stroke="#3b82f6" strokeWidth={2} fill="url(#colorCpu)" />
|
||||
<Area type="monotone" dataKey="memory" stroke="#8b5cf6" strokeWidth={2} fill="url(#colorMemory)" />
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-6 mt-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-3 h-3 rounded-full bg-blue-500" />
|
||||
<span className="text-xs text-slate-600">CPU</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-3 h-3 rounded-full bg-violet-500" />
|
||||
<span className="text-xs text-slate-600">内存</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 最近告警 */}
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-base flex items-center gap-2">
|
||||
<Bell className="w-4 h-4 text-red-500" />
|
||||
最近告警
|
||||
</CardTitle>
|
||||
<Link href="/monitoring/alerts">
|
||||
<Button variant="ghost" size="sm">
|
||||
查看全部
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-3">
|
||||
{recentAlerts.map((alert) => (
|
||||
<div key={alert.id} className="flex items-start gap-3 p-3 bg-slate-50/80 rounded-lg">
|
||||
{alert.type === "error" ? (
|
||||
<AlertTriangle className="w-5 h-5 text-red-500 mt-0.5" />
|
||||
) : alert.type === "warning" ? (
|
||||
<AlertTriangle className="w-5 h-5 text-amber-500 mt-0.5" />
|
||||
) : (
|
||||
<CheckCircle2 className="w-5 h-5 text-blue-500 mt-0.5" />
|
||||
)}
|
||||
<div className="flex-1">
|
||||
<p className="text-sm text-slate-700">{alert.message}</p>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className="text-xs text-slate-500">{alert.time}</span>
|
||||
{alert.status === "active" ? (
|
||||
<Badge className="bg-red-100 text-red-700 text-xs">待处理</Badge>
|
||||
) : (
|
||||
<Badge className="bg-emerald-100 text-emerald-700 text-xs">已解决</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 服务状态 */}
|
||||
<Card className="bg-white/70 backdrop-blur border-slate-200/60">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-base flex items-center gap-2">
|
||||
<Server className="w-4 h-4 text-slate-500" />
|
||||
服务状态
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{serviceStatus.map((service, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={`flex items-center justify-between p-4 rounded-lg border ${
|
||||
service.status === "healthy"
|
||||
? "bg-emerald-50/50 border-emerald-200"
|
||||
: service.status === "degraded"
|
||||
? "bg-amber-50/50 border-amber-200"
|
||||
: "bg-red-50/50 border-red-200"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className={`w-3 h-3 rounded-full ${
|
||||
service.status === "healthy"
|
||||
? "bg-emerald-500"
|
||||
: service.status === "degraded"
|
||||
? "bg-amber-500 animate-pulse"
|
||||
: "bg-red-500 animate-pulse"
|
||||
}`}
|
||||
/>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-slate-800">{service.name}</p>
|
||||
<p className="text-xs text-slate-500">
|
||||
可用性: {service.uptime} | 延迟: {service.latency}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Badge
|
||||
className={
|
||||
service.status === "healthy"
|
||||
? "bg-emerald-100 text-emerald-700"
|
||||
: service.status === "degraded"
|
||||
? "bg-amber-100 text-amber-700"
|
||||
: "bg-red-100 text-red-700"
|
||||
}
|
||||
>
|
||||
{service.status === "healthy" ? "正常" : service.status === "degraded" ? "降级" : "故障"}
|
||||
</Badge>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user