一 、菜单组件封装
1.1 编写静态路由
const menuItems = reactive([
{
name: 'index',
path:'/index',
meta: {
title: '首页',
hidden: false,
icon: 'HomeFilled',
},
},
{
name: 'Acl',
path:'/acl',
meta: {
title: '权限管理',
icon: 'Lock',
},
redirect: '/acl/user',
children:[
{
name: 'User',
path: '/acl/user',
component: AclUser,
meta: {
title: '用户管理',
icon: 'User',
},
},
{
path: '/acl/role',
component: Role,
name: 'Role',
meta: {
title: '角色管理',
icon: 'UserFilled'
},
},
{
path: '/acl/permission',
component: Permission,
name: 'Permission',
meta: {
title: '菜单管理',
icon: 'Monitor',
}
}
]
},
{
name: 'system',
path:'/system',
meta: {
title: '系统管理',
icon: 'Setting',
},
children:[
{
path: '/system/dict',
component: Dict,
name: 'Permission',
meta: {
title: '字典管理',
icon: 'Monitor',
hidden: false,
}
}
]
},
])
1.2 编写对应的子页面
1.2.1 权限管理
1.2.2 系统管理
二、route下index.ts调整
import {
createRouter,
createWebHashHistory,
type RouteRecordRaw
} from 'vue-router'
// 引入 login.ts
import LoginRouter from '@/router/modules/login.ts'
// 引入 组件页面
import Home from '@/views/layout/Index.vue'
import Index from '@/views/layout/index/Index.vue';
import System from '@/views/system/System.vue'
import DictList from "@/views/system/dict/Dict.vue";
import Acl from "@/views/acl/Acl.vue";
import AclUser from "@/views/acl/user/AclUser.vue";
import Role from "@/views/acl/role/Role.vue";
import Permission from "@/views/acl/permission/Permission.vue";
// RouteRecordRaw 内置的接口类型
const routes: RouteRecordRaw[] = [
...LoginRouter,
{
path: '/',
redirect: '/home'
},
{
path: '/home', // 布局页
name: 'Home',
component: Home,
redirect:'/index',// 进入主页的时候重定向到Index页面
children: [
{
path: '/index',
name: 'index',
component: Index,
},
{
path: '/acl',
component: Acl,
name: 'Acl',
children: [
{
path: '/acl/user',
component: AclUser,
name: 'User',
},
{
path: '/acl/role',
component: Role,
name: 'Role',
},
{
path: '/acl/permission',
component:Permission,
name: 'Permission',
},
]
},
{
path: '/system',
name: 'system',
component: System,
children:[
{
path: '/system/dict',
name: 'dict',
component: DictList,
}
]
}
],
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
// 导出路由
export default router
三 、MenuTree组件封装
3.1 components新建MenuTree文件夹,文件夹 新建MenuTree.vue
<template>
<div>
<template v-for="(item, index) in menuList" :key="item.path">
<!-- 没有子路由 -->
<template v-if="!item.children">
<el-menu-item
:index="item.path"
v-if="!item.meta.hidden"
@click="goRoute"
>
<template #title>
<!-- 图标 -->
<el-icon>
<component :is="item.meta.icon"></component>
</el-icon>
<span>{{ item.meta.title }}</span>
</template>
</el-menu-item>
</template>
<!-- 只有一个子集 -->
<template v-if="item.children && item.children.length == 1">
<el-menu-item
@click="goRoute"
:index="item.children[0].path"
v-if="!item.children[0].meta.hidden"
>
<el-icon>
<component :is="item.children[0].meta.icon"></component>
</el-icon>
<template #title>
<span>{{ item.children[0].meta.title }}</span>
</template>
</el-menu-item>
</template>
<!-- 有子路由, 有子菜单的继续遍历(递归) -->
<el-sub-menu
:index="item.path"
v-if="item.children && item.children.length >= 1"
>
<template #title>
<!-- 图标 -->
<el-icon>
<component :is="item.meta.icon"></component>
</el-icon>
<span>{{ item.meta.title }}</span>
</template>
<!-- 路由递归 : 二级路由还有子路由,子路由又还有子路由-->
<MenuTree :menuList="item.children"></MenuTree>
</el-sub-menu>
</template>
</div>
</template>
<script lang="ts" setup>
import { useRouter } from 'vue-router'
const router = useRouter()
// 获取父组件传递过来的全部路由数组
defineProps(['menuList'])
const goRoute = (e: any) => {
console.log(e.index)
router.push(e.index)
}
</script>
<style lang="scss" scoped></style>
四、NarBar.vue中使用
<template>
<el-aside class="layout-menu">
<!-- logo -->
<div class="layout-logo">
<img src="@/assets/vue.svg" alt="logo图标" />
<p>v3后台管理系统</p>
</div>
<!-- 展示菜单 -->
<!-- 滚动组件 -->
<el-scrollbar class="layout-scrollbar">
<el-menu>
<!-- 菜单组件-->
<MenuTree :menu-list="menuItems"></MenuTree>
</el-menu>
<!-- <el-menu default-active="2" class="el-menu-vertical-demo" router>-->
<!-- <el-menu-item index="/index">-->
<!-- <el-icon><House /></el-icon>-->
<!-- <span>首页</span>-->
<!-- </el-menu-item>-->
<!-- <el-sub-menu index="/user">-->
<!-- <template #title>-->
<!-- <el-icon><User /></el-icon>-->
<!-- <span>用户管理</span>-->
<!-- </template>-->
<!-- <el-menu-item index="/user/userList">-->
<!-- <span>用户列表</span>-->
<!-- </el-menu-item>-->
<!-- </el-sub-menu>-->
<!-- <el-sub-menu index="/system">-->
<!-- <template #title>-->
<!-- <el-icon><Setting /></el-icon>-->
<!-- <span>系统管理</span>-->
<!-- </template>-->
<!-- <el-menu-item index="/system/dict">-->
<!-- <span>字典列表页面</span>-->
<!-- </el-menu-item>-->
<!-- <el-menu-item index="/system/roleList">-->
<!-- <span>角色列表页面</span>-->
<!-- </el-menu-item>-->
<!-- </el-sub-menu>-->
<!-- </el-menu>-->
</el-scrollbar>
</el-aside>
</template>
<script setup lang="ts">
import { HomeFilled, Setting } from '@element-plus/icons-vue'
import { reactive } from 'vue'
import MenuTree from '@/components/MenuTree/MenuTree.vue'
import Dict from '@/views/system/dict/Dict.vue'
import Acl from '@/views/acl/Acl.vue'
import Role from '@/views/acl/role/Role.vue'
import Permission from '@/views/acl/permission/Permission.vue'
import AclUser from '@/views/acl/user/AclUser.vue'
const menuItems = reactive([
{
name: 'index',
path: '/index',
meta: {
title: '首页',
hidden: false,
icon: 'HomeFilled'
}
},
{
name: 'Acl',
path: '/acl',
meta: {
title: '权限管理',
icon: 'Lock'
},
redirect: '/acl/user',
children: [
{
name: 'User',
path: '/acl/user',
component: AclUser,
meta: {
title: '用户管理',
icon: 'User'
}
},
{
path: '/acl/role',
component: Role,
name: 'Role',
meta: {
title: '角色管理',
icon: 'UserFilled'
}
},
{
path: '/acl/permission',
component: Permission,
name: 'Permission',
meta: {
title: '菜单管理',
icon: 'Monitor'
}
}
]
},
{
name: 'system',
path: '/system',
meta: {
title: '系统管理',
icon: 'Setting'
},
children: [
{
path: '/system/dict',
component: Dict,
name: 'Permission',
meta: {
title: '字典管理',
icon: 'Monitor',
hidden: false
}
}
]
}
])
</script>
<style lang="scss" scoped>
.layout-menu {
border-right: 1px solid #e4e7ed;
width: $base-menu-width;
background-color: $base-menu-background;
.layout-logo {
width: 100%;
height: $base-menu-logo-height;
color: white;
display: flex;
align-items: center;
img {
width: 40px;
height: 40px;
}
p {
color: white;
}
}
.layout-scrollbar {
width: 100%;
height: calc(100vh - $base-menu-logo-height);
.el-menu {
border-right: none;
background-color: $base-menu-background;
}
}
}
</style>
五、运行代码查看效果
这里字典管理出现了两次。是因为 我们需要根据自己的需要选择当前菜单下只有一个子菜单时,父菜单是否展示。
如果父菜单展示的话,红色部分的注释掉即可
如果父菜单不展示的红色部分改为大于1就可以了。
我喜欢都展示,所以注释掉了=1的情况。
六、最终版以及效果
<template>
<div>
<template v-for="(item, index) in menuList" :key="item.path">
<!-- 没有子路由 -->
<template v-if="!item.children">
<el-menu-item
:index="item.path"
v-if="!item.meta.hidden"
@click="goRoute"
>
<template #title>
<!-- 图标 -->
<el-icon>
<component :is="item.meta.icon"></component>
</el-icon>
<span>{{ item.meta.title }}</span>
</template>
</el-menu-item>
</template>
<!-- 只有一个子集 -->
<!-- <template v-if="item.children && item.children.length == 1">-->
<!-- <el-menu-item-->
<!-- @click="goRoute"-->
<!-- :index="item.children[0].path"-->
<!-- v-if="!item.children[0].meta.hidden"-->
<!-- >-->
<!-- <el-icon>-->
<!-- <component :is="item.children[0].meta.icon"></component>-->
<!-- </el-icon>-->
<!-- <template #title>-->
<!-- <span>{{ item.children[0].meta.title }}</span>-->
<!-- </template>-->
<!-- </el-menu-item>-->
<!-- </template>-->
<!-- 有子路由, 有子菜单的继续遍历(递归) -->
<el-sub-menu
:index="item.path"
v-if="item.children && item.children.length >= 1"
>
<template #title>
<!-- 图标 -->
<el-icon>
<component :is="item.meta.icon"></component>
</el-icon>
<span>{{ item.meta.title }}</span>
</template>
<!-- 路由递归 : 二级路由还有子路由,子路由又还有子路由-->
<MenuTree :menuList="item.children"></MenuTree>
</el-sub-menu>
</template>
</div>
</template>
<script lang="ts" setup>
import { useRouter } from 'vue-router'
const router = useRouter()
// 获取父组件传递过来的全部路由数组
defineProps(['menuList'])
const goRoute = (e: any) => {
console.log(e.index)
router.push(e.index)
}
</script>
<style lang="scss" scoped></style>