Skyroc Admin React
布局系统

布局系统

理解 apps/admin 的后台主布局如何通过 WebAdminLayout、Logo、页脚、通知和用户头像插槽组装

这一页解释 apps/admin 的后台主布局如何组装。重点不是复述每个视觉组件,而是判断布局能力应该放在应用侧、共享布局包,还是主题包中。

当前后台布局入口是 apps/admin/src/pages/(admin)/layout.tsx。它作为 TanStack Router 的后台 layout route,负责进入后台前的守卫,并把应用自己的 Logo、页脚、通知按钮和用户头像插入 @skyroc/web-admin-layouts 提供的后台壳。

适用场景

场景重点位置
修改后台 Header、Sider、Tab、Content、Footer 的组合方式apps/admin/src/pages/(admin)/layout.tsxpackages/web/admin-layouts/src/AdminLayout.tsx
替换系统 Logo 或标题SystemLogologologoTitlelogoTo
接入通知、用户头像或业务操作区headerLeftActionsheaderMiddleActionsheaderRightActions
修改页脚内容footer slot、AdminFooter
判断某个布局逻辑应不应该写进 apps/admin@skyroc/web-admin-layouts 的 slot 和运行时状态边界
排查后台页面进入前的登录、权限或外链行为beforeLoadguardAdminRoute

当前实现位置

文件职责
apps/admin/src/pages/(admin)/layout.tsx后台 layout route。渲染 WebAdminLayout,接入 Logo、footer、通知、头像,并执行后台守卫。
apps/admin/src/components/SystemLogo应用自己的品牌图标。
apps/admin/src/features/auth/components/UserAvatarHeader 右侧用户头像和用户操作入口。
packages/web/admin-layouts/src/AdminLayout.tsx共享后台壳。组合 Header、Sider、Tab、Content、Footer、菜单、主题抽屉和布局副作用。
packages/web/admin-layouts/src/context.tsx定义 AdminLayout slot API,例如 Logo、Header actions、footer 和 content。
packages/web/admin-layouts/src/modules/AdminContent.tsx内容区。默认渲染 TanStack Router 的 <Outlet />
packages/web/admin-layouts/src/modules/admin-header/AdminHeader.tsxHeader 组合。渲染搜索、全屏、语言、主题按钮和应用注入的 actions。
apps/admin/src/features/router/guard.ts后台 layout route 的登录、权限、动态授权和外链守卫。

核心概念

(admin)/layout.tsx 是后台壳边界

后台页面都挂在 apps/admin/src/pages/(admin) 下。这个目录里的 layout.tsx 不是普通页面,而是后台应用壳的入口:

import { AdminLayout as WebAdminLayout } from '@skyroc/web-admin-layouts';
import { NotificationButton } from '@skyroc/web-admin-notification';
import { DarkModeContainer } from '@skyroc/web-ui-compose';
import { createFileRoute } from '@tanstack/react-router';
import { useTranslation } from 'react-i18next';

import SystemLogo from '@/components/SystemLogo';
import UserAvatar from '@/features/auth/components/UserAvatar';
import { guardAdminRoute } from '@/features/router/guard';

const AdminFooter = () => {
  return (
    <DarkModeContainer className="h-full flex-center">
      <a href="https://github.com/Ohh-889/skyroc-admin/blob/main/LICENSE" rel="noopener noreferrer" target="_blank">
        Copyright MIT © 2021 Skyroc
      </a>
    </DarkModeContainer>
  );
};

const AdminLayout = () => {
  const { t } = useTranslation();

  return (
    <WebAdminLayout
      footer={<AdminFooter />}
      headerMiddleActions={<NotificationButton className="px-12px" />}
      headerRightActions={<UserAvatar />}
      logo={<SystemLogo className="text-32px text-primary" />}
      logoTitle={t('system.title')}
    />
  );
};

export const Route = createFileRoute('/(admin)')({
  component: AdminLayout,
  beforeLoad: async ({ context, location, matches, preload }) => {
    await guardAdminRoute({ context, location, matches, preload });
  }
});

这里有两个职责:

职责说明
后台守卫beforeLoad 统一调用 guardAdminRoute,所以后台页面不需要重复写登录和权限判断。
应用插槽apps/admin 只注入业务相关 slot:Logo、标题、页脚、通知按钮和用户头像。

不要把菜单生成、tabs 状态、主题抽屉、响应式布局和侧边栏计算写进这个文件。它们属于共享后台壳或主题系统。

WebAdminLayout 负责完整后台框架

WebAdminLayout@skyroc/web-admin-layouts 导出的 AdminLayout。它内部再组合底层材料布局和后台运行时模块:

WebAdminLayout
  -> AdminLayoutProvider
    -> @skyroc/materials/AdminLayout
      -> AdminHeader
      -> AdminSider
      -> AdminTab
      -> AdminContent
      -> AdminMenu
      -> ThemeDrawer
      -> AdminEffect

这些模块的职责是:

模块责任
AdminHeader根据主题布局模式决定是否显示 Logo、菜单、折叠按钮、面包屑,并插入 header actions。
AdminSider渲染侧边栏、一级菜单和混合布局下的子级菜单区域。
AdminTab根据当前路由和菜单索引创建、激活、关闭、固定和缓存 tabs。
AdminContent渲染内容区,默认使用 TanStack Router 的 <Outlet />
AdminMenu在不同布局模式下把菜单挂到 Header 或 Sider 的目标区域。
ThemeDrawer渲染主题抽屉,读取 @skyroc/web-admin-theme 的主题设置。
AdminEffect处理移动端布局切换、mix 菜单固定缓存和 tabs 缓存副作用。

