You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

189 lines
6.0 KiB
TypeScript

1 year ago
import './index.less'
import { getRender } from '../../utils'
import {
defaultRenderCollapsedButton,
defaultRenderLogo,
defaultRenderLogoAndTitle,
privateSiderMenuProps
} from '../SiderMenu/SiderMenu'
import { pureSettingsProps } from '../../defaultSettings'
import type { MenuDataItem, WithFalse } from '../../types'
import type { PureSettings } from '../../defaultSettings'
import type { HeaderViewProps } from '../../Header'
import type { CSSProperties, PropType, ExtractPropTypes } from 'vue'
import type { SiderMenuProps } from '../SiderMenu/SiderMenu'
import type { HeaderContentRender, MenuRender, RightContentRender } from '../../renderTypes'
import { VueNodeOrRenderPropType } from '#/types'
import type { VueNodeOrRender } from '#/types'
import TopNavHeader from '#/layout/components/TopNavHeader'
import { clearMenuItem } from '#/layout/utils/utils'
export const globalHeaderProps = () => ({
...privateSiderMenuProps(),
...pureSettingsProps,
// 覆盖下默认值
headerTheme: {
type: String as PropType<PureSettings['headerTheme']>,
default: 'dark'
},
// 自有属性
collapsed: { type: Boolean, default: undefined },
onCollapse: {
type: Function as PropType<(collapsed: boolean) => void>,
default: undefined
},
isMobile: { type: Boolean, default: undefined },
logo: {
type: VueNodeOrRenderPropType as PropType<VueNodeOrRender>,
default: () => undefined
},
/**
* menuRender SiderMenu
*
* @example menuRender={(props,defaultDom)=> props.collapsed ? null : defaultDom}
* @example menuRender={false}
*/
menuRender: {
type: [Function, Boolean] as PropType<WithFalse<MenuRender>>,
default: () => undefined
},
/**
* ,
*
* @example : rightRender={(props) => <Avatar shape="square" size="small" icon={<UserOutlined />} />}
* @example : rightRender={(props) => [<Button type="primary"></Button>,<Button type="primary"></Button>]}
*/
rightContentRender: {
type: [Function, Boolean] as PropType<WithFalse<RightContentRender>>,
default: () => undefined
},
prefixCls: { type: String, default: undefined },
menuData: {
type: Array as PropType<MenuDataItem[]>,
default: () => undefined
},
onMenuHeaderClick: Function as PropType<(e: MouseEvent) => void>,
menuHeaderRender: {
type: [Function, Boolean] as PropType<SiderMenuProps['menuHeaderRender']>,
default: undefined
},
/**
* menu
*
* @example headerContentRender={(props) => <div> </div>}
*/
headerContentRender: {
type: [Function, Boolean] as PropType<WithFalse<HeaderContentRender>>,
default: undefined
},
collapsedButtonRender: {
type: [Function, Boolean] as PropType<SiderMenuProps['collapsedButtonRender']>,
default: () => defaultRenderCollapsedButton
},
splitMenus: { type: Boolean, default: undefined }
})
export type GlobalHeaderProps = Partial<ExtractPropTypes<ReturnType<typeof globalHeaderProps>>>
const renderLogo = (
props: SiderMenuProps,
menuHeaderRender: SiderMenuProps['menuHeaderRender'],
logoDom: VueNodeOrRender
) => {
if (menuHeaderRender === false) {
return null
}
if (menuHeaderRender) {
return menuHeaderRender(props, logoDom, null)
}
return logoDom
}
export default defineComponent({
name: 'GlobalHeader',
props: globalHeaderProps(),
setup(props, { slots, attrs }) {
// TODO 布局方向支持
const direction = undefined
const baseClassName = `${props.prefixCls}-global-header`
const className = computed(() => [
attrs.class,
baseClassName,
{ [`${baseClassName}-layout-${props.layout}`]: props.layout && props.headerTheme === 'dark' }
])
return () => {
if (props.layout === 'mix' && !props.isMobile && props.splitMenus) {
const noChildrenMenuData = (props.menuData || []).map(item => ({
...item,
children: undefined
}))
const clearMenuData = clearMenuItem(noChildrenMenuData)
return (
<TopNavHeader
mode="horizontal"
{...props}
splitMenus={false}
menuData={clearMenuData}
theme={props.headerTheme as 'light' | 'dark'}
>
{slots}
</TopNavHeader>
)
}
const logoClassNames = [
`${baseClassName}-logo`,
{ [`${baseClassName}-logo-rtl`]: direction === 'rtl' }
]
// 添加 slots 支持
const logoRender = getRender<VueNodeOrRender>(props, slots, 'logo')
const logoDom = (
<span class={logoClassNames} key="logo">
<a>{defaultRenderLogo(logoRender)}</a>
</span>
)
const rightContentRender = getRender<RightContentRender>(props, slots, 'rightContentRender')
return (
<div class={className.value} style={attrs.style as CSSProperties}>
{props.isMobile && renderLogo(props, props.menuHeaderRender, logoDom)}
{props.isMobile && props.collapsedButtonRender && (
<span
class={`${baseClassName}-collapsed-button`}
onClick={() => {
if (props.onCollapse) {
props.onCollapse(!props.collapsed)
}
}}
>
{props.collapsedButtonRender(props.collapsed)}
</span>
)}
{props.layout === 'mix' && !props.isMobile && (
<>
<div class={logoClassNames} onClick={props.onMenuHeaderClick}>
{defaultRenderLogoAndTitle(
{ ...props, collapsed: false },
slots,
'headerTitleRender'
)}
</div>
</>
)}
<div style={{ flex: 1 }}>{slots.default?.()}</div>
{rightContentRender && rightContentRender(props as HeaderViewProps)}
</div>
)
}
}
})