refactor: ScriptManager split into 5 files (730 to 546 lines)
- script-helpers.tsx (97 lines): Script/ExecutionRecord types, mapScript, scriptTypes, getTypeBadge, genCode, getStatusBadge, formatDate - ScriptFormDialog.tsx (113 lines): new/edit script dialog - ScriptDetailDialog.tsx (49 lines): view script content dialog - ScriptLogsDialog.tsx (39 lines): execution logs dialog - ScriptManager.tsx (546 lines): main component, imports sub-components Verified: tsc --noEmit zero errors, npm run build passes, browser tested all 3 dialogs + tabs + stats cards, console zero errors
This commit is contained in:
1 parent
23fe742cc2
commit
745dde358e
5 files changed
+331
-217
No files matched your search
@@ -0,0 +1,97 @@
|
||||
// ScriptManager 共享类型与工具函数 // Logan.Li
|
||||
import React from 'react';
|
||||
import { Clock, Loader2, CheckCircle, XCircle } from 'lucide-react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
|
||||
// ---- 类型定义 ----
|
||||
|
||||
export 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;
|
||||
}
|
||||
|
||||
export 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;
|
||||
}
|
||||
|
||||
// 将后端返回的脚本原始对象映射为前端结构
|
||||
export 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,
|
||||
};
|
||||
}
|
||||
|
||||
export 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<string, string> = {
|
||||
shell: 'border-green-500/30 text-green-500 bg-green-500/10',
|
||||
bash: 'border-green-500/30 text-green-500 bg-green-500/10',
|
||||
python: 'border-blue-500/30 text-blue-500 bg-blue-500/10',
|
||||
powershell: 'border-cyan-500/30 text-cyan-500 bg-cyan-500/10',
|
||||
yaml: 'border-purple-500/30 text-purple-500 bg-purple-500/10',
|
||||
};
|
||||
|
||||
export const getTypeBadge = (type: string) => (
|
||||
<Badge variant="outline" className={typeColors[type] || typeColors.shell}>
|
||||
{(type || 'shell').toUpperCase()}
|
||||
</Badge>
|
||||
);
|
||||
|
||||
// 生成唯一脚本编码
|
||||
export const genCode = () => `sc-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
||||
|
||||
// 执行状态 Badge
|
||||
export function getStatusBadge(status: string) {
|
||||
switch (status) {
|
||||
case 'pending':
|
||||
return <Badge variant="outline" className="gap-1"><Clock className="w-3 h-3" />等待中</Badge>;
|
||||
case 'running':
|
||||
return <Badge variant="default" className="bg-blue-500 gap-1"><Loader2 className="w-3 h-3 animate-spin" />执行中</Badge>;
|
||||
case 'success':
|
||||
return <Badge variant="default" className="bg-green-600 gap-1"><CheckCircle className="w-3 h-3" />成功</Badge>;
|
||||
case 'failed':
|
||||
return <Badge variant="destructive" className="gap-1"><XCircle className="w-3 h-3" />失败</Badge>;
|
||||
default:
|
||||
return <Badge variant="secondary">{status || '未知'}</Badge>;
|
||||
}
|
||||
}
|
||||
|
||||
// 日期格式化
|
||||
export const formatDate = (s: string | null) => {
|
||||
if (!s) return '-';
|
||||
const d = new Date(s);
|
||||
return Number.isNaN(d.getTime()) ? '-' : d.toLocaleString('zh-CN', { hour12: false });
|
||||
};
|
||||
Reference in new issue
Block a user