/** * 日志查看组件 */ app.component('logs-page', { setup() { const { ref, onMounted, onUnmounted } = Vue; const logs = ref([]); const autoScroll = ref(true); const levelFilter = ref('all'); const searchText = ref(''); const socket = ref(null); const isConnected = ref(false); // 日志级别选项 const levels = [ { value: 'all', label: '全部' }, { value: 'info', label: 'INFO' }, { value: 'warn', label: 'WARN' }, { value: 'error', label: 'ERROR' }, { value: 'debug', label: 'DEBUG' } ]; // 过滤后的日志 const filteredLogs = Vue.computed(() => { return logs.value.filter(log => { if (levelFilter.value !== 'all' && log.level !== levelFilter.value) { return false; } if (searchText.value && !log.message.toLowerCase().includes(searchText.value.toLowerCase())) { return false; } return true; }); }); // 连接日志 WebSocket function connect() { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const wsUrl = `${protocol}//${window.location.host}/ws/logs`; try { socket.value = new WebSocket(wsUrl); socket.value.onopen = () => { isConnected.value = true; addLog('info', '已连接到日志服务'); }; socket.value.onmessage = (event) => { try { const log = JSON.parse(event.data); addLogEntry(log); } catch { // 纯文本日志 addLog('info', event.data); } }; socket.value.onclose = () => { isConnected.value = false; addLog('warn', '日志连接已断开'); }; socket.value.onerror = () => { addLog('error', '日志连接错误'); }; } catch (e) { addLog('error', '无法连接日志服务: ' + e.message); } } // 添加日志条目 function addLogEntry(log) { logs.value.push({ id: Date.now() + Math.random(), time: log.timestamp || new Date().toISOString(), level: log.level || 'info', message: log.message || log.event || JSON.stringify(log), source: log.logger || log.source || '' }); // 限制日志数量 if (logs.value.length > 1000) { logs.value = logs.value.slice(-500); } // 自动滚动 if (autoScroll.value) { scrollToBottom(); } } // 添加简单日志 function addLog(level, message) { addLogEntry({ level, message, timestamp: new Date().toISOString() }); } // 滚动到底部 function scrollToBottom() { Vue.nextTick(() => { const container = document.querySelector('.logs-body'); if (container) { container.scrollTop = container.scrollHeight; } }); } // 清空日志 function clearLogs() { logs.value = []; } // 导出日志 function exportLogs() { const data = filteredLogs.value.map(log => `[${log.time}] [${log.level.toUpperCase()}] ${log.message}` ).join('\n'); const blob = new Blob([data], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `minenasai-logs-${new Date().toISOString().slice(0, 10)}.txt`; a.click(); URL.revokeObjectURL(url); } // 格式化时间 function formatTime(time) { try { return new Date(time).toLocaleString('zh-CN'); } catch { return time; } } // 加载历史日志 async function loadHistory() { try { const res = await fetch('/api/logs?limit=100'); const data = await res.json(); if (data.logs) { data.logs.forEach(log => addLogEntry(log)); } } catch (e) { console.error('加载历史日志失败:', e); } } // 生命周期 onMounted(() => { loadHistory(); connect(); }); onUnmounted(() => { if (socket.value) { socket.value.close(); } }); return { logs, filteredLogs, autoScroll, levelFilter, searchText, isConnected, levels, clearLogs, exportLogs, formatTime }; }, template: `