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

  • 相关阅读:
    找出互联网符合的产品实例
    以软件周期来说明不同的测试的使用情况
    scrapy多个page爬取, post请求, 通过爬到的URL继续发请求爬页面
    Scrapy 安装, 基础使用, 持久化存储
    Linux nginx+uWSGI+django+virtualenv+supervisor发布web服务器
    Linux Nginx
    Linux virtualenv, virtualenvwrapper, pip freeze
    Linux Python安装
    Redis, Python操作Redis, Linux操作Redis, Redis命令, Redis发布订阅, Redis持久化, Redis主从同步
    爬虫 selenium
  • 原文地址:https://www.cnblogs.com/-flq/p/9444012.html
Copyright © 2011-2022 走看看