diff --git a/web/src/components/commandMenu/index.vue b/web/src/components/commandMenu/index.vue index eb403d4a1e..27ef93fc48 100644 --- a/web/src/components/commandMenu/index.vue +++ b/web/src/components/commandMenu/index.vue @@ -54,7 +54,8 @@ const options = reactive([]) const deepMenus = (menus) => { const arr = [] - menus.forEach((menu) => { + menus?.forEach((menu) => { + if (!menu?.children) return if (menu.children && menu.children.length > 0) { arr.push(...deepMenus(menu.children)) } else { @@ -77,7 +78,7 @@ label: '跳转', children: [] } - const menus = deepMenus(routerStore.asyncRouters[0].children) + const menus = deepMenus(routerStore.asyncRouters[0]?.children || []) option.children.push(...menus) options.push(option) } diff --git a/web/src/pathInfo.json b/web/src/pathInfo.json index e08798c046..bf0f039458 100644 --- a/web/src/pathInfo.json +++ b/web/src/pathInfo.json @@ -27,6 +27,7 @@ "/src/view/layout/aside/normalMode.vue": "GvaAside", "/src/view/layout/header/index.vue": "Index", "/src/view/layout/header/tools.vue": "Tools", + "/src/view/layout/iframe.vue": "GvaLayoutIframe", "/src/view/layout/index.vue": "GvaLayout", "/src/view/layout/screenfull/index.vue": "Screenfull", "/src/view/layout/search/search.vue": "BtnBox", diff --git a/web/src/permission.js b/web/src/permission.js index 70c21bee89..b61bf651e2 100644 --- a/web/src/permission.js +++ b/web/src/permission.js @@ -4,134 +4,138 @@ import getPageTitle from '@/utils/page' import router from '@/router' import Nprogress from 'nprogress' import 'nprogress/nprogress.css' -Nprogress.configure({ showSpinner: false, ease: 'ease', speed: 500 }) -const whiteList = ['Login', 'Init'] +// 配置 NProgress +Nprogress.configure({ + showSpinner: false, + ease: 'ease', + speed: 500 +}) -const getRouter = async (userStore) => { - const routerStore = useRouterStore() - await routerStore.SetAsyncRouter() - await userStore.GetUserInfo() - const asyncRouters = routerStore.asyncRouters - asyncRouters.forEach((asyncRouter) => { - router.addRoute(asyncRouter) - }) +// 白名单路由 +const WHITE_LIST = ['Login', 'Init', 'Iframe'] + +// 处理路由加载 +const setupRouter = async (userStore) => { + try { + const routerStore = useRouterStore() + await Promise.all([routerStore.SetAsyncRouter(), userStore.GetUserInfo()]) + + routerStore.asyncRouters.forEach((route) => router.addRoute(route)) + return true + } catch (error) { + console.error('Setup router failed:', error) + return false + } } +// 移除加载动画 const removeLoading = () => { const element = document.getElementById('gva-loading-box') - if (element) { - element.remove() - } + element?.remove() } -async function handleKeepAlive(to) { - if (to.matched.some((item) => item.meta.keepAlive)) { - if (to.matched && to.matched.length > 2) { - for (let i = 1; i < to.matched.length; i++) { - const element = to.matched[i - 1] - if (element.name === 'layout') { - to.matched.splice(i, 1) - await handleKeepAlive(to) - } - // 如果没有按需加载完成则等待加载 - if (typeof element.components.default === 'function') { - await element.components.default() - await handleKeepAlive(to) - } +// 处理组件缓存 +const handleKeepAlive = async (to) => { + if (!to.matched.some((item) => item.meta.keepAlive)) return + + if (to.matched?.length > 2) { + for (let i = 1; i < to.matched.length; i++) { + const element = to.matched[i - 1] + + if (element.name === 'layout') { + to.matched.splice(i, 1) + await handleKeepAlive(to) + continue + } + + if (typeof element.components.default === 'function') { + await element.components.default() + await handleKeepAlive(to) } } } } +// 处理路由重定向 +const handleRedirect = (to, userStore) => { + if (router.hasRoute(userStore.userInfo.authority.defaultRouter)) { + return { ...to, replace: true } + } + return { path: '/layout/404' } +} + +// 路由守卫 router.beforeEach(async (to, from) => { + const userStore = useUserStore() const routerStore = useRouterStore() + const token = userStore.token + Nprogress.start() - const userStore = useUserStore() + + // 处理元数据和缓存 to.meta.matched = [...to.matched] - handleKeepAlive(to) - const token = userStore.token - // 在白名单中的判断情况 + await handleKeepAlive(to) + + // 设置页面标题 document.title = getPageTitle(to.meta.title, to) - if (to.meta.client) { + // 白名单路由处理 + if (WHITE_LIST.includes(to.name)) { + if ( + token && + !routerStore.asyncRouterFlag && + !WHITE_LIST.includes(from.name) + ) { + await setupRouter(userStore) + } return true } - if (whiteList.indexOf(to.name) > -1) { - if (token) { - if (!routerStore.asyncRouterFlag && whiteList.indexOf(from.name) < 0) { - await getRouter(userStore) - } - // token 可以解析但是却是不存在的用户 id 或角色 id 会导致无限调用 - if (userStore.userInfo?.authority?.defaultRouter != null) { - if (router.hasRoute(userStore.userInfo.authority.defaultRouter)) { - return { name: userStore.userInfo.authority.defaultRouter } - } else { - return { path: '/layout/404' } - } - } else { - // 强制退出账号 - userStore.ClearStorage() - return { - name: 'Login', - query: { - redirect: document.location.hash - } - } - } - } else { - return true + + // 需要登录的路由处理 + if (token) { + // 处理需要跳转到首页的情况 + if (sessionStorage.getItem('needToHome') === 'true') { + sessionStorage.removeItem('needToHome') + return { path: '/' } } - } else { - // 不在白名单中并且已经登录的时候 - if (token) { - if (sessionStorage.getItem('needToHome') === 'true') { - sessionStorage.removeItem('needToHome') - return { path: '/' } - } - // 添加flag防止多次获取动态路由和栈溢出 - if (!routerStore.asyncRouterFlag && whiteList.indexOf(from.name) < 0) { - await getRouter(userStore) - if (userStore.token) { - if (router.hasRoute(userStore.userInfo.authority.defaultRouter)) { - return { ...to, replace: true } - } else { - return { path: '/layout/404' } - } - } else { - return { - name: 'Login', - query: { redirect: to.href } - } - } - } else { - if (to.matched.length) { - return true - } else { - return { path: '/layout/404' } - } + + // 处理异步路由 + if (!routerStore.asyncRouterFlag && !WHITE_LIST.includes(from.name)) { + const setupSuccess = await setupRouter(userStore) + + if (setupSuccess && userStore.token) { + return handleRedirect(to, userStore) } - } - // 不在白名单中并且未登录的时候 - if (!token) { + return { name: 'Login', - query: { - redirect: document.location.hash - } + query: { redirect: to.href } } } + + return to.matched.length ? true : { path: '/layout/404' } + } + + // 未登录跳转登录页 + return { + name: 'Login', + query: { + redirect: document.location.hash + } } }) +// 路由加载完成 router.afterEach(() => { - // 路由加载完成后关闭进度条 - document.getElementsByClassName('main-cont main-right')[0]?.scrollTo(0, 0) + document.querySelector('.main-cont.main-right')?.scrollTo(0, 0) Nprogress.done() }) -router.onError(() => { - // 路由发生错误后销毁进度条 +// 路由错误处理 +router.onError((error) => { + console.error('Router error:', error) Nprogress.remove() }) +// 移除初始加载动画 removeLoading() diff --git a/web/src/router/index.js b/web/src/router/index.js index d5203b2f87..6928e0845e 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -21,6 +21,11 @@ const routes = [ closeTab: true }, component: () => import('@/view/error/index.vue') + }, + { + path: '/iframe', + name: 'Iframe', + component: () => import('@/view/layout/iframe.vue') } ] diff --git a/web/src/view/dashboard/index.vue b/web/src/view/dashboard/index.vue index ac58ee523d..353bca608e 100644 --- a/web/src/view/dashboard/index.vue +++ b/web/src/view/dashboard/index.vue @@ -2,19 +2,19 @@
{{ activity.content }}