基础

布局

用于快捷划分页面整体布局

概述

  • Layout:布局容器,其下可嵌套 Header Sider Content Footer 或 Layout 本身,可以放在任何父容器中。
  • Header:顶部布局,其下可嵌套任何元素,只能放在 Layout 中。
  • Sider:侧边栏,其下可嵌套任何元素,只能放在 Layout 中。
  • Content:内容部分,其下可嵌套任何元素,只能放在 Layout 中。
  • Footer:底部布局,其下可嵌套任何元素,只能放在 Layout 中。

代码演示

如何引入

<script setup lang="ts">
    import { Layout } from '@fibrous/semi-ui-vue';
</script>

子组件通过 Layout.Header、Layout.Sider、Layout.Content、Layout.Footer 使用,也可以按名导入 LayoutHeader、LayoutSider、LayoutContent、LayoutFooter。

三行布局

Header
Content
Footer
代码
<script setup lang="ts">
    import { Layout } from '@fibrous/semi-ui-vue';

    const commonStyle = {
        height: '64px',
        lineHeight: '64px',
        background: 'var(--semi-color-fill-0)',
    };
</script>

<template>
    <Layout class="components-layout-demo">
        <Layout.Header :style="commonStyle">Header</Layout.Header>
        <Layout.Content :style="{ height: '300px', lineHeight: '300px' }">Content</Layout.Content>
        <Layout.Footer :style="commonStyle">Footer</Layout.Footer>
    </Layout>
</template>

左侧边栏布局

Header
Content
Footer
代码
<script setup lang="ts">
    import { Layout } from '@fibrous/semi-ui-vue';

    const commonStyle = {
        height: '64px',
        lineHeight: '64px',
        background: 'var(--semi-color-fill-0)',
    };
</script>

<template>
    <Layout class="components-layout-demo">
        <Layout.Header :style="commonStyle">Header</Layout.Header>
        <Layout>
            <Layout.Sider :style="{ width: '120px', background: 'var(--semi-color-fill-2)' }">Sider</Layout.Sider>
            <Layout.Content :style="{ height: '300px', lineHeight: '300px' }">Content</Layout.Content>
        </Layout>
        <Layout.Footer :style="commonStyle">Footer</Layout.Footer>
    </Layout>
</template>

右侧边栏布局

Header
Content
Footer
代码
<script setup lang="ts">
    import { Layout } from '@fibrous/semi-ui-vue';

    const commonStyle = {
        height: '64px',
        lineHeight: '64px',
        background: 'var(--semi-color-fill-0)',
    };
</script>

<template>
    <Layout class="components-layout-demo">
        <Layout.Header :style="commonStyle">Header</Layout.Header>
        <Layout>
            <Layout.Content :style="{ height: '300px', lineHeight: '300px' }">Content</Layout.Content>
            <Layout.Sider :style="{ width: '120px', background: 'var(--semi-color-fill-2)' }">Sider</Layout.Sider>
        </Layout>
        <Layout.Footer :style="commonStyle">Footer</Layout.Footer>
    </Layout>
</template>

侧边栏布局

Header
Content
Footer
代码
<script setup lang="ts">
    import { Layout } from '@fibrous/semi-ui-vue';

    const commonStyle = {
        height: '64px',
        lineHeight: '64px',
        background: 'var(--semi-color-fill-0)',
    };
</script>

<template>
    <Layout class="components-layout-demo">
        <Layout.Sider :style="{ width: '120px', background: 'var(--semi-color-fill-2)' }">Sider</Layout.Sider>
        <Layout>
            <Layout.Header :style="commonStyle">Header</Layout.Header>
            <Layout.Content :style="{ height: '300px', lineHeight: '300px' }">Content</Layout.Content>
            <Layout.Footer :style="commonStyle">Footer</Layout.Footer>
        </Layout>
    </Layout>
</template>

响应式布局

侧边栏预设了六个响应尺寸:xs、sm、md、lg、xl、xxl。可以通过设置 breakpoint 属性设置断点。

通过 @breakpoint 监听断点命中状态的变化。

