feat: use simpler nitro instead of nestjs to implement mock service
This commit is contained in:
@@ -4,7 +4,7 @@ import type { Preferences } from './types';
|
||||
|
||||
import { markRaw, reactive, readonly, watch } from 'vue';
|
||||
|
||||
import { StorageManager, merge } from '@vben-core/toolkit';
|
||||
import { StorageManager, isMacOs, merge } from '@vben-core/toolkit';
|
||||
|
||||
import {
|
||||
breakpointsTailwind,
|
||||
@@ -46,7 +46,7 @@ class PreferenceManager {
|
||||
|
||||
this.savePreferences = useDebounceFn(
|
||||
(preference: Preferences) => this._savePreferences(preference),
|
||||
200,
|
||||
100,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -81,6 +81,11 @@ class PreferenceManager {
|
||||
}
|
||||
}
|
||||
|
||||
private initPlatform() {
|
||||
const dom = document.documentElement;
|
||||
dom.dataset.platform = isMacOs() ? 'macOs' : 'window';
|
||||
}
|
||||
|
||||
/**
|
||||
* 从缓存中加载偏好设置。如果缓存中没有找到对应的偏好设置,则返回默认偏好设置。
|
||||
*/
|
||||
@@ -187,6 +192,8 @@ class PreferenceManager {
|
||||
this.updatePreferences(mergedPreference);
|
||||
|
||||
this.setupWatcher();
|
||||
|
||||
this.initPlatform();
|
||||
// 标记为已初始化
|
||||
this.isInitialized = true;
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ import type { RouteRecordNormalized, Router } from 'vue-router';
|
||||
|
||||
import { toRaw } from 'vue';
|
||||
|
||||
import { startProgress, stopProgress } from '@vben-core/toolkit';
|
||||
import { openWindow, startProgress, stopProgress } from '@vben-core/toolkit';
|
||||
|
||||
import { acceptHMRUpdate, defineStore } from 'pinia';
|
||||
|
||||
@@ -226,6 +226,18 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
|
||||
|
||||
await this.closeTab(this.tabs[index], router);
|
||||
},
|
||||
/**
|
||||
* @zh_CN 新窗口打开标签页
|
||||
* @param tab
|
||||
*/
|
||||
async openTabInNewWindow(tab: TabDefinition) {
|
||||
const { hash, origin } = location;
|
||||
const path = tab.fullPath;
|
||||
const fullPath = path.startsWith('/') ? path : `/${path}`;
|
||||
const url = `${origin}${hash ? '/#' : ''}${fullPath}`;
|
||||
openWindow(url, { target: '_blank' });
|
||||
},
|
||||
|
||||
/**
|
||||
* @zh_CN 固定标签页
|
||||
* @param tab
|
||||
@@ -257,6 +269,23 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
|
||||
this.renderRouteView = true;
|
||||
stopProgress();
|
||||
},
|
||||
|
||||
/**
|
||||
* @zh_CN 重置标签页标题
|
||||
*/
|
||||
async resetTabTitle(tab: TabDefinition) {
|
||||
if (!tab?.meta?.newTabTitle) {
|
||||
return;
|
||||
}
|
||||
const findTab = this.tabs.find(
|
||||
(item) => getTabPath(item) === getTabPath(tab),
|
||||
);
|
||||
if (findTab) {
|
||||
findTab.meta.newTabTitle = undefined;
|
||||
await this.updateCacheTab();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 设置固定标签页
|
||||
* @param tabs
|
||||
@@ -267,6 +296,21 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
|
||||
this.addTab(routeToTab(tab));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @zh_CN 设置标签页标题
|
||||
* @param tab
|
||||
* @param title
|
||||
*/
|
||||
async setTabTitle(tab: TabDefinition, title: string) {
|
||||
const findTab = this.tabs.find(
|
||||
(item) => getTabPath(item) === getTabPath(tab),
|
||||
);
|
||||
if (findTab) {
|
||||
findTab.meta.newTabTitle = title;
|
||||
await this.updateCacheTab();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @zh_CN 设置标签页顺序
|
||||
* @param oldIndex
|
||||
@@ -278,6 +322,15 @@ const useCoreTabbarStore = defineStore('core-tabbar', {
|
||||
this.tabs.splice(newIndex, 0, currentTab);
|
||||
this.dragEndIndex = this.dragEndIndex + 1;
|
||||
},
|
||||
/**
|
||||
* @zh_CN 切换固定标签页
|
||||
* @param tab
|
||||
*/
|
||||
async toggleTabPin(tab: TabDefinition) {
|
||||
const affixTab = tab?.meta?.affixTab ?? false;
|
||||
await (affixTab ? this.unpinTab(tab) : this.pinTab(tab));
|
||||
},
|
||||
|
||||
/**
|
||||
* @zh_CN 取消固定标签页
|
||||
* @param tab
|
||||
|
@@ -37,11 +37,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vben-core/constants": "workspace:*",
|
||||
"@vben-core/preferences": "workspace:*",
|
||||
"@vben-core/stores": "workspace:*",
|
||||
"@vben-core/toolkit": "workspace:*",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"radix-vue": "^1.9.1",
|
||||
"sortablejs": "^1.15.2",
|
||||
"vue": "^3.4.32"
|
||||
"vue": "^3.4.32",
|
||||
"vue-router": "^4.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/sortablejs": "^1.15.8"
|
||||
|
@@ -1,6 +1,9 @@
|
||||
export * from './use-content-height';
|
||||
export * from './use-content-maximize';
|
||||
export * from './use-namespace';
|
||||
export * from './use-refresh';
|
||||
export * from './use-sortable';
|
||||
export * from './use-tabs';
|
||||
export {
|
||||
useEmitAsProps,
|
||||
useForwardExpose,
|
||||
|
24
packages/@core/hooks/src/use-content-maximize.ts
Normal file
24
packages/@core/hooks/src/use-content-maximize.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { updatePreferences, usePreferences } from '@vben-core/preferences';
|
||||
/**
|
||||
* 主体区域最大化
|
||||
*/
|
||||
export function useContentMaximize() {
|
||||
const { contentIsMaximize } = usePreferences();
|
||||
|
||||
function toggleMaximize() {
|
||||
const isMaximize = contentIsMaximize.value;
|
||||
|
||||
updatePreferences({
|
||||
header: {
|
||||
hidden: !isMaximize,
|
||||
},
|
||||
sidebar: {
|
||||
hidden: !isMaximize,
|
||||
},
|
||||
});
|
||||
}
|
||||
return {
|
||||
contentIsMaximize,
|
||||
toggleMaximize,
|
||||
};
|
||||
}
|
16
packages/@core/hooks/src/use-refresh.ts
Normal file
16
packages/@core/hooks/src/use-refresh.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { useCoreTabbarStore } from '@vben-core/stores';
|
||||
|
||||
export function useRefresh() {
|
||||
const router = useRouter();
|
||||
const coreTabbarStore = useCoreTabbarStore();
|
||||
|
||||
function refresh() {
|
||||
coreTabbarStore.refresh(router);
|
||||
}
|
||||
|
||||
return {
|
||||
refresh,
|
||||
};
|
||||
}
|
111
packages/@core/hooks/src/use-tabs.ts
Normal file
111
packages/@core/hooks/src/use-tabs.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { type RouteLocationNormalized, useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { useCoreTabbarStore } from '@vben-core/stores';
|
||||
|
||||
export function useTabs() {
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const coreTabbarStore = useCoreTabbarStore();
|
||||
|
||||
async function closeLeftTabs(tab?: RouteLocationNormalized) {
|
||||
await coreTabbarStore.closeLeftTabs(tab || route);
|
||||
}
|
||||
|
||||
async function closeAllTabs() {
|
||||
await coreTabbarStore.closeAllTabs(router);
|
||||
}
|
||||
|
||||
async function closeRightTabs(tab?: RouteLocationNormalized) {
|
||||
await coreTabbarStore.closeRightTabs(tab || route);
|
||||
}
|
||||
|
||||
async function closeOtherTabs(tab?: RouteLocationNormalized) {
|
||||
await coreTabbarStore.closeOtherTabs(tab || route);
|
||||
}
|
||||
|
||||
async function closeCurrentTab(tab?: RouteLocationNormalized) {
|
||||
await coreTabbarStore.closeTab(tab || route, router);
|
||||
}
|
||||
|
||||
async function pinTab(tab?: RouteLocationNormalized) {
|
||||
await coreTabbarStore.pinTab(tab || route);
|
||||
}
|
||||
|
||||
async function unpinTab(tab?: RouteLocationNormalized) {
|
||||
await coreTabbarStore.unpinTab(tab || route);
|
||||
}
|
||||
|
||||
async function toggleTabPin(tab?: RouteLocationNormalized) {
|
||||
await coreTabbarStore.toggleTabPin(tab || route);
|
||||
}
|
||||
|
||||
async function refreshTab() {
|
||||
await coreTabbarStore.refresh(router);
|
||||
}
|
||||
|
||||
async function openTabInNewWindow(tab?: RouteLocationNormalized) {
|
||||
coreTabbarStore.openTabInNewWindow(tab || route);
|
||||
}
|
||||
|
||||
async function closeTabByKey(key: string) {
|
||||
await coreTabbarStore.closeTabByKey(key, router);
|
||||
}
|
||||
|
||||
async function setTabTitle(title: string) {
|
||||
await coreTabbarStore.setTabTitle(route, title);
|
||||
}
|
||||
|
||||
async function resetTabTitle() {
|
||||
await coreTabbarStore.resetTabTitle(route);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作是否禁用
|
||||
* @param tab
|
||||
*/
|
||||
function getTabDisableState(tab: RouteLocationNormalized = route) {
|
||||
const tabs = coreTabbarStore.getTabs;
|
||||
const affixTabs = coreTabbarStore.affixTabs;
|
||||
const index = tabs.findIndex((item) => item.path === tab.path);
|
||||
|
||||
const disabled = tabs.length <= 1;
|
||||
|
||||
const { meta } = tab;
|
||||
const affixTab = meta?.affixTab ?? false;
|
||||
const isCurrentTab = route.path === tab.path;
|
||||
|
||||
// 当前处于最左侧或者减去固定标签页的数量等于0
|
||||
const disabledCloseLeft =
|
||||
index === 0 || index - affixTabs.length <= 0 || !isCurrentTab;
|
||||
|
||||
const disabledCloseRight = !isCurrentTab || index === tabs.length - 1;
|
||||
|
||||
const disabledCloseOther =
|
||||
disabled || !isCurrentTab || tabs.length - affixTabs.length <= 1;
|
||||
return {
|
||||
disabledCloseAll: disabled,
|
||||
disabledCloseCurrent: !!affixTab || disabled,
|
||||
disabledCloseLeft,
|
||||
disabledCloseOther,
|
||||
disabledCloseRight,
|
||||
disabledRefresh: !isCurrentTab,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
closeAllTabs,
|
||||
closeCurrentTab,
|
||||
closeLeftTabs,
|
||||
closeOtherTabs,
|
||||
closeRightTabs,
|
||||
closeTabByKey,
|
||||
getTabDisableState,
|
||||
openTabInNewWindow,
|
||||
pinTab,
|
||||
refreshTab,
|
||||
resetTabTitle,
|
||||
setTabTitle,
|
||||
toggleTabPin,
|
||||
unpinTab,
|
||||
};
|
||||
}
|
@@ -13,6 +13,7 @@
|
||||
"workspace": "Workspace"
|
||||
},
|
||||
"vben": {
|
||||
"title": "Project",
|
||||
"about": "About",
|
||||
"document": "Document"
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@
|
||||
"workspace": "工作台"
|
||||
},
|
||||
"vben": {
|
||||
"title": "项目",
|
||||
"about": "关于",
|
||||
"document": "文档"
|
||||
}
|
||||
@@ -123,7 +124,7 @@
|
||||
"sendCode": "获取验证码",
|
||||
"sendText": "{0}秒后重新获取",
|
||||
"thirdPartyLogin": "其他登录方式",
|
||||
"loginAgainTitle": "请重新登录",
|
||||
"loginAgainTitle": "重新登录",
|
||||
"loginAgainSubTitle": "您的登录状态已过期,请重新登录以继续。",
|
||||
"layout": {
|
||||
"center": "居中",
|
||||
|
@@ -83,19 +83,20 @@
|
||||
@apply m-0 appearance-none;
|
||||
}
|
||||
|
||||
/* 考虑只在mac下打开 */
|
||||
/* 只有非mac下才进行调整,mac下使用默认滚动条 */
|
||||
html:not([data-platform='macOs']) {
|
||||
*::-webkit-scrollbar {
|
||||
@apply h-[1px] w-[10px];
|
||||
}
|
||||
|
||||
/* *::-webkit-scrollbar {
|
||||
@apply h-[1px] w-[10px];
|
||||
*::-webkit-scrollbar-thumb {
|
||||
@apply bg-border rounded-sm border-none;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
@apply rounded-sm border-none bg-transparent shadow-none;
|
||||
}
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
@apply bg-border rounded-sm border-none;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
@apply rounded-sm border-none bg-transparent shadow-none;
|
||||
} */
|
||||
}
|
||||
|
||||
@layer components {
|
||||
|
@@ -54,7 +54,7 @@ function handleScroll(event: Event) {
|
||||
v-if="shadow"
|
||||
:class="{
|
||||
'opacity-100': !isAtTop && !isAtBottom,
|
||||
'border-border border-t': shadowBorder && !isAtTop && !isAtBottom,
|
||||
'border-border border-b': shadowBorder && !isAtTop && !isAtBottom,
|
||||
}"
|
||||
class="scrollbar-bottom-shadow pointer-events-none absolute bottom-0 z-10 h-12 w-full opacity-0 transition-opacity duration-300 ease-in-out will-change-[opacity]"
|
||||
></div>
|
||||
|
@@ -71,7 +71,7 @@ function scrollIntoView() {
|
||||
|
||||
<template>
|
||||
<div :style="style" class="tabs-chrome size-full flex-1 overflow-hidden pt-1">
|
||||
<VbenScrollbar class="h-full" horizontal>
|
||||
<VbenScrollbar class="tabs-chrome__scrollbar h-full" horizontal>
|
||||
<!-- footer -> 4px -->
|
||||
<div
|
||||
ref="contentRef"
|
||||
@@ -237,6 +237,7 @@ function scrollIntoView() {
|
||||
}
|
||||
}
|
||||
|
||||
&__scrollbar,
|
||||
&__label {
|
||||
mask-image: linear-gradient(
|
||||
90deg,
|
||||
|
@@ -72,7 +72,7 @@ function scrollIntoView() {
|
||||
|
||||
<template>
|
||||
<div class="h-full flex-1 overflow-hidden">
|
||||
<VbenScrollbar class="h-full" horizontal>
|
||||
<VbenScrollbar class="tabs-scrollbar h-full" horizontal>
|
||||
<div
|
||||
:class="contentClass"
|
||||
class="relative !flex h-full w-max items-center"
|
||||
@@ -147,3 +147,14 @@ function scrollIntoView() {
|
||||
</VbenScrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tabs-scrollbar {
|
||||
mask-image: linear-gradient(
|
||||
90deg,
|
||||
#000 0%,
|
||||
#000 calc(100% - 16px),
|
||||
transparent
|
||||
);
|
||||
}
|
||||
</style>
|
||||
|
@@ -11,7 +11,7 @@ interface Props {
|
||||
* Specified codes is visible
|
||||
* @default []
|
||||
*/
|
||||
permissions?: string[];
|
||||
codes?: string[];
|
||||
|
||||
/**
|
||||
* 通过什么方式来控制组件,如果是 role,则传入角色,如果是 code,则传入权限码
|
||||
@@ -25,21 +25,19 @@ defineOptions({
|
||||
});
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
permissions: () => [],
|
||||
codes: () => [],
|
||||
type: 'role',
|
||||
});
|
||||
|
||||
const { hasAccessByCodes, hasAccessByRoles } = useAccess();
|
||||
|
||||
const hasAuth = computed(() => {
|
||||
const { permissions, type } = props;
|
||||
return type === 'role'
|
||||
? hasAccessByRoles(permissions)
|
||||
: hasAccessByCodes(permissions);
|
||||
const { codes, type } = props;
|
||||
return type === 'role' ? hasAccessByRoles(codes) : hasAccessByCodes(codes);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot v-if="!permissions"></slot>
|
||||
<slot v-if="!codes"></slot>
|
||||
<slot v-else-if="hasAuth"></slot>
|
||||
</template>
|
||||
|
@@ -20,7 +20,7 @@ const { getCachedTabs, getExcludeCachedTabs, renderRouteView } =
|
||||
storeToRefs(tabbarStore);
|
||||
|
||||
// 页面切换动画
|
||||
function getTransitionName(route: RouteLocationNormalizedLoaded) {
|
||||
function getTransitionName(_route: RouteLocationNormalizedLoaded) {
|
||||
// 如果偏好设置未设置,则不使用动画
|
||||
const { tabbar, transition } = preferences;
|
||||
const transitionName = transition.name;
|
||||
@@ -38,9 +38,10 @@ function getTransitionName(route: RouteLocationNormalizedLoaded) {
|
||||
// return;
|
||||
// }
|
||||
// 已经打开且已经加载过的页面不使用动画
|
||||
const inTabs = getCachedTabs.value.includes(route.name as string);
|
||||
// const inTabs = getCachedTabs.value.includes(route.name as string);
|
||||
|
||||
return inTabs && route.meta.loaded ? undefined : transitionName;
|
||||
// return inTabs && route.meta.loaded ? undefined : transitionName;
|
||||
return transitionName;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@@ -1,2 +1,2 @@
|
||||
export { default as LayoutTabbar } from './tabbar.vue';
|
||||
export * from './use-tabs';
|
||||
export * from './use-tabbar';
|
||||
|
@@ -2,11 +2,12 @@
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { useContentMaximize, useTabs } from '@vben-core/hooks';
|
||||
import { preferences } from '@vben-core/preferences';
|
||||
import { useCoreTabbarStore } from '@vben-core/stores';
|
||||
import { TabsToolMore, TabsToolScreen, TabsView } from '@vben-core/tabs-ui';
|
||||
|
||||
import { updateContentScreen, useTabs } from './use-tabs';
|
||||
import { useTabbar } from './use-tabbar';
|
||||
|
||||
defineOptions({
|
||||
name: 'LayoutTabbar',
|
||||
@@ -14,9 +15,10 @@ defineOptions({
|
||||
|
||||
defineProps<{ showIcon?: boolean; theme?: string }>();
|
||||
|
||||
const coreTabbarStore = useCoreTabbarStore();
|
||||
|
||||
const route = useRoute();
|
||||
const coreTabbarStore = useCoreTabbarStore();
|
||||
const { toggleMaximize } = useContentMaximize();
|
||||
const { unpinTab } = useTabs();
|
||||
|
||||
const {
|
||||
createContextMenus,
|
||||
@@ -24,8 +26,7 @@ const {
|
||||
currentTabs,
|
||||
handleClick,
|
||||
handleClose,
|
||||
handleUnpinTab,
|
||||
} = useTabs();
|
||||
} = useTabbar();
|
||||
|
||||
const menus = computed(() => {
|
||||
return createContextMenus(route);
|
||||
@@ -48,15 +49,15 @@ if (!preferences.tabbar.persist) {
|
||||
:tabs="currentTabs"
|
||||
@close="handleClose"
|
||||
@sort-tabs="coreTabbarStore.sortTabs"
|
||||
@unpin="handleUnpinTab"
|
||||
@unpin="unpinTab"
|
||||
@update:active="handleClick"
|
||||
/>
|
||||
<div class="flex-center h-full">
|
||||
<TabsToolMore :menus="menus" />
|
||||
<TabsToolScreen
|
||||
:screen="preferences.sidebar.hidden"
|
||||
@change="updateContentScreen"
|
||||
@update:screen="updateContentScreen"
|
||||
@change="toggleMaximize"
|
||||
@update:screen="toggleMaximize"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -8,6 +8,7 @@ import type {
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { useContentMaximize, useTabs } from '@vben-core/hooks';
|
||||
import {
|
||||
ArrowLeftToLine,
|
||||
ArrowRightLeft,
|
||||
@@ -22,33 +23,32 @@ import {
|
||||
X,
|
||||
} from '@vben-core/icons';
|
||||
import { $t, useI18n } from '@vben-core/locales';
|
||||
import { updatePreferences, usePreferences } from '@vben-core/preferences';
|
||||
import {
|
||||
storeToRefs,
|
||||
useCoreAccessStore,
|
||||
useCoreTabbarStore,
|
||||
} from '@vben-core/stores';
|
||||
import { filterTree, openWindow } from '@vben-core/toolkit';
|
||||
import { filterTree } from '@vben-core/toolkit';
|
||||
|
||||
function updateContentScreen(screen: boolean) {
|
||||
updatePreferences({
|
||||
header: {
|
||||
hidden: !!screen,
|
||||
},
|
||||
sidebar: {
|
||||
hidden: !!screen,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function useTabs() {
|
||||
export function useTabbar() {
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const accessStore = useCoreAccessStore();
|
||||
const { contentIsMaximize } = usePreferences();
|
||||
const coreTabbarStore = useCoreTabbarStore();
|
||||
const { accessMenus } = storeToRefs(accessStore);
|
||||
|
||||
const { contentIsMaximize, toggleMaximize } = useContentMaximize();
|
||||
const {
|
||||
closeAllTabs,
|
||||
closeCurrentTab,
|
||||
closeLeftTabs,
|
||||
closeOtherTabs,
|
||||
closeRightTabs,
|
||||
closeTabByKey,
|
||||
getTabDisableState,
|
||||
openTabInNewWindow,
|
||||
refreshTab,
|
||||
toggleTabPin,
|
||||
} = useTabs();
|
||||
const currentActive = computed(() => {
|
||||
return route.path;
|
||||
});
|
||||
@@ -76,7 +76,7 @@ function useTabs() {
|
||||
|
||||
// 关闭tab
|
||||
const handleClose = async (key: string) => {
|
||||
await coreTabbarStore.closeTabByKey(key, router);
|
||||
await closeTabByKey(key);
|
||||
};
|
||||
|
||||
function wrapperTabLocale(tab: RouteLocationNormalizedGeneric) {
|
||||
@@ -106,30 +106,22 @@ function useTabs() {
|
||||
);
|
||||
|
||||
const createContextMenus = (tab: TabDefinition) => {
|
||||
const tabs = coreTabbarStore.getTabs;
|
||||
const affixTabs = coreTabbarStore.affixTabs;
|
||||
const index = tabs.findIndex((item) => item.path === tab.path);
|
||||
const {
|
||||
disabledCloseAll,
|
||||
disabledCloseCurrent,
|
||||
disabledCloseLeft,
|
||||
disabledCloseOther,
|
||||
disabledCloseRight,
|
||||
disabledRefresh,
|
||||
} = getTabDisableState(tab);
|
||||
|
||||
const disabled = tabs.length <= 1;
|
||||
|
||||
const { meta } = tab;
|
||||
const affixTab = meta?.affixTab ?? false;
|
||||
const isCurrentTab = route.path === tab.path;
|
||||
|
||||
// 当前处于最左侧或者减去固定标签页的数量等于0
|
||||
const closeLeftDisabled =
|
||||
index === 0 || index - affixTabs.length <= 0 || !isCurrentTab;
|
||||
|
||||
const closeRightDisabled = !isCurrentTab || index === tabs.length - 1;
|
||||
|
||||
const closeOtherDisabled =
|
||||
disabled || !isCurrentTab || tabs.length - affixTabs.length <= 1;
|
||||
const affixTab = tab?.meta?.affixTab ?? false;
|
||||
|
||||
const menus: IContextMenuItem[] = [
|
||||
{
|
||||
disabled: !!affixTab || disabled,
|
||||
disabled: disabledCloseCurrent,
|
||||
handler: async () => {
|
||||
await coreTabbarStore.closeTab(tab, router);
|
||||
await closeCurrentTab(tab);
|
||||
},
|
||||
icon: X,
|
||||
key: 'close',
|
||||
@@ -137,9 +129,7 @@ function useTabs() {
|
||||
},
|
||||
{
|
||||
handler: async () => {
|
||||
await (affixTab
|
||||
? coreTabbarStore.unpinTab(tab)
|
||||
: coreTabbarStore.pinTab(tab));
|
||||
await toggleTabPin(tab);
|
||||
},
|
||||
icon: affixTab ? MdiPinOff : MdiPin,
|
||||
key: 'affix',
|
||||
@@ -152,7 +142,7 @@ function useTabs() {
|
||||
if (!contentIsMaximize.value) {
|
||||
await router.push(tab.fullPath);
|
||||
}
|
||||
updateContentScreen(!contentIsMaximize.value);
|
||||
toggleMaximize();
|
||||
},
|
||||
icon: contentIsMaximize.value ? Minimize2 : Fullscreen,
|
||||
key: contentIsMaximize.value ? 'restore-maximize' : 'maximize',
|
||||
@@ -161,22 +151,15 @@ function useTabs() {
|
||||
: $t('preferences.tabbar.contextMenu.maximize'),
|
||||
},
|
||||
{
|
||||
disabled: !isCurrentTab,
|
||||
handler: async () => {
|
||||
await coreTabbarStore.refresh(router);
|
||||
},
|
||||
disabled: disabledRefresh,
|
||||
handler: refreshTab,
|
||||
icon: RotateCw,
|
||||
key: 'reload',
|
||||
text: $t('preferences.tabbar.contextMenu.reload'),
|
||||
},
|
||||
|
||||
{
|
||||
handler: async () => {
|
||||
const { hash, origin } = location;
|
||||
const path = tab.fullPath;
|
||||
const fullPath = path.startsWith('/') ? path : `/${path}`;
|
||||
const url = `${origin}${hash ? '/#' : ''}${fullPath}`;
|
||||
openWindow(url, { target: '_blank' });
|
||||
await openTabInNewWindow(tab);
|
||||
},
|
||||
icon: ExternalLink,
|
||||
key: 'open-in-new-window',
|
||||
@@ -185,18 +168,18 @@ function useTabs() {
|
||||
},
|
||||
|
||||
{
|
||||
disabled: closeLeftDisabled,
|
||||
disabled: disabledCloseLeft,
|
||||
handler: async () => {
|
||||
await coreTabbarStore.closeLeftTabs(tab);
|
||||
await closeLeftTabs(tab);
|
||||
},
|
||||
icon: ArrowLeftToLine,
|
||||
key: 'close-left',
|
||||
text: $t('preferences.tabbar.contextMenu.closeLeft'),
|
||||
},
|
||||
{
|
||||
disabled: closeRightDisabled,
|
||||
disabled: disabledCloseRight,
|
||||
handler: async () => {
|
||||
await coreTabbarStore.closeRightTabs(tab);
|
||||
await closeRightTabs(tab);
|
||||
},
|
||||
icon: ArrowRightToLine,
|
||||
key: 'close-right',
|
||||
@@ -204,19 +187,17 @@ function useTabs() {
|
||||
text: $t('preferences.tabbar.contextMenu.closeRight'),
|
||||
},
|
||||
{
|
||||
disabled: closeOtherDisabled,
|
||||
disabled: disabledCloseOther,
|
||||
handler: async () => {
|
||||
await coreTabbarStore.closeOtherTabs(tab);
|
||||
await closeOtherTabs(tab);
|
||||
},
|
||||
icon: FoldHorizontal,
|
||||
key: 'close-other',
|
||||
text: $t('preferences.tabbar.contextMenu.closeOther'),
|
||||
},
|
||||
{
|
||||
disabled,
|
||||
handler: async () => {
|
||||
await coreTabbarStore.closeAllTabs(router);
|
||||
},
|
||||
disabled: disabledCloseAll,
|
||||
handler: closeAllTabs,
|
||||
icon: ArrowRightLeft,
|
||||
key: 'close-all',
|
||||
text: $t('preferences.tabbar.contextMenu.closeAll'),
|
||||
@@ -225,21 +206,11 @@ function useTabs() {
|
||||
return menus;
|
||||
};
|
||||
|
||||
/**
|
||||
* 取消固定标签页
|
||||
*/
|
||||
const handleUnpinTab = async (tab: TabDefinition) => {
|
||||
await coreTabbarStore.unpinTab(tab);
|
||||
};
|
||||
|
||||
return {
|
||||
createContextMenus,
|
||||
currentActive,
|
||||
currentTabs,
|
||||
handleClick,
|
||||
handleClose,
|
||||
handleUnpinTab,
|
||||
};
|
||||
}
|
||||
|
||||
export { updateContentScreen, useTabs };
|
Reference in New Issue
Block a user