我一人全干!之二,vue3后台管理系统树形目录的实现。

news2024/12/24 8:34:17

一个完整的后台管理系统需要一个树形结构的目录,方便用户切换页面。

毒蘑菇后台管理
因为使用的是element-plus的ui库,所以首选el-menu组件,点击查看文档。
因为此组件不是树形结构的,所以需要封装成系统需要的树形结构组件。可以使用vue的递归组件。
menu.vue代码如下

<template>
    <div class="menu-container">
        <el-scrollbar 
            height="100%">
            <el-menu 
                ref="ElMenuRef"
                :collapse="false"
                :default-active="route.path"
                :router="false">
                <MenuItem
                    v-for="item,index in dataContainer.dataList"
                    :key="item.path"
                    :dataInfo="item"></MenuItem>
            </el-menu>
        </el-scrollbar>
    </div>
</template>
<script>
import {
    defineComponent,
    ref,
    reactive,
    computed,
    onMounted,
    watch,
    toRef,
    onUnmounted,
} from 'vue';
import SvgIcon from "@/components/svgIcon/index.vue";
import { useRouter,useRoute } from "vue-router";
import MenuItem from "./MenuItem.vue";

export default {
    name: 'Menu',
    components: {
        SvgIcon,
        MenuItem,
    },
    props:{
        /** 所显示的数据列表 */
        dataList:{
            type:Array,
            default:()=>{
                return [];
            },
        },
    },
    setup(props) {
        const router = useRouter();
        const route = useRoute();
        const ElMenuRef = ref(null);
        const dataContainer = reactive({
            dataList:toRef(props,'dataList'),
        });
        /** 
         * 当页面加载后设置设置当前打开的父节点,因为如果父节点可点击的话不会自动打开
         * */
        onMounted(()=>{
            if(!ElMenuRef.value) return;
            let el = ElMenuRef.value.$el;
            let hasActiveSubEl = el.querySelector('.el-sub-menu .is-sub-defin-active');
            if(!hasActiveSubEl) return;
            ElMenuRef.value.open(route.path);
        });
        return {
            dataContainer,
            route,
            ElMenuRef,
        };
    },
};
</script>
<style scoped lang="scss">
.menu-container {
    height: 100%;
    width: 100%;
    /** 基础目录配置 */
    --local-active-text-color:#ffffff;
    --local-active-bg-color:#5240ff96;
    --local-active-sub-bg-color:#3634ac57;
    --local-hover-color:#3634ac57;
    --local-font-size:15px;
    --local-text-color:#b6cce2;
    --local-box-shadow: 0 1px 4px #001247;
    --local-border-radius:8px;
    :deep(.el-menu){
        border:none !important;
        --el-menu-active-color:var(--local-active-text-color) !important;
        --el-menu-item-font-size:var(--local-font-size) !important;
        --el-menu-text-color:var(--local-text-color) !important;
        --el-menu-hover-bg-color:var(--local-hover-color) !important;
        --active-item-bg-color:var(--local-active-bg-color) !important;
        --el-menu-bg-color:transparent !important;
        --el-menu-base-level-padding:15px !important;
        --el-menu-level-padding:20px !important;
        --el-menu-icon-width:calc(15px + 0) !important; 
        --el-menu-item-height:55px !important;
        --el-menu-sub-item-height:55px !important;
        --active-sub-bg-color:transparent !important;
        padding: 10px;
        box-sizing: border-box;
        .el-sub-menu__icon-arrow{
            margin-top: 0 !important;
            top:initial !important;
        }
        .el-menu{
            padding: 0;
        }
        .el-sub-menu{
            >.el-sub-menu__title{
                border-radius: var(--local-border-radius);
            }
            &.is-active{
                background-color: var(--active-sub-bg-color);
                >.el-sub-menu__title{
                    background-color: var(--local-active-sub-bg-color);
                }
            }
            .el-sub-menu__icon-arrow{
                font-size: 17px !important;
            }
            &.is-sub-defin-active{
                >.el-sub-menu__title{
                    background-color: var(--active-item-bg-color);
                    // font-weight: bold;
                    color: var(--el-menu-active-color);
                    box-shadow: var(--local-box-shadow);
                }
            }
            /** 表示有已经活动的sub目录 */
            &:has(.is-sub-defin-active){
                background-color: var(--active-sub-bg-color);
                >.el-sub-menu__title{
                    background-color: var(--local-active-sub-bg-color);
                }
            }
        }
        .el-menu-item{
            border-radius: var(--local-border-radius);
            &.is-active{
                background-color: var(--active-item-bg-color);
                // font-weight: bold;
                box-shadow: var(--local-box-shadow);
            }
        }
    }
    :deep(.el-scrollbar){
        .el-scrollbar__bar{
            .el-scrollbar__thumb{
                background-color: rgba(194, 194, 194, 0.51) !important;
            }
        }
    }
}
</style>

