切換語言為:簡體
使用 Vue3 利用遞迴的方式實現無限層級選單

使用 Vue3 利用遞迴的方式實現無限層級選單

  • 爱糖宝
  • 2024-07-15
  • 2083
  • 0
  • 0

還只會使用開源的元件嗎,有沒有想過自己去實現一下這些開源ui框架的某個元件的功能呢。今天就用vue3 + 遞迴思想實現一個無限極的選單

無限級選單結構實現

首先來思考 程式碼的結構

  1. 首先有個Menu 資料夾

  2. menu 資料夾下面有個index.vue出口檔案

  3. menu MenuItem.vue 迴圈的選單項

  4. 迴圈的選單裡面又可能有子選單,所以就又要去引用menu 下面的index.vue,遞迴就開始形成了

來看程式碼的實現: Menu 下面的 index.vue

<template>
  <div class="menu-list">
    <MenuItem v-for="item in menuList" :key="item.title" :menuData="item" />
  </div>
</template>
<script setup>
import MenuItem from './MenuItem.vue'
const props = defineProps({
  menuList: {
    type: Array
  }
})
</script>
<style>
.menu-list {
  background: #181818;
  color: #fff;
  padding-left: 20px;
}
</style>

menu 下面的MenuItem.vue

<template>
  <div class="menu-item">
    <p class="menu-text">{{ menuData.title }}</p>
    <Menu :menuList="menuData.children" v-if="menuData.children && menuData.children.length > 0" />
  </div>
</template>
<script setup>
import Menu from './index.vue'
const props = defineProps({
  menuData: {
    type: Object,
    default () {
      return []
    }
  }
})
</script>
<style>
.menu-text {
  margin: 0;
  padding: 10px;
  cursor: pointer;
}
.menu-text:hover {
  background: #333333;
}
</style>

選單結構資料:

export const menuList = [
  {
    title: '選單一級',
    children: [
      {
        title: '選單二級1'
      },
      {
        title: '選單二級2'
      },
      {
        title: '選單二級3',
        children: [
          {
            title: '選單三級1'
          },
          {
            title: '選單三級1'
          },
        ]
      }
    ]
  },
  {
    title: '選單一級2',
  },
  {
    title: '選單一級3',
  },
  {
    title: '選單一級4',
  }
]

頁面使用

<template>
  <div class="book-content">
    <Menu :menuList="menuList"/>
  </div>
</template>
<script setup>
import Menu from '@/components/Menu/index.vue'
import { menuList } from './data.js'
</script>
<style>
</style>

一個簡單的無限級選單就基本形成了,來看下效果

使用 Vue3 利用遞迴的方式實現無限層級選單

基礎選單缺陷思考

是不是很開心呢!別高興太早,還有些功能還沒實現呢!

1.選單預設一般除第一級選單外都是收起來的,

2.選單一般都是可以點選收縮和展開的,現在選單還沒法點選呢!

選單互動功能實現

接下來就來實現這兩個功能

首先來看預設只顯示一級選單, 一級以下選單收起的實現 怎麼實現呢?思考一分鐘。。。

預設只顯示一級選單實現

一分鐘到:開始吧! 要實現預設只展開第一級,那第一步肯定就是要知道怎麼區分第一級和其他級。 那怎麼區分呢!讓使用選單元件的人在數據結構裡面加幾個層級嗎!可以是可以,但是增加了使用成本,選單資料的每一個項都需要加一個欄位。顯得麻煩,那該怎麼辦呢! ... 想到了一個好辦法,遞迴呼叫的時候從MenuItem傳一個標識過來到index.vue,就叫menu-item-parent 吧, 再在index.vue 進行判斷有沒有在這個引數, 有說明不是第一級,沒有則說明是第一級。 來看下現在的MenuItem.vue

<template>
  <div class="menu-item">
    <p class="menu-text">{{ menuData.title }}</p>
    <Menu 
      :menuList="menuData.children" 
      v-if="menuData.children && menuData.children.length > 0"
      :menu-item-parent="true"
     />
  </div>
</template>
<script setup>
import Menu from './index.vue'
const props = defineProps({
  menuData: {
    type: Object,
    default () {
      return []
    }
  }
})
</script>
<style>
.menu-text {
  margin: 0;
  padding: 10px;
  cursor: pointer;
}
.menu-text:hover {
  background: #333333;
}
</style>

