结构示意图
  1. ├── index.html
  2. ├── main.js
  3. ├── router
  4. │ └── index.js # 路由配置文件
  5. ├── components # 组件目录
  6. │ ├── App.vue # 根组件
  7. │ ├── Home.vue # 大的框架结构组件
  8. │ ├── TreeView.vue
  9. │ ├── TreeViewItem.vue
  10. │ └── TreeDetail.vue
  11. ├── store
  12. ├── index.js # 我们组装模块并导出 store 的地方
  13. ├── modules # 模块目录
  14. └── menusModule.js # 菜单模块

这个多级菜单实现的功能如下:

  • 1、可展示多级菜单,理论上可以展无限级菜单
  • 2、当前菜单高亮功能
  • 3、刷新后依然会自动定位到上一次点击的菜单,即使这个是子菜单,并且父菜单会自动展开
  • 4、子菜单的显示隐藏有收起、展开,同时带有淡入效果

这个例子用到的知识点:路由、状态管理、组件。

状态管理安装:

  1. npm install --save vuex

更多关于 vuex 的介绍可以看官方文档:https://vuex.vuejs.org/zh-cn/

我们先来看看效果演示图:

程序员是用代码来沟通的,所以费话不多说,直接上码:

index.html
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width,initial-scale=1.0">
  6. <title>Vue 实现树形菜单(多级菜单)功能模块- 云库网</title>
  7. </head>
  8. <body>
  9. <div id="app"></div>
  10. </body>
  11. </html>
main.js
  1. import Vue from 'vue'
  2. import App from './components/App'
  3. import router from './router'
  4. import store from './store/index'
  5. Vue.config.productionTip = false
  6. /* eslint-disable no-new */
  7. new Vue({
  8. el: '#app',
  9. router,
  10. store,
  11. components: {
  12. App
  13. },
  14. template: '<App/>'
  15. })

在 main.js 中引入 路由和状态管理配置

App.vue
  1. <template>
  2. <div id="app">
  3. <Home></Home>
  4. </div>
  5. </template>
  6. <script>
  7. import Home from "./Home";
  8. export default {
  9. components: {
  10. Home
  11. },
  12. name: "App"
  13. };
  14. </script>
  15. <style>
  16. * {
  17. padding: 0;
  18. margin: 0;
  19. }
  20. #app {
  21. font-family: "Avenir", Helvetica, Arial, sans-serif;
  22. -webkit-font-smoothing: antialiased;
  23. -moz-osx-font-smoothing: grayscale;
  24. color: #2c3e50;
  25. }
  26. html,
  27. body,
  28. #app,
  29. .home {
  30. height: 100%;
  31. }
  32. html,
  33. body {
  34. overflow: hidden;
  35. }
  36. </style>
Home.vue
  1. <template>
  2. <div class="home">
  3. <div class="side-bar">
  4. <Tree-view></Tree-view>
  5. </div>
  6. <div class="continer">
  7. <router-view></router-view>
  8. </div>
  9. </div>
  10. </template>
  11. <script>
  12. import TreeView from "./TreeView";
  13. export default {
  14. components: {
  15. TreeView
  16. },
  17. name: "Home"
  18. };
  19. </script>
  20. <style scoped>
  21. .side-bar {
  22. width: 300px;
  23. height: 100%;
  24. overflow-y: auto;
  25. overflow-x: hidden;
  26. font-size: 14px;
  27. position: absolute;
  28. top: 0;
  29. left: 0;
  30. }
  31. .continer {
  32. padding-left: 320px;
  33. }
  34. </style>

这个 Home.vue 主要是用来完成页面的大框架结构。

