cleanup: 移除3个废弃AI组件(AIOpsHub/AIFaultDiagnosis/AIScriptGenerator), 清理1087行死代码

This commit is contained in:
attacker committed 2026-06-15 02:40:13 +08:00
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>
);
}
-250
View File
@@ -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>
);
}
+1 -1
View File
@@ -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: '系统管理',