一、大致思路
1.后台接口返回路由数据,路由数据在后台添加完成,数据格式参考iview-admin路由配置。
2.前端拿到数据之后进行格式化处理。
3.在路由守卫处进行动态添加 addRoutes。
二、实现过程
1.因为大部分路由从后台获取,所以本地routers.js文件只保留login、home、401、500等不需要权限控制的路由(404跟动态路由一同注入)。
{
path: '/login',
name: 'login',
meta: {
title: '登录',
hideInMenu: true,
},
component: () => import('@/view/login/login.vue')
}, {
path: '/',
name: '_data',
redirect: '/data',
component: Main,
meta: {
notCache: true,
hideInBread: true
},
children: [{
path: '/data',
name: 'data',
meta: {
title: '收据统计',
notCache: true,
icon: 'md-stats'
},
component: () => import('@/view/data/data_statistics.vue')
}]
},
{
path: '/401',
name: 'error_401',
meta: {
hideInMenu: true
},
component: () => import('@/view/error-page/401.vue')
},
{
path: '/500',
name: 'error_500',
meta: {
hideInMenu: true
},
component: () => import('@/view/error-page/500.vue')
}
]
2.在libs文件夹下创建格式化路由数据的工具文件 router-util.js
3.在router-util.js里写入以下方法
import {
hasChild,
localRead,
getToken
} from '@/libs/util'
import store from '@/store'
import Main from '@/components/main'
import {
forEach
} from '@/libs/tools'
// 加载菜单
export const loadMenu = () => {
let list = []
let data = localRead('route')
if (!data) {
return list
}
list = formatMenu(JSON.parse(data))
return list
}
// 格式化菜单
export const formatMenu = (list) => {
let res = []
forEach(list, item => {
let obj = {
path: item.path,
name: item.name
}
obj.meta = item.meta
if (item.parentid === 0) {
obj.component = Main
} else {
let data = item.component
// 这里的data应为 /notice/notice.vue 类似的数据,对应的是src/view/下的本地文件
obj.component = () => import('@/view' + data)
}
if (hasChild(item)) {
obj.children = formatMenu(item.children)
}
res.push(obj)
})
return res
}
3.修改菜单展示数据,这里的数据保存在store/module/app.js下
①引入路由数据读取方法
import { loadMenu } from '@/libs/router-util'
②修改getters,将原本的menuList修改为下面的方法
// 通过路由列表得到菜单列表
menuList: (state, getters, rootState) => getMenuByRouter(loadMenu(),rootState.user.access),
③在mutations中添加下面方法
// 接受前台数组,刷新菜单
updateMenuList(state, routes) {
router.addRoutes(routes);
state.menuList = routes
},
4.处理路由守卫文件router/index.js,这里代码比较多我就都贴出来了
注意:这里我们不在使用iview-admin自带的turnTo方法,我在这里被卡了很久。
import Vue from 'vue'
import Router from 'vue-router'
import routes from './routers'
import store from '@/store'
import iView from 'iview'
import {
setToken,
getToken,
setTitle,
localSave,
localRead
} from '@/libs/util'
import config from '@/config'
const {
homeName
} = config
// 引入加载菜单
import {
loadMenu,
formatMenu
} from '@/libs/router-util'
import {
routeindex // 这里是封装的后台返回菜单数据的接口,方法名称根据实际情况改变
} from '@/api/setting'
Vue.use(Router)
const router = new Router({
routes: [...routes, ...loadMenu()],
mode: 'hash' //这个一定要是 hash,如果是一定需要配置为history,请查看路由模式
})
const LOGIN_PAGE_NAME = 'login'
router.beforeEach((to, from, next) => {
iView.LoadingBar.start()
const token = getToken()
const menu = localRead('route') // 读取路由数据
if (!token && to.name !== LOGIN_PAGE_NAME) {
// 未登录且要跳转的页面不是登录页
next({
name: LOGIN_PAGE_NAME // 跳转到登录页
})
} else if (!token && to.name === LOGIN_PAGE_NAME) {
// 未登陆且要跳转的页面是登录页
next() // 跳转
} else if (token && to.name === LOGIN_PAGE_NAME) {
// 已登录且要跳转的页面是登录页
next({
name: homeName // 跳转到homeName页
})
} else {
store.dispatch('getUserInfo').then(user => {
// 如果本地不存在路由数据则动态获取
if (!menu || menu.length === 0) {
routeindex().then(res => {
var list = []
var menuData = res.data.data
localSave('route', JSON.stringify(menuData))
// 格式化菜单
list = formatMenu(menuData)
// 将404路由动态注入,防止第一次没有路由数据跳转到404,
list.push({
path: '*',
name: 'error_404',
meta: {
hideInMenu: true
},
component: () => import('@/view/error-page/404.vue')
})
// 刷新界面菜单
store.commit('updateMenuList', list)
// next() 因为 router 版本的原因,现在改为下面这种方式。
router.push({path:homeName}).catch(err => { console.log(err) })
})
} else {
next()
}
}).catch(() => {
setToken('')
next({
name: 'login'
})
})
}
})
router.afterEach(to => {
setTitle(to, router.app)
iView.LoadingBar.finish()
window.scrollTo(0, 0)
})
export default router