feat(MultipleTab): add tabs auto collapse

pull/40/MERGE
xingyu 2023-11-09 11:49:20 +08:00
parent 21e51d128d
commit b0ff73285f
13 changed files with 121 additions and 17 deletions

View File

@ -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,
} }
} }

View File

@ -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>

View File

@ -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>

View File

@ -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}

View File

@ -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,

View File

@ -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)

View File

@ -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;

View File

@ -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,
}, },
] ]
}) })

View File

@ -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",

View File

@ -28,6 +28,7 @@
"setting": { "setting": {
"animation": "动画", "animation": "动画",
"animationType": "动画类型", "animationType": "动画类型",
"autoCollapseTabsInFold": "fold模式下自动收起标签页",
"autoScreenLock": "自动锁屏", "autoScreenLock": "自动锁屏",
"breadcrumb": "面包屑", "breadcrumb": "面包屑",
"breadcrumbIcon": "面包屑图标", "breadcrumbIcon": "面包屑图标",

View File

@ -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主题色预设

View File

@ -129,6 +129,7 @@ const setting: ProjectConfig = {
showRedo: true, showRedo: true,
// 是否显示折叠按钮 // 是否显示折叠按钮
showFold: true, showFold: true,
autoCollapse: false,
}, },
// 动画配置 // 动画配置

View File

@ -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 {