TreeView.vue
  1. <template>
  2. <div class="tree-view-menu">
  3. <Tree-view-item :menus='menus'></Tree-view-item>
  4. </div>
  5. </template>
  6. <script>
  7. import TreeViewItem from "./TreeViewItem";
  8. const menusData = [];
  9. export default {
  10. components: {
  11. TreeViewItem
  12. },
  13. name: "TreeViewMenu",
  14. data() {
  15. return {
  16. menus: this.$store.state.menusModule.menus
  17. };
  18. }
  19. };
  20. </script>
  21. <style scoped>
  22. .tree-view-menu {
  23. width: 300px;
  24. height: 100%;
  25. overflow-y: auto;
  26. overflow-x: hidden;
  27. }
  28. .tree-view-menu::-webkit-scrollbar {
  29. height: 6px;
  30. width: 6px;
  31. }
  32. .tree-view-menu::-webkit-scrollbar-trac {
  33. -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
  34. box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
  35. }
  36. .tree-view-menu::-webkit-scrollbar-thumb {
  37. background-color: #6e6e6e;
  38. outline: 1px solid #333;
  39. }
  40. .tree-view-menu::-webkit-scrollbar {
  41. height: 4px;
  42. width: 4px;
  43. }
  44. .tree-view-menu::-webkit-scrollbar-track {
  45. -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
  46. box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
  47. }
  48. .tree-view-menu::-webkit-scrollbar-thumb {
  49. background-color: #6e6e6e;
  50. outline: 1px solid #708090;
  51. }
  52. </style>

这个组件也非常地简单,拿到菜单数据,传给子组件,并把菜单的滚动条样式修改了下。

TreeViewItem.vue
  1. <template>
  2. <div class="tree-view-item">
  3. <div class="level" :class="'level-'+ menu.level" v-for="menu in menus" :key="menu.id">
  4. <div v-if="menu.type === 'link'">
  5. <router-link class="link" v-bind:to="menu.url" @click.native="toggle(menu)">{{menu.name}}</router-link>
  6. </div>
  7. <div v-if="menu.type === 'button'">
  8. <div class="button heading" :class="{selected: menu.isSelected,expand:menu.isExpanded}" @click="toggle(menu)">
  9. {{menu.name}}
  10. <div class="icon">
  11. <svg xmlns="http://www.w3.org/2000/svg" focusable="false" viewBox="0 0 24 24">
  12. <path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z "></path>
  13. </svg>
  14. </div>
  15. </div>
  16. <transition name="fade">
  17. <div class="heading-children" v-show="menu.isExpanded" v-if="menu.subMenu">
  18. <Tree-view-item :menus='menu.subMenu'></Tree-view-item>
  19. </div>
  20. </transition>
  21. </div>
  22. </div>
  23. </div>
  24. </template>
  25. <script>
  26. export default {
  27. name: "TreeViewItem",
  28. props: ["menus"],
  29. created() {
  30. this.$store.commit("firstInit", { url: this.$route.path });
  31. },
  32. methods: {
  33. toggle(menu) {
  34. this.$store.commit("findParents", { menu });
  35. }
  36. }
  37. };
  38. </script>
  39. <style scoped>
  40. a {
  41. text-decoration: none;
  42. color: #333;
  43. }
  44. .link,
  45. .button {
  46. display: block;
  47. padding: 10px 15px;
  48. transition: background-color 0.2s ease-in-out 0s, color 0.3s ease-in-out 0.1s;
  49. -moz-user-select: none;
  50. -webkit-user-select: none;
  51. -ms-user-select: none;
  52. -khtml-user-select: none;
  53. user-select: none;
  54. }
  55. .button {
  56. position: relative;
  57. }
  58. .link:hover,
  59. .button:hover {
  60. color: #1976d2;
  61. background-color: #eee;
  62. cursor: pointer;
  63. }
  64. .icon {
  65. position: absolute;
  66. right: 0;
  67. display: inline-block;
  68. height: 24px;
  69. width: 24px;
  70. fill: currentColor;
  71. transition: -webkit-transform 0.15s;
  72. transition: transform 0.15s;
  73. transition: transform 0.15s, -webkit-transform 0.15s;
  74. transition-timing-function: ease-in-out;
  75. }
  76. .heading-children {
  77. padding-left: 14px;
  78. overflow: hidden;
  79. }
  80. .expand {
  81. display: block;
  82. }
  83. .collapsed {
  84. display: none;
  85. }
  86. .expand .icon {
  87. -webkit-transform: rotate(90deg);
  88. transform: rotate(90deg);
  89. }
  90. .selected {
  91. color: #1976d2;
  92. }
  93. .fade-enter-active {
  94. transition: all 0.5s ease 0s;
  95. }
  96. .fade-enter {
  97. opacity: 0;
  98. }
  99. .fade-enter-to {
  100. opacity: 1;
  101. }
  102. .fade-leave-to {
  103. height: 0;
  104. }
  105. </style>

