使用的第三方
- vue
- vue-router
- elementUI
达到的效果
通过服务端配置获取菜单,这里使用MOCK来模拟服务端的数据,数据如下,数据很长,先用看图片讲逻辑,再最后我把完成数据贴出来
在系统中的效果如下:
实现方式
这里实现需要用vue-router,vue的官方路由;路由的作用就是帮忙管理各个页面,能够让页面做不同的跳转,比如我们使用http://localhost:8080/#/,这样会跳转到首页,http://localhost:8080/#/Login会跳转到登录页面,这是很正常的行为,会有这样的行为就是因为有路由进行管理;路由是在代码中运作的,在界面上其实看不到,比如我们的login界面在菜单上就没有。界面上能看到的,是通过html来展现出来的,所以这里实现动态菜单需要分为两步,一步配置路由,第二步在界面上进行展示。
配置路由表
代码如下
import Vue from 'vue'
import Router from 'vue-router'
import Login from '@/views/Login'
import Home from '@/views/Home'
import NotFound from '@/views/404'
import api from '@/http/api'
import store from '@/store'
import Intro from '@/views/Intro/Intro'
import dept from '@/views/Sys/dept'
Vue.use(Router)
//获取原型对象上的push函数
const originalPush = Router.prototype.push
//修改原型对象中的push方法
Router.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err)
}
// 这里并不在菜单显示,只是在路由中增加了
const router = new Router({
routes: [
{
path: '/',
name: '首页',
component: Home,
children: [
{
path: '',
name: '系统介绍',
component: Intro,
meta: {
icon: 'fa fa-home fa-lg',
index: 0
}
},
{
path: '/dept',
name: 'dept',
component: dept
}
]
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/404',
name: 'NotFound',
component: NotFound
}
]
})
router.beforeEach((to, from, next) => {
// 登录界面登录成功之后,会把用户信息保存在会话
// 存在时间为会话生命周期,页面关闭即失效。
let userName = sessionStorage.getItem('user')
if (to.path === '/login') {
// 如果是访问登录界面,如果用户会话信息存在,代表已登录过,跳转到主页
if(userName) {
next({ path: '/' })
} else {
next()
}
} else {
if (!userName) {
// 如果访问非登录界面,且户会话信息不存在,代表未登录,则跳转到登录界面
next({ path: '/login' })
} else {
// 加载动态菜单和路由
addDynamicMenuAndRoutes(userName, to, from)
next()
}
}
})
/**
* 加载动态菜单和路由
*/
function addDynamicMenuAndRoutes(userName, to, from) {
if(store.state.app.menuRouteLoaded) {
console.log('动态菜单和路由已经存在.')
return
}
api.menu.findNavTree({'userName':userName})
.then(res => {
// 添加动态路由
// 添加动态路由
let dynamicRoutes = addDynamicRoutes(res.data)
console.log("dynamicRoutes")
console.log(dynamicRoutes)
// routes[0]就是首页,这里就是在首页里增加
router.options.routes[0].children = router.options.routes[0].children.concat(dynamicRoutes)
console.log(router.options.routes[0])
console.log("router.options.routes")
console.log(router.options.routes[0].children)
router.addRoutes(router.options.routes)
// 保存加载状态
store.commit('menuRouteLoaded', true)
// 保存菜单树,根据res.data的值来生成菜单
store.commit('setNavTree', res.data)
}).then(res => {
api.user.findPermissions({'name':userName}).then(res => {
// 保存用户权限标识集合
store.commit('setPerms', res.data)
})
})
.catch(function(res) {
})
}
/**
* 添加动态(菜单)路由
* @param {*} menuList 菜单列表
* @param {*} routes 递归创建的动态(菜单)路由
*/
function addDynamicRoutes (menuList = [], routes = []) {
var temp = []
for (var i = 0; i < menuList.length; i++) {
if (menuList[i].children && menuList[i].children.length >= 1) {
temp = temp.concat(menuList[i].children)
} else if (menuList[i].url && /\S/.test(menuList[i].url)) {
menuList[i].url = menuList[i].url.replace(/^\//, '')
// 创建路由配置
var route = {
path: menuList[i].url,
component: null,
name: menuList[i].name,
meta: {
icon: menuList[i].icon,
index: menuList[i].id
}
}
try {
// 根据菜单URL动态加载vue组件,这里要求vue组件须按照url路径存储
// 如url="sys/user",则组件路径应是"@/views/sys/user.vue",否则组件加载不到
let array = menuList[i].url.split('/')
let url = ''
for(let i=0; i<array.length; i++) {
url += array[i].substring(0,1).toUpperCase() + array[i].substring(1) + '/'
}
url = url.substring(0, url.length - 1)
route['component'] = resolve => require([`@/views/${url}`], resolve)
} catch (e) {}
routes.push(route)
}
}
if (temp.length >= 1) {
addDynamicRoutes(temp, routes)
} else {
console.log('动态路由加载...')
console.log(routes)
console.log('动态路由加载完成.')
}
return routes
}
console.log("befor export default router")
console.log(router)
export default router
界面展示
代码如下:menu-tree是一个自定义组件
<!-- 导航菜单树组件,动态加载菜单 -->
<menu-tree v-for="item in navTree" :key="item.id" :menu="item"></menu-tree>