【vue】iView-admin2.0動態菜單路由【版1】

vue項目實現動態路由有倆種方式html

一.前端在routers中寫好--全部--路由表 <前端控制路由>,登陸時根據用戶的角色權限來動態的顯示菜單路由前端

二.前端經過調用接口請求拿到當前用戶--對應權限的--路由表  <後端處理路由返回>,以動態的顯示菜單路由 vue

 

介紹第二種 (參考資料 segmentfault-大師兄)node

左側菜單可經過 ①本地mock假數據 ②easymock假數據 ③從後臺請求返回的數據  方式之一請求而來ios

介紹方式①本地mock假數據git

1.iview-admin的src->mock->data目錄下新增菜單路由數據menus-data.js (字段可參照src->router->routers.js中設置)github

menus-data.jsjson

export const mockMenuData = [
  {
    'path': '/multilevel',
    'name': 'multilevel',
    'meta': {
      'icon': 'md-menu',
      'title': '多級菜單'
    },
    'component': 'Main',
    'children': [
      {
        'path': '/level_2_1',
        'name': 'level_2_1',
        'meta': {
          'icon': 'md-funnel',
          'title': '二級-1'
        },
        'component': 'multilevel/level-2-1'
      },
      {
        'path': '/level_2_2',
        'name': 'level_2_2',
        'meta': {
          'icon': 'md-funnel',
          'showAlways': true,
          'title': '二級-2'
        },
        'component': 'parentView',
        'children': [
          {
            'path': '/level_2_2_1',
            'name': 'level_2_2_1',
            'meta': {
              'icon': 'md-funnel',
              'title': '三級'
            },
            'component': 'multilevel/level-2-2/level-2-2-1'
          },
          {
            'path': '/level_2_2_2',
            'name': 'level_2_2_2',
            'meta': {
              'icon': 'md-funnel',
              'title': '三級'
            },
            'component': 'multilevel/level-2-2/level-2-2-2'
          }
        ]
      },
      {
        'path': '/level_2_3',
        'name': 'level_2_3',
        'meta': {
          'icon': 'md-funnel',
          'title': '二級-3'
        },
        'component': 'multilevel/level-2-3'
      }
    ]
  },
  {
    'path': '/auth',
    'name': 'auth',
    'meta': {
      'icon': 'md-menu',
      'title': '權限設置',
      'access': ['super_admin']
    },
    'component': 'Main',
    'children': [
      {
        'path': '/role',
        'name': 'role',
        'meta': {
          'icon': 'ios-paper-outline',
          'title': '角色'
        },
        'component': 'auth/role',
        'permission': ['add', 'edit']
      },
      {
        'path': '/cmenu',
        'name': 'cmenu',
        'meta': {
          'icon': 'ios-paper-outline',
          'title': '菜單'
        },
        'component': 'auth/cmenu',
        'permission': ['add', 'del']
      },
      {
        'path': '/account',
        'name': 'account',
        'meta': {
          'icon': 'ios-paper-outline',
          'title': '帳號'
        },
        'component': 'auth/account'
      }
    ]
  },
  {
    'path': '/lesmessage',
    'name': 'lesmessage',
    'meta': {
      'icon': 'ios-paper',
      'title': '留言管理'
    },
    'component': 'Main',
    'children': [
      {
        'path': '/list',
        'name': 'list',
        'meta': {
          'icon': 'ios-paper',
          'title': '數據列表'
        },
        'component': 'lesmessage/list'
      }
    ]
  }
]
View Code

2/3/4步驟官方文檔axios

2.src->api->data.js中添加menus-data接口定義(參照iview-admin原有的mock數據接口封裝寫法)segmentfault

export const getMockMenuData = () => {
  return axios.request({
    url: 'get_mock_menu_data',
    method: 'post'
  })
}
View Code

3.src->mock->data.js中添加menus-data請求函數(參照iview-admin原有的mock數據請求函數封裝寫法)

export const getMockMenuData = req => {
  return mockMenuData
}
View Code

4.src->mock->index.js中添加menus-data的Mock導出封裝

import { ... , getMockMenuData } from './data'

.
.
.

Mock.mock(/\/get_mock_menu_data/, getMockMenuData) 
View Code

 以上,mock數據定義好了請求接口get_mock_menu_data

5.src->libs目錄下新增router-util.js ,  用於轉化請求的menus-data數據爲能被vue識別的路由格式

router-util.js

/**
 * ①添
 * @@新增 定義初始化菜單
 */
import store from '@/store'
import { getToken, localSave, localRead } from '@/libs/util'
// import config from '@/config'
import { lazyLoadingCop } from '@/libs/tools'
import { getMockMenuData } from '@/api/data'
import Main from '@/components/main' // Main 是架構組件,不在後臺返回,在文件裏單獨引入
import parentView from '@/components/parent-view' // parentView 是二級架構組件,不在後臺返回,在文件裏單獨引入
const _import = require('@/router/_import_' + process.env.NODE_ENV)// 獲取組件的方法

