227 lines
6.4 KiB
TypeScript
227 lines
6.4 KiB
TypeScript
import {
|
|
computed,
|
|
reactive,
|
|
unref,
|
|
defineComponent,
|
|
toRefs,
|
|
provide
|
|
} from 'vue'
|
|
|
|
import type { DefineComponent, ExtractPropTypes, PropType, CSSProperties, Plugin, App } from 'vue'
|
|
import { Layout } from 'ant-design-vue'
|
|
import useConfigInject from 'ant-design-vue/es/_util/hooks/useConfigInject'
|
|
import { defaultSettingProps, defaultSettings } from './defaultSetting'
|
|
import type { PureSettings } from './defaultSetting'
|
|
import type { BreadcrumbProps, RouteContextProps } from './RouteContext'
|
|
import type {
|
|
BreadcrumbRender,
|
|
CollapsedButtonRender, CustomRender,
|
|
FooterRender,
|
|
HeaderContentRender,
|
|
HeaderRender,
|
|
MenuContentRender,
|
|
MenuExtraRender,
|
|
MenuFooterRender,
|
|
MenuHeaderRender,
|
|
MenuItemRender,
|
|
RightContentRender,
|
|
SubMenuItemRender
|
|
} from './typings'
|
|
import SiderMenuWrapper, { siderMenuProps } from 'components/Layout/components/SiderMenu/SiderMenu'
|
|
import { getSlot } from '@/utils/comm'
|
|
import { getMenuFirstChildren } from 'components/Layout/utils'
|
|
import { pick } from 'lodash-es'
|
|
import { routeContextInjectKey } from './RouteContext'
|
|
import { HeaderView, headerViewProps } from './components/Header'
|
|
|
|
export const basicLayoutProps = {
|
|
...defaultSettingProps,
|
|
...siderMenuProps,
|
|
...headerViewProps,
|
|
|
|
breadcrumb: {
|
|
type: [Object, Function] as PropType<BreadcrumbProps>,
|
|
default: () => null
|
|
},
|
|
breadcrumbRender: {
|
|
type: [Object, Function, Boolean] as PropType<BreadcrumbRender>,
|
|
default() {
|
|
return null
|
|
}
|
|
},
|
|
contentStyle: {
|
|
type: [String, Object] as PropType<CSSProperties>,
|
|
default: () => {
|
|
return null
|
|
}
|
|
},
|
|
pure: {
|
|
type: Boolean,
|
|
default: () => false
|
|
}
|
|
}
|
|
|
|
export type BasicLayoutProps = Partial<ExtractPropTypes<typeof basicLayoutProps>>;
|
|
|
|
export default defineComponent({
|
|
name: 'ProLayout',
|
|
inheritAttrs: false,
|
|
props: basicLayoutProps,
|
|
emits: [
|
|
'update:collapsed',
|
|
'update:open-keys',
|
|
'update:selected-keys',
|
|
'collapse',
|
|
'openKeys',
|
|
'select',
|
|
'menuHeaderClick',
|
|
'menuClick'
|
|
],
|
|
setup(props, { emit, attrs, slots }) {
|
|
const siderWidth = computed(() => (props.collapsed ? props.collapsedWidth : props.siderWidth))
|
|
|
|
const onCollapse = (collapsed: boolean) => {
|
|
emit('update:collapsed', collapsed)
|
|
emit('collapse', collapsed)
|
|
}
|
|
const onOpenKeys = (openKeys: string[] | false) => {
|
|
emit('update:open-keys', openKeys)
|
|
emit('openKeys', openKeys)
|
|
}
|
|
const onSelect = (selectedKeys: string[] | false) => {
|
|
emit('update:selected-keys', selectedKeys)
|
|
emit('select', selectedKeys)
|
|
}
|
|
const onMenuHeaderClick = (e: MouseEvent) => {
|
|
emit('menuHeaderClick', e)
|
|
}
|
|
const onMenuClick = (args: any) => {
|
|
emit('menuClick', args)
|
|
}
|
|
const headerRender = (
|
|
p: BasicLayoutProps & {
|
|
hasSiderMenu: boolean;
|
|
headerRender: HeaderRender;
|
|
rightContentRender: RightContentRender;
|
|
},
|
|
matchMenuKeys?: string[]
|
|
): CustomRender | null => {
|
|
if (p.headerRender === false) {
|
|
return null
|
|
}
|
|
return <HeaderView {...p} />
|
|
}
|
|
|
|
const breadcrumb = computed<BreadcrumbProps>(() => ({
|
|
...props.breadcrumb,
|
|
itemRender: getSlot<BreadcrumbRender>(slots, props, 'breadcrumbRender') as BreadcrumbRender
|
|
}))
|
|
|
|
const flatMenuData = computed(
|
|
() => (props.selectedKeys && getMenuFirstChildren(props.menuData, props.selectedKeys[0])) || [])
|
|
|
|
const routeContext = reactive<RouteContextProps>({
|
|
...(pick(toRefs(props), [
|
|
'menuData',
|
|
'openKeys',
|
|
'selectedKeys',
|
|
'contentWidth',
|
|
'headerHeight'
|
|
]) as any),
|
|
siderWidth,
|
|
breadcrumb,
|
|
flatMenuData
|
|
})
|
|
|
|
provide(routeContextInjectKey, routeContext)
|
|
|
|
return () => {
|
|
const {
|
|
pure,
|
|
onCollapse: propsOnCollapse,
|
|
onOpenKeys: propsOnOpenKeys,
|
|
onSelect: propsOnSelect,
|
|
onMenuClick: propsOnMenuClick,
|
|
...restProps
|
|
} = props
|
|
|
|
const collapsedButtonRender = getSlot<CollapsedButtonRender>(slots, props, 'collapsedButtonRender')
|
|
const rightContentRender = getSlot<RightContentRender>(slots, props, 'rightContentRender')
|
|
const customHeaderRender = getSlot<HeaderRender>(slots, props, 'headerRender')
|
|
|
|
// menu
|
|
const menuHeaderRender = getSlot<MenuHeaderRender>(slots, props, 'menuHeaderRender')
|
|
const menuExtraRender = getSlot<MenuExtraRender>(slots, props, 'menuExtraRender')
|
|
const menuContentRender = getSlot<MenuContentRender>(slots, props, 'menuContentRender')
|
|
const menuItemRender = getSlot<MenuItemRender>(slots, props, 'menuItemRender')
|
|
const subMenuItemRender = getSlot<SubMenuItemRender>(slots, props, 'subMenuItemRender')
|
|
|
|
const headerDom = computed(() =>
|
|
headerRender(
|
|
{
|
|
...props,
|
|
hasSiderMenu: true,
|
|
menuItemRender,
|
|
subMenuItemRender,
|
|
menuData: props.menuData,
|
|
onCollapse,
|
|
onOpenKeys,
|
|
onSelect,
|
|
onMenuHeaderClick,
|
|
rightContentRender,
|
|
collapsedButtonRender,
|
|
menuExtraRender,
|
|
menuContentRender,
|
|
headerRender: customHeaderRender,
|
|
theme: props.navTheme
|
|
},
|
|
[]
|
|
)
|
|
)
|
|
|
|
return (
|
|
<>
|
|
{
|
|
pure ? (
|
|
slots.default?.()
|
|
) : (
|
|
<Layout
|
|
class={'pro-layout'}
|
|
style={{
|
|
minHeight: '100vh'
|
|
}}
|
|
>
|
|
<SiderMenuWrapper
|
|
{...restProps}
|
|
theme={props.navTheme}
|
|
menuHeaderRender={menuHeaderRender}
|
|
menuExtraRender={menuExtraRender}
|
|
menuContentRender={menuContentRender}
|
|
menuItemRender={menuItemRender}
|
|
subMenuItemRender={subMenuItemRender}
|
|
collapsedButtonRender={collapsedButtonRender}
|
|
onCollapse={onCollapse}
|
|
onSelect={onSelect}
|
|
onOpenKeys={onOpenKeys}
|
|
onMenuClick={onMenuClick}
|
|
/>
|
|
|
|
<Layout>
|
|
{headerDom.value}
|
|
<Layout>
|
|
{slots.default?.()}
|
|
</Layout>
|
|
</Layout>
|
|
</Layout>
|
|
)
|
|
}
|
|
</>
|
|
)
|
|
}
|
|
}
|
|
})
|
|
|
|
|
|
|
|
|