diff --git a/web/src/views/history/History.vue b/web/src/views/history/History.vue
index 43fc279..65102dd 100644
--- a/web/src/views/history/History.vue
+++ b/web/src/views/history/History.vue
@@ -59,8 +59,15 @@ const isWsLoading = ref(false)
let logSocket: WebSocket | null = null
+import { decompressFromBase64 } from '@/utils/decompress'
+
const decompressedOutput = computed(() => {
- return wsContent.value || '无输出'
+ if (!wsContent.value) return '无输出'
+ // 检查是否是 base64 (尝试性,如果开头是 eJ 且长度较大,很可能是压缩后的)
+ if (wsContent.value.length > 20 && wsContent.value.startsWith('eJ')) {
+ return decompressFromBase64(wsContent.value)
+ }
+ return wsContent.value
})
async function loadLogs() {
@@ -159,6 +166,18 @@ async function selectLog(log: TaskLog) {
wsContent.value = ''
isWsLoading.value = true
+ if (log.status !== TASK_STATUS.RUNNING) {
+ try {
+ const res = await api.logs.get(log.id)
+ wsContent.value = res.output
+ } catch {
+ toast.error('加载详情失败')
+ } finally {
+ isWsLoading.value = false
+ }
+ return
+ }
+
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
const host = window.location.host
const baseUrl = (window as any).__BASE_URL__ || ''
@@ -174,16 +193,12 @@ async function selectLog(log: TaskLog) {
logSocket.onmessage = (event) => {
isWsLoading.value = false
- if (log.status !== TASK_STATUS.RUNNING) {
- wsContent.value = event.data
- } else {
- wsContent.value += event.data
- // 自动滚动到底部
- nextTick(() => {
- const pre = document.querySelector('.log-pre')
- if (pre) pre.scrollTop = pre.scrollHeight
- })
- }
+ wsContent.value += event.data
+ // 自动滚动到底部
+ nextTick(() => {
+ const pre = document.querySelector('.log-pre')
+ if (pre) pre.scrollTop = pre.scrollHeight
+ })
}
logSocket.onerror = (e) => {
diff --git a/web/src/views/tasks/Tasks.vue b/web/src/views/tasks/Tasks.vue
index 4b19a57..e0c977f 100644
--- a/web/src/views/tasks/Tasks.vue
+++ b/web/src/views/tasks/Tasks.vue
@@ -14,7 +14,7 @@ import { api, type Agent, type Task } from '@/api'
import { toast } from 'vue-sonner'
import { useSiteSettings } from '@/composables/useSiteSettings'
import { useRouter, useRoute } from 'vue-router'
-import { TASK_TYPE, AGENT_STATUS, TRIGGER_TYPE } from '@/constants'
+import { TASK_TYPE, AGENT_STATUS, TRIGGER_TYPE, TASK_STATUS } from '@/constants'
import TextOverflow from '@/components/TextOverflow.vue'
const router = useRouter()
@@ -247,6 +247,16 @@ onUnmounted(() => {
cleanupLogSocket()
})
+import { decompressFromBase64 } from '@/utils/decompress'
+
+const displayLogContent = computed(() => {
+ if (!logContent.value) return '无输出'
+ if (latestLogStatus.value !== TASK_STATUS.RUNNING && logContent.value.length > 20 && logContent.value.startsWith('eJ')) {
+ return decompressFromBase64(logContent.value)
+ }
+ return logContent.value
+})
+
async function viewLogs(taskId: string) {
try {
const res = await api.logs.list({ task_id: taskId, page: 1, page_size: 1 })
@@ -259,7 +269,17 @@ async function viewLogs(taskId: string) {
logContent.value = ''
showLogViewer.value = true
- // Connect WebSocket to load log content
+ if (latestLog.status !== TASK_STATUS.RUNNING) {
+ try {
+ const detail = await api.logs.get(latestLog.id)
+ logContent.value = detail.output
+ } catch {
+ toast.error('加载日志详情失败')
+ }
+ return
+ }
+
+ // Connect WebSocket to load log content for running tasks
cleanupLogSocket()
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
const host = window.location.host
@@ -269,11 +289,7 @@ async function viewLogs(taskId: string) {
logSocket = new WebSocket(wsUrl)
logSocket.onmessage = (event) => {
- if (latestLog.status !== 'running') {
- logContent.value = event.data
- } else {
- logContent.value += event.data
- }
+ logContent.value += event.data
}
} else {
toast.info('该任务暂无执行日志')
@@ -511,7 +527,7 @@ watch(() => route.query.agent_id, (newVal) => {
+ :content="displayLogContent || '无输出'" :status="latestLogStatus" />