vue+elementUI组件递归实现可折叠动态渲染多级侧边栏导航

2022-04-15 0 782

早就实现了功能,但是发现点击的时候,选中的菜单项背景色会变白,周五时候仔细观察了一下,发现并不是调整样式的问题,而是选项没有被选中,于是好好研究了一下组件递归这块,总结记录一下心路历程

一、概念

递归:递归其实说白了,就是自己调用自己,样子就像是套娃一个套一个的,小时候玩过一个游戏汉诺塔就是利用的递归原理:

vue+elementUI组件递归实现可折叠动态渲染多级侧边栏导航

函数递归:函数利用函数名还调用自己
组件递归:所以组件递归利用的是vue组件中的name属性来实现的

二、需求

实现可折叠动态渲染多级侧边栏导航

三、分析

vue+elementUI组件递归实现可折叠动态渲染多级侧边栏导航

1、观察到侧边栏导航是一级一级的,第二级就相当于再重复一遍第一级 2、有一个特点,有的菜单有下级,有的没有下一级 3、动态渲染,说明是从后台接口获取的树类型数据,动态的渲染上去 四、代码实现 1、首先先执行一下文档里的demo试一下:

文档:element文档

2、改成自己需要的样式,第一次是这么写的

父组件SideBar

<template>
  <el-menu class="menu-wrap" :default-active="menuActiveName || 'home'" :active="menuActiveName || 'home'"
           :collapse="sidebarFold" :collapseTransition="false" :unique-opened="true" @select="selectItem">
    <template>
      <el-menu-item @click="sidebarFold = !sidebarFold">
        <i v-show="!sidebarFold" class="el-icon-s-fold"></i>
        <i v-show="sidebarFold" class="el-icon-s-unfold"></i>
        <span slot="title" class="sidebar-one">导航列表</span>
      </el-menu-item>
    </template>
<!--    <side-bar-item :list="menuList"></side-bar-item>-->
    <template v-for="(item,index) in menuList" class="menu">
      <!-- 标题 -->
      <template v-if="item.children.length" >
        <el-submenu :key="index" :index="item.id" class="sub-menu-item">
          <template :index="item.index" slot="title">
            <!--            <i :class="item.icon"></i>-->
            <i class="iconfont icon-danganjianying"></i>
            <span>{{item.name}}</span>
          </template>
          <el-menu-item-group class="menu-item-group">
            <side-bar-item :list="item.children"></side-bar-item>
          </el-menu-item-group>
        </el-submenu>
      </template>
      <!-- 选项 -->
      <template v-else>
        <el-menu-item :key="index" :index="item.id" class="menu-item">
          <!--          <i :class="item.icon"></i>-->
          <i class="iconfont icon-danganjianying"></i>
          <span>{{item.name}}</span>
        </el-menu-item>
      </template>
    </template>
  </el-menu>
</template>

<script>

export default {
  name: 'SideBar',
  components: {
    SideBarItem: () => import('@/components/common/SideBarItem')
  },
  data () {
    return {

    }
  },
  mounted () {
  },
  methods: {
    selectItem(name, path){
      // alert(name)
      this.$router.push(path)
      this.$store.commit('common/updateMenuActiveName', name)
    }
  },
  computed: {
    menuList: {
      get () {
        return this.$store.state.common.menuList
      },
      set (val) {
        this.$store.commit('common/updateMenuList', val)
      }
    },
    menuActiveName: {
      get () { return this.$store.state.common.menuActiveName },
      set (val) { this.$store.commit('common/updateMenuActiveName', val) }
    },
    sidebarFold: {
      get() {return this.$store.state.common.sidebarFold;},
      set(val) {this.$store.commit("common/updateSidebarFold", val);}
    },
  },
}
</script>
<style lang="less" scoped>
.menu-wrap{
  width: 200px;
  min-height: 1020px;
  background: url('../../assets/img/sidebar_bg.png') no-repeat;
  background-size: 100% 100%;
}

/deep/ .el-menu{
  background-color: transparent !important;
  .iconfont {
    font-size: 18px;
    vertical-align: sub;
    margin-right: 5px;
    display: inline-block;
    width: 20px;
    text-align: center;
  }
}

/deep/ .el-menu-item,
/deep/ .el-submenu__title{
  color: #fff;

  .iconfont{
    color: #fff;
  }
}

/deep/ .el-menu-item span,
/deep/ .el-submenu__title span{
  padding-left: 10px;
}

