VUE 自定义左侧菜单

效果图

 CustomLeftMenu.vue 

右边浮动大白块,分页模式显示二级、三级菜单

<template>
  <div class="menuPage">
    <div class="page" v-for="(item, i) in pageList" :key="i">
      <div v-for="(menu, j) in menuArray" :key="j">
        <div
          v-if="
            j + 1 >= i * colCount + 1 && j + 1 < i * colCount + 1 + colCount
          "
        >
          <h3
            class="title" style="margin:0px"
            v-if="menu.type == 'title'"
          >
            {{ menu.data.meta.title }}
          </h3>
          <div class="menu" v-if="menu.type == 'menu'">
            <span @click="handleChild(menu.pdata, menu.data)">{{
              menu.data.meta.title
            }}</span>
          </div>
          <div class="kong" v-if="menu.type == 'kong'">
            <span>&nbsp;</span>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: "CustomLeftMenu",
  props: {
    //一列显示几个
    colCount: {
      type: Number,
      require: true,
      default: () => {
        return 6;
      },
    },
    // 菜单数据
    menuList: {
      type: Array,
      require: true,
      default: [],
    },
  },
  mounted() {
    //重新组装数据
    try{
        for (var rootMenu of this.menuList) {
          this.menuArray.push({ type: "title", data: rootMenu });
          
          for (var menu of rootMenu.children) {
            if(menu.hidden==true){
              continue;
            }
            this.menuArray.push({ type: "menu", data: menu, pdata: rootMenu });
          }
          //补空
          this.menuArray.push({ type: "kong", data: null });
        }
        //分页
        var page = Math.ceil(this.menuArray.length / this.colCount);

        for (var i = 0; i < page; i++) {
          this.pageList.push(i + 1);
        }
    }catch{

    }
  },
  data() {
    return {
      //重新组装的数据
      menuArray: [],
      //可以分几页
      pageList: [],
    };
  },
  methods: {
    handleChild(root, child) {
      this.$emit("change-child", root, child);
    },
  },
};
</script>
<style lang="scss" scoped>
.menuPage {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  margin: 10px 0px 10px 10px;
}
.page {
  margin-right: 10px;
}
.title {
  border-left: 4px solid #304156;
  padding-left: 10px;
  height: 20px;
  line-height: 20px;
  font-weight: bold;
  font-size: 16px;
  text-align: left;
}
.menu {
  width: 120px;
  height: 25px;
  color: #676767;
  padding-left: 5px;
  padding-top: 2px;
  margin-top: 5px;
  margin-bottom: 5px;
  white-space:nowrap; /*不换行 */
  overflow:hidden; /*内容超出宽度时隐藏超出部分的内容 */
  text-overflow:ellipsis;/* 当对象内文本溢出时显示省略标记(...) ;需与overflow:hidden;一起使用。*/
}
.menu:hover{
  background-color: #304156;
  color: white;
}
.kong {
  height: 20px;
  color: #676767;
  padding-left: 5px;
  padding-top: 2px;
  margin-top: 5px;
  margin-bottom: 5px;
}
</style>

index.vue

一级菜单+二级菜单

<template>
  <div class="custom-menu-container">
    <logo v-if="showLogo" :collapse="isCollapse" />
    <div class="custom-menu-wrapper">
      <div
        :class="[
          'custom-menu-item',
          { 'small-coll': isCollapse },
          { 't-active': currentIndex === index },
        ]"
        v-for="(item, index) in menuList"
        :key="index"
        @mouseleave="handleHide"
        @mouseover="showToggle(item, index)"
      >
        <h3 :class="['custom-menu-title']">
          <template v-if="isCollapse">
            <item-icon v-if="item.meta" :icon="item.meta && item.meta.icon" />
          </template>
          <template v-else>
            <item-icon
              v-if="item.meta"
              :icon="item.meta && item.meta.icon"
              :title="item.meta.title"
            />
          </template>
        </h3>
        <i
          class="el-icon-caret-right arrow-icon"
          v-if="!isCollapse"
          @mouseover="showToggle(item, index)"
        ></i>
        <div
          @mouseover="showToggle(item, index)"
          @mouseleave="handleHide"
          :style="'top:'+getMenuHeight(index,menuList.length)+'px'"
          :class="[
            'dynamic-menu-child',
            { 'dynamic-active': currentIndex === index },
            { 'space-left': isCollapse },
          ]"
        >
          <i
            class="el-icon-close arrow-close"
            @click="handleHide"
            title="点击关闭"
          ></i>
          <div class="conten-list">
            <CustomLeftMenu
              :menuList="item.children"
			        :colCount="10"
              @change-child="handleGrandIitem"
            />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import path from "path";
