基于element-ui定义自己的Menu 菜单组件并以component方式去定渲染

生成菜单方法createMenu.jsimport navMenu from "./navMenu";

/**
 * 菜单组件
 * @module widgets/my-menu
 * @example
 *
 * // 使用说明
 */
export default {
  name: "createMenu",
  mixins: [navMenu],
  /**
   * 属性参数
   * @property {Array} [data = []] data 构成菜单内容的数组
   * @property {Object} [props = { id: 'id', text: 'text', icon: 'icon', children: 'children', group: 'group', route: 'route' }] props 指引菜单组件根据data数组中元素的键名作为菜单中对应的显示内容。例如:数组内元素为 {g_id:1, str:'第一组'}, 可以通过设置"props={id: 'g_id', text: 'str'}"来将数组的g_id对应为id,str对应为text
   * @property {String} [mode = 'verticle'] mode 默认为垂直模式,横向 ‘horizontal’
   * @property {Boolean} [collapse = false] 是否水平折叠收起菜单(仅在 mode 为 vertical 时可用)
   * @property {String} [backgroundColor] backgroundColor='#FFFFFF' 菜单的背景色(仅支持 hex 格式)
   * @property {String} [textColor] 菜单的文字颜色(仅支持 hex 格式)
   * @property {String} [activeTextColor] 当前激活菜单的文字颜色(仅支持 hex 格式)
   * @property {String} [defaultActive] 当前激活菜单的 index。 defaultActive = '1'
   * @property {Array} [defaultOpeneds] 当前打开的sub-menu的 key 数组。 defaultOpeneds = ['2', '4']
   * @property {Boolean} [uniqueOpened = false] 是否只保持一个子菜单的展开
   * @property {String} [menuTrigger = 'click'] 子菜单打开的触发方式(只在 mode 为 horizontal 时有效)
   * @property {Boolean} [router = false] 是否使用 vue-router 的模式,启用该模式会在激活导航时以 index 作为 path 进行路由跳转
   */
  props: {
    // 菜单数据
    data: {
      type: Array,
      default() {
        return [];
      }
    },
    // 数据字段名称映射
    props: {
      type: Object,
      default() {
        return {
          id: "id",
          text: "text",
          icon: "icon",
          children: "children",
          group: "group",
          route: "route"
        };
      }
    },
    // 模式 horizontal / vertical
    mode: String,
    // 是否水平折叠收起菜单(仅在 mode 为 vertical 时可用)
    collapse: Boolean,
    // 菜单的背景色(仅支持 hex 格式)
    backgroundColor: String,
    // 菜单的文字颜色(仅支持 hex 格式)
    textColor: String,
    // 当前激活菜单的文字颜色(仅支持 hex 格式)
    activeTextColor: String,
    // 当前激活菜单的 index
    defaultActive: String,
    // 当前打开的sub-menu的 key 数组
    defaultOpeneds: Array,
    // 是否只保持一个子菜单的展开
    uniqueOpened: Boolean,
    // 子菜单打开的触发方式(只在 mode 为 horizontal 时有效)
    menuTrigger: String,
    // 是否使用 vue-router 的模式,启用该模式会在激活导航时以 index 作为 path 进行路由跳转
    router: Boolean,
    // 弹出菜单的自定义类名
    popperClass: String
  },
  methods: {
    grouping(array) {
      let groups = {
        default: []
      };
      let props = this.props;
      array.forEach(n => {
        let key = n[props.group];
        if (key) {
          groups[key] = groups[key] || [];
          groups[key].push(n);
        } else {
          groups["default"].push(n);
        }
      });
      return groups;
    },
    createTitle(h, item) {
      return [
        h("i", {
          class: item[this.props.icon]
        }),
        h(
          "span",
          {
            slot: "title"
          },
          item[this.props.text]
        )
      ];
    },
    createItem(h, item) {
      const key = (item[this.props.id] || "").toString();
      return h(
        "el-menu-item",
        {
          props: {
            index: key,
            route: item[this.props.route],
            name: item[this.props.text]
          },
          key: key,
          on: {
            // eslint-disable-next-line no-undef
//绑定点击事件 点击事件来源navMenu.js
click: (index, indexPath) => this.clickMenu(index, indexPath, item[this.props.text]) } }, this.createTitle(h, item) ); }, createSubmenu(h, item) { const key = (item[this.props.id] || "").toString(); return h( "el-submenu", { props: { index: (item[this.props.id] || "").toString(), popperClass: this.popperClass }, key: key }, [ h( "template", { slot: "title" }, this.createTitle(h, item) ), this.createNodes(h, item[this.props.children]) ] ); }, createGroup(h, title, items) { let nodes = []; let props = this.props; items.forEach(item => { if (item[props.children] && item[props.children].length > 0) { nodes.push(this.createSubmenu(h, item)); } else { nodes.push(this.createItem(h, item)); } }); nodes.unshift( h( "template", { slot: "title" }, title ) ); return h("el-menu-item-group", null, nodes); }, createNodes(h, array) { let nodes = [], groups = this.grouping(array); let props = this.props; for (let key in groups) { let items = groups[key] || []; if (key === "default") { items.forEach(item => { if (item[props.children] && item[props.children].length > 0) { nodes.push(this.createSubmenu(h, item)); } else { nodes.push(this.createItem(h, item)); } }); } else { nodes.push(this.createGroup(h, key, items)); } } return nodes; }, open(index) { console.log(index); this.$refs.menu.open(index); }, close(index) { this.$refs.menu.close(index); } }, render(h) { return h( "el-menu", { props: { ...this.$props }, class: "my-menu", on: { /** * 触发选中事件 * @event select * @param index {String} 选中列表id * @param indexPath {Array} 选中列表对应的路径 如:['item1', 'item1-1'] */ select: (index, indexPath) => this.$emit("select", index, indexPath), /** * 触发列表组(子菜单)展开事件 * @event open * @param index {String} 选中列表id * @param indexPath {Array} 选中列表对应的路径 如:['item1', 'item1-1'] */ open: (index, indexPath) => this.$emit("open", index, indexPath), /** * 触发列表组(子菜单)收起事件 * @event close * @param index {String} 选中列表id * @param indexPath {Array} 选中列表对应的路径 如:['item1', 'item1-1'] */ close: (index, indexPath) => this.$emit("close", index, indexPath) }, ref: "menu" }, this.createNodes(h, this.data) ); } };

