Skip to content

AppLayout ​

AppLayout 是完整的后台外壳:渐变顶栏(搜索、消息、通知、语言、颜色模式、用户菜单)、带激活路径的多级侧边菜单、用户资料、设置面板、页面标题和页脚。通过 config.layout 在 vertical、mini 和 horizontal 之间切换。

预览 ​

预览
vue
<div class="layout-frame"><iframe src="http://localhost:5182/" title="Vue playground" loading="lazy"></iframe><p class="layout-frame__hint">Live Vue playground (run <code>npm run dev:vue</code>). Open the ⚙ settings panel to switch skin, layout, dark mode and language.</p></div>
tsx
import { AppLayout, Page } from '@thl-ui/react';
import { Link, useLocation } from 'react-router';

<AppLayout menu={menu} currentPath={loc.pathname} user={{ ...user, onSelect: (k) => k === 'logout' && logout() }}
  onSearch={(q) => nav(`/search?q=${q}`)}
  renderLink={(item, children, className) => <Link to={item.href!} className={className}>{children}</Link>}
  topbarRight={<TopbarMessages items={messages} icon="mail" />}
  sidebarFooter={<Button variant="success" block>Upgrade</Button>}>
  <Page title="Dashboard" breadcrumb={[{ label: 'Home', href: '/' }, { label: 'Dashboard' }]}>…</Page>
</AppLayout>
html
<thl-app-layout [menu]="menu" [currentPath]="path()" [user]="user" searchable (search)="search($event)" (userSelect)="onUser($event)">
  <ng-template thlMenuLink let-item let-cls="cls" let-content="content">
    <a [routerLink]="item.href" [class]="cls"><ng-container [ngTemplateOutlet]="content" /></a>
  </ng-template>
  <ng-container thlTopbarRight><thl-topbar-messages [items]="messages" icon="mail" /></ng-container>
  <div thlSidebarFooter><button thl-button variant="success" block>Upgrade</button></div>
  <router-outlet />
</thl-app-layout>

Vue ​

vue
<ThlAppLayout :menu="menu" :current-path="route.path" :user="user" searchable @search="onSearch" @user-select="onUser">
  <template #topbar-right><ThlTopbarMessages :items="messages" icon="mail" /></template>
  <template #link="{ item, content, class: cls }"><RouterLink :to="item.href" :class="cls"><component :is="() => content" /></RouterLink></template>
  <template #sidebar-footer><ThlButton variant="success" block>升级</ThlButton></template>
  <ThlPage title="Dashboard" :breadcrumb="[{ label: '首页', href: '/' }, { label: 'Dashboard' }]">…</ThlPage>
</ThlAppLayout>

菜单 ​

ts
import type { MenuItem } from '@thl-ui/core';

export const menu: MenuItem[] = [
  { key: 'dash', label: 'menu.dashboard', icon: 'layout-dashboard', badge: { text: 4, color: 'danger' }, children: [
    { key: 'd1', label: 'menu.dashboard1', href: '/' },
    { key: 'd2', label: 'menu.dashboard2', href: '/dashboard-2' },
  ] },
  { key: 'h', label: 'menu.apps', heading: true },
  { key: 'inbox', label: 'menu.inbox', icon: 'mail', href: '/inbox' },
];

label 可以是 i18n key(自动翻译)或普通字符串。激活路径根据 currentPath 计算。

横向布局 ​

设置 layout: 'horizontal' 后,菜单从侧边移到顶栏下方的横条中(与 Cubic-horizontal 相同)。子菜单在鼠标悬停或键盘聚焦时以下拉方式展开。菜单始终保持一行:放不下的项目会收进 更多 下拉菜单,窗口变宽后再回到横条中。小于 768px 时恢复为普通的抽屉式侧边栏。同一个 menu 数组适用于所有布局。

tsx
// main.tsx: 以横向模式启动
<ThlProvider config={{ layout: 'horizontal' }}>
  <AppLayout menu={menu} currentPath={loc.pathname} renderLink={renderLink}>…</AppLayout>
</ThlProvider>

// 运行时切换(⚙ 设置面板也是这样做的)
const [, setConfig] = useThlConfig();
setConfig({ layout: 'horizontal' });
ts
// main.ts: 以横向模式启动
app.use(ThlUI, { layout: 'horizontal' });

// 运行时切换(⚙ 设置面板也是这样做的)
const { set } = useThlConfig();
set({ layout: 'horizontal' });
ts
// main.ts: 以横向模式启动
bootstrapApplication(AppComponent, { providers: [provideThlUI({ layout: 'horizontal' })] });

// 运行时切换(⚙ 设置面板也是这样做的)
inject(ThlConfigService).set({ layout: 'horizontal' });

在 playground 中试用:localhost:5182/?layout=horizontal。playground 还支持 ?layout=mini、?theme=red-dark、?lang=ja 和 ?mode=dark。

响应式行为 ​

  • ≥ 1170px:完整侧边栏(或根据 layout 显示 mini / 横向)
  • 768 – 1169px:自动切换为 mini 侧边栏
  • < 768px:带遮罩的抽屉式(off-canvas)侧边栏,从顶栏切换

断点可通过 components.Sidebar.breakpoint 配置。

API ​

AppLayout

<ThlAppLayout> · import { ThlAppLayout } from '@thl-ui/vue'

Prop类型默认值
menu*array—
currentPathstring—
brandstring"thl-ui"
brandHrefstring"/"
userobject—
searchableboolean—
settingsbooleantrue
footerstring | boolean—

事件: searchuser-select

插槽: brandtopbar-lefttopbar-rightlinksidebar-footerdefaultfooter

SideMenu

<ThlSideMenu> · import { ThlSideMenu } from '@thl-ui/vue'

Prop类型默认值
items*array—
currentPathstring—
accordionbooleantrue
horizontalboolean—

插槽: link

TopbarMessages

<ThlTopbarMessages> · import { ThlTopbarMessages } from '@thl-ui/vue'

Prop类型默认值
items*array—
iconstring"mail"
titlestring—
footerstring—
footerHrefstring—

SettingsPanel

<ThlSettingsPanel> · import { ThlSettingsPanel } from '@thl-ui/vue'

Prop类型默认值
openboolean—

事件: update:open

Page

<ThlPage> · import { ThlPage } from '@thl-ui/vue'

Prop类型默认值
titlestring—
breadcrumbarray—

插槽: actionslinkdefault