admin-vben/src/store/modules/multipleTab.ts

356 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import type { RouteLocationNormalized, RouteLocationRaw, Router } from 'vue-router'
import { toRaw, unref } from 'vue'
import { defineStore } from 'pinia'
import { store } from '@/store'
import { useGo, useRedo } from '@/hooks/web/usePage'
import { Persistent } from '@/utils/cache/persistent'
import { PageEnum } from '@/enums/pageEnum'
import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '@/router/routes/basic'
import { getRawRoute } from '@/utils'
import { MULTIPLE_TABS_KEY } from '@/enums/cacheEnum'
import projectSetting from '@/settings/projectSetting'
export interface MultipleTabState {
cacheTabList: Set<string>
tabList: RouteLocationNormalized[]
lastDragEndIndex: number
}
function handleGotoPage(router: Router) {
const go = useGo(router)
go(unref(router.currentRoute).path, true)
}
const getToTarget = (tabItem: RouteLocationNormalized) => {
const { params, path, query } = tabItem
return {
params: params || {},
path,
query: query || {}
}
}
const cacheTab = projectSetting.multiTabsSetting.cache
export const useMultipleTabStore = defineStore('app-multiple-tab', {
state: (): MultipleTabState => ({
// Tabs that need to be cached
cacheTabList: new Set(),
// multiple tab list
tabList: cacheTab ? Persistent.getLocal(MULTIPLE_TABS_KEY) || [] : [],
// Index of the last moved tab
lastDragEndIndex: 0
}),
getters: {
getTabList(state): RouteLocationNormalized[] {
return state.tabList
},
getCachedTabList(state): string[] {
return Array.from(state.cacheTabList)
},
getLastDragEndIndex(state): number {
return state.lastDragEndIndex
}
},
actions: {
/**
* Update the cache according to the currently opened tabs
*/
async updateCacheTab() {
const cacheMap: Set<string> = new Set()
for (const tab of this.tabList) {
const item = getRawRoute(tab)
// Ignore the cache
const needCache = !item.meta?.ignoreKeepAlive
if (!needCache) {
continue
}
const name = item.name as string
cacheMap.add(name)
}
this.cacheTabList = cacheMap
},
/**
* Refresh tabs
*/
async refreshPage(router: Router) {
const { currentRoute } = router
const route = unref(currentRoute)
const name = route.name
const findTab = this.getCachedTabList.find((item) => item === name)
if (findTab) {
this.cacheTabList.delete(findTab)
}
const redo = useRedo(router)
await redo()
},
clearCacheTabs(): void {
this.cacheTabList = new Set()
},
resetState(): void {
this.tabList = []
this.clearCacheTabs()
},
goToPage(router: Router) {
const go = useGo(router)
const len = this.tabList.length
const { path } = unref(router.currentRoute)
let toPath: PageEnum | string = PageEnum.BASE_HOME
if (len > 0) {
const page = this.tabList[len - 1]
const p = page.fullPath || page.path
if (p) {
toPath = p
}
}
// Jump to the current page and report an error
path !== toPath && go(toPath as PageEnum, true)
},
async addTab(route: RouteLocationNormalized) {
const { path, name, fullPath, params, query, meta } = getRawRoute(route)
// 404 The page does not need to add a tab
if (
path === PageEnum.ERROR_PAGE ||
path === PageEnum.BASE_LOGIN ||
!name ||
[REDIRECT_ROUTE.name, PAGE_NOT_FOUND_ROUTE.name].includes(name as string)
) {
return
}
let updateIndex = -1
// Existing pages, do not add tabs repeatedly
const tabHasExits = this.tabList.some((tab, index) => {
updateIndex = index
return (tab.fullPath || tab.path) === (fullPath || path)
})
// If the tab already exists, perform the update operation
if (tabHasExits) {
const curTab = toRaw(this.tabList)[updateIndex]
if (!curTab) {
return
}
curTab.params = params || curTab.params
curTab.query = query || curTab.query
curTab.fullPath = fullPath || curTab.fullPath
this.tabList.splice(updateIndex, 1, curTab)
} else {
// Add tab
// 获取动态路由打开数,超过 0 即代表需要控制打开数
const dynamicLevel = meta?.dynamicLevel ?? -1
if (dynamicLevel > 0) {
// 如果动态路由层级大于 0 了,那么就要限制该路由的打开数限制了
// 首先获取到真实的路由,使用配置方式减少计算开销.
// const realName: string = path.match(/(\S*)\//)![1];
const realPath = meta?.realPath ?? ''
// 获取到已经打开的动态路由数, 判断是否大于某一个值
if (this.tabList.filter((e) => e.meta?.realPath ?? '' === realPath).length >= dynamicLevel) {
// 关闭第一个
const index = this.tabList.findIndex((item) => item.meta.realPath === realPath)
index !== -1 && this.tabList.splice(index, 1)
}
}
this.tabList.push(route)
}
this.updateCacheTab()
cacheTab && Persistent.setLocal(MULTIPLE_TABS_KEY, this.tabList)
},
async closeTab(tab: RouteLocationNormalized, router: Router) {
const close = (route: RouteLocationNormalized) => {
const { fullPath, meta: { affix } = {} } = route
if (affix) {
return
}
const index = this.tabList.findIndex((item) => item.fullPath === fullPath)
index !== -1 && this.tabList.splice(index, 1)
}
const { currentRoute, replace } = router
const { path } = unref(currentRoute)
if (path !== tab.path) {
// Closed is not the activation tab
close(tab)
this.updateCacheTab()
return
}
// Closed is activated atb
let toTarget: RouteLocationRaw = {}
const index = this.tabList.findIndex((item) => item.path === path)
// If the current is the leftmost tab
if (index === 0) {
// There is only one tab, then jump to the homepage, otherwise jump to the right tab
if (this.tabList.length === 1) {
toTarget = PageEnum.BASE_HOME
} else {
// Jump to the right tab
const page = this.tabList[index + 1]
toTarget = getToTarget(page)
}
} else {
// Close the current tab
const page = this.tabList[index - 1]
toTarget = getToTarget(page)
}
close(currentRoute.value)
await replace(toTarget)
},
// Close according to key
async closeTabByKey(key: string, router: Router) {
const index = this.tabList.findIndex((item) => (item.fullPath || item.path) === key)
if (index !== -1) {
await this.closeTab(this.tabList[index], router)
const { currentRoute, replace } = router
// 检查当前路由是否存在于tabList中
const isActivated = this.tabList.findIndex((item) => {
return item.fullPath === currentRoute.value.fullPath
})
// 如果当前路由不存在于TabList中尝试切换到其它路由
if (isActivated === -1) {
let pageIndex
if (index > 0) {
pageIndex = index - 1
} else if (index < this.tabList.length - 1) {
pageIndex = index + 1
} else {
pageIndex = -1
}
if (pageIndex >= 0) {
const page = this.tabList[index - 1]
const toTarget = getToTarget(page)
await replace(toTarget)
}
}
}
},
// Sort the tabs
async sortTabs(oldIndex: number, newIndex: number) {
const currentTab = this.tabList[oldIndex]
this.tabList.splice(oldIndex, 1)
this.tabList.splice(newIndex, 0, currentTab)
this.lastDragEndIndex = this.lastDragEndIndex + 1
},
// Close the tab on the right and jump
async closeLeftTabs(route: RouteLocationNormalized, router: Router) {
const index = this.tabList.findIndex((item) => item.path === route.path)
if (index > 0) {
const leftTabs = this.tabList.slice(0, index)
const pathList: string[] = []
for (const item of leftTabs) {
const affix = item?.meta?.affix ?? false
if (!affix) {
pathList.push(item.fullPath)
}
}
this.bulkCloseTabs(pathList)
}
this.updateCacheTab()
handleGotoPage(router)
},
// Close the tab on the left and jump
async closeRightTabs(route: RouteLocationNormalized, router: Router) {
const index = this.tabList.findIndex((item) => item.fullPath === route.fullPath)
if (index >= 0 && index < this.tabList.length - 1) {
const rightTabs = this.tabList.slice(index + 1, this.tabList.length)
const pathList: string[] = []
for (const item of rightTabs) {
const affix = item?.meta?.affix ?? false
if (!affix) {
pathList.push(item.fullPath)
}
}
this.bulkCloseTabs(pathList)
}
this.updateCacheTab()
handleGotoPage(router)
},
async closeAllTab(router: Router) {
this.tabList = this.tabList.filter((item) => item?.meta?.affix ?? false)
this.clearCacheTabs()
this.goToPage(router)
},
/**
* Close other tabs
*/
async closeOtherTabs(route: RouteLocationNormalized, router: Router) {
const closePathList = this.tabList.map((item) => item.fullPath)
const pathList: string[] = []
for (const path of closePathList) {
if (path !== route.fullPath) {
const closeItem = this.tabList.find((item) => item.path === path)
if (!closeItem) {
continue
}
const affix = closeItem?.meta?.affix ?? false
if (!affix) {
pathList.push(closeItem.fullPath)
}
}
}
this.bulkCloseTabs(pathList)
this.updateCacheTab()
handleGotoPage(router)
},
/**
* Close tabs in bulk
*/
async bulkCloseTabs(pathList: string[]) {
this.tabList = this.tabList.filter((item) => !pathList.includes(item.fullPath))
},
/**
* Set tab's title
*/
async setTabTitle(title: string, route: RouteLocationNormalized) {
const findTab = this.getTabList.find((item) => item === route)
if (findTab) {
findTab.meta.title = title
await this.updateCacheTab()
}
},
/**
* replace tab's path
* **/
async updateTabPath(fullPath: string, route: RouteLocationNormalized) {
const findTab = this.getTabList.find((item) => item === route)
if (findTab) {
findTab.fullPath = fullPath
findTab.path = fullPath
await this.updateCacheTab()
}
}
}
})
// Need to be used outside the setup
export function useMultipleTabWithOutStore() {
return useMultipleTabStore(store)
}