iot-ui-vue/src/components/Layout/BasicLayout.tsx

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>
)
}
</>
)
}
}
})