Header
Content
Footer
代码
<script setup lang="ts">
    import { Layout } from '@fibrous/semi-ui-vue';

    const commonStyle = {
        height: '64px',
        lineHeight: '64px',
        background: 'var(--semi-color-fill-0)',
    };
</script>

<template>
    <Layout class="components-layout-demo">
        <Layout.Header :style="commonStyle">Header</Layout.Header>
        <Layout>
            <Layout.Sider :breakpoint="['md']" :style="{ width: '120px', background: 'var(--semi-color-fill-2)' }" @breakpoint="(screen, matches) => console.log(screen, matches)"> Sider </Layout.Sider>
            <Layout.Content :style="{ height: '300px', lineHeight: '300px' }">Content</Layout.Content>
        </Layout>
        <Layout.Footer :style="commonStyle">Footer</Layout.Footer>
    </Layout>
</template>

布局示例

顶部导航布局

Copyright © 2023 ByteDance. All Rights Reserved. 平台客服反馈建议
代码
<script setup lang="ts">
    import { IconBell, IconBytedanceLogo, IconHelpCircle, IconHome, IconLive, IconSemiLogo, IconSetting } from '@fibrous/semi-icons-vue';
    import { Avatar, Breadcrumb, Button, Layout, Nav, Skeleton } from '@fibrous/semi-ui-vue';

    const routes = ['首页', '当这个页面标题很长时需要省略', '上一页', '详情页'];
    const actionStyle = { color: 'var(--semi-color-text-2)', marginRight: '12px' };
</script>

<template>
    <Layout :style="{ border: '1px solid var(--semi-color-border)' }">
        <Layout.Header :style="{ backgroundColor: 'var(--semi-color-bg-1)' }">
            <div>
                <Nav :defaultSelectedKeys="['Home']" mode="horizontal">
                    <Nav.Header>
                        <IconSemiLogo :style="{ fontSize: '36px' }" />
                    </Nav.Header>
                    <Nav.Item itemKey="Home" text="首页">
                        <template #icon>
                            <IconHome size="large" />
                        </template>
                    </Nav.Item>
                    <Nav.Item itemKey="Live" text="直播">
                        <template #icon>
                            <IconLive size="large" />
                        </template>
                    </Nav.Item>
                    <Nav.Item itemKey="Setting" text="设置">
                        <template #icon>
                            <IconSetting size="large" />
                        </template>
                    </Nav.Item>
                    <Nav.Footer>
                        <Button aria-label="通知" :style="actionStyle" theme="borderless">
                            <template #icon>
                                <IconBell size="large" />
                            </template>
                        </Button>
                        <Button aria-label="帮助" :style="actionStyle" theme="borderless">
                            <template #icon>
                                <IconHelpCircle size="large" />
                            </template>
                        </Button>
                        <Avatar color="orange" size="small">YJ</Avatar>
                    </Nav.Footer>
                </Nav>
            </div>
        </Layout.Header>
        <Layout.Content :style="{ padding: '24px', backgroundColor: 'var(--semi-color-bg-0)' }">
            <Breadcrumb :routes="routes" :style="{ marginBottom: '24px' }" />
            <div
                :style="{
                    borderRadius: '10px',
                    border: '1px solid var(--semi-color-border)',
                    height: '376px',
                    padding: '32px',
                }"
            >
                <Skeleton loading>
                    <template #placeholder>
                        <Skeleton.Paragraph :rows="2" />
                    </template>
                    <p>Hi, Bytedance dance dance.</p>
                    <p>Hi, Bytedance dance dance.</p>
                </Skeleton>
            </div>
        </Layout.Content>
        <Layout.Footer
            :style="{
                display: 'flex',
                justifyContent: 'space-between',
                padding: '20px',
                color: 'var(--semi-color-text-2)',
                backgroundColor: 'var(--semi-grey-0)',
            }"
        >
            <span :style="{ display: 'flex', alignItems: 'center' }">
                <IconBytedanceLogo size="large" :style="{ marginRight: '8px' }" />
                <span>Copyright © 2023 ByteDance. All Rights Reserved. </span>
            </span>
            <span>
                <span :style="{ marginRight: '24px' }">平台客服</span>
                <span>反馈建议</span>
            </span>
        </Layout.Footer>
    </Layout>
