import { reactive } from 'vue' import { defineStore } from 'pinia' import { STORE_TAB_VIEW_CONFIG } from '@/stores/constant/cacheKey' import type { NavTabs } from '@/stores/interface/index' import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router' import { adminBaseRoutePath } from '@/router/static' import { set } from 'lodash' export const useNavTabs = defineStore( 'navTabs', () => { const state: NavTabs = reactive({ // 激活tab的index activeIndex: 0, // 激活的tab activeRoute: null, // tab列表 tabsView: [], // 当前tab是否全屏 tabFullScreen: false, // 从后台加载到的菜单路由列表 tabsViewRoutes: [], // 按钮权限节点 authNode: new Map() }) function addTab(route: RouteLocationNormalized) { if (!route.meta.addtab) return for (const key in state.tabsView) { if (state.tabsView[key].path === route.path) { state.tabsView[key].params = route.params ? route.params : state.tabsView[key].params state.tabsView[key].query = route.query ? route.query : state.tabsView[key].query state.tabsView[key].meta = route.query ? route.meta : state.tabsView[key].meta return } } state.tabsView.push(route) } function closeTab(route: RouteLocationNormalized) { state.tabsView.map((v, k) => { if (v.path == route.path) { state.tabsView.splice(k, 1) return } }) } /** * 关闭多个标签 * @param retainMenu 需要保留的标签,否则关闭全部标签 */ const closeTabs = (retainMenu: RouteLocationNormalized | false = false) => { if (retainMenu) { state.tabsView = [retainMenu] } else { state.tabsView = [] } } const setActiveRoute = (route: RouteLocationNormalized): void => { const currentRouteIndex: number = state.tabsView.findIndex((item: RouteLocationNormalized) => { return item.path === route.path }) if (currentRouteIndex === -1) return state.activeRoute = route state.activeIndex = currentRouteIndex } const setTabsViewRoutes = (data: RouteRecordRaw[]): void => { state.tabsViewRoutes = encodeRoutesURI(JSON.parse(JSON.stringify(data))) } const setAuthNode = (key: string, data: string[]) => { state.authNode.set(key, data) } const fillAuthNode = (data: Map) => { state.authNode = data } const setFullScreen = (fullScreen: boolean): void => { state.tabFullScreen = fullScreen } const refresh = () => { // setTimeout(() => { // console.log(123, state.tabsViewRoutes) let list = matchAndReturnRouteData(state.tabsViewRoutes, state.tabsView) state.tabsView = [] list.forEach(item => { addTab(item) }) // }, 1000) } return { state, addTab, closeTab, closeTabs, setActiveRoute, setTabsViewRoutes, setAuthNode, fillAuthNode, setFullScreen, refresh } }, { persist: { key: STORE_TAB_VIEW_CONFIG, paths: ['state.tabFullScreen'] } } ) /** * 核心逻辑: * 1. 递归遍历树形菜单,筛选出与routeList中name匹配的节点 * 2. 将匹配到的节点格式转换为routeList的结构并返回 */ function matchAndReturnRouteData(tree, routeList) { // 1. 构建路由name映射(name -> 完整路由对象) const routeMap = new Map() routeList.forEach(route => { if (route.name) { routeMap.set(route.name, route) } }) // 2. 递归遍历树形菜单,收集匹配的节点 const matchedNodes = [] function recursion(node) { // 匹配当前节点 if (routeMap.has(node.name)) { // 深度克隆路由对象,避免修改原数据 const matchedRoute = JSON.parse(JSON.stringify(routeMap.get(node.name))) matchedNodes.push(matchedRoute) } // 递归处理子节点 if (node.children && node.children.length) { node.children.forEach(child => recursion(child)) } } // 遍历所有顶级节点 tree.forEach(node => recursion(node)) // 3. 返回匹配后的第二个数据格式(和routeList结构一致) return matchedNodes } /** * 对iframe的url进行编码 */ function encodeRoutesURI(data: RouteRecordRaw[]) { data.forEach(item => { if (item.meta?.menu_type == 'iframe') { item.path = adminBaseRoutePath + '/iframe/' + encodeURIComponent(item.path) } if (item.children && item.children.length) { item.children = encodeRoutesURI(item.children) } }) return data }