35 changed files with 589 additions and 630 deletions
@ -1,214 +0,0 @@ |
|||
import './index.less'; |
|||
|
|||
import type { FunctionalComponent } from 'vue'; |
|||
import type { Component } from '/@/components/types'; |
|||
|
|||
import { defineComponent, unref, computed } from 'vue'; |
|||
|
|||
import { Layout, Tooltip, Badge } from 'ant-design-vue'; |
|||
import { AppLogo } from '/@/components/Application'; |
|||
import LayoutMenu from '../menu'; |
|||
import LockAction from './actions/LockAction'; |
|||
import LayoutTrigger from '../trigger/index.vue'; |
|||
import NoticeAction from './notice/NoticeActionItem.vue'; |
|||
import { LockOutlined, BugOutlined } from '@ant-design/icons-vue'; |
|||
|
|||
import { AppSearch } from '/@/components/Application'; |
|||
import { useModal } from '/@/components/Modal'; |
|||
|
|||
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting'; |
|||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; |
|||
import { useRootSetting } from '/@/hooks/setting/useRootSetting'; |
|||
import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting'; |
|||
|
|||
import { useRouter } from 'vue-router'; |
|||
|
|||
import { errorStore } from '/@/store/modules/error'; |
|||
|
|||
import { PageEnum } from '/@/enums/pageEnum'; |
|||
import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum'; |
|||
import { AppLocalePicker } from '/@/components/Application'; |
|||
import { useI18n } from '/@/hooks/web/useI18n'; |
|||
import { propTypes } from '/@/utils/propTypes'; |
|||
|
|||
import { UserDropDown, LayoutBreadcrumb, FullScreen } from './components'; |
|||
import { useAppInject } from '/@/hooks/web/useAppInject'; |
|||
import { useDesign } from '../../../hooks/web/useDesign'; |
|||
interface TooltipItemProps { |
|||
title: string; |
|||
} |
|||
|
|||
const TooltipItem: FunctionalComponent<TooltipItemProps> = (props, { slots }) => { |
|||
return ( |
|||
<Tooltip> |
|||
{{ |
|||
title: () => props.title, |
|||
default: () => slots.default?.(), |
|||
}} |
|||
</Tooltip> |
|||
); |
|||
}; |
|||
|
|||
export default defineComponent({ |
|||
name: 'LayoutHeader', |
|||
props: { |
|||
fixed: propTypes.bool, |
|||
}, |
|||
setup(props) { |
|||
const { t } = useI18n(); |
|||
const { prefixCls } = useDesign('layout-header'); |
|||
const { getShowTopMenu, getShowHeaderTrigger, getSplit } = useMenuSetting(); |
|||
const { getShowLocale } = useLocaleSetting(); |
|||
const { getUseErrorHandle } = useRootSetting(); |
|||
|
|||
const { |
|||
getHeaderTheme, |
|||
getUseLockPage, |
|||
getShowFullScreen, |
|||
getShowNotice, |
|||
getShowContent, |
|||
getShowBread, |
|||
getShowHeaderLogo, |
|||
} = useHeaderSetting(); |
|||
|
|||
const { push } = useRouter(); |
|||
const [register, { openModal }] = useModal(); |
|||
const { getIsMobile } = useAppInject(); |
|||
|
|||
const headerClass = computed(() => { |
|||
const theme = unref(getHeaderTheme); |
|||
return theme ? `${prefixCls}__header--${theme}` : ''; |
|||
}); |
|||
|
|||
const getSplitType = computed(() => { |
|||
return unref(getSplit) ? MenuSplitTyeEnum.TOP : MenuSplitTyeEnum.NONE; |
|||
}); |
|||
|
|||
const getMenuMode = computed(() => { |
|||
return unref(getSplit) ? MenuModeEnum.HORIZONTAL : null; |
|||
}); |
|||
|
|||
function handleToErrorList() { |
|||
push(PageEnum.ERROR_LOG_PAGE).then(() => { |
|||
errorStore.commitErrorListCountState(0); |
|||
}); |
|||
} |
|||
|
|||
function handleLockPage() { |
|||
openModal(true); |
|||
} |
|||
|
|||
function renderHeaderLeft() { |
|||
return ( |
|||
<> |
|||
{unref(getShowContent) && ( |
|||
<div class={`${prefixCls}__left`}> |
|||
{unref(getShowHeaderTrigger) && ( |
|||
<LayoutTrigger theme={unref(getHeaderTheme)} sider={false} /> |
|||
)} |
|||
{unref(getShowBread) && !unref(getIsMobile) && ( |
|||
<LayoutBreadcrumb theme={unref(getHeaderTheme)} /> |
|||
)} |
|||
</div> |
|||
)} |
|||
</> |
|||
); |
|||
} |
|||
|
|||
function renderHeaderContent() { |
|||
return ( |
|||
<div class={`${prefixCls}__content`}> |
|||
{unref(getShowTopMenu) && !unref(getIsMobile) && ( |
|||
<div class={[`${prefixCls}__menu `]}> |
|||
{/* <div class={[`layout-header__menu `]}> */} |
|||
<LayoutMenu |
|||
isHorizontal={true} |
|||
// class={`justify-${unref(getTopMenuAlign)}`}
|
|||
theme={unref(getHeaderTheme)} |
|||
splitType={unref(getSplitType)} |
|||
menuMode={unref(getMenuMode)} |
|||
/> |
|||
</div> |
|||
)} |
|||
</div> |
|||
); |
|||
} |
|||
|
|||
function renderActionDefault(Comp: Component | any, event: Fn) { |
|||
return ( |
|||
<div class={`${prefixCls}__action-item`} onClick={event}> |
|||
<Comp class={`${prefixCls}__action-icon`} /> |
|||
</div> |
|||
); |
|||
} |
|||
|
|||
function renderAction() { |
|||
return ( |
|||
<div class={`${prefixCls}__action`}> |
|||
{!unref(getIsMobile) && <AppSearch class={`${prefixCls}__action-item`} />} |
|||
|
|||
{unref(getUseErrorHandle) && !unref(getIsMobile) && ( |
|||
<TooltipItem title={t('layout.header.tooltipErrorLog')}> |
|||
{() => ( |
|||
<Badge |
|||
count={errorStore.getErrorListCountState} |
|||
offset={[0, 10]} |
|||
dot |
|||
overflowCount={99} |
|||
> |
|||
{() => renderActionDefault(BugOutlined, handleToErrorList)} |
|||
</Badge> |
|||
)} |
|||
</TooltipItem> |
|||
)} |
|||
|
|||
{unref(getUseLockPage) && !unref(getIsMobile) && ( |
|||
<TooltipItem title={t('layout.header.tooltipLock')}> |
|||
{() => renderActionDefault(LockOutlined, handleLockPage)} |
|||
</TooltipItem> |
|||
)} |
|||
|
|||
{unref(getShowNotice) && !unref(getIsMobile) && ( |
|||
<TooltipItem title={t('layout.header.tooltipNotify')}> |
|||
{() => <NoticeAction />} |
|||
</TooltipItem> |
|||
)} |
|||
|
|||
{unref(getShowFullScreen) && !unref(getIsMobile) && <FullScreen />} |
|||
|
|||
<UserDropDown theme={unref(getHeaderTheme)} /> |
|||
|
|||
{unref(getShowLocale) && ( |
|||
<AppLocalePicker |
|||
reload={true} |
|||
showText={false} |
|||
class={`${prefixCls}__action-item locale`} |
|||
/> |
|||
)} |
|||
</div> |
|||
); |
|||
} |
|||
|
|||
function renderHeaderDefault() { |
|||
return ( |
|||
<> |
|||
{unref(getShowHeaderLogo) && ( |
|||
<AppLogo class={`${prefixCls}__logo`} theme={unref(getHeaderTheme)} /> |
|||
)} |
|||
{renderHeaderLeft()} |
|||
{renderHeaderContent()} |
|||
{renderAction()} |
|||
<LockAction onRegister={register} /> |
|||
</> |
|||
); |
|||
} |
|||
|
|||
return () => { |
|||
return ( |
|||
<Layout.Header class={[prefixCls, unref(headerClass), { fixed: props.fixed }]}> |
|||
{() => renderHeaderDefault()} |
|||
</Layout.Header> |
|||
); |
|||
}; |
|||
}, |
|||
}); |
|||
@ -1,18 +0,0 @@ |
|||
@import (reference) '../../../design/index.less'; |
|||
|
|||
.multiple-tab-header { |
|||
margin-left: 1px; |
|||
transition: width 0.2s; |
|||
flex: 0 0 auto; |
|||
|
|||
&.dark { |
|||
margin-left: 0; |
|||
} |
|||
|
|||
&.fixed { |
|||
position: fixed; |
|||
top: 0; |
|||
z-index: @multiple-tab-fixed-z-index; |
|||
width: 100%; |
|||
} |
|||
} |
|||
@ -1,125 +0,0 @@ |
|||
import './LayoutMultipleHeader.less'; |
|||
|
|||
import { defineComponent, unref, computed, ref, watch, nextTick, CSSProperties } from 'vue'; |
|||
|
|||
import LayoutHeader from './index.vue'; |
|||
import MultipleTabs from '../tabs/index.vue'; |
|||
|
|||
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting'; |
|||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; |
|||
import { useFullContent } from '/@/hooks/web/useFullContent'; |
|||
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting'; |
|||
import { useLayoutContext } from '../useLayoutContext'; |
|||
import { useAppInject } from '/@/hooks/web/useAppInject'; |
|||
|
|||
export default defineComponent({ |
|||
name: 'LayoutMultipleHeader', |
|||
setup() { |
|||
const placeholderHeightRef = ref(0); |
|||
const fullHeaderHeightRef = ref(0); |
|||
const headerElRef = ref<ComponentRef>(null); |
|||
const tabElRef = ref<ComponentRef>(null); |
|||
|
|||
const injectValue = useLayoutContext(); |
|||
|
|||
const { getCalcContentWidth, getSplit } = useMenuSetting(); |
|||
const { getIsMobile } = useAppInject(); |
|||
const { |
|||
getFixed, |
|||
getShowInsetHeaderRef, |
|||
getShowFullHeaderRef, |
|||
getShowHeader, |
|||
getUnFixedAndFull, |
|||
getHeaderTheme, |
|||
} = useHeaderSetting(); |
|||
|
|||
const { getFullContent } = useFullContent(); |
|||
|
|||
const { getShowMultipleTab } = useMultipleTabSetting(); |
|||
|
|||
const getShowTabs = computed(() => { |
|||
return unref(getShowMultipleTab) && !unref(getFullContent); |
|||
}); |
|||
|
|||
const getPlaceholderDomStyle = computed( |
|||
(): CSSProperties => { |
|||
return { |
|||
height: `${unref(placeholderHeightRef)}px`, |
|||
}; |
|||
} |
|||
); |
|||
|
|||
const getIsShowPlaceholderDom = computed(() => { |
|||
return unref(getFixed) || unref(getShowFullHeaderRef); |
|||
}); |
|||
|
|||
const getWrapStyle = computed( |
|||
(): CSSProperties => { |
|||
const style: CSSProperties = {}; |
|||
if (unref(getFixed)) { |
|||
style.width = unref(getIsMobile) ? '100%' : unref(getCalcContentWidth); |
|||
} |
|||
if (unref(getShowFullHeaderRef)) { |
|||
style.top = `${unref(fullHeaderHeightRef)}px`; |
|||
} |
|||
return style; |
|||
} |
|||
); |
|||
|
|||
const getIsFixed = computed(() => { |
|||
return unref(getFixed) || unref(getShowFullHeaderRef); |
|||
}); |
|||
|
|||
watch( |
|||
() => [ |
|||
unref(getFixed), |
|||
unref(getShowFullHeaderRef), |
|||
unref(getShowHeader), |
|||
unref(getShowMultipleTab), |
|||
], |
|||
() => { |
|||
if (unref(getUnFixedAndFull)) return; |
|||
nextTick(() => { |
|||
const headerEl = unref(headerElRef)?.$el; |
|||
const tabEl = unref(tabElRef)?.$el; |
|||
const fullHeaderEl = unref(injectValue.fullHeader)?.$el; |
|||
|
|||
let height = 0; |
|||
if (headerEl && !unref(getShowFullHeaderRef) && !unref(getSplit)) { |
|||
height += headerEl.offsetHeight; |
|||
} |
|||
|
|||
if (tabEl) { |
|||
height += tabEl.offsetHeight; |
|||
} |
|||
|
|||
if (fullHeaderEl && unref(getShowFullHeaderRef)) { |
|||
const fullHeaderHeight = fullHeaderEl.offsetHeight; |
|||
height += fullHeaderHeight; |
|||
fullHeaderHeightRef.value = fullHeaderHeight; |
|||
} |
|||
|
|||
placeholderHeightRef.value = height; |
|||
}); |
|||
}, |
|||
{ |
|||
immediate: true, |
|||
} |
|||
); |
|||
|
|||
return () => { |
|||
return ( |
|||
<> |
|||
{unref(getIsShowPlaceholderDom) && <div style={unref(getPlaceholderDomStyle)} />} |
|||
<div |
|||
style={unref(getWrapStyle)} |
|||
class={['multiple-tab-header', unref(getHeaderTheme), { fixed: unref(getIsFixed) }]} |
|||
> |
|||
{unref(getShowInsetHeaderRef) && <LayoutHeader ref={headerElRef} />} |
|||
{unref(getShowTabs) && <MultipleTabs ref={tabElRef} />} |
|||
</div> |
|||
</> |
|||
); |
|||
}; |
|||
}, |
|||
}); |
|||
@ -0,0 +1,124 @@ |
|||
<template> |
|||
<div :style="getPlaceholderDomStyle" v-if="getIsShowPlaceholderDom" /> |
|||
<div :style="getWrapStyle" :class="getClass"> |
|||
<LayoutHeader v-if="getShowInsetHeaderRef" /> |
|||
<MultipleTabs v-if="getShowTabs" /> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, unref, computed, CSSProperties } from 'vue'; |
|||
|
|||
import LayoutHeader from './index.vue'; |
|||
import MultipleTabs from '../tabs/index.vue'; |
|||
|
|||
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting'; |
|||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; |
|||
import { useFullContent } from '/@/hooks/web/useFullContent'; |
|||
import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting'; |
|||
import { useAppInject } from '/@/hooks/web/useAppInject'; |
|||
import { useDesign } from '/@/hooks/web/useDesign'; |
|||
|
|||
const HEADER_HEIGHT = 48; |
|||
|
|||
const TABS_HEIGHT = 32; |
|||
export default defineComponent({ |
|||
name: 'LayoutMultipleHeader', |
|||
components: { LayoutHeader, MultipleTabs }, |
|||
setup() { |
|||
const { prefixCls } = useDesign('layout-multiple-header'); |
|||
|
|||
const { getCalcContentWidth, getSplit } = useMenuSetting(); |
|||
const { getIsMobile } = useAppInject(); |
|||
const { |
|||
getFixed, |
|||
getShowInsetHeaderRef, |
|||
getShowFullHeaderRef, |
|||
getHeaderTheme, |
|||
} = useHeaderSetting(); |
|||
|
|||
const { getFullContent } = useFullContent(); |
|||
|
|||
const { getShowMultipleTab } = useMultipleTabSetting(); |
|||
|
|||
const getShowTabs = computed(() => { |
|||
return unref(getShowMultipleTab) && !unref(getFullContent); |
|||
}); |
|||
|
|||
const getIsShowPlaceholderDom = computed(() => { |
|||
return unref(getFixed) || unref(getShowFullHeaderRef); |
|||
}); |
|||
|
|||
const getWrapStyle = computed( |
|||
(): CSSProperties => { |
|||
const style: CSSProperties = {}; |
|||
if (unref(getFixed)) { |
|||
style.width = unref(getIsMobile) ? '100%' : unref(getCalcContentWidth); |
|||
} |
|||
if (unref(getShowFullHeaderRef)) { |
|||
style.top = `${HEADER_HEIGHT}px`; |
|||
} |
|||
return style; |
|||
} |
|||
); |
|||
|
|||
const getIsFixed = computed(() => { |
|||
return unref(getFixed) || unref(getShowFullHeaderRef); |
|||
}); |
|||
|
|||
const getPlaceholderDomStyle = computed( |
|||
(): CSSProperties => { |
|||
let height = 0; |
|||
if (unref(getShowFullHeaderRef) || !unref(getSplit)) { |
|||
height += HEADER_HEIGHT; |
|||
} |
|||
if (unref(getShowMultipleTab)) { |
|||
height += TABS_HEIGHT; |
|||
} |
|||
return { |
|||
height: `${height}px`, |
|||
}; |
|||
} |
|||
); |
|||
|
|||
const getClass = computed(() => { |
|||
return [ |
|||
prefixCls, |
|||
`${prefixCls}--${unref(getHeaderTheme)}`, |
|||
{ [`${prefixCls}--fixed`]: unref(getIsFixed) }, |
|||
]; |
|||
}); |
|||
|
|||
return { |
|||
getClass, |
|||
prefixCls, |
|||
getPlaceholderDomStyle, |
|||
getIsFixed, |
|||
getWrapStyle, |
|||
getIsShowPlaceholderDom, |
|||
getShowTabs, |
|||
getShowInsetHeaderRef, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
<style lang="less" scoped> |
|||
@import (reference) '../../../design/index.less'; |
|||
@prefix-cls: ~'@{namespace}-layout-multiple-header'; |
|||
|
|||
.@{prefix-cls} { |
|||
margin-left: 1px; |
|||
transition: width 0.2s; |
|||
flex: 0 0 auto; |
|||
|
|||
&--dark { |
|||
margin-left: 0; |
|||
} |
|||
|
|||
&--fixed { |
|||
position: fixed; |
|||
top: 0; |
|||
z-index: @multiple-tab-fixed-z-index; |
|||
width: 100%; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,67 @@ |
|||
<template> |
|||
<div :class="getClass" :style="getDragBarStyle" /> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, computed, unref } from 'vue'; |
|||
|
|||
import { useDesign } from '/@/hooks/web/useDesign'; |
|||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; |
|||
|
|||
export default defineComponent({ |
|||
name: 'DargBar', |
|||
props: { |
|||
mobile: Boolean, |
|||
}, |
|||
setup(props) { |
|||
const { getMiniWidthNumber, getCollapsed, getCanDrag } = useMenuSetting(); |
|||
|
|||
const { prefixCls } = useDesign('darg-bar'); |
|||
const getDragBarStyle = computed(() => { |
|||
if (unref(getCollapsed)) { |
|||
return { left: `${unref(getMiniWidthNumber)}px` }; |
|||
} |
|||
return {}; |
|||
}); |
|||
|
|||
const getClass = computed(() => { |
|||
return [ |
|||
prefixCls, |
|||
{ |
|||
[`${prefixCls}--hide`]: !unref(getCanDrag) || props.mobile, |
|||
}, |
|||
]; |
|||
}); |
|||
|
|||
return { |
|||
prefixCls, |
|||
getDragBarStyle, |
|||
getClass, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
<style lang="less" scoped> |
|||
@import (reference) '../../../design/index.less'; |
|||
@prefix-cls: ~'@{namespace}-darg-bar'; |
|||
|
|||
.@{prefix-cls} { |
|||
position: absolute; |
|||
top: 0; |
|||
right: -2px; |
|||
z-index: @side-drag-z-index; |
|||
width: 2px; |
|||
height: 100%; |
|||
cursor: col-resize; |
|||
border-top: none; |
|||
border-bottom: none; |
|||
|
|||
&--hide { |
|||
display: none; |
|||
} |
|||
|
|||
&:hover { |
|||
background: @primary-color; |
|||
box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.15); |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,54 @@ |
|||
<template> |
|||
<Drawer |
|||
v-if="getIsMobile" |
|||
placement="left" |
|||
:class="prefixCls" |
|||
:width="getMenuWidth" |
|||
:getContainer="null" |
|||
:visible="!getCollapsed" |
|||
@close="handleClose" |
|||
> |
|||
<Sider /> |
|||
</Drawer> |
|||
<Sider v-else /> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
|
|||
import Sider from './LayoutSider'; |
|||
import { Drawer } from 'ant-design-vue'; |
|||
import { useAppInject } from '/@/hooks/web/useAppInject'; |
|||
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; |
|||
import { useDesign } from '/@/hooks/web/useDesign'; |
|||
export default defineComponent({ |
|||
name: 'SiderWrapper', |
|||
components: { Sider, Drawer }, |
|||
setup() { |
|||
const { prefixCls } = useDesign('layout-sider-wrapper'); |
|||
const { getIsMobile } = useAppInject(); |
|||
const { setMenuSetting, getCollapsed, getMenuWidth } = useMenuSetting(); |
|||
|
|||
function handleClose() { |
|||
setMenuSetting({ |
|||
collapsed: true, |
|||
}); |
|||
} |
|||
|
|||
return { prefixCls, getIsMobile, getCollapsed, handleClose, getMenuWidth }; |
|||
}, |
|||
}); |
|||
</script> |
|||
<style lang="less"> |
|||
@import (reference) '../../../design/index.less'; |
|||
@prefix-cls: ~'@{namespace}-layout-sider-wrapper'; |
|||
.@{prefix-cls} { |
|||
.ant-drawer-body { |
|||
height: 100vh; |
|||
padding: 0; |
|||
} |
|||
|
|||
.ant-drawer-header-no-title { |
|||
display: none; |
|||
} |
|||
} |
|||
</style> |
|||
@ -1,16 +0,0 @@ |
|||
import { InjectionKey, Ref } from 'vue'; |
|||
import { createContext, useContext } from '/@/hooks/core/useContext'; |
|||
|
|||
export interface LayoutContextProps { |
|||
fullHeader: Ref<ComponentRef>; |
|||
} |
|||
|
|||
const key: InjectionKey<LayoutContextProps> = Symbol(); |
|||
|
|||
export function createLayoutContext(context: LayoutContextProps) { |
|||
return createContext<LayoutContextProps>(context, key); |
|||
} |
|||
|
|||
export function useLayoutContext() { |
|||
return useContext<LayoutContextProps>(key); |
|||
} |
|||
Loading…
Reference in new issue