diff --git a/frontend/src/components/automation/CronJobManager.tsx b/frontend/src/components/automation/CronJobManager.tsx index e316f4e..5ee5cc4 100644 --- a/frontend/src/components/automation/CronJobManager.tsx +++ b/frontend/src/components/automation/CronJobManager.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useCallback, Fragment } from 'react'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -41,21 +41,25 @@ import { XCircle, Loader2, Terminal, + RefreshCw, + PackageOpen, } from 'lucide-react'; import { toast } from 'sonner'; import { cronJobApi, taskExecutionApi } from '@/services/api'; +import { useConfirm } from '@/components/ui/confirm-dialog'; export function CronJobManager() { const [searchQuery, setSearchQuery] = useState(''); const [jobs, setJobs] = useState([]); - const [loading, setLoading] = useState(true); + const [initialLoading, setInitialLoading] = useState(true); + const [refreshing, setRefreshing] = useState(false); const [isFormDialogOpen, setIsFormDialogOpen] = useState(false); const [isLogDialogOpen, setIsLogDialogOpen] = useState(false); const [executions, setExecutions] = useState([]); const [executionsLoading, setExecutionsLoading] = useState(false); const [expandedExec, setExpandedExec] = useState(null); const [executingId, setExecutingId] = useState(null); - // editingId === null 表示新建模式;非 null 表示编辑已有任务 + // editingId === null 表示新建模式;非 null 表示编辑已有任务 // Logan.Li const [editingId, setEditingId] = useState(null); const [formJob, setFormJob] = useState({ name: '', @@ -65,6 +69,8 @@ export function CronJobManager() { content: '', }); + const _confirm = useConfirm(); + const openCreate = () => { setEditingId(null); setFormJob({ name: '', description: '', cron_expr: '', type: 'command', content: '' }); @@ -83,21 +89,24 @@ export function CronJobManager() { setIsFormDialogOpen(true); }; - const loadData = async () => { + // 加载任务列表(双 loading state:首次全屏 spinner,手动刷新只 spin icon) // Logan.Li + const refreshData = useCallback(async () => { + setRefreshing(true); try { - setLoading(true); const data = await cronJobApi.getJobs(); setJobs(data.list || []); } catch (error) { console.error('Failed to load cron jobs:', error); + toast.error('加载失败'); } finally { - setLoading(false); + setRefreshing(false); + setInitialLoading(false); } - }; + }, []); useEffect(() => { - loadData(); - }, []); + refreshData(); + }, [refreshData]); const loadExecutions = async () => { try { @@ -147,7 +156,7 @@ export function CronJobManager() { ); default: return ( - + 从未执行 ); @@ -186,7 +195,7 @@ export function CronJobManager() { toast.success('任务创建成功'); } setIsFormDialogOpen(false); - loadData(); + refreshData(); } catch (error) { console.error('Failed to save cron job:', error); toast.error(editingId !== null ? '更新失败' : '创建失败'); @@ -196,7 +205,8 @@ export function CronJobManager() { const handleToggleJob = async (id: any) => { try { await cronJobApi.toggleJob(id); - loadData(); + toast.success('状态已切换'); + refreshData(); } catch (error) { console.error('Failed to toggle job:', error); toast.error('操作失败'); @@ -213,7 +223,7 @@ export function CronJobManager() { } else { toast.error('执行失败', { description: (result?.output || '').slice(0, 100) }); } - loadData(); + refreshData(); } catch (error) { console.error('Failed to execute job:', error); toast.error('执行失败'); @@ -222,11 +232,18 @@ export function CronJobManager() { } }; - const handleDeleteJob = async (id: any) => { + // 删除任务(带确认框) // Logan.Li + const handleDeleteJob = async (job: any) => { + const ok = await _confirm({ + title: '删除定时任务', + description: `确定删除「${job.name}」吗?此操作不可恢复。`, + variant: 'destructive', + }); + if (!ok) return; try { - await cronJobApi.deleteJob(id); - toast.success('已删除'); - loadData(); + await cronJobApi.deleteJob(job.id); + toast.success('任务已删除'); + refreshData(); } catch (error) { console.error('Failed to delete job:', error); toast.error('删除失败'); @@ -238,14 +255,27 @@ export function CronJobManager() { {/* Header */}
-

定时任务

-

Cron 任务管理与执行

+

定时任务

+

Cron 任务管理与执行

- + @@ -401,66 +438,66 @@ export function CronJobManager() { {/* Create / Edit Job Dialog (共用) */} - + {editingId !== null ? '编辑定时任务' : '新建定时任务'} - 配置 Cron 定时任务 + 配置 Cron 定时任务
- + setFormJob({ ...formJob, name: e.target.value })} placeholder="例如: 数据库备份" - className="bg-slate-800 border-slate-700 text-slate-100 mt-1" + className="bg-[var(--dg-bg-secondary)] border-[var(--dg-border)] text-[var(--dg-text-primary)] mt-1" />
- +