import { useState, useEffect, useCallback } from 'react'; import { Clock, XCircle, CheckCircle, AlertTriangle, Database, Zap, Loader2, Calendar, Ban, } from 'lucide-react'; import api from '@/api/request'; interface ScheduledTicket { id: number; ticket_no: string; title: string; db_type: string; instance_name: string; instance_db_type: string; target_database: string; sql_content: string; status: string; scheduled_at: string | null; priority: string; create_time: string | null; } const DB_TYPE_ICON: Record = { mysql: '🐬', postgres: '🐘', redis: '🔴', mongo: '🍃', }; const DB_TYPE_LABEL: Record = { mysql: 'MySQL', postgres: 'PostgreSQL', redis: 'Redis', mongo: 'MongoDB', }; export default function AutoTaskPage() { const [tasks, setTasks] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const [cancelling, setCancelling] = useState(null); const fetchTasks = useCallback(async () => { setLoading(true); setError(''); try { const res = await api.get('/tickets/scheduled/list') as { items: ScheduledTicket[]; total: number }; setTasks(res.items || []); } catch { setError('加载定时任务失败'); } finally { setLoading(false); } }, []); useEffect(() => { fetchTasks(); }, [fetchTasks]); // 自动刷新(每 30 秒) useEffect(() => { const timer = setInterval(fetchTasks, 30000); return () => clearInterval(timer); }, [fetchTasks]); const handleCancel = useCallback(async (id: number) => { setCancelling(id); try { await api.post(`/tickets/${id}/cancel-schedule`); setTasks(prev => prev.filter(t => t.id !== id)); } catch { setError('取消定时任务失败'); } finally { setCancelling(null); } }, []); const formatTime = (iso: string | null) => { if (!iso) return '-'; return new Date(iso).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', }); }; const getCountdown = (iso: string | null) => { if (!iso) return ''; const diff = new Date(iso).getTime() - Date.now(); if (diff <= 0) return '即将执行'; const h = Math.floor(diff / 3600000); const m = Math.floor((diff % 3600000) / 60000); const s = Math.floor((diff % 60000) / 1000); if (h > 0) return `${h}h ${m}m 后执行`; if (m > 0) return `${m}m ${s}s 后执行`; return `${s}s 后执行`; }; return (
{/* Header */}

定时执行任务

审批通过并设置定时执行的工单,到时间后自动执行 SQL
{/* Error */} {error && (
{error}
)} {/* Empty */} {!loading && tasks.length === 0 && (

暂无定时执行中的工单

在「新建数据变更工单」中勾选「定时执行」可创建定时任务

)} {/* Task Cards */}
{tasks.map(task => { const countdown = getCountdown(task.scheduled_at); return (
{/* Row 1: Title + Status + Countdown */}
{DB_TYPE_ICON[task.db_type] || '💾'}
{task.title} 定时执行中
{task.ticket_no} · {task.instance_name} · {DB_TYPE_LABEL[task.db_type] || task.db_type} · {task.target_database}
{/* Countdown */}
{countdown}
{formatTime(task.scheduled_at)}
{/* Row 2: SQL Preview */}
                  {task.sql_content}
                
{/* Row 3: Actions */}
创建于 {formatTime(task.create_time)}
); })}
); }