This commit is contained in:
Julian Freeman
2026-04-19 11:08:28 -04:00
parent bcadf36b71
commit 634c349ebb
5 changed files with 250 additions and 11 deletions

View File

@@ -1,21 +1,34 @@
<script setup lang="ts">
import { ref, computed, nextTick, watch, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useLogsStore } from '../stores/logs'
import { Trash2, Search } from 'lucide-vue-next'
import { useQueueStore } from '../stores/queue'
import { Trash2, Search, X } from 'lucide-vue-next'
const logsStore = useLogsStore()
const queueStore = useQueueStore()
const route = useRoute()
const router = useRouter()
const logsContainer = ref<HTMLElement | null>(null)
const filterLevel = ref<'all' | 'info' | 'error'>('all')
const searchQuery = ref('')
const selectedTaskId = ref<string>((route.query.taskId as string) || '')
const selectedTask = computed(() => queueStore.tasks.find(task => task.id === selectedTaskId.value) || null)
const filteredLogs = computed(() => {
return logsStore.logs.filter(log => {
if (filterLevel.value !== 'all' && log.level !== filterLevel.value) return false
if (selectedTaskId.value && log.taskId !== selectedTaskId.value) return false
if (searchQuery.value && !log.message.toLowerCase().includes(searchQuery.value.toLowerCase()) && !log.taskId.includes(searchQuery.value)) return false
return true
})
})
const logTaskOptions = computed(() => queueStore.tasks.filter(task =>
logsStore.logs.some(log => log.taskId === task.id)
).slice(0, 20))
// Restore scroll position on mount
onMounted(() => {
if (logsContainer.value) {
@@ -27,6 +40,10 @@ onMounted(() => {
}
})
watch(() => route.query.taskId, (taskId) => {
selectedTaskId.value = typeof taskId === 'string' ? taskId : ''
})
// Auto-scroll watcher
watch(() => logsStore.logs.length, () => {
if (logsStore.autoScroll) {
@@ -57,6 +74,11 @@ function formatTime(ts: number) {
const seconds = String(d.getSeconds()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
function selectTask(taskId: string) {
selectedTaskId.value = taskId
router.replace({ path: '/logs', query: taskId ? { taskId } : {} })
}
</script>
<template>
@@ -69,6 +91,12 @@ function formatTime(ts: number) {
</div>
<div class="flex items-center gap-3">
<div v-if="selectedTask" class="hidden lg:flex items-center gap-2 rounded-xl border border-gray-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 px-3 py-2 text-xs">
<span class="truncate max-w-48">{{ selectedTask.title }}</span>
<button @click="selectTask('')" class="text-gray-400 hover:text-red-500">
<X class="w-4 h-4" />
</button>
</div>
<!-- Search -->
<div class="relative">
<Search class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" />
@@ -110,6 +138,26 @@ function formatTime(ts: number) {
</div>
<!-- Logs Console -->
<div class="mb-4 flex flex-wrap gap-2">
<button
@click="selectTask('')"
class="px-3 py-1.5 rounded-lg text-xs border transition-colors"
:class="selectedTaskId === '' ? 'bg-blue-50 text-blue-600 border-blue-200 dark:bg-blue-900/20 dark:text-blue-400 dark:border-blue-800' : 'bg-white dark:bg-zinc-900 border-gray-200 dark:border-zinc-800 text-gray-500'"
>
全部任务
</button>
<button
v-for="task in logTaskOptions"
:key="task.id"
@click="selectTask(task.id)"
class="px-3 py-1.5 rounded-lg text-xs border transition-colors max-w-56 truncate"
:class="selectedTaskId === task.id ? 'bg-blue-50 text-blue-600 border-blue-200 dark:bg-blue-900/20 dark:text-blue-400 dark:border-blue-800' : 'bg-white dark:bg-zinc-900 border-gray-200 dark:border-zinc-800 text-gray-500'"
:title="task.title"
>
{{ task.title }}
</button>
</div>
<div class="flex-1 overflow-hidden relative bg-white dark:bg-zinc-900 rounded-2xl shadow-sm border border-gray-200 dark:border-zinc-800 font-mono text-xs md:text-sm">
<div
ref="logsContainer"
@@ -121,6 +169,7 @@ function formatTime(ts: number) {
</div>
<div v-for="log in filteredLogs" :key="log.id" class="flex gap-4 hover:bg-gray-50 dark:hover:bg-zinc-800/50 px-2 py-1 rounded transition-colors">
<span class="text-gray-400 dark:text-zinc-600 shrink-0 select-none w-36">{{ formatTime(log.timestamp) }}</span>
<span class="text-blue-500 dark:text-blue-400 shrink-0 select-none w-24 truncate">{{ log.taskId }}</span>
<span
class="break-all whitespace-pre-wrap"
:class="log.level === 'error' ? 'text-red-500 dark:text-red-400' : 'text-zinc-700 dark:text-gray-300'"
@@ -139,4 +188,4 @@ function formatTime(ts: number) {
</div>
</div>
</div>
</template>
</template>