Browse Source

添加动态(菜单)路由

daxiongYang 7 years ago
parent
commit
80d6867603

+ 2 - 2
src/mock/index.js

@ -8,7 +8,7 @@ import * as config from './modules/config'
8 8
import * as oss from './modules/oss'
9 9
import * as schedule from './modules/schedule'
10 10
11
console.log('\n%c!<-------------------- 接口拦截, mock模拟数据 s -------------------->', 'color:red')
11
console.log('\n%c!<-------------------- 接口拦截, mock模拟数据 s -------------------->', 'color:blue')
12 12
13 13
// tips
14 14
// 1. 关闭[业务模块集]拦截, create方法[第2个参数]设置. (默认开启)
@ -23,4 +23,4 @@ create(config, false)      // 参数管理
23 23
create(oss, false)         // 文件服务
24 24
create(schedule, false)    // 定时任务
25 25
26
console.log('%c!<-------------------- 接口拦截, mock模拟数据 e -------------------->\n', 'color:red')
26
console.log('%c!<-------------------- 接口拦截, mock模拟数据 e -------------------->\n\n', 'color:blue')

+ 103 - 45
src/router/index.js

@ -1,9 +1,11 @@
1
// 路由定义使用说明:
2
// 1. 代码中路由统一使用name属性跳转.
3
// 2. 开放path属性用做简短路由, 比如: '/a1', 访问地址: www.renren.io/#/a1
1
/**
2
 * 全站路由配置
3
 *
4
 * 建议:
5
 * 1. 代码中路由统一使用name属性跳转(不使用path属性)
6
 */
