微信小程序页面迁移校验之前 P5任务处理之前
This commit is contained in:
@@ -138,6 +138,14 @@ const QueueTab: React.FC = () => {
|
||||
};
|
||||
|
||||
const columns: ColumnsType<QueuedTask> = [
|
||||
{
|
||||
title: '任务 ID', dataIndex: 'id', key: 'id', width: 120,
|
||||
render: (id: string) => (
|
||||
<Text copyable={{ text: id }} style={{ fontSize: 11 }}>
|
||||
{id.slice(0, 8)}…
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '任务', dataIndex: ['config', 'tasks'], key: 'tasks',
|
||||
render: (tasks: string[]) => (
|
||||
@@ -234,6 +242,26 @@ const HistoryTab: React.FC = () => {
|
||||
const [detail, setDetail] = useState<ExecutionLog | null>(null);
|
||||
const [historyLogLines, setHistoryLogLines] = useState<string[]>([]);
|
||||
const [logLoading, setLogLoading] = useState(false);
|
||||
const [wsConnected, setWsConnected] = useState(false);
|
||||
const historyWsRef = useRef<WebSocket | null>(null);
|
||||
|
||||
/** 关闭 WebSocket 连接并重置状态 */
|
||||
const closeHistoryWs = useCallback(() => {
|
||||
historyWsRef.current?.close();
|
||||
historyWsRef.current = null;
|
||||
setWsConnected(false);
|
||||
}, []);
|
||||
|
||||
/* 组件卸载时清理 WebSocket */
|
||||
useEffect(() => {
|
||||
return () => { historyWsRef.current?.close(); };
|
||||
}, []);
|
||||
|
||||
const handleCancelHistory = useCallback(async (id: string) => {
|
||||
try { await cancelExecution(id); message.success('已发送终止信号'); load(); }
|
||||
catch { message.error('终止失败'); }
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
setLoading(true);
|
||||
@@ -244,29 +272,73 @@ const HistoryTab: React.FC = () => {
|
||||
|
||||
useEffect(() => { load(); }, [load]);
|
||||
|
||||
/** 点击行时加载详情和日志 */
|
||||
/** 点击行时加载详情和日志;running 任务走 WebSocket 实时流 */
|
||||
const handleRowClick = useCallback(async (record: ExecutionLog) => {
|
||||
setDetail(record);
|
||||
setHistoryLogLines([]);
|
||||
setLogLoading(true);
|
||||
try {
|
||||
const { data: logData } = await apiClient.get<{
|
||||
execution_id: string;
|
||||
output_log: string | null;
|
||||
error_log: string | null;
|
||||
}>(`/execution/${record.id}/logs`);
|
||||
const parts: string[] = [];
|
||||
if (logData.output_log) parts.push(logData.output_log);
|
||||
if (logData.error_log) parts.push(logData.error_log);
|
||||
setHistoryLogLines(parts.join('\n').split('\n').filter(Boolean));
|
||||
} catch {
|
||||
/* 日志可能不存在,静默处理 */
|
||||
} finally {
|
||||
setLogLoading(false);
|
||||
closeHistoryWs();
|
||||
|
||||
if (record.status === 'running') {
|
||||
// running 任务:通过 WebSocket 实时推送日志
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const host = window.location.host;
|
||||
const ws = new WebSocket(`${protocol}//${host}/ws/logs/${record.id}`);
|
||||
historyWsRef.current = ws;
|
||||
|
||||
ws.onopen = () => { setWsConnected(true); setLogLoading(false); };
|
||||
ws.onmessage = (event) => {
|
||||
setHistoryLogLines((prev) => [...prev, event.data]);
|
||||
};
|
||||
ws.onclose = () => {
|
||||
setWsConnected(false);
|
||||
// 任务结束后刷新历史列表以更新状态
|
||||
load();
|
||||
};
|
||||
ws.onerror = () => {
|
||||
message.error('WebSocket 连接失败,回退到静态日志');
|
||||
setWsConnected(false);
|
||||
// 回退:用 REST API 拉取已有日志
|
||||
apiClient.get<{
|
||||
execution_id: string;
|
||||
output_log: string | null;
|
||||
error_log: string | null;
|
||||
}>(`/execution/${record.id}/logs`).then(({ data: logData }) => {
|
||||
const parts: string[] = [];
|
||||
if (logData.output_log) parts.push(logData.output_log);
|
||||
if (logData.error_log) parts.push(logData.error_log);
|
||||
setHistoryLogLines(parts.join('\n').split('\n').filter(Boolean));
|
||||
}).catch(() => {}).finally(() => setLogLoading(false));
|
||||
};
|
||||
} else {
|
||||
// 已完成任务:REST API 一次性拉取
|
||||
try {
|
||||
const { data: logData } = await apiClient.get<{
|
||||
execution_id: string;
|
||||
output_log: string | null;
|
||||
error_log: string | null;
|
||||
}>(`/execution/${record.id}/logs`);
|
||||
const parts: string[] = [];
|
||||
if (logData.output_log) parts.push(logData.output_log);
|
||||
if (logData.error_log) parts.push(logData.error_log);
|
||||
setHistoryLogLines(parts.join('\n').split('\n').filter(Boolean));
|
||||
} catch {
|
||||
/* 日志可能不存在,静默处理 */
|
||||
} finally {
|
||||
setLogLoading(false);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
}, [closeHistoryWs, load]);
|
||||
|
||||
const columns: ColumnsType<ExecutionLog> = [
|
||||
{
|
||||
title: '执行 ID', dataIndex: 'id', key: 'id', width: 120,
|
||||
render: (id: string) => (
|
||||
<Text copyable={{ text: id }} style={{ fontSize: 11 }}>
|
||||
{id.slice(0, 8)}…
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '任务', dataIndex: 'task_codes', key: 'task_codes',
|
||||
render: (codes: string[]) => (
|
||||
@@ -275,6 +347,12 @@ const HistoryTab: React.FC = () => {
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '调度 ID', dataIndex: 'schedule_id', key: 'schedule_id', width: 120,
|
||||
render: (id: string | null) => id
|
||||
? <Text copyable={{ text: id }} style={{ fontSize: 11 }}>{id.slice(0, 8)}…</Text>
|
||||
: '—',
|
||||
},
|
||||
{
|
||||
title: '状态', dataIndex: 'status', key: 'status', width: 90,
|
||||
render: (s: string) => <Tag color={STATUS_COLOR[s] ?? 'default'}>{s}</Tag>,
|
||||
@@ -287,6 +365,21 @@ const HistoryTab: React.FC = () => {
|
||||
<Tag color={v === 0 ? 'success' : 'error'}>{v}</Tag>
|
||||
) : '—',
|
||||
},
|
||||
{
|
||||
title: '操作', key: 'action', width: 80, align: 'center',
|
||||
render: (_: unknown, record: ExecutionLog) => {
|
||||
if (record.status === 'running') {
|
||||
return (
|
||||
<Popconfirm title="确认终止该任务?" onConfirm={(e) => { e?.stopPropagation(); handleCancelHistory(record.id); }} onCancel={(e) => e?.stopPropagation()}>
|
||||
<Button type="link" danger icon={<StopOutlined />} size="small" onClick={(e) => e.stopPropagation()}>
|
||||
终止
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -303,7 +396,18 @@ const HistoryTab: React.FC = () => {
|
||||
/>
|
||||
|
||||
<Drawer
|
||||
title="执行详情" open={!!detail} onClose={() => setDetail(null)}
|
||||
title={
|
||||
<Space>
|
||||
<span>执行详情</span>
|
||||
{detail?.status === 'running' && (
|
||||
wsConnected
|
||||
? <Tag color="processing">实时连接中</Tag>
|
||||
: <Tag>未连接</Tag>
|
||||
)}
|
||||
</Space>
|
||||
}
|
||||
open={!!detail}
|
||||
onClose={() => { closeHistoryWs(); setDetail(null); }}
|
||||
width={720}
|
||||
styles={{ body: { padding: 12 } }}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user