var gotRouter
// 初始化路由
export const initRouter = (vm) => {
  if (!getToken()) {
    return
  }
  var routerData
  console.log(gotRouter,!gotRouter, vm,store, 'initRouter')
  if (!gotRouter) {
    getMockMenuData().then(res => {
      routerData = res.data // 後臺拿到路由
      localSave('dynamicRouter', JSON.stringify(routerData)) // 存儲路由到localStorage
      gotRouter = filterAsyncRouter(routerData) // 過濾路由,路由組件轉換
      console.log(gotRouter, 'filterAsyncRouter')
      store.commit('updateMenuList', gotRouter);
      dynamicRouterAdd()
    })
  } else {
    gotRouter = dynamicRouterAdd()
  }
  return gotRouter
}

// 加載路由菜單,從localStorage拿到路由,在建立路由時使用
export const dynamicRouterAdd = () => {
  let dynamicRouter = []
  let data = localRead('dynamicRouter')
  if (!data) {
    return dynamicRouter
  }
  dynamicRouter = filterAsyncRouter(JSON.parse(data))
  return dynamicRouter
}

// @函數: 遍歷後臺傳來的路由字符串,轉換爲組件對象
export const filterAsyncRouter = (asyncRouterMap) => {
  const accessedRouters = asyncRouterMap.filter(route => {
    if (route.component) {
      if (route.component === 'Main') { // Main組件特殊處理
        route.component = Main
      } else if (route.component === 'parentView') { // parentView組件特殊處理
        route.component = parentView
      } else {
        // route.component = _import(route.component)
        route.component = lazyLoadingCop(route.component)
      }
    }
    if (route.children && route.children.length) {
      route.children = filterAsyncRouter(route.children)
    }
    return true
  })
  return accessedRouters
}
View Code

附: src->libs->toos.js中添加 引入.vue組件的封裝函數 (不分環境可用)

//  @函數: 引入組件
export const lazyLoadingCop = file => require('@/view/' + file + '.vue').default
View Code

 附:src-->router中新增_import_development.js和_import_production.js爲引入.vue組件的封裝 (倆種環境)

_import_development.js

module.default = file => require('@/view/' + file + '.vue').default // vue-loader at least v13.0.0+
View Code

_import_production.js

module.exports = file => () => import('@/view/' + file + '.vue')
View Code

vux部分updateMenuList更新菜單數據

 附:src->store->module->app.js中的mutations添加 updateMenuList 操做 state中的 menuList:[] (添加) 、   getters中menuList修改的獲取方式

    updateMenuList (state, routes) { // ①添 接受前臺數組,刷新菜單
      router.addRoutes(routes); // 動態添加路由
      state.menuList = routes;
      console.log('①updateMenuList添menuList', this);
    }
View Code
  getters: {
    // menuList: (state, getters, rootState) => getMenuByRouter(routers, rootState.user.access),
    // menuList: (state, getters, rootState) => getMenuByRouter(dynamicRouterAdd(), rootState.user.access), // ①改 經過路由列表獲得菜單列表
    menuList: (state, getters, rootState) => getMenuByRouter(state.menuList, rootState.user.access), // ①改 經過路由列表獲得菜單列表
 ...
  },
View Code

 

src->router->routers.js   主要是左側菜單的加入

import Main from '@/components/main'
import { dynamicRouterAdd } from '@/libs/router-util' // ①添 引入加載菜單

/**
 * iview-admin中meta除了原生參數外可配置的參數:
 * meta: {
 *  title: { String|Number|Function }
 *         顯示在側邊欄、麪包屑和標籤欄的文字
 *         使用'{{ 多語言字段 }}'形式結合多語言使用,例子看多語言的路由配置;
 *         能夠傳入一個回調函數,參數是當前路由對象,例子看動態路由和帶參路由
 *  hideInBread: (false) 設爲true後此級路由將不會出如今麪包屑中,示例看QQ羣路由配置
 *  hideInMenu: (false) 設爲true後在左側菜單不會顯示該頁面選項
 *  notCache: (false) 設爲true後頁面在切換標籤後不會緩存,若是須要緩存,無需設置這個字段,並且須要設置頁面組件name屬性和路由配置的name一致
 *  access: (null) 可訪問該頁面的權限數組,當前路由設置的權限會影響子路由
 *  icon: (-) 該頁面在左側菜單、麪包屑和標籤導航處顯示的圖標,若是是自定義圖標,須要在圖標名稱前加下劃線'_'
 *  beforeCloseName: (-) 設置該字段,則在關閉當前tab頁時會去'@/router/before-close.js'裏尋找該字段名對應的方法,做爲關閉前的鉤子函數
 * }
 */
// 不做爲Main組件的子頁面展現的頁面單獨寫
export const otherRouter = [{
  path: '/login',
  name: 'login',
  meta: {
    title: 'Login - 登陸',
    hideInMenu: true
  },
  component: () => import('@/view/login/login.vue')
}, {
  path: '/401',
  name: 'error_401',
  meta: {
    hideInMenu: true
  },
  component: () => import('@/view/error-page/401.vue')
}, {
  path: '/500',
  meta: {
    title: '500-服務端錯誤'
  },
  name: 'error_500',
  component: () => import('@/view/error-page/500.vue')
}
];

// 做爲Main組件的子頁面展現可是不在左側菜單顯示的路由寫在mainRouter裏
export const mainRouter = [{
  path: '/',
  name: '_home',
  redirect: '/home',
  component: Main,
  meta: {
    hideInMenu: true,
    notCache: true
  },
  children: [
    {
      path: '/home',
      name: 'home',
      meta: {
        hideInMenu: true,
        title: '首頁',
        notCache: true,
        icon: 'md-home'
      },
      component: () => import('@/view/single-page/home')
    }
  ]
}, {
  path: '/message',
  name: 'message',
  component: Main,
  meta: {
    hideInBread: true,
    hideInMenu: true
  },
  children: [
    {
      path: 'message_page',
      name: 'message_page',
      meta: {
        icon: 'md-notifications',
        title: '消息中心'
      },
      component: () => import('@/view/single-page/message/index.vue')
    }
  ]
}];

// 做爲Main組件的子頁面展現而且在左側菜單顯示的路由寫在appRouter裏
export const appRouter = [...dynamicRouterAdd()];

export const routes = [
  ...otherRouter,
  ...mainRouter,
  ...appRouter
]

// 全部上面定義的路由都要寫在下面輸出
export default routes
View Code

 src->main.js 實例化對象 添加掛載時的動態路由調用

.
.
.
import { initRouter } from '@/libs/router-util' // ①新增  引入動態菜單渲染
.
.
.
new Vue({
.
.
.
  mounted() {
    initRouter(this);  // ①新增 調用方法,動態生成路由
  },
.
})
View Code

登陸菜單未渲染,可在路由跳轉前執行一次initRouter (此方案捨去)  src->router->routers.js

import { initRouter } from '@/libs/router-util'
.
.
.

const turnTo = (to, access, next) => {
  initRouter();
  ...
};
View Code

登陸菜單未渲染,可在路由跳轉前執行一次initRouter(此方案體驗更佳,解決附加6的問題)   src->router->routers.js     

import { InitRouter } from '@/libs/router-util'
...
router.beforeEach((to, from, next) => {
  .
  if (!token && to.name !== LOGIN_PAGE_NAME) {
    // 未登陸且要跳轉的頁面不是登陸頁
      .
  } else if (!token && to.name === LOGIN_PAGE_NAME) {
    // 未登錄且要跳轉的頁面是登陸頁
    InitRouter()  // 登陸頁刷新從新獲取,確保路由跳轉前即beforeEach獲取到動態路由表
    next() // 跳轉
  } else if (token && to.name === LOGIN_PAGE_NAME) {
    // 已登陸且要跳轉的頁面是登陸頁
      .
  } else {
     .
    }
})
View Code

以上。動態載入請求本地mock菜單路由結束。

介紹方式 ②easymock假數據

1.使用easy-mock,建立項目,複製項目Base URL

2.配置proxy (在vue.config.js中) 

  devServer: {
    proxy: {
      '/mock': {   // easymock跨域請求配置
        target: 'easy-mock項目的Base URL',
        changeOrigin: true,
        pathRewrite: {'^/mock': '/'}
      }
    }
  }
View Code

3.配置package.json腳本

"easy-mock": "node build/dev-server.js mock",

4.配置api請求基礎路徑baseUrl(在src->config->index.js中)

  /**
   * @description api請求基礎路徑
   */
  baseUrl: {
    dev: '步驟2中的easymock項目地址Base URL',
    pro: 'https://produce.com'
  },
View Code

以後就是接口封裝和使用了。(可參照iview-admin本來寫mock假數據的封裝寫法及使用)

5.附加 easy-mock工具使用

6.附加  vue 解決addRoutes動態添加路由後,刷新失效問題   

備註:這篇文章屬於一邊摸索一邊寫下的,有出現問題會修改一些步驟,基本原理已經表現。

在此文章基礎上加寫一篇,iView-admin2.0動態菜單路由【版2】,解決這篇文章中忽略的/沒講清的點。

相關文章
相關標籤/搜索