上面的这个组件才是这个树型结构重点代码,用了递归的思想来实现这个树型菜单。

TreeViewDetail.vue
  1. <template>
  2. <h3>
  3. 这里是{{currentRoute}}导航详情
  4. </h3>
  5. </template>
  6. <script>
  7. export default {
  8. name: "TreeViewDetail",
  9. data() {
  10. return {
  11. currentRoute: this.$route.path
  12. };
  13. },
  14. watch: {
  15. //监听路由,只要路由有变化(路径,参数等变化)都有执行下面的函数
  16. $route: {
  17. handler: function(val, oldVal) {
  18. this.currentRoute = val.name;
  19. },
  20. deep: true
  21. }
  22. }
  23. };
  24. </script>
  25. <style scoped>
  26. h3 {
  27. margin-top: 10px;
  28. font-weight: normal;
  29. }
  30. </style>
router/index.js
  1. import Vue from 'vue';
  2. import Router from 'vue-router';
  3. import App from '@/components/App';
  4. import TreeViewDetail from '@/components/TreeViewDetail';
  5. Vue.use(Router)
  6. export default new Router({
  7. linkActiveClass: 'selected',
  8. routes: [{
  9. path: '/',
  10. name: 'App',
  11. component: App
  12. },
  13. {
  14. path: '/detail/quickstart',
  15. name: 'quickstart',
  16. component: TreeViewDetail
  17. },
  18. {
  19. path: '/detail/tutorial',
  20. name: 'tutorial',
  21. component: TreeViewDetail
  22. },
  23. {
  24. path: '/detail/toh-pt1',
  25. name: 'toh-pt1',
  26. component: TreeViewDetail
  27. },
  28. {
  29. path: '/detail/toh-pt2',
  30. name: 'toh-pt2',
  31. component: TreeViewDetail
  32. },
  33. {
  34. path: '/detail/toh-pt3',
  35. name: 'toh-pt3',
  36. component: TreeViewDetail
  37. },
  38. {
  39. path: '/detail/toh-pt4',
  40. name: 'toh-pt4',
  41. component: TreeViewDetail
  42. },
  43. {
  44. path: '/detail/toh-pt5',
  45. name: 'toh-pt5',
  46. component: TreeViewDetail
  47. },
  48. {
  49. path: '/detail/toh-pt6',
  50. name: 'toh-pt6',
  51. component: TreeViewDetail
  52. },
  53. {
  54. path: '/detail/architecture',
  55. name: 'architecture',
  56. component: TreeViewDetail
  57. },
  58. {
  59. path: '/detail/displaying-data',
  60. name: 'displaying-data',
  61. component: TreeViewDetail
  62. },
  63. {
  64. path: '/detail/template-syntax',
  65. name: 'template-syntax',
  66. component: TreeViewDetail
  67. },
  68. {
  69. path: '/detail/lifecycle-hooks',
  70. name: 'lifecycle-hooks',
  71. component: TreeViewDetail
  72. },
  73. {
  74. path: '/detail/component-interaction',
  75. name: 'component-interaction',
  76. component: TreeViewDetail
  77. },
  78. {
  79. path: '/detail/component-styles',
  80. name: 'component-styles',
  81. component: TreeViewDetail
  82. },
  83. {
  84. path: '/detail/dynamic-component-loader',
  85. name: 'dynamic-component-loader',
  86. component: TreeViewDetail
  87. },
  88. {
  89. path: '/detail/attribute-directives',
  90. name: 'attribute-directives',
  91. component: TreeViewDetail
  92. },
  93. {
  94. path: '/detail/structural-directives',
  95. name: 'structural-directives',
  96. component: TreeViewDetail
  97. },
  98. {
  99. path: '/detail/pipes',
  100. name: 'pipes',
  101. component: TreeViewDetail
  102. },
  103. {
  104. path: '/detail/animations',
  105. name: 'animations',
  106. component: TreeViewDetail
  107. },
  108. {
  109. path: '/detail/user-input',
  110. name: 'user-input',
  111. component: TreeViewDetail
  112. },
  113. {
  114. path: '/detail/forms',
  115. name: 'forms',
  116. component: TreeViewDetail
  117. },
  118. {
  119. path: '/detail/form-validation',
  120. name: 'form-validation',
  121. component: TreeViewDetail
  122. },
  123. {
  124. path: '/detail/reactive-forms',
  125. name: 'reactive-forms',
  126. component: TreeViewDetail
  127. },
  128. {
  129. path: '/detail/dynamic-form',
  130. name: 'dynamic-form',
  131. component: TreeViewDetail
  132. },
  133. {
  134. path: '/detail/bootstrapping',
  135. name: 'bootstrapping',
  136. component: TreeViewDetail
  137. },
  138. {
  139. path: '/detail/ngmodule',
  140. name: 'ngmodule',
  141. component: TreeViewDetail
  142. },
  143. {
  144. path: '/detail/ngmodule-faq',
  145. name: 'ngmodule-faq',
  146. component: TreeViewDetail
  147. },
  148. {
  149. path: '/detail/dependency-injection',
  150. name: 'dependency-injection',
  151. component: TreeViewDetail
  152. },
  153. {
  154. path: '/detail/hierarchical-dependency-injection',
  155. name: 'hierarchical-dependency-injection',
  156. component: TreeViewDetail
  157. },
  158. {
  159. path: '/detail/dependency-injection-in-action',
  160. name: 'dependency-injection-in-action',
  161. component: TreeViewDetail
  162. },
  163. {
  164. path: '/detail/http',
  165. name: 'http',
  166. component: TreeViewDetail
  167. },
  168. {
  169. path: '/detail/router',
  170. name: 'router',
  171. component: TreeViewDetail
  172. },
  173. {
  174. path: '/detail/testing',
  175. name: 'testing',
  176. component: TreeViewDetail
  177. },
  178. {
  179. path: '/detail/cheatsheet',
  180. name: 'cheatsheet',
  181. component: TreeViewDetail
  182. },
  183. {
  184. path: '/detail/i18n',
  185. name: 'i18n',
  186. component: TreeViewDetail
  187. },
  188. {
  189. path: '/detail/language-service',
  190. name: 'language-service',
  191. component: TreeViewDetail
  192. },
  193. {
  194. path: '/detail/security',
  195. name: 'security',
  196. component: TreeViewDetail
  197. },
  198. {
  199. path: '/detail/setup',
  200. name: 'setup',
  201. component: TreeViewDetail
  202. },
  203. {
  204. path: '/detail/setup-systemjs-anatomy',
  205. name: 'setup-systemjs-anatomy',
  206. component: TreeViewDetail
  207. },
  208. {
  209. path: '/detail/browser-support',
  210. name: 'browser-support',
  211. component: TreeViewDetail
  212. },
  213. {
  214. path: '/detail/npm-packages',
  215. name: 'npm-packages',
  216. component: TreeViewDetail
  217. },
  218. {
  219. path: '/detail/typescript-configuration',
  220. name: 'typescript-configuration',
  221. component: TreeViewDetail
  222. },
  223. {
  224. path: '/detail/aot-compiler',
  225. name: 'aot-compiler',
  226. component: TreeViewDetail
  227. },
  228. {
  229. path: '/detail/metadata',
  230. name: 'metadata',
  231. component: TreeViewDetail
  232. },
  233. {
  234. path: '/detail/deployment',
  235. name: 'deployment',
  236. component: TreeViewDetail
  237. },
  238. {
  239. path: '/detail/upgrade',
  240. name: 'upgrade',
  241. component: TreeViewDetail
  242. },
  243. {
  244. path: '/detail/ajs-quick-reference',
  245. name: 'ajs-quick-reference',
  246. component: TreeViewDetail
  247. },
  248. {
  249. path: '/detail/visual-studio-2015',
  250. name: 'visual-studio-2015',
  251. component: TreeViewDetail
  252. },
  253. {
  254. path: '/detail/styleguide',
  255. name: 'styleguide',
  256. component: TreeViewDetail
  257. },
  258. {
  259. path: '/detail/glossary',
  260. name: 'glossary',
  261. component: TreeViewDetail
  262. },
  263. {
  264. path: '/detail/api',
  265. name: 'api',
  266. component: TreeViewDetail
  267. }
  268. ]
  269. })
