Tasks.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import { useEffect, useState, useCallback } from 'react'
  2. import { Table, Tag, Button, message, Badge } from 'antd'
  3. import { StopOutlined } from '@ant-design/icons'
  4. import { getTasks, stopTask, type Task } from '../api'
  5. import { useAppStore } from '../store'
  6. import TaskControl from '../components/TaskControl'
  7. const taskTypeColor: Record<string, string> = {
  8. full: 'purple',
  9. discover: 'blue',
  10. search: 'cyan',
  11. github: 'geekblue',
  12. scrape: 'orange',
  13. crawl: 'green',
  14. clean: 'lime',
  15. score: 'gold',
  16. }
  17. const taskStatusBadge: Record<string, 'processing' | 'success' | 'error' | 'warning' | 'default'> = {
  18. running: 'processing',
  19. completed: 'success',
  20. failed: 'error',
  21. stopped: 'warning',
  22. pending: 'default',
  23. }
  24. function formatDateTime(dateStr: string | null) {
  25. if (!dateStr) return '-'
  26. const d = new Date(dateStr)
  27. return d.toLocaleString('zh-CN')
  28. }
  29. export default function Tasks() {
  30. const [tasks, setTasks] = useState<Task[]>([])
  31. const [total, setTotal] = useState(0)
  32. const [page, setPage] = useState(1)
  33. const [loading, setLoading] = useState(false)
  34. const [stoppingId, setStoppingId] = useState<number | null>(null)
  35. const { runningTask, setRunningTask } = useAppStore()
  36. const fetchTasks = useCallback(async (currentPage = page) => {
  37. setLoading(true)
  38. try {
  39. const res = await getTasks({ page: currentPage, page_size: 20 })
  40. setTasks(res.data.items)
  41. setTotal(res.data.total)
  42. } catch {
  43. message.error('获取任务列表失败')
  44. } finally {
  45. setLoading(false)
  46. }
  47. }, [page])
  48. useEffect(() => {
  49. fetchTasks(page)
  50. }, [page, fetchTasks])
  51. useEffect(() => {
  52. if (!runningTask) return
  53. const timer = setInterval(() => fetchTasks(page), 3000)
  54. return () => clearInterval(timer)
  55. }, [runningTask, fetchTasks, page])
  56. const handleStop = async (id: number) => {
  57. setStoppingId(id)
  58. try {
  59. await stopTask(id)
  60. setRunningTask(null)
  61. message.success('任务已停止')
  62. fetchTasks(page)
  63. } catch {
  64. message.error('停止任务失败')
  65. } finally {
  66. setStoppingId(null)
  67. }
  68. }
  69. const columns = [
  70. { title: 'ID', dataIndex: 'id', key: 'id', width: 70 },
  71. {
  72. title: '任务类型',
  73. dataIndex: 'task_type',
  74. key: 'task_type',
  75. render: (type: string) => <Tag color={taskTypeColor[type] ?? 'default'}>{type}</Tag>,
  76. },
  77. {
  78. title: '状态',
  79. dataIndex: 'status',
  80. key: 'status',
  81. render: (status: string) => (
  82. <Badge status={taskStatusBadge[status] ?? 'default'} text={status} />
  83. ),
  84. },
  85. {
  86. title: '参数预览',
  87. dataIndex: 'params',
  88. key: 'params',
  89. render: (params: Record<string, unknown>) => (
  90. <span style={{ fontSize: 12, color: '#666' }}>
  91. {JSON.stringify(params).slice(0, 60)}
  92. {JSON.stringify(params).length > 60 ? '...' : ''}
  93. </span>
  94. ),
  95. ellipsis: true,
  96. },
  97. {
  98. title: '创建时间',
  99. dataIndex: 'created_at',
  100. key: 'created_at',
  101. render: (t: string) => formatDateTime(t),
  102. },
  103. {
  104. title: '完成时间',
  105. dataIndex: 'completed_at',
  106. key: 'completed_at',
  107. render: (t: string | null) => formatDateTime(t),
  108. },
  109. {
  110. title: '操作',
  111. key: 'action',
  112. render: (_: unknown, record: Task) =>
  113. record.status === 'running' ? (
  114. <Button
  115. danger
  116. size="small"
  117. icon={<StopOutlined />}
  118. loading={stoppingId === record.id}
  119. onClick={() => handleStop(record.id)}
  120. >
  121. 停止
  122. </Button>
  123. ) : null,
  124. },
  125. ]
  126. return (
  127. <div>
  128. <TaskControl onTaskStarted={() => fetchTasks(1)} />
  129. <Table
  130. dataSource={tasks}
  131. columns={columns}
  132. rowKey="id"
  133. loading={loading}
  134. pagination={{
  135. current: page,
  136. pageSize: 20,
  137. total,
  138. onChange: (p) => setPage(p),
  139. showTotal: (t) => `共 ${t} 条`,
  140. }}
  141. />
  142. </div>
  143. )
  144. }