先看效果,测试案例,随便加了个css动画:
不知道使用 vue 递归组件注意事项的自行查看官方文档哈 【查看】
比较简单的一个 demo 仅供参考,其它复杂功能的话,能传值,能定位到具体数据了,什么功能做不了,是不是???
准备数据 store.js:
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
list: [ // 数据库查出来的数据是这个
{
id: 1,
name: '一级1',
pid: 0,
level: 1
},
{
id: 2,
name: '一级2',
pid: 0,
level: 1
},
{
id: 3,
name: '一级3',
pid: 0,
level: 1
},
{
id: 4,
name: '二级1-1',
pid: 1,
level: 2
},
{
id: 5,
name: '二级1-2',
pid: 1,
level: 2
},
{
id: 6,
name: '二级1-3',
pid: 1,
level: 2
},
{
id: 7,
name: '二级2-1',
pid: 2,
level: 2
},
{
id: 8,
name: '二级2-2',
pid: 2,
level: 2
},
{
id: 9,
name: '二级2-3',
pid: 2,
level: 2
},
{
id: 10,
name: '二级2-4',
pid: 2,
level: 2
},
{
id: 11,
name: '二级3-1',
pid: 3,
level: 2
},
{
id: 12,
name: '二级3-2',
pid: 3,
level: 2
},
{
id: 13,
name: '三级1-1-1',
pid: 4,
level: 3
},
{
id: 14,
name: '三级1-1-2',
pid: 4,
level: 3
},
{
id: 15,
name: '三级1-1-3',
pid: 4,
level: 3
},
{
id: 16,
name: '三级1-2-1',
pid: 5,
level: 3
},
{
id: 17,
name: '三级1-2-2',
pid: 5,
level: 3
},
{
id: 18,
name: '三级2-1-1',
pid: 7,
level: 3
},
{
id: 19,
name: '三级2-1-2',
pid: 7,
level: 3
},
{
id: 20,
name: '三级3-2-1',
pid: 12,
level: 3
},
{
id: 21,
name: '四级1-2-1-1',
pid: 16,
level: 4
},
{
id: 22,
name: '四级1-2-1-2',
pid: 16,
level: 4
},
{
id: 22,
name: '四级3-2-1-1',
pid: 20,
level: 4
}
]
},
mutations: {
},
getters: {
menus: (state) => {
let menu = menusHandler(state.list) // 对数据库查出来的数据进行整合
return menu
}
}
})
function menusHandler(list, menu = [], pid = 0) {
if (menu.length <= 0) {
// 如果 menu 是空的,先找顶级目录
let tmp = []
list.forEach((item, index) => {
if (item.pid == pid) {
item.flag = true
item.open = false
item.pids = '' + pid
tmp.push(item)
}
})
if (tmp.length > 0) {
menu = menusHandler(list, tmp)
}
} else {
// menu 不为空,说明不是顶级的,需查找子级
menu.forEach(el => {
let tmp = []
list.forEach(item => {
if (el.id == item.pid) {
item.flag = false
item.open = false
item.pids = el.pids + '-' + item.pid
tmp.push(item)
}
})
if (tmp.length > 0) {
el.children = tmp
menusHandler(list, tmp)
} else {
el.children = []
}
})
}
return menu
}
上方 getters 里边 menus 生成整合后的数据格式如下图所示:
准备递归组件 component/recursion.vue:
<template>
<div class="recursion">
<div class="list-item" v-for="(item, index) in list" :key="index">
<div
class="item-name"
@click="
triggerMenu({
id: item.id,
pid: item.pid,
pids: item.pids,
level: item.level,
})
"
>
<span class="item-name-icon" v-if="item.children.length">{
{
item.open ? "-" : "+"
}}</span>
<span class="item-name-icon" v-else>-</span>
<span class="item-name-txt">{
{ item.name }}</span>
</div>
<transition name="show-hide">
<div v-show="item.open" class="children-item" :id="item.id">
<recursion
:list="item.children"
@triggerMenu="triggerMenu"
></recursion>
</div>
</transition>
</div>
</div>
</template>
<script>
export default {
name: "recursion",
props: {
list: Array,
},
methods: {
triggerMenu({
id, pid, pids, level }) {
this.$emit("triggerMenu", {
id, level, pids, pid });
},
},
};
</script>
<style lang='scss'>
.recursion {
text-align: left;
.list-item {
box-sizing: border-box;
padding-top: 6px;
padding-left: 40px;
.item-name {
// width:300px;
.item-name-icon {
display: inline-block;
vertical-align: middle;
width: 28px;
height: 28px;
line-height: 28px;
text-align: center;
}
.item-name-txt {
display: inline-block;
vertical-align: middle;
}
}
}
}
.show-hide-enter-active {
animation: show-hide 0.5s;
}
.show-hide-leave-active {
animation: show-hide 0.5s reverse;
}
@keyframes show-hide {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
</style>
调用 pages/index.vue:
<template>
<div class="home">
<div class="menu">
<recursion :list="list" @triggerMenu="triggerMenu"></recursion>
</div>
</div>
</template>
<script>
import Recursion from "./../components/Recursion";
export default {
name: "index",
components: {
Recursion },
data() {
return {
list: [],
};
},
mounted() {
setTimeout(() => {
// 异步模拟接口请求过来数据,这个随意啦,写 vuex 里啥的都没问题,自行修改吧
this.list = this.$store.getters.menus;
}, 1000);
},
methods: {
triggerMenu({
id, pid, pids, level }) {
let pidsArr = ("" + pids).split("-");
let menus = JSON.parse(JSON.stringify(this.list));
function showHideMenus(menus, id) {
menus.forEach((item) => {
if (pidsArr.includes("" + item.id)) {
// 这里定位当前菜单直属所有上级
item.open = true;
} else if (item.id == id) {
// 这里定位点击的当前菜单
item.open = !item.open;
} else {
// 这里定位当前菜单以外的其它菜单
item.open = false;
}
if (item.children && item.children.length > 0) {
showHideMenus(item.children, id);
}
});
}
showHideMenus(menus, id);
this.list = menus;
},
},
};
</script>
<style lang='scss'>
.menu {
width: 400px;
overflow: auto;
}
</style>