使用 element-plus 组件库。
<!-- 左侧菜单 -->
<div class="layout_slider">
<Logo></Logo>
<!-- 展示菜单 -->
<!-- 滚动组件 -->
<el-scrollbar class="scrollbar">
<!-- 菜单组件 -->
<el-menu :collapse="LayoutSettingStore.fold ? true : false" :default-active="$route.path" background-color="rgb(18 18 18)" text-color="white">
<!-- 根据路由动态生成菜单 -->
<Menu :menuList="userStore.menuRoutes"></Menu>
</el-menu>
</el-scrollbar>
</div>
menuRoutes:
// 常量路由(全部用户都可以访问到)
export const constantRoute = [
{
// 登录
path: '/login',
component: () => import('@/views/login/index.vue'),
name: 'login',
meta: {
// 菜单标题
title: '登录',
// 路由标题在菜单中是否隐藏
hidden: true,
// 菜单文字左侧图标
icon: 'Promotion'
}
},
{
// 登录成功后展示数据的路由
path: '/',
component: () => import('@/layout/index.vue'),
name: 'layout',
meta: {
// 菜单标题
hidden: false,
icon: 'Memo',
},
redirect: '/home',
children: [
{
path: '/home',
component: () => import('@/views/home/index.vue'),
meta: {
title: '首页',
hidden: false,
icon: 'HomeFilled'
}
},
]
},
{
path: '/screen',
component: () => import('@/views/screen/index.vue'),
name: 'Screen',
meta: {
title: '数据大屏',
hidden: false,
icon: 'CameraFilled'
}
},
{
path: '/acl',
component: () => import('@/layout/index.vue'),
name: 'Acl',
meta: {
title: '权限管理',
icon: 'CoffeeCup'
},
redirect: '/acl/role',
children: [
{
path: '/acl/role',
component: () => import('@/views/acl/role/index.vue'),
name: 'role',
meta: {
title: '角色管理',
icon: 'DishDot'
}
},
{
path: '/acl/user',
component: () => import('@/views/acl/user/index.vue'),
name: 'User',
meta: {
title: '用户管理',
icon: 'IceDrink'
}
},
{
path: '/acl/permission',
component: () => import('@/views/acl/permission/index.vue'),
name: 'Permission',
meta: {
title: '菜单管理',
icon: 'HotWater'
}
},
]
},
{
path: '/product',
component: () => import('@/layout/index.vue'),
name: 'Product',
meta: {
title: '商品管理',
icon: 'IceCreamRound'
},
redirect: '/product/attr',
children: [
{
path: '/product/attr',
component: () => import('@/views/product/attr/index.vue'),
name: 'Attr',
meta: {
title: '属性管理',
icon: 'IceCreamSquare'
}
},
{
path: '/product/trademark',
component: () => import('@/views/product/trademark/index.vue'),
name: 'Trademark',
meta: {
title: '品牌管理',
icon: 'ReadingLamp'
}
},
{
path: '/product/spu',
component: () => import('@/views/product/spu/index.vue'),
name: 'Spu',
meta: {
title: 'SPU管理',
icon: 'Handbag'
}
},
{
path: '/product/sku',
component: () => import('@/views/product/sku/index.vue'),
name: 'Sku',
meta: {
title: 'SKU管理',
icon: 'ShoppingBag'
}
}
]
},
{
//404
path: '/404',
component: () => import('@/views/404/index.vue'),
name: '404',
meta: {
// 菜单标题
title: '404',
hidden: true,
icon: 'MoreFilled'
}
},
{
path: '/:pathMatch(.*)*',
redirect: '/404',
name: 'Any',
meta: {
// 菜单标题
title: '任意路由',
hidden: true,
icon: 'CirclePlusFilled'
}
}
]
Menu:
<template>
<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">
<el-icon>
<component :is="item.meta.icon"></component>
</el-icon>
<template #title>
<span>{{ item.meta.title }}</span>
</template>
</el-menu-item>
</template>
<!-- 有但是只有一个子路由 -->
<template v-if="item.children && item.children.length == 1">
<el-menu-item :index="item.children[0].path" v-if="!item.children[0].meta.hidden" @click="goRoute">
<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>
<!-- 递归组件要求组件必须有名字 name -->
<Menu :menuList="item.children"></Menu>
</el-sub-menu>
</template>
</template>
<script setup lang="ts">
// 获取路由器对象
import { useRouter } from 'vue-router';
// 获取父组件传递过来的全部路由数组
defineProps(['menuList'])
const $router = useRouter();
// 点击菜单的回调,路由跳转
const goRoute = (vc: any) => {
// 路由跳转
$router.push(vc.index);
}
</script>
<script lang="ts">
export default {
name: 'Menu'
}
</script>
<style scoped></style>