feat: configurable persistent tabs
This commit is contained in:
@@ -10,12 +10,12 @@ import { useContentSpinner } from './use-content-spinner';
|
||||
|
||||
defineOptions({ name: 'LayoutContent' });
|
||||
|
||||
const tabsStore = useCoreTabbarStore();
|
||||
const tabbarStore = useCoreTabbarStore();
|
||||
const { keepAlive } = usePreferences();
|
||||
const { spinning } = useContentSpinner();
|
||||
|
||||
const { getCacheTabs, getExcludeTabs, renderRouteView } =
|
||||
storeToRefs(tabsStore);
|
||||
const { getCachedTabs, getExcludeCachedTabs, renderRouteView } =
|
||||
storeToRefs(tabbarStore);
|
||||
|
||||
// 页面切换动画
|
||||
function getTransitionName(route: RouteLocationNormalizedLoaded) {
|
||||
@@ -36,7 +36,7 @@ function getTransitionName(route: RouteLocationNormalizedLoaded) {
|
||||
// return;
|
||||
// }
|
||||
// 已经打开且已经加载过的页面不使用动画
|
||||
const inTabs = getCacheTabs.value.includes(route.name as string);
|
||||
const inTabs = getCachedTabs.value.includes(route.name as string);
|
||||
|
||||
return inTabs && route.meta.loaded ? undefined : transitionName;
|
||||
}
|
||||
@@ -54,8 +54,8 @@ function getTransitionName(route: RouteLocationNormalizedLoaded) {
|
||||
<Transition :name="getTransitionName(route)" appear mode="out-in">
|
||||
<KeepAlive
|
||||
v-if="keepAlive"
|
||||
:exclude="getExcludeTabs"
|
||||
:include="getCacheTabs"
|
||||
:exclude="getExcludeCachedTabs"
|
||||
:include="getCachedTabs"
|
||||
>
|
||||
<component
|
||||
:is="Component"
|
||||
|
@@ -34,7 +34,7 @@ const { globalSearchShortcutKey } = usePreferences();
|
||||
<div class="flex h-full min-w-0 flex-shrink-0 items-center">
|
||||
<GlobalSearch
|
||||
:enable-shortcut-key="globalSearchShortcutKey"
|
||||
:menus="accessStore.getAccessMenus"
|
||||
:menus="accessStore.accessMenus"
|
||||
class="mr-4"
|
||||
/>
|
||||
<ThemeToggle class="mr-2" />
|
||||
|
@@ -13,7 +13,7 @@ function useExtraMenu() {
|
||||
const accessStore = useCoreAccessStore();
|
||||
const { navigation } = useNavigation();
|
||||
|
||||
const menus = computed(() => accessStore.getAccessMenus);
|
||||
const menus = computed(() => accessStore.accessMenus);
|
||||
|
||||
const route = useRoute();
|
||||
const extraMenus = ref<MenuRecordRaw[]>([]);
|
||||
|
@@ -29,7 +29,7 @@ function useMixedMenu() {
|
||||
}
|
||||
return enableSidebar;
|
||||
});
|
||||
const menus = computed(() => accessStore.getAccessMenus);
|
||||
const menus = computed(() => accessStore.accessMenus);
|
||||
|
||||
/**
|
||||
* 头部菜单
|
||||
|
@@ -1,4 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { preferences } from '@vben-core/preferences';
|
||||
import { useCoreTabbarStore } from '@vben-core/stores';
|
||||
import { TabsView } from '@vben-core/tabs-ui';
|
||||
|
||||
import { useTabs } from './use-tabs';
|
||||
@@ -9,14 +13,23 @@ defineOptions({
|
||||
|
||||
defineProps<{ showIcon?: boolean }>();
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const coreTabbarStore = useCoreTabbarStore();
|
||||
|
||||
const {
|
||||
createContextMenus,
|
||||
currentActive,
|
||||
currentTabs,
|
||||
handleClick,
|
||||
handleClose,
|
||||
handleUnPushPin,
|
||||
handleUnpinTab,
|
||||
} = useTabs();
|
||||
|
||||
// 刷新后如果不保持tab状态,关闭其他tab
|
||||
if (!preferences.tabbar.persist) {
|
||||
coreTabbarStore.closeOtherTabs(route);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -26,7 +39,7 @@ const {
|
||||
:show-icon="showIcon"
|
||||
:tabs="currentTabs"
|
||||
@close="handleClose"
|
||||
@un-push-pin="handleUnPushPin"
|
||||
@unpin-tab="handleUnpinTab"
|
||||
@update:active="handleClick"
|
||||
/>
|
||||
</template>
|
||||
|
@@ -30,7 +30,7 @@ function useTabs() {
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const accessStore = useCoreAccessStore();
|
||||
const tabbarStore = useCoreTabbarStore();
|
||||
const coreTabbarStore = useCoreTabbarStore();
|
||||
const { accessMenus } = storeToRefs(accessStore);
|
||||
|
||||
const currentActive = computed(() => {
|
||||
@@ -39,7 +39,7 @@ function useTabs() {
|
||||
|
||||
const { locale } = useI18n();
|
||||
const currentTabs = ref<RouteLocationNormalizedGeneric[]>();
|
||||
watch([() => tabbarStore.getTabs, () => locale.value], ([tabs, _]) => {
|
||||
watch([() => coreTabbarStore.getTabs, () => locale.value], ([tabs, _]) => {
|
||||
currentTabs.value = tabs.map((item) => wrapperTabLocale(item));
|
||||
});
|
||||
|
||||
@@ -50,7 +50,7 @@ function useTabs() {
|
||||
const affixTabs = filterTree(router.getRoutes(), (route) => {
|
||||
return !!route.meta?.affixTab;
|
||||
});
|
||||
tabbarStore.setAffixTabs(affixTabs);
|
||||
coreTabbarStore.setAffixTabs(affixTabs);
|
||||
};
|
||||
|
||||
// 点击tab,跳转路由
|
||||
@@ -60,7 +60,7 @@ function useTabs() {
|
||||
|
||||
// 关闭tab
|
||||
const handleClose = async (key: string) => {
|
||||
await tabbarStore.closeTabByKey(key, router);
|
||||
await coreTabbarStore.closeTabByKey(key, router);
|
||||
};
|
||||
|
||||
function wrapperTabLocale(tab: RouteLocationNormalizedGeneric) {
|
||||
@@ -84,14 +84,14 @@ function useTabs() {
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
tabbarStore.addTab(route as RouteLocationNormalized);
|
||||
coreTabbarStore.addTab(route as RouteLocationNormalized);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const createContextMenus = (tab: TabItem) => {
|
||||
const tabs = tabbarStore.getTabs;
|
||||
const affixTabs = tabbarStore.affixTabs;
|
||||
const tabs = coreTabbarStore.getTabs;
|
||||
const affixTabs = coreTabbarStore.affixTabs;
|
||||
const index = tabs.findIndex((item) => item.path === tab.path);
|
||||
|
||||
const disabled = tabs.length <= 1;
|
||||
@@ -113,7 +113,7 @@ function useTabs() {
|
||||
{
|
||||
disabled: !isCurrentTab,
|
||||
handler: async () => {
|
||||
await tabbarStore.refresh(router);
|
||||
await coreTabbarStore.refresh(router);
|
||||
},
|
||||
icon: IcRoundRefresh,
|
||||
key: 'reload',
|
||||
@@ -122,7 +122,7 @@ function useTabs() {
|
||||
{
|
||||
disabled: !!affixTab || disabled,
|
||||
handler: async () => {
|
||||
await tabbarStore.closeTab(tab, router);
|
||||
await coreTabbarStore.closeTab(tab, router);
|
||||
},
|
||||
icon: IcRoundClose,
|
||||
key: 'close',
|
||||
@@ -131,8 +131,8 @@ function useTabs() {
|
||||
{
|
||||
handler: async () => {
|
||||
await (affixTab
|
||||
? tabbarStore.unPushPinTab(tab)
|
||||
: tabbarStore.pushPinTab(tab));
|
||||
? coreTabbarStore.unpinTab(tab)
|
||||
: coreTabbarStore.pinTab(tab));
|
||||
},
|
||||
icon: affixTab ? MdiPinOff : MdiPin,
|
||||
key: 'affix',
|
||||
@@ -144,7 +144,7 @@ function useTabs() {
|
||||
{
|
||||
disabled: closeLeftDisabled,
|
||||
handler: async () => {
|
||||
await tabbarStore.closeLeftTabs(tab);
|
||||
await coreTabbarStore.closeLeftTabs(tab);
|
||||
},
|
||||
icon: MdiFormatHorizontalAlignLeft,
|
||||
key: 'close-left',
|
||||
@@ -153,7 +153,7 @@ function useTabs() {
|
||||
{
|
||||
disabled: closeRightDisabled,
|
||||
handler: async () => {
|
||||
await tabbarStore.closeRightTabs(tab);
|
||||
await coreTabbarStore.closeRightTabs(tab);
|
||||
},
|
||||
icon: MdiFormatHorizontalAlignRight,
|
||||
key: 'close-right',
|
||||
@@ -163,7 +163,7 @@ function useTabs() {
|
||||
{
|
||||
disabled: closeOtherDisabled,
|
||||
handler: async () => {
|
||||
await tabbarStore.closeOtherTabs(tab);
|
||||
await coreTabbarStore.closeOtherTabs(tab);
|
||||
},
|
||||
icon: MdiArrowExpandHorizontal,
|
||||
key: 'close-other',
|
||||
@@ -172,7 +172,7 @@ function useTabs() {
|
||||
{
|
||||
disabled,
|
||||
handler: async () => {
|
||||
await tabbarStore.closeAllTabs(router);
|
||||
await coreTabbarStore.closeAllTabs(router);
|
||||
},
|
||||
icon: IcRoundMultipleStop,
|
||||
key: 'close-all',
|
||||
@@ -190,8 +190,8 @@ function useTabs() {
|
||||
/**
|
||||
* 取消固定标签页
|
||||
*/
|
||||
const handleUnPushPin = async (tab: TabItem) => {
|
||||
await tabbarStore.unPushPinTab(tab);
|
||||
const handleUnpinTab = async (tab: TabItem) => {
|
||||
await coreTabbarStore.unpinTab(tab);
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -200,7 +200,7 @@ function useTabs() {
|
||||
currentTabs,
|
||||
handleClick,
|
||||
handleClose,
|
||||
handleUnPushPin,
|
||||
handleUnpinTab,
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -11,7 +11,7 @@ import { useCoreTabbarStore } from '@vben-core/stores';
|
||||
defineOptions({ name: 'IFrameRouterView' });
|
||||
|
||||
const spinningList = ref<boolean[]>([]);
|
||||
const tabsStore = useCoreTabbarStore();
|
||||
const coreTabbarStore = useCoreTabbarStore();
|
||||
const route = useRoute();
|
||||
|
||||
const enableTabbar = computed(() => preferences.tabbar.enable);
|
||||
@@ -20,7 +20,7 @@ const iframeRoutes = computed(() => {
|
||||
if (!enableTabbar.value) {
|
||||
return route.meta.iframeSrc ? [route] : [];
|
||||
}
|
||||
return tabsStore.getTabs.filter((tab) => !!tab.meta?.iframeSrc);
|
||||
return coreTabbarStore.getTabs.filter((tab) => !!tab.meta?.iframeSrc);
|
||||
});
|
||||
|
||||
const tabNames = computed(
|
||||
@@ -36,7 +36,7 @@ function routeShow(tabItem: RouteLocationNormalized) {
|
||||
function canRender(tabItem: RouteLocationNormalized) {
|
||||
const { meta, name } = tabItem;
|
||||
|
||||
if (!name || !tabsStore.renderRouteView) {
|
||||
if (!name || !coreTabbarStore.renderRouteView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ function canRender(tabItem: RouteLocationNormalized) {
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return tabsStore.getTabs.some((tab) => tab.name === name);
|
||||
return coreTabbarStore.getTabs.some((tab) => tab.name === name);
|
||||
}
|
||||
|
||||
function hideLoading(index: number) {
|
||||
|
@@ -11,6 +11,7 @@ defineProps<{ disabled?: boolean }>();
|
||||
|
||||
const tabbarEnable = defineModel<boolean>('tabbarEnable');
|
||||
const tabbarShowIcon = defineModel<boolean>('tabbarShowIcon');
|
||||
const tabbarPersist = defineModel<boolean>('tabbarPersist');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -20,4 +21,7 @@ const tabbarShowIcon = defineModel<boolean>('tabbarShowIcon');
|
||||
<SwitchItem v-model="tabbarShowIcon" :disabled="!tabbarEnable">
|
||||
{{ $t('preferences.tabbar.icon') }}
|
||||
</SwitchItem>
|
||||
<SwitchItem v-model="tabbarPersist" :disabled="!tabbarEnable">
|
||||
{{ $t('preferences.tabbar.persist') }}
|
||||
</SwitchItem>
|
||||
</template>
|
||||
|
@@ -96,6 +96,7 @@ const breadcrumbHideOnlyOne = defineModel<boolean>('breadcrumbHideOnlyOne');
|
||||
|
||||
const tabbarEnable = defineModel<boolean>('tabbarEnable');
|
||||
const tabbarShowIcon = defineModel<boolean>('tabbarShowIcon');
|
||||
const tabbarPersist = defineModel<boolean>('tabbarPersist');
|
||||
|
||||
const navigationStyleType = defineModel<NavigationStyleType>(
|
||||
'navigationStyleType',
|
||||
@@ -341,6 +342,7 @@ async function handleReset() {
|
||||
<Block :title="$t('preferences.tabbar.title')">
|
||||
<Tabbar
|
||||
v-model:tabbar-enable="tabbarEnable"
|
||||
v-model:tabbar-persist="tabbarPersist"
|
||||
v-model:tabbar-show-icon="tabbarShowIcon"
|
||||
/>
|
||||
</Block>
|
||||
|
Reference in New Issue
Block a user