vue3.2/Vite/Ts25: 递归组件生成动态菜单
·
Yin灏
新建 menu 组件
- src / layout / menu / index.vue
<template>
<template v-for="(item, index) in menuList" :key="item.path">
<!-- 没有子路由 -->
<template v-if="!item.children">
<el-menu-item v-if="!item.meta.hidden" :index="item.path">
<template #title>
<span>{{ item.meta.title }}</span>
</template>
</el-menu-item>
</template>
<!-- 只有一个子路由 -->
<template v-if="item?.children?.length === 1">
<el-menu-item
v-if="!item.children[0].meta.hidden"
:index="item.children[0].path"
>
<template #title>
<span>{{ item.children[0].meta.title }}</span>
</template>
</el-menu-item>
</template>
<!-- 有超过1个的子路由 -->
<el-sub-menu v-if="item?.children?.length > 1" :index="item.path">
<template #title>
<span>{{ item.meta.title }}</span>
</template>
<!-- 重点:递归组件 -->
<menu :menuList="item.children"></menu>
</el-sub-menu>
</template>
</template>
<script setup lang="ts">
defineProps(["menuList"]);
</script>
<!-- 给当前组件取一个名字,不然递归组件用不了 -->
<script lang="ts">
export default {
name: "Menu",
};
</script>
处理路由文件
- src / router / routes.ts
- 同时给每一个路由添加路由元信息
export const contantRoute = [
{
path: "/login",
component: () => import("@/views/login/index.vue"),
name: "login",
meta: {
title: "登录", // 菜单标题
hidden: true, // 此路由在导航中是否隐藏
},
},
//...
{
path: "/",
component: () => import("@/layout/index.vue"),
name: "layout",
meta: {
title: "layout",
hidden: false,
},
children: [
{
path: "/home",
component: () => import("@/views/home/index.vue"),
meta: {
title: "首页",
hidden: false,
},
},
],
},
{
path: "/404",
component: () => import("@/views/404/index.vue"),
name: "404",
meta: {
title: "404",
hidden: true,
},
},
{
path: "/:pathMatch(.*)*",
redirect: "/404",
name: "Any",
meta: {
title: "任意路由",
hidden: true,
},
},
//...
];
将路由挂载在用户小仓库
- src / store / modules / user.ts
//...
import { contantRoute } from "@/router/routes";
//...
let useUserStore = defineStore("User", {
state: (): UserState => {
return {
//...
// 将路由对象挂载到用户小仓库
menuRoutes: contantRoute,
};
},
});
修改 UserState 类型值,用来满足 menuRoutes 的 ts 类型检测
- src / store / modules / types / type.ts
import type { RouteRecordRaw } from "vue-router";
export interface UserState {
token: string | null;
menuRoutes: RouteRecordRaw[];
}
使用新建 menu 组件
- src / layout / index.vue
<template>
<div class="layout_container">
<!-- 左侧菜单 -->
<div class="layout_slider">
<Logo></Logo>
<!-- 展示菜单 -->
<el-scrollbar class="scrollbar">
<el-menu background-color="#001529" text-color="white">
<menu :menuList="userStore.menuRoutes"></menu>
</el-menu>
</el-scrollbar>
</div>
<!-- 顶部导航 -->
<div class="layout_tabbar"></div>
<!-- 内容展示区域 -->
<div class="layout_main"></div>
</div>
</template>
<script setup lang="ts">
import Logo from "./logo/index.vue";
import Menu from "./menu/index.vue";
// 获取用户先关小仓库
import useUserStore from "@/store/modules/user";
const userStore = useUserStore();
</script>
<style scoped lang="scss">
.scrollbar {
width: 100%;
height: calc(100vh - $base-menu-logo-height);
}
</style>