fix(ui): 空catch块添加日志 + ErrorBoundary覆盖高风险组件
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

空catch块修复 (12处, 6文件):
- ModelsAPI: 4处 localStorage 配置读写添加 console.warn
- VikingPanel: 2处 viking 操作添加日志
- Workspace/MCPServices/SaaSStatus/TOTPSettings: 各1-3处

ErrorBoundary新增覆盖:
- ChatArea: 两种UI模式均包裹(防白屏)
- RightPanel: 两种UI模式均包裹
- AuditLogsPanel/HeartbeatConfig/VikingPanel: 设置页包裹
This commit is contained in:
iven
2026-04-11 00:26:24 +08:00
parent 717f2eab4f
commit 9772d6ec94
7 changed files with 59 additions and 24 deletions

View File

@@ -3,6 +3,7 @@ import './index.css';
import { Sidebar } from './components/Sidebar'; import { Sidebar } from './components/Sidebar';
import { ChatArea } from './components/ChatArea'; import { ChatArea } from './components/ChatArea';
import { RightPanel } from './components/RightPanel'; import { RightPanel } from './components/RightPanel';
import { ErrorBoundary } from './components/ui/ErrorBoundary';
import { SettingsLayout } from './components/Settings/SettingsLayout'; import { SettingsLayout } from './components/Settings/SettingsLayout';
import { AgentOnboardingWizard } from './components/AgentOnboardingWizard'; import { AgentOnboardingWizard } from './components/AgentOnboardingWizard';
import { HandApprovalModal } from './components/HandApprovalModal'; import { HandApprovalModal } from './components/HandApprovalModal';
@@ -458,7 +459,9 @@ function App() {
{/* 主聊天区域 */} {/* 主聊天区域 */}
<div className="flex-1 flex flex-col overflow-hidden"> <div className="flex-1 flex flex-col overflow-hidden">
<ChatArea compact onOpenDetail={() => setShowDetailDrawer(true)} /> <ErrorBoundary fallback={<div className="flex-1 flex items-center justify-center text-gray-500"></div>}>
<ChatArea compact onOpenDetail={() => setShowDetailDrawer(true)} />
</ErrorBoundary>
</div> </div>
{/* 详情抽屉 - 简洁模式仅 状态/Agent/管家 */} {/* 详情抽屉 - 简洁模式仅 状态/Agent/管家 */}
@@ -467,7 +470,9 @@ function App() {
onClose={() => setShowDetailDrawer(false)} onClose={() => setShowDetailDrawer(false)}
title="详情" title="详情"
> >
<RightPanel simpleMode /> <ErrorBoundary fallback={<div className="p-6 text-center text-gray-500"></div>}>
<RightPanel simpleMode />
</ErrorBoundary>
</DetailDrawer> </DetailDrawer>
{/* Hand Approval Modal (global) */} {/* Hand Approval Modal (global) */}
@@ -502,7 +507,9 @@ function App() {
/> />
{/* 聊天区域 */} {/* 聊天区域 */}
<ChatArea /> <ErrorBoundary fallback={<div className="flex-1 flex items-center justify-center text-gray-500"></div>}>
<ChatArea />
</ErrorBoundary>
</div> </div>
{/* 详情抽屉 - 按需显示 */} {/* 详情抽屉 - 按需显示 */}
@@ -511,7 +518,9 @@ function App() {
onClose={() => setShowDetailDrawer(false)} onClose={() => setShowDetailDrawer(false)}
title="详情" title="详情"
> >
<RightPanel /> <ErrorBoundary fallback={<div className="p-6 text-center text-gray-500"></div>}>
<RightPanel />
</ErrorBoundary>
</DetailDrawer> </DetailDrawer>
{/* Hand Approval Modal (global) */} {/* Hand Approval Modal (global) */}

View File

@@ -49,7 +49,8 @@ export function SaaSStatus({ isLoggedIn, account, saasUrl, onLogout, onLogin }:
signal: AbortSignal.timeout(5000), signal: AbortSignal.timeout(5000),
}); });
setHealthOk(response.ok); setHealthOk(response.ok);
} catch { } catch (e) {
console.warn('[SaaSStatus] Health check failed:', e);
setHealthOk(false); setHealthOk(false);
} finally { } finally {
setCheckingHealth(false); setCheckingHealth(false);

View File

@@ -30,7 +30,8 @@ export function TOTPSettings() {
setVerifyCode(''); setVerifyCode('');
try { try {
await setupTotp(); await setupTotp();
} catch { } catch (e) {
console.warn('[TOTP] Setup failed:', e);
// error already in store // error already in store
} }
}; };
@@ -43,7 +44,8 @@ export function TOTPSettings() {
await verifyTotp(verifyCode); await verifyTotp(verifyCode);
setVerifyCode(''); setVerifyCode('');
setSuccess('TOTP 已成功启用'); setSuccess('TOTP 已成功启用');
} catch { } catch (e) {
console.warn('[TOTP] Enable failed:', e);
// error already in store // error already in store
} }
}; };
@@ -60,7 +62,8 @@ export function TOTPSettings() {
setDisablePassword(''); setDisablePassword('');
setShowDisable(false); setShowDisable(false);
setSuccess('TOTP 已成功禁用'); setSuccess('TOTP 已成功禁用');
} catch { } catch (e) {
console.warn('[TOTP] Disable failed:', e);
// error already in store // error already in store
} }
}; };

