Vue+elementUI component recursively implements foldable dynamic rendering multi-level sidebar navigation

Vue+elementUI component recursively implements foldable dynamic rendering multi-level sidebar navigation

The function has been implemented a long time ago, but I found that when I clicked it, the background color of the selected menu item would turn white. I carefully observed it on Friday and found that it was not a problem of adjusting the style, but that the option was not selected. So I studied the component recursion carefully and summarized and recorded my thoughts.

1. Concept

Recursion: To put it simply, recursion is to call itself, just like nesting dolls. When I was a child, I played a game called Tower of Hanoi, which used the recursive principle:

insert image description here

Function recursion: The function uses the function name to call its own component recursion: Therefore, component recursion is implemented by using the name attribute in the vue component

2. Demand

Implement foldable dynamic rendering of multi-level sidebar navigation

Analysis

insert image description here

1. Observe that the sidebar navigation is one level at a time, and the second level is equivalent to repeating the first level. 2. There is a feature that some menus have lower levels, while some do not. 3. Dynamic rendering means that the tree type data is obtained from the background interface and dynamically rendered. 4. Code implementation 1. First, execute the demo in the document to try it out:

Documentation: element documentation

2. Change it to the style you need. This is how it was written for the first time.

Parent component 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">Navigation List</span>
      </el-menu-item>
    </template>
<!-- <side-bar-item :list="menuList"></side-bar-item>-->
    <template v-for="(item,index) in menuList" class="menu">
      <!-- Title -->
      <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>
      <!-- Options -->
      <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>

Subcomponent SideBarItem

<template>
  <div class="menu">
    <template v-for="(item,index) in list">
      <!-- Title -->
      <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>
      <!-- Options -->
      <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: 'province',
        children: [{
          label: 'Provincial Committee of the Communist Party of China'
          // children: [{
          // label: 'Level 3 1-1-1'
          // }]
        }, {
          label: 'CPC Provincial Office'
        }, {
          label: 'Organization Department of a certain province of the Communist Party of China'
        }
        ]
      }
      ],
      isShow: false
      // menuList: []
    }
  },
  mounted () {
    this.loadSysMenu()
  },
  methods: {
    loadSysMenu () {
      // console.log('menu', this.menuList)
    },
    // personManage (name) {
    // if (name === 'personnel management') {
    // 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>

Later I found that the folding was not successful, and the style of the selected item did not change after selection. Later I found that it was not selected. After research, I found that it was because an extra layer of div was nested, and the el-menu-item-group project did not need this, so the improvement was as follows:

Parent component 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">Navigation List</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>

Subcomponent SideBarItem

<template>
    <!-- There are submenus under this menu-->
    <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>
    <!-- There is no submenu under this menu-->
    <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>

The function is basically implemented, but there is a bug. When the mouse clicks on the fold, a certain event will be called cyclically, resulting in a stack overflow error. To view the article, just set the attribute of the submenu: popper-append-to-body="false". Refer to the article: Element-ui NavMenu submenu uses recursive generation to use error

Finally, attach some simple test data:

testData: [
            {"id":"34161C2E8-7348-4439-8899-9A8039AE6AE4","pid":"0","code":"HOME","name":"Home","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":"Temperature","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":"Alarm","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":"Anti-theft","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":"Alarm bell","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":"immersion","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":"Access Control","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":"State","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":"Work","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":"Log","path":"/log","type":null,"icon":null,"sysId":"2AB00274-73DF-459A-A02E-C79A4D8A8929","orderNo":0,"isCheck":null,
              "children":[]
            }
          ]

The effect is as shown below:

insert image description here

After folding, as shown:

insert image description here

This is the end of this article about vue+elementUI component recursive implementation of foldable dynamic rendering multi-level sidebar navigation. For more related elementUI foldable dynamic sidebar navigation content, please search 123WORDPRESS.COM's previous articles or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • Detailed explanation of how to use the navigation bar to jump to the route in element-ui
  • An example of using vue-router with ElementUI to implement navigation
  • Let's talk about the router problem of element-ui sidebar

<<:  Is it necessary to create a separate index for the MySQL partition field column?

>>:  File sharing between Ubuntu and Windows under VMware

Recommend

Steps to set up HTTPS website based on Nginx

Table of contents Preface: Encryption algorithm: ...

10 skills that make front-end developers worth millions

The skills that front-end developers need to mast...

Linux system command notes

This article describes the linux system commands....

HTML+CSS to create a top navigation bar menu

Navigation bar creation: Technical requirements: ...

Summary of 10 common HBase operation and maintenance tools

Abstract: HBase comes with many operation and mai...

Installation and deployment of MySQL Router

Table of contents 01 Introduction to MySQL Router...

canvas.toDataURL image/png error handling method recommendation

Problem background: There is a requirement to tak...

Summary of the understanding of virtual DOM in Vue

It is essentially a common js object used to desc...

A brief discussion on React Component life cycle functions

What are the lifecycle functions of React compone...

Detailed explanation of several ways to create a top-left triangle in CSS

Today we will introduce several ways to use CSS t...

JavaScript Basics Series: Functions and Methods

Table of contents 1. The difference between funct...

JavaScript MouseEvent Case Study

MouseEvent When the mouse performs a certain oper...

js to realize the function of uploading pictures

The principle of uploading pictures on the front ...