diff --git a/frontend/src/api/taskLog.ts b/frontend/src/api/taskLog.ts index 34b7050..4791b15 100644 --- a/frontend/src/api/taskLog.ts +++ b/frontend/src/api/taskLog.ts @@ -79,3 +79,49 @@ function parseResponseBody(text: string) { return { error: text } } } + +export interface TaskLogStreamCallbacks { + onInit?: (lines: TaskLogLine[]) => void + onUpdate?: (lines: TaskLogLine[]) => void + onFinished?: () => void + onError?: (error: string) => void +} + +export function createTaskLogStream(taskId: string, callbacks: TaskLogStreamCallbacks): EventSource { + const token = getToken() + const url = `/auth/api/v1/task-logs/stream?task_id=${encodeURIComponent(taskId)}&access_token=${encodeURIComponent(token || '')}` + + const es = new EventSource(url) + + es.onmessage = (event) => { + try { + const data = JSON.parse(event.data) + switch (data.type) { + case 'init': + callbacks.onInit?.(Array.isArray(data.lines) ? data.lines : []) + break + case 'update': + callbacks.onUpdate?.(Array.isArray(data.lines) ? data.lines : []) + break + case 'finished': + callbacks.onFinished?.() + es.close() + break + case 'error': + callbacks.onError?.(data.error || 'Unknown error') + break + } + } catch { + // ignore parse errors for heartbeat etc. + } + } + + es.onerror = () => { + // EventSource will auto-reconnect, but we report connection errors + if (es.readyState === EventSource.CLOSED) { + callbacks.onError?.('Connection closed') + } + } + + return es +} diff --git a/frontend/src/views/task/TaskCenter.vue b/frontend/src/views/task/TaskCenter.vue index 021802c..ace6795 100644 --- a/frontend/src/views/task/TaskCenter.vue +++ b/frontend/src/views/task/TaskCenter.vue @@ -52,6 +52,7 @@

任务日志 {{ selectedTaskName }} + ● 实时更新中

{{ selectedTaskMeta }} @@ -72,8 +73,8 @@