- 引入 React Router v7 (Declarative mode) 实现 SPA 路由 - 重构 Layout 为 Header + 侧边栏 + 内容区的企业 Admin 布局 - 新增侧边栏菜单组件,支持折叠/展开,状态持久化到 localStorage - 新增示例页面:仪表盘、用户管理、系统设置、404 - 菜单配置与路由统一为单一数据源 (menu.tsx) - Vite code splitting 新增 vendor-router 组 - 更新 DEVELOPMENT.md 和 README.md 文档
72 lines
2.4 KiB
TypeScript
72 lines
2.4 KiB
TypeScript
import { useEffect } from "react";
|
|
import { useLocation } from "react-router";
|
|
import { ChevronLeftIcon, ChevronRightIcon } from "tdesign-icons-react";
|
|
import { Button, Layout, RadioGroup } from "tdesign-react";
|
|
|
|
import { APP } from "../shared/app";
|
|
import { Sidebar } from "./components/Sidebar";
|
|
import { useSidebarCollapsed } from "./hooks/use-sidebar-collapsed";
|
|
import { type ThemePreference, useThemePreference } from "./hooks/use-theme-preference";
|
|
import { MENU_ITEMS } from "./menu";
|
|
import { AppRoutes } from "./routes";
|
|
|
|
const { Aside, Content, Header } = Layout;
|
|
|
|
const THEME_OPTIONS = [
|
|
{ label: "系统", value: "system" },
|
|
{ label: "明亮", value: "light" },
|
|
{ label: "黑暗", value: "dark" },
|
|
] as const;
|
|
|
|
export function App() {
|
|
const { preference: themePreference, setPreference: setThemePreference } = useThemePreference();
|
|
const { collapsed, toggleCollapsed } = useSidebarCollapsed();
|
|
const location = useLocation();
|
|
|
|
useEffect(() => {
|
|
document.title = APP.title;
|
|
document.querySelector('meta[name="description"]')?.setAttribute("content", APP.description);
|
|
}, []);
|
|
|
|
const handleThemeChange = (value: ThemePreference) => {
|
|
setThemePreference(value);
|
|
};
|
|
|
|
const currentPath = location.pathname;
|
|
const currentItem = MENU_ITEMS.find((item) => item.path === currentPath);
|
|
const pageTitle = currentItem?.label ?? APP.title;
|
|
|
|
return (
|
|
<Layout className="app-layout">
|
|
<Header className="app-header">
|
|
<div className="app-header-left">
|
|
<Button className="app-sidebar-toggle" onClick={toggleCollapsed} shape="square" variant="text">
|
|
{collapsed ? <ChevronRightIcon /> : <ChevronLeftIcon />}
|
|
</Button>
|
|
<span className="app-brand">{APP.title}</span>
|
|
<span className="app-page-title">{pageTitle}</span>
|
|
</div>
|
|
<div className="app-header-right">
|
|
<RadioGroup
|
|
onChange={handleThemeChange}
|
|
options={THEME_OPTIONS.map((option) => ({ label: option.label, value: option.value }))}
|
|
theme="button"
|
|
value={themePreference}
|
|
variant="default-filled"
|
|
/>
|
|
</div>
|
|
</Header>
|
|
<Layout>
|
|
<Aside className="app-sidebar" width={collapsed ? "80px" : "232px"}>
|
|
<Sidebar collapsed={collapsed} />
|
|
</Aside>
|
|
<Layout>
|
|
<Content className="app-content">
|
|
<AppRoutes />
|
|
</Content>
|
|
</Layout>
|
|
</Layout>
|
|
</Layout>
|
|
);
|
|
}
|