Files
qiming/qiming-vite-plugin-design-mode/examples/full-featured/src/main.tsx
2026-06-01 13:43:09 +08:00

215 lines
6.6 KiB
TypeScript

/**
* @fileoverview 应用程序入口文件
* @description 负责初始化 React 应用、路由、状态管理和设计模式桥接
*/
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { create } from 'zustand';
import { subscribeWithSelector } from 'zustand/middleware';
import { devtools } from 'zustand/middleware';
import { AnimatePresence } from 'framer-motion';
// 导入应用样式
import './index.css';
// 导入主要组件
import App from './App';
// 创建设计模式状态存储
const useDesignModeStore = create<{
isDesignModeEnabled: boolean;
selectedComponent: any;
hoveredComponent: any;
componentTree: any[];
toggleDesignMode: () => void;
setSelectedComponent: (component: any) => void;
setHoveredComponent: (component: any) => void;
setComponentTree: (tree: any[]) => void;
clearSelection: () => void;
}>()(
devtools(
subscribeWithSelector((set, get) => ({
// 设计模式状态
isDesignModeEnabled: true,
selectedComponent: null,
hoveredComponent: null,
componentTree: [],
// 操作方法
toggleDesignMode: () => set((state) => ({
isDesignModeEnabled: !state.isDesignModeEnabled
})),
setSelectedComponent: (component) => set({ selectedComponent: component }),
setHoveredComponent: (component) => set({ hoveredComponent: component }),
setComponentTree: (tree) => set({ componentTree: tree }),
// 清除选择
clearSelection: () => set({
selectedComponent: null,
hoveredComponent: null
}),
})),
{
name: 'design-mode-store',
}
)
);
// 创建 React Query 客户端配置
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5分钟
gcTime: 1000 * 60 * 30, // 30分钟
retry: (failureCount, error) => {
if (error instanceof Error && error.message.includes('404')) {
return false;
}
return failureCount < 3;
},
refetchOnWindowFocus: false,
},
mutations: {
retry: 1,
},
},
});
// 错误处理组件
const ErrorBoundary: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [hasError, setHasError] = React.useState(false);
const [error, setError] = React.useState<Error | null>(null);
React.useEffect(() => {
const handleError = (error: ErrorEvent) => {
console.error('Global error caught:', error);
setHasError(true);
setError(error.error || new Error(error.message));
};
const handleUnhandledRejection = (event: PromiseRejectionEvent) => {
console.error('Unhandled promise rejection:', event.reason);
setHasError(true);
setError(event.reason instanceof Error ? event.reason : new Error(String(event.reason)));
};
window.addEventListener('error', handleError);
window.addEventListener('unhandledrejection', handleUnhandledRejection);
return () => {
window.removeEventListener('error', handleError);
window.removeEventListener('unhandledrejection', handleUnhandledRejection);
};
}, []);
if (hasError) {
return (
<div className="min-h-screen flex items-center justify-center bg-neutral-50">
<div className="max-w-md p-6 bg-white rounded-lg shadow-lg">
<div className="flex items-center mb-4">
<div className="w-8 h-8 bg-error-100 rounded-full flex items-center justify-center mr-3">
<svg className="w-4 h-4 text-error-600" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
</svg>
</div>
<h2 className="text-lg font-semibold text-neutral-900">Application Error</h2>
</div>
<p className="text-neutral-600 mb-4">
Something went wrong while loading the application. Please try refreshing the page.
</p>
{error && (
<details className="mb-4 p-3 bg-neutral-50 rounded border">
<summary className="cursor-pointer text-sm font-medium text-neutral-700 mb-2">
Error Details
</summary>
<pre className="text-xs text-neutral-600 overflow-auto">
{error.message}
{error.stack && `\n\n${error.stack}`}
</pre>
</details>
)}
<div className="flex space-x-3">
<button
onClick={() => window.location.reload()}
className="flex-1 px-4 py-2 bg-primary-600 text-white rounded-md hover:bg-primary-700 transition-colors"
>
Reload Page
</button>
<button
onClick={() => {
setHasError(false);
setError(null);
}}
className="flex-1 px-4 py-2 bg-neutral-200 text-neutral-700 rounded-md hover:bg-neutral-300 transition-colors"
>
Try Again
</button>
</div>
</div>
</div>
);
}
return <>{children}</>;
};
// 主应用组件
const RootApp: React.FC = () => {
return (
<ErrorBoundary>
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<AnimatePresence mode="wait">
<App />
</AnimatePresence>
</BrowserRouter>
</QueryClientProvider>
</ErrorBoundary>
);
};
// 渲染应用
const rootElement = document.getElementById('root');
if (!rootElement) {
throw new Error('Root element not found');
}
const root = ReactDOM.createRoot(rootElement);
root.render(<RootApp />);
// 开发环境下的热模块替换
if (import.meta.hot) {
import.meta.hot.accept();
}
// 设计模式桥接(如果存在)
declare global {
interface Window {
__APPDEV_DESIGN_MODE__?: {
store: typeof useDesignModeStore;
toggleDesignMode: () => void;
selectComponent: (component: any) => void;
highlightComponent: (component: any) => void;
};
}
}
// 初始化设计模式桥接
if (typeof window !== 'undefined') {
window.__APPDEV_DESIGN_MODE__ = {
store: useDesignModeStore,
toggleDesignMode: () => useDesignModeStore.getState().toggleDesignMode(),
selectComponent: (component) => useDesignModeStore.getState().setSelectedComponent(component),
highlightComponent: (component) => useDesignModeStore.getState().setHoveredComponent(component),
};
}
export { useDesignModeStore };
export default RootApp;