'use client'; import React, { useEffect, useState } from 'react'; import { Clock, Plus, Trash2, Play, RefreshCw, Loader2, AlertCircle, X, } from 'lucide-react'; import { listCronJobs, addCronJob, removeCronJob, toggleCronJob, runCronJob, } from '@/lib/api'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Switch } from '@/components/ui/switch'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import type { CronJob } from '@/types'; export default function CronPage() { const [jobs, setJobs] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [showAdd, setShowAdd] = useState(false); const loadJobs = async () => { setLoading(true); setError(null); try { const data = await listCronJobs(true); setJobs(data); } catch (err: any) { setError(err.message || '加载任务失败'); } finally { setLoading(false); } }; useEffect(() => { loadJobs(); }, []); const handleToggle = async (jobId: string, enabled: boolean) => { try { await toggleCronJob(jobId, enabled); loadJobs(); } catch { // ignore } }; const handleDelete = async (jobId: string) => { try { await removeCronJob(jobId); loadJobs(); } catch { // ignore } }; const handleRun = async (jobId: string) => { try { await runCronJob(jobId); loadJobs(); } catch { // ignore } }; const handleAdd = async (params: { name: string; message: string; every_seconds?: number; cron_expr?: string; }) => { try { await addCronJob(params); setShowAdd(false); loadJobs(); } catch (err: any) { setError(err.message); } }; const formatTime = (ms: number | null) => { if (!ms) return '-'; return new Date(ms).toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', }); }; if (loading) { return (
); } return (

定时任务

{error && (
{error}
)} {/* Add Job Form */} {showAdd && ( setShowAdd(false)} /> )} {/* Jobs Table */} {jobs.length === 0 ? (

暂无定时任务

新建一个任务,让智能体按计划自动执行。

) : ( 启用 名称 计划 消息 上次运行 下次运行 状态 操作 {jobs.map((job) => ( handleToggle(job.id, checked) } />
{job.name} {job.id}
{job.schedule_display} {job.message} {formatTime(job.last_run_at_ms)} {formatTime(job.next_run_at_ms)} {job.last_status === 'ok' && ( OK )} {job.last_status === 'error' && ( 错误 )} {!job.last_status && ( - )}
))}
)}
); } function AddJobForm({ onAdd, onCancel, }: { onAdd: (params: { name: string; message: string; every_seconds?: number; cron_expr?: string; }) => void; onCancel: () => void; }) { const [name, setName] = useState(''); const [message, setMessage] = useState(''); const [scheduleType, setScheduleType] = useState<'every' | 'cron'>('every'); const [everySeconds, setEverySeconds] = useState('3600'); const [cronExpr, setCronExpr] = useState('0 9 * * *'); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!name.trim() || !message.trim()) return; const params: any = { name: name.trim(), message: message.trim() }; if (scheduleType === 'every') { params.every_seconds = parseInt(everySeconds, 10) || 3600; } else { params.cron_expr = cronExpr.trim(); } onAdd(params); }; return (
新建定时任务
setName(e.target.value)} placeholder="例如:日报汇总" />
{scheduleType === 'every' ? (
setEverySeconds(e.target.value)} min="10" placeholder="3600" />

{parseInt(everySeconds, 10) >= 3600 ? `约 ${Math.floor(parseInt(everySeconds, 10) / 3600)} 小时 ${Math.floor((parseInt(everySeconds, 10) % 3600) / 60)} 分` : parseInt(everySeconds, 10) >= 60 ? `约 ${Math.floor(parseInt(everySeconds, 10) / 60)} 分 ${parseInt(everySeconds, 10) % 60} 秒` : ''}

) : (
setCronExpr(e.target.value)} placeholder="0 9 * * *" />

格式:分钟 小时 日 月 周

)}
setMessage(e.target.value)} placeholder="例如:检查我的邮件并生成摘要" />
); }