vue 二级菜单

1,103 阅读1分钟

vue带展开动画二级菜单,大家如果有更好的写法欢迎交流

index.vue

<template>
    <div class="left-nav"
         :class="{'left-nav_close': navClose}"
         ref="leftNav">
        <div class="nav-inner">
            <el-scrollbar>
                <menu-item :navClose="navClose"></menu-item>
            </el-scrollbar>
        </div>
    </div>
</template>
<script>
import MenuItem from "./NavItem";
export default {
    components: {
        MenuItem,
    },
    data () {
        return {
          navClose: false,
        };
    },
    computed: {

    },
    watch: {},
    methods: {
        clickPutBtn () {
            this.navClose = !this.navClose;
        },
    },
    created () {
    },
    mounted () { },
};
</script>
<style lang='scss' scoped>
.left-nav {
    // width: 265px;
    flex: none;
    height: 100%;
    user-select: none;
    color: #333;
    background: #fff;
    font-weight: 600;
    font-size: $f14;
    position: relative;
    transition: width 0.03s linear;
    padding-top: 10px;
    box-sizing: border-box;
    @include boxShadow;
    &.left-nav_close .nav-inner {
        width: 50px;
        overflow: hidden;
    }
    .nav-inner {
        height: 100%;
        // width: 225px;
        border-top: 1px solid #4c84f6;
        box-sizing: border-box;
        padding-top: 6px;
        .el-scrollbar {
            height: 100%;
            ::v-deep .el-scrollbar__wrap {
                overflow-x: hidden;
                overflow-y: auto;
            }
            ::v-deep .is-horizontal {
                display: none;
            }
        }
    }

    .put-btn {
        position: absolute;
        right: 0;
        top: 50%;
        transform: translate3d(100%, -50%, 0);
        // height: 50px;
        width: 16px;
        height: 40px;
        @include flex;
        border-top-right-radius: 9px;
        border-bottom-right-radius: 9px;
        background: $blue-color;
        cursor: pointer;
        z-index: 9;
        .el-icon-arrow-left {
            height: 60%;
            width: auto;
            flex: none;
            color: #fff;
            @include flex;
            font-size: $f16;
        }
        &.put-btn_close {
            .el-icon-arrow-left {
                transform: rotate(180deg);
            }
        }
    }
}
</style>

NavItem.vue

<template>
<ul class="list1">
  <li class="list1-item" v-for="item in navData" :key="item.id" >
    <div class="list1-title" :class="{'list1-title_active': navActive1 == item.id}" @click="clickItem(1,item)">
      <span class="list1-title_text">{{item.display}}</span>
      <i class="list-title_arrow el-icon-caret-right" v-if="item.subMenu && item.subMenu.length>0"></i>
    </div>
    <transition name="fade">
      <ul class="list2" v-show="navActive1 == item.id" v-if="item.subMenu && item.subMenu.length>0" :style="{height: item.subMenu.length * 38 + 'px' }">
        <li class="list2-item" v-for="(item2) in item.subMenu" :key="item2.id">
          <div class="list2-title" :class="{'list2-title_active': navActive2 == item2.linkAction}" @click="clickItem(2,item2)">
            <i :class="`iconfont ${item2.iconClass}`"></i>
            <span class="list2-title_text">{{item2.display}}</span>
          </div>
        </li>
      </ul>
    </transition>
  </li>
</ul>
</template>
<script>
import { mapState, mapMutations } from "vuex";
export default {
    props: {
        navClose: {
            type: Boolean,
            default: false,
        },
    },
    inject:['reload'],
    components: {},
    data () {
        return {
            navActive1: "", // 是否选中
            navActive2: "",  //  是否展开
            navArr: [], // 展示的导航列表
        };
    },
    computed: {
      ...mapState(['navData'])
    },
    watch: {
        navClose (val) {
            if (val) {
              this.navActive1 = "";
            }
        },
        '$route': 'routeChange'
    },
    methods: {
        clickItem (level, { id, linkAction }) { // 点击导航
          const objStr = `navActive${level}`;
          if(this[objStr] == id){ //  重复点击
            if(linkAction){
               this.reload();
            }else {
              this[objStr] = '';
            }
            return;
          }
          if(level == 1){
             this[objStr] = id;
             this.$Storage.set(objStr,id);
          }
          
          if(level == 2 && linkAction){
            this.$router.push({ path: linkAction });
          }
        },
        routeChange (val) {  // 手动输入网址 匹配标题和选中状态
          if(val){
            let path = val.path;
            this.$wlhStorage.set('navActive2',path);
            this.navActive2 = path;  //  设置样式选中  
          }
        },
        initNavStatus () { // 初始化nav的状态
            this.navActive1 = this.$wlhStorage.get('navActive1') || "";
            this.navActive2 = this.$wlhStorage.get('navActive2') || "";
            if(!this.navActive2) this.navActive2 = this.$route.fullPath;
        }
    },
    created () {
      this.initNavStatus();
    },
    mounted () { },
};
</script>
<style lang='scss' scoped>
.list1 {
  width: 215px;
  margin: 0 20px;
  .list1-title {
    @include font(14px,#333,40px,bold);
    position: relative;
    cursor: pointer;
    &:hover {
      text-decoration: underline;
    }
    &::before {
      content: "";
      display: block;
      height: 14px;
      width: 4px;
      background: #5892D2;
      position: absolute;
      left: 0;
      top: 50%;
      transform: translateY(-50%);
    }
    &.list1-title_active {
      .list-title_arrow {
        transform: translateY(-50%) rotate(90deg);
      }
    }
    .list1-title_text {
      margin-left: 12px;
    }
    .list-title_arrow {
      position: absolute;
      right: 0;
      top: 50%;
      transform: translateY(-50%);
      color: #9CA1AE;
    }
  }
  .list2 {
    overflow: hidden;
    .list2-title {
      line-height: 38px;
      padding-left: 18px;
      font-weight: 600;
      cursor: pointer;
      border-radius: 19px;
      &.list2-title_active {
        background: #1273E0 !important;
        color: #fff;
        .iconfont {
          color: #fff;
        }
      }
      &:hover {
        background: rgba($color: #1273E0, $alpha: 0.1);
      }
      .iconfont {
        color: #164DC0;
        margin-right: 10px;
      }
    }
  }
}
/*过渡动画*/ 
.fade-enter-active,
.fade-leave-active {
    transition: height 0.05s linear;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
    height: 0 !important;
}

</style>