import{_ as u,r as c,o as r,c as d,b as n,d as s,a as e,w as o,e as l,f as a}from"./app.bd9c95b7.js";const k={},v=n("h1",{id:"fesjs-plugin-layout",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#fesjs-plugin-layout","aria-hidden":"true"},"#"),s(" @fesjs/plugin-layout")],-1),g=n("h2",{id:"介绍",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#介绍","aria-hidden":"true"},"#"),s(" 介绍")],-1),h=n("p",null,[s("为了进一步降低研发成本,我们将布局利用 "),n("code",null,"fes.js"),s(" 插件的方式内置,只需通过简单的配置即可拥有布局,包括导航以及侧边栏。从而做到用户无需关心布局。")],-1),m=a("
  • 侧边栏菜单数据根据路由中的配置自动生成。
  • 布局,提供 sidetopmixinleft-right 四种布局。
  • 主题,提供 lightdark 两种主题。
  • 默认实现对路由的 404、403 处理。
  • ",4),b=n("li",null,"支持自定义头部或者侧边栏区域。",-1),f=n("li",null,"菜单支持配置 icon。",-1),_=n("li",null,"菜单标题支持国际化。",-1),x=n("li",null,"可配置页面是否需要 layout。",-1),y=a(`

    启用方式

    package.json 中引入依赖:

    {
        "dependencies": {
            "@fesjs/fes": "^3.0.0",
            "@fesjs/plugin-layout": "^5.0.0"
        }
    }
    

    布局类型

    配置参数是 navigation, 布局有三种类型 sidemixintopleft-right, 默认是 side

    side

    `,6),j=["src"],w=n("h3",{id:"top",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#top","aria-hidden":"true"},"#"),s(" top")],-1),A=["src"],E=n("h3",{id:"mixin",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#mixin","aria-hidden":"true"},"#"),s(" mixin")],-1),B=["src"],F=n("h3",{id:"left-right",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#left-right","aria-hidden":"true"},"#"),s(" left-right")],-1),C=["src"],R=a(`

    页面个性化

    可以为页面单独设置布局类型:

    import { defineRouteMete } from '@fesjs/fes';
    
    defineRouteMeta({
        layout: {
            navigation: null,
        },
    });
    

    当设置为 null 时,页面不使用布局。

    页面缓存

    `,5),q=a(`
    import { defineRouteMete } from '@fesjs/fes';
    
    defineRouteMeta({
        'keep-alive': true,
    });
    

    处理嵌套路由

    Fes.js 里约定目录下有 layout.vue 时会生成嵌套路由,以 layout.vue 为该目录的公共父组件,layout.vue 中必须实现 <RouterView/>。如果嵌套路由下的页面设置了 keep-alive,则需要用 <Page/> 替换 <RouterView/><Page/>实现了页面缓存。

    <template>
        <Page></Page>
    </template>
    <script>
    import { Page } from '@fesjs/fes';
    export default {
        components: {
            Page,
        },
    };
    </script>
    

    配置

    编译时配置方式

    .fes.js 中配置:

    export default {
        layout: {
            // 标题
            title: "Fes.js",
            // 底部文字
            footer: 'Created by MumbleFE',
            // 主题light
            theme: 'dark',
            menus: [{
                name: 'index'
            }, {
                name: 'onepiece'
            }, {
                name: 'store'
            }, {
                name: 'simpleList'
            }],
    
        },
    

    运行时配置方式

    app.js 中配置:

    import UserCenter from '@/components/UserCenter';
    export const layout = {
        renderCustom: () => <UserCenter />,
        menus: [
            {
                name: 'index',
            },
        ],
    };
    

    fes.js中,运行时配置有定义对象和函数两种方式,当使用函数配置layout时,layoutConfig是编译时配置结果,initialStatebeforeRender.action执行后创建的应用初始状态数据。

    export const layout = (layoutConfig, { initialState }) => ({
        renderCustom: () => <UserCenter />,
        menus: () => {
            const menusRef = ref(layoutConfig.menus);
            watch(
                () => initialState.userName,
                () => {
                    menusRef.value = [
                        {
                            name: 'store',
                        },
                    ];
                },
            );
            return menusRef;
        },
    });
    

    最终配置结果是运行时配置跟编译时配置合并的结果,运行时配置优先于编译时配置。

    实际上运行配置能做的事情更多,推荐用运行时配置方式。

    theme

    isFixedHeader

    isFixedSidebar

    title

    `,26),S=n("li",null,[n("p",null,[n("strong",null,"类型"),s(":"),n("code",null,"String")])],-1),T=n("strong",null,"默认值",-1),H=n("li",null,[n("p",null,[n("strong",null,"详情"),s(":产品名。")])],-1),I=a('

    multiTabs

    ',5),P=n("li",null,[n("p",null,[n("strong",null,"类型"),s(":"),n("code",null,"[] | () => Ref<[]> | () => []")])],-1),N=n("li",null,[n("p",null,[n("strong",null,"默认值"),s(":"),n("code",null,"[]")])],-1),V=n("p",null,[n("strong",null,"详情"),s(":菜单配置")],-1),L=n("p",null,"子项具体配置如下:",-1),$=n("strong",null,"name",-1),M=n("code",null,"name",-1),U=n("code",null,"name",-1),z=n("code",null,"title",-1),D=n("code",null,"path",-1),K=a(`
  • path:菜单的路径,可配置第三方地址。

  • match (v4.0.0+):额外匹配的路径,当前路由命中匹配规则时,此菜单高亮。

    {
        path: '/product',
        match: ['/product/*', '/product/create']
    }
    
  • `,2),O=n("p",null,[n("strong",null,"title"),s(":菜单的标题。")],-1),W=n("code",null,"title",-1),G=n("code",null,"$",-1),J=n("code",null,"$",-1),Q=n("li",null,[n("p",null,[s("title支持配置函数,对应 Fes Design 中 Menu 组件的"),n("code",null,"label"),s("插槽。仅在运行时配置中支持。")])],-1),X=n("p",null,[n("strong",null,"icon"),s(": 菜单的图标,只一级标题展示图标。")],-1),Y={href:"https://fes-design-4gvn317r3b6bfe17-1254145788.ap-shanghai.app.tcloudbase.com/zh/components/icon.html",target:"_blank",rel:"noopener noreferrer"},Z=a(`
  • 图标使用本地或者远程 svg 图片。

    {
        icon: '/wine-outline.svg';
    }
    
  • `,1),nn=n("li",null,[n("p",null,[n("strong",null,"children"),s(":子菜单配置。")])],-1),sn=a('

    提示

    函数类型仅在运行时可用,可以实现动态变更菜单。

    sideWidth

    renderCustom

    unAccessHandler

    ',8),an=a("
  • 类型({ to, from, next})=> void

  • 默认值null

  • 详情:仅运行时,当进入某个路由时,如果路由对应的页面不属于可见资源列表,则会暂停进入,调用 unAccessHandler 函数。

  • ",3),en=n("p",null,[n("strong",null,"参数")],-1),tn=n("li",null,"router:createRouter 创建的路由实例",-1),on=n("li",null,"to: 准备进入的路由",-1),pn=n("li",null,"from:离开的路由",-1),ln={href:"https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%8F%AF%E9%80%89%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-next",target:"_blank",rel:"noopener noreferrer"},cn=a(`

    比如:

    export const layout = {
        unAccessHandler({ to, next }) {
            const accesssIds = accessApi.getAccess();
            if (to.path === '/404') {
                accessApi.setAccess(accesssIds.concat(['/404']));
                return next('/404');
            }
            if (!accesssIds.includes('/403')) {
                accessApi.setAccess(accesssIds.concat(['/403']));
            }
            next('/403');
        },
    };
    

    noFoundHandler

    `,3),un=a("
  • 类型({ to, from, next})=> void

  • 默认值null

  • 详情:仅运行时,当进入某个路由时,如果路由对应的页面不存在,则会调用 noFoundHandler 函数。

  • ",3),rn=n("p",null,[n("strong",null,"参数")],-1),dn=n("li",null,"router:createRouter 创建的路由实例",-1),kn=n("li",null,"to: 准备进入的路由",-1),vn=n("li",null,"from:离开的路由",-1),gn={href:"https://next.router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%8F%AF%E9%80%89%E7%9A%84%E7%AC%AC%E4%B8%89%E4%B8%AA%E5%8F%82%E6%95%B0-next",target:"_blank",rel:"noopener noreferrer"},hn=a(`

    比如:

    export const layout = {
        noFoundHandler({ next }) {
            const accesssIds = accessApi.getAccess();
            if (!accesssIds.includes('/404')) {
                accessApi.setAccess(accesssIds.concat(['/404']));
            }
            next('/404');
        },
    };
    

    API

    useTabTitle

    类型定义如下:

    function useTabTitle(title: string | Ref<string>): void;
    

    当使用多页签模式时,在页面中使用 useTabTitle 可以自定义页面标签:

    <script setup>
    import { useRoute, useTabTitle } from '@fesjs/fes';
    
    const titleRef = useTabTitle(\`详情-\${route.params?.id}\`);
    
    //如果要更新
    titleRef.value = "changed"
    </script>
    

    4.x 升级到 5.x

    1. 个性化 layout 配置改为使用传入 navigation
    2. customHeader 改为 renderCustom
    3. fixedHeader 改为 isFixedHeader
    4. menusConfig 改为 menuProps
    5. fixedSideBar 改为 isFixedSidebar
    6. 去掉运行时 logo、header、sidebar 三个区域显示配置,请改为使用 navigation: left-right
    `,10);function mn(p,bn){const t=c("RouterLink"),i=c("ExternalLinkIcon");return r(),d("div",null,[v,g,h,n("ul",null,[m,n("li",null,[s("搭配 "),e(t,{to:"/reference/plugin/plugins/access.html"},{default:o(()=>[s("@fesjs/plugin-access")]),_:1}),s(" 插件使用,可以完成对路由的权限控制。")]),n("li",null,[s("搭配 "),e(t,{to:"/reference/plugin/plugins/locale.html"},{default:o(()=>[s("@fesjs/plugin-locale")]),_:1}),s(" 插件使用,提供切换语言的能力。")]),b,f,_,x]),y,l(" ![side](/side.png) "),n("img",{src:p.$withBase("side.png"),alt:"side"},null,8,j),w,l(" ![top](/top.png) "),n("img",{src:p.$withBase("top.png"),alt:"top"},null,8,A),E,l(" ![mixin](/mixin.png) "),n("img",{src:p.$withBase("mixin.png"),alt:"mixin"},null,8,B),F,l(" ![mixin](/mixin.png) "),n("img",{src:p.$withBase("left-right.png"),alt:"left-right"},null,8,C),R,n("p",null,[s("支持配置页面缓存,通过"),e(t,{to:"/guide/route.html#%E6%89%A9%E5%B1%95%E8%B7%AF%E7%94%B1%E5%85%83%E4%BF%A1%E6%81%AF"},{default:o(()=>[s("定义路由元信息")]),_:1}),s("开启缓存:")]),q,n("ul",null,[S,n("li",null,[n("p",null,[T,s(":默认为 "),e(t,{to:"/reference/config/#title"},{default:o(()=>[s("编译时配置 title")]),_:1})])]),H]),I,n("ul",null,[P,N,n("li",null,[V,L,n("ul",null,[n("li",null,[n("p",null,[$,s(":菜单的名称。通过匹配 "),M,s(" 和路由元信息 "),e(t,{to:"/guide/route.html#%E6%89%A9%E5%B1%95%E8%B7%AF%E7%94%B1%E5%85%83%E4%BF%A1%E6%81%AF"},{default:o(()=>[s("meta")]),_:1}),s(" 中的 "),U,s(",把菜单和路由关联起来,\b 然后使用路由元信息补充菜单配置,比如 "),z,s("、"),D,s(" \b 等。")])]),K,n("li",null,[O,n("ul",null,[n("li",null,[n("p",null,[s("如果同时使用"),e(t,{to:"/reference/plugin/plugins/locale.html"},{default:o(()=>[s("国际化插件")]),_:1}),s(",而且"),W,s("的值以"),G,s("开头,则使用"),J,s("后面的内容去匹配语言设置。")])]),Q])]),n("li",null,[X,n("ul",null,[n("li",null,[n("p",null,[s("图标使用"),n("a",Y,[s("fes-design icon"),e(i)]),s(",编译时配置使用组件名称,我们会自动引入组件。")])]),Z])]),nn])])]),sn,n("ul",null,[an,n("li",null,[en,n("ul",null,[tn,on,pn,n("li",null,[s("next: "),n("a",ln,[s("next 函数"),e(i)])])])])]),cn,n("ul",null,[un,n("li",null,[rn,n("ul",null,[dn,kn,vn,n("li",null,[s("next: "),n("a",gn,[s("next 函数"),e(i)])])])])]),hn])}const _n=u(k,[["render",mn],["__file","layout.html.vue"]]);export{_n as default};