apps/admin 不直接关心这些模块的内部布局分支。修改“应用展示什么业务入口”时改 slot;修改“后台壳如何根据主题和路由运行”时才进入 packages/web/admin-layouts

Slot API 按位置命名

AdminLayout 提供的主要 slot 是位置型 API:

Slot当前使用说明
logoSystemLogo交给默认 Logo 组件渲染的品牌图标。
logoTitlet('system.title')品牌标题,随 i18n 变化。
logoTo未显式传入不传时跳转到 setupAdminLayouts 配置的 defaultHome
logoComponent当前未使用自定义完整 Logo,可以直接接管图标、标题和布局样式。
footerAdminFooter页脚区域;是否显示和高度由主题设置控制。
content当前未使用不传时内容区默认渲染 <Outlet />
headerLeftActions当前未使用位于搜索和全屏按钮之间。
headerMiddleActionsNotificationButton位于语言切换和主题模式按钮之间。
headerRightActionsUserAvatar位于所有内置操作之后。

slot 命名不带业务含义。共享包只知道它们的位置,具体放通知、用户、帮助入口还是其他业务组件,由 apps/admin 决定。

内容区默认来自路由 Outlet

AdminContent 的默认行为是渲染 TanStack Router 的 <Outlet />

const GlobalContent = () => {
  const { content } = useAdminLayoutContext();

  return (
    <div className="h-full grow bg-layout p-16px">
      {content ?? <Outlet />}
    </div>
  );
};

因此,普通后台页面只需要放在 pages/(admin) 下,不需要手动把页面传给布局。只有在嵌入特殊内容壳、预览模式或测试布局时,才考虑传 content slot。

主题系统决定布局模式

WebAdminLayout 读取 @skyroc/web-admin-theme 的主题设置来决定当前布局模式、Header 高度、Footer 显示、Sider 宽度、Tab 高度和是否固定顶部区域。

当前布局模式包括 vertical、horizontal、vertical-mix、top-hybrid、vertical-hybrid 等。apps/admin/src/pages/(admin)/layout.tsx 不需要为每个模式写条件分支;共享布局包会根据主题设置计算 Header、Sider、Tab 和菜单挂载位置。

最小可用示例

在 Header 中间区域增加一个帮助入口

如果要在语言切换和主题按钮之间增加应用级入口,可以在 headerMiddleActions 中组合多个节点:

const AdminLayout = () => {
  const { t } = useTranslation();

  return (
    <WebAdminLayout
      footer={<AdminFooter />}
      headerMiddleActions={
        <>
          <HelpCenterButton />
          <NotificationButton className="px-12px" />
        </>
      }
      headerRightActions={<UserAvatar />}
      logo={<SystemLogo className="text-32px text-primary" />}
      logoTitle={t('system.title')}
    />
  );
};

判断标准:这是应用业务入口,应该留在 apps/admin 的 layout route;不是共享后台壳的通用行为。

替换完整 Logo 渲染

默认 Logo 组件适合“图标 + 标题 + 默认跳转”。如果某个应用需要完整接管样式,可以使用 logoComponent

const AdminLayout = () => {
  const { t } = useTranslation();

  return (
    <WebAdminLayout
      headerMiddleActions={<NotificationButton className="px-12px" />}
      headerRightActions={<UserAvatar />}
      logoComponent={style => (
        <CustomBrandLogo
          logo={<SystemLogo className="text-32px text-primary" />}
          style={style}
          title={t('system.title')}
        />
      )}
    />
  );
};

logoComponent 可以拿到不同布局位置传入的 style,适合 Header、Sider、混合布局下 Logo 宽度不一致的场景。

使用默认内容区

新增后台页面时不需要改 layout:

import { createFileRoute } from '@tanstack/react-router';

const ReportCenter = () => {
  return <div>Report Center</div>;
};

export const Route = createFileRoute('/(admin)/report/center')({
  component: ReportCenter,
  staticData: {
    title: 'report_center',
    i18nKey: 'route.report_center',
    menu: {
      icon: 'mdi:file-chart-outline',
      order: 12
    }
  }
});

页面会自动进入 (admin) layout,渲染到 AdminContent<Outlet /> 中,并经过后台守卫、菜单、tabs 和权限链路。

常见误区

误区正确做法
在每个后台页面里重复包一层主布局后台页面放在 (admin) 下即可,统一由 (admin)/layout.tsx 提供主布局。
把通知、头像、业务按钮写进 @skyroc/web-admin-layouts这些是应用业务 slot,应该留在 apps/admin
(admin)/layout.tsx 中直接生成菜单或控制 tabs菜单和 tabs 由 setupAdminLayoutsuseMenususeAdminMenususeAdminTab 管理。
为每种布局模式在应用侧写 Header/Sider 分支布局模式来自主题设置,分支应在共享布局包中收口。
修改 Logo 点击行为时硬编码跳转优先传 logoTo,不传时使用 defaultHome
复制旧 Vue 文档里的 layout、Pinia 或 NaiveUI 结构当前实现以 React、TanStack Router、Jotai、Ant Design 和 Skyroc workspace packages 为准。

相关链接

内容位置
启动时如何初始化布局配置启动流程
菜单、动态菜单和 tabs菜单与标签页
路由如何进入后台 layout路由概览
路由元信息如何影响菜单和 tabs路由元信息
登录守卫路由守卫
权限和 403权限
共享布局包完整 APIdocs/web-kit-docs/content/docs/admin-layouts/*

On this page