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
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:
@@ -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) */}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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([]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 />;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user