其中子组件MenuItem.vue代码如下

<template>
    <div class="menu-item-container">
        <!-- 没有子目录的 -->
        <el-menu-item
            v-if="!dataContainer.dataInfo.childs || dataContainer.dataInfo.childs.length==0"
            :index="dataContainer.dataInfo.path"
            :class="{
                'is-active':dataContainer.dataInfo.path==route.path,
            }"
            @click="handleClick(dataContainer.dataInfo)">
            <div class="item-target">
                <SvgIcon
                    v-if="dataContainer.dataInfo.iconName"
                    :style="'width: 17px;min-width:17px;height: 17px;'"
                    :name="dataContainer.dataInfo.iconName"></SvgIcon>
                {{dataContainer.dataInfo.title}}
                <div
                    v-if="dataContainer.dataInfo.content"
                    class="content">
                    {{dataContainer.dataInfo.content}}
                </div>
                <div
                    v-if="dataContainer.dataInfo.number"
                    class="sign">
                    {{dataContainer.dataInfo.number}}
                </div>
            </div>
        </el-menu-item>
        <!-- 有子目录且父节点可点击 -->
        <el-sub-menu 
            v-else-if="dataContainer.dataInfo.path"
            :class="{
                'is-sub-defin-active':route.path==dataContainer.dataInfo.path,
            }"
            :index="dataContainer.dataInfo.path">
            <template #title>
                <div 
                    class="item-target"
                    @click.stop="handleClick(dataContainer.dataInfo)">
                    <SvgIcon
                        v-if="dataContainer.dataInfo.iconName"
                        :style="'width: 17px;min-width:17px;height: 17px;'"
                        :name="dataContainer.dataInfo.iconName"></SvgIcon>
                    {{dataContainer.dataInfo.title}}
                    <div
                        v-if="dataContainer.dataInfo.content"
                        class="content">
                        {{dataContainer.dataInfo.content}}
                    </div>
                    <div
                        v-if="dataContainer.dataInfo.number"
                        class="sign">
                        {{dataContainer.dataInfo.number}}
                    </div>
                </div>
            </template>
            <MenuItem
                v-for="item,index in dataContainer.dataInfo.childs"
                :key="item.path"
                :dataInfo="item"></MenuItem>
        </el-sub-menu>
        <!-- 有子目录且父节点不可点击 -->
        <el-sub-menu 
            v-else
            :index="dataContainer.dataInfo.sign">
            <template #title>
                <div class="item-target">
                    <SvgIcon
                        v-if="dataContainer.dataInfo.iconName"
                        :style="'width: 17px;min-width:17px;height: 17px;'"
                        :name="dataContainer.dataInfo.iconName"></SvgIcon>
                    {{dataContainer.dataInfo.title}}
                    <div
                        v-if="dataContainer.dataInfo.content"
                        class="content">
                        {{dataContainer.dataInfo.content}}
                    </div>
                    <div
                        v-if="dataContainer.dataInfo.number"
                        class="sign">
                        {{dataContainer.dataInfo.number}}
                    </div>
                </div>
            </template>
            <MenuItem
                v-for="item,index in dataContainer.dataInfo.childs"
                :key="item.path"
                :dataInfo="item"></MenuItem>
        </el-sub-menu>
    </div>
</template>
<script>
import {
    defineComponent,
    ref,
    reactive,
    computed,
    onMounted,
    watch,
    toRef,
    onUnmounted,
} from 'vue';
import SvgIcon from "@/components/svgIcon/index.vue";
import { useRouter,useRoute } from "vue-router";

