import { useState, useEffect, ReactNode } from 'react'; import { useLaunch } from '@tarojs/taro'; import { IntlProvider } from 'react-intl'; import { ConfigProvider } from 'antd'; import { Provider } from 'react-redux'; import store, { useAppDispatch, useAppSelector } from './states/store'; import { initializeProductState } from './states/productSlice'; import { loadI18nMessages } from './states/i18nSlice'; import { checkServerConnection } from './features/serverConfig'; import { initializeAnnotationManager, cleanupAnnotationManager } from './features/imageAnnotation'; import { platform } from './utils/platform'; import './app.css'; import ProductSelector from './components/ProductSelector'; import ThemeSwitcher from './components/ThemeSwitcher'; import QuotaAlertModal from './pages/security/QuotaAlertModal'; import AcquisitionTracer from './pages/exam/components/acquisitionTracer'; import FeatureNotAvailableFeedback from './components/FeatureNotAvailableFeedback'; import { setFeatureNotAvailableOpen } from './states/featureNotAvailableSlice'; import { setBusinessFlow } from './states/BusinessFlowSlice'; import { logger } from './log/logger'; import { theme } from 'antd'; import ServerConfigModal from './features/serverConfig/components/ServerConfigModal'; console.log = logger.log; console.warn = logger.warn; console.error = logger.error; console.debug = logger.debug; console.log(`process.env.USE_MSW: ${process.env.USE_MSW}`); console.log(`process.env.NODE_ENV: ${process.env.NODE_ENV}`); console.debug('debug level') if (process.env.NODE_ENV === 'development' && process.env.USE_MSW === 'true') { import('../mocks/server') .then(({ server }): void => { server.start({ onUnhandledRequest: 'error', // 未处理的请求触发网络错误 }); console.log(`启动了MSW`); }) .catch((err): void => { console.warn('Mock server module not found:', err); }); } function AppContent({ children }: { children: ReactNode }): JSX.Element { const dispatch = useAppDispatch(); const { messages, loading, error, currentLocale } = useAppSelector( (state) => state.i18n ); const isFeatureNotAvailableOpen = useAppSelector( (state) => state.featureNotAvailable.isOpen ); const { currentTheme, themeType } = useAppSelector((state) => state.theme); const [isI18nReady, setIsI18nReady] = useState(false); const [showConfigModal, setShowConfigModal] = useState(false); const [connectionChecked, setConnectionChecked] = useState(false); const themeWithAlgorithm = { ...currentTheme, algorithm: themeType === 'light' ? theme.defaultAlgorithm : theme.darkAlgorithm, }; useLaunch((): void => { console.log('App launched.'); }); useEffect(() => { // 浏览器环境不需要服务器连接检查,直接初始化应用 if (platform.isBrowser) { console.log('浏览器环境,跳过服务器连接检查,直接初始化应用'); initializeApp(); return; } // Electron/Cordova 环境:检查服务器连接 dispatch(checkServerConnection()) .unwrap() .then((result) => { setConnectionChecked(true); if (result.needsConfig) { // 需要配置,显示对话框 console.log('检测到需要服务器配置,显示配置对话框'); setShowConfigModal(true); } else { // 连接正常,继续正常初始化 console.log('服务器连接正常,开始应用初始化'); return initializeApp(); } }) .catch((error) => { console.error('连接检查失败:', error); setConnectionChecked(true); setShowConfigModal(true); }); // 应用退出时清理注释管理器 return () => { cleanupAnnotationManager().catch(console.error); }; }, [dispatch]); // 应用正常初始化函数 const initializeApp = async () => { try { const productState = await dispatch(initializeProductState()).unwrap(); console.log(`初始化,拉取到产品信息:${JSON.stringify(productState)}`); const languageCode = productState.language; await dispatch(loadI18nMessages(languageCode)).unwrap(); // ✅ 初始化注释管理器(在产品状态和国际化之后) await initializeAnnotationManager(); setIsI18nReady(true); } catch (error) { console.error('应用初始化失败:', error); // 显示配置对话框,让用户重新配置 setShowConfigModal(true); } }; // 配置保存后的处理 const handleConfigSaved = () => { setShowConfigModal(false); // 重新检查连接并初始化应用 dispatch(checkServerConnection()) .unwrap() .then((result) => { if (!result.needsConfig) { initializeApp(); } }) .catch((error) => { console.error('重新检查连接失败:', error); }); }; console.log('当前语言:', currentLocale); console.log('messages', messages); // children 是将要会渲染的页面 // IntlProvider 始终存在,使用默认值避免 useIntl 报错 return ( en,提供默认值 messages={(messages as Record) || {}} // 提供空对象作为默认值 > {/* 加载状态覆盖层 */} {(loading || (!isI18nReady && !connectionChecked)) && (
加载多语言资源中...
)} {/* 服务器配置对话框 */} setShowConfigModal(false)} /> {/* 错误状态覆盖层 */} {error && (
多语言资源加载失败: {error}
)} {/* children 始终被渲染,满足 Taro 框架要求 */}
dispatch(setFeatureNotAvailableOpen(false))} onContinue={() => { dispatch(setFeatureNotAvailableOpen(false)); dispatch(setBusinessFlow('continueAfterFeatureNotAvailable')); }} /> {children} {process.env.NODE_ENV === 'development' && }
); } function App({ children }: { children: ReactNode }): JSX.Element { // 只在 Cypress 测试环境下暴露 store 到 window 对象 if ( typeof window !== 'undefined' && (window as unknown as { Cypress: unknown }).Cypress ) { (window as unknown as { store: typeof store }).store = store; } return ( {children} ); } export default App;