zoukankan      html  css  js  c++  java
  • 基于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>

    效果:

  • 相关阅读:
    html background 背景颜色美化 类似毛玻璃
    HTML
    export、exports、modules.exports 和 require 、import 的一些组合套路和坑
    C#实现监控网络流量
    PHP乱码问题,UTF-8(乱码)
    LitDB笔记
    LitDB文章
    NoSQL 35 个非主流数据库
    mysql中int转varchar
    CSS设置DIV背景色渐变显示
  • 原文地址:https://www.cnblogs.com/Mr-lin66/p/12859599.html
Copyright © 2011-2022 走看看