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: 'agent-alerts', name: 'Agent告警', icon: AlertTriangle, path: '/agent-alerts' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ id: 'approval', name: '资源申请', icon: CheckSquare, path: '/approval' },
|
|
||||||
{
|
{
|
||||||
id: 'automation',
|
id: 'automation',
|
||||||
name: '自动化运维',
|
name: '自动化运维',
|
||||||
@@ -115,6 +114,7 @@ const menuItems: MenuItem[] = [
|
|||||||
{ id: 'ai-predictive', name: '容量预测', icon: BarChart3, path: '/ai-predictive' },
|
{ id: 'ai-predictive', name: '容量预测', icon: BarChart3, path: '/ai-predictive' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{ id: 'approval', name: '资源申请', icon: CheckSquare, path: '/approval' },
|
||||||
{
|
{
|
||||||
id: 'system',
|
id: 'system',
|
||||||
name: '系统管理',
|
name: '系统管理',
|
||||||
|
|||||||
Reference in new issue
Block a user