/** * 权限控制 */ import type { LocationQueryValue, RouteLocationNormalized } from 'vue-router' import config from './config' import { PageEnum } from './enums/pageEnum' import router, { findFirstValidRoute } from './router' import { INDEX_ROUTE, INDEX_ROUTE_NAME } from './router/routes' import useTabsStore from './stores/modules/multipleTabs' import useUserStore from './stores/modules/user' import { clearAuthInfo } from './utils/auth' import { isExternal } from './utils/validate' function firstQueryString(q: LocationQueryValue | LocationQueryValue[] | undefined): string { if (q === undefined || q === null) { return '' } if (Array.isArray(q)) { const v = q[0] return v == null ? '' : String(v).trim() } return String(q).trim() } /** * 企微扫码绑定 OAuth 回调:须放行当前页并保留 code,否则守卫会跳到 /bind-work-wechat 导致丢参、绑定失败。 * Vue Router 的 query 值可能是 string | string[],仅用 typeof === 'string' 会误判,进而 next({ path }) 清掉整段 query。 */ function isWorkWechatBindOAuthCallback(to: RouteLocationNormalized): boolean { const code = firstQueryString(to.query.code) if (!code) { return false } const state = firstQueryString(to.query.state) if (state === 'bind_wxwork' || state === 'admin_bind_wx') { return true } return firstQueryString(to.query.bind_wxwork) === '1' } // 动态添加路由-使用递归进行调整-(fix: 修复之前超过3级菜单导致使用keep-alive功能无效问题 const addRoutesRecursively = (routes: any, parentPath = '') => { try { routes.forEach((route: any) => { // 如果路由是外部链接,则不添加 if (isExternal(route.path)) { return } // 拼接父路由路径和当前路由路径 const fullPath = parentPath + route.path // 创建路由对象,确保每个路由都有唯一的名称 const routerEntry = { ...route, path: fullPath, name: route.name || fullPath.replace(/\//g, '_').replace('_', '') // 替换斜杠为下划线,生成唯一名称 } // 添加路由 if (!route.children) { router.addRoute(INDEX_ROUTE_NAME, routerEntry) } else { router.addRoute(routerEntry) } // 递归处理子路由 if (route.children && route.children.length > 0) { addRoutesRecursively(route.children, fullPath + '/') } }) } catch (e) { console.error('Error adding routes:', e) } } const loginPath = PageEnum.LOGIN const defaultPath = PageEnum.INDEX const changePasswordPath = '/change-password' const bindWorkWechatPath = '/bind-work-wechat' // 免登录白名单 const whiteList: string[] = [PageEnum.LOGIN, PageEnum.ERROR_403] router.beforeEach(async (to, from, next) => { document.title = to.meta.title ?? config.title const userStore = useUserStore() const tabsStore = useTabsStore() if (to.path === bindWorkWechatPath) { if (!userStore.token) { next({ path: loginPath }) return } next() return } // 特殊处理:修改密码页面 if (to.path === changePasswordPath) { if (!userStore.token) { // 未登录,跳转到登录页 next({ path: loginPath }) return } // 需要修改密码,允许访问 next() return } if (whiteList.includes(to.path)) { // 在免登录白名单,直接进入 next() } else if (userStore.token) { // 获取用户信息 const hasGetUserInfo = Object.keys(userStore.userInfo).length !== 0 if (hasGetUserInfo) { // 已经获取过用户信息,检查是否需要修改密码 if (userStore.isPaw === 0) { // 需要修改密码,强制跳转到修改密码页面 next({ path: changePasswordPath }) return } if (userStore.userInfo?.need_bind_work_wechat) { if (to.path !== bindWorkWechatPath) { if (isWorkWechatBindOAuthCallback(to)) { next() return } next({ path: bindWorkWechatPath }) return } } // 须先绑企微时未 addRoute;绑定后须补注册 if ( !userStore.userInfo?.need_bind_work_wechat && userStore.routes?.length > 0 && !router.hasRoute(INDEX_ROUTE_NAME) ) { const routeName = findFirstValidRoute(userStore.routes) if (!routeName) { clearAuthInfo() next(PageEnum.ERROR_403) return } tabsStore.setRouteName(routeName!) INDEX_ROUTE.redirect = { name: routeName! } router.addRoute(INDEX_ROUTE) addRoutesRecursively(userStore.routes) next({ ...to, replace: true }) return } if (to.path === loginPath) { next({ path: defaultPath }) } else { next() } } else { try { await userStore.getUserInfo() // 获取用户信息后,检查是否需要修改密码 if (userStore.isPaw === 0) { // 需要修改密码,强制跳转到修改密码页面 next({ path: changePasswordPath }) return } if (userStore.userInfo?.need_bind_work_wechat) { if (isWorkWechatBindOAuthCallback(to)) { next({ ...to, replace: true }) return } next({ path: bindWorkWechatPath }) return } const routes = userStore.routes // 找到第一个有效路由 const routeName = findFirstValidRoute(routes) // 没有有效路由跳转到403页面 if (!routeName) { clearAuthInfo() next(PageEnum.ERROR_403) return } tabsStore.setRouteName(routeName!) INDEX_ROUTE.redirect = { name: routeName } // 动态添加index路由 router.addRoute(INDEX_ROUTE) // 动态添加其余路由 addRoutesRecursively(routes) next({ ...to, replace: true }) } catch (err) { clearAuthInfo() next({ path: loginPath, query: { redirect: to.fullPath } }) } } } else { next({ path: loginPath, query: { redirect: to.fullPath } }) } })