store/module/menusModule.js
  1. let menus = [
  2. { id: 1, level: 1, name: '快速上手', type: "link", url: "/detail/quickstart" },
  3. {
  4. id: 2,
  5. level: 1,
  6. name: '教程',
  7. type: "button",
  8. isExpanded: false,
  9. isSelected: false,
  10. subMenu: [
  11. { id: 21, level: 2, name: '简介', type: "link", url: "/detail/tutorial" },
  12. { id: 22, level: 2, name: '英雄编辑器', type: "link", url: "/detail/toh-pt1" },
  13. { id: 23, level: 2, name: '主从结构', type: "link", url: "/detail/toh-pt2" },
  14. { id: 24, level: 2, name: '多个组件', type: "link", url: "/detail/toh-pt3" },
  15. { id: 25, level: 2, name: '服务', type: "link", url: "/detail/toh-pt4" },
  16. { id: 26, level: 2, name: '路由', type: "link", url: "/detail/toh-pt5" },
  17. { id: 27, level: 2, name: 'HTTP', type: "link", url: "/detail/toh-pt6" },
  18. ]
  19. },
  20. {
  21. id: 3,
  22. level: 1,
  23. name: '核心知识',
  24. type: "button",
  25. isExpanded: false,
  26. isSelected: false,
  27. subMenu: [
  28. { id: 31, level: 2, name: '架构', type: "link", url: "/detail/architecture" },
  29. {
  30. id: 32,
  31. level: 2,
  32. name: '模板与数据绑定',
  33. type: "button",
  34. isExpanded: false,
  35. isSelected: false,
  36. subMenu: [
  37. { id: 321, level: 3, name: '显示数据', type: "link", url: "/detail/displaying-data" },
  38. { id: 322, level: 3, name: '模板语法', type: "link", url: "/detail/template-syntax" },
  39. { id: 323, level: 3, name: '生命周期钩子', type: "link", url: "/detail/lifecycle-hooks" },
  40. { id: 324, level: 3, name: '组件交互', type: "link", url: "/detail/component-interaction" },
  41. { id: 325, level: 3, name: '组件样式', type: "link", url: "/detail/component-styles" },
  42. { id: 326, level: 3, name: '动态组件', type: "link", url: "/detail/dynamic-component-loader" },
  43. { id: 327, level: 3, name: '属性型指令', type: "link", url: "/detail/attribute-directives" },
  44. { id: 328, level: 3, name: '结构型指令', type: "link", url: "/detail/structural-directives" },
  45. { id: 329, level: 3, name: '管道', type: "link", url: "/detail/pipes" },
  46. { id: 3210, level: 3, name: '动画', type: "link", url: "/detail/animations" },
  47. ]
  48. },
  49. {
  50. id: 33,
  51. level: 2,
  52. name: '表单',
  53. type: "button",
  54. isExpanded: false,
  55. isSelected: false,
  56. subMenu: [
  57. { name: '用户输入', type: "link", url: "/detail/user-input" },
  58. { name: '模板驱动表单', type: "link", url: "/detail/forms" },
  59. { name: '表单验证', type: "link", url: "/detail/form-validation" },
  60. { name: '响应式表单', type: "link", url: "/detail/reactive-forms" },
  61. { name: '动态表单', type: "link", url: "/detail/dynamic-form" }
  62. ]
  63. },
  64. { id: 34, level: 2, name: '引用启动', type: "link", url: "/detail/bootstrapping" },
  65. {
  66. id: 35,
  67. level: 2,
  68. name: 'NgModules',
  69. type: "button",
  70. isExpanded: false,
  71. isSelected: false,
  72. subMenu: [
  73. { id: 341, level: 3, name: 'NgModule', type: "link", url: "/detail/ngmodule" },
  74. { id: 342, level: 3, name: 'NgModule 常见问题', type: "link", url: "/detail/ngmodule-faq" }
  75. ]
  76. },
  77. {
  78. id: 36,
  79. level: 2,
  80. name: '依赖注入',
  81. type: "button",
  82. isExpanded: false,
  83. isSelected: false,
  84. subMenu: [
  85. { id: 361, level: 3, name: '依赖注入', type: "link", url: "/detail/dependency-injection" },
  86. { id: 362, level: 3, name: '多级注入器', type: "link", url: "/detail/hierarchical-dependency-injection" },
  87. { id: 363, level: 3, name: 'DI 实例技巧', type: "link", url: "/detail/dependency-injection-in-action" }
  88. ]
  89. },
  90. { id: 37, level: 2, name: 'HttpClient', type: "link", url: "/detail/http" },
  91. { id: 38, level: 2, name: '路由与导航', type: "link", url: "/detail/router" },
  92. { id: 39, level: 2, name: '测试', type: "link", url: "/detail/testing" },
  93. { id: 310, level: 2, name: '速查表', type: "link", url: "/detail/cheatsheet" },
  94. ]
  95. },
  96. {
  97. id: 4,
  98. level: 1,
  99. name: '其它技术',
  100. type: "button",
  101. isExpanded: false,
  102. isSelected: false,
  103. subMenu: [
  104. { id: 41, level: 2, name: '国际化(i18n)', type: "link", url: "/detail/i18n" },
  105. { id: 42, level: 2, name: '语言服务', type: "link", url: "/detail/language-service" },
  106. { id: 43, level: 2, name: '安全', type: "link", url: "/detail/security" },
  107. {
  108. id: 44,
  109. level: 2,
  110. name: '环境设置与部署',
  111. type: "button",
  112. isExpanded: false,
  113. isSelected: false,
  114. subMenu: [
  115. { id: 441, level: 3, name: '搭建本地开发环境', type: "link", url: "/detail/setup" },
  116. { id: 442, level: 3, name: '搭建方式剖析', type: "link", url: "/detail/setup-systemjs-anatomy" },
  117. { id: 443, level: 3, name: '浏览器支持', type: "link", url: "/detail/browser-support" },
  118. { id: 444, level: 3, name: 'npm 包', type: "link", url: "/detail/npm-packages" },
  119. { id: 445, level: 3, name: 'TypeScript 配置', type: "link", url: "/detail/typescript-configuration" },
  120. { id: 446, level: 3, name: '预 (AoT) 编译器', type: "link", url: "/detail/aot-compiler" },
  121. { id: 447, level: 3, name: '预 (AoT) 编译器', type: "link", url: "/detail/metadata" },
  122. { id: 448, level: 3, name: '部署', type: "link", url: "/detail/deployment" }
  123. ]
  124. },
  125. {
  126. id: 45,
  127. level: 2,
  128. name: '升级',
  129. type: "button",
  130. isExpanded: false,
  131. isSelected: false,
  132. subMenu: [
  133. { id: 451, level: 3, name: '从 AngularJS 升级', type: "link", url: "/detail/upgrade" },
  134. { id: 452, level: 3, name: '升级速查表', type: "link", url: "/detail/ajs-quick-reference" }
  135. ]
  136. },
  137. { id: 46, level: 2, name: 'Visual Studio 2015 快速上手', type: "link", url: "/detail/visual-studio-2015" },
  138. { id: 47, level: 2, name: '风格指南', type: "link", url: "/detail/styleguide" },
  139. { id: 48, level: 2, name: '词汇表', type: "link", url: "/detail/glossary" }
  140. ]
  141. },
  142. { id: 5, level: 1, name: 'API 参考手册', type: "link", url: "/detail/api" }
  143. ];
  144. let levelNum = 1;
  145. let startExpand = []; // 保存刷新后当前要展开的菜单项
  146. function setExpand(source, url) {
  147. let sourceItem = '';
  148. for (let i = 0; i < source.length; i++) {
  149. sourceItem = JSON.stringify(source[i]); // 把菜单项转为字符串
  150. if (sourceItem.indexOf(url) > -1) { // 查找当前 URL 所对应的子菜单属于哪一个祖先菜单
  151. if (source[i].type === 'button') { // 导航菜单为按钮
  152. source[i].isSelected = true; // 设置选中高亮
  153. source[i].isExpanded = true; // 设置为展开
  154. startExpand.push(source[i]);
  155. // 递归下一级菜单,以此类推
  156. setExpand(source[i].subMenu, url);
  157. }
  158. break;
  159. }
  160. }
  161. }
  162. const state = {
  163. menus,
  164. levelNum
  165. };
  166. const mutations = {
  167. findParents(state, payload) {
  168. if (payload.menu.type === "button") {
  169. payload.menu.isExpanded = !payload.menu.isExpanded;
  170. } else if (payload.menu.type === "link") {
  171. if (startExpand.length > 0) {
  172. for (let i = 0; i < startExpand.length; i++) {
  173. startExpand[i].isSelected = false;
  174. }
  175. }
  176. startExpand = []; // 清空展开菜单记录项
  177. setExpand(state.menus, payload.menu.url);
  178. };
  179. },
  180. firstInit(state, payload) {
  181. setExpand(state.menus, payload.url);
  182. }
  183. }
  184. export default {
  185. state,
  186. mutations
  187. };

