diff --git a/src/pages/dashboard.js b/src/pages/dashboard.js index 2c483b3..371f277 100644 --- a/src/pages/dashboard.js +++ b/src/pages/dashboard.js @@ -66,6 +66,14 @@ export async function render() { cardsEl.innerHTML = `
${t('common.loadFailed')}: ${escapeHtml(String(e?.message || e))}
` } }) + setTimeout(() => { + const cardsEl = page.querySelector('#stat-cards') + if (cardsEl && cardsEl.querySelector('.loading-placeholder')) { + console.warn('[dashboard] first paint fallback: dashboard APIs are still pending') + renderStatCards(page, [], _dashboardVersionCache || {}, [], null, null) + renderLogs(page, '') + } + }, 1200) page.__retryLoad = () => loadDashboardData(page).catch(() => {}) // 监听 Gateway 状态变化,节流刷新仪表盘(至少间隔 5 秒,防止状态抖动导致 UI 闪烁) @@ -148,27 +156,12 @@ async function _loadDashboardDataInner(page, fullRefresh) { if (shouldFetchVersion && (fullRefresh || versionInfoIncomplete(_dashboardVersionCache))) { invalidate('get_version_info') } - const versionP = shouldFetchVersion - ? withTimeout(api.getVersionInfo(), 8000) - .then(v => { - if (v) _dashboardVersionCache = v - return _dashboardVersionCache || {} - }) - .catch(() => _dashboardVersionCache || {}) - : Promise.resolve(_dashboardVersionCache || {}) // 每个请求独立超时:避免单个慢请求拖垮整体渲染 const coreP = Promise.allSettled([ - withTimeout(api.getServicesStatus(), 12000), - withTimeout(api.readOpenclawConfig(), 5000), - withTimeout(api.readPanelConfig(), 5000), + withTimeout(api.getServicesStatus(), 2500), + withTimeout(api.readOpenclawConfig(), 2000), + withTimeout(api.readPanelConfig(), 2000), ]) - const secondaryP = Promise.allSettled([ - withTimeout(api.listAgents(), 10000), - withTimeout(api.readMcpConfig(), 10000), - withTimeout(api.listBackups(), 10000), - withTimeout(api.listConfiguredPlatforms(), 10000).catch(() => []), - ]) - const logsP = api.readLogTail('gateway', 20).catch(() => '') // 第一波:服务状态 + 配置 + 版本 → 立即渲染统计卡片 const [servicesRes, configRes, panelConfigRes] = await coreP @@ -178,16 +171,16 @@ async function _loadDashboardDataInner(page, fullRefresh) { const panelConfig = panelConfigRes.status === 'fulfilled' ? panelConfigRes.value : null const gw = services.find(s => s.label === 'ai.openclaw.gateway') let agents = [] - versionP.then(v => { - if (!page.isConnected) return - version = v || {} - renderStatCards(page, services, version, agents, config, panelConfig) - }) const shouldLoadStatusSummary = gw?.running === true if (!shouldLoadStatusSummary) { _dashboardStatusSummaryCache = null } - if (servicesRes.status === 'rejected') toast(t('dashboard.servicesLoadFail'), 'error') + if (servicesRes.status === 'rejected') { + console.warn('[dashboard] getServicesStatus slow/failed:', servicesRes.reason) + toast(t('dashboard.servicesLoadFail'), 'error') + } + if (configRes.status === 'rejected') console.warn('[dashboard] readOpenclawConfig slow/failed:', configRes.reason) + if (panelConfigRes.status === 'rejected') console.warn('[dashboard] readPanelConfig slow/failed:', panelConfigRes.reason) // 自愈:补全关键默认值(先重新读取最新配置再 patch,避免用缓存覆盖其他页面的写入) if (config) { @@ -215,6 +208,7 @@ async function _loadDashboardDataInner(page, fullRefresh) { } renderStatCards(page, services, version, [], config, panelConfig) + renderLogs(page, '') if (gw) { maybeShowForeignGatewayBindingPrompt({ service: gw, @@ -222,6 +216,34 @@ async function _loadDashboardDataInner(page, fullRefresh) { }).catch(() => {}) } + const versionP = shouldFetchVersion + ? withTimeout(api.getVersionInfo(), 8000) + .then(v => { + if (v) _dashboardVersionCache = v + return _dashboardVersionCache || {} + }) + .catch(e => { + console.warn('[dashboard] getVersionInfo slow/failed:', e) + return _dashboardVersionCache || {} + }) + : Promise.resolve(_dashboardVersionCache || {}) + versionP.then(v => { + if (!page.isConnected) return + version = v || {} + renderStatCards(page, services, version, agents, config, panelConfig) + }) + + const secondaryP = Promise.allSettled([ + withTimeout(api.listAgents(), 5000), + withTimeout(api.readMcpConfig(), 5000), + withTimeout(api.listBackups(), 5000), + withTimeout(api.listConfiguredPlatforms(), 5000).catch(() => []), + ]) + const logsP = withTimeout(api.readLogTail('gateway', 20), 5000).catch(e => { + console.warn('[dashboard] readLogTail slow/failed:', e) + return '' + }) + // 第二波:Agent、MCP、备份 → 更新卡片 + 渲染总览 const [agentsRes, mcpRes, backupsRes, channelsRes] = await secondaryP agents = agentsRes.status === 'fulfilled' ? agentsRes.value : []