import { mapGetters, mapState } from "vuex";
import CustomLeftMenu from "./CustomLeftMenu";
import Empty from "./Empty";
import MenuItem from "./MenuItem";
import ItemIcon from "./ItemIcon";
import Logo from "./Logo";
export default {
  components: { CustomLeftMenu, Empty, MenuItem, ItemIcon, Logo },

  data() {
    return {
      tempList: [],
      isShowChildren: false,
      currentIndex: -1,
      childrenMenu: null, // 当前子菜单
    };
  },
  props: {
    menuList: {
      type: Array,
      default: () => [],
    },
  },
  computed: {
    ...mapState(["settings"]),
    ...mapGetters(["sidebarRouters", "sidebar"]),
    showLogo() {
      return this.$store.state.settings.sidebarLogo;
    },
    isCollapse() {
      return !this.sidebar.opened;
    },
  },
  methods: {
    getMenuHeight(index,menuLength){
      if(index ==menuLength - 1){
        return -286;
      }
      if(index ==menuLength - 2){
        return -251;
      }
      return 0;
    },
    handleChildIitem(value) {
      try {
        this.$router.push(path.resolve(this.childrenMenu.path, value.path));
        setTimeout(() => {
          this.handleHide();
        }, 500);
      } catch (error) {
        console.log(error);
      }
    },
    handleGrandIitem(root,node) {
      try {
        if(node.query==undefined){
          node.query="{}";
        }
        var url =path.resolve(this.childrenMenu.path, `${root.path}/${node.path}`);
        var query=JSON.parse(node.query);
        
        this.$router.push({path:url,query:query});
        setTimeout(() => {
          this.handleHide();
        }, 1000);
      } catch (error) {
        console.log(error);
      }
    },
    showToggle(item, index) {
      this.currentIndex = index;
      this.childrenMenu = item;
      // this.isShowChildren = true
    },
    handleHide() {
      // this.isShowChildren = false
      // console.log('handleHide')
      this.currentIndex = -1;
    },
  },
};
</script>
<style lang="scss" scoped>
@import "@/assets/styles/custom-menu.scss";
@import "./style.scss";
.menu-items {
  display: block;
}
::v-deep .custom-sub-menu-item .sub-menu-item-title {
  display: block;
}
::v-deep .custom-sub-menu-item {
  color: #bfcbd9;
}
.custom-menu-container .custom-menu-item .dynamic-menu-child .conten-list {
  min-height: 0px;
  padding-left: 10px;
  
}

</style>

最外层调用,给菜单传数据

<template>
    <div style="background-color:#304156;">
    <!-- <div v-if="user.menuMode==='menuFold'" :class="{'has-logo':showLogo}" :style="{ backgroundColor: settings.sideTheme === 'theme-dark' ? variables.menuBg : variables.menuLightBg }">
        <logo v-if="showLogo" :collapse="isCollapse" />
        <el-scrollbar :class="settings.sideTheme" wrap-class="scrollbar-wrapper">
           <el-menu
                :default-active="activeMenu"
                :collapse="isCollapse"
                :background-color="settings.sideTheme === 'theme-dark' ? variables.menuBg : variables.menuLightBg"
                :text-color="settings.sideTheme === 'theme-dark' ? variables.menuText : 'rgba(0,0,0,.65)'"
                :unique-opened="true"
                :active-text-color="settings.theme"
                :collapse-transition="false"
                mode="vertical"
            >
                <sidebar-item
                    v-for="(route, index) in sidebarRouters"
                    :key="route.path  + index"
                    :item="route"
                    :base-path="route.path"
                />
            </el-menu>
        </el-scrollbar>
    </div> -->
    <!-- 自定义菜单  -->
    <custom-menus :menuList="menuList"></custom-menus>
    </div>
</template>

<script>
import { mapGetters, mapState } from "vuex";
import Logo from "./Logo";
import SidebarItem from "./SidebarItem";
import variables from "@/assets/styles/variables.scss";
// import CustomMenus from '@/components/CustomMenus';
import CustomMenus from '../CustomMenus';

export default {
    components: { SidebarItem, Logo, CustomMenus },
    data() {
        return {
            menuList:[]
        }
    },
    computed: {
        ...mapState(["settings", "user"]),
        ...mapGetters(["sidebarRouters", "sidebar"]),
        activeMenu() {
            const route = this.$route;
            const { meta, path } = route;
            // if set path, the sidebar will highlight the path you set
            if (meta.activeMenu) {
                return meta.activeMenu;
            }
            return path;
        },
        showLogo() {
            return this.$store.state.settings.sidebarLogo;
        },
        variables() {
            return variables;
        },
        isCollapse() {
            return !this.sidebar.opened;
        },
        
    },
    methods:{
        getCount(list){
            var count=0;
            for(var item of list){
                const arrays=item.children ?? [];
                count+=arrays.length;
                count+=this.getCount(arrays);
            }
            return count;
        }
    },
    mounted() {
        this.menuList = this.sidebarRouters.filter(e => e.hidden === false)
        for(var item of this.menuList){
            const arrays=item.children ?? [];
            var count=arrays.length;
            count+=this.getCount(arrays);
            item.meta.count=count;
        }
    },
};
</script>

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值