關鍵:增加了:menu-item-parent="true" Menu 下面的index.vue .menu-list {   background: #181818;   color: #fff;   padding-left: 20px; }

關鍵: (1)增加了menuItemParent: {     type: String,     default: ''   } 屬性接收 (2)增加了v-if="!menuItemParent"判斷

來看下現在的選單效果

使用 Vue3 利用遞迴的方式實現無限層級選單

已經實現我們的第一個功能, 預設只顯示第一級選單了

點選展開和收起功能實現

接下來就是實現點選展開和收起了。 這個功能怎麼實現呢! 思考一分鐘 。。。 時間到, 開始吧! (1)要實現這個功能,肯定要新增點選事件, 所以在 MenuItem.vue 的 menu-text 上增加一個點選事件,就叫toggle吧

(2)宣告一個變數來進行選單的顯示隱藏判斷, 就叫clickToMenu 吧

(3)menu-item-parent 這個屬性值也改為一個變數來控制,因為點選的時候需要把他變為false , 顯示隱藏完全由點選的這個變數來控制

(4) 點選的時候 clickToMenu.value = !clickToMenu.value

menuItemParent.value = false

來看下具體程式碼的實現 MenuItem.vue

<template>
  <div class="menu-item">
    <p class="menu-text" @click="toggle">{{ menuData.title }}</p>
    <Menu 
      :menuList="menuData.children" 
      v-if="menuData.children && menuData.children.length > 0 && clickToMenu"
      :menu-item-parent="menuItemParent"
     />
  </div>
</template>
<script setup>
import { ref } from 'vue' 
import Menu from './index.vue'
const props = defineProps({
  menuData: {
    type: Object,
    default () {
      return []
    }
  }
})
const menuItemParent = ref(true)
const clickToMenu = ref(false)
function toggle () {
  clickToMenu.value = !clickToMenu.value
  menuItemParent.value = false
}
</script>
<style>
.menu-text {
  margin: 0;
  padding: 10px;
  cursor: pointer;
}
.menu-text:hover {
  background: #333333;
}
</style>

關鍵:增加了

const menuItemParent = ref(true)
const clickToMenu = ref(false)
function toggle () {
  clickToMenu.value = !clickToMenu.value
  menuItemParent.value = false
}

模板if 判斷增加 menuData.children && menuData.children.length > 0 && clickToMenu

index.vue 沒有改動

來看下效果,點選一級選單

使用 Vue3 利用遞迴的方式實現無限層級選單

點選二級選單

使用 Vue3 利用遞迴的方式實現無限層級選單

再次點選二級選單進行收起

使用 Vue3 利用遞迴的方式實現無限層級選單

到此一個完整的選單基本上就開發完成了。

選單顯示細節最佳化--- 根據狀態新增收縮標識

還有沒有什麼缺陷呢? 其實還有, 就是現在看不出來哪些選單是有子選單的

來最佳化下吧,主要就是根據狀態增加一個圖示,來看下具體程式碼

<template>
  <div class="menu-item">
    <p class="menu-text" 
      :class="{ 'menu-icon': menuData.children && menuData.children.length > 0, up: !clickToMenu}"
      @click="toggle"
    >{{ menuData.title }}</p>
    <Menu 
      :menuList="menuData.children" 
      v-if="menuData.children && menuData.children.length > 0 && clickToMenu"
      :menu-item-parent="menuItemParent"
     />
  </div>
</template>
<script setup>
import { ref } from 'vue' 
import Menu from './index.vue'
const props = defineProps({
  menuData: {
    type: Object,
    default () {
      return []
    }
  }
})
const menuItemParent = ref(true)
const clickToMenu = ref(false)
function toggle () {
  clickToMenu.value = !clickToMenu.value
  menuItemParent.value = false
}
</script>
<style>
.menu-text {
  margin: 0;
  padding: 10px;
  cursor: pointer;
  position: relative;
}
.menu-text:hover {
  background: #333333;
}
.menu-icon::after{
  content: '';
  width: 20px;
  height: 20px;
  position: absolute;
  display: inline-block;
  right: 10px;
  top: 50%;
  transform: translateY(-50%) rotate(180deg);
  background: url("@/assets/images/menu-arrow.svg") no-repeat;
  background-size: contain;
}
.menu-icon.up::after{
  transform: rotate(0deg);
}
</style>

關鍵增加:class="{ 'menu-icon': menuData.children && menuData.children.length > 0, up: !clickToMenu}"

增加樣式:

.menu-icon::after{
  content: '';
  width: 20px;
  height: 20px;
  position: absolute;
  display: inline-block;
  right: 10px;
  top: 50%;
  transform: translateY(-50%) rotate(180deg);
  background: url("@/assets/images/menu-arrow.svg") no-repeat;
  background-size: contain;
}
.menu-icon.up::after{
  transform: rotate(0deg);
}

到此選單就基本接近完美了。來看下具體的效果吧

使用 Vue3 利用遞迴的方式實現無限層級選單

無限級選單到這裏就開發結束了,感謝收看

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.