权限控制方式
路由完全由后端返回
确认控制逻辑

约定后端返回数据格式
children: [ // 子路由放入children属性中
{
children: []
component: "views/index/content/index"
id: 5
meta: {isLogin: 1, icon: "el-icon-postcard", title: "xx导航子菜单", isShow: 1}
name: "/home/index/content"
path: "/home/index/content"
}
]
component: "views/index" // 对应页面的文件地址
id: 3 // 唯一属性
// isLogin判断是否需要登录访问,isShow判断是否显示在导航栏上,title展示导航栏名字
meta: {isLogin: 1, icon: "el-icon-date", title: "xx导航", isShow: 1}
name: "index" // 组件名
path: "index" // 路由地址
vue页面配置
1. APP.vue 配置
<template>
<div id="app">
<router-view />
</div>
</template>
2.首页homepage设置
根据父子路由的配置,子路由必须要在有rout-view情况下才能切换,所以需要在首页设置导航栏,还有导航栏路由对应的页面
类似这种布局
<template>
<div class="main">
<div class="main-left">
<!-- 放置导航栏菜单组件 -->
</div>
<div class="main-right">
<!-- 右边内容 -->
<div class="main-right-container">
<router-view></router-view>
</div>
</div>
</div>
</template>
登录页设置
引入vuex数据
computed: {
...mapGetters([
'menuList'
])
},
methods: {
...mapActions([
'getMenuList',
]),
}
// 登录后触发的函数
login() {
let url='/你的api地址';
let reqData={};
let res = await this.$Http.axiosPost(url, reqData); // 这里使用axios,晚点出一篇axios配置到原型链的文章
if (res.code === 200) {
// 存登录状态
localStorage.setItem('Flag','isLogin');
// 存登录token
localStorage.setItem('TOKEN',res.obj);
// 调用mapGetters,查看vuex中是否有路由数据
let code = await this.getMenuList();
if(code === 200){ // 存在路由
// 跳转导航栏首页
let Path= this.menuList[0].children[0].children[0].path
this.$router.push({path: Path})
}else{ // 不存在路由,说明配置问题了
console.log('路由获取失败', code)
MessageBox({
title: '警告',
message: '导航配置问题,请联系管理员',
callback: action => { // 回调函数
localStorage.clear(); // 清除缓存数据
next({path: '/login', replace: true }) // 跳转登录
}
})
}
}
}
配置vuex
import Vue from 'vue'
import Vuex from 'vuex'
import { normalRouter } from '../router'
import createLogger from 'vuex/dist/logger'
Vue.use(Vuex)
// 设置只有在开发环境的时候才打印日志
const debug = process.env.NODE_ENV !== 'production'
const state = {
menuList: []
}
const getters = {
menuList: state => state.menuList
}
const mutations = {
setMenuList (state, value) {
// 存入数据
state.menuList = menus;
// 加入登录页面等一级路由
menus = menus.concat(normalRouter)
// matcher是用来解决 Duplicate named routes definition 报错解决路由重复
VueRouter.matcher = new Router().matcher
// 错误页面要最后添加,不然访问动态的路由会先出现404
menus.push({
path: '*', // 页面不存在的情况下会跳到404页面
redirect: '/error',
})
VueRouter.addRoutes(menus)
}
}
const actions = {
// 获取动态路由
async getMenuList({commit}) {
// 通过接口获取路由数据
let url='/你的api地址';
let reqData={};
let res = await this.$Http.axiosPost(url, reqData); // 这里使用axios,晚点出一篇axios配置到原型链的文章
if(res.code === 200){ // 成功
// res.obj[0].component = "views/homepage"
let menus = needRoutes(res.obj) // 生成路由信息
// 1. 定义homepage文件夹为首页
// 2. 获取的路由都是/home下面的子路由,所以路由数据获取后,放入children里面
commit('setMenuList', [{
path: "/home",
component:(resolve) =>{
require(['@/views/homepage'],resolve)
},
meta: { isLogin: true // 添加该字段,表示进入这个路由是需要登录的
},
children:menus
}])
return res.code
} else { // 失败
return res.code
}
}
}
// 生成路由数据
let needRoutes = (data) => {
// 判断是否是数组
if (!Array.isArray(data)) {
return new TypeError('arr must be an array.');
}
let arr = formatComponent(data)
return arr;
}
// 递归函数,用来对组件和路由关联,进行异步渲染
let formatComponent = (data) => {
for (let obj of data) {
const component = obj.component
// 把后台返回的路由参数,拼接路径
obj.component = resolve => { require(['@/' + component], resolve) }
if (obj.children.length > 0) { // children有长度,说明有子路由
formatComponent(obj.children)
}
}
return data
}
const store = new Vuex.Store({
state,
getters,
mutations,
actions,
strict: debug,
plugins: debug ? [createLogger()] : []
})
export default store
在router页面构建导航守卫
1. 确认默认路由
// 配置一级路由配置和重定向
// login页面和error页面是不需要登录判断的,可以放在默认路由内
export const normalRouter = [
{
path: "/",
redirect: '/login',
// meta: {
// isLogin: false, // 添加该字段,表示进入这个路由是需要登录的
// title:'登录'
// }
},
{
path: "/login",
name: "Login",
// components:require('./views/login'),
component: resolve => require (['@/views/login'], resolve),
meta: {
isLogin: false, // 添加该字段,表示进入这个路由是需要登录的
title:'登录'
}
},
{
path: "/home",
redirect: '/home/index',
},
{
path: "/error",
name: "error",
component: error,
meta: {
isLogin: false, // 添加该字段,表示进入这个路由是需要登录的
title:'错误'
}
},
]
2. 导入路由中
let router = new Router({
mode: "history",
base: process.env.BASE_URL,
routes:normalRouter
});
3. 配置全局路由导航守卫
// 因为用到了element ui,所以要引入一下
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
router.beforeEach( async (to, from, next) => {
console.log('before路由', to);
// 获取缓存的登录状态
let hasLogin = localStorage.getItem("Flag")
// 有登录状态
if (hasLogin) {
console.log('state', store.state.menuList.length, to, from)
// 判断vuex中是否有存入路由数据
if (store.state.menuList.length === 0) {
// 进入到这一步用户已经登录过,但是又刷新了浏览器,导致路由清空了,所以要在vuex中重新请求路由
let res = await store.dispatch('getMenuList')
// code 不为200 时候,说明路由接口报错,需要重新登录
if (res !== 200) {
// element 提示框
MessageBox({
title: '警告',
message: '导航配置问题,请联系管理员',
callback: action => { // 回调函数
// 清除登录缓存数据
localStorage.clear();
// 跳转登录页
next({path: '/login', replace: true })
}
})
} else { // 有路由数据
// router.addRoutes是异步的,所以把全局的跳转 *也动态添加了,同时使用 next({ ...to, replace: true })重新载入
next({...to, replace: true })
}
}
if (to.name === 'Login') { // 已经登录过,但是访问的是login,强制跳转至首页
// 这里设置的是vuex中首个导航栏的path地址
next({
path: recursiveRouter(store.state.menuList[0]),
})
} else { // 正常跳转
next()
}
} else { // 没有登录,则强制跳转登录页
console.log('to', to)
// 没有登录想访问其他页面,跳转至
if (to.name !== 'Login') {
next({
path: '/login',
})
} else {
// 当匹配不到路由,则通过递归函数进入路由第一个页面
if (to.name === 'error') {
let path = recursiveRouter(store.state.menuList[0])
next({
path: path
})
} else {
next()
}
}
}
})
// 递归路由,确认第一个路由地址
let recursiveRouter = (data) => {
if (data.children && data.children.length > 0) {
return recursiveRouter(data.children[0])
} else {
return data.path
}
}
总结
暂时来说,这是一份能大部分覆盖业务场景的通用权限控制,具体的路由跳转需要结合自己的业务场景来进行修改,有什么疑问或者建议可以在评论区写出