</template>

顶部导航-侧边布局

Copyright © 2023 ByteDance. All Rights Reserved. 平台客服反馈建议
代码
<script setup lang="ts">
    import { IconBell, IconBytedanceLogo, IconHelpCircle, IconHistogram, IconHome, IconLive, IconSemiLogo, IconSetting } from '@fibrous/semi-icons-vue';
    import { Avatar, Breadcrumb, Button, Layout, Nav, Skeleton, type NavItemConfig, type NavItems } from '@fibrous/semi-ui-vue';

    const items: NavItems = [
        { itemKey: 'Home', text: '首页' },
        { itemKey: 'Histogram', text: '基础数据' },
        { itemKey: 'Live', text: '测试功能' },
        { itemKey: 'Setting', text: '设置' },
    ];
    const icons = { Home: IconHome, Histogram: IconHistogram, Live: IconLive, Setting: IconSetting };
    const iconOf = (item: NavItemConfig) => icons[item.itemKey as keyof typeof icons];
    const routes = ['首页', '当这个页面标题很长时需要省略', '上一页', '详情页'];
    const actionStyle = { color: 'var(--semi-color-text-2)', marginRight: '12px' };
</script>

<template>
    <Layout :style="{ border: '1px solid var(--semi-color-border)' }">
        <Layout.Header :style="{ backgroundColor: 'var(--semi-color-bg-1)' }">
            <div>
                <Nav aria-label="顶部导航" :defaultSelectedKeys="['Home']" mode="horizontal">
                    <Nav.Header>
                        <IconSemiLogo :style="{ height: '36px', fontSize: '36px' }" />
                    </Nav.Header>
                    <span :style="{ color: 'var(--semi-color-text-2)' }">
                        <span :style="{ marginRight: '24px', color: 'var(--semi-color-text-0)', fontWeight: '600' }"> 模版推荐 </span>
                        <span :style="{ marginRight: '24px' }">所有模版</span>
                        <span>我的模版</span>
                    </span>
                    <Nav.Footer>
                        <Button aria-label="通知" :style="actionStyle" theme="borderless">
                            <template #icon>
                                <IconBell size="large" />
                            </template>
                        </Button>
                        <Button aria-label="帮助" :style="actionStyle" theme="borderless">
                            <template #icon>
                                <IconHelpCircle size="large" />
                            </template>
                        </Button>
                        <Avatar color="orange" size="small">YJ</Avatar>
                    </Nav.Footer>
                </Nav>
            </div>
        </Layout.Header>
        <Layout>
            <Layout.Sider :style="{ backgroundColor: 'var(--semi-color-bg-1)' }">
                <Nav aria-label="侧边导航" :defaultSelectedKeys="['Home']" :footer="{ collapseButton: true }" :items="items" :style="{ maxWidth: '220px', height: '100%' }">
                    <template #icon="{ item }">
                        <component :is="iconOf(item)" size="large" />
                    </template>
                </Nav>
            </Layout.Sider>
            <Layout.Content :style="{ padding: '24px', backgroundColor: 'var(--semi-color-bg-0)' }">
                <Breadcrumb :routes="routes" :style="{ marginBottom: '24px' }" />
                <div
                    :style="{
                        borderRadius: '10px',
                        border: '1px solid var(--semi-color-border)',
                        height: '376px',
                        padding: '32px',
                    }"
                >
                    <Skeleton loading>
                        <template #placeholder>
                            <Skeleton.Paragraph :rows="2" />
                        </template>
                        <p>Hi, Bytedance dance dance.</p>
                        <p>Hi, Bytedance dance dance.</p>
                    </Skeleton>
                </div>
            </Layout.Content>
        </Layout>
        <Layout.Footer
            :style="{
                display: 'flex',
                justifyContent: 'space-between',
                padding: '20px',
                color: 'var(--semi-color-text-2)',
                backgroundColor: 'var(--semi-grey-0)',
            }"
        >
            <span :style="{ display: 'flex', alignItems: 'center' }">
                <IconBytedanceLogo size="large" :style="{ marginRight: '8px' }" />
                <span>Copyright © 2023 ByteDance. All Rights Reserved. </span>
            </span>
            <span>
                <span :style="{ marginRight: '24px' }">平台客服</span>
                <span>反馈建议</span>
            </span>
        </Layout.Footer>
    </Layout>
