import { defineStore } from 'pinia'
|
import { ref, computed } from 'vue'
|
import { router } from '@/router'
|
import { useCommon } from '@/hooks/core/useCommon'
|
const useWorktabStore = defineStore(
|
'worktabStore',
|
() => {
|
const current = ref({})
|
const opened = ref([])
|
const keepAliveExclude = ref([])
|
const hasOpenedTabs = computed(() => opened.value.length > 0)
|
const hasMultipleTabs = computed(() => opened.value.length > 1)
|
const currentTabIndex = computed(() =>
|
current.value.path ? opened.value.findIndex((tab) => tab.path === current.value.path) : -1
|
)
|
const findTabIndex = (path) => {
|
return opened.value.findIndex((tab) => tab.path === path)
|
}
|
const getTab = (path) => {
|
return opened.value.find((tab) => tab.path === path)
|
}
|
const isTabClosable = (tab) => {
|
return !tab.fixedTab
|
}
|
const safeRouterPush = (tab) => {
|
if (!tab.path) {
|
console.warn('尝试跳转到无效路径的标签页')
|
return
|
}
|
try {
|
router.push({
|
path: tab.path,
|
query: tab.query
|
})
|
} catch (error) {
|
console.error('路由跳转失败:', error)
|
}
|
}
|
const openTab = (tab) => {
|
if (!tab.path) {
|
console.warn('尝试打开无效的标签页')
|
return
|
}
|
if (tab.name) {
|
removeKeepAliveExclude(tab.name)
|
}
|
let existingIndex = -1
|
if (tab.name) {
|
existingIndex = opened.value.findIndex((t) => t.name === tab.name)
|
}
|
if (existingIndex === -1) {
|
existingIndex = findTabIndex(tab.path)
|
}
|
if (existingIndex === -1) {
|
const insertIndex = tab.fixedTab ? findFixedTabInsertIndex() : opened.value.length
|
const newTab = { ...tab }
|
if (tab.fixedTab) {
|
opened.value.splice(insertIndex, 0, newTab)
|
} else {
|
opened.value.push(newTab)
|
}
|
current.value = newTab
|
} else {
|
const existingTab = opened.value[existingIndex]
|
opened.value[existingIndex] = {
|
...existingTab,
|
path: tab.path,
|
params: tab.params,
|
query: tab.query,
|
title: tab.title || existingTab.title,
|
fixedTab: tab.fixedTab ?? existingTab.fixedTab,
|
keepAlive: tab.keepAlive ?? existingTab.keepAlive,
|
name: tab.name || existingTab.name,
|
icon: tab.icon || existingTab.icon
|
}
|
current.value = opened.value[existingIndex]
|
}
|
}
|
const findFixedTabInsertIndex = () => {
|
let insertIndex = 0
|
for (let i = 0; i < opened.value.length; i++) {
|
if (opened.value[i].fixedTab) {
|
insertIndex = i + 1
|
} else {
|
break
|
}
|
}
|
return insertIndex
|
}
|
const removeTab = (path) => {
|
const targetTab = getTab(path)
|
const targetIndex = findTabIndex(path)
|
if (targetIndex === -1) {
|
console.warn(`尝试关闭不存在的标签页: ${path}`)
|
return
|
}
|
if (targetTab && !isTabClosable(targetTab)) {
|
console.warn(`尝试关闭固定标签页: ${path}`)
|
return
|
}
|
opened.value.splice(targetIndex, 1)
|
if (targetTab?.name) {
|
addKeepAliveExclude(targetTab)
|
}
|
const { homePath } = useCommon()
|
if (!hasOpenedTabs.value) {
|
if (path !== homePath.value) {
|
current.value = {}
|
safeRouterPush({ path: homePath.value })
|
}
|
return
|
}
|
if (current.value.path === path) {
|
const newIndex = targetIndex >= opened.value.length ? opened.value.length - 1 : targetIndex
|
current.value = opened.value[newIndex]
|
safeRouterPush(current.value)
|
}
|
}
|
const removeLeft = (path) => {
|
const targetIndex = findTabIndex(path)
|
if (targetIndex === -1) {
|
console.warn(`尝试关闭左侧标签页,但目标标签页不存在: ${path}`)
|
return
|
}
|
const leftTabs = opened.value.slice(0, targetIndex)
|
const closableLeftTabs = leftTabs.filter(isTabClosable)
|
if (closableLeftTabs.length === 0) {
|
console.warn('左侧没有可关闭的标签页')
|
return
|
}
|
markTabsToRemove(closableLeftTabs)
|
opened.value = opened.value.filter(
|
(tab, index) => index >= targetIndex || !isTabClosable(tab)
|
)
|
const targetTab = getTab(path)
|
if (targetTab) {
|
current.value = targetTab
|
}
|
}
|
const removeRight = (path) => {
|
const targetIndex = findTabIndex(path)
|
if (targetIndex === -1) {
|
console.warn(`尝试关闭右侧标签页,但目标标签页不存在: ${path}`)
|
return
|
}
|
const rightTabs = opened.value.slice(targetIndex + 1)
|
const closableRightTabs = rightTabs.filter(isTabClosable)
|
if (closableRightTabs.length === 0) {
|
console.warn('右侧没有可关闭的标签页')
|
return
|
}
|
markTabsToRemove(closableRightTabs)
|
opened.value = opened.value.filter(
|
(tab, index) => index <= targetIndex || !isTabClosable(tab)
|
)
|
const targetTab = getTab(path)
|
if (targetTab) {
|
current.value = targetTab
|
}
|
}
|
const removeOthers = (path) => {
|
const targetTab = getTab(path)
|
if (!targetTab) {
|
console.warn(`尝试关闭其他标签页,但目标标签页不存在: ${path}`)
|
return
|
}
|
const otherTabs = opened.value.filter((tab) => tab.path !== path)
|
const closableTabs = otherTabs.filter(isTabClosable)
|
if (closableTabs.length === 0) {
|
console.warn('没有其他可关闭的标签页')
|
return
|
}
|
markTabsToRemove(closableTabs)
|
opened.value = opened.value.filter((tab) => tab.path === path || !isTabClosable(tab))
|
current.value = targetTab
|
}
|
const removeAll = () => {
|
const { homePath } = useCommon()
|
const hasFixedTabs = opened.value.some((tab) => tab.fixedTab)
|
const closableTabs = opened.value.filter((tab) => {
|
if (!isTabClosable(tab)) return false
|
return hasFixedTabs || tab.path !== homePath.value
|
})
|
if (closableTabs.length === 0) {
|
console.warn('没有可关闭的标签页')
|
return
|
}
|
markTabsToRemove(closableTabs)
|
opened.value = opened.value.filter((tab) => {
|
return !isTabClosable(tab) || (!hasFixedTabs && tab.path === homePath.value)
|
})
|
if (!hasOpenedTabs.value) {
|
current.value = {}
|
safeRouterPush({ path: homePath.value })
|
return
|
}
|
const homeTab = opened.value.find((tab) => tab.path === homePath.value)
|
const targetTab = homeTab || opened.value[0]
|
current.value = targetTab
|
safeRouterPush(targetTab)
|
}
|
const addKeepAliveExclude = (tab) => {
|
if (!tab.keepAlive || !tab.name) return
|
if (!keepAliveExclude.value.includes(tab.name)) {
|
keepAliveExclude.value.push(tab.name)
|
}
|
}
|
const removeKeepAliveExclude = (name) => {
|
if (!name) return
|
keepAliveExclude.value = keepAliveExclude.value.filter((item) => item !== name)
|
}
|
const markTabsToRemove = (tabs) => {
|
tabs.forEach((tab) => {
|
if (tab.name) {
|
addKeepAliveExclude(tab)
|
}
|
})
|
}
|
const toggleFixedTab = (path) => {
|
const targetIndex = findTabIndex(path)
|
if (targetIndex === -1) {
|
console.warn(`尝试切换不存在标签页的固定状态: ${path}`)
|
return
|
}
|
const tab = { ...opened.value[targetIndex] }
|
tab.fixedTab = !tab.fixedTab
|
opened.value.splice(targetIndex, 1)
|
if (tab.fixedTab) {
|
const firstNonFixedIndex = opened.value.findIndex((t) => !t.fixedTab)
|
const insertIndex = firstNonFixedIndex === -1 ? opened.value.length : firstNonFixedIndex
|
opened.value.splice(insertIndex, 0, tab)
|
} else {
|
const fixedCount = opened.value.filter((t) => t.fixedTab).length
|
opened.value.splice(fixedCount, 0, tab)
|
}
|
if (current.value.path === path) {
|
current.value = tab
|
}
|
}
|
const validateWorktabs = (routerInstance) => {
|
try {
|
const isTabRouteValid = (tab) => {
|
try {
|
if (tab.name) {
|
const routes = routerInstance.getRoutes()
|
if (routes.some((r) => r.name === tab.name)) return true
|
}
|
if (tab.path) {
|
const resolved = routerInstance.resolve({
|
path: tab.path,
|
query: tab.query || void 0
|
})
|
return resolved.matched.length > 0
|
}
|
return false
|
} catch {
|
return false
|
}
|
}
|
const validTabs = opened.value.filter((tab) => isTabRouteValid(tab))
|
if (validTabs.length !== opened.value.length) {
|
console.warn('发现无效的标签页路由,已自动清理')
|
opened.value = validTabs
|
}
|
const isCurrentValid = current.value && isTabRouteValid(current.value)
|
if (!isCurrentValid && validTabs.length > 0) {
|
console.warn('当前激活标签无效,已自动切换')
|
current.value = validTabs[0]
|
} else if (!isCurrentValid) {
|
current.value = {}
|
}
|
} catch (error) {
|
console.error('验证工作台标签页失败:', error)
|
}
|
}
|
const clearAll = () => {
|
current.value = {}
|
opened.value = []
|
keepAliveExclude.value = []
|
}
|
const getStateSnapshot = () => {
|
return {
|
current: { ...current.value },
|
opened: [...opened.value],
|
keepAliveExclude: [...keepAliveExclude.value]
|
}
|
}
|
const getTabTitle = (path) => {
|
const tab = getTab(path)
|
return tab
|
}
|
const updateTabTitle = (path, title) => {
|
const tab = getTab(path)
|
if (tab) {
|
tab.customTitle = title
|
}
|
}
|
const resetTabTitle = (path) => {
|
const tab = getTab(path)
|
if (tab) {
|
tab.customTitle = ''
|
}
|
}
|
return {
|
// 状态
|
current,
|
opened,
|
keepAliveExclude,
|
// 计算属性
|
hasOpenedTabs,
|
hasMultipleTabs,
|
currentTabIndex,
|
// 方法
|
openTab,
|
removeTab,
|
removeLeft,
|
removeRight,
|
removeOthers,
|
removeAll,
|
toggleFixedTab,
|
validateWorktabs,
|
clearAll,
|
getStateSnapshot,
|
// 工具方法
|
findTabIndex,
|
getTab,
|
isTabClosable,
|
addKeepAliveExclude,
|
removeKeepAliveExclude,
|
markTabsToRemove,
|
getTabTitle,
|
updateTabTitle,
|
resetTabTitle
|
}
|
},
|
{
|
persist: {
|
key: 'worktab',
|
storage: localStorage
|
}
|
}
|
)
|
export { useWorktabStore }
|