// 内置系统任务面板 — 表格式紧凑布局 // Logan.Li import { useState, useEffect, useCallback } from 'react'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { ContentPanel } from '@/components/layout/ContentPanel'; import { PageEmpty } from '@/components/layout/PageEmpty'; import { systemTaskApi, type SystemTaskItem } from '@/api'; import { usePermission } from '@/hooks/usePermission'; import { useConfirm } from '@/components/ui/confirm-dialog'; import { toast } from 'sonner'; import { formatRelativeTime } from './cronJobHelpers'; import { ServerCog, Play, Loader2, Shield, Trash2, Clock, CheckCircle, XCircle, AlertCircle, } from 'lucide-react'; const categoryLabel: Record = { monitor: '监控', cleanup: '清理', }; function statusBadge(status?: string) { switch (status) { case 'success': return ( 成功 ); case 'failed': return ( 失败 ); case 'running': return ( 运行中 ); default: return ( 待执行 ); } } interface Props { refreshing?: boolean; } export function SystemTasksPanel({ refreshing }: Props) { const _confirm = useConfirm(); const { hasAnyPermission, isAdmin } = usePermission(); const canRun = isAdmin || hasAnyPermission(['auto:script', 'auto:cron', 'auto:ansible']); const [tasks, setTasks] = useState([]); const [loading, setLoading] = useState(true); const [runningCode, setRunningCode] = useState(null); const load = useCallback(async () => { try { const res = await systemTaskApi.list(); setTasks(res.list || []); } catch (e: unknown) { toast.error(e instanceof Error ? e.message : '加载系统任务失败'); } finally { setLoading(false); } }, []); useEffect(() => { load(); }, [load, refreshing]); const handleRun = async (task: SystemTaskItem) => { const ok = await _confirm({ title: '立即执行', description: `确定手动执行系统任务「${task.name}」吗?`, confirmText: '执行', }); if (!ok) return; setRunningCode(task.code); try { const res = await systemTaskApi.run(task.code); toast.success(res.last_message || '执行完成'); await load(); } catch (e: unknown) { toast.error(e instanceof Error ? e.message : '执行失败'); } finally { setRunningCode(null); } }; if (loading) { return (
); } if (tasks.length === 0) { return ; } const failedCount = tasks.filter((t) => t.last_status === 'failed').length; return (

进程内置调度,不可编辑;清理类任务支持手动触发

{tasks.length} 个任务 {failedCount > 0 && ( {failedCount} 个失败 )}
{/* 表头 */}
任务 调度 / 说明 最近执行 操作
{tasks.map((task) => (
{task.name} 内置 {categoryLabel[task.category] || task.category}
{statusBadge(task.last_status)}

{task.description}

{task.schedule} {task.category === 'cleanup' && task.code === 'audit_log_cleanup' && task.extra?.retention_days != null && ( 保留 {String(task.extra.retention_days)} 天 )} {task.code === 'alert_engine' && task.extra && ( {(task.extra as { is_leader?: boolean }).is_leader ? 'Leader' : 'Follower'} )}
{formatRelativeTime(task.last_run_at ?? undefined)}
{task.last_duration_ms != null && task.last_duration_ms > 0 && ( {task.last_duration_ms}ms )} {task.run_count ?? 0} 次
{task.last_message && (

{task.last_message}

)}
{task.runnable && canRun ? ( ) : ( 自动 )}
))}
); }