绑定点击事件js  navMenus.js

export default {
  name: "navMenu",
  methods: {
    clickMenu(index, indexPath, text) {
      // console.log(index, indexPath, text);
      console.log("左边", text);
      let componentName = index.$options.propsData.route;
      this.openedTab = this.$store.state.openedTab;
      // tabNum 为当前点击的列表项在openedTab中的index,若不存在则为-1
      let tabNum = this.openedTab.indexOf(componentName);
      if (tabNum === -1) {
        // 该标签当前没有打开
        // 将componentName加入到已打开标签页state.openedTab数组中
        this.$store.commit("addTab", { name: text, componentName });
      } else {
        // 该标签是已经打开过的,需要激活此标签页
        this.$store.commit("changeTab", componentName);
      }
    }
  },
  data() {
    return {
      openedTab: []
    };
  }
};

菜单数组配置JS menu-config.js

module.exports = [
  {
    id: "Index",
    text: "首页",
    icon: "el-icon-star-on",
    route: "BasicIndex"
  },
  {
    id: "log",
    text: "日志",
    icon: "el-icon-s-order",
    route: "BasicLayout"
  },
  {
    id: "basic",
    text: "个人中心",
    icon: "el-icon-s-custom",
    children: [
      {
        id: 21,
        text: "选项一"
      },
      {
        id: 22,
        text: "选项二"
      },
      {
        id: 23,
        text: "选项三"
      }
    ]
  }
];

生成菜单 navMenu.vue

<!--本页为左侧下拉菜单-->
<template>
  <el-row class="tac" style=" 200px;">
    <el-col :span="24">
      <createMenu
        :data="menu"
        backgroundColor="#545c64"
        textColor="#FFFFFF"
      ></createMenu>

    </el-col>
  </el-row>
</template>

