后端生成当前用户相应的路由后由前端addRoutes动态加载路由,小编写了个栗子,能对小伙伴有些帮助,已经发到github上了,vue请求接口动态添加路由css
在遇到的一些问题,我都在写在的代码注释中html
import Vue from 'vue' import App from './admin.vue' import store from './store' import router from './router' import '@/style/reset.css' // 把请求方法也挂在到原型上 import * as types from './http/http.js' Vue.prototype.$http = types // 将自定义工具扩展到原型上 import utils from '@/util/utils.js' Vue.prototype.$utils = utils // 使用ElementUI import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI); Vue.config.productionTip = false new Vue({ store, router, render: h => h(App), }).$mount('#app') 复制代码
先从登陆开始就从登陆看起前端
<template> <div> <el-input v-model="name" placeholder="请输入内容"></el-input> <el-input v-model="pas" placeholder="请输入内容"></el-input> <el-button type="primary" @click="btnLogin">登陆</el-button> </div> </template> <script> import { mapState, mapMutations, mapActions } from 'vuex' export default { data(){ return { name:'123456', pas:'123456' } }, methods:{ ...mapActions([ 'getMenuList', 'getUserInfo' ]), // async await 同步写法 async btnLogin(){ // 获取用户信息 let userInfo = await this.getUserInfo(); if(userInfo && userInfo.name){ // 获取后台返回路由 let menuList = await this.getMenuList(); if(menuList){ // 跳转动态添加后的动态路由 this.$router.push({path:'/home'}) }else{ console.log('出错提示') } } } } } </script> <style lang="less" scoped> button{ width:100%; } </style> 复制代码
import Vue from "vue"; import VueRouter from "vue-router"; import store from '../store' Vue.use(VueRouter); // 默认路由 const defaultRouter = [{ path: "/", name: "index", redirect: "/main", meta: { title: "main", icon: '', shows: false }, }, { path: "/main", name: "main", component: resolve => require(["@/views/admin/src/layout/main.vue"], resolve), meta: { title: "主页", icon: '', shows: true }, children: [] }, { path: "/login", name: "login", component: resolve => require(["@/views/admin/src/login/index.vue"], resolve), meta: { title: "登陆", icon: '', shows: true }, } ] // 建立实例 const router = new VueRouter({ routes: defaultRouter, mode: "hash", base: __dirname, //好比设置 base: test 路由连接解析后 test/#/views-b strict: process.env.NODE_ENV !== "production" }); // 路由全局拦截 router.beforeEach( async (to, from, next) => { let hasLogin = localStorage.getItem("hasLogin") if (hasLogin) { if (!store.state.menuList.length) { // 进入到这一步用户已经登陆过,可是又刷新了浏览器,致使路由清空了 // 因此要从新请求路由,其实也阔以把路由存在路由localStorage中,我这里为了演示同步的写法,也能够达到一样的目的 await store.dispatch('getMenuList') // router.addRoutes是异步的,因此把全局的跳转 *也动态添加了,同时使用 next({ ...to, replace: true })从新载入 next({...to, replace: true }) } // 已经登陆过访问的是login,跳转至home if (to.name === 'login') { next({ path: '/home', }) } else { next() } } else { // 没有登陆想访问其余页面,跳转至 if (to.name !== 'login') { next({ path: '/login', }) } else { next() } } }) export default router; 复制代码
import Vuex from 'vuex' import Vue from 'vue' import VueRouter from '../router' import { defaultRouter } from '../router/admin' import { getRole, getUserinfo } from '@/views/admin/http/api' import app from '../config/app' Vue.use(Vuex); export default new Vuex.Store({ state: { userInfo: {}, hasLogin: false, // 表示没有获取过权限,获取完毕后,把状态改为true menuList: [], // 存放菜单数据 }, getters: { }, mutations: { setMenuList(state, menus) { state.menuList = menus; // 把请求的接口放在mian下面 defaultRouter[1].children = menus; // 添加到router当中 VueRouter.addRoutes(defaultRouter); // VueRouter.options.routes = VueRouter.options.routes.concat(defaultRouter) // 解决组件中 this.$router.options.routes 获取不到新添加的router }, setUserInfo(state, user) { // 更新用户信息 state.userInfo = user; state.hasLogin = true; localStorage.setItem("hasLogin", state.hasLogin); // 存储登陆过权限 } }, actions: { // 会调用setMenuList 生成路由并添加路由 async getMenuList({ commit }) { let { data } = await getRole(); // 请求后端接口拉取路由 let menus = needRoutes(data.menus) // 生成路由信息 commit('setMenuList', menus) // actions调用mutations return menus; }, async getUserInfo({ dispatch, commit }) { let { data } = await getUserinfo(); // 获取用户信息 commit('setUserInfo', data); // 更新Vuex-setUserInfo return data } } }) // 生成路由数据 function needRoutes(data) { // 判断是不是数组 if (!Array.isArray(data)) { return new TypeError('arr must be an array.'); } let arr = []; for (let obj of data) { const component = obj.component // 把后台返回的路由参数,拼接路径 obj.component = resolve => { require(['@/' + component + '.vue'], resolve) } arr.push(obj) } // 考虑到后台排序 进行排序 compare 是我封装的数组对象排序的方法 arr.sort(Vue.prototype.$utils.compare('sort')) // 根据后台返回数据sort字段进行排序 arr = Vue.prototype.$utils.toTree(arr) // 生成树状结构,为后面生成左侧菜单准备 arr.push(app.defaultErr) // 404路由须要最后添加,否则访问动态的路由会出现404 return arr; } 复制代码
<template> <div class="main"> <section class="main-left"> <!-- 左边菜单 --> <sidebar></sidebar> </section> <section class="main-right"> <!-- 右边头部 --> <div class="main-right-head"> <rightHead></rightHead> </div> <!-- 右边内容 --> <div class="main-right-container"> <div class="app-main"> <router-view></router-view> </div> </div> </section> </div> </template> <script> // 能够看github import sidebar from './sidebar.vue' // https://github.com/hangjob/vue-admin/blob/master/src/views/admin/src/layout/sidebar.vue import rightHead from './header.vue' // https://github.com/hangjob/vue-admin/blob/master/src/views/admin/src/layout/treeMenus.vue export default { components:{ sidebar, rightHead } } </script> <style lang="less" scoped> </style> 复制代码
let express = require('express'); let app = express(); //在后端配置,cors跨域 app.use('*', function(req, res, next) { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Headers', 'Content-Type'); res.header('Access-Control-Allow-Methods', '*'); res.header('Content-Type', 'application/json;charset=utf-8'); next(); }); app.get('/role', (req, res) => { res.json({ menus: [ { id: 1, sort: 1, path: '/home', component: 'views/admin/src/home/index', meta: { title: "首页", icon: 'el-icon-grape', shows: true }, pid: '' }, { id: 2, sort: 8, path: '/financial', component: 'views/admin/src/financial/index', meta: { title: "财务管理", icon: 'el-icon-refrigerator', shows: true }, pid: '' }, { id: 3, sort: 1, path: '/certification', component: 'views/admin/src/certification/index', meta: { title: "认证资质", icon: 'el-icon-watermelon', shows: true }, pid: '' }, { id: 4, sort: 1, path: '/integrated', component: 'views/admin/src/integrated/index', meta: { title: "综合管理", icon: 'el-icon-cherry', shows: true }, pid: '' } , { id: 5, sort: 6, path: '/project', component: 'views/admin/src/project/index', meta: { title: "项目管理", icon: 'el-icon-apple', shows: true }, pid: '' } ] }) }) app.get('/userinfo', (req, res) => { res.json({ name: '羊先生', mesg: 'admin' }) }) //监听3000端口 app.listen(3001); // 启动node 执行该文件 复制代码
先定义一份公共的路由表,里面仅有一些公共的路由,如 login
登陆成功后
获取用户路由列表,既然是后端返回路由,因此后端就知道该用户的路由信息权限,返回相应的路由列表
获取到路由列表,把这份路由表动态添加到router中便可
404页面须要最后添加,否则刷新页面请求的异步路由,会出现404
当用户刷新后,若是路由存vuex
会消失,能够存在localStorage
,当用户失去权限后清除localStorage
相关信息,我这里是采用同步再次获取获取路由列表
vue
代码已上传githubnode