diff --git a/frontend/src/components/automation/AnsibleExecutionsTable.tsx b/frontend/src/components/automation/AnsibleExecutionsTable.tsx new file mode 100644 index 0000000..045f2c2 --- /dev/null +++ b/frontend/src/components/automation/AnsibleExecutionsTable.tsx @@ -0,0 +1,73 @@ +// Ansible Playbook 执行记录表格(6 列:#/Playbook/状态/开始时间/耗时/操作) // Logan.Li +import React from 'react'; +import { RotateCcw, Terminal } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent } from '@/components/ui/card'; +import { getStatusBadge, formatDate, type ExecutionRecord } from './ansible-helpers'; + +interface AnsibleExecutionsTableProps { + executions: ExecutionRecord[]; + onRefresh: () => void; + onViewLogs: (exec: ExecutionRecord) => void; +} + +const AnsibleExecutionsTable: React.FC = ({ + executions, onRefresh, onViewLogs, +}) => { + return ( +
+
+ +
+ + +
+ + + + + + + + + + + + + {executions.length === 0 ? ( + + + + ) : ( + executions.map((job) => ( + + + + + + + + + )) + )} + +
#Playbook状态开始时间耗时操作
+ 暂无执行记录 +
{job.id}{job.task_name}{getStatusBadge(job.status)}{formatDate(job.started_at)} + {job.duration ? `${(job.duration / 1000).toFixed(1)}s` : '-'} + + +
+
+
+
+
+ ); +}; + +export default AnsibleExecutionsTable; diff --git a/frontend/src/components/automation/AnsibleManager.tsx b/frontend/src/components/automation/AnsibleManager.tsx index 6e29de5..90c646d 100644 --- a/frontend/src/components/automation/AnsibleManager.tsx +++ b/frontend/src/components/automation/AnsibleManager.tsx @@ -1,12 +1,9 @@ import React, { useState, useEffect } from 'react'; import { - FileCode, CheckCircle, XCircle, RotateCcw, Eye, - Plus, Search, Trash2, Edit, Copy, Terminal, Loader2, Play, + RotateCcw, Plus, Search, Trash2, Loader2, } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; -import { Badge } from '@/components/ui/badge'; -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Checkbox } from '@/components/ui/checkbox'; @@ -14,12 +11,15 @@ import { toast } from 'sonner'; import { useConfirm } from '@/components/ui/confirm-dialog'; import { ansibleApi, taskExecutionApi } from '@/services/api'; import { - mapPlaybook, categories, samplePlaybook, getStatusBadge, formatDate, + mapPlaybook, categories, samplePlaybook, type Playbook, type ExecutionRecord, } from './ansible-helpers'; import AnsibleFormDialog, { type PlaybookFormData } from './AnsibleFormDialog'; import AnsibleDetailDialog from './AnsibleDetailDialog'; import AnsibleLogsDialog from './AnsibleLogsDialog'; +import AnsibleStatsCards from './AnsibleStatsCards'; +import AnsiblePlaybookCard from './AnsiblePlaybookCard'; +import AnsibleExecutionsTable from './AnsibleExecutionsTable'; const AnsibleManager: React.FC = () => { const _confirm = useConfirm(); @@ -250,60 +250,12 @@ const AnsibleManager: React.FC = () => { return (
{/* 统计卡片 */} -
- - -
-
-

Playbook 总数

-

{playbooks.length}

-
-
- -
-
-
-
- - -
-
-

执行中

-

{runningCount}

-
-
- -
-
-
-
- - -
-
-

今日成功

-

{todaySuccess}

-
-
- -
-
-
-
- - -
-
-

今日失败

-

{todayFailed}

-
-
- -
-
-
-
-
+ { setActiveTab(v); if (v === 'jobs') fetchExecutions(); }}> @@ -389,60 +341,18 @@ const AnsibleManager: React.FC = () => {
暂无 Playbook
) : ( filteredPlaybooks.map((playbook) => ( - - -
-
- toggleSelect(playbook.id)} - className="shrink-0" - /> - - {playbook.name} -
- - {categories.find(c => c.value === playbook.category)?.label || playbook.category} - -
- {playbook.description || '暂无描述'} -
- - {playbook.tags.length > 0 && ( -
- {playbook.tags.map((tag, idx) => ( - {tag} - ))} -
- )} -
- 执行 {playbook.run_count} 次 - 超时 {playbook.timeout}s -
-
- - - - - -
-
-
+ { setSelectedPlaybook(pb); setDetailOpen(true); }} + onExecute={handleExecute} + onEdit={openEdit} + onClone={handleClone} + onDelete={handleDelete} + /> )) )}
@@ -450,57 +360,11 @@ const AnsibleManager: React.FC = () => { {/* ============ 执行记录 ============ */} -
- -
- - -
- - - - - - - - - - - - - {executions.length === 0 ? ( - - - - ) : ( - executions.map((job) => ( - - - - - - - - - )) - )} - -
#Playbook状态开始时间耗时操作
- 暂无执行记录 -
{job.id}{job.task_name}{getStatusBadge(job.status)}{formatDate(job.started_at)} - {job.duration ? `${(job.duration / 1000).toFixed(1)}s` : '-'} - - -
-
-
-
+ { setSelectedExec(exec); setLogsOpen(true); }} + />
diff --git a/frontend/src/components/automation/AnsiblePlaybookCard.tsx b/frontend/src/components/automation/AnsiblePlaybookCard.tsx new file mode 100644 index 0000000..18d57c4 --- /dev/null +++ b/frontend/src/components/automation/AnsiblePlaybookCard.tsx @@ -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 = ({ + playbook, selected, executing, + onToggleSelect, onView, onExecute, onEdit, onClone, onDelete, +}) => { + return ( + + +
+
+ onToggleSelect(playbook.id)} + className="shrink-0" + /> + + {playbook.name} +
+ + {categories.find(c => c.value === playbook.category)?.label || playbook.category} + +
+ {playbook.description || '暂无描述'} +
+ + {playbook.tags.length > 0 && ( +
+ {playbook.tags.map((tag, idx) => ( + {tag} + ))} +
+ )} +
+ 执行 {playbook.run_count} 次 + 超时 {playbook.timeout}s +
+
+ + + + + +
+
+
+ ); +}; + +export default AnsiblePlaybookCard; diff --git a/frontend/src/components/automation/AnsibleStatsCards.tsx b/frontend/src/components/automation/AnsibleStatsCards.tsx new file mode 100644 index 0000000..be9a1dd --- /dev/null +++ b/frontend/src/components/automation/AnsibleStatsCards.tsx @@ -0,0 +1,74 @@ +// Ansible Playbook 统计卡片(4 张:总数/执行中/今日成功/今日失败) // Logan.Li +import React from 'react'; +import { FileCode, CheckCircle, XCircle, Loader2 } from 'lucide-react'; +import { Card, CardContent } from '@/components/ui/card'; + +interface AnsibleStatsCardsProps { + total: number; + running: number; + todaySuccess: number; + todayFailed: number; +} + +const AnsibleStatsCards: React.FC = ({ + total, running, todaySuccess, todayFailed, +}) => { + return ( +
+ + +
+
+

Playbook 总数

+

{total}

+
+
+ +
+
+
+
+ + +
+
+

执行中

+

{running}

+
+
+ +
+
+
+
+ + +
+
+

今日成功

+

{todaySuccess}

+
+
+ +
+
+
+
+ + +
+
+

今日失败

+

{todayFailed}

+
+
+ +
+
+
+
+
+ ); +}; + +export default AnsibleStatsCards;