</template>

侧边导航

Copyright © 2019 ByteDance. All Rights Reserved. 平台客服反馈建议
代码
<script setup lang="ts">
    import { IconBell, IconBytedanceLogo, IconHelpCircle, IconHistogram, IconHome, IconLive, IconSemiLogo, IconSetting } from '@fibrous/semi-icons-vue';
    import { Avatar, Breadcrumb, Button, Layout, Nav, Skeleton, type NavItemConfig, type NavItems } from '@fibrous/semi-ui-vue';

    const items: NavItems = [
        { itemKey: 'Home', text: '首页' },
        { itemKey: 'Histogram', text: '基础数据' },
        { itemKey: 'Live', text: '测试功能' },
        { itemKey: 'Setting', text: '设置' },
    ];
    const icons = { Home: IconHome, Histogram: IconHistogram, Live: IconLive, Setting: IconSetting };
    const iconOf = (item: NavItemConfig) => icons[item.itemKey as keyof typeof icons];
    const routes = ['首页', '当这个页面标题很长时需要省略', '上一页', '详情页'];
    const actionStyle = { color: 'var(--semi-color-text-2)', marginRight: '12px' };
</script>

<template>
    <Layout :style="{ border: '1px solid var(--semi-color-border)' }">
        <Layout.Sider :style="{ backgroundColor: 'var(--semi-color-bg-1)' }">
            <Nav aria-label="侧边导航" :defaultSelectedKeys="['Home']" :footer="{ collapseButton: true }" :items="items" :style="{ maxWidth: '220px', height: '100%' }">
                <template #icon="{ item }">
                    <component :is="iconOf(item)" size="large" />
                </template>
                <Nav.Header text="Semi Design">
                    <template #logo>
                        <IconSemiLogo :style="{ fontSize: '36px' }" />
                    </template>
                </Nav.Header>
            </Nav>
        </Layout.Sider>
        <Layout>
            <Layout.Header :style="{ backgroundColor: 'var(--semi-color-bg-1)' }">
                <Nav aria-label="顶部导航" mode="horizontal">
                    <template #footer>
                        <Button aria-label="通知" :style="actionStyle" theme="borderless">
                            <template #icon>
                                <IconBell size="large" />
                            </template>
                        </Button>
                        <Button aria-label="帮助" :style="actionStyle" theme="borderless">
                            <template #icon>
                                <IconHelpCircle size="large" />
                            </template>
                        </Button>
                        <Avatar color="orange" size="small">YJ</Avatar>
                    </template>
                </Nav>
            </Layout.Header>
            <Layout.Content :style="{ padding: '24px', backgroundColor: 'var(--semi-color-bg-0)' }">
                <Breadcrumb :routes="routes" :style="{ marginBottom: '24px' }" />
                <div
                    :style="{
                        borderRadius: '10px',
                        border: '1px solid var(--semi-color-border)',
                        height: '376px',
                        padding: '32px',
                    }"
                >
                    <Skeleton loading>
                        <template #placeholder>
                            <Skeleton.Paragraph :rows="2" />
                        </template>
                        <p>Hi, Bytedance dance dance.</p>
                        <p>Hi, Bytedance dance dance.</p>
                    </Skeleton>
                </div>
            </Layout.Content>
            <Layout.Footer
                :style="{
                    display: 'flex',
                    justifyContent: 'space-between',
                    padding: '20px',
                    color: 'var(--semi-color-text-2)',
                    backgroundColor: 'var(--semi-grey-0)',
                }"
            >
                <span :style="{ display: 'flex', alignItems: 'center' }">
                    <IconBytedanceLogo size="large" :style="{ marginRight: '8px' }" />
                    <span>Copyright © 2019 ByteDance. All Rights Reserved. </span>
                </span>
                <span>
                    <span :style="{ marginRight: '24px' }">平台客服</span>
                    <span>反馈建议</span>
                </span>
            </Layout.Footer>
        </Layout>
    </Layout>
