import { useState } from 'react' import { Link, useNavigate, useParams } from '../../shared/router.jsx' import { useDataProvider, useList, useOne } from '../../shared/hooks/dataHooks.js' import { Alert } from '../../components/ui/alert' import { Button } from '../../components/ui/button' import { Card, CardContent } from '../../components/ui/card' import { Input } from '../../components/ui/input' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../components/ui/select' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../../components/ui/table' import { DetailList, Field, PageHeader, PageState, StatusPill } from '../../shared/ui/ui.jsx' import { useTitle } from '../../shared/hooks/hooks.js' const stateTone = { queued: 'neutral', executing: 'info', succeeded: 'success', failed: 'danger', needs_confirmation: 'warning', policy_hold: 'warning', cancelled: 'neutral' } const stateText = { queued: '已排队', executing: '执行中', succeeded: '已成功', failed: '失败', needs_confirmation: '需要人工确认', policy_hold: '策略暂停', cancelled: '已取消' } const holdText = { account_paused: '账号已暂停', account_revoked: '账号授权已撤销', binding_missing: '运行环境绑定缺失', environment_missing: '原运行环境不可用', exit_missing: '固定出口缺失', exit_unhealthy: '固定出口失败或发生漂移', runtime_stop_pending: '停止结果未知,等待人工核验', runtime_missing: '原运行实例不可用', runtime_lease_expired: '运行实例租约已过期', execution_lease_expired: '执行租约过期,结果未知', task_result_uncertain: '执行结果不确定', task_policy_hold: '执行器请求策略暂停', account_version_changed: '账号版本已变化', draft_version_changed: '草稿版本已变化', confirmation_missing: '确认快照缺失', confirmation_version_changed: '确认版本已变化', binding_version_changed: '环境或出口版本已变化', legacy_confirmation_required: '历史任务结果未知,等待人工核验', } const stateLabel = state => stateText[state] || `未知状态:${state}` const holdLabel = reason => reason ? holdText[reason] || `未知停止原因:${reason}` : '无' function actionErrorText(error) { const reason = error?.body?.reason_code if (error?.status === 409) return `当前版本仍不允许恢复(409):${holdLabel(reason)}` if (error?.status === 503) return `原资源尚未恢复(503):${holdLabel(reason)}` return error?.message || '操作失败' } export function TaskList() { const [accountFilter, setAccountFilter] = useState('') const [stateFilter, setStateFilter] = useState('') const { result, query } = useList({ resource: 'tasks', filters: [ { field: 'account_id', value: accountFilter }, { field: 'state', value: stateFilter }, ], }) const error = query.error const isPending = query.isPending const tasks = result.data ?? [] useTitle('CreatorHub · 任务中心') return (
setAccountFilter(event.target.value)} placeholder="账号 ID" />
{error ? {error.message} : null} 任务 账号 确认 状态 停止原因 更新时间 操作 {tasks.map(task => ( {task.id} {task.account_id} {task.confirmation_id || '未确认'} {holdLabel(task.hold_reason)} {new Date(task.updated_at || task.created_at).toLocaleString('zh-CN')} ))}
) } export function TaskDetail() { const { id } = useParams() const navigate = useNavigate() const dataProvider = useDataProvider()('default') const [verification, setVerification] = useState('not_executed') const [busy, setBusy] = useState(false) const [notice, setNotice] = useState(null) const { result, error, isPending, query } = useOne({ resource: 'tasks', id, queryOptions: { retry: false } }) const task = result useTitle('CreatorHub · 任务详情') if (isPending) return if (error || !task) return {error?.message || '任务不存在'} async function runAction(action, data) { setBusy(true) setNotice(null) try { await dataProvider.taskAction(task.id, action, data) setNotice({ variant: 'success', text: action === 'verify' ? '人工核验已记录;现在只能按该结论恢复或结束。' : action === 'resume' ? '任务已恢复排队。' : '任务已按核验结论结束。' }) await query.refetch() } catch (reason) { setNotice({ variant: 'destructive', text: actionErrorText(reason) }) } finally { setBusy(false) } } const confirmation = task.confirmation || {} const unknownHold = task.hold_reason && !holdText[task.hold_reason] return (
{task.id}}>