基於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="width: 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>

效果:

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM