後端生成當前用戶相應的路由後由前端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