refactor: 全局代码模块化重构 — 所有文件<700行

后端 handler 拆分:
- automation.go(1067→4文件), server.go(1011→5文件), ldap.go(815→3文件), user.go(704→2文件)

前端组件拆分:
- SystemManager(1782→569), AlertManager(1133→220), K8sClusters(1062→4文件)
- Dashboard(941→4文件), ServerManager(905→640), ApprovalFlow(830→548)
- AlertCenter(775→693), AIPredictiveAnalytics(760→670)
- CloudBilling(728→663), GrafanaDashboard(717→643), sidebar(726→689)

Mock 数据模块化:
- mocks/data.ts(2552行→barrel+11个domain模块)

新增 CLAUDE.md 项目全局开发规范

修复: AlertCenter/LogManager ListResponse.data→.list 类型不匹配
验证: go build ✅ + tsc --noEmit ✅ + npm run build ✅
This commit is contained in:
attacker committed 2026-06-15 01:32:15 +08:00
1 parent e8dbe65955
commit 73b5a48df9
37 files changed
+5148 -5237

No files matched your search

@@ -22,6 +22,7 @@ import { Separator } from '@/components/ui/separator';
import { toast } from 'sonner';
import { useConfirm } from '@/components/ui/confirm-dialog';
import { dashboardApi } from '@/services/api';
import { GrafanaPanelContent } from './GrafanaPanelContent';
interface Dashboard {
id: string;
@@ -36,7 +37,7 @@ interface Dashboard {
isDefault: boolean;
}
interface Panel {
export interface Panel {
id: string;
title: string;
type: 'graph' | 'stat' | 'gauge' | 'table' | 'pie' | 'heatmap' | 'text';
@@ -250,81 +251,6 @@ const GrafanaDashboard: React.FC = () => {
}
};
// 模拟图表数据
const generateChartData = (panel: Panel) => {
const dataPoints = 24;
const data = [];
for (let i = 0; i < dataPoints; i++) {
data.push({
time: new Date(Date.now() - (dataPoints - i) * 3600000).toLocaleTimeString(),
value: Math.random() * 100
});
}
return data;
};
// 渲染面板内容
const renderPanelContent = (panel: Panel) => {
const data = generateChartData(panel);
switch (panel.type) {
case 'stat':
return (
<div className="flex flex-col items-center justify-center h-full">
<span className="text-4xl font-bold text-blue-500">{Math.floor(Math.random() * 1000)}</span>
<span className="text-sm text-muted-foreground mt-2">{panel.options?.unit || 'requests/s'}</span>
<div className="flex items-center gap-1 mt-2 text-green-500 text-sm">
<TrendingUp className="w-4 h-4" />
<span>+12.5%</span>
</div>
</div>
);
case 'gauge':
return (
<div className="flex flex-col items-center justify-center h-full">
<div className="relative w-24 h-24">
<svg className="w-full h-full" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke="#e5e7eb" strokeWidth="10" />
<circle
cx="50"
cy="50"
r="40"
fill="none"
stroke="#3b82f6"
strokeWidth="10"
strokeDasharray={`${Math.random() * 251} 251`}
transform="rotate(-90 50 50)"
/>
</svg>
<div className="absolute inset-0 flex items-center justify-center">
<span className="text-xl font-bold">{Math.floor(Math.random() * 100)}%</span>
</div>
</div>
<span className="text-sm text-muted-foreground mt-2">CPU使用率</span>
</div>
);
case 'graph':
default:
return (
<div className="h-full flex flex-col">
<div className="flex-1 flex items-end gap-1 px-2">
{data.map((d, i) => (
<div
key={i}
className="flex-1 bg-blue-500 rounded-t"
style={{ height: `${d.value}%`, opacity: 0.3 + (i / data.length) * 0.7 }}
/>
))}
</div>
<div className="flex justify-between text-xs text-muted-foreground px-2 pt-2">
<span>{data[0].time}</span>
<span>{data[data.length - 1].time}</span>
</div>
</div>
);
}
};
return (
<div className={`space-y-4 ${fullscreen ? 'fixed inset-0 z-50 bg-background p-4' : ''}`}>
{/* 顶部工具栏 */}
@@ -448,7 +374,7 @@ const GrafanaDashboard: React.FC = () => {
</div>
</CardHeader>
<CardContent className="p-4 pt-0 flex-1">
{renderPanelContent(panel)}
<GrafanaPanelContent panel={panel} />
</CardContent>
</Card>
))}
@@ -0,0 +1,77 @@
import { TrendingUp } from 'lucide-react';
import type { Panel } from './GrafanaDashboard';
/** 模拟图表数据 */
function generateChartData(panel: Panel) {
const dataPoints = 24;
const data = [];
for (let i = 0; i < dataPoints; i++) {
data.push({
time: new Date(Date.now() - (dataPoints - i) * 3600000).toLocaleTimeString(),
value: Math.random() * 100
});
}
return data;
}
/** 渲染面板内容 */
export function GrafanaPanelContent({ panel }: { panel: Panel }) {
const data = generateChartData(panel);
switch (panel.type) {
case 'stat':
return (
<div className="flex flex-col items-center justify-center h-full">
<span className="text-4xl font-bold text-blue-500">{Math.floor(Math.random() * 1000)}</span>
<span className="text-sm text-muted-foreground mt-2">{panel.options?.unit || 'requests/s'}</span>
<div className="flex items-center gap-1 mt-2 text-green-500 text-sm">
<TrendingUp className="w-4 h-4" />
<span>+12.5%</span>
</div>
</div>
);
case 'gauge':
return (
<div className="flex flex-col items-center justify-center h-full">
<div className="relative w-24 h-24">
<svg className="w-full h-full" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke="#e5e7eb" strokeWidth="10" />
<circle
cx="50"
cy="50"
r="40"
fill="none"
stroke="#3b82f6"
strokeWidth="10"
strokeDasharray={`${Math.random() * 251} 251`}
transform="rotate(-90 50 50)"
/>
</svg>
<div className="absolute inset-0 flex items-center justify-center">
<span className="text-xl font-bold">{Math.floor(Math.random() * 100)}%</span>
</div>
</div>
<span className="text-sm text-muted-foreground mt-2">CPU使用率</span>
</div>
);
case 'graph':
default:
return (
<div className="h-full flex flex-col">
<div className="flex-1 flex items-end gap-1 px-2">
{data.map((d, i) => (
<div
key={i}
className="flex-1 bg-blue-500 rounded-t"
style={{ height: `${d.value}%`, opacity: 0.3 + (i / data.length) * 0.7 }}
/>
))}
</div>
<div className="flex justify-between text-xs text-muted-foreground px-2 pt-2">
<span>{data[0].time}</span>
<span>{data[data.length - 1].time}</span>
</div>
</div>
);
}
}