</template>

API 参考

Layout

Props

属性说明类型默认值
prefixCls类名前缀。stringsemiDefault("Layout", "prefixCls", cssClasses.PREFIX)
tagName根元素的标签名。keyof HTMLElementTagNameMapsemiDefault("Layout", "tagName", "section")
hasSider表示子元素里有 Sider。直接子节点中的 Sider 会自动识别;嵌套更深的 Sider 在挂载后才登记,服务端渲染时传 true 可避免样式闪动。传 false 不能去掉 Sider 带来的 has-sider 类。booleansemiGlobalDefault("Layout", "hasSider")

Events

事件说明回调参数
无

Slots

插槽说明作用域参数
defaultLayout 的内容,可放 Layout.Header、Layout.Sider、Layout.Content、Layout.Footer 或嵌套的 Layout。any

Exposes

名称说明类型
无

class、style 与原生属性(含 role、aria-*)经 attrs 落在根元素上。

Layout.Header、Layout.Footer、Layout.Content

三者的 API 相同,默认标签依次为 header、footer、main。

Layout.Header

Props

属性说明类型默认值
prefixCls类名前缀。stringcssClasses.PREFIX
tagName根元素的标签名。keyof HTMLElementTagNameMap"header"
type内部使用,决定类名后缀。string"header"

Events

事件说明回调参数
无

Slots

插槽说明作用域参数
default顶部区域的内容。any

Exposes

名称说明类型
无

Props

属性说明类型默认值
prefixCls类名前缀。stringcssClasses.PREFIX
tagName根元素的标签名。keyof HTMLElementTagNameMap"footer"
type内部使用,决定类名后缀。string"footer"

Events

事件说明回调参数
无

Slots

插槽说明作用域参数
default底部区域的内容。any

Exposes

名称说明类型
无

Layout.Content

Props

属性说明类型默认值
prefixCls类名前缀。stringcssClasses.PREFIX
tagName根元素的标签名。keyof HTMLElementTagNameMap"main"
type内部使用,决定类名后缀。string"content"

Events

事件说明回调参数
无

Slots

插槽说明作用域参数
default内容区域的内容。any

Exposes

名称说明类型
无

class、style 与原生属性(含 role、aria-*)经 attrs 落在各自的根元素上。

Layout.Sider

Props

属性说明类型默认值
prefixCls类名前缀。stringcssClasses.PREFIX
breakpoint触发响应式布局的断点,可选 xs、sm、md、lg、xl、xxl。仅在挂载时读取一次。Breakpoint[]undefined

Events

事件说明回调参数
breakpoint挂载时对 breakpoint 中的每个断点按当前视口触发一次,之后在命中或离开时触发,参数为断点名与是否命中。[screen: Breakpoint, match: boolean]

Slots

插槽说明作用域参数
default侧边栏的内容。any

Exposes

名称说明类型
无

class、style 与原生属性(含 role、aria-*)经 attrs 落在根元素 aside 上。

responsive map

{
    xs: '(max-width: 575px)',
    sm: '(min-width: 576px)',
    md: '(min-width: 768px)',
    lg: '(min-width: 992px)',
    xl: '(min-width: 1200px)',
    xxl: '(min-width: 1600px)',
};

Accessibility

ARIA

  • Sider 渲染 aside,可传入 aria-label 描述该 Sider 的作用。
  • Header、Footer 默认渲染 header、footer,位于 Layout 的 section 内时不会成为 banner、contentinfo 地标;作为整页的页头、页脚时传入 role="banner"、role="contentinfo"。
  • Content 默认渲染 main,一个页面只应有一个 main;嵌套使用时可通过 tagName 换成其他标签。
  • Header、Content、Footer 可传入 role、aria-label 描述对应元素的作用。