cleanup: 移除3个废弃AI组件(AIOpsHub/AIFaultDiagnosis/AIScriptGenerator), 清理1087行死代码
This commit is contained in:
1 parent
d1415561ef
commit
674fe5b17f
4 files changed
+1
-1088
No files matched your search
@@ -1,437 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from '@/components/ui/accordion';
|
||||
import { toast } from 'sonner';
|
||||
import {
|
||||
Brain,
|
||||
Search,
|
||||
AlertTriangle,
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
Clock,
|
||||
Server,
|
||||
Activity,
|
||||
FileText,
|
||||
Terminal,
|
||||
TrendingUp,
|
||||
AlertCircle,
|
||||
Sparkles,
|
||||
Cpu,
|
||||
MemoryStick,
|
||||
HardDrive,
|
||||
Network,
|
||||
Database,
|
||||
Globe,
|
||||
Shield,
|
||||
Zap,
|
||||
Lightbulb,
|
||||
ChevronRight,
|
||||
Loader2,
|
||||
MessageSquare,
|
||||
History,
|
||||
} from 'lucide-react';
|
||||
|
||||
interface DiagnosisResult {
|
||||
id: string;
|
||||
problem: string;
|
||||
symptoms: string[];
|
||||
rootCauses: {
|
||||
description: string;
|
||||
probability: number;
|
||||
evidence: string[];
|
||||
}[];
|
||||
solutions: {
|
||||
title: string;
|
||||
steps: string[];
|
||||
difficulty: 'easy' | 'medium' | 'hard';
|
||||
estimatedTime: string;
|
||||
}[];
|
||||
relatedLogs: {
|
||||
timestamp: string;
|
||||
level: string;
|
||||
message: string;
|
||||
source: string;
|
||||
}[];
|
||||
similarCases: {
|
||||
id: string;
|
||||
title: string;
|
||||
resolution: string;
|
||||
date: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
const commonProblems = [
|
||||
{ id: 'slow', name: '服务响应缓慢', icon: Clock },
|
||||
{ id: 'crash', name: '应用崩溃/重启', icon: XCircle },
|
||||
{ id: 'memory', name: '内存泄漏', icon: MemoryStick },
|
||||
{ id: 'cpu', name: 'CPU使用率过高', icon: Cpu },
|
||||
{ id: 'disk', name: '磁盘空间不足', icon: HardDrive },
|
||||
{ id: 'network', name: '网络连接问题', icon: Network },
|
||||
{ id: 'db', name: '数据库性能问题', icon: Database },
|
||||
{ id: 'security', name: '安全异常', icon: Shield },
|
||||
];
|
||||
|
||||
export function AIFaultDiagnosis() {
|
||||
const [symptomInput, setSymptomInput] = useState('');
|
||||
const [isDiagnosing, setIsDiagnosing] = useState(false);
|
||||
const [diagnosisResult, setDiagnosisResult] = useState<DiagnosisResult | null>(null);
|
||||
const [selectedProblem, setSelectedProblem] = useState<string | null>(null);
|
||||
const [diagnosisHistory, setDiagnosisHistory] = useState<DiagnosisResult[]>([]);
|
||||
|
||||
const handleDiagnose = () => {
|
||||
if (!symptomInput.trim()) return;
|
||||
toast.info('AI 诊断功能需配置 LLM 服务');
|
||||
};
|
||||
|
||||
const handleProblemSelect = (problemId: string) => {
|
||||
setSelectedProblem(problemId);
|
||||
const problemMap: Record<string, string> = {
|
||||
slow: 'Web服务响应缓慢,页面加载时间超过5秒',
|
||||
crash: '应用服务频繁崩溃重启,错误日志显示内存溢出',
|
||||
memory: '服务器内存持续增长,疑似存在内存泄漏',
|
||||
cpu: 'CPU使用率持续在90%以上,系统响应变慢',
|
||||
disk: '磁盘空间使用率超过95%,影响日志写入',
|
||||
network: '网络连接不稳定,丢包率较高',
|
||||
db: '数据库查询变慢,部分SQL执行超时',
|
||||
security: '检测到异常登录尝试和安全告警',
|
||||
};
|
||||
setSymptomInput(problemMap[problemId] || '');
|
||||
};
|
||||
|
||||
const getDifficultyColor = (difficulty: string) => {
|
||||
switch (difficulty) {
|
||||
case 'easy':
|
||||
return 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30';
|
||||
case 'medium':
|
||||
return 'bg-amber-500/10 text-amber-400 border-amber-500/30';
|
||||
case 'hard':
|
||||
return 'bg-red-500/10 text-red-400 border-red-500/30';
|
||||
default:
|
||||
return 'bg-slate-500/10 text-slate-400 border-slate-500/30';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-lg bg-gradient-to-br from-red-500 to-orange-500 flex items-center justify-center">
|
||||
<Brain className="h-5 w-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-slate-100">AI 故障诊断</h1>
|
||||
<p className="text-sm text-slate-500">智能分析故障症状,定位根因并提供解决方案</p>
|
||||
</div>
|
||||
</div>
|
||||
<Badge variant="outline" className="border-purple-500/30 text-purple-400 bg-purple-500/10">
|
||||
<Sparkles className="h-3 w-3 mr-1" />
|
||||
AI 诊断引擎
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<Tabs defaultValue="diagnose" className="space-y-4">
|
||||
<TabsList className="bg-slate-800 border border-slate-700">
|
||||
<TabsTrigger value="diagnose" className="data-[state=active]:bg-slate-700">
|
||||
<Search className="h-4 w-4 mr-2" />
|
||||
故障诊断
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="history" className="data-[state=active]:bg-slate-700">
|
||||
<History className="h-4 w-4 mr-2" />
|
||||
诊断历史
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="diagnose" className="space-y-4">
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
{/* Common Problems */}
|
||||
<Card className="bg-slate-800 border-slate-700 col-span-1">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-slate-100 text-sm">常见问题</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2">
|
||||
{commonProblems.map((problem) => (
|
||||
<Button
|
||||
key={problem.id}
|
||||
variant="ghost"
|
||||
className={`w-full justify-start ${
|
||||
selectedProblem === problem.id
|
||||
? 'bg-purple-500/20 text-purple-300'
|
||||
: 'text-slate-300 hover:text-slate-100 hover:bg-slate-700'
|
||||
}`}
|
||||
onClick={() => handleProblemSelect(problem.id)}
|
||||
>
|
||||
<problem.icon className="h-4 w-4 mr-2" />
|
||||
{problem.name}
|
||||
</Button>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Input Area */}
|
||||
<Card className="bg-slate-800 border-slate-700 col-span-2">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-slate-100 flex items-center gap-2">
|
||||
<MessageSquare className="h-5 w-5 text-blue-400" />
|
||||
描述故障症状
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<Textarea
|
||||
value={symptomInput}
|
||||
onChange={(e) => setSymptomInput(e.target.value)}
|
||||
placeholder="请详细描述故障现象,例如:服务响应缓慢、应用崩溃、内存占用过高等..."
|
||||
className="bg-slate-900 border-slate-700 text-slate-100 min-h-[120px]"
|
||||
/>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
className="flex-1 bg-purple-600 hover:bg-purple-700"
|
||||
onClick={handleDiagnose}
|
||||
disabled={isDiagnosing || !symptomInput.trim()}
|
||||
>
|
||||
{isDiagnosing ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
||||
AI诊断中...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Brain className="h-4 w-4 mr-2" />
|
||||
开始诊断
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<Button variant="outline" className="border-slate-700 text-slate-300">
|
||||
<Terminal className="h-4 w-4 mr-2" />
|
||||
上传日志
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Diagnosis Result */}
|
||||
{diagnosisResult && (
|
||||
<Card className="bg-slate-800 border-slate-700">
|
||||
<CardHeader className="border-b border-slate-700">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-slate-100 flex items-center gap-2">
|
||||
<Sparkles className="h-5 w-5 text-purple-400" />
|
||||
诊断结果
|
||||
</CardTitle>
|
||||
<Badge variant="outline" className="border-emerald-500/30 text-emerald-400 bg-emerald-500/10">
|
||||
<CheckCircle className="h-3 w-3 mr-1" />
|
||||
诊断完成
|
||||
</Badge>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="p-6">
|
||||
<div className="space-y-6">
|
||||
{/* Problem Summary */}
|
||||
<div className="p-4 bg-slate-900 rounded-lg border border-slate-700">
|
||||
<h3 className="text-lg font-medium text-slate-100 mb-2">{diagnosisResult.problem}</h3>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{diagnosisResult.symptoms.map((symptom, idx) => (
|
||||
<Badge key={idx} variant="outline" className="border-amber-500/30 text-amber-400">
|
||||
<AlertTriangle className="h-3 w-3 mr-1" />
|
||||
{symptom}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Root Causes */}
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-slate-300 mb-3 flex items-center gap-2">
|
||||
<AlertCircle className="h-4 w-4 text-red-400" />
|
||||
可能的根因
|
||||
</h4>
|
||||
<div className="space-y-3">
|
||||
{diagnosisResult.rootCauses.map((cause, idx) => (
|
||||
<div key={idx} className="p-4 bg-slate-900 rounded-lg border border-slate-700">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-slate-200">{cause.description}</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-slate-400">概率</span>
|
||||
<div className="w-20 h-2 bg-slate-700 rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full bg-gradient-to-r from-purple-500 to-pink-500"
|
||||
style={{ width: `${cause.probability}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span className="text-purple-400 font-medium">{cause.probability}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
{cause.evidence.map((ev, eidx) => (
|
||||
<div key={eidx} className="flex items-center gap-2 text-sm text-slate-500">
|
||||
<ChevronRight className="h-3 w-3" />
|
||||
{ev}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Solutions */}
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-slate-300 mb-3 flex items-center gap-2">
|
||||
<Lightbulb className="h-4 w-4 text-emerald-400" />
|
||||
解决方案
|
||||
</h4>
|
||||
<Accordion type="single" collapsible className="space-y-2">
|
||||
{diagnosisResult.solutions.map((solution, idx) => (
|
||||
<AccordionItem
|
||||
key={idx}
|
||||
value={`solution-${idx}`}
|
||||
className="border border-slate-700 rounded-lg bg-slate-900"
|
||||
>
|
||||
<AccordionTrigger className="px-4 py-3 hover:no-underline">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="font-medium text-slate-200">{solution.title}</span>
|
||||
<Badge variant="outline" className={getDifficultyColor(solution.difficulty)}>
|
||||
{solution.difficulty === 'easy' ? '简单' : solution.difficulty === 'medium' ? '中等' : '困难'}
|
||||
</Badge>
|
||||
<span className="text-sm text-slate-500 flex items-center gap-1">
|
||||
<Clock className="h-3 w-3" />
|
||||
{solution.estimatedTime}
|
||||
</span>
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent className="px-4 pb-4">
|
||||
<div className="space-y-2">
|
||||
{solution.steps.map((step, sidx) => (
|
||||
<div key={sidx} className="flex items-start gap-2">
|
||||
<span className="flex-shrink-0 w-5 h-5 rounded-full bg-emerald-500/20 text-emerald-400 flex items-center justify-center text-xs">
|
||||
{sidx + 1}
|
||||
</span>
|
||||
<span className="text-sm text-slate-300">{step}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
</div>
|
||||
|
||||
{/* Related Logs */}
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-slate-300 mb-3 flex items-center gap-2">
|
||||
<FileText className="h-4 w-4 text-blue-400" />
|
||||
相关日志
|
||||
</h4>
|
||||
<div className="space-y-2">
|
||||
{diagnosisResult.relatedLogs.map((log, idx) => (
|
||||
<div key={idx} className="p-3 bg-slate-950 rounded-lg border border-slate-700 font-mono text-sm">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className="text-slate-500">{log.timestamp}</span>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={
|
||||
log.level === 'ERROR'
|
||||
? 'border-red-500/30 text-red-400'
|
||||
: 'border-amber-500/30 text-amber-400'
|
||||
}
|
||||
>
|
||||
{log.level}
|
||||
</Badge>
|
||||
<span className="text-slate-500">[{log.source}]</span>
|
||||
</div>
|
||||
<span className="text-slate-300">{log.message}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Similar Cases */}
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-slate-300 mb-3 flex items-center gap-2">
|
||||
<History className="h-4 w-4 text-purple-400" />
|
||||
相似历史案例
|
||||
</h4>
|
||||
<div className="space-y-2">
|
||||
{diagnosisResult.similarCases.map((caseItem, idx) => (
|
||||
<div key={idx} className="p-3 bg-slate-900 rounded-lg border border-slate-700">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-slate-200">{caseItem.title}</span>
|
||||
<span className="text-sm text-slate-500">{caseItem.date}</span>
|
||||
</div>
|
||||
<p className="text-sm text-slate-400 mt-1">解决方案: {caseItem.resolution}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="history">
|
||||
<Card className="bg-slate-800 border-slate-700">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-slate-100">诊断历史</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{diagnosisHistory.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-16 text-slate-500">
|
||||
<History className="h-10 w-10 mb-3 opacity-30" />
|
||||
<p>暂无诊断记录</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{diagnosisHistory.map((item) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className="p-4 rounded-lg border border-slate-700 bg-slate-800/50 hover:border-slate-600 cursor-pointer"
|
||||
onClick={() => setDiagnosisResult(item)}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-lg bg-purple-500/20 flex items-center justify-center">
|
||||
<Brain className="h-5 w-5 text-purple-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-medium text-slate-100">{item.problem}</h4>
|
||||
<p className="text-sm text-slate-400">
|
||||
{item.symptoms.length} 个症状 · {item.rootCauses.length} 个根因 · {item.solutions.length} 个方案
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button variant="ghost" size="sm" className="text-slate-400">
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { AIAlertAnalyzer } from './AIAlertAnalyzer';
|
||||
import { AIScriptGenerator } from './AIScriptGenerator';
|
||||
import { AIFaultDiagnosis } from './AIFaultDiagnosis';
|
||||
import { AIPredictiveAnalytics } from './AIPredictiveAnalytics';
|
||||
import { AIOpsAssistant } from './AIOpsAssistant';
|
||||
import {
|
||||
Brain,
|
||||
Bot,
|
||||
Sparkles,
|
||||
AlertTriangle,
|
||||
Code,
|
||||
Search,
|
||||
TrendingUp,
|
||||
MessageSquare,
|
||||
Settings,
|
||||
Zap,
|
||||
Cpu,
|
||||
Activity,
|
||||
CheckCircle,
|
||||
Clock,
|
||||
BarChart3,
|
||||
ArrowRight,
|
||||
} from 'lucide-react';
|
||||
|
||||
interface AIFeature {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
icon: React.ElementType;
|
||||
component: React.ComponentType;
|
||||
badge?: string;
|
||||
badgeColor?: string;
|
||||
}
|
||||
|
||||
const aiFeatures: AIFeature[] = [
|
||||
{
|
||||
id: 'assistant',
|
||||
name: 'AI 智能助手',
|
||||
description: '自然语言交互,解答运维问题,提供智能建议',
|
||||
icon: Bot,
|
||||
component: AIOpsAssistant,
|
||||
badge: '核心',
|
||||
badgeColor: 'bg-purple-500/10 text-purple-400 border-purple-500/30',
|
||||
},
|
||||
{
|
||||
id: 'alert-analyzer',
|
||||
name: '告警智能分析',
|
||||
description: 'AI分析告警根因,提供自动修复建议',
|
||||
icon: AlertTriangle,
|
||||
component: AIAlertAnalyzer,
|
||||
badge: '热门',
|
||||
badgeColor: 'bg-red-500/10 text-red-400 border-red-500/30',
|
||||
},
|
||||
{
|
||||
id: 'script-generator',
|
||||
name: '脚本生成器',
|
||||
description: '自然语言生成运维脚本,支持多种语言',
|
||||
icon: Code,
|
||||
component: AIScriptGenerator,
|
||||
badge: '新功能',
|
||||
badgeColor: 'bg-emerald-500/10 text-emerald-400 border-emerald-500/30',
|
||||
},
|
||||
{
|
||||
id: 'fault-diagnosis',
|
||||
name: '故障诊断',
|
||||
description: '智能分析故障症状,定位根因并提供方案',
|
||||
icon: Search,
|
||||
component: AIFaultDiagnosis,
|
||||
},
|
||||
{
|
||||
id: 'predictive',
|
||||
name: '预测分析',
|
||||
description: '基于历史数据预测趋势,容量规划预警',
|
||||
icon: TrendingUp,
|
||||
component: AIPredictiveAnalytics,
|
||||
badge: 'AI驱动',
|
||||
badgeColor: 'bg-blue-500/10 text-blue-400 border-blue-500/30',
|
||||
},
|
||||
];
|
||||
|
||||
export function AIOpsHub() {
|
||||
const [activeFeature, setActiveFeature] = useState<string>('assistant');
|
||||
|
||||
const ActiveComponent = aiFeatures.find(f => f.id === activeFeature)?.component || AIOpsAssistant;
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-purple-600 via-pink-500 to-orange-400 flex items-center justify-center shadow-lg shadow-purple-500/20">
|
||||
<Brain className="h-6 w-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-slate-100">AIOps 智能运维中心</h1>
|
||||
<p className="text-sm text-slate-500 flex items-center gap-2">
|
||||
<Sparkles className="h-3 w-3 text-purple-400" />
|
||||
AI驱动的智能运维平台,让运维更高效、更智能
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-2 px-3 py-1.5 rounded-lg bg-emerald-500/10 border border-emerald-500/30">
|
||||
<div className="w-2 h-2 rounded-full bg-emerald-400 animate-pulse" />
|
||||
<span className="text-sm text-emerald-400">AI 引擎运行中</span>
|
||||
</div>
|
||||
<Button variant="outline" className="border-slate-700 text-slate-300">
|
||||
<Settings className="h-4 w-4 mr-2" />
|
||||
模型配置
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* AI Status Overview */}
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
<Card className="bg-slate-800 border-slate-700">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-lg bg-purple-500/20 flex items-center justify-center">
|
||||
<Activity className="h-5 w-5 text-purple-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-slate-400">今日AI处理</p>
|
||||
<p className="text-xl font-bold text-slate-100">1,247</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="bg-slate-800 border-slate-700">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-lg bg-emerald-500/20 flex items-center justify-center">
|
||||
<CheckCircle className="h-5 w-5 text-emerald-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-slate-400">自动修复成功</p>
|
||||
<p className="text-xl font-bold text-slate-100">89</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="bg-slate-800 border-slate-700">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-lg bg-amber-500/20 flex items-center justify-center">
|
||||
<Clock className="h-5 w-5 text-amber-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-slate-400">平均响应时间</p>
|
||||
<p className="text-xl font-bold text-slate-100">2.3s</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="bg-slate-800 border-slate-700">
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-lg bg-blue-500/20 flex items-center justify-center">
|
||||
<BarChart3 className="h-5 w-5 text-blue-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-slate-400">预测准确率</p>
|
||||
<p className="text-xl font-bold text-slate-100">87.3%</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Feature Navigation */}
|
||||
<div className="grid grid-cols-5 gap-3">
|
||||
{aiFeatures.map((feature) => (
|
||||
<button
|
||||
key={feature.id}
|
||||
onClick={() => setActiveFeature(feature.id)}
|
||||
className={`p-4 rounded-xl border text-left transition-all ${
|
||||
activeFeature === feature.id
|
||||
? 'border-purple-500 bg-purple-500/10'
|
||||
: 'border-slate-700 bg-slate-800/50 hover:border-slate-600 hover:bg-slate-800'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start justify-between mb-2">
|
||||
<div
|
||||
className={`w-10 h-10 rounded-lg flex items-center justify-center ${
|
||||
activeFeature === feature.id
|
||||
? 'bg-purple-500'
|
||||
: 'bg-slate-700'
|
||||
}`}
|
||||
>
|
||||
<feature.icon
|
||||
className={`h-5 w-5 ${
|
||||
activeFeature === feature.id ? 'text-white' : 'text-slate-400'
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
{feature.badge && (
|
||||
<Badge variant="outline" className={`text-xs ${feature.badgeColor}`}>
|
||||
{feature.badge}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<h3
|
||||
className={`font-medium mb-1 ${
|
||||
activeFeature === feature.id ? 'text-purple-300' : 'text-slate-200'
|
||||
}`}
|
||||
>
|
||||
{feature.name}
|
||||
</h3>
|
||||
<p className="text-xs text-slate-500 line-clamp-2">{feature.description}</p>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Active Feature Component */}
|
||||
<div className="min-h-[600px]">
|
||||
<ActiveComponent />
|
||||
</div>
|
||||
|
||||
{/* AI Integration Info */}
|
||||
<Card className="bg-gradient-to-r from-purple-900/30 to-blue-900/30 border-purple-500/30">
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 rounded-xl bg-purple-500/20 flex items-center justify-center">
|
||||
<Zap className="h-6 w-6 text-purple-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-slate-100">AI大模型与运维深度集成</h3>
|
||||
<p className="text-sm text-slate-400 mt-1">
|
||||
EZOps平台集成了多种AI能力,包括智能告警分析、自动脚本生成、故障诊断预测等,
|
||||
支持OpenAI、Anthropic、Azure OpenAI、Ollama等多种大模型提供商。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button className="bg-purple-600 hover:bg-purple-700">
|
||||
了解更多
|
||||
<ArrowRight className="h-4 w-4 ml-2" />
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,400 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { toast } from 'sonner';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import {
|
||||
Bot,
|
||||
Code,
|
||||
Terminal,
|
||||
Play,
|
||||
Copy,
|
||||
Check,
|
||||
Sparkles,
|
||||
History,
|
||||
Star,
|
||||
Download,
|
||||
Save,
|
||||
FileCode,
|
||||
Shell,
|
||||
Settings,
|
||||
Cpu,
|
||||
Database,
|
||||
Server,
|
||||
Cloud,
|
||||
Loader2,
|
||||
Wand2,
|
||||
MessageSquare,
|
||||
} from 'lucide-react';
|
||||
|
||||
interface GeneratedScript {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
language: string;
|
||||
code: string;
|
||||
createdAt: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
interface ScriptTemplate {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
icon: React.ElementType;
|
||||
prompt: string;
|
||||
}
|
||||
|
||||
const scriptTemplates: ScriptTemplate[] = [
|
||||
{
|
||||
id: 'server-check',
|
||||
name: '服务器健康检查',
|
||||
description: '生成系统资源监控脚本',
|
||||
icon: Server,
|
||||
prompt: '生成一个Linux服务器健康检查脚本,包括CPU、内存、磁盘、网络状态检查,并输出报告',
|
||||
},
|
||||
{
|
||||
id: 'log-analysis',
|
||||
name: '日志分析脚本',
|
||||
description: '分析应用日志,提取错误信息',
|
||||
icon: FileCode,
|
||||
prompt: '生成一个日志分析脚本,可以分析应用日志文件,提取错误、警告信息,并生成统计报告',
|
||||
},
|
||||
{
|
||||
id: 'backup-script',
|
||||
name: '数据备份脚本',
|
||||
description: '自动化数据库备份',
|
||||
icon: Database,
|
||||
prompt: '生成一个MySQL数据库备份脚本,支持全量和增量备份,自动压缩和清理旧备份',
|
||||
},
|
||||
{
|
||||
id: 'deploy-script',
|
||||
name: '应用部署脚本',
|
||||
description: '自动化应用发布部署',
|
||||
icon: Cloud,
|
||||
prompt: '生成一个Docker应用部署脚本,包括镜像拉取、容器管理、健康检查',
|
||||
},
|
||||
{
|
||||
id: 'k8s-script',
|
||||
name: 'K8s运维脚本',
|
||||
description: 'Kubernetes集群管理',
|
||||
icon: Cpu,
|
||||
prompt: '生成一个Kubernetes运维脚本,包括Pod状态检查、资源清理、故障排查',
|
||||
},
|
||||
{
|
||||
id: 'security-check',
|
||||
name: '安全扫描脚本',
|
||||
description: '系统安全基线检查',
|
||||
icon: Settings,
|
||||
prompt: '生成一个Linux系统安全基线检查脚本,检查用户权限、开放端口、敏感文件权限',
|
||||
},
|
||||
];
|
||||
|
||||
export function AIScriptGenerator() {
|
||||
const [prompt, setPrompt] = useState('');
|
||||
const [isGenerating, setIsGenerating] = useState(false);
|
||||
const [generatedScript, setGeneratedScript] = useState<GeneratedScript | null>(null);
|
||||
const [selectedLanguage, setSelectedLanguage] = useState('bash');
|
||||
const [savedScripts, setSavedScripts] = useState<GeneratedScript[]>([]);
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState('generate');
|
||||
|
||||
const handleGenerate = async () => {
|
||||
if (!prompt.trim()) return;
|
||||
toast.info('AI 脚本生成功能需配置 LLM 服务');
|
||||
};
|
||||
|
||||
const handleTemplateClick = (template: ScriptTemplate) => {
|
||||
setPrompt(template.prompt);
|
||||
};
|
||||
|
||||
const copyScript = () => {
|
||||
if (generatedScript) {
|
||||
navigator.clipboard.writeText(generatedScript.code);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
}
|
||||
};
|
||||
|
||||
const saveScript = () => {
|
||||
if (generatedScript) {
|
||||
setSavedScripts([generatedScript, ...savedScripts]);
|
||||
setActiveTab('history');
|
||||
}
|
||||
};
|
||||
|
||||
const getLanguageIcon = (language: string) => {
|
||||
switch (language) {
|
||||
case 'bash':
|
||||
return <Terminal className="h-4 w-4" />;
|
||||
case 'python':
|
||||
return <Code className="h-4 w-4" />;
|
||||
case 'yaml':
|
||||
return <FileCode className="h-4 w-4" />;
|
||||
default:
|
||||
return <Shell className="h-4 w-4" />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-lg bg-gradient-to-br from-blue-500 to-cyan-500 flex items-center justify-center">
|
||||
<Wand2 className="h-5 w-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-slate-100">AI 脚本生成器</h1>
|
||||
<p className="text-sm text-slate-500">自然语言描述,AI自动生成运维脚本</p>
|
||||
</div>
|
||||
</div>
|
||||
<Badge variant="outline" className="border-purple-500/30 text-purple-400 bg-purple-500/10">
|
||||
<Sparkles className="h-3 w-3 mr-1" />
|
||||
AI 驱动
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-4">
|
||||
<TabsList className="bg-slate-800 border border-slate-700">
|
||||
<TabsTrigger value="generate" className="data-[state=active]:bg-slate-700">
|
||||
<Sparkles className="h-4 w-4 mr-2" />
|
||||
生成脚本
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="history" className="data-[state=active]:bg-slate-700">
|
||||
<History className="h-4 w-4 mr-2" />
|
||||
历史记录
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="templates" className="data-[state=active]:bg-slate-700">
|
||||
<Star className="h-4 w-4 mr-2" />
|
||||
模板库
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="generate" className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
{/* Input Area */}
|
||||
<Card className="bg-slate-800 border-slate-700">
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="text-slate-100 flex items-center gap-2">
|
||||
<MessageSquare className="h-5 w-5 text-blue-400" />
|
||||
描述你的需求
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div>
|
||||
<Label className="text-slate-300 mb-2 block">脚本语言</Label>
|
||||
<Select value={selectedLanguage} onValueChange={setSelectedLanguage}>
|
||||
<SelectTrigger className="bg-slate-900 border-slate-700 text-slate-100">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="bg-slate-800 border-slate-700">
|
||||
<SelectItem value="bash">
|
||||
<div className="flex items-center gap-2">
|
||||
<Terminal className="h-4 w-4" />
|
||||
Bash Shell
|
||||
</div>
|
||||
</SelectItem>
|
||||
<SelectItem value="python">
|
||||
<div className="flex items-center gap-2">
|
||||
<Code className="h-4 w-4" />
|
||||
Python
|
||||
</div>
|
||||
</SelectItem>
|
||||
<SelectItem value="ansible">
|
||||
<div className="flex items-center gap-2">
|
||||
<Cpu className="h-4 w-4" />
|
||||
Ansible Playbook
|
||||
</div>
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label className="text-slate-300 mb-2 block">需求描述</Label>
|
||||
<Textarea
|
||||
value={prompt}
|
||||
onChange={(e) => setPrompt(e.target.value)}
|
||||
placeholder="例如:生成一个检查服务器磁盘空间的脚本,当使用率超过90%时发送告警邮件..."
|
||||
className="bg-slate-900 border-slate-700 text-slate-100 min-h-[200px]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
className="w-full bg-blue-600 hover:bg-blue-700"
|
||||
onClick={handleGenerate}
|
||||
disabled={isGenerating || !prompt.trim()}
|
||||
>
|
||||
{isGenerating ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
||||
AI生成中...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Sparkles className="h-4 w-4 mr-2" />
|
||||
生成脚本
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Output Area */}
|
||||
<Card className="bg-slate-800 border-slate-700 flex flex-col">
|
||||
<CardHeader className="pb-3 border-b border-slate-700">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-slate-100 flex items-center gap-2">
|
||||
<Terminal className="h-5 w-5 text-emerald-400" />
|
||||
生成的脚本
|
||||
</CardTitle>
|
||||
{generatedScript && (
|
||||
<div className="flex gap-2">
|
||||
<Button variant="ghost" size="sm" onClick={copyScript} className="text-slate-400">
|
||||
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" onClick={saveScript} className="text-slate-400">
|
||||
<Save className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" className="text-slate-400">
|
||||
<Download className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="flex-1 p-0">
|
||||
<ScrollArea className="h-[400px]">
|
||||
{generatedScript ? (
|
||||
<div className="p-4">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
{getLanguageIcon(generatedScript.language)}
|
||||
<span className="text-sm text-slate-400">{generatedScript.name}</span>
|
||||
</div>
|
||||
<pre className="text-sm text-slate-300 whitespace-pre-wrap font-mono">
|
||||
{generatedScript.code}
|
||||
</pre>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center h-full text-slate-500">
|
||||
<Bot className="h-16 w-16 mb-4 opacity-30" />
|
||||
<p>在左侧描述需求,AI将为你生成脚本</p>
|
||||
</div>
|
||||
)}
|
||||
</ScrollArea>
|
||||
</CardContent>
|
||||
{generatedScript && (
|
||||
<div className="p-4 border-t border-slate-700 flex gap-2">
|
||||
<Button className="flex-1 bg-emerald-600 hover:bg-emerald-700">
|
||||
<Play className="h-4 w-4 mr-2" />
|
||||
测试运行
|
||||
</Button>
|
||||
<Button variant="outline" className="flex-1 border-slate-700 text-slate-300">
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
保存到脚本库
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="history">
|
||||
<Card className="bg-slate-800 border-slate-700">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-slate-100">历史生成的脚本</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{savedScripts.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-16 text-slate-500">
|
||||
<History className="h-10 w-10 mb-3 opacity-30" />
|
||||
<p>暂无生成的脚本</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{savedScripts.map((script) => (
|
||||
<div
|
||||
key={script.id}
|
||||
className="p-4 rounded-lg border border-slate-700 bg-slate-800/50 hover:border-slate-600 cursor-pointer"
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-lg bg-slate-700 flex items-center justify-center">
|
||||
{getLanguageIcon(script.language)}
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-medium text-slate-100">{script.name}</h4>
|
||||
<p className="text-sm text-slate-400">{script.description}</p>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<Badge variant="outline" className="text-xs border-slate-600 text-slate-400">
|
||||
{script.language.toUpperCase()}
|
||||
</Badge>
|
||||
<span className="text-xs text-slate-500">{script.createdAt}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
{script.tags.map((tag) => (
|
||||
<Badge key={tag} variant="outline" className="text-xs border-slate-600 text-slate-400">
|
||||
{tag}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="templates">
|
||||
<Card className="bg-slate-800 border-slate-700">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-slate-100">脚本模板库</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
{scriptTemplates.map((template) => (
|
||||
<div
|
||||
key={template.id}
|
||||
className="p-4 rounded-lg border border-slate-700 bg-slate-800/50 hover:border-blue-500/50 hover:bg-slate-700/50 cursor-pointer transition-all"
|
||||
onClick={() => handleTemplateClick(template)}
|
||||
>
|
||||
<div className="w-10 h-10 rounded-lg bg-blue-500/20 flex items-center justify-center mb-3">
|
||||
<template.icon className="h-5 w-5 text-blue-400" />
|
||||
</div>
|
||||
<h4 className="font-medium text-slate-100 mb-1">{template.name}</h4>
|
||||
<p className="text-sm text-slate-400">{template.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -94,7 +94,6 @@ const menuItems: MenuItem[] = [
|
||||
{ id: 'agent-alerts', name: 'Agent告警', icon: AlertTriangle, path: '/agent-alerts' },
|
||||
],
|
||||
},
|
||||
{ id: 'approval', name: '资源申请', icon: CheckSquare, path: '/approval' },
|
||||
{
|
||||
id: 'automation',
|
||||
name: '自动化运维',
|
||||
@@ -115,6 +114,7 @@ const menuItems: MenuItem[] = [
|
||||
{ id: 'ai-predictive', name: '容量预测', icon: BarChart3, path: '/ai-predictive' },
|
||||
],
|
||||
},
|
||||
{ id: 'approval', name: '资源申请', icon: CheckSquare, path: '/approval' },
|
||||
{
|
||||
id: 'system',
|
||||
name: '系统管理',
|
||||
|
||||
Reference in new issue
Block a user