4 7
import Vue from 'vue'
5 8
import Router from 'vue-router'
6
import { currentRouteIsDefault, addMenuListRoutes } from './utils'
7 9
import http from '@/utils/http'
8 10
9 11
Vue.use(Router)
@ -11,52 +13,45 @@ Vue.use(Router)
11 13
// 开发环境不使用懒加载, 因为懒加载页面太多的话会造成webpack热更新太慢, 所以只有开发环境使用懒加载
12 14
const _import = require('./import-' + process.env.NODE_ENV)
13 15
16
// 全局路由(无需嵌套上左右整体布局)
17
const globalRoutes = [
18
  { path: '/404', component: _import('common/404'), name: '404', desc: '404未找到' },
19
  { path: '/login', component: _import('common/login'), name: 'login', desc: '登录' }
20
]
21
22
// 主入口路由(需嵌套上左右整体布局)
23
const mainRoutes = {
24
  path: '/',
25
  component: _import('main'),
26
  name: 'main',
27
  redirect: { name: 'home' },
28
  desc: '主入口整体布局',
29
  children: [
30
    { path: '/home', component: _import('common/home'), name: 'home', desc: '首页' },
31
    { path: '/theme', component: _import('common/theme'), name: 'theme', desc: '主题' }
32
  ],
33
  beforeEnter (to, from, next) {
34
    let token = Vue.cookie.get('token')
35
    if (!token || !/\S/.test(token)) {
36
      next({ name: 'login' })
37
    }
38
    next()
39
  }
40
}
41
14 42
const router = new Router({
15 43
  mode: 'hash',
16 44
  scrollBehavior: () => ({ y: 0 }),
17
  isAddMenuListRoutes: false,
18
  routes: [
19
    { path: '/404', component: _import('error/404'), name: '404', desc: '404未找到' },
20
    { path: '/login', component: _import('login/index'), name: 'login', desc: '登录' },
21
    {
22
      path: '/',
23
      component: _import('main'),
24
      name: 'main',
25
      redirect: { name: 'home' },
26
      desc: '主入口整体布局',
27
      children: [
28
        { path: '/home', component: _import('home/index'), name: 'home', desc: '首页' },
29
        { path: '/theme', component: _import('theme/index'), name: 'theme', desc: '主题' }
30
        // { path: '/schedule', component: _import('modules/schedule/index'), name: 'schedule', desc: '定时任务', meta: { isTab: true } },
31
        // { path: '/oss', component: _import('modules/oss/index'), name: 'oss', desc: '文件上传', meta: { isTab: true } },
32
        // { path: '/config', component: _import('modules/config/index'), name: 'config', desc: '参数管理', meta: { isTab: true } },
33
        // { path: '/log', component: _import('modules/log/index'), name: 'log', desc: '系统日志', meta: { isTab: true } },
34
        // { path: '/menu', component: _import('modules/menu/index'), name: 'menu', desc: '菜单管理', meta: { isTab: true } },
35
        // { path: '/role', component: _import('modules/role/index'), name: 'role', desc: '角色管理', meta: { isTab: true } },
36
        // { path: '/user', component: _import('modules/user/index'), name: 'user', desc: '管理员管理', meta: { isTab: true } }
37
      ],
38
      beforeEnter (to, from, next) {
39
        let token = Vue.cookie.get('token')
40
        if (!token || !/\S/.test(token)) {
41
          next({ name: 'login' })
42
        }
43
        next()
44
      }
45
    }
46
    // { path: '*', redirect: { name: '404' } }
47
  ]
45
  isAddDynamicRoutes: false, // 是否已经添加动态(菜单)路由
46
  routes: globalRoutes.concat(mainRoutes)
48 47
})
49 48
50
console.log(router)
51
52 49
router.beforeEach((to, from, next) => {
53
  console.log(router.options.isAddMenuListRoutes)
54
  if (router.options.isAddMenuListRoutes || currentRouteIsDefault(to, router.options.routes.filter(item => item.path !== '/'))) {
50
  // 添加动态(菜单)路由
51
  // 1. 已经添加 or 全局路由, 直接访问
52
  // 2. 获取菜单列表, 添加并保存本地存储
53
  if (router.options.isAddDynamicRoutes || fnCurrentRouteType(to) === 'global') {
55 54
    next()
56
  } else if (sessionStorage.getItem('menuList')) {
57
    router.addRoutes(addMenuListRoutes(JSON.parse(sessionStorage.getItem('menuList'))))
58
    router.options.isAddMenuListRoutes = true
59
    next({ ...to, replace: true })
60 55
  } else {
61 56
    http({
62 57
      url: http.adornUrl('/sys/menu/nav'),
@ -64,8 +59,8 @@ router.beforeEach((to, from, next) => {
64 59
      params: http.adornParams()
65 60
    }).then(({data}) => {
66 61
      if (data && data.code === 0) {
67
        router.addRoutes(addMenuListRoutes(data.menuList))
68
        router.options.isAddMenuListRoutes = true
62
        fnAddDynamicRoutes(data.menuList)
63
        router.options.isAddDynamicRoutes = true
69 64
        sessionStorage.setItem('menuList', JSON.stringify(data.menuList || '[]'))
70 65
        sessionStorage.setItem('permissions', JSON.stringify(data.permissions || '[]'))
71 66
      } else {
@ -77,4 +72,67 @@ router.beforeEach((to, from, next) => {
77 72
  }
78 73
})
79 74
75
/**
76
 * 判断当前路由类型, global: 全局路由, main: 主入口路由
77
 * @param {*} route 当前路由
78
 */
79
function fnCurrentRouteType (route) {
80
  var temp = []
81
  for (var i = 0; i < globalRoutes.length; i++) {
82
    if (route.path === globalRoutes[i].path) {
83
      return 'global'
84
    } else if (globalRoutes[i].children && globalRoutes[i].children.length >= 1) {
85
      temp = temp.concat(globalRoutes[i].children)
86
    }
87
  }
88
  return temp.length >= 1 ? fnCurrentRouteType(route, temp) : 'main'
89
}
90
91
/**
92
 * 添加动态(菜单)路由
93
 * @param {*} menuList 菜单列表
94
 * @param {*} routes 递归创建的动态(菜单)路由
95
 */
96
function fnAddDynamicRoutes (menuList = [], routes = []) {
97
  var temp = []
98
  for (var i = 0; i < menuList.length; i++) {
99
    if (menuList[i].list && menuList[i].list.length >= 1) {
100
      temp = temp.concat(menuList[i].list)
101
    } else if (/\S/.test(menuList[i].url)) {
102
      var route = {
103
        path: menuList[i].url.replace('/', '-'),
104
        component: null,
105
        name: menuList[i].url.replace('/', '-'),
106
        desc: menuList[i].name,
107
        meta: { menuId: menuList[i].menuId, isTab: true }
108
      }
109
      // url以http[s]?开头, 通过iframe展示
110
      if (/^http[s]?:\/\/.*/.test(menuList[i].url)) {
111
        route['path'] = `i-${menuList[i].menuId}`
112
        route['name'] = `i-${menuList[i].menuId}`
113
        route['meta']['isIframe'] = true
114
      } else {
115
        try {
116
          route['component'] = _import(`modules/${menuList[i].url}`) || null
117
        } catch (e) {}
118
      }
119
      routes.push(route)
120
    }
121
  }
122
  if (temp.length >= 1) {
123
    fnAddDynamicRoutes(temp, routes)
124
  } else {
125
    mainRoutes.name = 'main-dynamic'
126
    mainRoutes.children = routes
127
    router.addRoutes([
128
      mainRoutes,
129
      { path: '*', redirect: { name: '404' } }
130
    ])
131
    sessionStorage.setItem('dynamicRoutes', JSON.stringify(mainRoutes.children || '[]'))
132
    console.log('\n%c!<-------------------- 动态(菜单)路由 s -------------------->', 'color:blue')
133
    console.log(mainRoutes.children)
134
    console.log('%c!<-------------------- 动态(菜单)路由 e -------------------->\n\n', 'color:blue')
135
  }
136
}
137
80 138
export default router

+ 0 - 67
src/router/utils.js

@ -1,67 +0,0 @@
1
import Vue from 'vue'
2
const _import = require('./import-' + process.env.NODE_ENV)
3
4
/**
5
 * 判断当前路由是否为默认路由
6
 * true: 默认路由, false: 动态菜单路由
7
 * @param {*} currentRoute
8
 * @param {*} defaultRouteList
9
 */
10
export function currentRouteIsDefault (currentRoute, defaultRouteList) {
11
  var temp = []
12
  for (var i = 0; i < defaultRouteList.length; i++) {
13
    if (currentRoute.path === defaultRouteList[i].path) {
14
      return true
15
    } else if (defaultRouteList[i].children && defaultRouteList[i].children.length >= 1) {
16
      temp = temp.concat(defaultRouteList[i].children)
17
    }
18
  }
19
  return temp.length >= 1 ? currentRouteIsDefault(currentRoute, temp) : false
20
}
21
22
/**
23
 * 添加动态菜单路由
24
 * @param {*} menuList
25
 */
26
export function addMenuListRoutes (menuList = [], routes = []) {
27
  var temp = []
28
  var tempRouteName = ''
29
  for (var i = 0; i < menuList.length; i++) {
30
    if (menuList[i].list && menuList[i].list.length >= 1) {
31
      temp = temp.concat(menuList[i].list)
32
    } else {
33
      tempRouteName = getRouteNameByUrl(menuList[i].url)
34
      if (/\S/.test(tempRouteName) && tempRouteName !== 'sql') {
35
        routes.push({
36
          path: tempRouteName,
37
          component: _import(`modules/${tempRouteName}/index`),
38
          name: tempRouteName,
39
          desc: tempRouteName,
40
          meta: { isTab: true }
41
        })
42
      }
43
    }
44
  }
45
  return temp.length >= 1 ? addMenuListRoutes(temp, routes) : [
46
    {
47
      path: '/',
48
      component: _import('main'),
49
      redirect: { name: 'home' },
50
      desc: '主入口整体布局',
51
      children: routes,
52
      beforeEnter (to, from, next) {
53
        let token = Vue.cookie.get('token')
54
        if (!token || !/\S/.test(token)) {
55
          next({ name: 'login' })
56
        }
57
        next()
58
      }
59
    },
60
    { path: '*', redirect: { name: '404' } }
61
  ]
62
}
63
64
function getRouteNameByUrl (url) {
65
  let val = /.*\/(.*)\.html/.exec(url)
66
  return val && val.length >= 1 ? val[1] : ''
67
}

+ 0 - 9
src/utils/index.js

@ -6,15 +6,6 @@ export function isAuth (key) {
6 6
  return JSON.parse(sessionStorage.getItem('permissions') || '[]').indexOf(key) !== -1 || false
7 7
}
8 8
9
/**
10
 * 获取路由名称, 根据url地址
11
 * @param {*} url
12
 */
13
export function getRouteNameByUrl (url) {
14
  let val = /.*\/(.*)\.html/.exec(url)
15
  return val && val.length >= 1 ? val[1] : ''
16
}
17
18 9
/**
19 10
 * 树形数据转换
20 11
 * @param {*} data

src/views/error/404.vue → src/views/common/404.vue


src/views/home/index.vue → src/views/common/home.vue


src/views/login/index.vue → src/views/common/login.vue


src/views/theme/index.vue → src/views/common/theme.vue


+ 17 - 18
src/views/main-sidebar-sub-menu.vue

@ -1,8 +1,5 @@
1 1
<template>
2
  <el-submenu
3
    v-if="menu.list && menu.list.length >= 1"
4
    :data-idx="menu.menuId + ''"
5
    :index="menu.menuId + ''">
2
  <el-submenu v-if="menu.list && menu.list.length >= 1" :index="menu.menuId + ''">
6 3
    <template slot="title">
7 4
      <icon-svg :name="menu.icon" class="site-sidebar__menu-icon"></icon-svg>
8 5
      <span>{{ menu.name }}</span>
@ -10,14 +7,11 @@
10 7
    <sub-menu
11 8
      v-for="item in menu.list" 
12 9
      :key="item.menuId"
13
      :menu="item">
10
      :menu="item"
11
      :dynamicRoutes="dynamicRoutes">
14 12
    </sub-menu>
15 13
  </el-submenu>
16
  <el-menu-item
17
    v-else
18
    :index="menu.menuId + ''"
19
    :data-idx="menu.menuId + ''"
20
    @click="gotoRouteHandle(menu.url)">
14
  <el-menu-item v-else :index="menu.menuId + ''" @click="gotoRouteHandle(menu)">
21 15
    <icon-svg :name="menu.icon" class="site-sidebar__menu-icon"></icon-svg>
22 16
    <span>{{ menu.name }}</span>
23 17
  </el-menu-item>
@ -25,22 +19,27 @@
25 19
26 20
<script>
27 21
  import SubMenu from './main-sidebar-sub-menu'
28
  import { getRouteNameByUrl } from '@/utils'
29 22
  export default {
30 23
    name: 'sub-menu',
31 24
    props: {
32
      menu: Object,
33
      required: true
25
      menu: {
26
        type: Object,
27
        required: true
28
      },
29
      dynamicRoutes: {
30
        type: Array,
31
        required: true
32
      }
34 33
    },
35 34
    components: {
36 35
      SubMenu
37 36
    },
38 37
    methods: {
39
      // 跳转到菜单对应路由
40
      gotoRouteHandle (url) {
41
        var routeName = getRouteNameByUrl(url)
42
        if (/\S/.test(routeName)) {
43
          this.$router.push({ name: routeName })
38
      // 通过menuId与动态(菜单)路由进行匹配跳转至指定路由
39
      gotoRouteHandle (menu) {
40
        var route = this.dynamicRoutes.filter(item => item.meta.menuId === menu.menuId)
41
        if (route.length >= 1) {
42
          this.$router.push({ name: route[0].name })
44 43
        }
45 44
      }
46 45
    }

+ 13 - 31
src/views/main-sidebar.vue

@ -13,7 +13,8 @@
13 13
        <sub-menu
14 14
          v-for="menu in menuList" 
15 15
          :key="menu.menuId"
16
          :menu="menu">
16
          :menu="menu"
17
          :dynamicRoutes="dynamicRoutes">
17 18
        </sub-menu>
18 19
      </el-menu>
19 20
    </div>
@ -22,11 +23,11 @@
22 23
23 24
<script>
24 25
  import SubMenu from './main-sidebar-sub-menu'
25
  import { getRouteNameByUrl } from '@/utils'
26 26
  import isEmpty from 'lodash/isEmpty'
27 27
  export default {
28 28
    data () {
29 29
      return {
30
        dynamicRoutes: []
30 31
      }
31 32
    },
32 33
    components: {
@ -65,33 +66,8 @@
65 66
      }
66 67
    },
67 68
    watch: {
68
      $route: 'routeHandle'
69
    },
70
    created () {
71
      this.getMenuList().then(() => {
72
        this.routeHandle(this.$route)
73
      })
74
    },
75
    methods: {
76
      // 获取菜单列表 / 权限
77
      getMenuList () {
78
        return this.$http({
79
          url: this.$http.adornUrl('/sys/menu/nav'),
80
          method: 'get',
81
          params: this.$http.adornParams()
82
        }).then(({data}) => {
83
          if (data && data.code === 0) {
84
            this.menuList = data.menuList
85
            sessionStorage.setItem('permissions', JSON.stringify(data.permissions || '[]'))
86
          } else {
87
            this.menuList = []
88
            sessionStorage.setItem('permissions', '[]')
89
          }
90
        })
91
      },
92
      // 路由操作
93
      routeHandle (route) {
94
        if (route.meta && route.meta.isTab) {
69
      '$route' (route) {
70
        if (route.meta.isTab) {
95 71
          var tab = this.mainTabs.filter(item => item.name === route.name)[0]
96 72
          // tab不存在, 先添加
97 73
          if (isEmpty(tab)) {
@ -112,14 +88,20 @@
112 88
          this.menuActiveName = tab.id + ''
113 89
          this.mainTabsActiveName = route.name
114 90
        }
115
      },
91
      }
92
    },
93
    created () {
94
      this.menuList = JSON.parse(sessionStorage.getItem('menuList') || '[]')
95
      this.dynamicRoutes = JSON.parse(sessionStorage.getItem('dynamicRoutes') || '[]')
96
    },
97
    methods: {
116 98
      // 获取菜单, 根据路由名称
117 99
      getMenuByRouteName (name, menuList) {
118 100
        var temp = []
119 101
        for (var i = 0; i < menuList.length; i++) {
120 102
          if (menuList[i].list && menuList[i].list.length >= 1) {
121 103
            temp = temp.concat(menuList[i].list)
122
          } else if (getRouteNameByUrl(menuList[i].url) === name) {
104
          } else if (menuList[i].url === name) {
123 105
            return menuList[i]
124 106
          }
125 107
        }

src/views/modules/schedule/add-or-update.vue → src/views/modules/job/schedule-add-or-update.vue


src/views/modules/schedule/log.vue → src/views/modules/job/schedule-log.vue


+ 2 - 2
src/views/modules/schedule/index.vue

@ -105,8 +105,8 @@
105 105
</template>
106 106
107 107
<script>
108
  import AddOrUpdate from './add-or-update'
109
  import Log from './log'
108
  import AddOrUpdate from './schedule-add-or-update'
109
  import Log from './schedule-log'
110 110
  export default {
111 111
    data () {
112 112
      return {

src/views/modules/oss/config.vue → src/views/modules/oss/oss-config.vue


src/views/modules/oss/upload.vue → src/views/modules/oss/oss-upload.vue


+ 2 - 2
src/views/modules/oss/index.vue

@ -67,8 +67,8 @@
67 67
</template>
68 68
69 69
<script>
70
  import Config from './config'
71
  import Upload from './upload'
70
  import Config from './oss-config'
71
  import Upload from './oss-upload'
72 72
  export default {
73 73
    data () {
74 74
      return {

src/views/modules/config/add-or-update.vue → src/views/modules/sys/config-add-or-update.vue


+ 1 - 1
src/views/modules/config/index.vue

@ -74,7 +74,7 @@
74 74
</template>
75 75
76 76
<script>
77
  import AddOrUpdate from './add-or-update'
77
  import AddOrUpdate from './config-add-or-update'
78 78
  export default {
79 79
    data () {
80 80
      return {

src/views/modules/log/index.vue → src/views/modules/sys/log.vue


src/views/modules/menu/add-or-update.vue → src/views/modules/sys/menu-add-or-update.vue


+ 1 - 1
src/views/modules/menu/index.vue

@ -90,7 +90,7 @@
90 90
91 91
<script>
92 92
  import TableTreeColumn from '@/components/table-tree-column'
93
  import AddOrUpdate from './add-or-update'
93
  import AddOrUpdate from './menu-add-or-update'
94 94
  import { treeDataTranslate } from '@/utils'
95 95
  export default {
96 96
    data () {

src/views/modules/role/add-or-update.vue → src/views/modules/sys/role-add-or-update.vue


+ 1 - 1
src/views/modules/role/index.vue

@ -75,7 +75,7 @@
75 75
</template>
76 76
77 77
<script>
78
  import AddOrUpdate from './add-or-update'
78
  import AddOrUpdate from './role-add-or-update'
79 79
  export default {
80 80
    data () {
81 81
      return {

src/views/modules/user/add-or-update.vue → src/views/modules/sys/user-add-or-update.vue


+ 1 - 1
src/views/modules/user/index.vue

@ -91,7 +91,7 @@
91 91
</template>
92 92
93 93
<script>
94
  import AddOrUpdate from './add-or-update'
94
  import AddOrUpdate from './user-add-or-update'
95 95
  export default {
96 96
    data () {
97 97
      return {