export default {
    name: 'MenuItem',
    components: {
        SvgIcon,
    },
    props:{
        /** 所显示的数据列表 */
        dataInfo:{
            type:Object,
            default:()=>{
                return {};
            },
        },
    },
    setup(props) {
        const router = useRouter();
        const route = useRoute();
        const dataContainer = reactive({
            dataInfo:toRef(props,'dataInfo'),
        });
        /** 跳转相应链接 */
        function handleClick(params){
            if(!params.path) return;
            /** 如果是一个链接的话直接跳转 */
            if(params.isLink){
                window.open(params.path);
            }else{
                router.push(params.path);
            }
        }
        return {
            dataContainer,
            handleClick,
            route,
        };
    },
};
</script>
<style scoped lang="scss">
.menu-item-container {
    height: fit-content;
    width: 100%;
    :deep(.item-target){
        width: 100%;
        display: flex;
        flex-direction: row;
        align-items: center;
        position: relative;
        >*{
            margin-right: 10px;
        }
        >.sign{
            right: 0;
            position: absolute;
            width: fit-content;
            background-color: #ffe4e4;
            color: #f56c6c;
            border-radius: 999px;
            padding: 5px 10px;
            box-sizing: border-box;
            line-height: 1;
            font-size: 12px;
            margin: 0;
            font-weight: bold;
        }
        >.content{
            font-size: 12px;
            margin-left: 5px;
            opacity: 0.8;
            font-weight: 400 !important;
        }
    }
}
</style>

这样就可以实现一个树形结构的系统目录了。
DEMO,源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1289277.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker架构、镜像操作和容器操作

一、docker基本管理和概念 1、概念 docker&#xff1a;开源的应用容器引擎。基于go语言开发的。运行在Linux系统中的开源的轻量级的“虚拟机” docker的容器技术可用在一台主机上轻松到达为任何应用创建一个轻量级到的&#xff0c;可移植的&#xff0c;自给自足的容器 dock…

微信小程序收款手续费怎么搞成0.2

今天&#xff0c;我将分享如何有效地降低日常中的收款手续费率。我们都知道&#xff0c;不管是微信支付还是支付宝&#xff0c;平台都会从中扣除一定的手续费。但你是否知道&#xff0c;其实手续费率是可以降低的呢&#xff1f;今天介绍如何申请最低手续费率为0.2%的方法&#…

【EI征稿中|SPIE出版】 第四届传感器与信息技术国际学术会议(ICSI 2024)

第四届传感器与信息技术国际学术会议&#xff08;ICSI 2024&#xff09; 2024 4th International Conference on Sensors and Information Technology&#xff08;ICSI 2024&#xff09; 第四届传感器与信息技术国际学术会议&#xff08;ICSI 2024&#xff09;将于2024年1月5…

IDEA Maven项目如何引用本地jar包,并打包发布

jar包位于当前路径下的lib目录中 引入所需要的配置 查看当前jar包的相关信息 包的引入,需要使用到当前包的artifactId, groupId, version 需要到包的/META-INF/maven/ 下面的 pom.xml 文件里面找 在Maven构建项目时&#xff0c;生成的依赖包中的/META-INF/maven目录存放了一些…

基于Echarts的大数据可视化模板:智慧交通管理

目录 引言智慧交通管理的重要性ECharts在智慧交通中的作用智慧交通管理系统架构系统总体架构数据收集与处理Echarts与大数据可视化Echarts库以及其在大数据可视化领域的应用优势开发过程和所选设计方案模板如何满足管理的特定需求模板功能与特性深入解析模板提供的各项功能模板…

MS8051运算放大器可Pin to Pin兼容AD8051/AD8052/AD8054

MS805x 系列为轨到轨输出的电压反馈运算放大器&#xff0c;具有易用、低成本等特点。可Pin to Pin兼容AD8051/AD8052/AD8054。相比于典型的电流反馈放大器&#xff0c;在带宽和转换率有更大的优势&#xff0c;并同时具备宽的输入共模电压范围和大的输出电压摆幅&#xff0c;这使…

速达软件全系产品任意文件上传漏洞

声明 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 1. 速达软件产品简介 速达软件专注中小企业管理软件,产品涵盖进销存软…

查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息

文章目录 摘要1. 查询CPU使用率命令&#xff1a;top -bn1 | grep \"Cpu(s)\" | awk {split($0,arr,\" \");print 100-arr[8]}2. 查询内存命令&#xff08;单位&#xff1a;G&#xff09;&#xff1a;top -bn1 | grep \"KiB Mem\" | awk {split($…

揭秘高效编程“武功秘笈”,手把手带你写一波!

