vue递归组件实现无限极分类菜单/无限极树状图,点击其中一项菜单关闭其它所有菜单项,vue递归组件通过数据定位当前组件层级及定位哪一条数据

先看效果,测试案例,随便加了个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>

猜你喜欢

转载自blog.csdn.net/qq_38652871/article/details/112008973