first commit

This commit is contained in:
rayd1o
2026-03-05 11:46:58 +08:00
commit e7033775d8
20657 changed files with 1988940 additions and 0 deletions

View File

@@ -0,0 +1,153 @@
import { useEffect, useState } from 'react'
import { Table, Tag, Card, Row, Col, Statistic, Button } from 'antd'
import { ReloadOutlined, CheckCircleOutlined, CloseCircleOutlined, SyncOutlined } from '@ant-design/icons'
import { useAuthStore } from '../../stores/auth'
interface Task {
id: number
collector: string
status: 'success' | 'failed' | 'running' | 'pending'
records_processed: number
started_at: string
completed_at: string
duration_seconds: number
}
function Tasks() {
const { token } = useAuthStore()
const [tasks, setTasks] = useState<Task[]>([])
const [loading, setLoading] = useState(false)
const [stats, setStats] = useState({
total_today: 0,
success: 0,
failed: 0,
running: 0,
})
const fetchTasks = async () => {
setLoading(true)
try {
const res = await fetch('/api/v1/tasks', {
headers: { Authorization: `Bearer ${token}` },
})
const data = await res.json()
setTasks(data.data || [])
setStats(data.stats || stats)
} catch (error) {
console.error('Failed to fetch tasks:', error)
} finally {
setLoading(false)
}
}
useEffect(() => {
fetchTasks()
}, [token])
const columns = [
{ title: 'ID', dataIndex: 'id', key: 'id', width: 80 },
{
title: '收集器',
dataIndex: 'collector',
key: 'collector',
render: (c: string) => <Tag color="blue">{c}</Tag>,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: (status: string) => {
const colors: Record<string, string> = {
success: 'success',
failed: 'error',
running: 'processing',
pending: 'default',
}
const icons: Record<string, JSX.Element> = {
success: <CheckCircleOutlined />,
failed: <CloseCircleOutlined />,
running: <SyncOutlined spin />,
pending: <SyncOutlined />,
}
return (
<Tag color={colors[status] || 'default'} icon={icons[status]}>
{status === 'success' ? '成功' : status === 'failed' ? '失败' : status === 'running' ? '运行中' : '等待中'}
</Tag>
)
},
},
{
title: '处理记录',
dataIndex: 'records_processed',
key: 'records_processed',
render: (n: number) => n.toLocaleString(),
},
{
title: '耗时',
dataIndex: 'duration_seconds',
key: 'duration_seconds',
render: (s: number) => s ? `${s.toFixed(2)}s` : '-',
},
{
title: '开始时间',
dataIndex: 'started_at',
key: 'started_at',
render: (t: string) => t ? new Date(t).toLocaleString('zh-CN') : '-',
},
]
const statusCounts = tasks.reduce(
(acc, task) => {
acc[task.status]++
return acc
},
{ success: 0, failed: 0, running: 0, pending: 0 } as Record<string, number>
)
const successRate = tasks.length > 0 ? (statusCounts.success / tasks.length) * 100 : 0
return (
<div>
<Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
<Col span={6}>
<Card>
<Statistic title="今日任务" value={tasks.length} prefix={<ReloadOutlined />} />
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="成功率"
value={successRate.toFixed(1)}
suffix="%"
valueStyle={{ color: successRate >= 90 ? '#52c41a' : '#faad14' }}
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic title="成功" value={statusCounts.success} valueStyle={{ color: '#52c41a' }} />
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic title="失败" value={statusCounts.failed} valueStyle={{ color: '#ff4d4f' }} />
</Card>
</Col>
</Row>
<Card
title="任务历史"
extra={
<Button type="primary" icon={<ReloadOutlined />} onClick={fetchTasks}>
</Button>
}
>
<Table columns={columns} dataSource={tasks} rowKey="id" loading={loading} pagination={{ pageSize: 10 }} />
</Card>
</div>
)
}
export default Tasks