随着今年人工智能技术的大火&#xff0c;越来越多的领域正在接受和利用这项强大的 AI 科技&#xff0c;以实现更高效、更智能的工作方式。在软件开发领域&#xff0c;AI 技术更是为我们的工作带来了前所未有的变革。从自动代码生成到智能编程助手&#xff0c;AI 正在逐步改变开…

解决IDEA中多个项目不在同一窗口下显示的问题和添加新的git的URL

以上是添加显示多个项目 以下是给新添加的项目添加git

React中使用react-json-view展示JSON数据

文章目录 一、前言1.1、在线demo1.2、Github仓库 二、实践2.1、安装react-json-view2.2、组件封装2.3、效果2.4、参数详解2.4.1、src(必须) &#xff1a;JSON Object2.4.2、name&#xff1a;string或false2.4.3、theme&#xff1a;string2.4.4、style&#xff1a;object2.4.5、…

处理实时视频流:第三方美颜SDK的实时图像处理策略

为了提高用户体验&#xff0c;许多应用和平台开始采用第三方美颜SDK&#xff0c;通过先进的图像处理技术实时改善视频中的面部外观。本文将深入探讨这些第三方美颜SDK在实时视频流处理中所采用的图像处理策略&#xff0c;揭示其背后的技术原理和创新。 一、引言 实时视频流处理…

使用MONAI时,如何选择合适的Dataset加载数据,提升训练速度!

在深度学习中&#xff0c;MONAI&#xff08;Medical Open Network for AI&#xff09;是一个专注于医学图像分析的开源框架。它提供了一系列用于医学图像处理和深度学习的工具和函数&#xff0c;其中包括了Dataset函数。 Dataset函数是MONAI框架中的一个重要组件&#xff0c;它…

【Redis】redis 高性能--线程模型以及epoll网络框架

目录 一.前言 二.多线程的弊端 2.1 锁的开销问题 2.2 多线程上下文切换带来的额外开销 2.3 多线程占用内存成本增高 三.基本IO模型与epoll 模式 3.1 基本IO模型 3.2 单线程处理机制 四.总结 一.前言 我们经常讨论到&#xff0c;redis 是单线程&#xff0c;那为什么单线…

【开发板测评】一起玩转ACM32G103开发板,释放MCU无限潜能!

为帮助小伙伴们更好的快速熟悉了解ACM32G103系列的特性&#xff0c;航芯特别发起了该系列开发板评测试用&#xff0c;以帮助大家更好地运用MCU进行项目设计。 ACM32G103开发板介绍 ACM32G103系列是航芯推出的一款有着丰富模拟外设及安全存储扩展能力的高性价比通用MCU。 高性…

git bash查看远程仓库地址

进入代码路径 git remote -vgit remote -v

springBoot如何快速发布webService接口?(含测试工具)

文章目录 引入maven依赖 org.apache.cxf cxf-rt-frontend-jaxws 3.4.5 org.apache.cxf cxf-rt-transports-http 3.4.5 org.apache.cxf cxf-spring-boot-starter-jaxws 3.4.5 新建webService接口 注意接口要添加注释WebService&#xff0c;且要添加name和targetNamespace属性…

「遮天」叶凡斩杀同等级,寻回丢失秘宝,暴打神桥境同等级强者

Hello,小伙伴们&#xff0c;我是拾荒君。 《遮天》国漫第34集已经更新了&#xff01;我的小伙伴们&#xff0c;包括拾荒君在内&#xff0c;都是迫不及待的去观看这一集。在这一集中&#xff0c;叶凡一直寻找的丢失的法器&#xff0c;被吴清风查出是被韩易水偷走的。这位韩长老…

Android audio环形缓冲队列

1、背景 在学习audio的过程中&#xff0c;看到了大神zyuanyun的博客&#xff0c;在博客的结尾&#xff0c;大神留下了这些问题&#xff1a; 但是大神没有出后续的博文来说明audio环形缓冲队列的具体实现&#xff0c;这勾起了我强烈的好奇心。经过一段时间的走读代码&#xff…

【日常总结】树莓派导致的公司无法上网 - 广播风暴

一、场景 二、问题 三、分析原因 四、解决方案 方案一&#xff1a;更换树莓派后ping路由器恢复正常 方案二&#xff1a;配置交换机 交换机广播风暴配置 也可以通过PPS来限速 查看配置 一、场景 宽带&#xff1a;公司3条500M光纤-联通 路由器&#xff1a;锐捷 在线用户…