/** * 仪表盘页面 */ import { api } from '../lib/tauri-api.js' import { toast } from '../components/toast.js' export async function render() { const page = document.createElement('div') page.className = 'page' page.innerHTML = `
最近日志
加载中...
` // 异步加载数据 loadDashboardData(page) return page } async function loadDashboardData(page) { try { const [services, version, logs] = await Promise.all([ api.getServicesStatus(), api.getVersionInfo(), api.readLogTail('gateway', 20), ]) renderStatCards(page, services, version) renderLogs(page, logs) bindActions(page) } catch (e) { toast('加载仪表盘数据失败: ' + e, 'error') } } function renderStatCards(page, services, version) { const cardsEl = page.querySelector('#stat-cards') const gw = services.find(s => s.label.includes('gateway')) const guardian = services.find(s => s.label.includes('guardian.watch')) const watchdog = services.find(s => s.label.includes('watchdog')) const runningCount = services.filter(s => s.running).length cardsEl.innerHTML = `
Gateway
${gw?.running ? '运行中' : '已停止'}
${gw?.pid ? 'PID: ' + gw.pid : ''}
Guardian
${guardian?.running ? '运行中' : '已停止'}
健康监控
Watchdog
${watchdog?.running ? '运行中' : '已停止'}
看门狗
版本
${version.current || '未知'}
服务 ${runningCount}/${services.length} 运行中
` } function renderLogs(page, logs) { const logsEl = page.querySelector('#recent-logs') if (!logs) { logsEl.textContent = '暂无日志'; return } const lines = logs.trim().split('\n') logsEl.innerHTML = lines.map(l => `
${escapeHtml(l)}
`).join('') logsEl.scrollTop = logsEl.scrollHeight } function bindActions(page) { const btnRestart = page.querySelector('#btn-restart-gw') const btnUpdate = page.querySelector('#btn-check-update') btnRestart?.addEventListener('click', async () => { btnRestart.disabled = true btnRestart.textContent = '重启中...' try { await api.restartService('ai.openclaw.gateway') toast('Gateway 已重启', 'success') setTimeout(() => loadDashboardData(page), 500) } catch (e) { toast('重启失败: ' + e, 'error') } finally { btnRestart.disabled = false btnRestart.textContent = '重启 Gateway' } }) btnUpdate?.addEventListener('click', async () => { btnUpdate.disabled = true btnUpdate.textContent = '检查中...' try { const info = await api.getVersionInfo() if (info.update_available) { toast(`发现新版本: ${info.latest}`, 'info') } else { toast('已是最新版本', 'success') } } catch (e) { toast('检查更新失败: ' + e, 'error') } finally { btnUpdate.disabled = false btnUpdate.textContent = '检查更新' } }) } function escapeHtml(str) { return str.replace(/&/g, '&').replace(//g, '>') }