在使用状态管理时,我们一定要记住,一旦数据写到了 state 中时,就不能再添加其它属性了,什么时间?就拿上面的 menus 数据来说,比如,本来菜单数据中没有 isExpanded 这个字段的,然后你在 mutations 的方法中给 menus 对象添加了一个 isExpanded 属性,但你会发现属性是不会被状态管理追踪到的,所以我们一开始就给这个数据添加了 isExpanded 和 isSelected 。

store/index.js
  1. import Vue from 'vue'
  2. import Vuex from 'vuex'
  3. import menusModule from './module/menusModule'
  4. Vue.use(Vuex);
  5. const store = new Vuex.Store({
  6. modules: {
  7. menusModule
  8. }
  9. })
  10. export default store;

上面这个例子在使用状态管理时,把菜单的相关配置封装成模块,然后再引入。如果把状态管理写成模块的形式的话,在调用这个模块中的状态时就需要注意了,写法可以参数示例中的代码。

上面这个例子可以直接用到自己的项目中,只要你理解了其中的思想,其他的都不是问题。Vue 实现树形菜单功能模块之旅只能带你到这里了。

Vue2 实现树形菜单(多级菜单)功能模块的更多相关文章

  1. zTree下拉菜单多级菜单多选实现

    惯例,先上图: 这是在一个项目中,为了满足样式美观.多级菜单以及多选而将zTree插件更改过后的效果. 在实际的开发过程中,本来zTree也是可以满足需求的,但是zTree多选的话需要checkbox ...

  2. jQuery实现多级手风琴树形下拉菜单(源码)

    前几天因为公司的菜单要调整,公司的UI框架是不支持的,所以就自己在网上找了一个下拉菜单,可以支持多级菜单数据的,菜单数据是从xml文件中配置后读取的,网上有许多这方面的例子感觉不是很好用,就打了个包贴 ...

  3. python3 实现一个多级菜单小功能

    记录下一下 #!/usr/bin/env python3 ''' 需求:三级菜单 三级菜单,依次进入子菜单 ''' City = { '北京':{ '大兴区':[ '亦庄','黄村','中信新城',' ...

  4. day1作业二:多级菜单

        作业二:多级菜单 1.三级菜单 2.可以次选择进入各子菜单 3.所需新知识点:列表.字典 4.打印b回到上一层 5.打印q退出循环 流程图如下: readme: (1)存储三级菜单的字典;设置 ...

  5. [前端随笔][Vue] 多级菜单实现思路——组件嵌套

    说在前面 本篇记录学习了vue-element-admin中的多级菜单的实现 [传送门] @vue/cli 4.2.2:vuex:scss:组件嵌套 正文 创建项目 npm create 项目名 // ...

  6. java生成多级菜单树

    使用java实现一个多级菜单树结构 先上数据库 ps_pid字段很重要,是父级菜单的id Menu类 Menu类要新增一个字段,用来存放子菜单 /** * 子菜单列表 */ private List& ...

  7. 轻量级多级菜单控制框架程序(C语言)

    1.前言 作为嵌入式软件开发,可能经常会使用命令行或者显示屏等设备实现人机交互的功能,功能中通常情况都包含 UI 菜单设计:很多开发人员都会有自己的菜单框架模块,防止重复造轮子,网上有很多这种菜单框架 ...

  8. MVC5+EF6 入门完整教程13 -- 动态生成多级菜单

    稍微有一定复杂性的系统,多级菜单都是一个必备组件. 本篇专题讲述如何生成动态多级菜单的通用做法. 我们不用任何第三方的组件,完全自己构建灵活通用的多级菜单. 需要达成的效果:容易复用,可以根据mode ...

  9. 单片机C语言下LCD多级菜单的一种实现方法

    摘要:     介绍了在C 语言环境下,在LCD 液晶显示屏上实现多级嵌套菜单的一种简便方法,提出了一个结构紧凑.实用的程序模型. 关键词: 液晶显示屏; 多级菜单; 单片机; C 语言; LCD 中 ...

