vue3 左侧目录无限嵌套

406 阅读1分钟

在开发中我们没有办法确定左侧的代码目录要是用多少层

如果写一个嵌套的话,就能无添加目录

目录结构

layout
    components
        Header
            index.vue
        Sidebar
            index.vue
            menuItem.vue
            subMenu.vue
    index.vue(下文的index.vue就是这个文件)

入口

type的类型

//路由的接口
export interface IRouterMap {
    redirect?: string,
    alwaysShow?: number,//当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
    hidden?: number,//当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1
    query: string,//访问路由的默认传递参数
    name: string,//设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
    path: string,//路由地址
    meta?: IMeta,
    component: string | object
    children?: [IRouterMap],
}
//mate的接口 
interface IMeta {
    title?: string, // 设置该路由在侧边栏和面包屑中展示的名字
    icon?: string,// 设置该路由的图标,对应路径src/assets/icons/svg
    breadcrumb?: boolean,// 如果设置为false,则不会在breadcrumb面包屑中显示
    noCache?: boolean,// 如果设置为true,则不会被 <keep-alive> 缓存(默认 false)
    menuType: number,//类型(1目录 2菜单 3按钮 4数据 5字段)
}

index.vue文件

<template>
	<el-radio-group v-model="isCollapse" style="position: absolute; top: 5px; z-index: 1; left: 10px; height: 50px">
		<el-radio-button :label="false">展开</el-radio-button>
		<el-radio-button :label="true">折叠</el-radio-button>
	</el-radio-group>
	<el-container class="layout-container-demo" style="height: 100%">
		<el-aside :class="!isCollapse ? 'asideOpen' : 'asideClose'">
			<Sidebar :isCollapse="isCollapse"></Sidebar>
		</el-aside>
		<el-container style="flex: 1">
			<el-header style="text-align: right; font-size: 12px">
				<Header></Header>
			</el-header>
			<el-main> 
				<router-view></router-view>
			</el-main>
		</el-container>
	</el-container>
</template>
  
<script lang="ts" setup>
import { ref } from "vue";
import { Sidebar, Header } from "./components";
const isCollapse = ref(false);
</script>
  
<style scoped>
.asideOpen {
	max-width: 200px;
}
.asideClose {
	max-width: 80px;
}
</style>

./components文件夹

./components/index.ts文件

export { default as Sidebar } from './Sidebar/index.vue'
export { default as Header } from './Header/index.vue'

./components/Sidebar/index.vue文件 

<template>
	<el-scrollbar>
		<div style="height: 50px"></div>
		<el-menu :default-openeds="['0']" :collapse="isCollapse" class="el-menu-vertical-demo">
			<sub-menu :data="sidebarRoutes" :index="0"></sub-menu>
		</el-menu>
	</el-scrollbar>
</template>
<script  lang="ts" setup>
import subMenu from "./subMenu.vue";
import { Menu as IconMenu, Message, Setting } from "@element-plus/icons-vue";
import { computed, ref, watchEffect, defineProps } from "vue";
import { useRouterStore } from "@/stores";
import type { IRouterMap } from "@/router/index";
const routerStore = useRouterStore();
const props = defineProps({
	isCollapse: Boolean,//是否折叠
});
const isCollapse = computed(() => props.isCollapse);
//左边的展示
const sidebarRoutes = computed<IRouterMap[]>(() => routerStore.sidebarRoutes);
// const isCollapse = ref(false);
</script>
<style lang="scss" scoped>
.el-menu {
	border: 0;
}
.el-menu-vertical-demo:not(.el-menu--collapse) {
	width: 200px;
	// overflow: hidden;
	min-height: 400px;
}
</style>

./components/Sidebar/subMenu.vue文件 

<template>
	<template v-for="(item, index) in sidebarRoutes" :key="index">
		<el-sub-menu v-if="item.meta?.menuType == 1" :index="indexKey(parentIndex, index)">
			<template #title>
				<el-icon><IconMenu /></el-icon>
				<!-- 需要放在一个标签没 要不然对折无隐藏标题 -->
				<span>{{ item.meta?.title }}</span>
			</template>
			<sub-menu :data="item.children" :index="indexKey(parentIndex, index)"></sub-menu>
		</el-sub-menu>
		<template v-if="item.meta?.menuType == 2">
			<menu-item :data="item" :index="indexKey(parentIndex, index)"></menu-item>
		</template>
	</template>
</template>
 
<script lang="ts" setup>
import menuItem from "./menuItem.vue";
import { defineProps, computed } from "vue";
import type { IRouterMap } from "@/router/index";
import { Menu as IconMenu  } from "@element-plus/icons-vue";
const props = defineProps<{
	data: IRouterMap[]; //具体的条目内容
	index: number | string;
}>();
const parentIndex = computed(() => props.index);
const indexKey = function (parentIndex: number | string, index: number): string {
	return `${parentIndex}-${index}`;
};
const sidebarRoutes = computed<IRouterMap[]>(() => props.data);
</script>

./components/Sidebar/menuItem.vue文件 

<template>
	<el-menu-item-group>
		<el-menu-item :index="parentIndex" @click="goRouter(item)">
			{{ item.meta?.title }}
		</el-menu-item>
	</el-menu-item-group>
</template> 
<script lang="ts" setup>
import { defineProps, computed } from "vue";
import type { IRouterMap } from "@/router/index";
import { useRouter } from "vue-router";
import { Menu as IconMenu, Message, Setting } from "@element-plus/icons-vue";
const props = defineProps<{
	data: IRouterMap; //具体的条目内容
	index: number | string; //当前是第几个
}>();
//路由跳转
const router = useRouter();
//本节点的信息
const item = computed(() => props.data);
//当前的index
const parentIndex = computed(() => props.index);
//点击菜单 进行了跳转
function goRouter(item: IRouterMap) {
	router.push({ path: item.path });
}
</script>