front/src/store/modules/tab-bar/index.ts

154 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { RouteLocationNormalized } from 'vue-router';
import { defineStore } from 'pinia';
import {
DEFAULT_ROUTE,
DEFAULT_ROUTE_NAME,
REDIRECT_ROUTE_NAME,
} from '@/router/constants';
import { isString } from '@/utils/is';
import { useRbacUserStore } from '@/store/modules/rbac-user';
import { buildMenuTitleMaps, resolveMenuTitleByPath, resolveMenuTitleFromTree } from '@/utils/menu-display';
import type { MenuTreeDto } from '@/api/rbac';
import { TabBarState, TagProps } from './types';
/** keep-alive 同时缓存的页面上限(LRU),避免页签开太多越切越顿 */
export const MAX_KEEP_ALIVE_TABS = 10;
function resolveRouteTabTitle(route: RouteLocationNormalized): string {
const rbacUserStore = useRbacUserStore();
const { pathToTitle } = buildMenuTitleMaps(rbacUserStore.menus as MenuTreeDto[]);
const menuTitle = resolveMenuTitleByPath(route.path, pathToTitle);
if (menuTitle) {
return menuTitle;
}
const metaTitle = route.meta?.title;
if (typeof metaTitle === 'string' && metaTitle.trim()) {
return metaTitle.trim();
}
return String(route.name || route.path || '');
}
const formatTag = (route: RouteLocationNormalized): TagProps => {
const { name, meta, fullPath, query } = route;
return {
title: resolveRouteTabTitle(route),
name: String(name),
fullPath,
query,
ignoreCache: meta.ignoreCache,
};
};
const BAN_LIST = [REDIRECT_ROUTE_NAME];
const useTabBarStore = defineStore('tabBar', {
state: (): TabBarState => ({
cacheTabList: new Set([DEFAULT_ROUTE_NAME]),
tagList: [DEFAULT_ROUTE],
}),
getters: {
getTabList(): TagProps[] {
return this.tagList;
},
getCacheList(): string[] {
return Array.from(this.cacheTabList);
},
},
actions: {
/** 超出上限时淘汰最久未访问的缓存(保留首页) */
pruneCache(exceptName?: string) {
while (this.cacheTabList.size > MAX_KEEP_ALIVE_TABS) {
let removed = false;
for (const name of this.cacheTabList) {
if (name === DEFAULT_ROUTE_NAME || name === exceptName) {
continue;
}
this.cacheTabList.delete(name);
removed = true;
break;
}
if (!removed) break;
}
},
/** LRU:访问时挪到末尾,并裁剪超量缓存 */
touchCache(name: string) {
if (!isString(name) || name === '') return;
if (this.cacheTabList.has(name)) {
this.cacheTabList.delete(name);
}
this.cacheTabList.add(name);
this.pruneCache(name);
},
updateTabList(route: RouteLocationNormalized) {
if (BAN_LIST.includes(route.name as string)) return;
this.tagList.push(formatTag(route));
if (!route.meta.ignoreCache) {
this.touchCache(String(route.name));
}
},
deleteTag(idx: number, tag: TagProps) {
this.tagList.splice(idx, 1);
this.cacheTabList.delete(tag.name);
},
addCache(name: string) {
if (isString(name) && name !== '') {
this.touchCache(name);
}
},
deleteCache(tag: TagProps) {
this.cacheTabList.delete(tag.name);
},
freshTabList(tags: TagProps[]) {
this.tagList = tags;
this.cacheTabList.clear();
this.cacheTabList.add(DEFAULT_ROUTE_NAME);
this.tagList
.filter((el) => !el.ignoreCache)
.map((el) => el.name)
.forEach((x) => {
if (x && x !== DEFAULT_ROUTE_NAME) {
this.cacheTabList.add(x);
}
});
this.pruneCache();
},
resetTabList() {
this.tagList = [DEFAULT_ROUTE];
this.cacheTabList.clear();
this.cacheTabList.add(DEFAULT_ROUTE_NAME);
},
delVisitedRoute(path: string) {
const idx = this.tagList.findIndex(tag => tag.fullPath === path);
if (idx !== -1) {
const tag = this.tagList[idx];
this.deleteTag(idx, tag);
}
},
/** 拖动排序标签:将 fromIndex 位置的标签移动到 toIndex */
reorderTabs(fromIndex: number, toIndex: number) {
if (fromIndex === toIndex || fromIndex < 0 || toIndex < 0) return;
const list = [...this.tagList];
const [item] = list.splice(fromIndex, 1);
list.splice(toIndex, 0, item);
this.freshTabList(list);
},
/** 菜单加载后刷新标签标题(使用 RBAC 菜单名称) */
refreshTagTitles() {
const rbacUserStore = useRbacUserStore();
const menus = rbacUserStore.menus as MenuTreeDto[];
this.tagList = this.tagList.map((tag) => ({
...tag,
title: resolveMenuTitleFromTree(menus, tag.fullPath, tag.title) || tag.title,
}));
},
},
});
export default useTabBarStore;