# Menu组件的封装
# 递归组件
在Vue中,可以使用在某一组件中调用自身实现递归组件。
需要注意的是,递归组件需要导出一个name,若使用了setup语法糖,我们还需要再添加一个script标签用于导出name。
<script lang="ts">
// 所有script标签的lang需相同
export default {
name: 'Menu', // 递归组件必须有name
}
</script>
# 递归的关键点
在拿到数据后需要遍历,通过if判断是否有子数据,若有需要调用自身组件进行递归
# 代码
<!-- Menu.vue -->
<template>
<!-- 不能用div包裹否则会产生无用div -->
<template v-for="item in menuList" :key="item.path">
<!-- 没有自路由 -->
<template v-if="!item.children">
<el-menu-item
v-if="!item.meta!.hidden"
:index="item.path"
@click="handleGoRoute"
>
<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
v-if="!item.children[0].meta!.hidden"
:index="item.children[0].path"
@click="handleGoRoute"
>
<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
v-if="item.children && item.children.length > 1"
:index="item.path"
>
<template #title>
<el-icon>
<component :is="item.meta!.icon"></component>
</el-icon>
<span>{{ item.meta!.title }}</span>
</template>
<Menu :menuList="item.children"></Menu>
</el-sub-menu>
</template>
</template>
<script setup lang="ts">
import type { RouteRecordRaw } from 'vue-router'
import { useRouter } from 'vue-router'
import { MenuItemRegistered } from 'element-plus'
const router = useRouter()
defineProps<{
menuList: RouteRecordRaw[]
}>()
const handleGoRoute = (vc: MenuItemRegistered) => {
router.push(vc.index)
}
</script>
<script lang="ts">
export default {
name: 'Menu', // 递归组件必须有name
}
</script>
<style lang="sccss" scoped></style>
传入Menu组件的数据结构
import { RouteRecordRaw } from 'vue-router'
export const constantRoutes: RouteRecordRaw[] = [
{
path: '/login',
name: 'Login',
component: () => import('@/views/Login/index.vue'),
meta: {
title: '登录',
hidden: true, // 路由标题是否隐藏
icon: 'Promotion', // 菜单左侧图标
},
},
{
path: '/',
name: 'Layout',
component: () => import('@/layout/index.vue'),
meta: {
hidden: false,
title: '',
icon: '',
},
redirect: '/home',
children: [
{
path: '/home',
component: () => import('@/views/Home/index.vue'),
name: 'Home',
meta: {
title: '首页',
hidden: false,
icon: 'HomeFilled',
},
},
],
},
{
path: '/screen',
component: () => import('@/views/screen/index.vue'),
name: 'Screen',
meta: {
title: '数据大屏',
hidden: false,
icon: 'Platform',
},
},
{
path: '/acl',
component: () => import('@/layout/index.vue'),
name: 'Acl',
meta: {
title: '权限管理',
hidden: false,
icon: 'Lock',
},
redirect: '/acl/user',
children: [
{
path: '/acl/user',
component: () => import('@/views/Acl/User/index.vue'),
name: 'Acl',
meta: {
title: '用户管理',
hidden: false,
icon: 'User',
},
},
{
path: '/acl/role',
component: () => import('@/views/Acl/Role/index.vue'),
name: 'Role',
meta: {
title: '角色管理',
hidden: false,
icon: 'UserFilled',
},
},
{
path: '/acl/permission',
component: () => import('@/views/Acl/Permission/index.vue'),
name: 'Permission',
meta: {
title: '菜单管理',
hidden: false,
icon: 'Grid',
},
},
],
},
{
path: '/product',
component: () => import('@/layout/index.vue'),
name: 'Product',
meta: {
title: '商品管理',
icon: 'Goods',
hidden: false,
},
redirect: '/product/trademark',
children: [
{
path: '/product/trademark',
component: () => import('@/views/Product/Trademark/index.vue'),
name: 'Trademark',
meta: {
title: '品牌管理',
icon: 'ShoppingCart',
hidden: false,
},
},
{
path: '/product/attr',
component: () => import('@/views/Product/Attr/index.vue'),
name: 'Attr',
meta: {
title: '属性管理',
icon: 'ChromeFilled',
hidden: false,
},
},
{
path: '/product/spu',
component: () => import('@/views/Product/Spu/index.vue'),
name: 'Spu',
meta: {
title: 'Spu管理',
icon: 'Management',
hidden: false,
},
},
{
path: '/product/sku',
component: () => import('@/views/Product/Sku/index.vue'),
name: 'Sku',
meta: {
title: 'Sku管理',
icon: 'Orange',
hidden: false,
},
},
],
},
{
path: '/404',
name: '404',
component: () => import('@/views/404/index.vue'),
meta: {
title: '404',
hidden: true,
icon: 'CloseBold',
},
},
{
// 任意路由
path: '/:pathMatch(.*)*',
redirect: '/404',
name: 'Any',
meta: {
title: '任意路由',
hidden: true,
icon: 'Warning',
},
},
]