fix: 增强错误处理,确保异常情况下也能显示页面
Some checks are pending
Build Windows GUI / build-windows (push) Waiting to run
Build Windows GUI / release (push) Blocked by required conditions

后端变更:
- 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:
impressionyang 2026-05-21 19:10:11 +08:00
parent f3fe6bafc4
commit 66f4b7e0c4
4 changed files with 105 additions and 4 deletions

View File

@ -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) => {

View File

@ -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()
}
}
}, [])

View 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

View File

@ -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>
<ErrorBoundary>
<App />
</ErrorBoundary>
</React.StrictMode>,
)