Vue 其他相关文章:
Vue Router 全面详解--【一、从零开始搭建Vue Router,一步一步的掌握Vue Router】
还只会使用开原的组件吗,有没有想过自己去实现一下这些开源ui框架的某个组件的功能呢。今天就用vue3 + 递归思想实现一个无限极的菜单
无限级菜单结构实现
首先来思考 代码的结构
- 首先有个Menu 文件夹
- menu 文件夹下面有个index.vue出口文件
- menu MenuItem.vue 循环的菜单项
- 循环的菜单里面又可能有子菜单,所以就又要去引用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>
一个简单的无限级菜单就基本形成了,来看下效果
基础菜单缺陷思考
是不是很开心呢!别高兴太早,还有些功能还没实现呢!
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"
判断
来看下现在的菜单效果
已经实现我们的第一个功能, 默认只显示第一级菜单了
点击展开和收起功能实现
接下来就是实现点击展开和收起了。 这个功能怎么实现呢! 思考一分钟 。。。 时间到, 开始吧! (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 没有改动
来看下效果,点击一级菜单
点击二级菜单
再次点击二级菜单进行收起
到此一个完整的菜单基本上就开发完成了。
菜单显示细节优化--- 根据状态添加收缩标识
还有没有什么缺陷呢? 其实还有, 就是现在看不出来哪些菜单是有子菜单的
来优化下吧,主要就是根据状态增加一个图标,来看下具体代码
<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);
}
到此菜单就基本接近完美了。来看下具体的效果吧
无限级菜单到这里就开发结束了,感谢收看,希望能和你一起交流技术
Vue 其他相关文章: