| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- import ReactDOM from 'react-dom/client';
- import { RouterProvider } from 'react-router-dom';
- import { router } from './router';
- import { logBrowserInfo } from '@/utils/browserDetect';
- import './index.css';
- // 全局视口高度修复:为所有使用 px2vh / vh 的地方提供统一、准确的高度基准
- // 修复键盘弹出导致界面缩小的问题:固定初始视口高度,键盘弹出时不更新
- const installViewportHeightFix = () => {
- if (typeof window === 'undefined') return;
- // 记录初始视口高度(键盘弹出前的高度)
- let initialHeight = window.innerHeight;
- let isInitialized = false;
- const setViewportHeight = (forceUpdate = false) => {
- // 首次加载时,记录初始高度
- if (!isInitialized) {
- initialHeight = window.innerHeight;
- isInitialized = true;
- }
- // 如果强制更新(如横竖屏切换),使用当前高度
- // 否则使用初始高度,避免键盘弹出时界面缩小
- const height = forceUpdate
- ? (window.visualViewport?.height ?? window.innerHeight)
- : initialHeight;
-
- const vh = height * 0.01;
- // 全局 1vh 的真实像素值
- document.documentElement.style.setProperty('--vh', `${vh}px`);
- };
- // 首次设置
- setViewportHeight();
- // 监听横竖屏切换(真正的视口变化,需要更新)
- window.addEventListener('orientationchange', () => {
- // 延迟执行,等待实际尺寸变化
- setTimeout(() => {
- initialHeight = window.innerHeight; // 更新初始高度
- setViewportHeight(true);
- }, 100);
- });
- // 监听窗口大小变化(仅处理真正的视口变化,如浏览器窗口调整)
- // 使用防抖避免频繁更新
- let resizeTimer: NodeJS.Timeout | null = null;
- window.addEventListener('resize', () => {
- if (resizeTimer) clearTimeout(resizeTimer);
- resizeTimer = setTimeout(() => {
- // 判断是否为真正的视口变化(高度变化超过 200px 认为是横竖屏切换)
- const currentHeight = window.innerHeight;
- if (Math.abs(currentHeight - initialHeight) > 200) {
- initialHeight = currentHeight;
- setViewportHeight(true);
- }
- }, 150);
- });
- };
- installViewportHeightFix();
- // 全局错误处理
- window.addEventListener('error', (event) => {
- console.error('🚨 全局错误:', event.error);
-
- // 如果错误页面已经加载,不再处理(避免循环)
- if (window.location.pathname === '/error') {
- return;
- }
-
- const errorMessage = event.error?.message || event.message || '应用加载失败';
- const errorStack = event.error?.stack || '';
-
- // 跳转到错误页面
- const params = new URLSearchParams({
- message: `${errorMessage}${errorStack ? `\n\n${errorStack}` : ''}`,
- code: 'RUNTIME_ERROR',
- retry: 'true',
- retryPath: window.location.pathname || '/',
- });
-
- window.location.href = `/error?${params.toString()}`;
- });
- window.addEventListener('unhandledrejection', (event) => {
- console.error('🚨 未处理的 Promise 拒绝:', event.reason);
-
- // 如果错误页面已经加载,不再处理(避免循环)
- if (window.location.pathname === '/error') {
- return;
- }
-
- const errorMessage = event.reason?.message || String(event.reason) || '未处理的错误';
-
- // 跳转到错误页面
- const params = new URLSearchParams({
- message: errorMessage,
- code: 'PROMISE_REJECTION',
- retry: 'true',
- retryPath: window.location.pathname || '/',
- });
-
- window.location.href = `/error?${params.toString()}`;
- });
- // 开发环境输出浏览器信息
- logBrowserInfo();
- // 渲染应用
- try {
- const rootElement = document.getElementById('root');
- if (!rootElement) {
- throw new Error('找不到 #root 元素');
- }
-
- ReactDOM.createRoot(rootElement).render(
- <RouterProvider router={router} />
- );
- } catch (error) {
- console.error('❌ React 应用渲染失败:', error);
-
- const errorMessage = error instanceof Error ? error.message : String(error);
- const errorStack = error instanceof Error ? error.stack : '';
-
- // 跳转到错误页面
- const params = new URLSearchParams({
- message: `${errorMessage}${errorStack ? `\n\n${errorStack}` : ''}`,
- code: 'RENDER_ERROR',
- retry: 'true',
- retryPath: '/',
- });
-
- window.location.href = `/error?${params.toString()}`;
- }
|