import { computed, nextTick, ref, shallowRef } from 'vue'; import type { RouteRecordRaw } from 'vue-router'; import { defineStore } from 'pinia'; import { useBoolean } from '@sa/hooks'; import type { CustomRoute, ElegantConstRoute, LastLevelRouteKey, RouteKey, RouteMap } from '@elegant-router/types'; import { router } from '@/router'; import { fetchGetConstantRoutes, fetchGetUserRoutes } from '@/service/api'; import { SetupStoreId } from '@/enum'; import { createStaticRoutes, getAuthVueRoutes } from '@/router/routes'; import { ROOT_ROUTE } from '@/router/routes/builtin'; import { getRouteName, getRoutePath } from '@/router/elegant/transform'; import { useAuthStore } from '../auth'; import { useTabStore } from '../tab'; import { filterAuthRoutesByRoles, getBreadcrumbsByRoute, getCacheRouteNames, getGlobalMenusByAuthRoutes, getSelectedMenuKeyPathByKey, isRouteExistByRouteName, sortRoutesByOrder, transformMenuToSearchMenus, updateLocaleOfGlobalMenus } from './shared'; import { log } from 'console'; export const useRouteStore = defineStore(SetupStoreId.Route, () => { const authStore = useAuthStore(); const tabStore = useTabStore(); const { bool: isInitConstantRoute, setBool: setIsInitConstantRoute } = useBoolean(); const { bool: isInitAuthRoute, setBool: setIsInitAuthRoute } = useBoolean(); /** * Auth route mode * * It recommends to use static mode in the development environment, and use dynamic mode in the production * environment, if use static mode in development environment, the auth routes will be auto generated by plugin * "@elegant-router/vue" */ const authRouteMode = ref(import.meta.env.VITE_AUTH_ROUTE_MODE); /** Home route key */ const routeHome = ref(import.meta.env.VITE_ROUTE_HOME); /** * Set route home * * @param routeKey Route key */ function setRouteHome(routeKey: LastLevelRouteKey) { routeHome.value = routeKey; } /** constant routes */ const constantRoutes = shallowRef([]); function addConstantRoutes(routes: ElegantConstRoute[]) { const constantRoutesMap = new Map([]); routes.forEach(route => { constantRoutesMap.set(route.name, route); }); constantRoutes.value = Array.from(constantRoutesMap.values()); } /** auth routes */ const authRoutes = shallowRef([]); function addAuthRoutes(routes: ElegantConstRoute[]) { const authRoutesMap = new Map([]); routes.forEach(route => { authRoutesMap.set(route.name, route); }); authRoutes.value = Array.from(authRoutesMap.values()); } const removeRouteFns: (() => void)[] = []; /** Global menus */ const menus = ref([]); const searchMenus = computed(() => transformMenuToSearchMenus(menus.value)); /** Get global menus */ function getGlobalMenus(routes: ElegantConstRoute[]) { menus.value = getGlobalMenusByAuthRoutes(routes); } /** Update global menus by locale */ function updateGlobalMenusByLocale() { menus.value = updateLocaleOfGlobalMenus(menus.value); } /** Cache routes */ const cacheRoutes = ref([]); /** * Exclude cache routes * * for reset route cache */ const excludeCacheRoutes = ref([]); /** * Get cache routes * * @param routes Vue routes */ function getCacheRoutes(routes: RouteRecordRaw[]) { cacheRoutes.value = getCacheRouteNames(routes); } /** * Reset route cache * * @default router.currentRoute.value.name current route name * @param routeKey */ async function resetRouteCache(routeKey?: RouteKey) { const routeName = routeKey || (router.currentRoute.value.name as RouteKey); excludeCacheRoutes.value.push(routeName); await nextTick(); excludeCacheRoutes.value = []; } /** Global breadcrumbs */ const breadcrumbs = computed(() => getBreadcrumbsByRoute(router.currentRoute.value, menus.value)); /** Reset store */ async function resetStore() { const routeStore = useRouteStore(); routeStore.$reset(); resetVueRoutes(); // after reset store, need to re-init constant route await initConstantRoute(); } /** Reset vue routes */ function resetVueRoutes() { removeRouteFns.forEach(fn => fn()); removeRouteFns.length = 0; } /** init constant route */ async function initConstantRoute() { if (isInitConstantRoute.value) return; const staticRoute = createStaticRoutes(); if (authRouteMode.value === 'static') { addConstantRoutes(staticRoute.constantRoutes); } else { console.log('cccccccccccc'); addConstantRoutes(staticRoute.constantRoutes); } handleConstantAndAuthRoutes(); setIsInitConstantRoute(true); tabStore.initHomeTab(); } /** Init auth route */ async function initAuthRoute() { // check if user info is initialized if (!authStore.userInfo.userId) { await authStore.initUserInfo(); } if (authRouteMode.value === 'static') { initStaticAuthRoute(); } else { await initDynamicAuthRoute(); } tabStore.initHomeTab(); } /** Init static auth route */ function initStaticAuthRoute() { const { authRoutes: staticAuthRoutes } = createStaticRoutes(); if (authStore.isStaticSuper) { addAuthRoutes(staticAuthRoutes); } else { const filteredAuthRoutes = filterAuthRoutesByRoles(staticAuthRoutes, authStore.userInfo.roles); addAuthRoutes(filteredAuthRoutes); } handleConstantAndAuthRoutes(); setIsInitAuthRoute(true); } /** Init dynamic auth route */ async function initDynamicAuthRoute() { const { response } = await fetchGetUserRoutes(); // 递归转换路由数据的函数 function transformRoutesData(routes: any[]): ElegantConstRoute[] { if (!Array.isArray(routes)) return []; return routes.map(item => { // 创建新对象,只保留需要的字段 const route: ElegantConstRoute = { name: item.name, path: item.url, component: item.condition, meta: { title: item.title, icon: item.icon, order: item.weigh, } }; // 如果有子路由,递归处理 if (Array.isArray(item.children) && item.children.length > 0) { route.children = transformRoutesData(item.children); } return route; }); } // 确保类型兼容的比较 if (response.data.code === '1' || response.data.code === 1) { // 转换路由数据并舍弃其他字段 const routes = transformRoutesData(Array.isArray(response.data.data) ? response.data.data : []); console.log('用户菜单',routes); addAuthRoutes(routes); handleConstantAndAuthRoutes(); // 获取首页路由,如果response中有home属性则使用,否则使用第一个路由 //使用数组中name=home那个 const homeRoute = routes.find(item => item.name === 'home')?.name as LastLevelRouteKey; console.log('homeRoute',homeRoute); setRouteHome(homeRoute); handleUpdateRootRouteRedirect(homeRoute); setIsInitAuthRoute(true); } else { // if fetch user routes failed, reset store authStore.resetStore(); } } /** handle constant and auth routes */ function handleConstantAndAuthRoutes() { const allRoutes = [...constantRoutes.value, ...authRoutes.value]; const sortRoutes = sortRoutesByOrder(allRoutes); const vueRoutes = getAuthVueRoutes(sortRoutes); console.log('sortRoutes',vueRoutes); resetVueRoutes(); addRoutesToVueRouter(vueRoutes); getGlobalMenus(sortRoutes); getCacheRoutes(vueRoutes); } /** * Add routes to vue router * * @param routes Vue routes */ function addRoutesToVueRouter(routes: RouteRecordRaw[]) { routes.forEach(route => { const removeFn = router.addRoute(route); addRemoveRouteFn(removeFn); }); } /** * Add remove route fn * * @param fn */ function addRemoveRouteFn(fn: () => void) { removeRouteFns.push(fn); } /** * Update root route redirect when auth route mode is dynamic * * @param redirectKey Redirect route key */ function handleUpdateRootRouteRedirect(redirectKey: LastLevelRouteKey) { const redirect = getRoutePath(redirectKey); if (redirect) { const rootRoute: CustomRoute = { ...ROOT_ROUTE, redirect }; router.removeRoute(rootRoute.name); const [rootVueRoute] = getAuthVueRoutes([rootRoute]); router.addRoute(rootVueRoute); } } /** * Get is auth route exist * * @param routePath Route path */ async function getIsAuthRouteExist(routePath: RouteMap[RouteKey]) { const routeName = getRouteName(routePath); if (!routeName) { return false; } if (authRouteMode.value === 'static') { const { authRoutes: staticAuthRoutes } = createStaticRoutes(); return isRouteExistByRouteName(routeName, staticAuthRoutes); } //const { data } = await fetchIsRouteExist(routeName); return false; } /** * Get selected menu key path * * @param selectedKey Selected menu key */ function getSelectedMenuKeyPath(selectedKey: string) { return getSelectedMenuKeyPathByKey(selectedKey, menus.value); } async function onRouteSwitchWhenLoggedIn() { await authStore.initUserInfo(); } async function onRouteSwitchWhenNotLoggedIn() { // some global init logic if it does not need to be logged in } return { resetStore, routeHome, menus, searchMenus, updateGlobalMenusByLocale, cacheRoutes, excludeCacheRoutes, resetRouteCache, breadcrumbs, initConstantRoute, isInitConstantRoute, initAuthRoute, isInitAuthRoute, setIsInitAuthRoute, getIsAuthRouteExist, getSelectedMenuKeyPath, onRouteSwitchWhenLoggedIn, onRouteSwitchWhenNotLoggedIn }; });