import type { CustomRoute, ElegantConstRoute, LastLevelRouteKey, RouteKey, RouteMap } from '@elegant-router/types' import type { RouteRecordRaw } from 'vue-router' import { useBoolean } from '@sa/hooks' import { defineStore } from 'pinia' import { computed, nextTick, ref, shallowRef } from 'vue' import { SetupStoreId } from '@/enum' import { router } from '@/router' import { getRouteName, getRoutePath } from '@/router/elegant/transform' import { createStaticRoutes, getAuthVueRoutes } from '@/router/routes' import { ROOT_ROUTE } from '@/router/routes/builtin' import { fetchGetConstantRoutes, fetchGetUserRoutes, fetchIsRouteExist } from '@/service/api' import { useAuthStore } from '../auth' import { useTabStore } from '../tab' import { filterAuthRoutesByRoles, getBreadcrumbsByRoute, getCacheRouteNames, getGlobalMenusByAuthRoutes, getSelectedMenuKeyPathByKey, isRouteExistByRouteName, sortRoutesByOrder, transformMenuToSearchMenus, updateLocaleOfGlobalMenus, } from './shared' export const useRouteStore = defineStore(SetupStoreId.Route, () => { const authStore = useAuthStore() const tabStore = useTabStore() const { bool: isInitConstantRoute, setBool: setIsInitConstantRoute } = useBoolean() const { bool: isInitAuthRoute, setBool: setIsInitAuthRoute } = useBoolean() /** * 权限路由模式 * * 建议在开发环境中使用静态模式,在生产环境中使用动态模式 * 如果在开发环境中使用静态模式,权限路由将由插件 "@elegant-router/vue" 自动生成 */ const authRouteMode = ref(import.meta.env.VITE_AUTH_ROUTE_MODE) /** 首页路由 key */ const routeHome = ref(import.meta.env.VITE_ROUTE_HOME) /** * 设置首页路由 * * @param routeKey 路由 key */ function setRouteHome(routeKey: LastLevelRouteKey) { routeHome.value = routeKey } /** 常量路由 */ 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()) } /** 权限路由 */ 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)[] = [] /** 全局菜单 */ const menus = ref([]) const menusForBreadcrumb = ref([]) const searchMenus = computed(() => transformMenuToSearchMenus(menus.value)) /** * 获取全局菜单 * * @param routes 路由 */ function getGlobalMenus(routes: ElegantConstRoute[]) { menus.value = getGlobalMenusByAuthRoutes(routes, true, false) menusForBreadcrumb.value = getGlobalMenusByAuthRoutes(routes, false, true) } /** 根据语言更新全局菜单 */ function updateGlobalMenusByLocale() { menus.value = updateLocaleOfGlobalMenus(menus.value) menusForBreadcrumb.value = updateLocaleOfGlobalMenus(menusForBreadcrumb.value) } /** 缓存路由 */ const cacheRoutes = ref([]) /** * 排除缓存路由 * * 用于重置路由缓存 */ const excludeCacheRoutes = ref([]) /** * 获取缓存路由 * * @param routes Vue 路由 */ function getCacheRoutes(routes: RouteRecordRaw[]) { cacheRoutes.value = getCacheRouteNames(routes) } /** * 重置路由缓存 * * @default * @param routeKey 路由 key */ async function resetRouteCache(routeKey?: RouteKey) { const routeName = routeKey || (router.currentRoute.value.name as RouteKey) excludeCacheRoutes.value.push(routeName) await nextTick() excludeCacheRoutes.value = [] } /** 全局面包屑 */ const breadcrumbs = computed(() => getBreadcrumbsByRoute(router.currentRoute.value, menusForBreadcrumb.value)) /** 重置 store */ async function resetStore() { const routeStore = useRouteStore() routeStore.$reset() resetVueRoutes() // 重置 store 后,需要重新初始化常量路由 await initConstantRoute() } /** 重置 vue 路由 */ function resetVueRoutes() { removeRouteFns.forEach(fn => fn()) removeRouteFns.length = 0 } /** 初始化常量路由 */ async function initConstantRoute() { if (isInitConstantRoute.value) return // 静态路由 const staticRoute = createStaticRoutes() // 如果是静态路由模式,直接添加静态常量路由 if (authRouteMode.value === 'static') { // 静态路由模式 addConstantRoutes(staticRoute.constantRoutes) }else {// 动态路由模式 const { data, error } = await fetchGetConstantRoutes() if (!error) { addConstantRoutes(data) } else { // 如果获取常量路由失败,使用静态常量路由 addConstantRoutes(staticRoute.constantRoutes) } } handleConstantAndAuthRoutes() setIsInitConstantRoute(true) tabStore.initHomeTab() } /** 初始化权限路由 */ async function initAuthRoute() { // 检查用户信息是否已初始化 if (!authStore.userInfo.userId) { await authStore.initUserInfo() } if (authRouteMode.value === 'static') { initStaticAuthRoute() } else { await initDynamicAuthRoute() } tabStore.initHomeTab() } /** 初始化静态权限路由 */ function initStaticAuthRoute() { const { authRoutes: staticAuthRoutes } = createStaticRoutes() if (authStore.isStaticSuper) { addAuthRoutes(staticAuthRoutes) } else { const filteredAuthRoutes = filterAuthRoutesByRoles(staticAuthRoutes, authStore.userInfo.roles) addAuthRoutes(filteredAuthRoutes) } handleConstantAndAuthRoutes() setIsInitAuthRoute(true) } /** 初始化动态权限路由 */ async function initDynamicAuthRoute() { const { data, error } = await fetchGetUserRoutes() if (!error) { const { routes, home } = data addAuthRoutes(routes) handleConstantAndAuthRoutes() setRouteHome(home) handleUpdateRootRouteRedirect(home) setIsInitAuthRoute(true) } else { // 如果获取用户路由失败,重置 store authStore.resetStore() } } /** 处理常量路由和权限路由 */ function handleConstantAndAuthRoutes() { const allRoutes = [...constantRoutes.value, ...authRoutes.value] const sortRoutes = sortRoutesByOrder(allRoutes) const vueRoutes = getAuthVueRoutes(sortRoutes) resetVueRoutes() addRoutesToVueRouter(vueRoutes) getGlobalMenus(sortRoutes) getCacheRoutes(vueRoutes) } /** * 添加路由到 vue router * * @param routes Vue 路由 */ function addRoutesToVueRouter(routes: RouteRecordRaw[]) { routes.forEach((route) => { const removeFn = router.addRoute(route) addRemoveRouteFn(removeFn) }) } /** * 添加删除路由函数 * * @param fn 删除函数 */ function addRemoveRouteFn(fn: () => void) { removeRouteFns.push(fn) } /** * 当权限路由模式为动态时,更新根路由重定向 * * @param redirectKey 重定向路由 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) } } /** * 获取权限路由是否存在 * * @param routePath 路由路径 */ 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 data } /** * 获取选中的菜单 key 路径 * * @param selectedKey 选中的菜单 key */ function getSelectedMenuKeyPath(selectedKey: string) { return getSelectedMenuKeyPathByKey(selectedKey, menus.value) } async function onRouteSwitchWhenLoggedIn() { // 登录并切换路由时的一些全局初始化逻辑 } async function onRouteSwitchWhenNotLoggedIn() { // 如果不需要登录时的一些全局初始化逻辑 } return { resetStore, routeHome, menus, searchMenus, updateGlobalMenusByLocale, cacheRoutes, excludeCacheRoutes, resetRouteCache, breadcrumbs, initConstantRoute, isInitConstantRoute, initAuthRoute, isInitAuthRoute, setIsInitAuthRoute, getIsAuthRouteExist, getSelectedMenuKeyPath, onRouteSwitchWhenLoggedIn, onRouteSwitchWhenNotLoggedIn, } })