feat(projects): support pinning and unpinning of tabs

This commit is contained in:
hooke
2025-12-03 07:25:46 -07:00
committed by Soybean
parent 605173a1cc
commit b8a767d704
6 changed files with 88 additions and 4 deletions

View File

@@ -26,7 +26,7 @@ const props = withDefaults(defineProps<Props>(), {
const visible = defineModel<boolean>('visible');
const { removeTab, clearTabs, clearLeftTabs, clearRightTabs } = useTabStore();
const { removeTab, clearTabs, clearLeftTabs, clearRightTabs, fixTab, unfixTab, isTabRetain } = useTabStore();
const { SvgIconVNode } = useSvgIcon();
type DropdownOption = {
@@ -64,6 +64,23 @@ const options = computed(() => {
icon: SvgIconVNode({ icon: 'ant-design:line-outlined', fontSize: 18 })
}
];
if (props.tabId !== '/home') {
if (isTabRetain(props.tabId)) {
opts.push({
key: 'unpin',
label: $t('dropdown.unpin'),
icon: SvgIconVNode({ icon: 'mdi:pin-off-outline', fontSize: 18 })
});
} else {
opts.push({
key: 'pin',
label: $t('dropdown.pin'),
icon: SvgIconVNode({ icon: 'mdi:pin-outline', fontSize: 18 })
});
}
}
const { excludeKeys, disabledKeys } = props;
const result = opts.filter(opt => !excludeKeys.includes(opt.key));
@@ -98,6 +115,12 @@ const dropdownAction: Record<App.Global.DropdownKey, () => void> = {
},
closeAll() {
clearTabs();
},
pin() {
fixTab(props.tabId);
},
unpin() {
unfixTab(props.tabId);
}
};

View File

@@ -336,7 +336,9 @@ const local: App.I18n.Schema = {
closeOther: 'Close Other',
closeLeft: 'Close Left',
closeRight: 'Close Right',
closeAll: 'Close All'
closeAll: 'Close All',
pin: 'Pin Tab',
unpin: 'Unpin Tab'
},
icon: {
themeConfig: 'Theme Configuration',

View File

@@ -333,7 +333,9 @@ const local: App.I18n.Schema = {
closeOther: '关闭其它',
closeLeft: '关闭左侧',
closeRight: '关闭右侧',
closeAll: '关闭所有'
closeAll: '关闭所有',
pin: '固定标签',
unpin: '取消固定'
},
icon: {
themeConfig: '主题配置',

View File

@@ -18,6 +18,7 @@ import {
getTabByRoute,
getTabIdByRoute,
isTabInTabs,
reorderFixedTabs,
updateTabByI18nKey,
updateTabsByI18nKey
} from './shared';
@@ -248,6 +249,48 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
await clearTabs(excludes);
}
/**
* Fix tab
*
* @param tabId
*/
function fixTab(tabId: string) {
const tabIndex = tabs.value.findIndex(t => t.id === tabId);
if (tabIndex === -1) return;
const tab = tabs.value[tabIndex];
const fixedCount = getFixedTabIds(tabs.value).length;
tab.fixedIndex = fixedCount;
if (tabIndex !== fixedCount) {
tabs.value.splice(tabIndex, 1);
tabs.value.splice(fixedCount, 0, tab);
}
reorderFixedTabs(tabs.value);
}
/**
* Unfix tab
*
* @param tabId
*/
function unfixTab(tabId: string) {
const tabIndex = tabs.value.findIndex(t => t.id === tabId);
if (tabIndex === -1) return;
const tab = tabs.value[tabIndex];
tab.fixedIndex = undefined;
const fixedCount = getFixedTabIds(tabs.value).length;
if (tabIndex !== fixedCount) {
tabs.value.splice(tabIndex, 1);
tabs.value.splice(fixedCount, 0, tab);
}
reorderFixedTabs(tabs.value);
}
/**
* Set new label of tab
*
@@ -328,6 +371,8 @@ export const useTabStore = defineStore(SetupStoreId.Tab, () => {
clearTabs,
clearLeftTabs,
clearRightTabs,
fixTab,
unfixTab,
switchRouteByTab,
setTabLabel,
resetTabLabel,

View File

@@ -198,6 +198,18 @@ export function getFixedTabIds(tabs: App.Global.Tab[]) {
return fixedTabs.map(tab => tab.id);
}
/**
* Reorder fixed tabs fixedIndex
*
* @param tabs
*/
export function reorderFixedTabs(tabs: App.Global.Tab[]) {
const fixedTabs = getFixedTabs(tabs);
fixedTabs.forEach((t, i) => {
t.fixedIndex = i;
});
}
/**
* Update tabs label
*

View File

@@ -282,7 +282,7 @@ declare namespace App {
type FormRule = import('naive-ui').FormItemRule;
/** The global dropdown key */
type DropdownKey = 'closeCurrent' | 'closeOther' | 'closeLeft' | 'closeRight' | 'closeAll';
type DropdownKey = 'closeCurrent' | 'closeOther' | 'closeLeft' | 'closeRight' | 'closeAll' | 'pin' | 'unpin';
}
/**