Vue-cli + Element-UI 利用递归动态生成菜单栏

Vue-cli + Element-UI 利用递归动态生成菜单栏

菜单项列表文件

// menu.js
export default [
  {
    title: '住院病人病情评估表',
    key: '0',
    meta: '',
  }, {
    title: '医护各类常用表格',
    key: '1',
    meta: '',
    children: [{
      title: '医疗安全不良事件报告表',
      key: '1-0',
      meta: ''
    }, {
      title: '科室不良事件记录表',
      key: '1-1',
      meta: ''
    }, {
      title: '病危告知书',
      key: '1-2',
      meta: '',
      children: [{
        title: 'A类病危通知书',
        key: '1-2-0',
        meta: ''
      }, {
        title: 'B类病危通知书',
        key: '1-2-1',
        meta: ''
      }, {
        title: 'C类病危通知书',
        key: '1-2-2',
        meta: '',
      }]
    }]
  }, {
    title: '全国通用标准模板',
    key: '2',
    meta: '',
    children: [{
      title: '出院记录模板',
      key: '2-0',
      meta: ''
    },{
      title: '手术记录模板',
      key: '2-1',
      meta: ''
    },{
      title: '手术知情同意书',
      key: '2-2',
      meta: ''
    }]
  }
]

注:菜单项的 meta 字段可以是任意数据类型,建议是对象,预留这个字段的目的是为了让菜单项携带其他需要的数据。

递归的菜单组件

有些项目中的图片路径、样式等等,我没有删除,大家自行过滤。

<!-- menu-item.vue -->
<template>
  <div
    class="menu-item"
    :class="openList.includes(menu.key) ? 'open-menu' : ''"
  >
    <div
      class="menu-title"
      @click="handleTab(menu)"
      :class="menu.key === current.key ? 'active-menu' : ''"
    >
      <i
        class="icon"
        :class="openList.includes(menu.key) ? 'el-icon-folder-remove' : 'el-icon-folder-add'"
        :style="{opacity: menu.children ? '1' : '0'}"
      ></i>
      <img src="../../assets/imgs/template-list/icon.png" alt="">
      <span>{{ menu.title }}</span>
    </div>
    <div v-if="menu.children" class="sub-menu">
      <menu-item
        v-for="subItem in menu.children"
        :currentMenu="currentMenu"
        :key="subItem.key"
        :menu="subItem"
        :tap-menu="tapMenu"
        :tap-submenu="tapSubmenu"
      />
    </div>
  </div>
</template>

<script>
  export default {
    name: "menu-item",
    props: ['menu', 'currentMenu', 'tapMenu', 'tapSubmenu'],
    data() {
      return {
        openList: ['1']
      }
    },
    computed: {
      current() {
        return this.$store.state.templateEdit.currentTemplateMenu;
      }
    },
    methods: {
      handleTab(menu) {
        if(menu.children) { 
          // 点击父级菜单,展开或合并子菜单
          const index = this.openList.indexOf(menu.key);
          if(index < 0) this.openList.push(menu.key);
          else this.openList.splice(index, 1);
          // 点击某个父级菜单的回调,可以在调用时传入
          this.tapMenu && this.tapMenu(menu);
        } else { 
          // 点击无子级的某个菜单,代表选中了某个菜单并且要执行动作,示范中向 store 提交了一个设置当前菜单值的事件
          this.$store.commit('set_template_menu', menu);
          // 点击无子级的某个菜单的回调
          this.tapSubmenu && this.tapSubmenu(menu);
        }
      }
    }
  }
</script>

<style scoped lang="less">
  .menu-item {
    color: #999;
    font-size: 12px;
    * {
      vertical-align: middle;
    }
    img {
      width: 12px;
      margin-right: 5px;
    }
    .icon {
      margin-right: 5px;
    }
    .sub-menu {
      padding-left: 15px;
      color: #999;
      height: 0;
      overflow-y: hidden;
    }
    .menu-title {
      padding: 5px 0;
      cursor: pointer;
      :hover {
        color: #333;
      }
    }
    .active-menu {
      background: #f7f8fd;
    }
  }
  .menu-item.open-menu {
    >.sub-menu {
      height: auto;
    }
  }
</style>

调用

<!-- home.vue -->
<template>
  <div class="template-menu">
    <MenuItem
      v-for="item in menuList"
      :menu="item"
      :key="item.key"
      :tap-menu="tapMenu"
      :tap-submenu="tapSubmenu"
    />
  </div>
</template>

<script>
  import MenuItem from './menu-item.vue'
  import menuList from './menu.js'
  export default {
    name: "template-menu",
    components: {
      MenuItem
    },
    data() {
      return {
        menuList
      }
    },
    methods: {
      tapMenu(menu) {
      	console.log(menu);
      },
      tapSubmenu(menu) {
		console.log(menu);
	  }
    }
  }
</script>

store.js

之所以要将当前被选中的菜单项保留在 store 中,是因为菜单项在递归的过程中,形成了类似于闭包的嵌套上下文环境,数据各自独立,要整体使用并查看选中效果时,无法通过 $emit 或父级的数据确定选中项,所以需要借助 store

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    templateEdit: {
      currentTemplateMenu: {}
    }
  },
  mutations: {
    set_template_menu(state, v) {
      state.templateEdit.currentTemplateMenu = v;
    }
  }
})

猜你喜欢

转载自blog.csdn.net/FlowGuanEr/article/details/105978137