mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-12-04 15:26:07 +08:00
feat(projects): support pinning and unpinning of tabs
This commit is contained in:
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -333,7 +333,9 @@ const local: App.I18n.Schema = {
|
||||
closeOther: '关闭其它',
|
||||
closeLeft: '关闭左侧',
|
||||
closeRight: '关闭右侧',
|
||||
closeAll: '关闭所有'
|
||||
closeAll: '关闭所有',
|
||||
pin: '固定标签',
|
||||
unpin: '取消固定'
|
||||
},
|
||||
icon: {
|
||||
themeConfig: '主题配置',
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
2
src/typings/app.d.ts
vendored
2
src/typings/app.d.ts
vendored
@@ -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';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user