/deep/ .el-menu-item.is-active {
  -webkit-box-shadow: inset 5px 100px 0px -2px #0064B6;
  box-shadow: inset 5px 100px 0px -2px #0064B6;
}

/deep/ .el-submenu__title:hover,
/deep/ .el-menu-item:hover{
  background: #0064B6;
}

/deep/ .el-menu-item-group__title{
  padding: 0;
}

</style>

子组件SideBarItem

<template>
  <div class="menu">
    <template v-for="(item,index) in list">
      <!-- 标题 -->
      <template v-if="item.children.length" >
        <el-submenu :key="index" :index="item.id" class="sub-menu-item">
          <template :index="item.index" slot="title">
<!--            <i :class="item.icon"></i>-->
            <i class="iconfont icon-danganjianying"></i>
            <span>{{item.name}}</span>
          </template>
          <el-menu-item-group class="menu-item-group">
            <side-bar-item :list="item.children"></side-bar-item>
          </el-menu-item-group>
        </el-submenu>
      </template>
      <!-- 选项 -->
      <template v-else>
        <el-menu-item :key="index" :index="item.id" class="menu-item" @click="selectItem(item.name, item.path)">
<!--          <i :class="item.icon"></i>-->
          <i class="iconfont icon-danganjianying"></i>
          <span>{{item.name}}</span>
        </el-menu-item>
      </template>
    </template>
  </div>
</template>
<script>

export default {
  name: 'SideBarItem',
  // props: ['list'],
  props: {
    list: {
      type: Array || ''
    }
  },
  data () {
    return {
      treeData: [{
        label: '某某省',
        children: [{
          label: '中共某某省委员会'
          // children: [{
          // label: '三级 1-1-1'
          // }]
        }, {
          label: '中共某某省办公室'
        }, {
          label: '中共某某省组织部'
        }
        ]
      }
      ],
      isShow: false
      // menuList: []
    }
  },
  mounted () {
    this.loadSysMenu()
  },
  methods: {
    loadSysMenu () {
      // console.log('menu', this.menuList)
    },
    // personManage (name) {
    //   if (name === '人员管理') {
    //     this.isShow = !this.$store.state.common.rbflag
    //     // alert('111' + this.isShow)
    //     this.$store.commit('common/updateShowRbox', this.isShow)
    //   }
    // },
    selectItem(name, path){
      // alert(name)
      this.$router.push(path)
      this.$store.commit('common/updateMenuActiveName', name)
    }
  },
}
</script>
<style lang="less" scoped>
.menu{
  width: 100%;

  .sub-menu-item /deep/ .el-submenu__title,
  .menu-item{
    height: 60px;
    line-height: 60px;
    text-align: left;
    //padding-left: 30px !important;
    //border-bottom: 1px solid #000;
    //border-right: 1px solid #000;
    color: #fff;
  }

  .sub-menu-item .el-menu-item{
    padding-right: 0;
  }

 /deep/ .el-menu-item .is-active{
    background-color: #0087df;
  }

  .menu-item:hover,
  /deep/ .el-submenu__title:hover{
    background-color: #0087df;
  }

  .menu-item span,
  .sub-menu-item /deep/ .el-submenu__title>span{
    font-weight: 700;
  }

  .menu-item-group /deep/ .el-menu-item-group__title{
    padding: 0 !important;
  }

  .menu-item-group .menu-item{
    background: url('../../assets/img/sidebar_bg.png') no-repeat;
  }

  .el-menu-item-group span{
    font-weight: normal;
  }

}

</style>

后来发现折叠不成功,而且选中之后选中项样式没变,后来发现是没选中,研究发现是因为多嵌套了一层div,而且用了el-menu-item-group项目中并不需要这个,于是改进如下:

父组件SideBar

<template>
  <el-menu class="menu-wrap" :default-active="menuActiveName" :collapse="sidebarFold" :collapseTransition="false" :unique-opened="true">
    <template>
      <el-menu-item @click="foldSideBar">
        <i v-show="!sidebarFold" class="el-icon-s-fold"></i>
        <i v-show="sidebarFold" class="el-icon-s-unfold"></i>
        <span slot="title" class="sidebar-one">导航列表</span>
      </el-menu-item>
    </template>
    <side-bar-item v-for="menu in menuList" :key="menu.id" :menu="menu"></side-bar-item>
  </el-menu>
</template>

<script>

