递归组件
递归组件在项目中的多级菜单功能再好用不过,它像普通函数进行递归一样,解决了一些不确定层级的场景
说到不确定层级的菜单,我们先看下大概的数据结构, 如下数组:
export default [
{
id: 1,
father_id: 0,
status: 1,
name: '生命科学竞赛',
_child: [
{
id: 2,
father_id: 1,
status: 1,
name: '野外实习类',
_child: [
{ id: 3, father_id: 2, status: 1, name: '植物学' },
{ id: 4, father_id: 2, status: 1, name: '动物学' },
{ id: 5, father_id: 2, status: 1, name: '微生物学' },
{ id: 6, father_id: 2, status: 1, name: '生态学' }
]
},
{
id: 7,
father_id: 1,
status: 1,
name: '科学研究类',
_child: [
{ id: 8, father_id: 7, status: 1, name: '植物学与植物生理学' },
{ id: 9, father_id: 7, status: 1, name: '动物学与动物生理学' },
{ id: 10, father_id: 7, status: 1, name: '微生物学' },
{ id: 11, father_id: 7, status: 1, name: '生态学' },
{
id: 21,
father_id: 7,
status: 1,
name: '农学',
_child: [
{ id: 22, father_id: 21, status: 1, name: '植物生产类' },
{ id: 23, father_id: 21, status: 1, name: '动物生产类' },
{ id: 24, father_id: 21, status: 1, name: '动物医学类' }
]
},
{
id: 41,
father_id: 7,
status: 1,
name: '药学'
},
{ id: 55, father_id: 7, status: 1, name: '其他' }
]
},
{ id: 71, father_id: 1, status: 1, name: '添加' }
]
},
{
id: 56,
father_id: 0,
status: 1,
name: '考研相关',
_child: [
{ id: 57, father_id: 56, status: 1, name: '政治' },
{ id: 58, father_id: 56, status: 1, name: '外国语' }
]
},
{
id: 65,
father_id: 0,
status: 1,
name: '找工作',
_child: [
{ id: 66, father_id: 65, status: 1, name: '招聘会' },
{ id: 67, father_id: 65, status: 1, name: '简历' }
]
},
{
id: 70,
father_id: 0,
status: 1,
name: '其他',
_child: [
{
id: 72,
father_id: 70,
status: 1,
name: '新增的根级12311111'
}
]
}
]
实现方案
- 递归组件遍历,利用v-if在层级不存在child情况下中止递归
- 给予每个层级菜单depth进行样式区别
- 设置每个层级菜单active当前选中菜单记录
- 默认参数actives设置,每个层级利用depth在数组(如:[1,2,3])默认值中获取自身默认值,若无则当前层级active为整个层级数组的第一个对象
- 每个层级的选中都会通过active的重新赋值,进而改变其子层级的data数组,直到最底层,所以,我们只记录最低层级的active变化向上传递改变,直到引用组件的最外层
代码实现
<template>
<div class="wrap">
<div class="menu-wrap">
<div
v-for="item in data"
:key="item.id"
:class="`menu-item menu-${depth} ${active.id == item.id ? 'acitve' : ''}`"
@click="active = item"
>
<span class="title">{{ item.name }}</span>
</div>
</div>
<next-menu
v-if="subMenu && subMenu.length"
:data="subMenu"
:actives="actives"
:depth="depth + 1"
@change="nextCompchange"
/>
</div>
</template>
<script>
export default {
name: 'NextMenu',
props: {
depth: { type: Number, default: () => 1 },
data: { type: Array, default: () => [] },
actives: { type: Array, default: () => [] }
},
data () {
return {
active: {},
subMenu: []
}
},
watch: {
data: {
handler: function () {
const activeId = this.actives[this.depth - 1]
const defaultValue = this.data[0] || {}
this.active = activeId
? this.data.find(item => item.id === activeId) || defaultValue
: defaultValue
},
immediate: true
},
active: {
handler: function () {
this.subMenu = this.active._child
if (!this.subMenu || this.subMenu.length <= 0) {
this.$emit('change', [this.active.id])
}
},
immediate: true
}
},
methods: {
nextCompchange (actives) {
this.$emit('change', [this.active.id].concat(actives))
}
}
}
</script>
<style lang="less" scoped>
.menu-wrap {
display: flex;
flex-wrap: wrap;
.menu-item {
margin-right: 10px;
}
.acitve {
color: red;
}
}
.menu-1 {
font-size: 18px;
color: #111111;
font-weight: bold;
}
.menu-2 {
font-size: 16px;
}
.menu-3 {
font-size: 14px;
}
</style>
需要注意的点:当click后改变active通过最底层emit传递当前的active.id,然后在父层级里面通过@change="nextCompchange"接收子层级传过来的active.id,这样再拼接上自身的选中id即[this.active.id].concat(actives),最终整合所有选中id,得到所要结果[1,2,4]