vue项目与iview3实现可折叠动态菜单。
菜单实现一下效果:
- 动态获取项目路由生成动态三级菜单导航
- 可折叠展开
- 根据路由name默认打开子目录,选中当前项
- 自动过滤需要隐藏的路由(例:登陆)
- 在手机端首次进入自动收起全部的导航栏,pc端进入导航栏展开
争议之处:当一级菜单项只有一个子元素时,只会显示一级菜单项不会展开下拉列表,设置子元素的显示(hidden)将无效。例如:主页
demo效果如图显示,
菜单使用iview3实现,菜单组件sider.vue的代码如下:
在sider.vue中,展开的菜单与折叠起来的菜单是分开写的,然后根据store中的状态判断是否展开收起。通过showMemuList()和filterObj()两个函数将不需要显示的路由过滤隐藏。
在layout.vue整体布局文件中引用sider组件,内容如下:
|
theme
在layout.vue文件中,重要的部分是组件引入部分以及收起展开的逻辑部分。导航菜单的展开和收起通过操作。动画效果通过store中getters实现,所以在layout.vue中引入了辅助函数。width()方法通过得到store中的pc值判断收起时的宽度。
store中使用modules中app.js,代码如下:
const moduleApp = { state: { isCollapsed: false, // 侧边栏是否折叠,默认不折叠 pc:true // 是否pc端打开 }, mutations: { collapsed (state) { // 这里的 `state` 对象是模块的局部状态 state.isCollapsed = !state.isCollapsed }, // 判断是否pc端,若不是pc端,将自动收起菜单 isPC(state,boo){ state.pc = boo; if(!boo + '' == 'true'){ state.isCollapsed = true } } }, getters: { rotateIcon (state, getters, rootState) { return [ 'menu-icon', state.isCollapsed ? 'rotate-icon' : '' ]; }, menuitemClasses (state, getters, rootState) { return [ 'menu-item', state.isCollapsed ? 'collapsed-menu' : '' ] } }, actions:{ // 测试actions incrementIfOddOnRootSum ({ state, commit, rootState },param) { console.log(state,rootState,param) commit('increment',param.num) } } } export default moduleApp
在创建vuex实例时,通过模块化引入app.js为app。
导航菜单是由路由动态生成,以下是router.js路由文件的代码:
import layout from '@/pages/layout' import home from '@/pages/home' let routes = [ { path: '/', name: 'layout', size:18, // 图标大小 type: 'md-home', // icon类型 text: '主页', // 文本内容 component: layout, redirect: '/page1', meta:{ hidden:false }, children: [ { path: 'page1', name: 'page1', size:18, type: 'ios-paper', text:'首页', meta: { hidden:true }, component: () => import('@/components/HelloWorld.vue') } ] }, { path:'/login', name:'login', meta:{ hidden:true }, component:()=>import('@/components/HelloWorld.vue') }, { path: '/component', name: 'component', size:18, // 图标大小 type: 'md-cube', // icon类型 text: '组件', // 文本内容 component: layout, meta: { hidden:false }, children: [ { path: 'other', name: 'other', // size:18, // 图标大小 type: 'ios-aperture', // icon类型 text: '二级菜单', // 文本内容 component: home, meta: { hidden:false }, children: [ { path: 'theme', name: 'theme', // size:18, // 图标大小 type: 'ios-brush', // icon类型 text: 'theme', // 文本内容 meta: { hidden:false }, component: () => import('@/components/theme.vue') }, ] }, { path: 'page2', name: 'page2', // size:18, // 图标大小 type: 'md-cafe', // icon类型 text: '单选框自定义样式', // 文本内容 meta: { hidden:false }, component: () => import('@/components/input.vue') } ] } ] export default routes
某个路由是否在导航菜单中显示,通过meta中hidden控制,true表示隐藏,false表示不隐藏。layout和hone为模板页,layout是上面layout.vue文件,layout是一级、二级菜单的模板页,home是三级菜单的模板。引入一般的显示页面通过路由懒加载的方式,当要打开对应页面时,在加载页面。当一级菜单项只有一个子元素时,只会显示一级菜单项不会展开下拉列表,设置子元素的显示(hidden)将无效。例如:主页的children只有一个子元素,这时,设置这个子元素的hidden为false,页面中也不会出现子菜单显示此项。因为判断时一级菜单项的子元素长度需要大于一才会出现子菜单。
另外,我在app.vue中判断是否pc端,然后修改store中的值。
当然,还可添加其他操作,例如添加权限,动态从后端获取路由等。
- 总结
其中在实现导航菜单展开收起(isCollapsed)操作时,我选择将isCollapsed的值存储在store中。目的是,如果以后要进入某一个页面需要将菜单收起来时,可以直接操作store中的值实现。如果不会有这种需求或者对store还不太熟悉的话,可以直接将isCollapsed当做参数传进菜单组件。
vue的一个很大的特点是组件化,上面关于展开和收起的菜单我写在了一个组件中。也可以将它们拆成组件,这样不显的累赘。
多有考虑不到之处,欢迎多多指教。