Demos#
#代码演示
#枚举属性切换
通过 Segmented 分段选择器一键切换 layout、navTheme、contentWidth、siderMenuType 四个枚举属性,直观对比不同配置的效果。
枚举属性切换
import {
GithubFilled,
InfoCircleFilled,
QuestionCircleFilled,
} from '@ant-design/icons';
import type { ProSettings } from '@ant-design/pro-components';
import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
import { Segmented, Space, theme } from 'antd';
import { useState } from 'react';
import defaultProps from './_defaultProps';
const Demo = () => {
const { token } = theme.useToken();
const [layout, setLayout] = useState<ProSettings['layout']>('mix');
const [navTheme, setNavTheme] = useState<ProSettings['navTheme']>('light');
const [contentWidth, setContentWidth] = useState<'Fluid' | 'Fixed'>('Fluid');
const [siderMenuType, setSiderMenuType] = useState<'sub' | 'group'>('group');
const [pathname, setPathname] = useState('/welcome');
return (
<div
style={{
height: 700,
overflow: 'auto',
border: `1px solid ${token.colorBorderSecondary}`,
borderRadius: token.borderRadius,
}}
>
<ProLayout
{...defaultProps}
layout={layout}
navTheme={navTheme}
contentWidth={contentWidth}
siderMenuType={siderMenuType}
fixSiderbar
location={{ pathname }}
menu={{ collapsedShowGroupTitle: true }}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: '书琰',
}}
actionsRender={() => [
<InfoCircleFilled key="info" />,
<QuestionCircleFilled key="question" />,
<GithubFilled key="github" />,
]}
menuItemRender={(item, dom) => (
<div onClick={() => setPathname(item.path || '/welcome')}>
{dom}
</div>
)}
>
<PageContainer>
<ProCard style={{ marginBlockEnd: 16 }} variant="outlined">
<Space orientation="vertical" size={12} style={{ width: '100%' }}>
<Space>
<span>layout 导航模式:</span>
<Segmented
value={layout}
onChange={(v) => setLayout(v as any)}
options={[
{ label: '侧栏 side', value: 'side' },
{ label: '顶部 top', value: 'top' },
{ label: '混合 mix', value: 'mix' },
]}
/>
</Space>
<Space>
<span>navTheme 导航主题:</span>
<Segmented
value={navTheme}
onChange={(v) => setNavTheme(v as any)}
options={[
{ label: '亮色 light', value: 'light' },
{ label: '暗色 realDark', value: 'realDark' },
]}
/>
</Space>
<Space>
<span>contentWidth 内容宽度:</span>
<Segmented
value={contentWidth}
onChange={(v) => setContentWidth(v as any)}
options={[
{ label: '流式 Fluid', value: 'Fluid' },
{ label: '定宽 Fixed', value: 'Fixed' },
]}
/>
</Space>
<Space>
<span>siderMenuType 菜单模式:</span>
<Segmented
value={siderMenuType}
onChange={(v) => setSiderMenuType(v as any)}
options={[
{ label: '子菜单 sub', value: 'sub' },
{ label: '分组 group', value: 'group' },
]}
/>
</Space>
</Space>
</ProCard>
<ProCard>
当前配置:layout=<b>{layout}</b>、navTheme=<b>{navTheme}</b>、
contentWidth=<b>{contentWidth}</b>、siderMenuType=
<b>{siderMenuType}</b>
</ProCard>
</PageContainer>
</ProLayout>
</div>
);
};
export default Demo;
#基础用法
基础使用
import {
CaretDownFilled,
DoubleRightOutlined,
GithubFilled,
InfoCircleFilled,
LogoutOutlined,
PlusCircleFilled,
QuestionCircleFilled,
SearchOutlined,
} from '@ant-design/icons';
import type { ProSettings } from '@ant-design/pro-components';
import {
PageContainer,
ProCard,
ProConfigProvider,
ProLayout,
SettingDrawer,
} from '@ant-design/pro-components';
import { css } from '@emotion/css';
import {
Button,
ConfigProvider,
Divider,
Dropdown,
Input,
Popover,
theme,
} from 'antd';
import React, { useState } from 'react';
import defaultProps from './_defaultProps';
import { demoOnMenuHeaderClick } from './_demoHandlers';
const SOLUTIONS = {
金融行业: [
'智能风控平台',
'供应链金融',
'数字银行核心',
'保险理赔系统',
'资产管理中台',
'合规审计平台',
],
通用方案: [
'统一权 限中心',
'数据可视化引擎',
'微服务治理框架',
'低代码搭建平台',
'智能客服系统',
'DevOps 工具链',
],
};
const HOT_PRODUCTS = [
{ name: 'Ant Design Pro', desc: '开箱即用的中台前端解决方案' },
{ name: '数据可视化引擎', desc: '企业级数据看板与图表分析工具' },
{ name: '微服务治理框架', desc: '基于 K8s 的服务注册与发现' },
];
const SolutionItem: React.FC<{ children: React.ReactNode }> = (props) => {
const { token } = theme.useToken();
return (
<div
className={css`
color: ${token.colorTextSecondary};
font-size: 14px;
cursor: pointer;
line-height: 22px;
margin-bottom: 8px;
&:hover {
color: ${token.colorPrimary};
}
`}
style={{
width: '33.33%',
}}
>
{props.children}
<DoubleRightOutlined
style={{
marginInlineStart: 4,
}}
/>
</div>
);
};
const SolutionCategory: React.FC<{
title: string;
items: string[];
style?: React.CSSProperties;
}> = (props) => {
const { token } = theme.useToken();
return (
<div
style={{
width: '100%',
...props.style,
}}
>
<div
style={{
fontSize: 16,
color: token.colorTextHeading,
lineHeight: '24px',
fontWeight: 500,
marginBlockEnd: 16,
}}
>
{props.title}
</div>
<div
style={{
display: 'flex',
flexWrap: 'wrap',
}}
>
{props.items.map((item) => (
<SolutionItem key={item}>{item}</SolutionItem>
))}
</div>
</div>
);
};
const MenuCard = () => {
const { token } = theme.useToken();
return (
<div
style={{
display: 'flex',
alignItems: 'center',
}}
>
<Divider
style={{
height: '1.5em',
}}
orientation="vertical"
/>
<Popover
placement="bottom"
styles={{
root: {
width: 'calc(100vw - 24px)',
padding: '24px',
paddingTop: 8,
height: '307px',
borderRadius: '0 0 6px 6px',
},
}}
content={
<div style={{ display: 'flex', padding: '32px 40px' }}>
<div style={{ flex: 1 }}>
<SolutionCategory
title="金融行业"
items={SOLUTIONS['金融行业']}
/>
<SolutionCategory
title="通用方案"
items={SOLUTIONS['通用方案']}
style={{ marginBlockStart: 32 }}
/>
</div>
<div
style={{
width: '308px',
borderInlineStart: '1px solid ' + token.colorBorder,
paddingInlineStart: 16,
}}
>
<div
className={css`
font-size: 14px;
color: ${token.colorText};
line-height: 22px;
`}
>
热门产品
</div>
{HOT_PRODUCTS.map((product) => (
<div
key={product.name}
className={css`
border-radius: 4px;
padding: 16px;
margin-top: 4px;
display: flex;
cursor: pointer;
&:hover {
background-color: ${token.colorBgTextHover};
}
`}
>
<img src="https://gw.alipayobjects.com/zos/antfincdn/6FTGmLLmN/bianzu%25252013.svg" alt="" />
<div
style={{
marginInlineStart: 14,
}}
>
<div
className={css`
font-size: 14px;
color: ${token.colorText};
line-height: 22px;
`}
>
{product.name}
</div>
<div
className={css`
font-size: 12px;
color: ${token.colorTextSecondary};
line-height: 20px;
`}
>
{product.desc}
</div>
</div>
</div>
))}
</div>
</div>
}
>
<div
style={{
color: token.colorTextHeading,
fontWeight: 500,
cursor: 'pointer',
display: 'flex',
gap: 4,
paddingInlineStart: 8,
paddingInlineEnd: 12,
alignItems: 'center',
}}
className={css`
&:hover {
background-color: ${token.colorBgTextHover};
}
`}
>
<span> 企业级资产中心</span>
<CaretDownFilled />
</div>
</Popover>
</div>
);
};
const SearchInput = () => {
const { token } = theme.useToken();
return (
<div
key="SearchOutlined"
aria-hidden
style={{
display: 'flex',
alignItems: 'center',
marginInlineEnd: 24,
}}
onMouseDown={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<Input
style={{
borderRadius: 4,
marginInlineEnd: 12,
backgroundColor: token.colorBgTextHover,
}}
prefix={
<SearchOutlined
style={{
color: token.colorTextLightSolid,
}}
/>
}
placeholder="搜索方案"
variant="borderless"
/>
<PlusCircleFilled
style={{
color: token.colorPrimary,
fontSize: 24,
}}
/>
</div>
);
};
const Demo = () => {
const [settings, setSetting] = useState<Partial<ProSettings> | undefined>({
fixSiderbar: true,
layout: 'mix',
splitMenus: true,
});
const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
const [num, setNum] = useState(40);
if (typeof document === 'undefined') {
return <div />;
}
return (
<div
id="test-pro-layout"
style={{
height: '100vh',
overflow: 'auto',
}}
>
<ProConfigProvider hashed={false}>
<ConfigProvider
getTargetContainer={() => {
return document.getElementById('test-pro-layout') || document.body;
}}
>
<ProLayout
prefixCls="my-prefix"
bgLayoutImgList={[
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
left: 85,
bottom: 100,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
bottom: -68,
right: -45,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
bottom: 0,
left: 0,
width: '331px',
},
]}
{...defaultProps}
location={{
pathname,
}}
token={{
header: {
colorBgMenuItemSelected: 'rgba(0,0,0,0.04)',
},
}}
siderMenuType="group"
menu={{
collapsedShowGroupTitle: true,
}}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: '书琰',
render: (props, dom) => {
return (
<Dropdown
menu={{
items: [
{
key: 'logout',
icon: <LogoutOutlined />,
label: '退出登录',
},
],
}}
>
{dom}
</Dropdown>
);
},
}}
actionsRender={(props) => {
if (props.isMobile) return [];
if (typeof window === 'undefined') return [];
return [
props.layout !== 'side' && document.body.clientWidth > 1400 ? (
<SearchInput key="SearchInput" />
) : undefined,
<InfoCircleFilled key="InfoCircleFilled" />,
<QuestionCircleFilled key="QuestionCircleFilled" />,
<GithubFilled key="GithubFilled" />,
];
}}
headerTitleRender={(logo, title, _) => {
const defaultDom = (
<button
type="button"
style={{
background: 'none',
border: 'none',
padding: 0,
font: 'inherit',
cursor: 'pointer',
}}
>
{logo}
{title}
</button>
);
if (typeof window === 'undefined') return defaultDom;
if (document.body.clientWidth < 1400) {
return defaultDom;
}
if (_.isMobile) return defaultDom;
return (
<>
{defaultDom}
<MenuCard />
</>
);
}}
menuFooterRender={(props) => {
if (props?.collapsed) return undefined;
return (
<div
style={{
textAlign: 'center',
paddingBlockStart: 12,
}}
>
<div>© 2021 Made with love</div>
<div>by Ant Design</div>
</div>
);
}}
onMenuHeaderClick={demoOnMenuHeaderClick}
menuItemRender={(item, dom) => (
<div
role="button"
tabIndex={0}
onClick={() => {
setPathname(item.path || '/welcome');
}}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setPathname(item.path || '/welcome');
}
}}
>
{dom}
</div>
)}
{...settings}
>
<PageContainer
token={{
paddingInlinePageContainerContent: num,
}}
extra={[
<Button key="3">操作</Button>,
<Button key="2">操作</Button>,
<Button
key="1"
type="primary"
onClick={() => {
setNum(num > 0 ? 0 : 40);
}}
>
主操作
</Button>,
]}
subTitle="简单的描述"
footer={[
<Button key="3">重置</Button>,
<Button key="2" type="primary">
提交
</Button>,
]}
>
<ProCard
style={{
height: '200vh',
minHeight: 800,
}}
>
<div />
</ProCard>
</PageContainer>
<SettingDrawer
pathname={pathname}
enableDarkTheme
getContainer={(e: any) => {
if (typeof window === 'undefined') return e;
return document.getElementById('test-pro-layout');
}}
settings={settings}
onSettingChange={(changeSetting) => {
setSetting(changeSetting);
}}
disableUrlParams={false}
/>
</ProLayout>
</ConfigProvider>
</ProConfigProvider>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#导航模式
侧栏导航(默认推荐)
import {
GithubFilled,
InfoCircleFilled,
QuestionCircleFilled,
} from '@ant-design/icons';
import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
import { useState } from 'react';
import defaultProps from './_defaultProps';
const Demo = () => {
const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
return (
<div
id="test-pro-layout"
style={{
height: '100vh',
}}
>
<ProLayout
siderWidth={216}
bgLayoutImgList={[
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
left: 85,
bottom: 100,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
bottom: -68,
right: -45,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
bottom: 0,
left: 0,
width: '331px',
},
]}
{...defaultProps}
location={{
pathname,
}}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
title: '书琰',
size: 'small',
}}
actionsRender={(props) => {
if (props.isMobile) return [];
return [
<InfoCircleFilled key="InfoCircleFilled" />,
<QuestionCircleFilled key="QuestionCircleFilled" />,
<GithubFilled key="GithubFilled" />,
];
}}
menuItemRender={(item, dom) => (
<div
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</div>
)}
>
<PageContainer>
<ProCard
style={{
height: '100vh',
minHeight: 800,
}}
>
<div />
</ProCard>
</PageContainer>
</ProLayout>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
顶部导航
import {
CaretDownFilled,
DoubleRightOutlined,
GithubFilled,
InfoCircleFilled,
PlusCircleFilled,
QuestionCircleFilled,
SearchOutlined,
} from '@ant-design/icons';
import type { ProSettings } from '@ant-design/pro-components';
import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
import { css } from '@emotion/css';
import { Divider, Input, Popover, theme } from 'antd';
import { useState } from 'react';
import defaultProps from './_defaultProps';
const SOLUTIONS = {
金融行业: [
'智能风控平台',
'供应链金融',
'数字银行核心',
'保险理赔系统',
'资产管理中台',
'合规审计平台',
],
通用方案: [
'统一权限中心',
'数据可视化引擎',
'微服务治理框架',
'低代码搭建平台',
'智能客服系统',
'DevOps 工具链',
],
};
const HOT_PRODUCTS = [
{ name: 'Ant Design Pro', desc: '开箱即用的中台前端解决方案' },
{ name: '数据可视化引擎', desc: '企业级数据看板与图表分析工具' },
{ name: '微服务治理框架', desc: '基于 K8s 的服务注册与发现' },
];
const SolutionItem: React.FC<{ children: React.ReactNode }> = (props) => {
const { token } = theme.useToken();
return (
<div
className={css`
color: ${token.colorTextSecondary};
font-size: 14px;
cursor: pointer;
line-height: 22px;
margin-bottom: 8px;
&:hover {
color: ${token.colorPrimary};
}
`}
style={{
width: '33.33%',
}}
>
{props.children}
<DoubleRightOutlined
style={{
marginInlineStart: 4,
}}
/>
</div>
);
};
const SolutionCategory: React.FC<{
title: string;
items: string[];
style?: React.CSSProperties;
}> = (props) => {
const { token } = theme.useToken();
return (
<div
style={{
width: '100%',
...props.style,
}}
>
<div
style={{
fontSize: 16,
color: token.colorTextHeading,
lineHeight: '24px',
fontWeight: 500,
marginBlockEnd: 16,
}}
>
{props.title}
</div>
<div
style={{
display: 'flex',
flexWrap: 'wrap',
}}
>
{props.items.map((item) => (
<SolutionItem key={item}>{item}</SolutionItem>
))}
</div>
</div>
);
};
const MenuCard = () => {
const { token } = theme.useToken();
return (
<div
style={{
display: 'flex',
alignItems: 'center',
}}
>
<Divider
style={{
height: '1.5em',
}}
orientation="vertical"
/>
<Popover
placement="bottom"
styles={{
root: {
width: 'calc(100vw - 24px)',
padding: '24px',
paddingTop: 8,
height: '307px',
borderRadius: '0 0 6px 6px',
},
}}
content={
<div style={{ display: 'flex', padding: '32px 40px' }}>
<div style={{ flex: 1 }}>
<SolutionCategory
title="金融行业"
items={SOLUTIONS['金融行业']}
/>
<SolutionCategory
title="通用方案"
items={SOLUTIONS['通用方案']}
style={{ marginBlockStart: 32 }}
/>
</div>
<div
style={{
width: '308px',
borderInlineStart: '1px solid ' + token.colorBorder,
paddingInlineStart: 16,
}}
>
<div
className={css`
font-size: 14px;
color: ${token.colorText};
line-height: 22px;
`}
>
热门产品
</div>
{HOT_PRODUCTS.map((product) => (
<div
key={product.name}
className={css`
border-radius: 4px;
padding: 16px;
margin-top: 4px;
display: flex;
cursor: pointer;
&:hover {
background-color: ${token.colorBgTextHover};
}
`}
>
<img src="https://gw.alipayobjects.com/zos/antfincdn/6FTGmLLmN/bianzu%25252013.svg" alt="" />
<div
style={{
marginInlineStart: 14,
}}
>
<div
className={css`
font-size: 14px;
color: ${token.colorText};
line-height: 22px;
`}
>
{product.name}
</div>
<div
className={css`
font-size: 12px;
color: ${token.colorTextSecondary};
line-height: 20px;
`}
>
{product.desc}
</div>
</div>
</div>
))}
</div>
</div>
}
>
<div
style={{
color: token.colorTextHeading,
fontWeight: 500,
cursor: 'pointer',
display: 'flex',
gap: 4,
paddingInlineStart: 8,
paddingInlineEnd: 12,
alignItems: 'center',
}}
className={css`
&:hover {
background-color: ${token.colorBgTextHover};
}
`}
>
<span> 企业级资产中心</span>
<CaretDownFilled />
</div>
</Popover>
</div>
);
};
const Demo = () => {
const settings: ProSettings | undefined = {
fixSiderbar: true,
layout: 'top',
splitMenus: true,
};
const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
return (
<div
id="test-pro-layout"
style={{
height: '100vh',
}}
>
<ProLayout
bgLayoutImgList={[
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
left: 85,
bottom: 100,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
bottom: -68,
right: -45,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
bottom: 0,
left: 0,
width: '331px',
},
]}
{...defaultProps}
location={{
pathname,
}}
menu={{
type: 'group',
}}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: '书琰',
}}
actionsRender={(props) => {
if (props.isMobile) return [];
return [
props.layout !== 'side' && document.body.clientWidth > 1400 ? (
<div
key="SearchOutlined"
aria-hidden
style={{
display: 'flex',
alignItems: 'center',
marginInlineEnd: 24,
}}
onMouseDown={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<Input
style={{
borderRadius: 4,
marginInlineEnd: 12,
backgroundColor: 'rgba(0,0,0,0.03)',
}}
prefix={
<SearchOutlined
style={{
color: 'rgba(0, 0, 0, 0.15)',
}}
/>
}
placeholder="搜索方案"
variant="borderless"
/>
<PlusCircleFilled
style={{
color: 'var(--ant-primary-color)',
fontSize: 24,
}}
/>
</div>
) : undefined,
<InfoCircleFilled key="InfoCircleFilled" />,
<QuestionCircleFilled key="QuestionCircleFilled" />,
<GithubFilled key="GithubFilled" />,
];
}}
menuItemRender={(item, dom) => (
<div
role="button"
tabIndex={0}
onClick={() => {
setPathname(item.path || '/welcome');
}}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setPathname(item.path || '/welcome');
}
}}
>
{dom}
</div>
)}
headerTitleRender={(logo, title, _) => {
const defaultDom = (
<button
type="button"
style={{
background: 'none',
border: 'none',
padding: 0,
font: 'inherit',
cursor: 'pointer',
}}
onClick={() => console.log('headerTitle clicked')}
>
{logo}
{title}
</button>
);
if (
typeof document === 'undefined' ||
document.body.clientWidth < 1400
) {
return defaultDom;
}
if (_.isMobile) return defaultDom;
return (
<>
{defaultDom}
<MenuCard />
</>
);
}}
{...settings}
>
<PageContainer>
<ProCard
style={{
height: '100vh',
minHeight: 800,
}}
>
<div />
</ProCard>
</PageContainer>
</ProLayout>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
混合导航
import {
GithubFilled,
InfoCircleFilled,
PlusCircleFilled,
QuestionCircleFilled,
SearchOutlined,
} from '@ant-design/icons';
import type { ProSettings } from '@ant-design/pro-components';
import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
import { Input } from 'antd';
import { useState } from 'react';
import defaultProps from './_defaultProps';
import { demoOnMenuHeaderClick } from './_demoHandlers';
const Demo = () => {
const settings: ProSettings | undefined = {
fixSiderbar: true,
layout: 'mix',
splitMenus: true,
};
const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
return (
<div
id="test-pro-layout"
style={{
height: '100vh',
}}
>
<ProLayout
bgLayoutImgList={[
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
left: 85,
bottom: 100,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
bottom: -68,
right: -45,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
bottom: 0,
left: 0,
width: '331px',
},
]}
{...defaultProps}
location={{
pathname,
}}
menu={{
type: 'group',
}}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: '书琰',
}}
actionsRender={(props) => {
if (props.isMobile) return [];
return [
props.layout !== 'side' && document.body.clientWidth > 1400 ? (
<div
key="SearchOutlined"
aria-hidden
style={{
display: 'flex',
alignItems: 'center',
marginInlineEnd: 24,
}}
onMouseDown={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<Input
style={{
borderRadius: 4,
marginInlineEnd: 12,
backgroundColor: 'rgba(0,0,0,0.03)',
}}
prefix={
<SearchOutlined
style={{
color: 'rgba(0, 0, 0, 0.15)',
}}
/>
}
placeholder="搜索方案"
variant="borderless"
/>
<PlusCircleFilled
style={{
color: 'var(--ant-primary-color)',
fontSize: 24,
}}
/>
</div>
) : undefined,
<InfoCircleFilled key="InfoCircleFilled" />,
<QuestionCircleFilled key="QuestionCircleFilled" />,
<GithubFilled key="GithubFilled" />,
];
}}
menuFooterRender={(props) => {
if (props?.collapsed) return undefined;
return (
<div
style={{
textAlign: 'center',
paddingBlockStart: 12,
}}
>
<div>© 2021 Made with love</div>
<div>by Ant Design</div>
</div>
);
}}
onMenuHeaderClick={demoOnMenuHeaderClick}
menuItemRender={(item, dom) => (
<div
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</div>
)}
{...settings}
>
<PageContainer>
<ProCard
style={{
height: '100vh',
minHeight: 800,
}}
>
<div />
</ProCard>
</PageContainer>
</ProLayout>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
经典导航样式
import {
GithubFilled,
InfoCircleFilled,
PlusCircleFilled,
QuestionCircleFilled,
SearchOutlined,
} from '@ant-design/icons';
import type { ProSettings } from '@ant-design/pro-components';
import {
PageContainer,
ProCard,
ProLayout,
SettingDrawer,
} from '@ant-design/pro-components';
import { Button, Input } from 'antd';
import { useState } from 'react';
import defaultProps from './_defaultProps';
import { demoOnMenuHeaderClick } from './_demoHandlers';
const Demo = () => {
const [settings, setSetting] = useState<Partial<ProSettings> | undefined>({
fixSiderbar: true,
layout: 'mix',
splitMenus: true,
});
const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
return (
<div
id="test-pro-layout"
style={{
height: '100vh',
}}
>
<ProLayout
token={{
header: {
colorBgHeader: '#292f33',
colorHeaderTitle: '#fff',
colorTextMenu: '#dfdfdf',
colorTextMenuSecondary: '#dfdfdf',
colorTextMenuSelected: '#fff',
colorBgMenuItemSelected: '#22272b',
colorTextMenuActive: 'rgba(255,255,255,0.85)',
colorTextRightActionsItem: '#dfdfdf',
},
colorTextAppListIconHover: '#fff',
colorTextAppListIcon: '#dfdfdf',
sider: {
colorMenuBackground: '#fff',
colorMenuItemDivider: '#dfdfdf',
colorBgMenuItemHover: '#f6f6f6',
colorTextMenu: '#595959',
colorTextMenuSelected: '#242424',
colorTextMenuActive: '#242424',
},
}}
bgLayoutImgList={[
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
left: 85,
bottom: 100,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
bottom: -68,
right: -45,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
bottom: 0,
left: 0,
width: '331px',
},
]}
{...defaultProps}
location={{
pathname,
}}
menu={{
type: 'group',
}}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: (
<div
style={{
color: '#dfdfdf',
}}
>
书琰
</div>
),
}}
actionsRender={(props) => {
if (props.isMobile) return [];
return [
props.layout !== 'side' && document.body.clientWidth > 1400 ? (
<div
key="SearchOutlined"
aria-hidden
style={{
display: 'flex',
alignItems: 'center',
marginInlineEnd: 24,
}}
onMouseDown={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<Input
style={{
borderRadius: 4,
marginInlineEnd: 12,
backgroundColor: 'rgba(57,62,67,1)',
color: '#fff',
}}
prefix={
<SearchOutlined
style={{
color: '#dfdfdf',
}}
/>
}
placeholder="搜索方案"
variant="borderless"
/>
<PlusCircleFilled
style={{
color: 'var(--ant-primary-color)',
fontSize: 24,
}}
/>
</div>
) : undefined,
<InfoCircleFilled key="InfoCircleFilled" />,
<QuestionCircleFilled key="QuestionCircleFilled" />,
<GithubFilled key="GithubFilled" />,
];
}}
menuFooterRender={(props) => {
if (props?.collapsed) return undefined;
return (
<div
style={{
textAlign: 'center',
paddingBlockStart: 12,
}}
>
<div>© 2021 Made with love</div>
<div>by Ant Design</div>
</div>
);
}}
onMenuHeaderClick={demoOnMenuHeaderClick}
menuItemRender={(item, dom) => (
<a
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</a>
)}
{...settings}
>
<PageContainer
breadcrumb={{
routes: [],
}}
onBack={() => window.history.back()}
extra={[
<Button key="3">操作</Button>,
<Button key="2">操作</Button>,
<Button key="1" type="primary">
主操作
</Button>,
]}
footer={[
<Button key="3">重置</Button>,
<Button key="2" type="primary">
提交
</Button>,
]}
>
<ProCard
style={{
height: '200vh',
minHeight: 800,
}}
>
<div />
</ProCard>
</PageContainer>
</ProLayout>
<SettingDrawer
pathname={pathname}
enableDarkTheme
getContainer={() => document.getElementById('test-pro-layout')}
settings={settings}
onSettingChange={(changeSetting) => {
setSetting(changeSetting);
}}
disableUrlParams={false}
/>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#菜单配置
收起时展示 title
import {
CrownFilled,
GithubFilled,
InfoCircleFilled,
PlusCircleFilled,
QuestionCircleFilled,
SearchOutlined,
SmileFilled,
TabletFilled,
} from '@ant-design/icons';
import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
import { Button, Input } from 'antd';
import { useState } from 'react';
import { demoOnMenuHeaderClick } from './_demoHandlers';
const Demo = () => {
const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
return (
<ProLayout
collapsed={true}
collapsedButtonRender={false}
layout="side"
route={{
routes: [
{
path: '/welcome',
name: '欢迎',
icon: <SmileFilled />,
component: './Welcome',
},
{
path: '/admin',
name: '管理',
icon: <CrownFilled />,
access: 'canAdmin',
component: './Admin',
},
{
path: '/list',
name: '列表',
icon: <TabletFilled />,
access: 'canAdmin',
component: './Admin',
},
],
}}
location={{
pathname,
}}
menu={{
type: 'group',
collapsedShowTitle: true,
}}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: '书琰',
}}
actionsRender={(props) => {
if (props.isMobile) return [];
return [
props.layout !== 'side' ? (
<div
key="SearchOutlined"
aria-hidden
style={{
display: 'flex',
alignItems: 'center',
marginInlineEnd: 24,
}}
onMouseDown={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<Input
style={{
borderRadius: 4,
marginInlineEnd: 12,
backgroundColor: 'rgba(0,0,0,0.03)',
}}
prefix={
<SearchOutlined
style={{
color: 'rgba(0, 0, 0, 0.15)',
}}
/>
}
placeholder="搜索方案"
variant="borderless"
/>
<PlusCircleFilled
style={{
color: 'var(--ant-primary-color)',
fontSize: 24,
}}
/>
</div>
) : undefined,
<InfoCircleFilled key="InfoCircleFilled" />,
<QuestionCircleFilled key="QuestionCircleFilled" />,
<GithubFilled key="GithubFilled" />,
];
}}
menuFooterRender={(props) => {
if (props?.collapsed) return undefined;
return (
<p
style={{
textAlign: 'center',
paddingBlockStart: 12,
}}
>
Power by Ant Design
</p>
);
}}
onMenuHeaderClick={demoOnMenuHeaderClick}
menuItemRender={(item, dom) => (
<a
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</a>
)}
>
<PageContainer
extra={[
<Button key="3">操作</Button>,
<Button key="2">操作</Button>,
<Button key="1" type="primary">
主操作
</Button>,
]}
footer={[
<Button key="3">重置</Button>,
<Button key="2" type="primary">
提交
</Button>,
]}
>
<ProCard
style={{
height: '200vh',
minHeight: 800,
}}
>
<div />
</ProCard>
</PageContainer>
</ProLayout>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
不分组菜单样式
import {
CrownOutlined,
InfoCircleOutlined,
MergeCellsOutlined,
QuestionCircleOutlined,
TabletOutlined,
UserOutlined,
} from '@ant-design/icons';
import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
import { Button, Result } from 'antd';
import { useState } from 'react';
import { demoOnMenuHeaderClick } from './_demoHandlers';
const Demo = () => {
const [pathname, setPathname] = useState('/welcome');
return (
<div
id="test-pro-layout"
style={{
height: '100vh',
}}
>
<ProLayout
fixSiderbar
route={{
path: '/',
routes: [
{
path: '/admin',
name: '管理页',
icon: <CrownOutlined />,
access: 'canAdmin',
component: './Admin',
routes: [
{
path: '/admin/sub-page1',
name: '一级页面',
icon: <CrownOutlined />,
component: './Welcome',
},
{
path: '/admin/sub-page2',
name: '二级页面',
icon: <CrownOutlined />,
component: './Welcome',
},
{
path: '/admin/sub-page3',
name: '三级页面',
icon: <CrownOutlined />,
component: './Welcome',
},
],
},
{
name: '列表页',
icon: <TabletOutlined />,
path: '/list',
component: './ListTableList',
routes: [
{
path: '/list/sub-page2',
name: '二级列表页面',
icon: <CrownOutlined />,
component: './Welcome',
},
{
path: '/list/sub-page3',
name: '三级列表页面',
icon: <CrownOutlined />,
component: './Welcome',
},
],
},
],
}}
location={{
pathname,
}}
waterMarkProps={{
content: 'Pro Layout',
}}
avatarProps={{
icon: <UserOutlined />,
size: 'small',
title: '书琰',
}}
actionsRender={() => [
<InfoCircleOutlined key="InfoCircleOutlined" />,
<QuestionCircleOutlined key="QuestionCircleOutlined" />,
<MergeCellsOutlined key="MergeCellsOutlined" />,
]}
menuFooterRender={(props) => {
if (props?.collapsed) return undefined;
return (
<p
style={{
textAlign: 'center',
color: 'rgba(0,0,0,0.6)',
paddingBlockStart: 12,
}}
>
Power by Ant Design
</p>
);
}}
onMenuHeaderClick={demoOnMenuHeaderClick}
menuItemRender={(item, dom) => (
<a
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</a>
)}
>
<PageContainer>
<ProCard
style={{
height: '120vh',
minHeight: 600,
}}
>
<Result
status="404"
style={{
height: '100%',
}}
title="Hello World"
subTitle="Sorry, you are not authorized to access this page."
extra={<Button type="primary">Back Home</Button>}
/>
</ProCard>
</PageContainer>
</ProLayout>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
搜索菜单
import { PlusCircleFilled, SearchOutlined } from '@ant-design/icons';
import type { MenuDataItem } from '@ant-design/pro-components';
import { PageContainer, ProLayout } from '@ant-design/pro-components';
import { Input, Space } from 'antd';
import { useState } from 'react';
import complexMenu from './complex-menu';
const filterByMenuData = (
data: MenuDataItem[],
keyWord: string,
): MenuDataItem[] =>
data
.map((item) => {
if (item.name?.includes(keyWord)) {
return { ...item };
}
const children = filterByMenuData(item.children || [], keyWord);
if (children.length > 0) {
return { ...item, children };
}
return undefined;
})
.filter((item) => item) as MenuDataItem[];
const loopMenuItem = (menus: any[]): MenuDataItem[] =>
menus.map(({ icon: _icon, routes, ...item }) => ({
...item,
children: routes && loopMenuItem(routes),
}));
const Demo = () => {
const [keyWord, setKeyWord] = useState('');
return (
<div
style={{
height: '100vh',
}}
>
<ProLayout
location={{
pathname: '/home/overview',
}}
menuExtraRender={({ collapsed }) =>
!collapsed && (
<Space
style={{
marginBlockStart: 16,
}}
align="center"
>
<Input
style={{
borderRadius: 4,
backgroundColor: 'rgba(0,0,0,0.03)',
}}
prefix={
<SearchOutlined
style={{
color: 'rgba(0, 0, 0, 0.15)',
}}
/>
}
placeholder="搜索方案"
variant="borderless"
onPressEnter={(e) => {
setKeyWord((e.target as HTMLInputElement).value);
}}
/>
<PlusCircleFilled
style={{
color: 'var(--ant-primary-color)',
fontSize: 24,
}}
/>
</Space>
)
}
menuDataRender={() => loopMenuItem(complexMenu)}
postMenuData={(menus) => filterByMenuData(menus || [], keyWord)}
>
<PageContainer content="欢迎使用">
<div>Hello World</div>
</PageContainer>
</ProLayout>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
菜单展开-站点地图
import {
GithubFilled,
InfoCircleFilled,
PlusCircleFilled,
QuestionCircleFilled,
SearchOutlined,
} from '@ant-design/icons';
import type { ProSettings } from '@ant-design/pro-components';
import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
import { Input } from 'antd';
import { useState } from 'react';
import defaultProps from './_defaultProps';
import { demoOnMenuHeaderClick } from './_demoHandlers';
const Demo = () => {
const settings: ProSettings | undefined = {
fixSiderbar: true,
layout: 'mix',
splitMenus: true,
};
const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
return (
<div
id="test-pro-layout"
style={{
height: '100vh',
}}
>
<ProLayout
bgLayoutImgList={[
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
left: 85,
bottom: 100,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
bottom: -68,
right: -45,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
bottom: 0,
left: 0,
width: '331px',
},
]}
{...defaultProps}
location={{
pathname,
}}
menu={{
type: 'group',
}}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: '书琰',
}}
actionsRender={(props) => {
if (props.isMobile) return [];
return [
props.layout !== 'side' && document.body.clientWidth > 1400 ? (
<div
key="SearchOutlined"
aria-hidden
style={{
display: 'flex',
alignItems: 'center',
marginInlineEnd: 24,
}}
onMouseDown={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<Input
style={{
borderRadius: 4,
marginInlineEnd: 12,
backgroundColor: 'rgba(0,0,0,0.03)',
}}
prefix={
<SearchOutlined
style={{
color: 'rgba(0, 0, 0, 0.15)',
}}
/>
}
placeholder="搜索方案"
variant="borderless"
/>
<PlusCircleFilled
style={{
color: 'var(--ant-primary-color)',
fontSize: 24,
}}
/>
</div>
) : undefined,
<InfoCircleFilled key="InfoCircleFilled" />,
<QuestionCircleFilled key="QuestionCircleFilled" />,
<GithubFilled key="GithubFilled" />,
];
}}
menuFooterRender={(props) => {
if (props?.collapsed) return undefined;
return (
<div
style={{
textAlign: 'center',
paddingBlockStart: 12,
}}
>
<div>© 2021 Made with love</div>
<div>by Ant Design</div>
</div>
);
}}
onMenuHeaderClick={demoOnMenuHeaderClick}
menuItemRender={(item, dom) => (
<div
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</div>
)}
{...settings}
>
<PageContainer>
<ProCard
style={{
height: '100vh',
minHeight: 800,
}}
>
<div />
</ProCard>
</PageContainer>
</ProLayout>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
多个路由对应一个菜单项
import { PageContainer, ProLayout } from '@ant-design/pro-components';
const Demo = () => (
<div
style={{
height: '100vh',
}}
>
<ProLayout
location={{
pathname: '/config/template/new',
}}
route={{
routes: [
{
path: '/config',
name: '配置中心',
routes: [
{
path: 'product',
name: '产品配置',
indexRoute: {
component: 'ConfigProduct/index',
},
routes: [
{
path: 'new',
component: 'ConfigProduct/NewConfig',
},
{
path: 'edit/:productKey',
component: 'ConfigProduct/NewConfig',
},
{
path: 'detail/:productKey',
component: 'ConfigProduct/Detail',
},
],
},
{
path: 'productManage',
name: '产品管理',
indexRoute: {
component: 'ConfigProductAll/index',
},
routes: [
{
path: 'detail/:productKey',
component: 'ConfigProductAll/Detail',
},
],
},
{
path: 'template',
name: '产品模板管理',
indexRoute: {
component: 'ConfigTemplate/index',
},
routes: [
{
path: 'new',
component: 'ConfigTemplate/NewConfig',
},
{
path: 'edit/:templateKey',
component: 'ConfigTemplate/NewConfig',
},
{
path: 'detail/:templateKey',
component: 'ConfigTemplate/Detail',
},
],
},
{
path: 'configItem',
name: '配置项模板管理',
indexRoute: {
component: 'ConfigItem/index',
},
routes: [
{
path: 'new',
component: 'ConfigItem/NewConfig',
},
{
path: 'edit/:productKey',
component: 'ConfigItem/NewConfig',
},
{
path: 'detail/:productKey',
component: 'ConfigItem/Detail',
},
],
},
{
path: 'meta',
name: '元数据管理',
component: 'ConfigMeta',
},
],
},
{
path: 'asset',
name: '资产',
routes: [
{
path: 'query',
name: '资产查询',
component: 'Asset',
},
{
path: 'collateral',
name: '抵押查询',
component: 'Collateral',
},
],
},
{
path: 'bill',
name: '账单',
routes: [
{
path: 'billNo',
name: '账单编号',
component: 'BillNo',
},
{
path: 'bill',
name: '账单查询',
component: 'Bill',
},
{
path: 'billItem',
name: '账单条目',
component: 'BillItem',
},
],
},
{
path: 'cif',
name: 'CIF',
routes: [
{
path: 'bankAccount',
name: '绑卡信息',
component: 'CifBankAccount',
},
{
path: 'userGroup',
name: '查询 Group',
component: 'CifUserGroup',
},
{
path: 'userId',
name: '查询 ID',
component: 'CifUserId',
},
{
path: 'newInstitution',
name: '新增机构',
indexRoute: {
component: 'CifNewInstitution/index',
},
routes: [
{
path: 'new',
component: 'CifNewInstitution/ApplyNew',
},
{
path: 'bind/:id',
component: 'CifNewInstitution/BindAccount',
},
],
},
],
},
{
path: 'tools',
name: '小工具',
routes: [
{
path: 'ttsql',
name: 'MySQL转BlinkTT流表',
component: 'ToolTT',
},
],
},
],
}}
>
<PageContainer content="欢迎使用">
<div>Hello World</div>
</PageContainer>
</ProLayout>
</div>
);
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#默认打开所有菜单
menu 配置 defaultOpenAll 可以默认打开所有菜单
import { PageContainer, ProLayout } from '@ant-design/pro-components';
import complexMenu from './complex-menu';
const Demo = () => (
<div
style={{
height: '100vh',
}}
>
<ProLayout
location={{
pathname: '/data_hui/data_hui2',
}}
collapsed={false}
collapsedButtonRender={false}
route={{
routes: complexMenu,
}}
menu={{ defaultOpenAll: true }}
>
<PageContainer content="欢迎使用">
<div>Hello World</div>
</PageContainer>
</ProLayout>
</div>
);
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#总是打开所有菜单
折叠按钮反复切换后 defaultOpenAll 将失效,menu 配置 ignoreFlatMenu 可以忽略手动折叠过的菜单,实现总是默认打开所有菜单。因为计算时机在组件渲染前,所以异步菜单不生效。
import { PageContainer, ProLayout } from '@ant-design/pro-components';
const Demo = () => (
<div
style={{
height: '100vh',
}}
>
<ProLayout
location={{
pathname: '/data_hui/data_hui2',
}}
collapsed={false}
collapsedButtonRender={false}
route={{
routes: [
{
path: '/home',
name: '首页',
locale: 'menu.home',
routes: [
{
path: '/home/overview',
name: '概述',
hideInMenu: true,
locale: 'menu.home.overview',
},
{
path: '/home/search',
name: '搜索',
hideInMenu: true,
locale: 'menu.home.search',
},
],
},
{
path: '/data_hui',
name: '汇总数据',
locale: 'menu.data_hui',
routes: [
{
collapsed: true,
menuName: '域买家维度交易',
name: '域买家维度交易',
path: '/xx',
routes: [
{
id: 2,
name: '月表',
path: '/data_hui2',
},
{
name: '日表',
path: '/data_hui3?tableName=adm_rk_cr_tb_trv_byr_ds&tableSchema=alifin_odps_birisk',
},
],
},
{
name: '维度交易',
path: '/',
routes: [
{
name: '月表',
path: '/data_hui4',
},
{
name: '日表',
key: 'tableName=adm_rk_cr_tb_trv_byr_ds&tableSchema=alifin_odps_birisk',
path: '/data_hui5',
},
],
},
],
},
],
}}
menu={{
defaultOpenAll: true,
ignoreFlatMenu: true,
}}
>
<PageContainer content="欢迎使用">
<div>Hello World</div>
</PageContainer>
</ProLayout>
</div>
);
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#从服务器加载 menu
ProLayout 提供了强大的菜单功能,但是这样必然会封装很多行为,导致需要一些特殊逻辑的用户感到不满。所以我们提供了很多的 API,期望可以满足绝大部分客户的方式。
从服务器加载 menu 主要使用的 API 是 menuDataRender 和 menuRender,menuDataRender可以控制当前的菜单数据,menuRender可以控制菜单的 dom 节点。
import { PageContainer, ProLayout } from '@ant-design/pro-components';
import { Button, Switch } from 'antd';
import { useRef, useState } from 'react';
import customMenuDate from './custom-menu';
const waitTime = (time: number = 100) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, time);
});
};
let serviceData: any[] = customMenuDate;
const Demo = () => {
const actionRef = useRef<{
reload: () => void;
}>();
const [toggle, setToggle] = useState(false);
return (
<>
<ProLayout
style={{
height: '100vh',
}}
actionRef={actionRef}
suppressSiderWhenMenuEmpty={toggle}
menu={{
request: async () => {
await waitTime(2000);
return serviceData;
},
}}
location={{
pathname: '/welcome/welcome',
}}
>
<PageContainer content="欢迎使用">
<div>
当从服务器获取的菜单为空时隐藏Sider:
<Switch checked={toggle} onChange={setToggle} />
</div>
Hello World
<Button
style={{
margin: 8,
}}
onClick={() => {
serviceData = customMenuDate;
actionRef.current?.reload();
}}
>
刷新菜单
</Button>
<Button
style={{
margin: 8,
}}
onClick={() => {
serviceData = [];
actionRef.current?.reload();
}}
>
刷新菜单(空数据)
</Button>
</PageContainer>
</ProLayout>
</>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#自定义 menu 的内容
通过 menuItemRender, subMenuItemRender,title,logo,menuHeaderRender 可以非常方便的自定义 menu 的样式。如果实在是不满意,可以使用 menuRender 完全的自定义。
import { PageContainer, ProLayout } from '@ant-design/pro-components';
import defaultProps from './_defaultProps';
const Demo = () => {
return (
<div
style={{
height: '100vh',
}}
>
<ProLayout
menuItemRender={(item, dom) => (
<div
style={{
display: 'flex',
alignItems: 'center',
gap: 8,
}}
>
pre {dom}
</div>
)}
subMenuItemRender={(_, dom) => (
<div
style={{
display: 'flex',
alignItems: 'center',
gap: 8,
}}
>
pre {dom}
</div>
)}
title="Remax"
logo="https://gw.alipayobjects.com/mdn/rms_b5fcc5/afts/img/A*1NHAQYduQiQAAAAAAAAAAABkARQnAQ"
menuHeaderRender={(logo, title) => (
<div
id="customize_menu_header"
style={{
height: '32px',
display: 'flex',
alignItems: 'center',
gap: 8,
}}
onClick={() => {
window.open('https://remaxjs.org/');
}}
>
{logo}
{title}
</div>
)}
{...defaultProps}
location={{
pathname: '/welcome',
}}
>
<PageContainer content="欢迎使用">Hello World</PageContainer>
</ProLayout>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#自定义页脚与工具栏
ProLayout 默认不提供页脚,要是和 Pro 官网相同的样式,需要自己引入一下页脚。
import {
DefaultFooter,
PageContainer,
ProLayout,
} from '@ant-design/pro-components';
import defaultProps from './_defaultProps';
const Demo = () => (
<ProLayout
{...defaultProps}
style={{
height: '100vh',
}}
breakpoint={false}
collapsed
location={{
pathname: '/welcome',
}}
footerRender={() => (
<DefaultFooter
links={[
{
key: 'procomponents',
title: 'ProComponents',
href: 'https://procomponents.ant.design/',
},
{
key: 'antd',
title: 'Ant Design',
href: 'https://ant.design/',
},
]}
copyright="ProComponents 页脚示例"
/>
)}
>
<PageContainer content="欢迎使用">Hello World</PageContainer>
</ProLayout>
);
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
页脚工具栏和全局公告
import {
GithubFilled,
InfoCircleFilled,
PlusCircleFilled,
QuestionCircleFilled,
SearchOutlined,
} from '@ant-design/icons';
import type { ProSettings } from '@ant-design/pro-components';
import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
import { Alert, Button, Input, Space } from 'antd';
import React, { useState } from 'react';
import defaultProps from './_defaultProps';
const Demo = () => {
const settings: ProSettings | undefined = {
layout: 'mix',
splitMenus: true,
};
const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
return (
<>
<div
id="test-pro-layout"
style={{
height: '100vh',
}}
>
<ProLayout
token={{
header: {
heightLayoutHeader: 108,
},
}}
headerRender={(props, defaultDom) => (
<>
<Alert
title={
<div
style={{
color: 'white',
}}
>
本网站提供的部分服务在你当前浏览器中无法使用,建议你更换为
Chrome 浏览器查看本网站。
</div>
}
icon={
<InfoCircleFilled
style={{
color: 'white',
}}
/>
}
banner
style={{
backgroundColor: 'black',
}}
action={
<Button
type="text"
style={{
color: 'white',
}}
>
查看详情
</Button>
}
/>
{React.cloneElement(defaultDom as any, {
style: {
height: '56px',
lineHeight: '56px',
},
})}
</>
)}
footerRender={() => (
<Space
style={{
height: 64,
display: 'flex',
justifyContent: 'end',
alignItems: 'center',
marginInlineEnd: 24,
}}
>
<Button key="1">上一步</Button>
<Button key="2" type="primary">
保存
</Button>
</Space>
)}
bgLayoutImgList={[
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
left: 85,
bottom: 100,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
bottom: -68,
right: -45,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
bottom: 0,
left: 0,
width: '331px',
},
]}
{...defaultProps}
location={{
pathname,
}}
menu={{
type: 'group',
}}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: '书琰',
}}
actionsRender={(props) => {
if (props.isMobile) return [];
return [
props.layout !== 'side' && document.body.clientWidth > 1400 ? (
<div
key="SearchOutlined"
aria-hidden
style={{
display: 'flex',
alignItems: 'center',
marginInlineEnd: 24,
}}
onMouseDown={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<Input
style={{
borderRadius: 4,
marginInlineEnd: 12,
backgroundColor: 'rgba(0,0,0,0.03)',
}}
prefix={
<SearchOutlined
style={{
color: 'rgba(0, 0, 0, 0.15)',
}}
/>
}
placeholder="搜索方案"
variant="borderless"
/>
<PlusCircleFilled
style={{
color: 'var(--ant-primary-color)',
fontSize: 24,
}}
/>
</div>
) : undefined,
<InfoCircleFilled key="InfoCircleFilled" />,
<QuestionCircleFilled key="QuestionCircleFilled" />,
<GithubFilled key="GithubFilled" />,
];
}}
menuFooterRender={(props) => {
if (props?.collapsed) return undefined;
return (
<div
style={{
textAlign: 'center',
paddingBlockStart: 12,
}}
>
<div>© 2021 Made with love</div>
<div>by Ant Design</div>
</div>
);
}}
menuItemRender={(item, dom) => (
<div
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</div>
)}
{...settings}
>
<PageContainer>
<ProCard
style={{
minHeight: 800,
}}
>
<div />
</ProCard>
</PageContainer>
</ProLayout>
</div>
</>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#布局嵌套与导航
嵌套布局
import { UserOutlined } from '@ant-design/icons';
import { PageContainer, ProLayout } from '@ant-design/pro-components';
import complexMenu from './complex-menu';
const Demo = () => (
<div
style={{
height: '100vh',
}}
>
<ProLayout
location={{
pathname: '/home',
}}
fixSiderbar={false}
collapsedButtonRender={false}
collapsed={false}
iconfontUrl="//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js"
route={{
routes: [
{
path: '/home',
name: '收藏',
icon: 'icon-shoucang1',
},
{
path: '/home/overview',
name: 'FaceBook',
icon: 'icon-facebook',
},
{
path: '/home/search',
name: 'Twitter',
icon: 'icon-twitter',
},
],
}}
headerRender={false}
>
<ProLayout
location={{
pathname: '/home/overview',
}}
fixSiderbar={false}
route={{
routes: complexMenu,
}}
style={{
height: '400px',
}}
avatarProps={{
icon: <UserOutlined />,
}}
menuHeaderRender={false}
>
<PageContainer content="欢迎使用">
<div>Hello World</div>
</PageContainer>
</ProLayout>
</ProLayout>
</div>
);
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
面包屑显示在顶部
import {
PageContainer,
ProBreadcrumb,
ProLayout,
} from '@ant-design/pro-components';
const Demo = () => (
<div
style={{
height: '100vh',
}}
>
<ProLayout
location={{
pathname: '/admin/process/edit/123',
}}
layout="mix"
ErrorBoundary={false}
headerContentRender={() => <ProBreadcrumb />}
breadcrumbRender={(routers = []) => [
{
path: '/',
title: '主页',
},
{
path: '/',
title: '测试页',
},
...routers,
]}
menuDataRender={() => [
{
path: '/welcome',
name: '欢迎',
},
{
path: '/admin',
name: '管理',
children: [
{
name: '申请单列表',
path: '/admin/process',
},
{
name: '申请单详情',
path: '/admin/process/detail/:id',
hideInMenu: true,
},
{
name: '编辑申请单',
path: '/admin/process/edit/:id',
hideInMenu: true,
},
{
name: '添加申请单',
path: '/admin/process/add',
hideInMenu: true,
},
],
},
]}
>
<PageContainer content="欢迎使用" breadcrumbRender={false}>
<div>Hello World</div>
</PageContainer>
</ProLayout>
</div>
);
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
多级站点导航
import {
CrownOutlined,
LeftOutlined,
SmileOutlined,
UserOutlined,
} from '@ant-design/icons';
import { ProLayout } from '@ant-design/pro-components';
import { Button, Result, Space, Tag } from 'antd';
import { useState } from 'react';
import { demoOnMenuHeaderClick } from './_demoHandlers';
const defaultProps = {
routes: [
{
path: '/admin/sub-page1',
name: '一级页面',
icon: <CrownOutlined />,
component: './Welcome',
},
{
path: '/admin/sub-page2',
name: '二级页面',
icon: <UserOutlined />,
component: './Welcome',
},
{
path: '/admin/sub-page3',
name: '三级页面',
icon: <SmileOutlined />,
component: './Welcome',
},
],
};
const defaultHomeProps = {
routes: [
{
path: '/',
name: '首页',
icon: <CrownOutlined />,
component: './Welcome',
},
{
path: '/admin/sub-page1',
name: '详情页',
icon: <SmileOutlined />,
component: './Welcome',
},
],
};
const Demo = () => {
const [pathname, setPathname] = useState('/admin/sub-page1');
return (
<>
<ProLayout
route={pathname === '/' ? defaultHomeProps : defaultProps}
location={{
pathname,
}}
logoStyle={{
backgroundColor: '#eee',
}}
menuHeaderRender={(logo, title, props) => {
if (pathname === '/') {
return (
<a>
{logo}
{title}
</a>
);
}
if (props?.collapsed) {
return (
<Space>
<LeftOutlined
style={{
fontSize: 24,
}}
/>
</Space>
);
}
return (
<Space orientation="vertical">
<Button
size="small"
icon={<LeftOutlined />}
onClick={() => {
setPathname('/');
}}
>
返回应用列表
</Button>
<b>alipay_dev_gzone</b>
<span>Creation Time 2017-01-10</span>
<Tag color="warning">运行中</Tag>
</Space>
);
}}
fixSiderbar
headerRender={() => {
return null;
}}
onMenuHeaderClick={demoOnMenuHeaderClick}
menuItemRender={(item, dom) => (
<a
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</a>
)}
avatarProps={{
icon: <UserOutlined />,
}}
>
<div
style={{
height: '120vh',
minHeight: 600,
}}
>
<Result
status="404"
style={{
height: '100%',
background: '#fff',
}}
icon={
<img
src="https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*aPIBS5gRPu4AAAAAAAAAAAAAARQnAQ"
alt="404"
/>
}
title="Hello World"
subTitle="Sorry, you are not authorized to access this page."
extra={<Button type="primary">Back Home</Button>}
/>
</div>
</ProLayout>
</>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
沉浸式导航
import { CrownOutlined, SmileOutlined, UserOutlined } from '@ant-design/icons';
import { PageContainer, ProLayout } from '@ant-design/pro-components';
import { Button, Input, Result, Tag } from 'antd';
import { useState } from 'react';
import { demoOnMenuHeaderClick } from './_demoHandlers';
const defaultProps = {
routes: [
{
path: '/welcome',
name: '欢迎',
icon: <CrownOutlined />,
component: './Welcome',
},
{
path: '/admin/sub-page2',
name: '二级页面',
icon: <UserOutlined />,
component: './Welcome',
},
{
path: '/admin/sub-page3',
name: '三级页面',
icon: <SmileOutlined />,
component: './Welcome',
},
],
};
const Demo = () => {
const [pathname, setPathname] = useState('/welcome');
return (
<>
<ProLayout
route={defaultProps}
location={{
pathname,
}}
fixSiderbar
headerRender={false}
onMenuHeaderClick={demoOnMenuHeaderClick}
menuItemRender={(item, dom) => (
<a
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</a>
)}
avatarProps={{
icon: <UserOutlined />,
}}
>
<PageContainer
onBack={() => null}
tags={<Tag color="blue">状态一</Tag>}
header={{
style: {
padding: '8px 16px',
backgroundColor: '#fff',
position: 'fixed',
top: 0,
width: '100%',
left: 0,
zIndex: 999,
boxShadow: '0 2px 8px #f0f1f2',
},
}}
style={{
paddingBlockStart: 48,
}}
extra={[
<Input.Search
key="search"
style={{
width: 240,
}}
/>,
<Button key="3">操作一</Button>,
<Button key="2" type="primary">
操作一
</Button>,
]}
>
<div
style={{
height: '120vh',
minHeight: 600,
}}
>
<Result
status="404"
style={{
height: '100%',
background: '#fff',
}}
title="Hello World"
subTitle="Sorry, you are not authorized to access this page."
extra={<Button type="primary">Back Home</Button>}
/>
</div>
</PageContainer>
</ProLayout>
</>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#跨站点导航
使用默认卡片展示,请确保每一项都有 desc,且值为真;使用分组展示,请确保每一项都有 children,且长度大于 0;
跨站点导航列表 分组模式
import type { AppItemProps, ProSettings } from '@ant-design/pro-components';
import { ProConfigProvider, ProLayout } from '@ant-design/pro-components';
import { Modal } from 'antd';
import defaultProps from './_defaultProps';
const AppGroupList: any = [
{
title: 'UI 设计语言',
desc: '杭州市较知名的 UI 设计语言',
children: [
{
icon: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
title: 'Ant Design',
desc: '杭州市较知名的 UI 设计语言',
url: 'https://ant.design',
},
{
icon: 'https://gw.alipayobjects.com/zos/antfincdn/upvrAjAPQX/Logo_Tech%252520UI.svg',
title: 'Pro Components',
desc: '专业级 UI 组件库',
url: 'https://procomponents.ant.design/',
},
],
},
{
title: 'UI 设计语言 2组111',
icon: 'https://gw.alipayobjects.com/zos/antfincdn/upvrAjAPQX/Logo_Tech%252520UI.svg',
desc: '专业级 UI 组件库',
url: 'https://procomponents.ant.design/',
children: [
{
icon: 'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
title: 'AntV',
desc: '蚂蚁集团全新一代数据可视化解决方案',
url: 'https://antv.vision/',
target: '_blank',
},
{
icon: 'https://gw.alipayobjects.com/zos/antfincdn/FLrTNDvlna/antv.png',
title: 'AntV',
desc: '蚂蚁集团全新一代数据可视化解决方案',
url: 'https://antv.vision/',
target: '_blank',
},
],
},
{
title: '待分组',
desc: '专业级 UI 组件库',
children: [
{
title: '工具',
desc: '知识创作与分享工具',
icon: 'https://gw.alipayobjects.com/zos/rmsportal/XuVpGqBFxXplzvLjJBZB.svg',
url: 'https://www.yuque.com/',
},
{
title: '前端应用框架',
desc: '插件化的企业级前端应用框架。',
icon: 'https://img.alicdn.com/tfs/TB1zomHwxv1gK0jSZFFXXb0sXXa-200-200.png',
url: 'https://umijs.org/zh-CN/docs',
},
{
icon: 'https://gw.alipayobjects.com/zos/bmw-prod/8a74c1d3-16f3-4719-be63-15e467a68a24/km0cv8vn_w500_h500.png',
title: 'qiankun',
desc: '可能是你见过最完善的微前端解决方案🧐',
url: 'https://qiankun.umijs.org/',
},
{
icon: 'https://gw.alipayobjects.com/zos/rmsportal/LFooOLwmxGLsltmUjTAP.svg',
title: 'Kitchen ',
desc: 'Sketch 工具集',
url: 'https://kitchen.alipay.com/',
},
{
icon: 'https://gw.alipayobjects.com/zos/bmw-prod/d3e3eb39-1cd7-4aa5-827c-877deced6b7e/lalxt4g3_w256_h256.png',
title: 'dumi',
desc: '为组件开发场景而生的文档工具',
url: 'https://d.umijs.org/zh-CN',
},
],
},
];
const Demo = () => {
const settings: ProSettings = {
fixSiderbar: true,
layout: 'mix',
splitMenus: true,
};
const itemClick = (
item: AppItemProps,
popoverRef?: React.RefObject<HTMLSpanElement>,
) => {
// 点击后关闭 Popover
popoverRef?.current?.click?.();
Modal?.confirm({
width: 600,
title: '点击项 详细数据',
content: (
<pre style={{ overflow: 'auto' }}>
{JSON.stringify(typeof item === 'object' ? item : {}, null, 2)}
</pre>
),
okText: '前往',
onOk: () => window.open(item?.url),
});
};
return (
<div id="test-pro-layout" style={{ height: '100vh' }}>
<ProConfigProvider hashed={false}>
<ProLayout
{...defaultProps}
appList={AppGroupList}
itemClick={itemClick}
location={{ pathname: '/list/sub-page/sub-sub-page1' }}
siderMenuType="group"
menu={{ collapsedShowGroupTitle: true }}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: '书琰',
}}
{...settings}
/>
</ProConfigProvider>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#主题与样式定制
通过 token 修改样式
import {
GithubFilled,
InfoCircleFilled,
PlusCircleFilled,
QuestionCircleFilled,
SearchOutlined,
} from '@ant-design/icons';
import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
import { Button, Input } from 'antd';
import { useState } from 'react';
import defaultProps from './_defaultProps';
import { demoOnMenuHeaderClick } from './_demoHandlers';
const Demo = () => {
const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
return (
<ProLayout
splitMenus
token={{
colorBgAppListIconHover: 'rgba(0,0,0,0.06)',
colorTextAppListIconHover: 'rgba(255,255,255,0.95)',
colorTextAppListIcon: 'rgba(255,255,255,0.85)',
sider: {
colorBgCollapsedButton: '#fff',
colorTextCollapsedButtonHover: 'rgba(0,0,0,0.65)',
colorTextCollapsedButton: 'rgba(0,0,0,0.45)',
colorMenuBackground: '#004FD9',
colorBgMenuItemCollapsedElevated: 'rgba(0,0,0,0.85)',
colorMenuItemDivider: 'rgba(255,255,255,0.15)',
colorBgMenuItemHover: 'rgba(0,0,0,0.06)',
colorBgMenuItemSelected: 'rgba(0,0,0,0.15)',
colorTextMenuSelected: '#fff',
colorTextMenuItemHover: 'rgba(255,255,255,0.75)',
colorTextMenu: 'rgba(255,255,255,0.75)',
colorTextMenuSecondary: 'rgba(255,255,255,0.65)',
colorTextMenuTitle: 'rgba(255,255,255,0.95)',
colorTextMenuActive: 'rgba(255,255,255,0.95)',
colorTextSubMenuSelected: '#fff',
},
header: {
colorBgHeader: '#004FD9',
colorBgRightActionsItemHover: 'rgba(0,0,0,0.06)',
colorTextRightActionsItem: 'rgba(255,255,255,0.65)',
colorHeaderTitle: '#fff',
colorBgMenuItemHover: 'rgba(0,0,0,0.06)',
colorBgMenuItemSelected: 'rgba(0,0,0,0.15)',
colorTextMenuSelected: '#fff',
colorTextMenu: 'rgba(255,255,255,0.75)',
colorTextMenuSecondary: 'rgba(255,255,255,0.65)',
colorTextMenuActive: 'rgba(255,255,255,0.95)',
},
}}
{...defaultProps}
location={{
pathname,
}}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: '书琰',
}}
actionsRender={(props) => {
if (props.isMobile) return [];
return [
props.layout !== 'side' ? (
<div
key="SearchOutlined"
aria-hidden
style={{
display: 'flex',
alignItems: 'center',
marginInlineEnd: 24,
}}
onMouseDown={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<Input
style={{
borderRadius: 4,
marginInlineEnd: 12,
backgroundColor: 'rgba(0,0,0,0.03)',
}}
prefix={
<SearchOutlined
style={{
color: 'rgba(0, 0, 0, 0.15)',
}}
/>
}
placeholder="搜索方案"
variant="borderless"
/>
<PlusCircleFilled
style={{
color: 'var(--ant-primary-color)',
fontSize: 24,
}}
/>
</div>
) : undefined,
<InfoCircleFilled key="InfoCircleFilled" />,
<QuestionCircleFilled key="QuestionCircleFilled" />,
<GithubFilled key="GithubFilled" />,
];
}}
menuFooterRender={(props) => {
if (props?.collapsed) return undefined;
return (
<p
style={{
textAlign: 'center',
paddingBlockStart: 12,
}}
>
Power by Ant Design
</p>
);
}}
onMenuHeaderClick={demoOnMenuHeaderClick}
menuItemRender={(item, dom) => (
<a
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</a>
)}
layout="top"
>
<PageContainer
extra={[
<Button key="3">操作</Button>,
<Button key="2">操作</Button>,
<Button key="1" type="primary">
主操作
</Button>,
]}
footer={[
<Button key="3">重置</Button>,
<Button key="2" type="primary">
提交
</Button>,
]}
>
<ProCard
style={{
height: '200vh',
minHeight: 800,
}}
>
<div />
</ProCard>
</PageContainer>
</ProLayout>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
黑色主题
import {
GithubFilled,
InfoCircleFilled,
PlusCircleFilled,
QuestionCircleFilled,
SearchOutlined,
} from '@ant-design/icons';
import {
PageContainer,
ProCard,
ProConfigProvider,
ProLayout,
} from '@ant-design/pro-components';
import { Button, Input } from 'antd';
import { useState } from 'react';
import defaultProps from './_defaultProps';
import { demoOnMenuHeaderClick } from './_demoHandlers';
const Demo = () => {
const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
return (
<ProConfigProvider dark={true}>
<ProLayout
{...defaultProps}
splitMenus
location={{
pathname,
}}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: '书琰',
}}
actionsRender={(props) => {
if (props.isMobile) return [];
return [
props.layout !== 'side' ? (
<div
key="SearchOutlined"
aria-hidden
style={{
display: 'flex',
alignItems: 'center',
marginInlineEnd: 24,
}}
onMouseDown={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<Input
style={{
borderRadius: 4,
marginInlineEnd: 12,
backgroundColor: 'rgba(0,0,0,0.03)',
}}
prefix={
<SearchOutlined
style={{
color: 'rgba(0, 0, 0, 0.15)',
}}
/>
}
placeholder="搜索方案"
variant="borderless"
/>
<PlusCircleFilled
style={{
color: 'var(--ant-primary-color)',
fontSize: 24,
}}
/>
</div>
) : undefined,
<InfoCircleFilled key="InfoCircleFilled" />,
<QuestionCircleFilled key="QuestionCircleFilled" />,
<GithubFilled key="GithubFilled" />,
];
}}
menuFooterRender={(props) => {
if (props?.collapsed) return undefined;
return (
<p
style={{
textAlign: 'center',
paddingBlockStart: 12,
}}
>
Power by Ant Design
</p>
);
}}
onMenuHeaderClick={demoOnMenuHeaderClick}
menuItemRender={(item, dom) => (
<a
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</a>
)}
>
<PageContainer
extra={[
<Button key="3">操作</Button>,
<Button key="2">操作</Button>,
<Button key="1" type="primary">
主操作
</Button>,
]}
footer={[
<Button key="3">重置</Button>,
<Button key="2" type="primary">
提交
</Button>,
]}
>
<ProCard
style={{
height: '200vh',
minHeight: 800,
}}
>
<div />
</ProCard>
</PageContainer>
</ProLayout>
</ProConfigProvider>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
通过调整页面背景内容调整整体氛围
import {
GithubFilled,
InfoCircleFilled,
PlusCircleFilled,
QuestionCircleFilled,
SearchOutlined,
} from '@ant-design/icons';
import type { ProSettings } from '@ant-design/pro-components';
import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
import { Input } from 'antd';
import { useState } from 'react';
import defaultProps from './_defaultProps';
import { demoOnMenuHeaderClick } from './_demoHandlers';
const Demo = () => {
const settings: Partial<ProSettings> | undefined = {
fixSiderbar: true,
layout: 'top',
splitMenus: true,
};
const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
return (
<div
id="test-pro-layout"
style={{
height: '100vh',
}}
>
<ProLayout
bgLayoutImgList={[
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
left: 85,
bottom: 100,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
bottom: -68,
right: -45,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
bottom: 0,
left: 0,
width: '331px',
},
]}
{...defaultProps}
location={{
pathname,
}}
menu={{
type: 'group',
}}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: <div>书琰</div>,
}}
actionsRender={(props) => {
if (props.isMobile) return [];
return [
props.layout !== 'side' && document.body.clientWidth > 1400 ? (
<div
key="SearchOutlined"
aria-hidden
style={{
display: 'flex',
alignItems: 'center',
marginInlineEnd: 24,
}}
onMouseDown={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<Input
style={{
borderRadius: 4,
marginInlineEnd: 12,
}}
prefix={<SearchOutlined />}
placeholder="搜索方案"
variant="borderless"
/>
<PlusCircleFilled
style={{
color: 'var(--ant-primary-color)',
fontSize: 24,
}}
/>
</div>
) : undefined,
<InfoCircleFilled key="InfoCircleFilled" />,
<QuestionCircleFilled key="QuestionCircleFilled" />,
<GithubFilled key="GithubFilled" />,
];
}}
menuFooterRender={(props) => {
if (props?.collapsed) return undefined;
return (
<div
style={{
textAlign: 'center',
paddingBlockStart: 12,
}}
>
<div>© 2021 Made with love</div>
<div>by Ant Design</div>
</div>
);
}}
onMenuHeaderClick={demoOnMenuHeaderClick}
menuItemRender={(item, dom) => (
<a
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</a>
)}
{...settings}
>
<PageContainer>
<ProCard
style={{
height: '200vh',
minHeight: 800,
}}
>
<div />
</ProCard>
</PageContainer>
</ProLayout>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
定制菜单样式
import {
GithubFilled,
InfoCircleFilled,
PlusCircleFilled,
QuestionCircleFilled,
SearchOutlined,
} from '@ant-design/icons';
import type { ProSettings } from '@ant-design/pro-components';
import {
PageContainer,
ProCard,
ProLayout,
SettingDrawer,
} from '@ant-design/pro-components';
import { Button, Input } from 'antd';
import { useState } from 'react';
import defaultProps from './_defaultProps';
import { demoOnMenuHeaderClick } from './_demoHandlers';
const Demo = () => {
const [settings, setSettings] = useState<Partial<ProSettings> | undefined>({
fixSiderbar: true,
layout: 'mix',
splitMenus: true,
});
const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
return (
<div
id="test-pro-layout"
style={{
height: '100vh',
}}
>
<ProLayout
token={{
header: {
colorBgHeader: '#292f33',
colorHeaderTitle: '#fff',
colorTextMenu: '#dfdfdf',
colorTextMenuSecondary: '#dfdfdf',
colorTextMenuSelected: '#fff',
colorBgMenuItemSelected: '#22272b',
colorTextRightActionsItem: '#dfdfdf',
},
sider: {
colorMenuBackground: '#fff',
colorMenuItemDivider: '#dfdfdf',
colorTextMenu: '#595959',
colorTextMenuSelected: 'rgba(42,122,251,1)',
colorBgMenuItemSelected: 'rgba(230,243,254,1)',
},
}}
bgLayoutImgList={[
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
left: 85,
bottom: 100,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
bottom: -68,
right: -45,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
bottom: 0,
left: 0,
width: '331px',
},
]}
{...defaultProps}
location={{
pathname,
}}
menu={{
type: 'group',
}}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: (
<div
style={{
color: '#dfdfdf',
}}
>
书琰
</div>
),
}}
actionsRender={(props) => {
if (props.isMobile) return [];
return [
props.layout !== 'side' && document.body.clientWidth > 1400 ? (
<div
key="SearchOutlined"
aria-hidden
style={{
display: 'flex',
alignItems: 'center',
marginInlineEnd: 24,
}}
onMouseDown={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<Input
style={{
borderRadius: 4,
marginInlineEnd: 12,
backgroundColor: 'rgba(57,62,67,1)',
color: '#fff',
}}
prefix={
<SearchOutlined
style={{
color: '#dfdfdf',
}}
/>
}
placeholder="搜索方案"
variant="borderless"
/>
<PlusCircleFilled
style={{
color: 'var(--ant-primary-color)',
fontSize: 24,
}}
/>
</div>
) : undefined,
<InfoCircleFilled key="InfoCircleFilled" />,
<QuestionCircleFilled key="QuestionCircleFilled" />,
<GithubFilled key="GithubFilled" />,
];
}}
menuFooterRender={(props) => {
if (props?.collapsed) return undefined;
return (
<div
style={{
textAlign: 'center',
paddingBlockStart: 12,
}}
>
<div>© 2021 Made with love</div>
<div>by Ant Design</div>
</div>
);
}}
onMenuHeaderClick={demoOnMenuHeaderClick}
menuItemRender={(item, dom) => (
<a
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</a>
)}
{...settings}
>
<PageContainer
extra={[
<Button key="3">操作</Button>,
<Button key="2">操作</Button>,
<Button key="1" type="primary">
主操作
</Button>,
]}
footer={[
<Button key="3">重置</Button>,
<Button key="2" type="primary">
提交
</Button>,
]}
>
<ProCard
style={{
height: '200vh',
minHeight: 800,
}}
>
<div />
</ProCard>
</PageContainer>
</ProLayout>
<SettingDrawer
pathname={pathname}
enableDarkTheme
getContainer={() => document.getElementById('test-pro-layout')}
settings={settings}
onSettingChange={(changeSetting) => {
setSettings(changeSetting);
}}
disableUrlParams={false}
/>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
侧栏导航宽度 256px
import {
GithubFilled,
InfoCircleFilled,
QuestionCircleFilled,
} from '@ant-design/icons';
import type { ProSettings } from '@ant-design/pro-components';
import {
PageContainer,
ProCard,
ProLayout,
SettingDrawer,
} from '@ant-design/pro-components';
import { useState } from 'react';
import defaultProps from './_defaultProps';
const Demo = () => {
const [settings, setSettings] = useState<Partial<ProSettings> | undefined>({
layout: 'side',
});
const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
return (
<div
id="test-pro-layout"
style={{
height: '100vh',
}}
>
<ProLayout
siderWidth={256}
bgLayoutImgList={[
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
left: 85,
bottom: 100,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
bottom: -68,
right: -45,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
bottom: 0,
left: 0,
width: '331px',
},
]}
{...defaultProps}
location={{
pathname,
}}
menu={{
type: 'group',
}}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
title: '书琰',
}}
actionsRender={(props) => {
if (props.isMobile) return [];
return [
<InfoCircleFilled key="InfoCircleFilled" />,
<QuestionCircleFilled key="QuestionCircleFilled" />,
<GithubFilled key="GithubFilled" />,
];
}}
menuItemRender={(item, dom) => (
<div
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</div>
)}
{...settings}
>
<PageContainer>
<ProCard
style={{
height: '100vh',
minHeight: 800,
}}
>
<div />
</ProCard>
</PageContainer>
</ProLayout>
<SettingDrawer
pathname={pathname}
enableDarkTheme
getContainer={() => document.getElementById('test-pro-layout')}
settings={settings}
onSettingChange={(changeSetting) => {
setSettings(changeSetting);
}}
disableUrlParams={false}
/>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
通过设置页背景和卡片样式简化界面层次
import {
GithubFilled,
InfoCircleFilled,
PlusCircleFilled,
QuestionCircleFilled,
SearchOutlined,
} from '@ant-design/icons';
import type { ProSettings } from '@ant-design/pro-components';
import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
import { Input } from 'antd';
import { useState } from 'react';
import defaultProps from './_defaultProps';
import { demoOnMenuHeaderClick } from './_demoHandlers';
const Demo = () => {
const settings: Partial<ProSettings> | undefined = {
fixSiderbar: true,
layout: 'mix',
splitMenus: true,
};
const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
return (
<div
id="test-pro-layout"
style={{
height: '100vh',
}}
>
<ProLayout
token={{
bgLayout: '#fff',
header: {
colorBgHeader: '#fff',
},
sider: {
colorMenuBackground: '#fff',
},
pageContainer: {
colorBgPageContainer: '#fff',
},
}}
bgLayoutImgList={[
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
left: 85,
bottom: 100,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
bottom: -68,
right: -45,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
bottom: 0,
left: 0,
width: '331px',
},
]}
{...defaultProps}
location={{
pathname,
}}
menu={{
type: 'group',
}}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: <div>书琰</div>,
}}
actionsRender={(props) => {
if (props.isMobile) return [];
return [
props.layout !== 'side' && document.body.clientWidth > 1400 ? (
<div
key="SearchOutlined"
aria-hidden
style={{
display: 'flex',
alignItems: 'center',
marginInlineEnd: 24,
}}
onMouseDown={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<Input
style={{
borderRadius: 4,
marginInlineEnd: 12,
}}
prefix={<SearchOutlined />}
placeholder="搜索方案"
variant="borderless"
/>
<PlusCircleFilled
style={{
color: 'var(--ant-primary-color)',
fontSize: 24,
}}
/>
</div>
) : undefined,
<InfoCircleFilled key="InfoCircleFilled" />,
<QuestionCircleFilled key="QuestionCircleFilled" />,
<GithubFilled key="GithubFilled" />,
];
}}
menuFooterRender={(props) => {
if (props?.collapsed) return undefined;
return (
<div
style={{
textAlign: 'center',
paddingBlockStart: 12,
}}
>
<div>© 2021 Made with love</div>
<div>by Ant Design</div>
</div>
);
}}
onMenuHeaderClick={demoOnMenuHeaderClick}
menuItemRender={(item, dom) => (
<a
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</a>
)}
{...settings}
>
<PageContainer>
<ProCard
style={{
height: '200vh',
minHeight: 800,
}}
>
<div />
</ProCard>
</PageContainer>
</ProLayout>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#补充示例
此前未挂载文档的示例,涵盖 ConfigProvider 前缀、breadcrumbRender、menuRender、分组 appList、顶层嵌套布局等。
ConfigProvider prefixCls + ProLayout
import { LikeOutlined, UserOutlined } from '@ant-design/icons';
import type { ProSettings } from '@ant-design/pro-components';
import {
PageContainer,
ProLayout,
SettingDrawer,
} from '@ant-design/pro-components';
import {
Button,
ConfigProvider,
Descriptions,
Result,
Space,
Statistic,
} from 'antd';
import { useState } from 'react';
import defaultProps from './_defaultProps';
import { demoOnMenuHeaderClick } from './_demoHandlers';
const content = (
<Descriptions size="small" column={2}>
<Descriptions.Item label="创建人">书琰</Descriptions.Item>
<Descriptions.Item label="联系方式">
<span>421421</span>
</Descriptions.Item>
<Descriptions.Item label="创建时间">2017-01-10</Descriptions.Item>
<Descriptions.Item label="更新时间">2017-10-10</Descriptions.Item>
<Descriptions.Item label="备注">
中国浙江省杭州市西湖区古翠路
</Descriptions.Item>
</Descriptions>
);
const Demo = () => {
const [settings, setSettings] = useState<Partial<ProSettings> | undefined>(
undefined,
);
const [pathname, setPathname] = useState('/welcome');
return (
<ConfigProvider prefixCls="qixian">
<ProLayout
{...defaultProps}
style={{
maxHeight: '100vh',
}}
location={{
pathname,
}}
menuFooterRender={(props) => {
return (
<a
style={{
lineHeight: '48rpx',
display: 'flex',
height: 48,
color: 'rgba(255, 255, 255, 0.65)',
alignItems: 'center',
}}
href="https://preview.pro.ant.design/dashboard/analysis"
target="_blank"
rel="noreferrer"
>
<img
alt="pro-logo"
src="https://procomponents.ant.design/favicon.ico"
style={{
width: 16,
height: 16,
margin: '0 16px',
marginInlineEnd: 10,
}}
/>
{!props?.collapsed && 'Preview Pro'}
</a>
);
}}
onMenuHeaderClick={demoOnMenuHeaderClick}
menuItemRender={(item, dom) => (
<div
role="button"
tabIndex={0}
onClick={() => {
setPathname(item.path || '/welcome');
}}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setPathname(item.path || '/welcome');
}
}}
>
{dom}
</div>
)}
avatarProps={{
icon: <UserOutlined />,
}}
{...settings}
>
<PageContainer
content={content}
tabList={[
{
tab: '基本信息',
key: 'base',
},
{
tab: '详细信息',
key: 'info',
},
]}
extraContent={
<Space size={24}>
<Statistic
title="Feedback"
value={1128}
prefix={<LikeOutlined />}
/>
<Statistic title="Unmerged" value={93} suffix="/ 100" />
</Space>
}
extra={[
<Button key="3">操作</Button>,
<Button key="2">操作</Button>,
<Button key="1" type="primary">
主操作
</Button>,
]}
footer={[
<Button key="3">重置</Button>,
<Button key="2" type="primary">
提交
</Button>,
]}
>
<div
style={{
height: '120vh',
minHeight: 600,
}}
>
<Result
status="404"
style={{
height: '100%',
background: '#fff',
}}
title="Hello World"
subTitle="Sorry, you are not authorized to access this page."
extra={<Button type="primary">Back Home</Button>}
/>
</div>
</PageContainer>
</ProLayout>
<SettingDrawer
getContainer={() => document.getElementById('test-pro-layout')}
settings={settings}
onSettingChange={(changeSetting) => setSettings(changeSetting)}
/>
</ConfigProvider>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
分组菜单(complex-menu.ts + menu.type=group)
import { PageContainer, ProLayout } from '@ant-design/pro-components';
import complexMenu from './complex-menu';
const Demo = () => (
<div
style={{
height: '100vh',
}}
>
<ProLayout
location={{
pathname: '/data_hui/data_hui2',
}}
route={{
routes: complexMenu,
}}
menu={{ type: 'group' }}
>
<PageContainer content="欢迎使用">
<div>Hello World</div>
</PageContainer>
</ProLayout>
</div>
);
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
自定义 menuRender(侧栏容器宽度)
import { PageContainer, ProLayout } from '@ant-design/pro-components';
import defaultProps from './_defaultProps';
const Demo = () => (
<ProLayout
{...defaultProps}
style={{
height: '100vh',
}}
location={{
pathname: '/welcome',
}}
menuRender={(props, dom) => (
<div
style={{
background: '#fff',
boxShadow: '2px 0 6px rgba(0, 21, 41, 0.35)',
overflow: 'hidden',
height: '100%',
width: props.collapsed ? 0 : props.siderWidth || 256,
}}
>
{dom}
</div>
)}
>
<PageContainer content="欢迎使用">Hello World</PageContainer>
</ProLayout>
);
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
顶部导航嵌套内层 ProLayout
import { PageContainer, ProLayout } from '@ant-design/pro-components';
import complexMenu from './complex-menu';
const Demo = () => (
<div
style={{
height: '100vh',
overflow: 'auto',
}}
>
<ProLayout
location={{
pathname: '/articles/new',
}}
route={{
routes: complexMenu,
}}
layout="top"
>
<ProLayout
location={{
pathname: '/home/overview',
}}
route={{
routes: complexMenu,
}}
menuHeaderRender={false}
>
<PageContainer content="欢迎使用">
<div>Hello World</div>
</PageContainer>
</ProLayout>
</ProLayout>
</div>
);
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
breadcrumbRender 前置首页面包屑
import { PageContainer, ProLayout } from '@ant-design/pro-components';
const Demo = () => (
<div
style={{
height: '100vh',
}}
>
<ProLayout
location={{
pathname: '/admin/process/edit/123',
}}
breadcrumbRender={(routes) => [
{
path: '/',
title: '主页',
},
...(routes || []),
]}
menuDataRender={() => [
{
path: '/welcome',
name: '欢迎',
},
{
path: '/admin',
name: '管理',
children: [
{
name: '申请单列表',
path: '/admin/process',
},
{
name: '申请单详情',
path: '/admin/process/detail/:id',
hideInMenu: true,
},
{
name: '编辑申请单',
path: '/admin/process/edit/:id',
hideInMenu: true,
},
{
name: '添加申请单',
path: '/admin/process/add',
hideInMenu: true,
},
],
},
]}
>
<PageContainer content="欢迎使用">
<div>Hello World</div>
</PageContainer>
</ProLayout>
</div>
);
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
跨站点导航分组(简化数据)
import type { ProSettings } from '@ant-design/pro-components';
import { ProConfigProvider, ProLayout } from '@ant-design/pro-components';
import defaultProps from './_defaultProps';
const AppGroupList: any = [
{
title: 'UI 设计语言',
children: [
{
icon: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
title: '应用内跳转',
url: '/components/page-container',
},
{
icon: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
title: 'Ant Design',
url: 'https://ant.design',
},
{
icon: () => (
<img src="https://gw.alipayobjects.com/zos/antfincdn/upvrAjAPQX/Logo_Tech%252520UI.svg" alt="" />
),
title: 'Pro Components',
url: 'https://procomponents.ant.design/',
},
],
},
{
title: 'UI 设计语言 2组111',
icon: 'https://gw.alipayobjects.com/zos/antfincdn/upvrAjAPQX/Logo_Tech%252520UI.svg',
url: 'https://procomponents.ant.design/',
children: [
{
icon: 'W',
title: 'AntV',
url: 'https://antv.vision/',
target: '_blank',
},
{
title: 'AntV',
url: 'https://antv.vision/',
target: '_blank',
},
],
},
{
title: '待分组',
children: [
{
title: '工具',
icon: 'https://gw.alipayobjects.com/zos/rmsportal/XuVpGqBFxXplzvLjJBZB.svg',
url: 'https://www.yuque.com/',
},
{
title: '前端应用框架',
icon: 'https://img.alicdn.com/tfs/TB1zomHwxv1gK0jSZFFXXb0sXXa-200-200.png',
url: 'https://umijs.org/zh-CN/docs',
},
{
icon: 'https://gw.alipayobjects.com/zos/bmw-prod/8a74c1d3-16f3-4719-be63-15e467a68a24/km0cv8vn_w500_h500.png',
title: 'qiankun',
url: 'https://qiankun.umijs.org/',
},
{
icon: 'https://gw.alipayobjects.com/zos/rmsportal/LFooOLwmxGLsltmUjTAP.svg',
title: 'Kitchen ',
url: 'https://kitchen.alipay.com/',
},
{
icon: 'https://gw.alipayobjects.com/zos/bmw-prod/d3e3eb39-1cd7-4aa5-827c-877deced6b7e/lalxt4g3_w256_h256.png',
title: 'dumi',
url: 'https://d.umijs.org/zh-CN',
},
],
},
];
const Demo = () => {
const settings: ProSettings = {
fixSiderbar: true,
layout: 'mix',
splitMenus: true,
};
return (
<div id="test-pro-layout" style={{ height: '100vh' }}>
<ProConfigProvider hashed={false}>
<ProLayout
{...defaultProps}
appList={AppGroupList}
location={{ pathname: '/list/sub-page/sub-sub-page1' }}
siderMenuType="group"
menu={{ collapsedShowGroupTitle: true }}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: '书琰',
}}
{...settings}
/>
</ProConfigProvider>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#其他
自定义的 collapse
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
import type { ProLayoutProps } from '@ant-design/pro-components';
import {
PageContainer,
ProFormRadio,
ProLayout,
} from '@ant-design/pro-components';
import { useState } from 'react';
import defaultProps from './_defaultProps';
const Demo = () => {
const [pathname, setPathname] = useState('/welcome');
const [collapsed, setCollapsed] = useState(true);
const [position, setPosition] = useState<'header' | 'menu'>('header');
const children = (
<PageContainer>
<div
style={{
height: '120vh',
minHeight: 600,
}}
>
<ProFormRadio.Group
label="按钮的位置"
options={['header', 'menu'].map((value) => ({
label: value,
value,
}))}
fieldProps={{
value: position,
onChange: (e) => setPosition(e.target.value),
}}
/>
</div>
</PageContainer>
);
const props: ProLayoutProps = {
...defaultProps,
location: {
pathname,
},
collapsed,
fixSiderbar: true,
collapsedButtonRender: false,
menuItemRender: (item, dom) => (
<a
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</a>
),
};
if (position === 'menu') {
return (
<ProLayout
{...props}
layout="mix"
onCollapse={setCollapsed}
postMenuData={(menuData) => {
return [
{
icon: collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />,
name: ' ',
onTitleClick: () => setCollapsed(!collapsed),
},
...(menuData || []),
];
}}
>
{children}
</ProLayout>
);
}
return (
<ProLayout
{...props}
layout="mix"
onCollapse={setCollapsed}
headerContentRender={() => {
return (
<div
onClick={() => setCollapsed(!collapsed)}
style={{
cursor: 'pointer',
fontSize: '16px',
}}
>
{collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
</div>
);
}}
>
{children}
</ProLayout>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
自定侧栏菜单下方区域
import {
GithubFilled,
InfoCircleFilled,
QuestionCircleFilled,
} from '@ant-design/icons';
import type { ProSettings } from '@ant-design/pro-components';
import {
PageContainer,
ProCard,
ProLayout,
SettingDrawer,
} from '@ant-design/pro-components';
import { Avatar, Image, Space } from 'antd';
import { useState } from 'react';
import defaultProps from './_defaultProps';
const Demo = () => {
const [settings, setSettings] = useState<Partial<ProSettings> | undefined>({
layout: 'side',
});
const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
return (
<div
id="test-pro-layout"
style={{
height: '100vh',
}}
>
<ProLayout
bgLayoutImgList={[
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
left: 85,
bottom: 100,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i2/O1CN01O4etvp1DvpFLKfuWq_!!6000000000279-2-tps-609-606.png',
bottom: -68,
right: -45,
height: '303px',
},
{
src: 'https://img.alicdn.com/imgextra/i3/O1CN018NxReL1shX85Yz6Cx_!!6000000005798-2-tps-884-496.png',
bottom: 0,
left: 0,
width: '331px',
},
]}
{...defaultProps}
location={{
pathname,
}}
collapsed={false}
menu={{
type: 'group',
}}
actionsRender={(props) => {
if (props.isMobile) return [];
return [
<div
key={1}
style={{
height: '200px',
}}
>
<Image
width={'100%'}
preview={false}
height={132}
src="https://gw.alipayobjects.com/zos/bmw-prod/d283f09a-64d6-4d59-bfc7-37b49ea0da2b.svg"
/>
<Space
align="center"
size="middle"
style={{
width: '100%',
marginBlockStart: '32px',
}}
>
<Avatar
src="https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg"
size="small"
/>
<div
style={{
fontSize: '14px',
marginInlineEnd: '32px',
}}
>
书琰
</div>
<InfoCircleFilled key="InfoCircleFilled" />
<QuestionCircleFilled key="QuestionCircleFilled" />
<GithubFilled key="GithubFilled" />
</Space>
</div>,
];
}}
menuRender={(_, defaultDom) => defaultDom}
menuItemRender={(item, dom) => (
<div
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</div>
)}
{...settings}
>
<PageContainer>
<ProCard
style={{
height: '100vh',
minHeight: 800,
}}
>
<div />
</ProCard>
</PageContainer>
</ProLayout>
<SettingDrawer
pathname={pathname}
enableDarkTheme
getContainer={() => document.getElementById('test-pro-layout')}
settings={settings}
onSettingChange={(changeSetting) => {
setSettings(changeSetting);
}}
disableUrlParams={false}
/>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
使用 IconFont
import { PageContainer, ProLayout } from '@ant-design/pro-components';
const Demo = () => (
<div
style={{
height: '100vh',
}}
>
<ProLayout
location={{
pathname: '/articles/new',
}}
iconfontUrl="//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js"
route={{
routes: [
{
path: '/home',
name: '收藏',
icon: 'icon-shoucang1',
},
{
path: '/home/overview',
name: 'FaceBook',
icon: 'icon-facebook',
},
{
path: '/home/search',
name: 'Twitter',
icon: 'icon-twitter',
},
],
}}
>
<PageContainer content="欢迎使用">
<div>Hello World</div>
</PageContainer>
</ProLayout>
</div>
);
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
菜单图标(SVG / React)
import {
FacebookOutlined,
StarFilled,
TwitterOutlined,
} from '@ant-design/icons';
import { PageContainer, ProLayout } from '@ant-design/pro-components';
/**
* 菜单 `icon` 支持 React 节点(含 @ant-design/icons)或图片/SVG URL 字符串。
*
* icon 选型说明:
* - `StarFilled`:通用动作类图标,有官方 Filled 版,统一用面性
* - `FacebookOutlined` / `TwitterOutlined`:品牌 logo 官方只有单一 Outlined 形态,保留
*/
const Demo = () => (
<div style={{ height: '100vh' }}>
<ProLayout
location={{
pathname: '/favorites',
}}
route={{
routes: [
{
path: '/favorites',
name: '我的收藏',
icon: <StarFilled />,
},
{
path: '/social/facebook',
name: 'Facebook',
icon: <FacebookOutlined />,
},
{
path: '/social/twitter',
name: 'Twitter',
icon: <TwitterOutlined />,
},
{
path: '/svg-asset',
name: 'SVG 资源',
icon: 'https://gw.alipayobjects.com/zos/antfincdn/UCSef1BATc/nice.svg',
},
],
}}
>
<PageContainer content="欢迎使用">
<div>Hello World</div>
</PageContainer>
</ProLayout>
</div>
);
export default () => (
<div>
<Demo />
</div>
);
ghost 模式
import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
import { Descriptions } from 'antd';
import defaultProps from './_defaultProps';
const content = (
<Descriptions size="small" column={2}>
<Descriptions.Item label="创建人">书琰</Descriptions.Item>
<Descriptions.Item label="联系方式">
<a>421421</a>
</Descriptions.Item>
<Descriptions.Item label="创建时间">2017-01-10</Descriptions.Item>
<Descriptions.Item label="更新时间">2017-10-10</Descriptions.Item>
<Descriptions.Item label="备注">
中国浙江省杭州市西湖区古翠路
</Descriptions.Item>
</Descriptions>
);
const Demo = () => {
return (
<div
style={{
height: '100vh',
}}
>
<ProLayout
{...defaultProps}
location={{
pathname: '/welcome',
}}
>
<PageContainer fixedHeader content={content}>
<ProCard
direction="column"
ghost
gutter={[0, 16]}
style={{
height: '200vh',
}}
>
<ProCard style={{ height: 200 }} />
<ProCard gutter={16} ghost style={{ height: 200 }}>
<ProCard colSpan={16} />
<ProCard colSpan={8} />
</ProCard>
</ProCard>
</PageContainer>
</ProLayout>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#帮助面板
高级帮助
import type { ProHelpDataSourceChildren } from '@ant-design/pro-components';
import { ProHelp, ProHelpPanel } from '@ant-design/pro-components';
import { App, Rate, Typography } from 'antd';
const Demo = () => {
const map = new Map<
string,
(
item: ProHelpDataSourceChildren<{
video: React.VideoHTMLAttributes<HTMLVideoElement>;
list: {
title: string;
children: {
title: string;
href: string;
}[];
};
}>,
index: number,
) => React.ReactNode
>();
map.set('video', (item) => {
return (
<video
key=""
style={{
width: '100%',
}}
controls
{...(item.children as React.VideoHTMLAttributes<HTMLVideoElement>)}
/>
);
});
map.set('list', (item) => {
const listConfig = item.children as {
title: string;
children: {
title: string;
href: string;
}[];
};
return (
<div>
<h3
style={{
margin: '8px 0',
}}
>
{listConfig.title}
</h3>
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: 8,
}}
>
{listConfig.children.map((child, index) => {
return (
<div key={index}>
<Typography.Text>
{child.href ? (
<a href={child.href}>{child.title}</a>
) : (
child.title
)}
</Typography.Text>
</div>
);
})}
</div>
</div>
);
});
return (
<App>
<div
style={{
margin: 24,
paddingBlockEnd: 128,
display: 'flex',
gap: 24,
flexDirection: 'column',
}}
>
<ProHelp<{
video: React.VideoHTMLAttributes<HTMLVideoElement>;
list: {
title: string;
children: {
title: string;
href: string;
}[];
};
}>
dataSource={[
{
title: '常见问题',
key: 'default',
children: [
{
title: '如何开始操作数据授权?',
key: '1',
children: [
{
valueType: 'h1',
children: '如何开始操作数据授权?',
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供 方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'h2',
children: '相关问题',
},
{
valueType: 'link',
children: {
href: 'www.alipay.com',
children: '鹊凿平台DCI申领操作手册?',
},
},
{
valueType: 'link',
children: {
href: 'www.alipay.com',
children: 'openAPI 注册工具?',
},
},
{
valueType: 'h2',
children: '帮助视频',
},
{
valueType: 'video',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/file/A*oJOJRZwe00kAAAAAAAAAAAAADml6AQ',
},
},
],
},
{
title: '证据包内包含哪些内容,如何下载证据包?',
key: '2',
children: [
{
valueType: 'h1',
children: '证据包内包含哪些内容,如何下载证据包?',
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
// @ts-expect-error
{
children:
'节点。并将各自的摩斯计算 节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*NcPORK7kRWMAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'list',
children: {
title: '相关问题',
children: [
{
href: 'www.alipay.com',
title: '鹊凿平台DCI申领操作手册?',
},
{
href: 'www.alipay.com',
title: 'openAPI 注册工具?',
},
],
},
},
],
},
],
},
{
title: '名词解释',
key: 'value',
infiniteScrollFull: true,
children: [
{
title: '数据管理(模块)',
key: 'name0',
children: [
{
valueType: 'h1',
children: '数据管理(模块)',
},
{
valueType: 'text',
children:
'这是一个用于管理和处理数据的模块,它提供了一套工具来帮助用户进一步管理域内的数据。用户可以使用数据管理模块来管理数据、处理数据以及保护数据。',
},
],
},
{
title: '网页上传方式',
key: 'name1',
children: [
{
valueType: 'h1',
children: '网页上传方式',
},
{
valueType: 'text',
children:
'一种网页提供给用户的上传文件方法,用户可以通过网页上传自己的文件进行处理。',
},
],
},
{
title: '其他获取方式',
key: 'name2',
children: [
{
valueType: 'h1',
children: '其他获取方式',
},
{
valueType: 'text',
children:
'即不通过网页上传的方式获取数据,例如从数据库、文件夹或其他数据源中获取数据。',
},
],
},
{
title: '数据字典',
key: 'name3',
children: [
{
valueType: 'h1',
children: '数据字典',
},
{
valueType: 'text',
children:
'一个固定格式的数据说明书,它包含了所有数据元素的定义和说明,以及它们的定义和格式。',
},
],
},
{
title: '项目空间(模块)',
key: 'name4',
children: [
{
valueType: 'h1',
children: '项目空间(模块)',
},
{
valueType: 'text',
children:
'这是一个用于存储和管理一组相关数据和文档的模块。在项目空间中,用户可以创建文件夹、上传文件、管理文件等操作。',
},
],
},
{
title: '项目合作方',
key: 'name5',
children: [
{
valueType: 'h1',
children: '项目合作方',
},
{
valueType: 'text',
children: '与用户进行数据合作的外部组织或个人。',
},
],
},
{
title: '项目数据资源',
key: 'name6',
children: [
{
valueType: 'h1',
children: '项目数据资源',
},
{
valueType: 'text',
children:
'在一个项目中产生或收集的所有数据资源,包括原始数据、处理数据、文档和元数据等。',
},
],
},
{
title: '离线批量数据',
key: 'name7',
children: [
{
valueType: 'h1',
children: '离线批量数据',
},
{
valueType: 'text',
children:
'一种离线处理数据的方式,用户将需要处理的数据批量上传到系统中,再通过系统进行处理。',
},
{
valueType: 'text',
children: '相关数据请查看:',
},
{
valueType: 'navigationSwitch',
children: {
selectKey: 'name9',
children: '节点场景',
},
},
],
},
{
title: '线上服务数据',
key: 'name8',
children: [
{
valueType: 'h1',
children: '线上服务数据',
},
{
valueType: 'text',
children:
'一种在线处理数据的方式,用户通过在线提交数据并调用相应的处理程序进行数据处理。',
},
],
},
{
title: '节点场景',
key: 'name9',
children: [
{
valueType: 'h1',
children: '节点场景',
},
{
valueType: 'text',
children:
'一个由多个节点组成的场景,每个节点都有不同的特征和功能,相互之间可以通信和互动。',
},
],
},
{
title: '模型配置',
key: 'name10',
children: [
{
valueType: 'h1',
children: '模型配置',
},
{
valueType: 'text',
children:
'根据用户的要求对模型参数进行设置和调整,以达到最佳的处理效果。',
},
],
},
{
title: '模型文件',
key: 'name11',
children: [
{
valueType: 'h1',
children: '模型文件',
},
{
valueType: 'text',
children:
'系统生成的模型文件,包含了所有的模型参数和处理算法。',
},
],
},
{
title: '预处理文件',
key: 'name12',
children: [
{
valueType: 'h1',
children: '预处理文件',
},
{
valueType: 'text',
children:
'用于预处理数据的文件,系统可根据用户的设置进行数据预处理。',
},
],
},
{
title: '后处理文件',
key: 'name13',
children: [
{
valueType: 'text',
children:
'用于后处理数据的文件,系统将处理完成的数据输出到后处理文件中。',
},
],
},
{
title: '安全模型',
key: 'name14',
children: [
{
valueType: 'h1',
children: '安全模型',
},
{
valueType: 'text',
children:
'一种用于保护用户数据的安全控制模型,可以对数据进行加密、访问控制和防止数据泄漏等处理。',
},
],
},
{
title: '安全匹配',
key: 'name15',
children: [
{
valueType: 'h1',
children: '安全匹配',
},
{
valueType: 'text',
children:
'一种用于数据匹配的安全控制方法,可以对数据进行匿名化处理,以保护用户的隐私。',
},
],
},
{
title: '安全统计',
key: 'name16',
children: [
{
valueType: 'h1',
children: '安全统计',
},
{
valueType: 'text',
children:
'一种用于保护数据隐私的统计方法,可以在保证数据隐私的情况下进行数据分析和统计。',
},
],
},
{
title: '安全联盟',
key: 'name17',
children: [
{
valueType: 'h1',
children: '安全联盟',
},
{
valueType: 'text',
children:
'一种由多方共同协作的数据处理和安全保护机制,可以保障数据的机密性和完整性。',
},
],
},
{
title: '安全脚本',
key: 'name18',
children: [
{
valueType: 'h1',
children: '安全脚本',
},
{
valueType: 'text',
children:
'一种用于数据处理和安全保护的脚本程序,可以自动化完成数据安全控制任务。',
},
],
},
{
title: '匿名查询',
key: 'name19',
children: [
{
valueType: 'h1',
children: '匿名查询',
},
{
valueType: 'text',
children:
'一种保护用户隐私的查询方法,可以匿名化处理查询请求和返回结果,以保护用户的隐私。',
},
],
},
{
title: '导出表',
key: 'name20',
children: [
{
valueType: 'h1',
children: '导出表',
},
{
valueType: 'text',
children:
'一种用于导出数据的文件表格,用户可以将处理完的数据导出到该表格中进行进一步的处理和使用。',
},
],
},
],
},
]}
valueTypeMap={map}
>
<div
style={{
width: 800,
}}
>
<ProHelpPanel
defaultSelectedKey="1"
height={648}
footer={
<div
style={{
textAlign: 'center',
borderTop: '1px solid #EEE',
padding: 12,
marginTop: 24,
}}
>
这篇文章的质量如何? <Rate />
</div>
}
/>
</div>
</ProHelp>
</div>
</App>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
浮层高级帮助
import type { ProHelpDataSourceChildren } from '@ant-design/pro-components';
import {
ProHelp,
ProHelpDrawer,
ProHelpModal,
ProHelpPopover,
} from '@ant-design/pro-components';
import { App, Typography } from 'antd';
import { useState } from 'react';
const Demo = () => {
const [modalOpen, setModalOpen] = useState<boolean>(false);
const [drawerOpen, setDrawerOpen] = useState<boolean>(false);
const map = new Map<
string,
(
item: ProHelpDataSourceChildren<{
video: React.VideoHTMLAttributes<HTMLVideoElement>;
list: {
title: string;
children: {
title: string;
href: string;
}[];
};
}>,
index: number,
) => React.ReactNode
>();
map.set('video', (item) => {
return (
<video
key=""
style={{
width: '100%',
}}
controls
{...(item.children as React.VideoHTMLAttributes<HTMLVideoElement>)}
/>
);
});
map.set('list', (item) => {
const listConfig = item.children as {
title: string;
children: {
title: string;
href: string;
}[];
};
return (
<div>
<h3
style={{
margin: '8px 0',
}}
>
{listConfig.title}
</h3>
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: 8,
}}
>
{listConfig.children.map((child, index) => {
return (
<div key={index}>
<Typography.Text>
{child.href ? (
<a href={child.href}>{child.title}</a>
) : (
child.title
)}
</Typography.Text>
</div>
);
})}
</div>
</div>
);
});
return (
<App>
<div
style={{
margin: 24,
paddingBlockEnd: 128,
display: 'flex',
gap: 24,
flexDirection: 'column',
}}
>
<ProHelp<{
video: React.VideoHTMLAttributes<HTMLVideoElement>;
list: {
title: string;
children: {
title: string;
href: string;
}[];
};
}>
dataSource={[
{
title: '常见问题',
key: 'default',
children: [
{
title: '如何开始操作数据授权?',
key: '1',
children: [
{
valueType: 'h1',
children: '如何开始操作数据授权?',
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人 。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'h2',
children: '相关问题',
},
{
valueType: 'link',
children: {
href: 'www.alipay.com',
children: '鹊凿平台DCI申领操作手册?',
},
},
{
valueType: 'link',
children: {
href: 'www.alipay.com',
children: 'openAPI 注册工具?',
},
},
{
valueType: 'h2',
children: '帮助视频',
},
{
valueType: 'video',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/file/A*oJOJRZwe00kAAAAAAAAAAAAADml6AQ',
},
},
],
},
{
title: '证据包内包含哪些内容,如何下载证据包?',
key: '2',
children: [
{
valueType: 'h1',
children: '证据包内包含哪些内容,如何下载证据包?',
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
// @ts-expect-error
{
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'list',
children: {
title: '相关问题',
children: [
{
href: 'www.alipay.com',
title: '鹊凿平台DCI申领操作手册?',
},
{
href: 'www.alipay.com',
title: 'openAPI 注册工具?',
},
],
},
},
],
},
],
},
]}
valueTypeMap={map}
>
<div
style={{
display: 'flex',
gap: 16,
width: 600,
justifyContent: 'space-between',
}}
>
<button onClick={() => setDrawerOpen(!drawerOpen)}>打开</button>
<button onClick={() => setModalOpen(!modalOpen)}>打开</button>
<ProHelpModal
modalProps={{
open: modalOpen,
afterClose: () => setModalOpen(false),
}}
/>
<ProHelpDrawer
drawerProps={{
open: drawerOpen,
afterOpenChange(open) {
setDrawerOpen(open);
},
}}
/>
<ProHelpPopover selectedKey="1">Morse</ProHelpPopover>
</div>
</ProHelp>
</div>
</App>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
远程加载帮助
import { ProHelp, ProHelpPanel } from '@ant-design/pro-components';
import { App } from 'antd';
import ReactMarkdown from 'react-markdown';
export const waitTimePromise = async (time: number = 100) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, time);
});
};
export const waitTime = async (time: number = 100) => {
await waitTimePromise(time);
};
const markdownSample = `
## 标题与段落
ProHelp 支持常用 **粗体**、*斜体* 与 \`行内代码\`,用于展示帮助说明。
### 列表示例
1. 完成环境准备
2. 提交申请并填写信息
3. 等待审核结果
### 代码块
\`\`\`ts
const ready = true;
\`\`\`
### 引用与链接
> 提示:左侧菜单项可使用 \`asyncLoad\` 异步拉取正文。
[ProComponents 文档](https://procomponents.ant.design/)
`;
const Demo = () => {
const map = new Map();
map.set(
'markdown',
(item: {
valueType: string;
children: {
className: string;
children: string;
};
}) => {
return (
<div className={item.children.className}>
<ReactMarkdown>{item.children.children?.trim()}</ReactMarkdown>
</div>
);
},
);
return (
<App>
<div
style={{
margin: 24,
paddingBlockEnd: 128,
display: 'flex',
gap: 24,
flexDirection: 'column',
}}
>
<ProHelp<{
markdown: {
className: string;
children: string;
};
}>
valueTypeMap={map}
onLoadContext={async (key) => {
await waitTime(1000);
if (key === '1') {
return [
{
valueType: 'h1',
children: '如何开始操作数据授权?',
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'html',
children: {
children: `<b>加粗文本</b><br><br>
<i>斜体文本</i><br><br>
<code>电脑自动输出</code><br><br>
这是 <sub>下标</sub> 和 <sup>上标</sup>
`,
},
},
];
}
return [
{
valueType: 'h1',
children: 'Markdown 示例',
},
{
valueType: 'markdown',
children: {
className: 'markdown',
children: markdownSample,
},
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.antgroup.com',
children: '相关产品线',
},
},
{
valueType: 'text',
children:
'请将各计算节点、子账号的版本信息、业务需求、数据量级等信息同步给产运负责人,便于评估与排期。',
},
];
}}
dataSource={[
{
title: '常见问题',
key: 'default',
children: [
{
title: 'HTML 片段',
key: '1',
asyncLoad: true,
},
{
title: 'Markdown 正文',
key: '2',
asyncLoad: true,
},
],
},
]}
>
<div
style={{
width: 400,
}}
>
<ProHelpPanel defaultSelectedKey="1" height={448} />
</div>
</ProHelp>
</div>
</App>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
支持拖动的高级帮助
import type { ProHelpDataSourceChildren } from '@ant-design/pro-components';
import {
ProHelp,
ProHelpPanel,
ProHelpSelect,
} from '@ant-design/pro-components';
import { App, Typography } from 'antd';
import Draggable from 'react-draggable';
const Demo = () => {
const map = new Map<
string,
(
item: ProHelpDataSourceChildren<{
video: React.VideoHTMLAttributes<HTMLVideoElement>;
list: {
title: string;
children: {
title: string;
href: string;
}[];
};
}>,
index: number,
) => React.ReactNode
>();
map.set('video', (item) => {
return (
<video
key=""
style={{
width: '100%',
}}
controls
{...(item.children as React.VideoHTMLAttributes<HTMLVideoElement>)}
/>
);
});
map.set('list', (item) => {
const listConfig = item.children as {
title: string;
children: {
title: string;
href: string;
}[];
};
return (
<div>
<h3
style={{
margin: '8px 0',
}}
>
{listConfig.title}
</h3>
<div
style={{
display: 'flex',
flexDirection: 'column',
gap: 8,
}}
>
{listConfig.children.map((child, index) => {
return (
<div key={index}>
<Typography.Text>
{child.href ? (
<a href={child.href}>{child.title}</a>
) : (
child.title
)}
</Typography.Text>
</div>
);
})}
</div>
</div>
);
});
return (
<App>
<div
style={{
margin: 24,
paddingBlockEnd: 128,
display: 'flex',
gap: 24,
flexDirection: 'column',
}}
>
<ProHelp<{
video: React.VideoHTMLAttributes<HTMLVideoElement>;
list: {
title: string;
children: {
title: string;
href: string;
}[];
};
}>
dataSource={[
{
title: '常见问题',
key: 'default',
children: [
{
title: '如何开始操作数据授权?',
key: '1',
children: [
{
valueType: 'h1',
children: '如何开始操作数据授权?',
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'h2',
children: '相关问题',
},
{
valueType: 'link',
children: {
href: 'www.alipay.com',
children: '鹊凿平台DCI申领操作手册?',
},
},
{
valueType: 'link',
children: {
href: 'www.alipay.com',
children: 'openAPI 注册工具?',
},
},
{
valueType: 'h2',
children: '帮助视频',
},
{
valueType: 'video',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/file/A*oJOJRZwe00kAAAAAAAAAAAAADml6AQ',
},
},
],
},
{
title: '证据包内包含哪些内容,如何下载证据包?',
key: '2',
children: [
{
valueType: 'h1',
children: '证据包内包含哪些内容,如何下载证据包?',
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'text',
children: `需要进行数据合作的数据提供方(数据源)和数据需求方双方都需要先安装部署`,
},
{
valueType: 'inlineLink',
children: {
href: 'https://www.alipay.com',
children: '摩斯产品',
},
},
{
valueType: 'text',
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
// @ts-expect-error
{
children:
'节点。并将各自的摩斯计算节点、子账号等的版本信息、业务需求、数据量级(几行几列)等信息同步给到摩斯产运负责人。',
},
{
valueType: 'image',
children: {
src: 'https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*Jj_qRqbIRqkAAAAAAAAAAAAADml6AQ/original',
style: {
maxWidth: 600,
},
},
},
{
valueType: 'list',
children: {
title: '相关问题',
children: [
{
href: 'www.alipay.com',
title: '鹊凿平台DCI申领操作手册?',
},
{
href: 'www.alipay.com',
title: 'openAPI 注册工具?',
},
],
},
},
],
},
],
},
]}
valueTypeMap={map}
>
<ProHelpSelect />
<Draggable>
<div
style={{
width: 900,
}}
>
<ProHelpPanel defaultSelectedKey="1" height={448} />
</div>
</Draggable>
</ProHelp>
</div>
</App>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
#错误边界
layout 自带了错误处理功能,防止白屏
import { PageContainer, ProLayout } from '@ant-design/pro-components';
import { Button, Result } from 'antd';
import type { ErrorInfo } from 'react';
import React, { useState } from 'react';
class CustomBoundary extends React.Component<
Record<string, any>,
{ hasError: boolean; errorInfo: string }
> {
state = { hasError: false, errorInfo: '' };
static getDerivedStateFromError(error: Error) {
return { hasError: true, errorInfo: error.message };
}
componentDidCatch(error: any, errorInfo: ErrorInfo) {
// You can also log the error to an error reporting service
console.error(error, errorInfo);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return (
<Result
icon={
<img
width={256}
src="https://gw.alipayobjects.com/zos/antfincdn/zIgkN%26mpMZ/shibaizhuangtaizuo.png"
/>
}
style={{
height: '100%',
background: '#fff',
}}
title="错误处理"
extra={
<>
<div
style={{
maxWidth: 620,
textAlign: 'start',
backgroundColor: 'rgba(255,229,100,0.3)',
borderInlineStartColor: '#ffe564',
borderInlineStartWidth: '9px',
borderInlineStartStyle: 'solid',
padding: '20px 45px 20px 26px',
margin: 'auto',
marginBlockEnd: '30px',
marginBlockStart: '20px',
}}
>
<p>注意</p>
<p>
错误边界<strong>无法</strong>捕获以下场景中产生的错误:
</p>
<ul
style={{
listStyle: 'none',
}}
>
<li>
事件处理(
<a href="https://zh-hans.reactjs.org/docs/error-boundaries.html#how-about-event-handlers#how-about-event-handlers">
了解更多
</a>
)
</li>
<li>
异步代码(例如 <code>setTimeout</code> 或{' '}
<code>requestAnimationFrame</code> 回调函数)
</li>
<li>服务端渲染</li>
<li>它自身抛出来的错误(并非它的子组件)</li>
</ul>
</div>
<Button
danger
type="primary"
onClick={() => {
window.location.reload();
}}
>
刷新页面
</Button>
</>
}
/>
);
}
return this.props.children;
}
}
const ErrorTriggerTestPage = () => {
// default to throw error for snapshot test
const [error, setError] = useState<boolean>(true);
if (error) throw new Error('渲染发生了错误');
return (
<Button
danger
type="primary"
onClick={() => {
setError(true);
}}
>
触发错误
</Button>
);
};
const Demo = () => {
const [pathname, setPathname] = useState('/default');
return (
<div
id="test-pro-layout"
style={{
height: '100vh',
}}
>
<ProLayout
route={{
path: '/',
routes: [
{
path: '/default',
name: '默认错误',
},
{
path: '/custom',
name: '自定义错误',
},
],
}}
location={{
pathname,
}}
menuItemRender={(item, dom) => (
<a
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</a>
)}
ErrorBoundary={pathname === '/custom' ? CustomBoundary : undefined}
>
<PageContainer>
<ErrorTriggerTestPage />
</PageContainer>
</ProLayout>
</div>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
黑色主紧凑主题
import {
GithubFilled,
InfoCircleFilled,
PlusCircleFilled,
QuestionCircleFilled,
SearchOutlined,
} from '@ant-design/icons';
import { PageContainer, ProCard, ProLayout } from '@ant-design/pro-components';
import { Button, Input } from 'antd';
import { useState } from 'react';
import defaultProps from './_defaultProps';
import { demoOnMenuHeaderClick } from './_demoHandlers';
const Demo = () => {
const [pathname, setPathname] = useState('/list/sub-page/sub-sub-page1');
return (
<ProLayout
splitMenus
layout="mix"
token={{
bgLayout: 'rgba(160, 217, 17, 0.1)',
colorTextAppListIcon: 'rgba(255, 255, 255, 1)',
colorTextAppListIconHover: 'rgba(245, 34, 45, 1)',
sider: {
colorBgMenuItemHover: 'rgba(24, 144, 255, 1)',
colorMenuBackground: 'rgba(24, 144, 255, 0.1)',
colorMenuItemDivider: 'rgba(250, 219, 20, 1)',
colorTextMenu: 'rgba(250, 219, 20, 1)',
colorTextMenuSelected: 'rgba(250, 84, 28, 1)',
colorTextMenuItemHover: 'rgba(255, 255, 255, 1)',
colorBgMenuItemSelected: 'rgb(82, 196, 26)',
colorBgCollapsedButton: 'rgba(250, 84, 28, 1)',
colorTextCollapsedButton: 'rgba(24, 144, 255, 1)',
colorTextCollapsedButtonHover: 'rgba(143, 35, 35, 1)',
},
header: {
colorBgMenuItemSelected: 'rgba(82, 196, 26, 1)',
colorTextMenuSelected: 'rgba(114, 46, 209, 1)',
colorBgHeader: 'rgba(250, 173, 20, 1)',
colorHeaderTitle: 'rgba(47, 84, 235, 1)',
colorTextMenuActive: 'rgba(255, 255, 255, 1)',
colorTextMenu: 'rgba(250, 84, 28, 1)',
colorBgMenuItemHover: 'rgba(24, 144, 255, 1)',
},
pageContainer: {
colorBgPageContainer: 'rgba(250, 84, 28, 1)',
},
}}
{...defaultProps}
location={{
pathname,
}}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
title: '书琰',
}}
actionsRender={(props) => {
if (props.isMobile) return [];
return [
props.layout !== 'side' ? (
<div
key="SearchOutlined"
aria-hidden
style={{
display: 'flex',
alignItems: 'center',
marginInlineEnd: 24,
}}
onMouseDown={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<Input
style={{
borderRadius: 4,
marginInlineEnd: 12,
backgroundColor: 'rgba(0,0,0,0.03)',
}}
prefix={
<SearchOutlined
style={{
color: 'rgba(0, 0, 0, 0.15)',
}}
/>
}
placeholder="搜索方案"
variant="borderless"
/>
<PlusCircleFilled
style={{
color: 'var(--ant-primary-color)',
fontSize: 24,
}}
/>
</div>
) : undefined,
<InfoCircleFilled key="InfoCircleFilled" />,
<QuestionCircleFilled key="QuestionCircleFilled" />,
<GithubFilled key="GithubFilled" />,
];
}}
menuFooterRender={(props) => {
if (props?.collapsed) return undefined;
return (
<p
style={{
textAlign: 'center',
paddingBlockStart: 12,
}}
>
Power by Ant Design
</p>
);
}}
onMenuHeaderClick={demoOnMenuHeaderClick}
menuItemRender={(item, dom) => (
<a
onClick={() => {
setPathname(item.path || '/welcome');
}}
>
{dom}
</a>
)}
>
<PageContainer
extra={[
<Button key="3">操作</Button>,
<Button key="2">操作</Button>,
<Button key="1" type="primary">
主操作
</Button>,
]}
footer={[
<Button key="3">重置</Button>,
<Button key="2" type="primary">
提交
</Button>,
]}
>
<ProCard
style={{
height: '200vh',
minHeight: 800,
}}
>
<div />
</ProCard>
</PageContainer>
</ProLayout>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);
splitMenus
import {
PageContainer,
ProLayout,
SettingDrawer,
} from '@ant-design/pro-components';
import defaultProps from './_defaultProps';
import { demoOnMenuHeaderClick } from './_demoHandlers';
const Demo = () => {
return (
<>
<ProLayout {...defaultProps} layout="mix" splitMenus pure>
<PageContainer>splitMenus + pure</PageContainer>
</ProLayout>
<ProLayout {...defaultProps} layout="mix" splitMenus>
<PageContainer>splitMenus</PageContainer>
</ProLayout>
<ProLayout
{...defaultProps}
breadcrumbRender={false}
layout="mix"
splitMenus
headerRender={false}
onMenuHeaderClick={demoOnMenuHeaderClick}
formatMessage={({ id }) => id}
style={{
height: '100vh',
}}
/>
<ProLayout
{...defaultProps}
layout="mix"
menuExtraRender={() => 'dom'}
menuHeaderRender={() => <div />}
splitMenus
location={{
pathname: '/welcome',
}}
style={{
height: '100vh',
}}
/>
<ProLayout
{...defaultProps}
layout="top"
collapsed
contentWidth="Fixed"
menuExtraRender={false}
menuHeaderRender={false}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
}}
location={{
pathname: '/welcome',
}}
style={{
height: '100vh',
}}
/>
<ProLayout
{...defaultProps}
layout="mix"
menuHeaderRender={() => null}
splitMenus
fixSiderbar
location={{
pathname: '/welcome',
}}
contentWidth="Fixed"
openKeys={false}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
}}
style={{
height: '100vh',
}}
>
<PageContainer>splitMenus + fixSiderbar</PageContainer>
</ProLayout>
<ProLayout
{...defaultProps}
layout="mix"
menuHeaderRender={() => null}
location={{
pathname: '/welcome',
}}
openKeys={false}
actionsRender={() => [
<a key="docs" href="https://procomponents.ant.design/">
文档
</a>,
]}
avatarProps={{
src: 'https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg',
size: 'small',
}}
style={{
height: '100vh',
}}
>
<PageContainer>顶部操作区示例</PageContainer>
</ProLayout>
<ProLayout
{...defaultProps}
layout="mix"
menuHeaderRender={() => null}
splitMenus={false}
fixSiderbar
location={{
pathname: '/welcome',
}}
contentWidth="Fixed"
openKeys={false}
style={{
height: '100vh',
}}
>
<PageContainer>splitMenus 关闭时的侧栏与内容</PageContainer>
</ProLayout>
<SettingDrawer collapse />
</>
);
};
export default () => (
<div style={{ padding: 24 }}>
<Demo />
</div>
);