增加生成后端服务代码。
This commit is contained in:
parent
ffbad526ac
commit
d5c4bb0c1f
@ -19,15 +19,18 @@ import { getDashboard, getScheduleJobs } from '../service';
|
||||
import type { DashboardResponse, ScheduleJobResponse, LogStatus } from '../types';
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import duration from 'dayjs/plugin/duration';
|
||||
import 'dayjs/locale/zh-cn';
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
dayjs.extend(duration);
|
||||
dayjs.locale('zh-cn');
|
||||
|
||||
const Dashboard: React.FC = () => {
|
||||
const [data, setData] = useState<DashboardResponse | null>(null);
|
||||
const [allJobs, setAllJobs] = useState<ScheduleJobResponse[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [currentTime, setCurrentTime] = useState(dayjs());
|
||||
|
||||
// 加载仪表盘数据
|
||||
const loadData = async () => {
|
||||
@ -52,6 +55,14 @@ const Dashboard: React.FC = () => {
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
// 实时更新当前时间(用于倒计时)
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
setCurrentTime(dayjs());
|
||||
}, 1000);
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
// 获取日志状态徽章
|
||||
const getLogStatusBadge = (status: LogStatus) => {
|
||||
const statusMap: Record<LogStatus, {
|
||||
@ -94,6 +105,22 @@ const Dashboard: React.FC = () => {
|
||||
.slice(0, 10);
|
||||
}, [allJobs]);
|
||||
|
||||
// 格式化倒计时
|
||||
const formatCountdown = (nextTime: string) => {
|
||||
const diff = dayjs(nextTime).diff(currentTime);
|
||||
if (diff <= 0) return '即将执行';
|
||||
const dur = dayjs.duration(diff);
|
||||
const days = dur.days();
|
||||
const hours = dur.hours();
|
||||
const minutes = dur.minutes();
|
||||
const seconds = dur.seconds();
|
||||
|
||||
if (days > 0) return `${days}天${hours}小时后`;
|
||||
if (hours > 0) return `${hours}小时${minutes}分后`;
|
||||
if (minutes > 0) return `${minutes}分${seconds}秒后`;
|
||||
return `${seconds}秒后`;
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-96">
|
||||
@ -103,9 +130,9 @@ const Dashboard: React.FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-col h-full space-y-4">
|
||||
{/* 任务概览统计卡片 */}
|
||||
<div className="grid gap-4 md:grid-cols-5">
|
||||
<div className="grid gap-4 md:grid-cols-5 flex-shrink-0">
|
||||
<Card className="border-l-4 border-l-blue-500">
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium">任务总数</CardTitle>
|
||||
@ -166,45 +193,9 @@ const Dashboard: React.FC = () => {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 正在执行的任务 */}
|
||||
{data && data.runningJobs.length > 0 && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Loader2 className="h-5 w-5 animate-spin text-orange-500" />
|
||||
正在执行的任务 ({data.runningJobs.length})
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{data.runningJobs.map((job) => (
|
||||
<div key={job.jobId} className="p-4 border rounded-lg space-y-3">
|
||||
<div className="flex items-start justify-between">
|
||||
<div>
|
||||
<h4 className="font-semibold text-lg">{job.jobName}</h4>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
开始时间: {dayjs(job.startTime).format('YYYY-MM-DD HH:mm:ss')}
|
||||
</p>
|
||||
</div>
|
||||
<Badge variant="outline" className="text-orange-600 border-orange-600">
|
||||
{job.progress}%
|
||||
</Badge>
|
||||
</div>
|
||||
<Progress value={job.progress} className="h-3" />
|
||||
{job.message && (
|
||||
<p className="text-sm text-muted-foreground flex items-center gap-2">
|
||||
<Activity className="h-4 w-4" />
|
||||
{job.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* 多视图 Tabs */}
|
||||
<Tabs defaultValue="timeline" className="space-y-4">
|
||||
<TabsList className="grid w-full grid-cols-3">
|
||||
<Tabs defaultValue="timeline" className="flex-1 flex flex-col min-h-0">
|
||||
<TabsList className="grid w-full grid-cols-3 flex-shrink-0">
|
||||
<TabsTrigger value="timeline">
|
||||
<Calendar className="h-4 w-4 mr-2" />
|
||||
时间线视图
|
||||
@ -220,22 +211,23 @@ const Dashboard: React.FC = () => {
|
||||
</TabsList>
|
||||
|
||||
{/* 时间线视图 */}
|
||||
<TabsContent value="timeline">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<TabsContent value="timeline" className="flex-1 min-h-0 mt-4">
|
||||
<Card className="h-full flex flex-col">
|
||||
<CardHeader className="flex-shrink-0">
|
||||
<CardTitle>任务执行时间线</CardTitle>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
即将执行的任务按时间顺序排列
|
||||
</p>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<CardContent className="flex-1 min-h-0 overflow-hidden">
|
||||
<ScrollArea className="h-full">
|
||||
{upcomingJobs.length === 0 ? (
|
||||
<div className="text-center py-8 text-muted-foreground">
|
||||
<Clock className="h-12 w-12 mx-auto mb-2 opacity-20" />
|
||||
<p>暂无即将执行的任务</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="relative pl-8">
|
||||
<div className="relative pl-8 pr-4">
|
||||
{/* 时间线 */}
|
||||
<div className="absolute left-3 top-0 bottom-0 w-0.5 bg-border" />
|
||||
|
||||
@ -276,13 +268,14 @@ const Dashboard: React.FC = () => {
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</ScrollArea>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
{/* 状态分组视图 */}
|
||||
<TabsContent value="status">
|
||||
<div className="grid gap-4 md:grid-cols-4 h-[calc(100vh-280px)]">
|
||||
<TabsContent value="status" className="flex-1 min-h-0 mt-4">
|
||||
<div className="grid gap-4 md:grid-cols-4 h-full">
|
||||
{/* 运行中 */}
|
||||
<Card className="border-l-4 border-l-orange-500 flex flex-col">
|
||||
<CardHeader className="pb-3 flex-shrink-0">
|
||||
@ -387,7 +380,7 @@ const Dashboard: React.FC = () => {
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground flex items-center gap-1">
|
||||
<Clock className="h-3 w-3" />
|
||||
下次: {job.nextExecuteTime ? dayjs(job.nextExecuteTime).format('MM-DD HH:mm') : '-'}
|
||||
{job.nextExecuteTime ? formatCountdown(job.nextExecuteTime) : '-'}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
@ -432,19 +425,20 @@ const Dashboard: React.FC = () => {
|
||||
</TabsContent>
|
||||
|
||||
{/* 最近日志 */}
|
||||
<TabsContent value="logs">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<TabsContent value="logs" className="flex-1 min-h-0 mt-4">
|
||||
<Card className="h-full flex flex-col">
|
||||
<CardHeader className="flex-shrink-0">
|
||||
<CardTitle>最近执行日志 (10条)</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<CardContent className="flex-1 min-h-0 overflow-hidden">
|
||||
<ScrollArea className="h-full">
|
||||
{!data || data.recentLogs.length === 0 ? (
|
||||
<div className="text-center py-8 text-muted-foreground">
|
||||
<AlertCircle className="h-12 w-12 mx-auto mb-2 opacity-20" />
|
||||
<p>暂无执行日志</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-3 pr-4">
|
||||
{data.recentLogs.map((log) => (
|
||||
<div key={log.id} className="p-3 border rounded-lg hover:bg-accent transition-colors">
|
||||
<div className="flex items-start justify-between mb-2">
|
||||
@ -470,6 +464,7 @@ const Dashboard: React.FC = () => {
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</ScrollArea>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user