204 lines
7.0 KiB
TypeScript
204 lines
7.0 KiB
TypeScript
/**
|
|
* 权限控制
|
|
*/
|
|
|
|
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 } })
|
|
}
|
|
})
|