# 9.15 布局系统 :::tip 概述 前端布局采用经典的企业后台布局:左侧菜单 + 顶部导航 + 内容区域,支持多标签页导航。 ::: ## 布局结构 ```text ┌─────────────────────────────────────────────────────────────┐ │ 顶部导航栏 │ │ [Logo] [面包屑] [搜索] [通知] [头像] │ ├──────────┬──────────────────────────────────────────────────┤ │ │ │ │ 侧边栏 │ 内容区域 │ │ 菜单 │ │ │ │ ┌──────────────────────────────────────────┐ │ │ ──────── │ │ 标签页导航 │ │ │ 系统管理 │ ├──────────────────────────────────────────┤ │ │ ├ 用户 │ │ │ │ │ ├ 角色 │ │ 页面内容 │ │ │ └ 菜单 │ │ │ │ │ │ │ │ │ │ 内容管理 │ │ │ │ │ ├ 文章 │ │ │ │ │ └ 分类 │ │ │ │ │ │ └──────────────────────────────────────────┘ │ └──────────┴──────────────────────────────────────────────────┘ ``` ## 布局组件 ```text ui/src/layout/ ├── index.vue # 布局入口 ├── parentLayout.vue # 父级布局(路由嵌套用) └── components/ ├── Header/ # 顶部导航(AmendPwd 修改密码、Notifier 通知、ProjectSetting 项目设置) ├── Sider/ # 侧边栏菜单(动态菜单、折叠、主题) ├── Main/ # 内容区域(keep-alive 缓存) ├── TagsView/ # 标签页导航(多标签切换、关闭) ├── Logo/ # 品牌标识 └── Footer/ # 页脚 ``` ## 侧边栏菜单 菜单数据从后端 `GET /api/index/getMenus` 接口获取,由 `asyncRouteStore` 管理: ```typescript // 菜单渲染逻辑 const menuList = asyncRouteStore.getMenus; // 菜单数据结构 interface MenuItem { id: number; parentId: number; name: string; path: string; component: string; icon: string; sort: number; hide: boolean; children?: MenuItem[]; } ``` :::tip 菜单数据即路由 侧边栏与动态路由消费**同一份** `getMenus` 数据(仅 `type=0` 可导航节点、`status=0`、`hide=0`、`is_delete=0`)——菜单管理里改 `hide` 即可隐藏入口而不删除路由。见 [9.5 路由与动态菜单](/zh/frontend/router.html)。 ::: ## 标签页导航 标签页自动记录访问过的页面,支持: - 点击切换 - 关闭当前/其他/全部 - 右键菜单操作 - keep-alive 缓存 ```typescript // 标签页 Store const tabsViewStore = useTabsViewStore(); // 添加标签 tabsViewStore.addTab(route); // 关闭标签 tabsViewStore.removeTab(route); ``` (keep-alive 组件列表由路由守卫 `afterEach` 统一维护,见 [9.5 路由与动态菜单](/zh/frontend/router.html)。) ## 响应式布局 布局支持桌面端和平板端: - 桌面端:侧边栏默认展开 - 平板端:侧边栏默认折叠 - 侧边栏支持手动折叠/展开 ```typescript // 侧边栏折叠状态 const collapsed = ref(false); // 响应式检测 const { width } = useWindowSize(); watchEffect(() => { collapsed.value = width.value < 1024; }); ```