一个完整的后台管理系统需要一个树形结构的目录,方便用户切换页面。
因为使用的是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"><MenuItemv-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-itemv-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"><SvgIconv-if="dataContainer.dataInfo.iconName":style="'width: 17px;min-width:17px;height: 17px;'":name="dataContainer.dataInfo.iconName"></SvgIcon>{{dataContainer.dataInfo.title}}<divv-if="dataContainer.dataInfo.content"class="content">{{dataContainer.dataInfo.content}}</div><divv-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)"><SvgIconv-if="dataContainer.dataInfo.iconName":style="'width: 17px;min-width:17px;height: 17px;'":name="dataContainer.dataInfo.iconName"></SvgIcon>{{dataContainer.dataInfo.title}}<divv-if="dataContainer.dataInfo.content"class="content">{{dataContainer.dataInfo.content}}</div><divv-if="dataContainer.dataInfo.number"class="sign">{{dataContainer.dataInfo.number}}</div></div></template><MenuItemv-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"><SvgIconv-if="dataContainer.dataInfo.iconName":style="'width: 17px;min-width:17px;height: 17px;'":name="dataContainer.dataInfo.iconName"></SvgIcon>{{dataContainer.dataInfo.title}}<divv-if="dataContainer.dataInfo.content"class="content">{{dataContainer.dataInfo.content}}</div><divv-if="dataContainer.dataInfo.number"class="sign">{{dataContainer.dataInfo.number}}</div></div></template><MenuItemv-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,源码