|
|
@@ -1,4 +1,5 @@
|
|
|
<script setup lang="ts">
|
|
|
+import type { IContextMenuItem } from '@velofex-core/tabs-ui';
|
|
|
import type { MenuProps } from 'antdv-next';
|
|
|
|
|
|
import { computed, h, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
|
|
@@ -431,6 +432,101 @@ function handleSortTabs(oldIndex: number, newIndex: number) {
|
|
|
iframeTabs.value = nextTabs;
|
|
|
}
|
|
|
|
|
|
+function addTabRefreshStamp(url: string) {
|
|
|
+ const rawUrl = String(url ?? '');
|
|
|
+ if (!rawUrl) {
|
|
|
+ return rawUrl;
|
|
|
+ }
|
|
|
+
|
|
|
+ const hashIndex = rawUrl.indexOf('#');
|
|
|
+ const pathAndQuery = hashIndex === -1 ? rawUrl : rawUrl.slice(0, hashIndex);
|
|
|
+ const hashPart = hashIndex === -1 ? '' : rawUrl.slice(hashIndex);
|
|
|
+
|
|
|
+ const [pathPart, queryPart = ''] = pathAndQuery.split('?');
|
|
|
+ const params = new URLSearchParams(queryPart);
|
|
|
+ params.set('__tab_refresh', String(Date.now()));
|
|
|
+ const nextQuery = params.toString();
|
|
|
+
|
|
|
+ return `${pathPart}${nextQuery ? `?${nextQuery}` : ''}${hashPart}`;
|
|
|
+}
|
|
|
+
|
|
|
+function refreshTabByKey(key: string) {
|
|
|
+ const normalizedKey = String(key);
|
|
|
+ const index = iframeTabs.value.findIndex((tab) => tab.key === normalizedKey);
|
|
|
+ if (index === -1) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const targetTab = iframeTabs.value[index];
|
|
|
+ if (!targetTab) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ iframeTabs.value.splice(index, 1, {
|
|
|
+ ...targetTab,
|
|
|
+ iframeSrc: addTabRefreshStamp(targetTab.iframeSrc),
|
|
|
+ });
|
|
|
+ setActiveTab(normalizedKey);
|
|
|
+}
|
|
|
+
|
|
|
+function closeOtherTabsByKey(key: string) {
|
|
|
+ const normalizedKey = String(key);
|
|
|
+ const targetTab = iframeTabs.value.find((tab) => tab.key === normalizedKey);
|
|
|
+ if (!targetTab) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ iframeTabs.value = [targetTab];
|
|
|
+ setActiveTab(normalizedKey);
|
|
|
+}
|
|
|
+
|
|
|
+function closeAllTabs() {
|
|
|
+ iframeTabs.value = [];
|
|
|
+ clearActiveTab();
|
|
|
+}
|
|
|
+
|
|
|
+function createTabContextMenus(tab: { key?: string }) {
|
|
|
+ const tabKey = String(tab?.key ?? '');
|
|
|
+ const hasTabs = iframeTabs.value.length > 0;
|
|
|
+ const isCurrentTabClosable =
|
|
|
+ hasTabs && tabKey && iframeTabs.value.some((item) => item.key === tabKey);
|
|
|
+
|
|
|
+ const menus: IContextMenuItem[] = [
|
|
|
+ {
|
|
|
+ disabled: !isCurrentTabClosable,
|
|
|
+ handler: () => {
|
|
|
+ refreshTabByKey(tabKey);
|
|
|
+ },
|
|
|
+ key: 'refresh-current',
|
|
|
+ text: '刷新当前',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ disabled: !isCurrentTabClosable,
|
|
|
+ handler: () => {
|
|
|
+ handleTabClose(tabKey);
|
|
|
+ },
|
|
|
+ key: 'close-current',
|
|
|
+ text: '关闭当前',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ disabled: !isCurrentTabClosable || iframeTabs.value.length <= 1,
|
|
|
+ handler: () => {
|
|
|
+ closeOtherTabsByKey(tabKey);
|
|
|
+ },
|
|
|
+ key: 'close-other',
|
|
|
+ text: '关闭其他',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ disabled: !hasTabs,
|
|
|
+ handler: closeAllTabs,
|
|
|
+ key: 'close-all',
|
|
|
+ text: '关闭全部',
|
|
|
+ },
|
|
|
+ ];
|
|
|
+
|
|
|
+ return menus;
|
|
|
+}
|
|
|
+
|
|
|
function openMobileSidebar() {
|
|
|
isMobileSidebarOpen.value = true;
|
|
|
}
|
|
|
@@ -536,6 +632,7 @@ function handleUserMenuClick({ key }: { key: string }) {
|
|
|
<div class="content-tabs-wrap">
|
|
|
<TabsView
|
|
|
:active="activeTabKey"
|
|
|
+ :context-menus="createTabContextMenus"
|
|
|
:draggable="preferences.tabbar.draggable"
|
|
|
:show-icon="false"
|
|
|
:style-type="preferences.tabbar.styleType"
|