export default {
  name: 'SideBar',
  components: {
    SideBarItem: () => import('@/components/common/SideBarItem')
  },
  data () {
    return {
    }
  },
  mounted () {
  },
  methods: {
    foldSideBar(){
      this.sidebarFold = !this.sidebarFold
      this.menuActiveName = 'NAV'
    }
  },
  computed: {
    menuList: {
      get () {
        return this.$store.state.common.menuList
      },
      set (val) {
        this.$store.commit('common/updateMenuList', val)
      }
    },
    menuActiveName: {
      get () {
        console.log(this.$store.state.common.menuActiveName)
        return this.$store.state.common.menuActiveName
      },
      set (val) {
        this.$store.commit('common/updateMenuActiveName', val)
      }
    },
    sidebarFold: {
      get() {return this.$store.state.common.sidebarFold;},
      set(val) {this.$store.commit("common/updateSidebarFold", val);}
    },
  },
}
</script>
<style lang="less" scoped>
.menu-wrap{
  width: 200px;
  min-height: 1020px;
  background: url('../../assets/img/sidebar_bg.png') no-repeat;
  background-size: 100% 100%;
}

/deep/ .el-menu{
  background-color: transparent !important;
  .iconfont {
    font-size: 18px;
    vertical-align: sub;
    margin-right: 5px;
    display: inline-block;
    width: 20px;
    text-align: center;
  }
}

/deep/ .el-menu-item,
/deep/ .el-submenu__title{
  color: #fff;

  .iconfont{
    color: #fff;
  }
}

/deep/ .el-menu-item span,
/deep/ .el-submenu__title span{
  padding-left: 10px;
}

/deep/ .el-menu-item.is-active {
  -webkit-box-shadow: inset 5px 100px 0px -2px #0064B6;
  box-shadow: inset 5px 100px 0px -2px #0064B6;
}

/deep/ .el-submenu__title:hover,
/deep/ .el-menu-item:hover{
  background: #0064B6;
}

</style>

子组件SideBarItem

<template>
    <!--    该菜单下还有子菜单-->
    <el-submenu v-if="menu.children.length" :index="menu.code" :popper-append-to-body=false>
        <template slot="title">
            <i class="iconfont icon-danganjianying"></i>
            <span>{{ menu.name }}</span>
        </template>
        <side-bar-item v-for="item in menu.children" :key="item.id" :menu="item"></side-bar-item>
    </el-submenu>
    <!--    该菜单下无子菜单-->
    <el-menu-item v-else :index="menu.code" @click="selectItem(menu.code, menu.path)">
        <i class="iconfont icon-danganjianying"></i>
        <span>{{ menu.name }}</span>
    </el-menu-item>
</template>
<script>

export default {
  name: 'SideBarItem',
  // props: ['menu'],
  props: {
      menu: {
      type: Object || {}
    }
  },
  data () {
    return {

    }
  },
  mounted () {
  },
  methods: {
    selectItem(code, path){
      // alert(name)
      console.log(code, path)
      this.$router.push(path)
      this.$store.commit('common/updateMenuActiveName', code)
    }
  },
}
</script>
<style lang="less" scoped>
.menu{
  width: 100%;

  .menu-item{
    height: 60px;
    line-height: 60px;
    text-align: left;
    color: #fff;
  }

  .sub-menu-item .el-menu-item{
    padding-right: 0;
  }

 /deep/ .el-menu-item .is-active{
    background-color: #0087df;
  }

  .menu-item:hover{
    background-color: #0087df;
  }

  .menu-item span{
    font-weight: 700;
  }

}

</style>

功能基本实现,但是出现一个bug,当鼠标点折叠时,会出现循环调用某个事件,导致栈溢出报错,查看文章只需对子菜单设置属性 :popper-append-to-body=“false” 即可
参考文章:Element-ui NavMenu子菜单使用递归生成时使用报错

最后附上简单的测试数据:

