page
import React, { useState } from 'react';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Server, MapPin, ExternalLink } from 'lucide-react';
const DataCenterMonitor = () => {
const [activeCity, setActiveCity] = useState('shanghai');
const [activeLab, setActiveLab] = useState('taishan');
const labData = {
shanghai: {
name: '上海',
labs: {
taishan: {
name: '泰山稳定性实验室',
facility: '浦东电脑实验室',
link: 'https://taishan.example.com',
equipment: { online: 500, offline: 80 },
tasks: { healthy: 500, subHealthy: 50, blocked: 80 }
},
huangpu: {
name: '黄浦云计算中心',
facility: '黄浦数据处理中心',
link: 'https://huangpu.example.com',
equipment: { online: 420, offline: 45 },
tasks: { healthy: 380, subHealthy: 60, blocked: 25 }
},
minhang: {
name: '闵行AI实验室',
facility: '闵行智能计算中心',
link: 'https://minhang.example.com',
equipment: { online: 360, offline: 30 },
tasks: { healthy: 340, subHealthy: 35, blocked: 15 }
}
}
},
nanjing: {
name: '南京',
labs: {
jinling: {
name: '金陵稳定性实验室',
facility: '江宁计算中心',
link: 'https://jinling.example.com',
equipment: { online: 420, offline: 60 },
tasks: { healthy: 400, subHealthy: 40, blocked: 40 }
},
qinhuai: {
name: '秦淮数据中心',
facility: '秦淮云服务中心',
link: 'https://qinhuai.example.com',
equipment: { online: 380, offline: 50 },
tasks: { healthy: 360, subHealthy: 35, blocked: 35 }
}
}
},
xian: {
name: '西安',
labs: {
qinling: {
name: '秦岭稳定性实验室',
facility: '高新技术园区',
link: 'https://qinling.example.com',
equipment: { online: 380, offline: 50 },
tasks: { healthy: 360, subHealthy: 35, blocked: 35 }
},
gaoxin: {
name: '高新超算中心',
facility: '西部云计算基地',
link: 'https://gaoxin.example.com',
equipment: { online: 320, offline: 40 },
tasks: { healthy: 300, subHealthy: 30, blocked: 30 }
}
}
}
};
const currentCityData = labData[activeCity];
const labKeys = Object.keys(currentCityData.labs);
const currentLabData = currentCityData.labs[activeLab] || currentCityData.labs[labKeys[0]];
React.useEffect(() => {
const firstLabKey = Object.keys(labData[activeCity].labs)[0];
setActiveLab(firstLabKey);
}, [activeCity]);
return (
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 py-8">
<div className="max-w-[1200px] mx-auto px-6">
<div className="flex gap-6">
{/* 左侧城市选择栏 */}
<div className="w-48 flex-shrink-0">
<Card className="bg-white shadow-sm border border-gray-200 overflow-hidden">
<CardHeader className="pb-4 bg-gradient-to-r from-blue-50 to-indigo-50 border-b border-gray-200">
<CardTitle className="text-base font-semibold text-gray-800">
{currentCityData.name}
</CardTitle>
<p className="text-xs text-gray-500 mt-0.5">数据中心</p>
</CardHeader>
<CardContent className="p-3">
<Tabs value={activeCity} onValueChange={setActiveCity} orientation="vertical" className="w-full">
<TabsList className="flex flex-col w-full bg-transparent gap-1.5 p-0">
<TabsTrigger
value="shanghai"
className="w-full justify-start px-3 py-2.5 text-sm data-[state=active]:bg-blue-600 data-[state=active]:text-white data-[state=active]:shadow-sm data-[state=inactive]:text-gray-700 hover:bg-gray-50 data-[state=inactive]:hover:bg-gray-50 transition-all rounded-md font-medium"
>
上海
</TabsTrigger>
<TabsTrigger
value="nanjing"
className="w-full justify-start px-3 py-2.5 text-sm data-[state=active]:bg-blue-600 data-[state=active]:text-white data-[state=active]:shadow-sm data-[state=inactive]:text-gray-700 hover:bg-gray-50 data-[state=inactive]:hover:bg-gray-50 transition-all rounded-md font-medium"
>
南京
</TabsTrigger>
<TabsTrigger
value="xian"
className="w-full justify-start px-3 py-2.5 text-sm data-[state=active]:bg-blue-600 data-[state=active]:text-white data-[state=active]:shadow-sm data-[state=inactive]:text-gray-700 hover:bg-gray-50 data-[state=inactive]:hover:bg-gray-50 transition-all rounded-md font-medium"
>
西安
</TabsTrigger>
</TabsList>
</Tabs>
</CardContent>
</Card>
</div>
{/* 右侧主内容区 */}
<div className="flex-1">
<Card className="bg-white shadow-sm border border-gray-200">
<CardContent className="p-6">
{/* 顶部:实验室信息 */}
<div className="flex items-center justify-between mb-5 pb-5 border-b border-gray-200">
<div className="flex items-center gap-8">
<div>
<p className="text-xs text-gray-500 mb-1">实验室名称</p>
<p className="text-base font-semibold text-gray-900">{currentLabData.name}</p>
</div>
<div className="w-px h-10 bg-gray-200"></div>
<div>
<p className="text-xs text-gray-500 mb-1">实验设施</p>
<p className="text-base font-semibold text-gray-900">{currentLabData.facility}</p>
</div>
</div>
</div>
{/* 实验室选择 Tabs */}
<div className="mb-6">
<Tabs value={activeLab} onValueChange={setActiveLab}>
<TabsList className="bg-gray-100 p-0.5 inline-flex rounded-lg h-auto">
{Object.entries(currentCityData.labs).map(([key, lab]) => (
<TabsTrigger
key={key}
value={key}
className="data-[state=active]:bg-white data-[state=active]:text-gray-900 data-[state=active]:shadow-sm data-[state=inactive]:text-gray-600 px-4 py-2 text-sm font-medium transition-all rounded-md"
>
{lab.name}
</TabsTrigger>
))}
</TabsList>
</Tabs>
</div>
{/* 主体内容:左侧图片(3/4) + 右侧状态(1/4) */}
<div className="flex gap-5">
{/* 左侧:机房图片占位符 */}
<div className="flex-[3]">
<div className="bg-gradient-to-br from-slate-900 to-slate-800 rounded-lg overflow-hidden shadow-md">
<div className="relative h-80 flex items-center justify-center">
{/* 服务器机架可视化 */}
<div className="absolute inset-0 flex items-center justify-center opacity-20">
<div className="grid grid-cols-14 gap-0.5">
{[...Array(140)].map((_, i) => (
<div
key={i}
className="w-1.5 h-12 rounded-sm"
style={{
background: i % 16 < 12 ? 'linear-gradient(to bottom, #3b82f6, #1d4ed8)' : '#475569',
animationDelay: `${i * 0.01}s`,
animation: i % 16 < 12 ? 'pulse 3s infinite' : 'none'
}}
/>
))}
</div>
</div>
{/* 中心信息 */}
<div className="relative z-10 text-center bg-slate-800/70 backdrop-blur-sm px-6 py-5 rounded-lg border border-slate-700/50">
<Server className="w-16 h-16 text-blue-400 mx-auto mb-2" />
<p className="text-white text-base font-semibold mb-0.5">数据中心机房</p>
<p className="text-slate-300 text-xs">{currentLabData.facility}</p>
</div>
</div>
</div>
</div>
{/* 右侧:设备状态 + 任务状态 */}
<div className="flex-[1] space-y-4">
{/* 设备状态 */}
<div className="bg-gray-50 rounded-lg p-4 border border-gray-200">
<h3 className="text-xs font-semibold text-gray-700 mb-3">设备状态</h3>
<div className="space-y-2.5">
<div className="flex justify-between items-center">
<span className="text-xs text-gray-600">Online</span>
<span className="text-xl font-bold text-green-600">{currentLabData.equipment.online}</span>
</div>
<div className="flex justify-between items-center">
<span className="text-xs text-gray-600">Offline</span>
<span className="text-xl font-bold text-red-600">{currentLabData.equipment.offline}</span>
</div>
</div>
</div>
{/* 任务状态 */}
<div className="bg-gray-50 rounded-lg p-4 border border-gray-200">
<h3 className="text-xs font-semibold text-gray-700 mb-3">任务状态</h3>
<div className="space-y-2.5">
<div className="flex justify-between items-center">
<span className="text-xs text-gray-600">健康</span>
<span className="text-xl font-bold text-emerald-600">{currentLabData.tasks.healthy}</span>
</div>
<div className="flex justify-between items-center">
<span className="text-xs text-gray-600">亚健康</span>
<span className="text-xl font-bold text-amber-600">{currentLabData.tasks.subHealthy}</span>
</div>
<div className="flex justify-between items-center">
<span className="text-xs text-gray-600">阻塞</span>
<span className="text-xl font-bold text-rose-600">{currentLabData.tasks.blocked}</span>
</div>
</div>
</div>
{/* 实验室链接 */}
<a
href={currentLabData.link}
target="_blank"
rel="noopener noreferrer"
className="block bg-white border-2 border-blue-200 hover:border-blue-400 rounded-lg p-3 transition-all group"
>
<div className="flex items-center justify-between">
<div className="flex-1">
<p className="text-xs text-gray-500 mb-0.5">访问链接</p>
<p className="text-sm font-medium text-blue-600 group-hover:text-blue-700 truncate">
{currentLabData.name}
</p>
</div>
<ExternalLink className="w-4 h-4 text-blue-500 flex-shrink-0 ml-2" />
</div>
</a>
</div>
</div>
</CardContent>
</Card>
</div>
</div>
</div>
</div>
);
};
export default DataCenterMonitor;