前言
最近在写一个基于element-plus的后台管理系统,我需要在切换菜单时,动态的修改面包屑导航。但是对于如何处理嵌套数据有点疑问,好在最后解决了。
菜单结构
src/store/modules/app/index.ts
interface state {
bread: string[]
menu: menuData[]
}
const state: state = {
//面包屑
bread: [],
//菜单数据
menu: [
{
id: 1,
pid: 0,
icon: 'iconfont icon-yibiao',
index: '/dashboard',
title: '仪表盘',
sub: []
},
{
id: 2,
pid: 0,
icon: 'iconfont icon-moban',
index: 'template',
title: '模板',
sub: [
{
id: 3,
pid: 2,
index: '/template/leven1',
title: 'leven1',
sub: []
},
{
id: 4,
pid: 2,
index: '',
title: '三级菜单',
sub: [
{
id: 5,
pid: 4,
index: '/template/leven1/leven3',
title: 'leven3',
sub: []
},
{
id: 6,
pid: 4,
index: '/template/leven1/leven4',
title: 'leven6',
sub: []
}
]
}
]
}
]
}
const getters = {}
const actions = {}
const mutations = {
coverBread(state: state, payload: string[]) {
state.bread = payload
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
菜单组件
src/layouts/components/side-bar/index.vue
关注80-94行,处理方法是以path为key,对menu嵌套对象进行扁平化处理。
<template>
<div class="aside">
<div :class="['logo', isCollapse ? 'collapse-logo' : '']">
<img src="@/assets/images/logo.png" />
</div>
<el-menu
class="el-menu-vertical"
:collapse="isCollapse"
background-color="#303133"
text-color="#fff"
active-text-color="#ffd04b"
:router="true"
unique-opened
:default-active="activePath"
>
<template v-for="(item, index) in menu" :key="index">
<el-submenu v-if="item.sub.length" :index="item.title">
<template #title>
<i :class="item.icon"></i>
<span class="menu-title">{{ item.title }}</span>
</template>
<template v-for="(item, index) in item.sub" :key="index">
<el-submenu v-if="item.sub.length" :index="item.title">
<template #title>
<i :class="item.icon"></i>
<span class="menu-title">{{ item.title }}</span>
</template>
<el-menu-item
v-for="item in item.sub"
:key="item.index"
:index="item.index"
class="menu-item-sub"
>{{ item.title }}
</el-menu-item>
</el-submenu>
<el-menu-item :index="item.index" v-else class="menu-item"
>{{ item.title }}
</el-menu-item>
</template>
</el-submenu>
<el-menu-item v-else :index="item.index">
<i :class="item.icon"></i>
<template #title>
<span class="menu-title">{{ item.title }}</span>
</template>
</el-menu-item>
</template>
</el-menu>
<i
:class="['check-menu-icon', isCollapse ? 'el-icon-s-fold' : 'el-icon-s-unfold']"
@click="isCollapse = !isCollapse"
></i>
</div>
</template>
<script lang="ts">
/* eslint-disable no-undef */
import { computed, defineComponent, ref, reactive } from 'vue'
import { useRoute } from 'vue-router'
import { useStore } from 'vuex'
export default defineComponent({
name: 'appSideBar',
components: {},
setup() {
const store = useStore()
const route = useRoute()
let isCollapse = ref(false)
let menu = computed(() => store.state.app.menu)
let dfsBreadData = computed(() => dfsMenu(menu.value)) //扁平化后的菜单,用于面包屑
const activePath = computed(() => {
const { meta, path } = route
console.log('path', path)
dfsBreadData.value[path] && store.commit('app/coverBread', dfsBreadData.value[path])
//切换菜单页面下级页面,菜单hover状态应该不变
if (meta.activeMenu) {
return meta.activeMenu
}
return path
})
function dfsMenu(menu: menuData[]): any {
let result: any = {}
menu.forEach((item: menuData) => dfs(item, []))
return result
// 深度优先遍历
function dfs(menu: menuData, path: string[]) {
if (!menu.sub.length) {
// 用路径做key,方便查找 path是title组成的数组.
result[menu.index] = [...path, menu.title]
} else {
path.push(menu.title) //把当前title push进数组
menu.sub.forEach((menu: menuData) => dfs(menu, path)) //递归
}
}
}
return {
menu,
dfsBreadData,
isCollapse,
activePath
}
}
})
</script>
结语
扁平化之后的数据,以路由path为key,可以很快捷的进行面包屑赋值。
{
"/dashboard": [
"仪表盘"
],
"/template/leven1": [
"模板",
"leven1"
],
"/template/leven1/leven3": [
"模板",
"三级菜单",
"leven3"
],
"/template/leven1/leven4": [
"模板",
"三级菜单",
"leven6"
]
}
如果觉得这篇文章对您有帮助的话,欢迎点赞评论加转发。
代码实例
首发于语雀文档@is_tao