import React, { useCallback, useEffect, useState } from 'react'; import { Button, Card, DatePicker, Input, Progress, Select, Space, Table, Tag, Tooltip, Typography, message } from 'antd'; import { EyeOutlined, ReloadOutlined, SearchOutlined } from '@ant-design/icons'; import { useNavigate } from 'react-router-dom'; import { getAdminShotTaskSets } from '../api'; import type { ShotTaskSetOut } from '../types'; import { formatDate } from '../utils/formatDate'; const PAGE_SIZE = 20; const TASK_STATUS_OPTIONS = [ { value: 'pending_analysis', label: '等待分析' }, { value: 'analyzing', label: '分析中' }, { value: 'analysis_completed', label: '分析完成' }, { value: 'analysis_failed', label: '分析失败' }, { value: 'splitting', label: '拆镜中' }, { value: 'split_completed', label: '拆镜完成' }, { value: 'partial_failed', label: '部分失败' }, { value: 'failed', label: '失败' }, ]; const ANALYSIS_STATUS_OPTIONS = [ { value: 'pending', label: '待分析' }, { value: 'processing', label: '分析中' }, { value: 'completed', label: '分析完成' }, { value: 'failed', label: '分析失败' }, ]; const SPLIT_STATUS_OPTIONS = [ { value: 'none', label: '未拆镜' }, { value: 'pending', label: '待拆镜' }, { value: 'processing', label: '拆镜中' }, { value: 'completed', label: '拆镜完成' }, { value: 'failed', label: '拆镜失败' }, { value: 'retry_waiting', label: '等待重试' }, ]; const STATUS_MAP: Record = { pending_analysis: { color: 'default', text: '等待分析' }, analyzing: { color: 'processing', text: '分析中' }, analysis_completed: { color: 'success', text: '分析完成' }, analysis_failed: { color: 'error', text: '分析失败' }, splitting: { color: 'warning', text: '拆镜中' }, split_completed: { color: 'success', text: '拆镜完成' }, partial_failed: { color: 'orange', text: '部分失败' }, failed: { color: 'error', text: '失败' }, none: { color: 'default', text: '未拆镜' }, pending: { color: 'default', text: '待处理' }, processing: { color: 'warning', text: '处理中' }, completed: { color: 'success', text: '完成' }, retry_waiting: { color: 'orange', text: '等待重试' }, }; const safeDate = (value?: string | null): string => (value ? formatDate(value) : '-'); const shortId = (value?: string | null): string => (!value ? '-' : value.length > 16 ? `${value.slice(0, 10)}...` : value); const StatusTag: React.FC<{ status?: string | null }> = ({ status }) => { if (!status) return -; const meta = STATUS_MAP[status] || { color: 'blue', text: status }; return {meta.text}; }; const AdminShotReplications: React.FC = () => { const navigate = useNavigate(); const [items, setItems] = useState([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(false); const [page, setPage] = useState(1); const [status, setStatus] = useState(''); const [analysisStatus, setAnalysisStatus] = useState(''); const [splitStatus, setSplitStatus] = useState(''); const [inputKeyword, setInputKeyword] = useState(''); const [inputUserId, setInputUserId] = useState(''); const [inputUserName, setInputUserName] = useState(''); const [createdRange, setCreatedRange] = useState(null); const [queryKeyword, setQueryKeyword] = useState(''); const [queryUserId, setQueryUserId] = useState(''); const [queryUserName, setQueryUserName] = useState(''); const [queryCreatedRange, setQueryCreatedRange] = useState(null); const [reloadKey, setReloadKey] = useState(0); const load = useCallback(async () => { setLoading(true); try { const res = await getAdminShotTaskSets({ status: status || undefined, analysisStatus: analysisStatus || undefined, splitStatus: splitStatus || undefined, keyword: queryKeyword || undefined, userId: queryUserId || undefined, userName: queryUserName || undefined, createdStart: queryCreatedRange?.[0]?.toISOString?.(), createdEnd: queryCreatedRange?.[1]?.toISOString?.(), page, pageSize: PAGE_SIZE, }); setItems(res.items || []); setTotal(res.total || 0); } catch (e: any) { message.error(e?.message || '加载拆镜复刻任务失败'); } finally { setLoading(false); } }, [analysisStatus, page, queryCreatedRange, queryKeyword, queryUserId, queryUserName, splitStatus, status]); useEffect(() => { load(); }, [load, reloadKey]); const doSearch = () => { setQueryKeyword(inputKeyword.trim()); setQueryUserId(inputUserId.trim()); setQueryUserName(inputUserName.trim()); setQueryCreatedRange(createdRange); setPage(1); }; const resetSearch = () => { setStatus(''); setAnalysisStatus(''); setSplitStatus(''); setInputKeyword(''); setInputUserId(''); setInputUserName(''); setCreatedRange(null); setQueryKeyword(''); setQueryUserId(''); setQueryUserName(''); setQueryCreatedRange(null); setPage(1); setReloadKey(v => v + 1); }; return (
拆镜复刻 只读排查页面:查看拆镜总任务、AI 分析结果、切片列表和关联复刻项目。
{ setAnalysisStatus(v || ''); setPage(1); }} options={ANALYSIS_STATUS_OPTIONS} /> } placeholder="关键词:标题/内容/分类/受众" style={{ width: 260 }} value={inputKeyword} onChange={e => setInputKeyword(e.target.value)} onPressEnter={doSearch} /> setInputUserId(e.target.value)} onPressEnter={doSearch} /> setInputUserName(e.target.value)} onPressEnter={doSearch} /> `共 ${value} 条`, onChange: setPage }} scroll={{ x: 1500 }} columns={[ { title: '任务集ID', dataIndex: 'id', width: 150, render: (v: string) => {shortId(v)} }, { title: '用户', width: 180, render: (_, record) => ( {record.userName || '-'} {shortId(record.userId)} ), }, { title: '任务信息', width: 300, render: (_, record) => ( {record.title || '-'} 分类:{record.originalVideoCategory || '-'} 受众:{record.originalVideoAudience || '-'} ), }, { title: '总状态', dataIndex: 'status', width: 120, render: (v: string) => }, { title: '分析状态', dataIndex: 'analysisStatus', width: 110, render: (v: string) => }, { title: '拆镜状态', dataIndex: 'splitStatus', width: 110, render: (v: string) => }, { title: '切片进度', width: 180, render: (_, record) => { const totalSegments = record.segmentCount || 0; const done = record.completedSegmentCount || 0; const percent = totalSegments ? Math.round((done / totalSegments) * 100) : 0; return `${done}/${totalSegments}`} />; }, }, { title: '失败片段', dataIndex: 'failedSegmentCount', width: 90, render: (v: number) => v ? {v} : 0 }, { title: '视频时长', dataIndex: 'videoDurationSeconds', width: 100, render: (v: number) => `${Number(v || 0).toFixed(1)}s` }, { title: '创建时间', dataIndex: 'createdAt', width: 170, render: safeDate }, { title: '更新时间', dataIndex: 'updatedAt', width: 170, render: safeDate }, { title: '操作', fixed: 'right', width: 120, render: (_, record) => , }, ]} /> ); }; export default AdminShotReplications;