<script>
import menu from "@/config/menu-config.js";
import createMenu from "./createMenu";
export default {
  components: {
    createMenu
  },
  name: "navMenu",
  data() {
    return {
      menu: menu
    };
  }
};
</script>

<style scoped>
.over-hide {
  overflow-x: hidden;
}
.el-submenu__title {
  border-bottom: 1px solid #8d98a2;
}
</style>
渲染组件页面navMain.vue
<!--本页为tab标签-->
<template>
  <div>
    <el-tabs
      v-model="editableTabsValue"
      type="card"
      closable
      @tab-remove="removeTab"
      @tab-click="handleClickTab($event.name)"
    >
      <el-tab-pane
        :key="item.name"
        v-for="item in editableTabs"
        :label="item.title"
        :name="item.name"
      >
      </el-tab-pane>
    </el-tabs>
    <!--动态组件 ,更具is的值来确定渲染哪个组件 -->
    <component :is="componentId"></component>
  </div>
</template>

<script>
export default {
  name: "navMain",
  data() {
    return {
      componentId: () =>
        import("@/components/navMain/mainComponents/BasicIndex.vue"),
      editableTabsValue: "BasicIndex",
      editableTabs: [
        {
          title: "首页",
          name: "BasicIndex"
        }
      ],
      tabIndex: 1,
      openedTab: ["BasicIndex"]
    };
  },
  methods: {
    handleClickTab(route) {
      this.$store.commit("changeTab", route);
      this.IsShowComponents(route);
      // this.$router.push(route);
    },
    removeTab(targetName) {
      // 首页不允许被关闭(为了防止el-tabs栏中一个tab都没有)
      if (targetName === "BasicIndex") {
        return false;
      }
      let tabs = this.editableTabs;
      let activeName = this.editableTabsValue;
      if (activeName === targetName) {
        tabs.forEach((tab, index) => {
          if (tab.name === targetName) {
            let nextTab = tabs[index + 1] || tabs[index - 1];
            if (nextTab) {
              activeName = nextTab.name;
            }
          }
        });
      }
      this.$store.commit("deductTab", targetName);
      let deductIndex = this.openedTab.indexOf(targetName);
      this.openedTab.splice(deductIndex, 1);
      this.IsShowComponents(activeName);
      this.editableTabsValue = activeName;
      this.editableTabs = tabs.filter(tab => tab.name !== targetName);
      if (this.openedTab.length === 0) {
        this.$store.commit("addTab", "BasicIndex");
        this.IsShowComponents("BasicIndex");
        // this.$router.push("BasicIndex");
      }
    },
    IsShowComponents(newTab) {
      this.componentId = () =>
        import("@/components/navMain/mainComponents/" + newTab + ".vue");
    }
  },
  computed: {
    getOpenedTab() {
      return this.$store.state.openedTab;
    },
    changeTab() {
      return this.$store.state.activeTab;
    }
  },
  watch: {
    getOpenedTab(val) {
      if (val.length > this.openedTab.length) {
        // 添加了新的tab页
        // 导致$store.state中的openedTab长度
        // 大于
        // 标签页中的openedTab长度
        // 因此需要新增标签页
        let newTitle = this.$store.state.openedTitle[val.length - 1];
        let newTab = val[val.length - 1]; // 新增的肯定在数组最后一个
        this.IsShowComponents(newTab);
        ++this.tabIndex;
        this.editableTabs.push({
          title: newTitle,
          name: newTab,
          content: ""
        });
        this.editableTabsValue = newTab;
        this.openedTab.push(newTab);
      }
    },
    changeTab(val) {
      // 监听activetab以实现点击左侧栏时激活已存在的标签
      if (val !== this.editableTabsValue) {
        this.editableTabsValue = val;
        this.IsShowComponents(val);
      }
    }
  },
  created() {
    // 刷新页面时(F11)
    // 因为<router-view>的<keep-alive>,会保留刷新时所在的router
    // 但是tab标签页因为刷新而被重构了,tab没有了
    // 因此需要将router回到index
    this.$router.push("Home");
  }
};
</script>

<style scoped></style>

效果:

原文地址:https://www.cnblogs.com/Mr-lin66/p/12859599.html