View File

@@ -25,7 +25,8 @@ export function MCPServices() {
try { try {
const running = await listMcpServices(); const running = await listMcpServices();
setRunningServices(running); setRunningServices(running);
} catch { } catch (e) {
console.warn('[MCPServices] Failed to list services:', e);
// MCP might not be available yet // MCP might not be available yet
setRunningServices([]); setRunningServices([]);
} }

View File

@@ -80,8 +80,8 @@ function loadEmbeddingConfigBase(): Omit<EmbeddingConfig, 'apiKey'> & { apiKey:
const parsed = JSON.parse(stored); const parsed = JSON.parse(stored);
return { ...parsed, apiKey: '' }; return { ...parsed, apiKey: '' };
} }
} catch { } catch (e) {
// ignore console.warn('[ModelsAPI] Failed to load embedding config:', e);
} }
return { return {
provider: 'local', provider: 'local',
@@ -99,8 +99,8 @@ function loadEmbeddingConfigBase(): Omit<EmbeddingConfig, 'apiKey'> & { apiKey:
function saveEmbeddingConfigBase(config: Omit<EmbeddingConfig, 'apiKey'>): void { function saveEmbeddingConfigBase(config: Omit<EmbeddingConfig, 'apiKey'>): void {
try { try {
localStorage.setItem(EMBEDDING_STORAGE_KEY, JSON.stringify(config)); localStorage.setItem(EMBEDDING_STORAGE_KEY, JSON.stringify(config));
} catch { } catch (e) {
// ignore console.warn('[ModelsAPI] Failed to save embedding config:', e);
} }
} }
@@ -129,8 +129,8 @@ function loadCustomModelsBase(): CustomModel[] {
if (stored) { if (stored) {
return JSON.parse(stored); return JSON.parse(stored);
} }
} catch { } catch (e) {
// ignore console.warn('[ModelsAPI] Failed to load model config:', e);
} }
return []; return [];
} }
@@ -143,8 +143,8 @@ function saveCustomModelsBase(models: CustomModel[]): void {
return rest; return rest;
}); });
localStorage.setItem(STORAGE_KEY, JSON.stringify(sanitized)); localStorage.setItem(STORAGE_KEY, JSON.stringify(sanitized));
} catch { } catch (e) {
// ignore console.warn('[ModelsAPI] Failed to save model config:', e);
} }
} }

View File

@@ -149,7 +149,14 @@ export function SettingsLayout({ onBack }: SettingsLayoutProps) {
</div> </div>
</div> </div>
); );
case 'audit': return <AuditLogsPanel />; case 'audit': return (
<ErrorBoundary
fallback={<div className="p-6 text-center text-gray-500"></div>}
onError={(err, info) => console.error('[Settings] Audit page error:', err, info.componentStack)}
>
<AuditLogsPanel />
</ErrorBoundary>
);
case 'tasks': return ( case 'tasks': return (
<div className="max-w-3xl"> <div className="max-w-3xl">
<h1 className="text-xl font-bold text-gray-900 mb-6"></h1> <h1 className="text-xl font-bold text-gray-900 mb-6"></h1>
@@ -159,11 +166,23 @@ export function SettingsLayout({ onBack }: SettingsLayoutProps) {
</div> </div>
); );
case 'heartbeat': return ( case 'heartbeat': return (
<div className="max-w-3xl h-full"> <ErrorBoundary
<HeartbeatConfig /> fallback={<div className="p-6 text-center text-gray-500"></div>}
</div> onError={(err, info) => console.error('[Settings] Heartbeat page error:', err, info.componentStack)}
>
<div className="max-w-3xl h-full">
<HeartbeatConfig />
</div>
</ErrorBoundary>
);
case 'viking': return (
<ErrorBoundary
fallback={<div className="p-6 text-center text-gray-500"></div>}
onError={(err, info) => console.error('[Settings] Viking page error:', err, info.componentStack)}
>
<VikingPanel />
</ErrorBoundary>
); );
case 'viking': return <VikingPanel />;
case 'feedback': return <Feedback />; case 'feedback': return <Feedback />;
case 'about': return <About />; case 'about': return <About />;
default: return <General />; default: return <General />;

View File

@@ -50,7 +50,8 @@ export function VikingPanel() {
try { try {
const resources = await listVikingResources(''); const resources = await listVikingResources('');
setMemoryCount(resources.length); setMemoryCount(resources.length);
} catch { } catch (e) {
console.warn('[VikingPanel] Failed to list resources:', e);
setMemoryCount(null); setMemoryCount(null);
} }
} }
@@ -99,7 +100,8 @@ export function VikingPanel() {
try { try {
const fullContent = await readVikingResource(uri, 'L2'); const fullContent = await readVikingResource(uri, 'L2');
setExpandedContent(fullContent); setExpandedContent(fullContent);
} catch { } catch (e) {
console.warn('[VikingPanel] Failed to read resource:', e);
setExpandedContent(null); setExpandedContent(null);
} finally { } finally {
setIsLoadingL2(false); setIsLoadingL2(false);