import { PageHeader, Tabs, Affix, ConfigProvider } from 'antd'; import type { ReactNode } from 'react'; import React, { useContext } from 'react'; import classNames from 'classnames'; import type { TabsProps, AffixProps, PageHeaderProps, TabPaneProps, SpinProps, BreadcrumbProps, } from 'antd'; import type { RouteContextType } from '../../RouteContext'; import RouteContext from '../../RouteContext'; import GridContent from '../GridContent'; import FooterToolbar from '../FooterToolbar'; import './index.less'; import PageLoading from '../PageLoading'; import type { WithFalse } from '../../typings'; import type { WaterMarkProps } from '../WaterMark'; import WaterMark from '../WaterMark'; export type PageHeaderTabConfig = { /** @name tabs 的列表 */ tabList?: (TabPaneProps & { key?: React.ReactText })[]; /** @name 当前选中 tab 的 key */ tabActiveKey?: TabsProps['activeKey']; /** @name tab 修改时触发 */ onTabChange?: TabsProps['onChange']; /** @name tab 上额外的区域 */ tabBarExtraContent?: TabsProps['tabBarExtraContent']; /** @name tabs 的其他配置 */ tabProps?: TabsProps; /** * @deprecated 请使用 fixedHeader * @name 固定 PageHeader 到页面顶部 */ fixHeader?: boolean; /** @name 固定 PageHeader 到页面顶部 */ fixedHeader?: boolean; }; export type PageContainerProps = { title?: React.ReactNode | false; content?: React.ReactNode; extraContent?: React.ReactNode; prefixCls?: string; footer?: ReactNode[]; /** @name 是否显示背景色 */ ghost?: boolean; /** * 与 antd 完全相同 * * @name PageHeader 的配置 */ header?: Partial & { children?: React.ReactNode; }; /** @name 自定义 pageHeader */ pageHeaderRender?: WithFalse<(props: PageContainerProps) => React.ReactNode>; /** * 与 antd 完全相同 * * @name 固钉的配置 */ affixProps?: AffixProps; /** * 只加载内容区域 * * @name 是否加载 */ loading?: boolean | SpinProps; /** @name 水印的配置 */ waterMarkProps?: WaterMarkProps; } & PageHeaderTabConfig & Omit; function genLoading(spinProps: boolean | SpinProps) { if (typeof spinProps === 'object') { return spinProps; } return { spinning: spinProps }; } /** * Render Footer tabList In order to be compatible with the old version of the PageHeader basically * all the functions are implemented. */ const renderFooter: React.FC< Omit< PageContainerProps & { prefixedClassName: string; }, 'title' > > = ({ tabList, tabActiveKey, onTabChange, tabBarExtraContent, tabProps, prefixedClassName }) => { if (tabList && tabList.length) { return ( { if (onTabChange) { onTabChange(key); } }} tabBarExtraContent={tabBarExtraContent} {...tabProps} > {tabList.map((item, index) => ( // eslint-disable-next-line react/no-array-index-key ))} ); } return null; }; const renderPageHeader = ( content: React.ReactNode, extraContent: React.ReactNode, prefixedClassName: string, ): React.ReactNode => { if (!content && !extraContent) { return null; } return (
{content &&
{content}
} {extraContent && (
{extraContent}
)}
); }; const defaultPageHeaderRender = ( props: PageContainerProps, value: RouteContextType & { prefixedClassName: string }, ): React.ReactNode => { const { title, content, pageHeaderRender, header, extraContent, style, prefixCls, ...restProps } = props; if (pageHeaderRender === false) { return null; } if (pageHeaderRender) { return pageHeaderRender({ ...props, ...value }); } let pageHeaderTitle = title; if (!title && title !== false) { pageHeaderTitle = value.title; } const pageHeaderProps: PageHeaderProps = { ...value, title: pageHeaderTitle, ...restProps, footer: renderFooter({ ...restProps, prefixedClassName: value.prefixedClassName, }), ...header, }; const { breadcrumb } = pageHeaderProps as { breadcrumb: BreadcrumbProps; }; if ( ['title', 'subTitle', 'breadcrumb', 'extra', 'tags', 'footer', 'avatar', 'backIcon'].every( (item) => !pageHeaderProps[item], ) && (!breadcrumb || (!breadcrumb?.itemRender && !breadcrumb?.routes?.length)) && !content && !extraContent ) { return null; } return ( {header?.children || renderPageHeader(content, extraContent, value.prefixedClassName)} ); }; const PageContainer: React.FC = (props) => { const { children, loading = false, style, footer, affixProps, ghost, fixedHeader } = props; const value = useContext(RouteContext); const { getPrefixCls } = useContext(ConfigProvider.ConfigContext); const prefixCls = props.prefixCls || getPrefixCls('pro'); const prefixedClassName = `${prefixCls}-page-container`; const className = classNames(prefixedClassName, props.className, { [`${prefixCls}-page-container-ghost`]: ghost, }); const content = children ? (
{children}
{value.hasFooterToolbar && (
)}
) : null; const pageHeaderDom = defaultPageHeaderRender(props, { ...value, prefixCls: undefined, prefixedClassName, }); const headerDom = pageHeaderDom ? (
{pageHeaderDom}
) : null; const renderContent = () => { const spinProps = genLoading(loading); const dom = spinProps.spinning ? : content; if (props.waterMarkProps || value.waterMarkProps) { return {dom}; } return dom; }; return (
{fixedHeader && headerDom ? ( // 在 hasHeader 且 fixedHeader 的情况下,才需要设置高度 {headerDom} ) : ( headerDom )} {renderContent()} {footer && {footer}}
); }; export default PageContainer;