feat(MultipleTab): add tabs auto collapse
parent
21e51d128d
commit
b0ff73285f
|
@ -16,6 +16,8 @@ export function useMultipleTabSetting() {
|
||||||
|
|
||||||
const getShowFold = computed(() => appStore.getMultiTabsSetting.showFold)
|
const getShowFold = computed(() => appStore.getMultiTabsSetting.showFold)
|
||||||
|
|
||||||
|
const getAutoCollapse = computed(() => appStore.getMultiTabsSetting.autoCollapse)
|
||||||
|
|
||||||
function setMultipleTabSetting(multiTabsSetting: Partial<MultiTabsSetting>) {
|
function setMultipleTabSetting(multiTabsSetting: Partial<MultiTabsSetting>) {
|
||||||
appStore.setProjectConfig({ multiTabsSetting })
|
appStore.setProjectConfig({ multiTabsSetting })
|
||||||
}
|
}
|
||||||
|
@ -26,5 +28,6 @@ export function useMultipleTabSetting() {
|
||||||
getShowQuick,
|
getShowQuick,
|
||||||
getShowRedo,
|
getShowRedo,
|
||||||
getShowFold,
|
getShowFold,
|
||||||
|
getAutoCollapse,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,13 +24,13 @@ const { setHeaderHeight } = useLayoutHeight()
|
||||||
const tabStore = useMultipleTabStore()
|
const tabStore = useMultipleTabStore()
|
||||||
const { prefixCls } = useDesign('layout-multiple-header')
|
const { prefixCls } = useDesign('layout-multiple-header')
|
||||||
|
|
||||||
const { getCalcContentWidth, getSplit } = useMenuSetting()
|
const { getCalcContentWidth, getSplit, getShowMenu } = useMenuSetting()
|
||||||
const { getIsMobile } = useAppInject()
|
const { getIsMobile } = useAppInject()
|
||||||
const { getFixed, getShowInsetHeaderRef, getShowFullHeaderRef, getHeaderTheme, getShowHeader } = useHeaderSetting()
|
const { getFixed, getShowInsetHeaderRef, getShowFullHeaderRef, getHeaderTheme, getShowHeader } = useHeaderSetting()
|
||||||
|
|
||||||
const { getFullContent } = useFullContent()
|
const { getFullContent } = useFullContent()
|
||||||
|
|
||||||
const { getShowMultipleTab } = useMultipleTabSetting()
|
const { getShowMultipleTab, getAutoCollapse } = useMultipleTabSetting()
|
||||||
|
|
||||||
const getShowTabs = computed(() => {
|
const getShowTabs = computed(() => {
|
||||||
return unref(getShowMultipleTab) && !unref(getFullContent)
|
return unref(getShowMultipleTab) && !unref(getFullContent)
|
||||||
|
@ -40,6 +40,8 @@ const getIsShowPlaceholderDom = computed(() => {
|
||||||
return unref(getFixed) || unref(getShowFullHeaderRef)
|
return unref(getFixed) || unref(getShowFullHeaderRef)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const getIsUnFold = computed(() => !unref(getShowMenu) && !unref(getShowHeader))
|
||||||
|
|
||||||
const getWrapStyle = computed((): CSSProperties => {
|
const getWrapStyle = computed((): CSSProperties => {
|
||||||
const style: CSSProperties = {}
|
const style: CSSProperties = {}
|
||||||
if (unref(getFixed))
|
if (unref(getFixed))
|
||||||
|
@ -57,13 +59,19 @@ const getIsFixed = computed(() => {
|
||||||
|
|
||||||
const getPlaceholderDomStyle = computed((): CSSProperties => {
|
const getPlaceholderDomStyle = computed((): CSSProperties => {
|
||||||
let height = 0
|
let height = 0
|
||||||
if ((unref(getShowFullHeaderRef) || !unref(getSplit)) && unref(getShowHeader) && !unref(getFullContent))
|
if (!(unref(getAutoCollapse) && unref(getIsUnFold))) {
|
||||||
|
if (
|
||||||
|
(unref(getShowFullHeaderRef) || !unref(getSplit))
|
||||||
|
&& unref(getShowHeader)
|
||||||
|
&& !unref(getFullContent)
|
||||||
|
)
|
||||||
height += HEADER_HEIGHT
|
height += HEADER_HEIGHT
|
||||||
|
|
||||||
if (unref(getShowMultipleTab) && !unref(getFullContent))
|
if (unref(getShowMultipleTab) && !unref(getFullContent))
|
||||||
height += TABS_HEIGHT
|
height += TABS_HEIGHT
|
||||||
|
|
||||||
setHeaderHeight(height)
|
setHeaderHeight(height)
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
height: `${height}px`,
|
height: `${height}px`,
|
||||||
}
|
}
|
||||||
|
@ -75,7 +83,11 @@ const getClass = computed(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="getIsShowPlaceholderDom" :style="getPlaceholderDomStyle" />
|
<div
|
||||||
|
v-if="getIsShowPlaceholderDom"
|
||||||
|
:class="[`${prefixCls}__placeholder`]"
|
||||||
|
:style="getPlaceholderDomStyle"
|
||||||
|
/>
|
||||||
<div :style="getWrapStyle" :class="getClass">
|
<div :style="getWrapStyle" :class="getClass">
|
||||||
<LayoutHeader v-if="getShowInsetHeaderRef" />
|
<LayoutHeader v-if="getShowInsetHeaderRef" />
|
||||||
<MultipleTabs v-if="getShowTabs" :key="tabStore.getLastDragEndIndex" />
|
<MultipleTabs v-if="getShowTabs" :key="tabStore.getLastDragEndIndex" />
|
||||||
|
@ -99,5 +111,9 @@ const getClass = computed(() => {
|
||||||
z-index: @multiple-tab-fixed-z-index;
|
z-index: @multiple-tab-fixed-z-index;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__placeholder {
|
||||||
|
transition: height 0.6s ease-in-out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,25 +1,32 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { Layout } from 'ant-design-vue'
|
|
||||||
import { computed, unref } from 'vue'
|
import { computed, unref } from 'vue'
|
||||||
|
import { Layout } from 'ant-design-vue'
|
||||||
|
|
||||||
import LayoutContent from './content/index.vue'
|
|
||||||
import LayoutHeader from './header/index.vue'
|
import LayoutHeader from './header/index.vue'
|
||||||
import LayoutMultipleHeader from './header/MultipleHeader.vue'
|
import LayoutContent from './content/index.vue'
|
||||||
import LayoutSideBar from './sider/index.vue'
|
import LayoutSideBar from './sider/index.vue'
|
||||||
|
import LayoutMultipleHeader from './header/MultipleHeader.vue'
|
||||||
|
import { createAsyncComponent } from '@/utils/factory/createAsyncComponent'
|
||||||
|
|
||||||
import { useHeaderSetting } from '@/hooks/setting/useHeaderSetting'
|
import { useHeaderSetting } from '@/hooks/setting/useHeaderSetting'
|
||||||
import { useMenuSetting } from '@/hooks/setting/useMenuSetting'
|
import { useMenuSetting } from '@/hooks/setting/useMenuSetting'
|
||||||
import { useAppInject } from '@/hooks/web/useAppInject'
|
import { useDesign } from '@/hooks/web/useDesign'
|
||||||
import { useLockPage } from '@/hooks/web/useLockPage'
|
import { useLockPage } from '@/hooks/web/useLockPage'
|
||||||
import { createAsyncComponent } from '@/utils/factory/createAsyncComponent'
|
|
||||||
|
import { useAppInject } from '@/hooks/web/useAppInject'
|
||||||
|
|
||||||
|
import { useMultipleTabSetting } from '@/hooks/setting/useMultipleTabSetting'
|
||||||
|
|
||||||
defineOptions({ name: 'DefaultLayout' })
|
defineOptions({ name: 'DefaultLayout' })
|
||||||
|
|
||||||
const LayoutFeatures = createAsyncComponent(() => import('@/layouts/default/feature/index.vue'))
|
const LayoutFeatures = createAsyncComponent(() => import('@/layouts/default/feature/index.vue'))
|
||||||
const LayoutFooter = createAsyncComponent(() => import('@/layouts/default/footer/index.vue'))
|
const LayoutFooter = createAsyncComponent(() => import('@/layouts/default/footer/index.vue'))
|
||||||
|
|
||||||
|
const { prefixCls } = useDesign('default-layout')
|
||||||
const { getIsMobile } = useAppInject()
|
const { getIsMobile } = useAppInject()
|
||||||
const { getShowFullHeaderRef } = useHeaderSetting()
|
const { getShowFullHeaderRef } = useHeaderSetting()
|
||||||
const { getShowSidebar, getIsMixSidebar, getShowMenu } = useMenuSetting()
|
const { getShowSidebar, getIsMixSidebar, getShowMenu } = useMenuSetting()
|
||||||
|
const { getAutoCollapse } = useMultipleTabSetting()
|
||||||
|
|
||||||
// Create a lock screen monitor
|
// Create a lock screen monitor
|
||||||
const lockEvents = useLockPage()
|
const lockEvents = useLockPage()
|
||||||
|
@ -29,17 +36,20 @@ const layoutClass = computed(() => {
|
||||||
if (unref(getIsMixSidebar) || unref(getShowMenu))
|
if (unref(getIsMixSidebar) || unref(getShowMenu))
|
||||||
cls.push('ant-layout-has-sider')
|
cls.push('ant-layout-has-sider')
|
||||||
|
|
||||||
|
if (!unref(getShowMenu) && unref(getAutoCollapse))
|
||||||
|
cls.push('ant-layout-auto-collapse-tabs')
|
||||||
|
|
||||||
return cls
|
return cls
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Layout class="min-h-full w-full flex flex-col" v-bind="lockEvents">
|
<Layout :class="prefixCls" v-bind="lockEvents">
|
||||||
<LayoutFeatures />
|
<LayoutFeatures />
|
||||||
<LayoutHeader v-if="getShowFullHeaderRef" fixed />
|
<LayoutHeader v-if="getShowFullHeaderRef" fixed />
|
||||||
<Layout :class="[layoutClass]">
|
<Layout :class="[layoutClass, `${prefixCls}-out`]">
|
||||||
<LayoutSideBar v-if="getShowSidebar || getIsMobile" />
|
<LayoutSideBar v-if="getShowSidebar || getIsMobile" />
|
||||||
<Layout class="ml-0.25 w-full">
|
<Layout :class="`${prefixCls}-main`">
|
||||||
<LayoutMultipleHeader />
|
<LayoutMultipleHeader />
|
||||||
<LayoutContent />
|
<LayoutContent />
|
||||||
<LayoutFooter />
|
<LayoutFooter />
|
||||||
|
@ -47,3 +57,34 @@ const layoutClass = computed(() => {
|
||||||
</Layout>
|
</Layout>
|
||||||
</Layout>
|
</Layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
@prefix-cls: ~'@{namespace}-default-layout';
|
||||||
|
|
||||||
|
.@{prefix-cls} {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
background-color: @content-bg;
|
||||||
|
|
||||||
|
> .ant-layout {
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-main {
|
||||||
|
width: 100%;
|
||||||
|
margin-left: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.@{prefix-cls}-out {
|
||||||
|
&.ant-layout-has-sider {
|
||||||
|
.@{prefix-cls} {
|
||||||
|
&-main {
|
||||||
|
margin-left: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -70,7 +70,7 @@ export default defineComponent({
|
||||||
|
|
||||||
const { getShowHeader, getFixed: getHeaderFixed, getHeaderBgColor, getShowSearch } = useHeaderSetting()
|
const { getShowHeader, getFixed: getHeaderFixed, getHeaderBgColor, getShowSearch } = useHeaderSetting()
|
||||||
|
|
||||||
const { getShowMultipleTab, getShowMultipleTabIcon, getShowQuick, getShowRedo, getShowFold } = useMultipleTabSetting()
|
const { getShowMultipleTab, getShowMultipleTabIcon, getShowQuick, getShowRedo, getShowFold, getAutoCollapse } = useMultipleTabSetting()
|
||||||
|
|
||||||
const getShowMenuRef = computed(() => {
|
const getShowMenuRef = computed(() => {
|
||||||
return unref(getShowMenu) && !unref(getIsHorizontal)
|
return unref(getShowMenu) && !unref(getIsHorizontal)
|
||||||
|
@ -183,6 +183,12 @@ export default defineComponent({
|
||||||
def={unref(getMenuFixed)}
|
def={unref(getMenuFixed)}
|
||||||
disabled={!unref(getShowMenuRef) || unref(getIsMixSidebar)}
|
disabled={!unref(getShowMenuRef) || unref(getIsMixSidebar)}
|
||||||
/>
|
/>
|
||||||
|
<SwitchItem
|
||||||
|
title={t('layout.setting.autoCollapseTabsInFold')}
|
||||||
|
event={HandlerEnum.TABS_AUTO_COLLAPSE}
|
||||||
|
def={unref(getAutoCollapse)}
|
||||||
|
disabled={!unref(getShowMultipleTab)}
|
||||||
|
/>
|
||||||
<SelectItem
|
<SelectItem
|
||||||
title={t('layout.setting.mixSidebarTrigger')}
|
title={t('layout.setting.mixSidebarTrigger')}
|
||||||
event={HandlerEnum.MENU_TRIGGER_MIX_SIDEBAR}
|
event={HandlerEnum.MENU_TRIGGER_MIX_SIDEBAR}
|
||||||
|
|
|
@ -37,6 +37,7 @@ export enum HandlerEnum {
|
||||||
TABS_SHOW,
|
TABS_SHOW,
|
||||||
TABS_SHOW_FOLD,
|
TABS_SHOW_FOLD,
|
||||||
TABS_SHOW_ICON,
|
TABS_SHOW_ICON,
|
||||||
|
TABS_AUTO_COLLAPSE,
|
||||||
|
|
||||||
LOCK_TIME,
|
LOCK_TIME,
|
||||||
FULL_CONTENT,
|
FULL_CONTENT,
|
||||||
|
|
|
@ -159,6 +159,9 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf
|
||||||
case HandlerEnum.TABS_SHOW_FOLD:
|
case HandlerEnum.TABS_SHOW_FOLD:
|
||||||
return { multiTabsSetting: { showFold: value } }
|
return { multiTabsSetting: { showFold: value } }
|
||||||
|
|
||||||
|
case HandlerEnum.TABS_AUTO_COLLAPSE:
|
||||||
|
return { multiTabsSetting: { autoCollapse: value } }
|
||||||
|
|
||||||
// ============header==================
|
// ============header==================
|
||||||
case HandlerEnum.HEADER_THEME:
|
case HandlerEnum.HEADER_THEME:
|
||||||
updateHeaderBgColor(value)
|
updateHeaderBgColor(value)
|
||||||
|
|
|
@ -1,4 +1,26 @@
|
||||||
@prefix-cls: ~"@{namespace}-multiple-tabs";
|
@prefix-cls: ~"@{namespace}-multiple-tabs";
|
||||||
|
@prefix-cls-default-layout: ~"@{namespace}-default-layout";
|
||||||
|
|
||||||
|
.@{prefix-cls-default-layout}-out {
|
||||||
|
&.ant-layout-auto-collapse-tabs {
|
||||||
|
.@{prefix-cls} {
|
||||||
|
margin-top: -(@multiple-height + 2);
|
||||||
|
opacity: 0.1;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&--hover {
|
||||||
|
margin-top: 0;
|
||||||
|
opacity: 1;
|
||||||
|
transition-delay: 0s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.@{prefix-cls} {
|
||||||
|
transition:
|
||||||
|
margin 0.2s ease-in-out 0.6s,
|
||||||
|
opacity 0.2s ease-in-out 0.6s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.@{prefix-cls} {
|
.@{prefix-cls} {
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { RouteLocationNormalized, RouteMeta } from 'vue-router'
|
import type { RouteLocationNormalized, RouteMeta } from 'vue-router'
|
||||||
|
|
||||||
|
import { useMouse } from '@vueuse/core'
|
||||||
|
|
||||||
import { computed, ref, unref } from 'vue'
|
import { computed, ref, unref } from 'vue'
|
||||||
|
|
||||||
import { Tabs } from 'ant-design-vue'
|
import { Tabs } from 'ant-design-vue'
|
||||||
|
@ -10,6 +12,7 @@ import FoldButton from './components/FoldButton.vue'
|
||||||
import TabRedo from './components/TabRedo.vue'
|
import TabRedo from './components/TabRedo.vue'
|
||||||
|
|
||||||
import { initAffixTabs, useTabsDrag } from './useMultipleTabs'
|
import { initAffixTabs, useTabsDrag } from './useMultipleTabs'
|
||||||
|
import { multipleTabHeight } from '@/settings/designSetting'
|
||||||
import { useGo } from '@/hooks/web/usePage'
|
import { useGo } from '@/hooks/web/usePage'
|
||||||
|
|
||||||
import { useMultipleTabStore } from '@/store/modules/multipleTab'
|
import { useMultipleTabStore } from '@/store/modules/multipleTab'
|
||||||
|
@ -41,11 +44,14 @@ const getTabsState = computed(() => {
|
||||||
|
|
||||||
const unClose = computed(() => unref(getTabsState).length === 1)
|
const unClose = computed(() => unref(getTabsState).length === 1)
|
||||||
|
|
||||||
|
const { y: mouseY } = useMouse()
|
||||||
|
|
||||||
const getWrapClass = computed(() => {
|
const getWrapClass = computed(() => {
|
||||||
return [
|
return [
|
||||||
prefixCls,
|
prefixCls,
|
||||||
{
|
{
|
||||||
[`${prefixCls}--hide-close`]: unref(unClose),
|
[`${prefixCls}--hide-close`]: unref(unClose),
|
||||||
|
[`${prefixCls}--hover`]: unref(mouseY) < multipleTabHeight,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
"setting": {
|
"setting": {
|
||||||
"animation": "Animation",
|
"animation": "Animation",
|
||||||
"animationType": "Animation type",
|
"animationType": "Animation type",
|
||||||
|
"autoCollapseTabsInFold": "Auto collapse tabs in fold",
|
||||||
"autoScreenLock": "Auto screen lock",
|
"autoScreenLock": "Auto screen lock",
|
||||||
"breadcrumb": "Breadcrumbs",
|
"breadcrumb": "Breadcrumbs",
|
||||||
"breadcrumbIcon": "Breadcrumbs Icon",
|
"breadcrumbIcon": "Breadcrumbs Icon",
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
"setting": {
|
"setting": {
|
||||||
"animation": "动画",
|
"animation": "动画",
|
||||||
"animationType": "动画类型",
|
"animationType": "动画类型",
|
||||||
|
"autoCollapseTabsInFold": "fold模式下自动收起标签页",
|
||||||
"autoScreenLock": "自动锁屏",
|
"autoScreenLock": "自动锁屏",
|
||||||
"breadcrumb": "面包屑",
|
"breadcrumb": "面包屑",
|
||||||
"breadcrumbIcon": "面包屑图标",
|
"breadcrumbIcon": "面包屑图标",
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { ThemeEnum } from '../enums/appEnum'
|
||||||
|
|
||||||
export const prefixCls = 'xingyuv'
|
export const prefixCls = 'xingyuv'
|
||||||
|
|
||||||
|
export const multipleTabHeight = 30
|
||||||
|
|
||||||
export const darkMode = ThemeEnum.LIGHT
|
export const darkMode = ThemeEnum.LIGHT
|
||||||
|
|
||||||
// app主题色预设
|
// app主题色预设
|
||||||
|
|
|
@ -129,6 +129,7 @@ const setting: ProjectConfig = {
|
||||||
showRedo: true,
|
showRedo: true,
|
||||||
// 是否显示折叠按钮
|
// 是否显示折叠按钮
|
||||||
showFold: true,
|
showFold: true,
|
||||||
|
autoCollapse: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
// 动画配置
|
// 动画配置
|
||||||
|
|
|
@ -44,6 +44,7 @@ export interface MultiTabsSetting {
|
||||||
canDrag: boolean
|
canDrag: boolean
|
||||||
showRedo: boolean
|
showRedo: boolean
|
||||||
showFold: boolean
|
showFold: boolean
|
||||||
|
autoCollapse: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HeaderSetting {
|
export interface HeaderSetting {
|
||||||
|
|
Loading…
Reference in New Issue