57 lines
16 KiB
JavaScript
57 lines
16 KiB
JavaScript
import{_ as i,o as a,c as n,a2 as l}from"./chunks/framework.B-QBKwc1.js";const c=JSON.parse('{"title":"布局系统","description":"前端布局采用经典的企业后台布局:左侧菜单 + 顶部导航 + 内容区域,支持多标签页导航。","frontmatter":{},"headers":[],"relativePath":"zh/frontend/layout.md","filePath":"zh/frontend/layout.md"}'),t={name:"zh/frontend/layout.md"};function p(e,s,h,k,r,d){return a(),n("div",null,[...s[0]||(s[0]=[l(`<h1 id="_9-15-布局系统" tabindex="-1">9.15 布局系统 <a class="header-anchor" href="#_9-15-布局系统" aria-label="Permalink to "9.15 布局系统""></a></h1><div class="tip custom-block"><p class="custom-block-title">概述</p><p>前端布局采用经典的企业后台布局:左侧菜单 + 顶部导航 + 内容区域,支持多标签页导航。</p></div><h2 id="布局结构" tabindex="-1">布局结构 <a class="header-anchor" href="#布局结构" aria-label="Permalink to "布局结构""></a></h2><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>┌─────────────────────────────────────────────────────────────┐</span></span>
|
||
<span class="line"><span>│ 顶部导航栏 │</span></span>
|
||
<span class="line"><span>│ [Logo] [面包屑] [搜索] [通知] [头像] │</span></span>
|
||
<span class="line"><span>├──────────┬──────────────────────────────────────────────────┤</span></span>
|
||
<span class="line"><span>│ │ │</span></span>
|
||
<span class="line"><span>│ 侧边栏 │ 内容区域 │</span></span>
|
||
<span class="line"><span>│ 菜单 │ │</span></span>
|
||
<span class="line"><span>│ │ ┌──────────────────────────────────────────┐ │</span></span>
|
||
<span class="line"><span>│ ──────── │ │ 标签页导航 │ │</span></span>
|
||
<span class="line"><span>│ 系统管理 │ ├──────────────────────────────────────────┤ │</span></span>
|
||
<span class="line"><span>│ ├ 用户 │ │ │ │</span></span>
|
||
<span class="line"><span>│ ├ 角色 │ │ 页面内容 │ │</span></span>
|
||
<span class="line"><span>│ └ 菜单 │ │ │ │</span></span>
|
||
<span class="line"><span>│ │ │ │ │</span></span>
|
||
<span class="line"><span>│ 内容管理 │ │ │ │</span></span>
|
||
<span class="line"><span>│ ├ 文章 │ │ │ │</span></span>
|
||
<span class="line"><span>│ └ 分类 │ │ │ │</span></span>
|
||
<span class="line"><span>│ │ └──────────────────────────────────────────┘ │</span></span>
|
||
<span class="line"><span>└──────────┴──────────────────────────────────────────────────┘</span></span></code></pre></div><h2 id="布局组件" tabindex="-1">布局组件 <a class="header-anchor" href="#布局组件" aria-label="Permalink to "布局组件""></a></h2><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>ui/src/layout/</span></span>
|
||
<span class="line"><span>├── index.vue # 布局入口</span></span>
|
||
<span class="line"><span>├── parentLayout.vue # 父级布局(路由嵌套用)</span></span>
|
||
<span class="line"><span>└── components/</span></span>
|
||
<span class="line"><span> ├── Header/ # 顶部导航(AmendPwd 修改密码、Notifier 通知、ProjectSetting 项目设置)</span></span>
|
||
<span class="line"><span> ├── Sider/ # 侧边栏菜单(动态菜单、折叠、主题)</span></span>
|
||
<span class="line"><span> ├── Main/ # 内容区域(keep-alive 缓存)</span></span>
|
||
<span class="line"><span> ├── TagsView/ # 标签页导航(多标签切换、关闭)</span></span>
|
||
<span class="line"><span> ├── Logo/ # 品牌标识</span></span>
|
||
<span class="line"><span> └── Footer/ # 页脚</span></span></code></pre></div><h2 id="侧边栏菜单" tabindex="-1">侧边栏菜单 <a class="header-anchor" href="#侧边栏菜单" aria-label="Permalink to "侧边栏菜单""></a></h2><p>菜单数据从后端 <code>GET /api/index/getMenus</code> 接口获取,由 <code>asyncRouteStore</code> 管理:</p><div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 菜单渲染逻辑</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">const</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> menuList</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> asyncRouteStore</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">getMenus</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 菜单数据结构</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">interface</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#2E8F82;--shiki-light-font-style:inherit;"> MenuItem</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> id</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#2E8F82;--shiki-light-font-style:inherit;"> number</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> parentId</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#2E8F82;--shiki-light-font-style:inherit;"> number</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> name</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#2E8F82;--shiki-light-font-style:inherit;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> path</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#2E8F82;--shiki-light-font-style:inherit;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> component</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#2E8F82;--shiki-light-font-style:inherit;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> icon</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#2E8F82;--shiki-light-font-style:inherit;"> string</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> sort</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#2E8F82;--shiki-light-font-style:inherit;"> number</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> hide</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#2E8F82;--shiki-light-font-style:inherit;"> boolean</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> children</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">?</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#2E8F82;--shiki-light-font-style:inherit;"> MenuItem</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">[];</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><div class="tip custom-block"><p class="custom-block-title">菜单数据即路由</p><p>侧边栏与动态路由消费<strong>同一份</strong> <code>getMenus</code> 数据(仅 <code>type=0</code> 可导航节点、<code>status=0</code>、<code>hide=0</code>、<code>is_delete=0</code>)——菜单管理里改 <code>hide</code> 即可隐藏入口而不删除路由。见 <a href="/zh/frontend/router.html">9.5 路由与动态菜单</a>。</p></div><h2 id="标签页导航" tabindex="-1">标签页导航 <a class="header-anchor" href="#标签页导航" aria-label="Permalink to "标签页导航""></a></h2><p>标签页自动记录访问过的页面,支持:</p><ul><li>点击切换</li><li>关闭当前/其他/全部</li><li>右键菜单操作</li><li>keep-alive 缓存</li></ul><div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 标签页 Store</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">const</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> tabsViewStore</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> useTabsViewStore</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">();</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 添加标签</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">tabsViewStore</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">addTab</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">route</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 关闭标签</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">tabsViewStore</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">removeTab</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">route</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span></code></pre></div><p>(keep-alive 组件列表由路由守卫 <code>afterEach</code> 统一维护,见 <a href="/zh/frontend/router.html">9.5 路由与动态菜单</a>。)</p><h2 id="响应式布局" tabindex="-1">响应式布局 <a class="header-anchor" href="#响应式布局" aria-label="Permalink to "响应式布局""></a></h2><p>布局支持桌面端和平板端:</p><ul><li>桌面端:侧边栏默认展开</li><li>平板端:侧边栏默认折叠</li><li>侧边栏支持手动折叠/展开</li></ul><div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 侧边栏折叠状态</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">const</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> collapsed</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> ref</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#1E754F;">false</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 响应式检测</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">const</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> width</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> }</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> useWindowSize</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">();</span></span>
|
||
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">watchEffect</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(()</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =></span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> collapsed</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">value</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> width</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">value</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> <</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 1024</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">});</span></span></code></pre></div>`,19)])])}const o=i(t,[["render",p]]);export{c as __pageData,o as default};
|