testData: [
            {"id":"34161C2E8-7348-4439-8899-9A8039AE6AE4","pid":"0","code":"HOME","name":"首页","path":"/home","type":null,"icon":null,"sysId":"2761C2E8-7348-4439-8899-9A8039AE6AE3","orderNo":0,"isCheck":null,"children":[]},
            {"id":"703DBEBD-F92C-4347-9203-F60A73153C3F","pid":"0","code":"WD","name":"温度","path":"/temperature","type":null,"icon":null,"sysId":"2AB00274-73DF-459A-A02E-C79A4D8A8929","orderNo":0,"isCheck":null,"children":[]},
            {"id":"73660AB4-48D3-4BDB-86FD-C8397D4D54EC","pid":"0","code":"BJ","name":"报警","path":"/alarm","type":null,"icon":null,"sysId":"2AB00274-73DF-459A-A02E-C79A4D8A8929","orderNo":0,"isCheck":null,
              "children":[
                {"id":"1C99333D-886F-4AD6-93C4-7C5244E48247","pid":"73660AB4-48D3-4BDB-86FD-C8397D4D54EC","code":"FD","name":"防盗","path":"/burg","type":null,"icon":null,"sysId":"3691C2E8-8848-4439-8899-9A8039AE6AB5","orderNo":0,"isCheck":null,"children":[]},
                {"id":"1DBDF678-F51F-444A-B995-61E5D9CCA5AF","pid":"73660AB4-48D3-4BDB-86FD-C8397D4D54EC","code":"JL","name":"警铃","path":"/bell","type":null,"icon":null,"sysId":"3691C2E8-8848-4439-8899-9A8039AE6AB5","orderNo":0,"isCheck":null,"children":[]},
                {"id":"BFC8C2E1-0E5B-4EEE-B91D-3DABC63FF481","pid":"73660AB4-48D3-4BDB-86FD-C8397D4D54EC","code":"JS","name":"浸水","path":"/immersion","type":null,"icon":null,"sysId":"3691C2E8-8848-4439-8899-9A8039AE6AB5","orderNo":0,"isCheck":null,"children":[]},
                {"id":"BFC8C2E1-0E5B-4EEE-B91D-3DABC63FF482","pid":"73660AB4-48D3-4BDB-86FD-C8397D4D54EC","code":"MJ","name":"门禁","path":"/punch","type":null,"icon":null,"sysId":"3691C2E8-8848-4439-8899-9A8039AE6AB5","orderNo":0,"isCheck":null,"children":[]},
                {"id":"BFC8C2E1-0E5B-4EEE-B91D-3DABC63FF483","pid":"73660AB4-48D3-4BDB-86FD-C8397D4D54EC","code":"ZT","name":"状态","path":"/state","type":null,"icon":null,"sysId":"3691C2E8-8848-4439-8899-9A8039AE6AB5","orderNo":0,"isCheck":null,"children":[]}
              ]
            },
            {"id":"34161C2E8-7348-4439-8899-9A8039AE6AE5","pid":"0","code":"GZ","name":"工作","path":"/work","type":null,"icon":null,"sysId":"3691C2E8-8848-4439-8899-9A8039AE6AB5","orderNo":0,"isCheck":null,
              "children":[]
            },
            {"id":"0CD6B09A-AA43-4AE9-9AC7-29BC5AC83495","pid":"0","code":"SJ","name":"数据","path":"/data","type":null,"icon":null,"sysId":"2AB00274-73DF-459A-A02E-C79A4D8A8929","orderNo":0,"isCheck":null,
              "children":[]
            },
            {"id":"049C670D-A33E-4188-9206-B3F3B5DDE77B","pid":"0","code":"SP","name":"视频","path":"/video","type":null,"icon":null,"sysId":"3691C2E8-8848-4439-8899-9A8039AE6AB5","orderNo":0,"isCheck":null,"children":[]},
            {"id":"0A15DBB6-3241-4C7F-AAD4-5417E7BBECAA","pid":"0","code":"RZ","name":"日志","path":"/log","type":null,"icon":null,"sysId":"2AB00274-73DF-459A-A02E-C79A4D8A8929","orderNo":0,"isCheck":null,
              "children":[]
            }
          ]

效果如图:

vue+elementUI组件递归实现可折叠动态渲染多级侧边栏导航

折叠后如图:

vue+elementUI组件递归实现可折叠动态渲染多级侧边栏导航

到此这篇关于vue+elementUI组件递归实现可折叠动态渲染多级侧边栏导航的文章就介绍到这了,更多相关elementUI可折叠动态侧边栏导航内容请搜索NICE源码以前的文章或继续浏览下面的相关文章希望大家以后多多支持NICE源码!

免责声明:
1、本网站所有发布的源码、软件和资料均为收集各大资源网站整理而来;仅限用于学习和研究目的,您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。 不得使用于非法商业用途,不得违反国家法律。否则后果自负!

2、本站信息来自网络,版权争议与本站无关。一切关于该资源商业行为与www.niceym.com无关。
如果您喜欢该程序,请支持正版源码、软件,购买注册,得到更好的正版服务。
如有侵犯你版权的,请邮件与我们联系处理(邮箱:skknet@qq.com),本站将立即改正。

NICE源码网 JavaScript vue+elementUI组件递归实现可折叠动态渲染多级侧边栏导航 https://www.niceym.com/33739.html