examples.md•24.2 kB
## Layout 组件示例
### 基本结构
典型的页面布局。
```tsx
import React from 'react';
import { Flex, Layout } from 'antd';
const { Header, Footer, Sider, Content } = Layout;
const headerStyle: React.CSSProperties = {
textAlign: 'center',
color: '#fff',
height: 64,
paddingInline: 48,
lineHeight: '64px',
backgroundColor: '#4096ff',
};
const contentStyle: React.CSSProperties = {
textAlign: 'center',
minHeight: 120,
lineHeight: '120px',
color: '#fff',
backgroundColor: '#0958d9',
};
const siderStyle: React.CSSProperties = {
textAlign: 'center',
lineHeight: '120px',
color: '#fff',
backgroundColor: '#1677ff',
};
const footerStyle: React.CSSProperties = {
textAlign: 'center',
color: '#fff',
backgroundColor: '#4096ff',
};
const layoutStyle = {
borderRadius: 8,
overflow: 'hidden',
width: 'calc(50% - 8px)',
maxWidth: 'calc(50% - 8px)',
};
const App: React.FC = () => (
<Flex gap="middle" wrap>
<Layout style={layoutStyle}>
<Header style={headerStyle}>Header</Header>
<Content style={contentStyle}>Content</Content>
<Footer style={footerStyle}>Footer</Footer>
</Layout>
<Layout style={layoutStyle}>
<Header style={headerStyle}>Header</Header>
<Layout>
<Sider width="25%" style={siderStyle}>
Sider
</Sider>
<Content style={contentStyle}>Content</Content>
</Layout>
<Footer style={footerStyle}>Footer</Footer>
</Layout>
<Layout style={layoutStyle}>
<Header style={headerStyle}>Header</Header>
<Layout>
<Content style={contentStyle}>Content</Content>
<Sider width="25%" style={siderStyle}>
Sider
</Sider>
</Layout>
<Footer style={footerStyle}>Footer</Footer>
</Layout>
<Layout style={layoutStyle}>
<Sider width="25%" style={siderStyle}>
Sider
</Sider>
<Layout>
<Header style={headerStyle}>Header</Header>
<Content style={contentStyle}>Content</Content>
<Footer style={footerStyle}>Footer</Footer>
</Layout>
</Layout>
</Flex>
);
export default App;
```
### 上中下布局
最基本的『上-中-下』布局。
一般主导航放置于页面的顶端,从左自右依次为:logo、一级导航项、辅助菜单(用户、设置、通知等)。通常将内容放在固定尺寸(例如:1200px)内,整个页面排版稳定,不受用户终端显示器影响;上下级的结构符合用户上下浏览的习惯,也是较为经典的网站导航模式。页面上下切分的方式提高了主工作区域的信息展示效率,但在纵向空间上会有一些牺牲。此外,由于导航栏水平空间的限制,不适合那些一级导航项很多的信息结构。
```tsx
import React from 'react';
import { Breadcrumb, Layout, Menu, theme } from 'antd';
const { Header, Content, Footer } = Layout;
const items = Array.from({ length: 15 }).map((_, index) => ({
key: index + 1,
label: `nav ${index + 1}`,
}));
const App: React.FC = () => {
const {
token: { colorBgContainer, borderRadiusLG },
} = theme.useToken();
return (
<Layout>
<Header style={{ display: 'flex', alignItems: 'center' }}>
<div className="demo-logo" />
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={['2']}
items={items}
style={{ flex: 1, minWidth: 0 }}
/>
</Header>
<Content style={{ padding: '0 48px' }}>
<Breadcrumb
style={{ margin: '16px 0' }}
items={[{ title: 'Home' }, { title: 'List' }, { title: 'App' }]}
/>
<div
style={{
background: colorBgContainer,
minHeight: 280,
padding: 24,
borderRadius: borderRadiusLG,
}}
>
Content
</div>
</Content>
<Footer style={{ textAlign: 'center' }}>
Ant Design ©{new Date().getFullYear()} Created by Ant UED
</Footer>
</Layout>
);
};
export default App;
```
### 顶部-侧边布局
拥有顶部导航及侧边栏的页面,多用于展示类网站。
```tsx
import React from 'react';
import { LaptopOutlined, NotificationOutlined, UserOutlined } from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Breadcrumb, Layout, Menu, theme } from 'antd';
const { Header, Content, Footer, Sider } = Layout;
const items1: MenuProps['items'] = ['1', '2', '3'].map((key) => ({
key,
label: `nav ${key}`,
}));
const items2: MenuProps['items'] = [UserOutlined, LaptopOutlined, NotificationOutlined].map(
(icon, index) => {
const key = String(index + 1);
return {
key: `sub${key}`,
icon: React.createElement(icon),
label: `subnav ${key}`,
children: Array.from({ length: 4 }).map((_, j) => {
const subKey = index * 4 + j + 1;
return {
key: subKey,
label: `option${subKey}`,
};
}),
};
},
);
const App: React.FC = () => {
const {
token: { colorBgContainer, borderRadiusLG },
} = theme.useToken();
return (
<Layout>
<Header style={{ display: 'flex', alignItems: 'center' }}>
<div className="demo-logo" />
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={['2']}
items={items1}
style={{ flex: 1, minWidth: 0 }}
/>
</Header>
<div style={{ padding: '0 48px' }}>
<Breadcrumb
style={{ margin: '16px 0' }}
items={[{ title: 'Home' }, { title: 'List' }, { title: 'App' }]}
/>
<Layout
style={{ padding: '24px 0', background: colorBgContainer, borderRadius: borderRadiusLG }}
>
<Sider style={{ background: colorBgContainer }} width={200}>
<Menu
mode="inline"
defaultSelectedKeys={['1']}
defaultOpenKeys={['sub1']}
style={{ height: '100%' }}
items={items2}
/>
</Sider>
<Content style={{ padding: '0 24px', minHeight: 280 }}>Content</Content>
</Layout>
</div>
<Footer style={{ textAlign: 'center' }}>
Ant Design ©{new Date().getFullYear()} Created by Ant UED
</Footer>
</Layout>
);
};
export default App;
```
### 顶部-侧边布局-通栏
同样拥有顶部导航及侧边栏,区别是两边未留边距,多用于应用型的网站。
```tsx
import React from 'react';
import { LaptopOutlined, NotificationOutlined, UserOutlined } from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Breadcrumb, Layout, Menu, theme } from 'antd';
const { Header, Content, Sider } = Layout;
const items1: MenuProps['items'] = ['1', '2', '3'].map((key) => ({
key,
label: `nav ${key}`,
}));
const items2: MenuProps['items'] = [UserOutlined, LaptopOutlined, NotificationOutlined].map(
(icon, index) => {
const key = String(index + 1);
return {
key: `sub${key}`,
icon: React.createElement(icon),
label: `subnav ${key}`,
children: Array.from({ length: 4 }).map((_, j) => {
const subKey = index * 4 + j + 1;
return {
key: subKey,
label: `option${subKey}`,
};
}),
};
},
);
const App: React.FC = () => {
const {
token: { colorBgContainer, borderRadiusLG },
} = theme.useToken();
return (
<Layout>
<Header style={{ display: 'flex', alignItems: 'center' }}>
<div className="demo-logo" />
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={['2']}
items={items1}
style={{ flex: 1, minWidth: 0 }}
/>
</Header>
<Layout>
<Sider width={200} style={{ background: colorBgContainer }}>
<Menu
mode="inline"
defaultSelectedKeys={['1']}
defaultOpenKeys={['sub1']}
style={{ height: '100%', borderInlineEnd: 0 }}
items={items2}
/>
</Sider>
<Layout style={{ padding: '0 24px 24px' }}>
<Breadcrumb
items={[{ title: 'Home' }, { title: 'List' }, { title: 'App' }]}
style={{ margin: '16px 0' }}
/>
<Content
style={{
padding: 24,
margin: 0,
minHeight: 280,
background: colorBgContainer,
borderRadius: borderRadiusLG,
}}
>
Content
</Content>
</Layout>
</Layout>
</Layout>
);
};
export default App;
```
### 侧边布局
侧边两列式布局。页面横向空间有限时,侧边导航可收起。
侧边导航在页面布局上采用的是左右的结构,一般主导航放置于页面的左侧固定位置,辅助菜单放置于工作区顶部。内容根据浏览器终端进行自适应,能提高横向空间的使用率,但是整个页面排版不稳定。侧边导航的模式层级扩展性强,一、二、三级导航项目可以更为顺畅且具关联性的被展示,同时侧边导航可以固定,使得用户在操作和浏览中可以快速的定位和切换当前位置,有很高的操作效率。但这类导航横向页面内容的空间会被牺牲一部分。
> 🛎️ 想要 3 分钟实现?试试 [ProLayout](https://procomponents.ant.design/components/layout)!
```tsx
import React, { useState } from 'react';
import {
DesktopOutlined,
FileOutlined,
PieChartOutlined,
TeamOutlined,
UserOutlined,
} from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Breadcrumb, Layout, Menu, theme } from 'antd';
const { Header, Content, Footer, Sider } = Layout;
type MenuItem = Required<MenuProps>['items'][number];
function getItem(
label: React.ReactNode,
key: React.Key,
icon?: React.ReactNode,
children?: MenuItem[],
): MenuItem {
return {
key,
icon,
children,
label,
} as MenuItem;
}
const items: MenuItem[] = [
getItem('Option 1', '1', <PieChartOutlined />),
getItem('Option 2', '2', <DesktopOutlined />),
getItem('User', 'sub1', <UserOutlined />, [
getItem('Tom', '3'),
getItem('Bill', '4'),
getItem('Alex', '5'),
]),
getItem('Team', 'sub2', <TeamOutlined />, [getItem('Team 1', '6'), getItem('Team 2', '8')]),
getItem('Files', '9', <FileOutlined />),
];
const App: React.FC = () => {
const [collapsed, setCollapsed] = useState(false);
const {
token: { colorBgContainer, borderRadiusLG },
} = theme.useToken();
return (
<Layout style={{ minHeight: '100vh' }}>
<Sider collapsible collapsed={collapsed} onCollapse={(value) => setCollapsed(value)}>
<div className="demo-logo-vertical" />
<Menu theme="dark" defaultSelectedKeys={['1']} mode="inline" items={items} />
</Sider>
<Layout>
<Header style={{ padding: 0, background: colorBgContainer }} />
<Content style={{ margin: '0 16px' }}>
<Breadcrumb style={{ margin: '16px 0' }} items={[{ title: 'User' }, { title: 'Bill' }]} />
<div
style={{
padding: 24,
minHeight: 360,
background: colorBgContainer,
borderRadius: borderRadiusLG,
}}
>
Bill is a cat.
</div>
</Content>
<Footer style={{ textAlign: 'center' }}>
Ant Design ©{new Date().getFullYear()} Created by Ant UED
</Footer>
</Layout>
</Layout>
);
};
export default App;
```
### 自定义触发器
要使用自定义触发器,可以设置 `trigger={null}` 来隐藏默认设定。
```tsx
import React, { useState } from 'react';
import {
MenuFoldOutlined,
MenuUnfoldOutlined,
UploadOutlined,
UserOutlined,
VideoCameraOutlined,
} from '@ant-design/icons';
import { Button, Layout, Menu, theme } from 'antd';
const { Header, Sider, Content } = Layout;
const App: React.FC = () => {
const [collapsed, setCollapsed] = useState(false);
const {
token: { colorBgContainer, borderRadiusLG },
} = theme.useToken();
return (
<Layout>
<Sider trigger={null} collapsible collapsed={collapsed}>
<div className="demo-logo-vertical" />
<Menu
theme="dark"
mode="inline"
defaultSelectedKeys={['1']}
items={[
{
key: '1',
icon: <UserOutlined />,
label: 'nav 1',
},
{
key: '2',
icon: <VideoCameraOutlined />,
label: 'nav 2',
},
{
key: '3',
icon: <UploadOutlined />,
label: 'nav 3',
},
]}
/>
</Sider>
<Layout>
<Header style={{ padding: 0, background: colorBgContainer }}>
<Button
type="text"
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
onClick={() => setCollapsed(!collapsed)}
style={{
fontSize: '16px',
width: 64,
height: 64,
}}
/>
</Header>
<Content
style={{
margin: '24px 16px',
padding: 24,
minHeight: 280,
background: colorBgContainer,
borderRadius: borderRadiusLG,
}}
>
Content
</Content>
</Layout>
</Layout>
);
};
export default App;
```
### 响应式布局
Layout.Sider 支持响应式布局。
> 说明:配置 `breakpoint` 属性即生效,视窗宽度小于 `breakpoint` 时 Sider 缩小为 `collapsedWidth` 宽度,若将 `collapsedWidth` 设置为 0,会出现特殊 trigger。
```tsx
import React from 'react';
import { UploadOutlined, UserOutlined, VideoCameraOutlined } from '@ant-design/icons';
import { Layout, Menu, theme } from 'antd';
const { Header, Content, Footer, Sider } = Layout;
const items = [UserOutlined, VideoCameraOutlined, UploadOutlined, UserOutlined].map(
(icon, index) => ({
key: String(index + 1),
icon: React.createElement(icon),
label: `nav ${index + 1}`,
}),
);
const App: React.FC = () => {
const {
token: { colorBgContainer, borderRadiusLG },
} = theme.useToken();
return (
<Layout>
<Sider
breakpoint="lg"
collapsedWidth="0"
onBreakpoint={(broken) => {
console.log(broken);
}}
onCollapse={(collapsed, type) => {
console.log(collapsed, type);
}}
>
<div className="demo-logo-vertical" />
<Menu theme="dark" mode="inline" defaultSelectedKeys={['4']} items={items} />
</Sider>
<Layout>
<Header style={{ padding: 0, background: colorBgContainer }} />
<Content style={{ margin: '24px 16px 0' }}>
<div
style={{
padding: 24,
minHeight: 360,
background: colorBgContainer,
borderRadius: borderRadiusLG,
}}
>
content
</div>
</Content>
<Footer style={{ textAlign: 'center' }}>
Ant Design ©{new Date().getFullYear()} Created by Ant UED
</Footer>
</Layout>
</Layout>
);
};
export default App;
```
### 固定头部
一般用于固定顶部导航,方便页面切换。
```tsx
import React from 'react';
import { Breadcrumb, Layout, Menu, theme } from 'antd';
const { Header, Content, Footer } = Layout;
const items = Array.from({ length: 3 }).map((_, index) => ({
key: String(index + 1),
label: `nav ${index + 1}`,
}));
const App: React.FC = () => {
const {
token: { colorBgContainer, borderRadiusLG },
} = theme.useToken();
return (
<Layout>
<Header
style={{
position: 'sticky',
top: 0,
zIndex: 1,
width: '100%',
display: 'flex',
alignItems: 'center',
}}
>
<div className="demo-logo" />
<Menu
theme="dark"
mode="horizontal"
defaultSelectedKeys={['2']}
items={items}
style={{ flex: 1, minWidth: 0 }}
/>
</Header>
<Content style={{ padding: '0 48px' }}>
<Breadcrumb
style={{ margin: '16px 0' }}
items={[{ title: 'Home' }, { title: 'List' }, { title: 'App' }]}
/>
<div
style={{
padding: 24,
minHeight: 380,
background: colorBgContainer,
borderRadius: borderRadiusLG,
}}
>
Content
</div>
</Content>
<Footer style={{ textAlign: 'center' }}>
Ant Design ©{new Date().getFullYear()} Created by Ant UED
</Footer>
</Layout>
);
};
export default App;
```
### 固定侧边栏
当内容较长时,使用固定侧边栏可以提供更好的体验。
```tsx
import React from 'react';
import {
AppstoreOutlined,
BarChartOutlined,
CloudOutlined,
ShopOutlined,
TeamOutlined,
UploadOutlined,
UserOutlined,
VideoCameraOutlined,
} from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Layout, Menu, theme } from 'antd';
const { Header, Content, Footer, Sider } = Layout;
const siderStyle: React.CSSProperties = {
overflow: 'auto',
height: '100vh',
position: 'sticky',
insetInlineStart: 0,
top: 0,
bottom: 0,
scrollbarWidth: 'thin',
scrollbarGutter: 'stable',
};
const items: MenuProps['items'] = [
UserOutlined,
VideoCameraOutlined,
UploadOutlined,
BarChartOutlined,
CloudOutlined,
AppstoreOutlined,
TeamOutlined,
ShopOutlined,
].map((icon, index) => ({
key: String(index + 1),
icon: React.createElement(icon),
label: `nav ${index + 1}`,
}));
const App: React.FC = () => {
const {
token: { colorBgContainer, borderRadiusLG },
} = theme.useToken();
return (
<Layout hasSider>
<Sider style={siderStyle}>
<div className="demo-logo-vertical" />
<Menu theme="dark" mode="inline" defaultSelectedKeys={['4']} items={items} />
</Sider>
<Layout>
<Header style={{ padding: 0, background: colorBgContainer }} />
<Content style={{ margin: '24px 16px 0', overflow: 'initial' }}>
<div
style={{
padding: 24,
textAlign: 'center',
background: colorBgContainer,
borderRadius: borderRadiusLG,
}}
>
<p>long content</p>
{
// indicates very long content
Array.from({ length: 100 }, (_, index) => (
<React.Fragment key={index}>
{index % 20 === 0 && index ? 'more' : '...'}
<br />
</React.Fragment>
))
}
</div>
</Content>
<Footer style={{ textAlign: 'center' }}>
Ant Design ©{new Date().getFullYear()} Created by Ant UED
</Footer>
</Layout>
</Layout>
);
};
export default App;
```
### 自定义触发器 Debug
修改内容前,请尝试此 Demo 查看样式是否抖动。
```tsx
import React, { useState } from 'react';
import {
DesktopOutlined,
FileOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined,
PieChartOutlined,
TeamOutlined,
UserOutlined,
} from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Button, Layout, Menu, theme } from 'antd';
const { Header, Sider, Content } = Layout;
const items: MenuProps['items'] = [
{
key: '1',
icon: <PieChartOutlined />,
label: 'Option 1',
},
{
key: '2',
icon: <DesktopOutlined />,
label: 'Option 2',
},
{
key: 'sub1',
icon: <UserOutlined />,
label: 'User',
children: [
{
key: '3',
label: 'Tom',
},
{
key: '4',
label: 'Bill',
},
{
key: '5',
label: 'Alex',
},
],
},
{
key: 'sub2',
icon: <TeamOutlined />,
label: 'Team',
children: [
{
key: '6',
label: 'Team 1',
},
{
key: '7',
label: 'Team 2',
},
],
},
{
key: '9',
icon: <FileOutlined />,
},
];
const App: React.FC = () => {
const [collapsed, setCollapsed] = useState(true);
const {
token: { colorBgContainer, borderRadiusLG },
} = theme.useToken();
return (
<Layout>
<Sider trigger={null} collapsible collapsed={collapsed}>
<div className="demo-logo-vertical" />
<Menu
theme="dark"
mode="inline"
defaultSelectedKeys={['3']}
defaultOpenKeys={['sub1']}
items={items}
/>
</Sider>
<Layout>
<Header style={{ padding: 0, background: colorBgContainer }}>
<Button
type="text"
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
onClick={() => setCollapsed(!collapsed)}
style={{
fontSize: '16px',
width: 64,
height: 64,
}}
/>
</Header>
<Content
style={{
margin: '24px 16px',
padding: 24,
minHeight: 280,
background: colorBgContainer,
borderRadius: borderRadiusLG,
}}
>
Content
</Content>
</Layout>
</Layout>
);
};
export default App;
```
### 组件 Token
组件 Token
```tsx
import React from 'react';
import { LaptopOutlined, NotificationOutlined, UserOutlined } from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { Breadcrumb, ConfigProvider, Layout, Menu, theme } from 'antd';
const { Header, Content, Sider } = Layout;
const items2: MenuProps['items'] = [UserOutlined, LaptopOutlined, NotificationOutlined].map(
(icon, index) => {
const key = String(index + 1);
return {
key: `sub${key}`,
icon: React.createElement(icon),
label: `subnav ${key}`,
children: Array.from({ length: 4 }).map((_, j) => {
const subKey = index * 4 + j + 1;
return {
key: subKey,
label: `option${subKey}`,
};
}),
};
},
);
const App: React.FC = () => {
const {
token: { colorBgContainer, colorBgLayout, borderRadiusLG },
} = theme.useToken();
return (
<ConfigProvider
theme={{
components: {
Layout: {
bodyBg: '#fff',
headerBg: '#1677ff',
headerHeight: 64,
headerPadding: `0 24px`,
headerColor: colorBgContainer,
siderBg: '#800080',
},
},
}}
>
<Layout>
<Header style={{ display: 'flex', alignItems: 'center' }}>
<div className="demo-logo" />
<div style={{ marginInlineStart: 24, fontSize: 24 }}>Ant Design</div>
</Header>
<Layout>
<ConfigProvider
theme={{
components: {
Layout: {
siderBg: 'red',
},
},
}}
>
<Sider width={32} />
</ConfigProvider>
<Sider width={200}>
<Menu
mode="inline"
defaultSelectedKeys={['1']}
defaultOpenKeys={['sub1']}
style={{ borderInlineEnd: 0 }}
items={items2}
/>
</Sider>
<Layout style={{ padding: '0 24px 24px' }}>
<Breadcrumb style={{ margin: '16px 0' }}>
<Breadcrumb.Item>Home</Breadcrumb.Item>
<Breadcrumb.Item>List</Breadcrumb.Item>
<Breadcrumb.Item>App</Breadcrumb.Item>
</Breadcrumb>
<Content
style={{
padding: 24,
margin: 0,
minHeight: 280,
background: colorBgLayout,
borderRadius: borderRadiusLG,
}}
>
Content
</Content>
</Layout>
</Layout>
</Layout>
</ConfigProvider>
);
};
export default App;
```