zoukankan      html  css  js  c++  java
  • Vue2 实现树形菜单(多级菜单)功能模块

    结构示意图
    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/

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

    Vue 实现树形菜单(多级菜单)功能模块

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

    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 实现树形菜单功能模块之旅只能带你到这里了。

  • 相关阅读:
    MOSS中的User的Title, LoginName, DisplayName, SID之间的关系
    如何在Network Monitor中高亮间隔时间过长的帧?
    SharePoint服务器如果需要安装杀毒软件, 需要注意什么?
    如何查看SQL Profiler? 如何查看SQL死锁?
    什么是Telnet
    The name or security ID (SID) of the domain specified is inconsistent with the trust information for that domain.
    Windows SharePoint Service 3.0的某个Web Application无搜索结果
    网络连接不上, 有TCP错误, 如果操作系统是Windows Server 2003, 请尝试一下这里
    在WinDBG中查看内存的命令
    The virtual machine could not be started because the hypervisor is not running
  • 原文地址:https://www.cnblogs.com/-flq/p/9444012.html
Copyright © 2011-2022 走看看