fix: 增强错误处理,确保异常情况下也能显示页面
后端变更: - src/app/mod.rs: 窗口 hide() 使用 _ 忽略错误,避免 unwrap() panic 前端变更: - web/src/components/ErrorBoundary.tsx: 新增错误边界组件,捕获未处理的错误 - web/src/main.tsx: 使用 ErrorBoundary 包裹 App 组件 - web/src/App.tsx: 优化主题加载和监听器的错误处理,确保失败时使用默认主题 错误处理能力: - 即使 Tauri 命令失败,页面也能正常显示 - 未捕获错误会显示友好的错误页面,提供刷新按钮 - 主题加载失败时自动使用 system 主题降级 - 事件监听器清理更安全,避免异步问题 构建结果: - Windows 包:dist/impress-asr-windows-x64-20260521_190528.zip - 文件大小:5.0MB
This commit is contained in:
parent
f3fe6bafc4
commit
66f4b7e0c4
@ -151,7 +151,7 @@ pub fn run() -> Result<()> {
|
||||
tauri::WindowEvent::CloseRequested { api, .. } => {
|
||||
info!(" [窗口] 关闭请求 - 隐藏窗口到托盘");
|
||||
// 隐藏窗口而不是关闭
|
||||
window.hide().unwrap();
|
||||
let _ = window.hide();
|
||||
api.prevent_close();
|
||||
}
|
||||
tauri::WindowEvent::Focused(focused) => {
|
||||
|
||||
@ -27,19 +27,28 @@ function App() {
|
||||
applyTheme(currentTheme)
|
||||
} catch (e) {
|
||||
console.error('Failed to load theme:', e)
|
||||
// 使用默认主题,不影响页面显示
|
||||
applyTheme('system')
|
||||
}
|
||||
}
|
||||
loadTheme()
|
||||
|
||||
// 监听主题变化事件
|
||||
const unlisten = window.__TAURI__.listen('theme-change', (event) => {
|
||||
let unlistenFn: (() => void) | null = null
|
||||
window.__TAURI__.listen('theme-change', (event) => {
|
||||
const newTheme = event.payload as string
|
||||
setTheme(newTheme as Theme)
|
||||
applyTheme(newTheme as Theme)
|
||||
}).then(fn => {
|
||||
unlistenFn = fn
|
||||
}).catch(e => {
|
||||
console.error('Failed to setup theme listener:', e)
|
||||
})
|
||||
|
||||
return () => {
|
||||
unlisten.then(fn => fn())
|
||||
if (unlistenFn) {
|
||||
unlistenFn()
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
||||
89
web/src/components/ErrorBoundary.tsx
Normal file
89
web/src/components/ErrorBoundary.tsx
Normal file
@ -0,0 +1,89 @@
|
||||
import { Component, ErrorInfo, ReactNode } from 'react'
|
||||
|
||||
interface Props {
|
||||
children: ReactNode
|
||||
fallback?: ReactNode
|
||||
}
|
||||
|
||||
interface State {
|
||||
hasError: boolean
|
||||
error: Error | null
|
||||
}
|
||||
|
||||
export class ErrorBoundary extends Component<Props, State> {
|
||||
public state: State = {
|
||||
hasError: false,
|
||||
error: null
|
||||
}
|
||||
|
||||
public static getDerivedStateFromError(error: Error): State {
|
||||
return { hasError: true, error }
|
||||
}
|
||||
|
||||
public componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
|
||||
console.error('ErrorBoundary caught an error:', error, errorInfo)
|
||||
}
|
||||
|
||||
public render(): ReactNode {
|
||||
if (this.state.hasError) {
|
||||
if (this.props.fallback) {
|
||||
return this.props.fallback
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
padding: '40px',
|
||||
textAlign: 'center',
|
||||
color: 'var(--text-secondary)',
|
||||
maxWidth: '600px',
|
||||
margin: '40px auto'
|
||||
}}>
|
||||
<h2 style={{ fontSize: '20px', marginBottom: '16px', color: 'var(--text-primary)' }}>
|
||||
发生错误
|
||||
</h2>
|
||||
<p style={{ marginBottom: '24px' }}>
|
||||
抱歉,页面加载时出现问题。请尝试刷新页面或重启应用。
|
||||
</p>
|
||||
{this.state.error && (
|
||||
<details style={{
|
||||
textAlign: 'left',
|
||||
background: 'var(--bg-tertiary)',
|
||||
padding: '16px',
|
||||
borderRadius: '8px',
|
||||
fontSize: '12px',
|
||||
fontFamily: 'monospace',
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-all',
|
||||
maxHeight: '300px',
|
||||
overflow: 'auto'
|
||||
}}>
|
||||
<summary style={{ cursor: 'pointer', marginBottom: '8px' }}>
|
||||
错误详情(点击展开)
|
||||
</summary>
|
||||
{this.state.error.toString()}
|
||||
</details>
|
||||
)}
|
||||
<button
|
||||
onClick={() => window.location.reload()}
|
||||
style={{
|
||||
marginTop: '20px',
|
||||
padding: '12px 24px',
|
||||
background: 'var(--accent)',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '8px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '14px'
|
||||
}}
|
||||
>
|
||||
刷新页面
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorBoundary
|
||||
@ -1,10 +1,13 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
import ErrorBoundary from './components/ErrorBoundary'
|
||||
import './index.css'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
<ErrorBoundary>
|
||||
<App />
|
||||
</ErrorBoundary>
|
||||
</React.StrictMode>,
|
||||
)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user