- {/* Page Header */}
-
-
-
-
-
-
-
-
- {/* Stats Cards */}
-
-
-
-
-
-
定时任务
-
{cronJobs.length}
-
-
-
-
-
-
-
-
-
-
-
-
运行中
-
- {cronJobs.filter((j) => j.enabled).length}
-
-
-
-
-
-
-
-
-
-
-
脚本数量
-
{scripts.length}
-
-
-
-
-
-
-
-
-
-
-
-
累计执行
-
- {cronJobs.reduce((sum, j) => sum + (j.success_count || 0) + (j.fail_count || 0), 0)}
-
-
-
-
-
-
-
-
-
-
- {/* Filters */}
-
-
-
- setSearchQuery(e.target.value)}
- className="pl-9 h-9 bg-slate-800/50 border-slate-700 text-slate-200 placeholder:text-slate-500"
- />
-
-
-
- {loading ? (
-
-
-
- ) : null}
-
- {/* Tabs */}
-
-
-
-
- 定时任务
-
-
-
- 脚本库
-
-
-
- 任务编排
-
-
-
-
-
-
-
-
-
-
- 任务名称
- 状态
- Cron表达式
- 上次执行
- 下次执行
- 执行统计
- 操作
-
-
-
- {filteredJobs.map((job) => (
-
-
-
-
-
- {job.name}
-
-
{job.description}
-
-
- {getJobStatusBadge(job.enabled)}
-
-
- {job.cron_expr}
-
-
-
-
- {job.last_run_at ? new Date(job.last_run_at).toLocaleString() : '--'}
-
-
-
- --
-
-
-
- {job.success_count || 0}
- /
- {job.fail_count || 0}
-
-
-
-
-
-
-
-
-
- ))}
-
-
-
-
-
-
-
-
-
- {filteredScripts.map((script) => (
-
-
-
-
-
-
-
-
-
{script.name}
-
{script.description}
-
-
-
- {getLanguageBadge(script.type)}
-
-
-
-
-
-
{(script.content || '').slice(0, 200)}...
-
-
-
- {parseParams(script.params).map((param: any) => (
-
- {param.name}
- {param.required && *}
-
- ))}
-
-
-
-
-
-
-
-
-
- ))}
-
-
-
-
-
-
-
-
-
任务编排
-
可视化编排复杂运维任务流程
-
-
-
-
-
-
-
- {/* Script Detail Dialog */}
-
-
- {/* Execute Script Dialog */}
-
-
- );
-}
diff --git a/frontend/src/components/automation/ScriptManager.tsx b/frontend/src/components/automation/ScriptManager.tsx
new file mode 100644
index 0000000..0099f92
--- /dev/null
+++ b/frontend/src/components/automation/ScriptManager.tsx
@@ -0,0 +1,658 @@
+import React, { useState, useEffect } from 'react';
+import {
+ Play, FileCode, Clock, CheckCircle, XCircle, RotateCcw, Eye,
+ Plus, Search, Trash2, Edit, Copy, Terminal, 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 {
+ Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter,
+} from '@/components/ui/dialog';
+import {
+ Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
+} from '@/components/ui/select';
+import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
+import { Textarea } from '@/components/ui/textarea';
+import { Label } from '@/components/ui/label';
+import { ScrollArea } from '@/components/ui/scroll-area';
+import { toast } from 'sonner';
+import { useConfirm } from '@/components/ui/confirm-dialog';
+import { scriptApi, taskExecutionApi } from '@/services/api';
+
+// ---- 类型定义 ----
+
+interface Script {
+ id: number;
+ name: string;
+ code: string;
+ description: string;
+ type: string; // shell | python | powershell | yaml
+ content: string;
+ timeout: number;
+ version: number;
+ run_count: number;
+ created_at: string;
+ updated_at: string;
+}
+
+interface ExecutionRecord {
+ id: number;
+ task_type: string;
+ task_id: number;
+ task_name: string;
+ status: string;
+ output: string;
+ duration: number;
+ started_at: string | null;
+ finished_at: string | null;
+ created_at: string;
+}
+
+// 将后端返回的脚本原始对象映射为前端结构
+function mapScript(raw: any): Script {
+ return {
+ id: raw.id,
+ name: raw.name,
+ code: raw.code || '',
+ description: raw.description || '',
+ type: raw.type || 'shell',
+ content: raw.content || '',
+ timeout: raw.timeout || 300,
+ version: raw.version || 1,
+ run_count: raw.run_count || 0,
+ created_at: raw.created_at,
+ updated_at: raw.updated_at,
+ };
+}
+
+const scriptTypes = [
+ { value: 'shell', label: 'Shell', sample: '#!/bin/bash\n# 系统巡检脚本\necho "===== 系统信息 ====="\nuname -a\necho ""\necho "===== 磁盘使用 ====="\ndf -h\necho ""\necho "===== 内存使用 ====="\nfree -h\n' },
+ { value: 'python', label: 'Python', sample: '#!/usr/bin/env python3\n# 主机信息采集\nimport platform, socket\nprint("主机名:", socket.gethostname())\nprint("系统:", platform.platform())\nprint("Python:", platform.python_version())\n' },
+ { value: 'powershell', label: 'PowerShell', sample: '# Windows 系统检查\nWrite-Host "===== 系统信息 ====="\nGet-ComputerInfo | Select-Object CsName, WindowsVersion\nWrite-Host "===== 磁盘使用 ====="\nGet-PSDrive -PSProvider FileSystem | Format-Table\n' },
+ { value: 'yaml', label: 'YAML', sample: '# YAML 配置/任务定义\nname: example-task\nsteps:\n - name: step1\n action: echo\n message: hello\n' },
+];
+
+const typeColors: Record