feat(tabs): added tab folding

This commit is contained in:
vben
2021-01-06 20:10:16 +08:00
parent 144ab577da
commit 0e7c57bd5e
30 changed files with 270 additions and 176 deletions

View File

@@ -85,7 +85,7 @@ export default defineComponent({
getShowSearch,
} = useHeaderSetting();
const { getShowMultipleTab, getShowQuick, getShowRedo } = useMultipleTabSetting();
const { getShowMultipleTab, getShowQuick, getShowRedo, getShowFold } = useMultipleTabSetting();
const getShowMenuRef = computed(() => {
return unref(getShowMenu) && !unref(getIsHorizontal);
@@ -105,33 +105,6 @@ export default defineComponent({
}}
def={unref(getMenuType)}
/>
<SwitchItem
title={t('layout.setting.splitMenu')}
event={HandlerEnum.MENU_SPLIT}
def={unref(getSplit)}
disabled={!unref(getShowMenuRef) || unref(getMenuType) !== MenuTypeEnum.MIX}
/>
<SwitchItem
title={t('layout.setting.mixSidebarFixed')}
event={HandlerEnum.MENU_FIXED_MIX_SIDEBAR}
def={unref(getMixSideFixed)}
disabled={!unref(getIsMixSidebar)}
/>
<SwitchItem
title={t('layout.setting.closeMixSidebarOnChange')}
event={HandlerEnum.MENU_CLOSE_MIX_SIDEBAR_ON_CHANGE}
def={unref(getCloseMixSidebarOnChange)}
disabled={!unref(getIsMixSidebar)}
/>
<SelectItem
title={t('layout.setting.mixSidebarTrigger')}
event={HandlerEnum.MENU_TRIGGER_MIX_SIDEBAR}
def={unref(getMixSideTrigger)}
options={mixSidebarTriggerOptions}
disabled={!unref(getIsMixSidebar)}
/>
</>
);
}
@@ -170,6 +143,32 @@ export default defineComponent({
return (
<>
<SwitchItem
title={t('layout.setting.splitMenu')}
event={HandlerEnum.MENU_SPLIT}
def={unref(getSplit)}
disabled={!unref(getShowMenuRef) || unref(getMenuType) !== MenuTypeEnum.MIX}
/>
<SwitchItem
title={t('layout.setting.mixSidebarFixed')}
event={HandlerEnum.MENU_FIXED_MIX_SIDEBAR}
def={unref(getMixSideFixed)}
disabled={!unref(getIsMixSidebar)}
/>
<SwitchItem
title={t('layout.setting.closeMixSidebarOnChange')}
event={HandlerEnum.MENU_CLOSE_MIX_SIDEBAR_ON_CHANGE}
def={unref(getCloseMixSidebarOnChange)}
disabled={!unref(getIsMixSidebar)}
/>
<SwitchItem
title={t('layout.setting.menuCollapse')}
event={HandlerEnum.MENU_COLLAPSED}
def={unref(getCollapsed)}
disabled={!unref(getShowMenuRef)}
/>
<SwitchItem
title={t('layout.setting.menuDrag')}
event={HandlerEnum.MENU_HAS_DRAG}
@@ -188,17 +187,12 @@ export default defineComponent({
def={unref(getAccordion)}
disabled={!unref(getShowMenuRef)}
/>
<SwitchItem
title={t('layout.setting.menuCollapse')}
event={HandlerEnum.MENU_COLLAPSED}
def={unref(getCollapsed)}
disabled={!unref(getShowMenuRef) || unref(getIsMixSidebar)}
/>
<SwitchItem
title={t('layout.setting.collapseMenuDisplayName')}
event={HandlerEnum.MENU_COLLAPSED_SHOW_TITLE}
def={unref(getCollapsedShowTitle)}
disabled={!unref(getShowMenuRef) || !unref(getCollapsed)}
disabled={!unref(getShowMenuRef) || !unref(getCollapsed) || unref(getIsMixSidebar)}
/>
<SwitchItem
@@ -213,6 +207,13 @@ export default defineComponent({
def={unref(getMenuFixed)}
disabled={!unref(getShowMenuRef) || unref(getIsMixSidebar)}
/>
<SelectItem
title={t('layout.setting.mixSidebarTrigger')}
event={HandlerEnum.MENU_TRIGGER_MIX_SIDEBAR}
def={unref(getMixSideTrigger)}
options={mixSidebarTriggerOptions}
disabled={!unref(getIsMixSidebar)}
/>
<SelectItem
title={t('layout.setting.topMenuLayout')}
event={HandlerEnum.MENU_TOP_ALIGN}
@@ -299,6 +300,12 @@ export default defineComponent({
def={unref(getShowQuick)}
disabled={!unref(getShowMultipleTab)}
/>
<SwitchItem
title={t('layout.setting.tabsFoldBtn')}
event={HandlerEnum.TABS_SHOW_FOLD}
def={unref(getShowFold)}
disabled={!unref(getShowMultipleTab)}
/>
<SwitchItem
title={t('layout.setting.sidebar')}

View File

@@ -39,6 +39,7 @@ export enum HandlerEnum {
TABS_SHOW_QUICK,
TABS_SHOW_REDO,
TABS_SHOW,
TABS_SHOW_FOLD,
LOCK_TIME,
FULL_CONTENT,

View File

@@ -71,7 +71,7 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf
return { menuSetting: { mixSideTrigger: value } };
case HandlerEnum.MENU_FIXED_MIX_SIDEBAR:
return { menuSetting: { mixSideTrigger: value } };
return { menuSetting: { mixSideFixed: value } };
// ============transition==================
case HandlerEnum.OPEN_PAGE_LOADING:
@@ -123,9 +123,13 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf
case HandlerEnum.TABS_SHOW:
return { multiTabsSetting: { show: value } };
case HandlerEnum.TABS_SHOW_REDO:
return { multiTabsSetting: { showRedo: value } };
case HandlerEnum.TABS_SHOW_FOLD:
return { multiTabsSetting: { showFold: value } };
// ============header==================
case HandlerEnum.HEADER_THEME:
updateHeaderBgColor(value);

View File

@@ -3,11 +3,13 @@
<div
v-click-outside="handleClickOutside"
:style="getWrapStyle"
:class="[
prefixCls,
getMenuTheme,
{
open: openMenu,
mini: getCollapsed,
},
]"
v-bind="getMenuEvents"
@@ -29,7 +31,7 @@
<MenuTag :item="item" :showTitle="false" :isHorizontal="false" />
<Icon
:class="`${prefixCls}-module__icon`"
:size="22"
:size="getCollapsed ? 16 : 20"
:icon="item.meta && item.meta.icon"
/>
<p :class="`${prefixCls}-module__name`">{{ t(item.name) }}</p>
@@ -50,12 +52,10 @@
<span class="text"> {{ title }}</span>
<Icon
:size="16"
v-if="getMixSideFixed"
icon="ri:pushpin-2-fill"
:icon="getMixSideFixed ? 'ri:pushpin-2-fill' : 'ri:pushpin-2-line'"
class="pushpin"
@click="handleFixedMenu"
/>
<Icon :size="16" v-else icon="ri:pushpin-2-line" class="pushpin" @click="handleFixedMenu" />
</div>
<ScrollContainer :class="`${prefixCls}-menu-list__content`">
<BasicMenu
@@ -92,7 +92,7 @@
import { useDragLine } from './useLayoutSider';
import { useGlobSetting } from '/@/hooks/setting';
import { SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum';
import { SIDE_BAR_SHOW_TIT_MINI_WIDTH, SIDE_BAR_MINI_WIDTH } from '/@/enums/appEnum';
import clickOutside from '/@/directives/clickOutside';
@@ -130,6 +130,8 @@
getMixSideFixed,
mixSideHasChildren,
setMenuSetting,
getIsMixSidebar,
getCollapsed,
} = useMenuSetting();
const { title } = useGlobSetting();
@@ -140,6 +142,7 @@
(): CSSProperties => {
return {
width: unref(openMenu) ? `${unref(getMenuWidth)}px` : 0,
left: `${unref(getMixSideWidth)}px`,
};
}
);
@@ -153,32 +156,33 @@
return isFixed;
});
const getMixSideWidth = computed(() => {
return unref(getCollapsed) ? SIDE_BAR_MINI_WIDTH : SIDE_BAR_SHOW_TIT_MINI_WIDTH;
});
const getDomStyle = computed(
(): CSSProperties => {
const fixedWidth = unref(getIsFixed) ? unref(getRealWidth) : 0;
const width = `${SIDE_BAR_SHOW_TIT_MINI_WIDTH + fixedWidth}px`;
return {
width,
maxWidth: width,
minWidth: width,
flex: `0 0 ${width}`,
};
const width = `${unref(getMixSideWidth) + fixedWidth}px`;
return getWrapCommonStyle(width);
}
);
const getWrapStyle = computed(
(): CSSProperties => {
const width = `${unref(getMixSideWidth)}px`;
return getWrapCommonStyle(width);
}
);
const getMenuEvents = computed(() => {
// return unref(getMixSideTrigger) === 'hover'
// ? {
// onMouseleave: () => {
// closeMenu();
// },
// }
// : {};
return {
onMouseleave: () => {
closeMenu();
},
};
return !unref(getMixSideFixed)
? {
onMouseleave: () => {
closeMenu();
},
}
: {};
});
const getShowDragBar = computed(() => unref(getCanDrag));
@@ -195,6 +199,16 @@
}
});
function getWrapCommonStyle(width: string): CSSProperties {
return {
width,
maxWidth: width,
minWidth: width,
flex: `0 0 ${width}`,
};
}
// Process module menu click
async function hanldeModuleClick(path: string, hover = false) {
const children = await getChildrenMenus(path);
@@ -223,20 +237,24 @@
chilrenMenus.value = children;
}
// Set the currently active menu and submenu
async function setActive(setChildren = false) {
const path = currentRoute.value?.path;
if (!path) return;
const parentPath = await getCurrentParentPath(path);
activePath.value = parentPath;
// hanldeModuleClick(parentPath);
if (unref(getMixSideFixed)) {
if (unref(getIsMixSidebar)) {
const activeMenu = unref(menuModules).find((item) => item.path === unref(activePath));
const p = activeMenu?.path;
if (p) {
const children = await getChildrenMenus(p);
if (setChildren) {
chilrenMenus.value = children;
openMenu.value = children.length > 0;
if (unref(getMixSideFixed)) {
openMenu.value = children.length > 0;
}
}
if (children.length === 0) {
chilrenMenus.value = [];
@@ -271,6 +289,7 @@
});
}
// Close menu
function closeMenu() {
if (!unref(getIsFixed)) {
openMenu.value = false;
@@ -298,6 +317,8 @@
getDomStyle,
handleFixedMenu,
getMixSideFixed,
getWrapStyle,
getCollapsed,
};
},
});
@@ -312,14 +333,10 @@
top: 0;
left: 0;
z-index: @layout-mix-sider-fixed-z-index;
width: @width;
height: 100%;
max-width: @width;
min-width: @width;
overflow: hidden;
background: @sider-dark-bg-color;
transition: all 0.3s ease 0s;
flex: 0 0 @width;
transition: all 0.2s ease 0s;
.@{tag-prefix-cls} {
position: absolute;
top: 6px;
@@ -327,13 +344,9 @@
}
&-dom {
width: @width;
height: 100%;
max-width: @width;
min-width: @width;
overflow: hidden;
transition: all 0.2s ease 0s;
flex: 0 0 @width;
}
&-logo {
@@ -354,7 +367,7 @@
}
&.open {
> .scroll-container {
> .scrollbar {
border-right: 1px solid rgb(238, 238, 238);
}
}
@@ -390,7 +403,7 @@
border-bottom: 1px solid @border-color;
}
> .scroll-container {
> .scrollbar {
border-right: 1px solid @border-color;
}
}
@@ -409,6 +422,16 @@
height: calc(100% - @header-height) !important;
}
&.mini &-module {
&__name {
display: none;
}
&__icon {
margin-bottom: 0;
}
}
&-module {
position: relative;
padding-top: 1px;
@@ -456,7 +479,6 @@
&-menu-list {
position: fixed;
top: 0;
left: 80px;
width: 0;
width: 200px;
height: calc(100%);

View File

@@ -0,0 +1,47 @@
<template>
<span :class="`${prefixCls}__extra-fold`" @click="handleFold">
<Icon :icon="getIcon" />
</span>
</template>
<script lang="ts">
import { defineComponent, unref, computed } from 'vue';
import { RedoOutlined } from '@ant-design/icons-vue';
import { useDesign } from '/@/hooks/web/useDesign';
import { Tooltip } from 'ant-design-vue';
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import Icon from '/@/components/Icon';
export default defineComponent({
name: 'FoldButton',
components: { RedoOutlined, Tooltip, Icon },
setup() {
const { prefixCls } = useDesign('multiple-tabs-content');
const { getShowMenu, setMenuSetting } = useMenuSetting();
const { getShowHeader, setHeaderSetting } = useHeaderSetting();
const getIsUnFold = computed(() => {
return !unref(getShowMenu) && !unref(getShowHeader);
});
const getIcon = computed(() => {
return unref(getIsUnFold) ? 'codicon:screen-normal' : 'codicon:screen-full';
});
function handleFold() {
const isScale = !unref(getShowMenu) && !unref(getShowHeader);
setMenuSetting({
show: isScale,
hidden: !isScale,
});
setHeaderSetting({
show: isScale,
});
}
return { prefixCls, getIcon, handleFold };
},
});
</script>

View File

@@ -153,7 +153,8 @@
&-content {
&__extra-quick,
&__extra-redo {
&__extra-redo,
&__extra-fold {
display: inline-block;
width: 36px;
height: @multiple-height;

View File

@@ -21,6 +21,7 @@
<template #tabBarExtraContent v-if="getShowRedo || getShowQuick">
<TabRedo v-if="getShowRedo" />
<QuickButton v-if="getShowQuick" />
<FoldButton v-if="getShowFold" />
</template>
</Tabs>
</div>
@@ -51,6 +52,7 @@
components: {
QuickButton: createAsyncComponent(() => import('./components/QuickButton.vue')),
TabRedo: createAsyncComponent(() => import('./components/TabRedo.vue')),
FoldButton: createAsyncComponent(() => import('./components/FoldButton.vue')),
Tabs,
TabPane: Tabs.TabPane,
TabContent,
@@ -62,7 +64,7 @@
useTabsDrag(affixTextList);
const { prefixCls } = useDesign('multiple-tabs');
const go = useGo();
const { getShowQuick, getShowRedo } = useMultipleTabSetting();
const { getShowQuick, getShowRedo, getShowFold } = useMultipleTabSetting();
const getTabsState = computed(() => {
return tabStore.getTabsState.filter((item) => !item.meta?.hideTab);
@@ -125,6 +127,7 @@
getTabsState,
getShowQuick,
getShowRedo,
getShowFold,
};
},
});

View File

@@ -8,8 +8,6 @@ import router from '/@/router';
import { RouteLocationNormalized } from 'vue-router';
import { useTabs } from '/@/hooks/web/useTabs';
import { useI18n } from '/@/hooks/web/useI18n';
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
const { t } = useI18n();
@@ -21,9 +19,6 @@ export function useTabDropdown(tabContentProps: TabContentProps) {
const { currentRoute } = router;
const { getShowMenu, setMenuSetting } = useMenuSetting();
const { getShowHeader, setHeaderSetting } = useHeaderSetting();
const isTabs = computed(() => tabContentProps.type === TabContentEnum.TAB_TYPE);
const getCurrentTab = computed(
@@ -32,10 +27,6 @@ export function useTabDropdown(tabContentProps: TabContentProps) {
}
);
const getIsScale = computed(() => {
return !unref(getShowMenu) && !unref(getShowHeader);
});
/**
* @description: drop-down list
*/
@@ -98,16 +89,6 @@ export function useTabDropdown(tabContentProps: TabContentProps) {
},
];
if (!unref(isTabs)) {
const isScale = unref(getIsScale);
dropMenuList.unshift({
icon: isScale ? 'codicon:screen-normal' : 'codicon:screen-full',
event: MenuEventEnum.SCALE,
text: isScale ? t('layout.multipleTab.putAway') : t('layout.multipleTab.unfold'),
disabled: false,
});
}
return dropMenuList;
});
@@ -125,20 +106,9 @@ export function useTabDropdown(tabContentProps: TabContentProps) {
};
}
function scaleScreen() {
const isScale = !unref(getShowMenu) && !unref(getShowHeader);
setMenuSetting({
show: isScale,
hidden: !isScale,
});
setHeaderSetting({
show: isScale,
});
}
// Handle right click event
function handleMenuEvent(menu: DropMenu): void {
const { refreshPage, closeAll, closeCurrent, closeLeft, closeOther, closeRight } = useTabs();
const { refreshPage, closeAll, close, closeLeft, closeOther, closeRight } = useTabs();
const { event } = menu;
switch (event) {
case MenuEventEnum.SCALE:
@@ -150,7 +120,7 @@ export function useTabDropdown(tabContentProps: TabContentProps) {
break;
// Close current
case MenuEventEnum.CLOSE_CURRENT:
closeCurrent();
close(tabContentProps.tabItem);
break;
// Close left
case MenuEventEnum.CLOSE_LEFT: