diff --git a/frontend/components.d.ts b/frontend/components.d.ts index b9d9b5d..07aa56a 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -22,6 +22,7 @@ declare module 'vue' { ElOption: typeof import('element-plus/es')['ElOption'] ElPagination: typeof import('element-plus/es')['ElPagination'] ElSelect: typeof import('element-plus/es')['ElSelect'] + ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElTable: typeof import('element-plus/es')['ElTable'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] RouterLink: typeof import('vue-router')['RouterLink'] diff --git a/frontend/package-lock.json b/frontend/package-lock.json index af667f6..4d25368 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@element-plus/icons-vue": "^2.1.0", + "@tanstack/vue-virtual": "^3.13.34", "axios": "^1.5.0", "element-plus": "^2.3.12", "pinia": "^2.1.4", @@ -735,9 +736,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -752,9 +750,6 @@ "arm" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -769,9 +764,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -786,9 +778,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -803,9 +792,6 @@ "loong64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -820,9 +806,6 @@ "loong64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -837,9 +820,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -854,9 +834,6 @@ "ppc64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -871,9 +848,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -888,9 +862,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -905,9 +876,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -922,9 +890,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -939,9 +904,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1032,6 +994,32 @@ "win32" ] }, + "node_modules/@tanstack/virtual-core": { + "version": "3.17.6", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.17.6.tgz", + "integrity": "sha512-h0/Ebo18CkOrChlQIhNtQkM5ySUnh/GumQ/D1st3hG2HWUPEF+ILUc2k29UtivCi/9G7w7G3/f7Xyd5cCFbKBw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/vue-virtual": { + "version": "3.13.34", + "resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.34.tgz", + "integrity": "sha512-CBqbCcnVsKpl9IJ7frPnbBmAqmc7JttSySg04kgLYJ4yYuC8JsAbtUHP0yLtWGLyByjihN3SwaDCgHQ+Z4iPoA==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.17.6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "vue": "^2.7.0 || ^3.0.0" + } + }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", diff --git a/frontend/package.json b/frontend/package.json index e7a601b..9acc643 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,19 +9,20 @@ "preview": "vite preview" }, "dependencies": { - "vue": "^3.3.4", - "vue-router": "^4.2.4", - "pinia": "^2.1.4", - "element-plus": "^2.3.12", "@element-plus/icons-vue": "^2.1.0", - "axios": "^1.5.0" + "@tanstack/vue-virtual": "^3.13.34", + "axios": "^1.5.0", + "element-plus": "^2.3.12", + "pinia": "^2.1.4", + "vue": "^3.3.4", + "vue-router": "^4.2.4" }, "devDependencies": { "@vitejs/plugin-vue": "^5.2.4", "typescript": "^5.2.2", - "vite": "^6.4.3", - "vue-tsc": "^2.2.12", "unplugin-auto-import": "^0.16.6", - "unplugin-vue-components": "^0.25.2" + "unplugin-vue-components": "^0.25.2", + "vite": "^6.4.3", + "vue-tsc": "^2.2.12" } } 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..f229aeb 100644 --- a/frontend/src/views/task/TaskCenter.vue +++ b/frontend/src/views/task/TaskCenter.vue @@ -52,12 +52,40 @@

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

- {{ selectedTaskMeta }} +
+ + {{ selectedTaskMeta }} +
-
-
- {{ log.time }}{{ log.message }} +
+ +
+
+ {{ logs[virtualRow.index].time }}{{ logs[virtualRow.index].message }} +
...加载中 @@ -72,10 +100,11 @@