随机推荐

  1. C#计算时间差 TimeSpan

    TimeSpan的相关属性 Add:与另一个TimeSpan值相加. Days:返回用天数计算的TimeSpan值. Duration:获取TimeSpan的绝对值. Hours:返回用小时计算的Ti ...

  2. c/c++ 类模板初探

    类模板 1,模板类里的函数都是模板函数 2,模板类里的函数,在类外面实现的时候,要用模板函数(方法:push_back)的方式实现,在类内部实现时,不需要用模板函数(方法:show)方式实现. 3,用 ...

  3. powershell脚本执行绕过powershell下脚本执行限制(cmd下执行)以及在cmd下隐藏脚本窗口

    powershell脚本执行绕过powershell下脚本执行限制(cmd下执行) powershell脚本运行方式有两种,一种是powshell中运行,另一种是在cmd中(在某些情况下相当有用) p ...

  4. CentOS6.5 安装并配置vsftpd

    一.获取root权限 su 输入root密码 二.检查是否安装 rpm -qa | grep vsftpd 如果安装,会显示安装版本号,没有就什么都不显示 三.若已安装过vsftpd,先卸载.卸载前, ...

  5. June 8. 2018 Week Week 23rd Friday

    You'll have bad times, but it'll always wake you up to the good stuff you weren't paying attention t ...

  6. 【项目 · Wonderland】需求规格说明书 · 终版

    [项目 · Wonderland]需求规格说明书 · 终版 Part 0 · 简 要 目 录 Part 1 · 流 程 / 分 工 Part 2 · 需 求 规 格 说 明 书 Part 1 · 流 ...

  7. 《Java大学教程》—第4章 方法的实现

    4.2~3 声明.实现.调用4.4 数据传递:实参.形参.返回值4.6 变量作用域:局部变量(区域内访问).全局变量4.7 重载:运算符重载.方法重载-->多态 1.答:P67方法(method ...

  8. 【Linux基础】tr命令替换和删除字符

    1.tr命令 tr可以对来自标准输入的字符进行替换.压缩和删除,可以将一组字符变成另外一组字符.通过使用 tr,您可以非常容易地实现 sed 的许多最基本功能.您可以将 tr 看作为 sed 的(极其 ...

  9. ES6切割原理

    ES6提供了 ... 操作,下面简单演示如何切割对象 const params = { page: 1, pageSize: 10, name: '名字', age: '13', weight: '7 ...

  10. MySQL高级知识(十四)——行锁

    前言:前面学习了表锁的相关知识,本篇主要介绍行锁的相关知识.行锁偏向InnoDB存储引擎,开销大,加锁慢,会出现死锁,锁定粒度小,发生锁冲突的概率低,但并发度高. 0.准备 #1.创建相关测试表tb_ ...