mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-27 14:31:41 +08:00
feat(tabs): added tab folding
This commit is contained in:
@@ -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')}
|
||||
|
@@ -39,6 +39,7 @@ export enum HandlerEnum {
|
||||
TABS_SHOW_QUICK,
|
||||
TABS_SHOW_REDO,
|
||||
TABS_SHOW,
|
||||
TABS_SHOW_FOLD,
|
||||
|
||||
LOCK_TIME,
|
||||
FULL_CONTENT,
|
||||
|
@@ -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);
|
||||
|
@@ -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%);
|
||||
|
47
src/layouts/default/tabs/components/FoldButton.vue
Normal file
47
src/layouts/default/tabs/components/FoldButton.vue
Normal 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>
|
@@ -153,7 +153,8 @@
|
||||
|
||||
&-content {
|
||||
&__extra-quick,
|
||||
&__extra-redo {
|
||||
&__extra-redo,
|
||||
&__extra-fold {
|
||||
display: inline-block;
|
||||
width: 36px;
|
||||
height: @multiple-height;
|
||||
|
@@ -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,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@@ -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:
|
||||
|
Reference in New Issue
Block a user