动态加载菜单
之前我们的导航树都是写死在页面里的,而实际应用中是需要从后台服务器获取菜单数据之后动态生成的。
我们在这里就用上一篇准备好的数据格式Mock出模拟数据,然后动态生成我们的导航菜单。
接口模块化
我们向来讲究模块化,之前接口都集中在,interface.js,我们现在把它改名为 api.js,并把里边原来登录、用户、菜单的相关接口都转移到我们新建的接口模块文件中。
模块化之后的文件结构如下图所示
模块化之后,模块接口写在相应的模块接口文件中,如下面是登录模块
login.js
import axios from '../axios' /* * 系统登录模块 */ // 登录 export const login = data => { return axios({ url: '/login', method: 'post', data }) } // 登出 export const logout = () => { return axios({ url: '/logout', method: 'get' }) }
模块化之后,父模块可以像这样引入
api.js
/* * 接口统一集成模块 */ import * as login from './moudules/login' import * as user from './moudules/user' import * as menu from './moudules/menu' // 默认全部导出 export default { login, user, menu }
因为我们这里是导出的是父模块,所以在具体接口调用的时候,也需要在原来的基础上加上模块了,像这样。
如上面 api.js 中,我们导出了 login 的整个文件,而 login 文件下有 login,logout 等多个方法。
导航菜单树接口
我们在 menu.js 下创建一个查询导航菜单树的接口。
import axios from '../axios' /* * 菜单管理模块 */ export const findMenuTree = () => { return axios({ url: '/menu/findTree', method: 'get' }) }
api.js 中如果没引入要记得引入。
页面接口调用
接口已经有了,我们在导航菜单组件 MenuBar.vue 中,加载菜单并存入 store 。
页面菜单渲染
还是在 MenuBar.vue 中,页面通过封装的菜单树组件读取store数据,递归生成菜单。
新建菜单树组件,递归生成菜单,并在点击响应函数里面根据菜单URL跳转到指定路由。
components/MenuTree/index.js
<template> <el-submenu v-if="menu.children && menu.children.length >= 1" :index="menu.menuId + ''"> <template slot="title"> <i :class="menu.icon"></i> <span slot="title">{{menu.name}}</span> </template> <MenuTree v-for="item in menu.children" :key="item.menuId" :menu="item"></MenuTree> </el-submenu> <el-menu-item v-else :index="menu.menuId + ''" @click="handleRoute(menu)"> <i :class="menu.icon"></i> <span slot="title">{{menu.name}}</span> </el-menu-item> </template> <script> export default { name: 'MenuTree', props: { menu: { type: Object, required: true } }, methods: { handleRoute (menu) { // 通过菜单URL跳转至指定路由 this.$router.push(menu.url) } } } </script>
提供Mock数据
接口有了,页面调用和渲染也写好了,该提供Mock数据了。
mock/modules/menu.js 中 mock findTree接口,data 对应数据太多,这里不贴了。
export function findTree() { return { url: 'http://localhost:8080/menu/findTree', type: 'get', data: menuTreeData // json 对象数据 } }
测试效果
启动完成,进入主页,我们看到导航菜单已经成功加载进来了,oh yeah!
然而,我们愉悦的点了点菜单,发现是这样的情况,oh no !
毛都没有,不过显然,聪明的你已经看穿了一切,我们之前只提供了一个叫 /user 的路由,并没有提供 /sys/user 的路由。
好吧,我们稍微修改一下,打开路由配置,把 /user 改成 /sys/user 试试。
果不其然,修改完之后便可以正常跳转到用户界面了。
但不对呀,这里路由配置是写死的,导航菜单是菜单数据动态生成的,这个路由配置也应该是根据菜单数据动态添加的啊,嗯,所以接下来我们就来讨论动态路由配置的问题。
动态路由实现
在 vue 的 route 中提供了 addRoutes 来实现动态路由,打开 MenuBar.vue ,我们在加载导航菜单的同时添加动态路由配置。
MenuBar.vue
其中 addDynamicMenuRoutes 是根据菜单返回动态路由配置的关键代码。
addDynamicMenuRoutes 方法详情:
/** * 添加动态(菜单)路由 * @param {*} menuList 菜单列表 * @param {*} routes 递归创建的动态(菜单)路由 */ addDynamicMenuRoutes (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: { menuId: menuList[i].menuId, title: menuList[i].name, isDynamic: true, isTab: true, iframeUrl: '' } } // url以http[s]://开头, 通过iframe展示 if (isURL(menuList[i].url)) { route['path'] = menuList[i].url route['name'] = menuList[i].name route['meta']['iframeUrl'] = menuList[i].url } else { try { // 根据菜单URL动态加载vue组件,这里要求vue组件须按照url路径存储 // 如url="sys/user",则组件路径应是"@/views/sys/user.vue",否则组件加载不到 let array = menuList[i].url.split('/') let url = array[0].substring(0,1).toUpperCase()+array[0].substring(1) + '/' + array[1].substring(0,1).toUpperCase()+array[1] .substring(1) route['component'] = resolve => require([`@/views/${url}`], resolve) } catch (e) {} } routes.push(route) } } if (temp.length >= 1) { this.addDynamicMenuRoutes(temp, routes) } else { console.log(routes) } return routes }
动态菜单页面的组件结构稍微调整下,需要跟菜单url匹配,才能根据菜单url确定组件路径来动态加载组件。
把路由文件清理一下,把动态菜单相关的路由配置处理掉,留下一些固定的全局路由就好。
动态路由测试
启动完成,进入主页,点击用户管理,路由到了用户管理页面。
点击机构管理,路由到了机构管理页面。
好了,到这里动态路由功能已经实现了,给自己鼓个掌吧。
源码下载
码云:https://gitee.com/liuge1988/kitty-ui.git
作者:朝雨忆轻尘
出处:https://www.cnblogs.com/xifengxiaoma/
版权所有,欢迎转载,转载请注明原文作者及出处。