路由代码解析
router.beforeEach((to, from, next) => {
let href = location.href
if (href.indexOf('#') > href.indexOf('?') && href.indexOf('?') > -1) {
location.href = href.replace(location.search, '') + location.search
return
}
if (location.search.indexOf('atk=') > -1) {
const atkStart = location.search.indexOf('atk=') + 4
let atkEnd = location.search.indexOf('&', atkStart)
if (atkEnd == -1) {
atkEnd = location.search.length
}
const atk = location.search.substring(atkStart, atkEnd)
sessionStorage.setItem('atk', atk)
location.search = ''
next({path: to.path})
return
}
let errCallback = () => {
next(false)
location.href = location.href.replace(location.hash, '#/loginInterface')
Message({
message: '请重新登录!',
type: 'error'
})
}
// meta中有guest为无需拦截的页面, sessionStorage中含有atk则是已登录状态
if (to.meta?.outlink || to.meta.loginPage) {
next()
} else if (!sessionStorage.getItem('atk') && ((typeof store.state.account === 'object' && Object.keys(store.state.account).length > 0) || (to.meta && to.meta.guest === true))) {
storeInitRules.init(to, store).then(() => {
next()
})
} else if (sessionStorage.getItem('atk')) {
if (Object.keys(store.getters.getAccount || {}).length) {
if (to.path === '/') {
let indexPath = '/reportUnit/main'
// 报审方的单位管理账号
if (store.getters.isReportManageOrg) {
indexPath = '/projectOverview'
}
next({path: indexPath})
} else {
next()
}
} else {
store.dispatch('public/account').then(() => {
return storeInitRules.init(to, store)
}).then(() => {
if (to.path === '/') {
let indexPath = '/reportUnit/main'
// 报审方的单位管理账号
if (store.getters.isReportManageOrg) {
indexPath = '/projectOverview'
}
next({path: indexPath})
} else {
next()
}
}).catch(errCallback)
}
} else {
// store.dispatch('public/account').then(res => {
// storeInitRules.init(to, store)
// }).then(next).catch(errCallback)
next(false)
location.href = location.href.replace(location.hash, '#/loginInterface')
}
})
这段代码是 Vue Router 的全局前置守卫(router.beforeEach),主要用于在路由跳转前进行一系列的逻辑判断,包括 URL 处理、登录状态校验、权限控制、页面跳转逻辑 等。下面逐段解析其作用:
1. URL 处理(修复 # 和 ? 顺序问题)
let href = location.href
if (href.indexOf('#') > href.indexOf('?') && href.indexOf('?') > -1) {
location.href = href.replace(location.search, '') + location.search
return
}
- 作用:
如果 URL 中#(hash)出现在?(query)之后(如http://example.com/path?query=1#hash),则重新拼接 URL,确保?在#之前(如http://example.com/path#hash?query=1)。 - 原因:
某些浏览器或框架可能对#和?的顺序敏感,调整顺序可避免路由解析异常。
2. 从 URL 提取 atk(认证 Token)并存储
if (location.search.indexOf('atk=') > -1) {
const atkStart = location.search.indexOf('atk=') + 4
let atkEnd = location.search.indexOf('&', atkStart)
if (atkEnd == -1) {
atkEnd = location.search.length
}
const atk = location.search.substring(atkStart, atkEnd)
sessionStorage.setItem('atk', atk) // 存储 token
location.search = '' // 清除 URL 中的 query 参数
next({ path: to.path }) // 重新跳转当前路径(无 atk)
return
}
- 作用:
如果 URL 中包含atk=xxx(如http://example.com?atk=token123),提取atk并存入sessionStorage,然后清除 URL 中的查询参数,避免 token 暴露。 - 用途:
适用于第三方登录回调后携带 token 的场景,安全存储 token 并清理 URL。
3. 错误回调(跳转登录页)
let errCallback = () => {
next(false) // 中断当前导航
location.href = location.href.replace(location.hash, '#/loginInterface') // 强制跳登录页
Message({ message: '请重新登录!', type: 'error' }) // 显示错误提示
}
- 作用:
当权限校验失败时,中断导航并跳转到登录页,同时显示错误消息。
4. 路由拦截逻辑
情况 1:放行无需拦截的路由
if (to.meta?.outlink || to.meta.loginPage) {
next() // 直接放行
}
- 条件:
如果路由的meta中包含outlink(外链)或loginPage(登录页),则直接放行。
情况 2:未登录但允许游客访问(guest)
else if (!sessionStorage.getItem('atk') &&
((typeof store.state.account === 'object' && Object.keys(store.state.account).length > 0) ||
(to.meta && to.meta.guest === true))) {
storeInitRules.init(to, store).then(() => {
next() // 初始化权限后放行
})
}
- 条件:
- 用户未登录(
sessionStorage无atk)。 - 但满足以下任一条件:
store.state.account已存在(可能是缓存用户信息)。- 路由标记为
meta.guest: true(允许游客访问)。
- 用户未登录(
- 行为:
调用storeInitRules.init初始化权限,完成后放行。
情况 3:已登录(atk 存在)
else if (sessionStorage.getItem('atk')) {
// 3.1 如果 store 中已有用户信息
if (Object.keys(store.getters.getAccount || {}).length) {
if (to.path === '/') {
// 根据角色跳转到不同首页
let indexPath = '/reportUnit/main'
if (store.getters.isReportManageOrg) {
indexPath = '/projectOverview'
}
next({ path: indexPath })
} else {
next() // 非根路径直接放行
}
}
// 3.2 如果 store 中无用户信息,先获取用户信息
else {
store.dispatch('public/account').then(() => {
return storeInitRules.init(to, store)
}).then(() => {
if (to.path === '/') {
// 同样根据角色跳转首页
let indexPath = '/reportUnit/main'
if (store.getters.isReportManageOrg) {
indexPath = '/projectOverview'
}
next({ path: indexPath })
} else {
next()
}
}).catch(errCallback) // 失败则跳登录页
}
}
- 逻辑:
- 如果用户信息已存在(
store.getters.getAccount),则根据角色跳转到对应首页(如/reportUnit/main或/projectOverview)。 - 如果用户信息不存在,先调用
store.dispatch('public/account')获取用户信息,再初始化权限,最后跳转。
- 如果用户信息已存在(
情况 4:未登录且无权限
else {
next(false) // 中断导航
location.href = location.href.replace(location.hash, '#/loginInterface') // 强制跳登录页
}
- 行为:
默认情况(未登录且无guest权限),直接跳转到登录页。
总结
这段代码的核心功能:
- URL 规范化:修复
#和?的顺序问题。 - Token 处理:从 URL 提取
atk并存储到sessionStorage。 - 登录状态校验:
- 已登录 → 根据角色跳转首页或放行。
- 未登录但允许游客访问 → 放行。
- 未登录且需权限 → 跳登录页。
- 权限初始化:通过
storeInitRules.init动态加载权限规则。 - 错误处理:统一跳登录页并提示错误。
适用场景:
- 需要登录认证的单页应用(SPA)。
- 动态权限控制(如不同角色跳不同首页)。
- 第三方登录回调后处理 token。