refactor: 拆分AnsibleManager.tsx(535→399行)提取AnsibleStatsCards+AnsiblePlaybookCard+AnsibleExecutionsTable子组件
This commit is contained in:
1 parent
7432431fc3
commit
c6e4b519e8
4 files changed
+256
-164
No files matched your search
@@ -0,0 +1,81 @@
|
||||
// Ansible Playbook 卡片(复选框 + 名称 + 分类徽章 + 描述 + 标签 + 元信息 + 5 行操作按钮) // Logan.Li
|
||||
import React from 'react';
|
||||
import { FileCode, Eye, Edit, Copy, Trash2, Play, Loader2 } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||
import { categories, type Playbook } from './ansible-helpers';
|
||||
|
||||
interface AnsiblePlaybookCardProps {
|
||||
playbook: Playbook;
|
||||
selected: boolean;
|
||||
executing: boolean;
|
||||
onToggleSelect: (id: number) => void;
|
||||
onView: (pb: Playbook) => void;
|
||||
onExecute: (pb: Playbook) => void;
|
||||
onEdit: (pb: Playbook) => void;
|
||||
onClone: (pb: Playbook) => void;
|
||||
onDelete: (pb: Playbook) => void;
|
||||
}
|
||||
|
||||
const AnsiblePlaybookCard: React.FC<AnsiblePlaybookCardProps> = ({
|
||||
playbook, selected, executing,
|
||||
onToggleSelect, onView, onExecute, onEdit, onClone, onDelete,
|
||||
}) => {
|
||||
return (
|
||||
<Card className="hover:shadow-md transition-shadow">
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<Checkbox
|
||||
checked={selected}
|
||||
onCheckedChange={() => onToggleSelect(playbook.id)}
|
||||
className="shrink-0"
|
||||
/>
|
||||
<FileCode className="w-5 h-5 text-blue-500 shrink-0" />
|
||||
<CardTitle className="text-base truncate">{playbook.name}</CardTitle>
|
||||
</div>
|
||||
<Badge variant="outline" className="shrink-0">
|
||||
{categories.find(c => c.value === playbook.category)?.label || playbook.category}
|
||||
</Badge>
|
||||
</div>
|
||||
<CardDescription className="line-clamp-2">{playbook.description || '暂无描述'}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-0">
|
||||
{playbook.tags.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1 mb-3">
|
||||
{playbook.tags.map((tag, idx) => (
|
||||
<Badge key={idx} variant="secondary" className="text-xs">{tag}</Badge>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center justify-between text-xs text-muted-foreground mb-3">
|
||||
<span>执行 {playbook.run_count} 次</span>
|
||||
<span>超时 {playbook.timeout}s</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Button variant="ghost" size="sm" title="查看内容" onClick={() => onView(playbook)}>
|
||||
<Eye className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" title="执行" disabled={executing} onClick={() => onExecute(playbook)}>
|
||||
{executing
|
||||
? <Loader2 className="w-4 h-4 animate-spin text-blue-600" />
|
||||
: <Play className="w-4 h-4 text-green-600" />}
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" title="编辑" onClick={() => onEdit(playbook)}>
|
||||
<Edit className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" title="复制" onClick={() => onClone(playbook)}>
|
||||
<Copy className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm" title="删除" onClick={() => onDelete(playbook)}>
|
||||
<Trash2 className="w-4 h-4 text-red-600" />
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnsiblePlaybookCard;
|
||||
Reference in new issue
Block a user