vue动态的 BreadCrumb 组件el-breadcrumb ElementUI详解

 更新时间:2024年07月18日 10:16:26   作者:十月ooOO  
这篇文章主要介绍了vue如何做一个动态的 BreadCrumb 组件,el-breadcrumb ElementUI
,本文通过图文示例代码相结合给大家介绍的非常详细,需要的朋友可以参考下

vue 如何做一个动态的 BreadCrumb 组件 el-breadcrumb ElementUI

一、ElementUI 中的 BreadCrumb 定义

elementUI 中的 Breadcrumb 组件是这样定义的

<template>
  <el-breadcrumb separator="/">
    <el-breadcrumb-item :to="{ path: '/' }">主页</el-breadcrumb-item>
    <el-breadcrumb-item>系统配置</el-breadcrumb-item>
    <el-breadcrumb-item>基础配置</el-breadcrumb-item>
    <el-breadcrumb-item>自动登录</el-breadcrumb-item>
  </el-breadcrumb>
</template>

效果如图:

二、实现原理

我们需要实现的是,让它自己通过路由去填写这部分内容
原理是根据当前路由值,拆分成多个段,然后通过路由 path 去匹配对应的路由名称,再填入到上面的内容中即可。

比如:

1. 当前的路由值是 /system/normal-setup/auto-login 2. 通过拆分 / 生成一个数组

3. 依次匹配对应的路由名称

得到这个数组之后,依次去路由列表中匹配对应的路由名称

  • /system 系统配置
  • /system/normal-setup
  • 基础配置/system/normal-setup/auto-login 自动登录

4. 结果

这样就得到了一个 breadCrumb 数组,直接遍历这个数组,显示 BreadCrumb 即可

三、具体实现过程

知道了上面的实现原理,才会有具体的实现过程,这个过程还是有点麻烦的。

1. 处理路由数据

项目中用到的路由数据是这样的树形结构,路由数据的定义是这样的,里面的 children 可以嵌套任意层:

interface MenuEntity {
    id?: number | null,
    parent_id: number,
    name: string,
    icon?: string,
    type: EnumMenuType, // 1->目录 2->菜单 3->按钮 4->外链
    path: string,
    component?: string,
    visible: EnumMenuVisible, // 1->可见 2->隐藏 默认为1
    redirect: string,
    sort: number, // 默认为 20
    perm: string, // permission
    created_at?: string,
    updated_at?: string,
    children?: MenuEntity[]
}

实际的数据是这样的:

{
    "name": "系统配置",
    "id": 10,
    "parent_id": -1,
    "type": 1,
    "path": "/system",
    "component": "",
    "visible": 1,
    "redirect": "",
    "perm": "",
    "sort": 100,
    "icon": "Setting",
    "created_at": "2024-02-26T14:55:12+08:00",
    "updated_at": "2024-02-26T16:12:34+08:00",
    "children": [
        {
            "name": "基础配置",
            "id": 12,
            "parent_id": -1,
            "type": 1,
            "path": "/system/normal-setup",
            "component": "",
            "visible": 1,
            "redirect": "",
            "perm": "",
            "sort": 10,
            "icon": "CreditCard",
            "created_at": "2024-02-26T15:20:15+08:00",
            "updated_at": "2024-02-26T16:11:56+08:00",
            "children": [
                {
                    "name": "自动登录",
                    "id": 13,
                    "parent_id": 12,
                    "type": 2,
                    "path": "/system/normal-setup/auto-login",
                    "component": "System/NormalSetup/AutoLoginSetup.vue",
                    "visible": 1,
                    "redirect": "",
                    "perm": "",
                    "sort": 30,
                    "icon": "User",
                    "created_at": "2024-02-26T15:24:18+08:00",
                    "updated_at": "2024-05-17T14:11:52+08:00",
                    "children": []
                },
                {
                    "name": "系统更新",
                    "id": 28,
                    "parent_id": 12,
                    "type": 2,
                    "path": "/system/normal-setup/system-update",
                    "component": "System/SystemUpdate.vue",
                    "visible": 1,
                    "redirect": "",
                    "perm": "",
                    "sort": 50,
                    "icon": "UploadFilled",
                    "created_at": "2024-02-26T16:19:49+08:00",
                    "updated_at": "2024-05-17T14:11:39+08:00",
                    "children": []
                },
                {
                    "name": "申请厂家技术支持",
                    "id": 29,
                    "parent_id": 12,
                    "type": 2,
                    "path": "/system/normal-setup/factory-help",
                    "component": "User/Space.vue",
                    "visible": 1,
                    "redirect": "",
                    "perm": "",
                    "sort": 40,
                    "icon": "SuitcaseLine",
                    "created_at": "2024-02-26T16:20:11+08:00",
                    "updated_at": "2024-03-27T09:04:20+08:00",
                    "children": []
                }
            ]
        }
    ]
}

为了好后续匹配 path 到路由名,需要将这个数据平化成一个数组,并构建一个 Map<path, RouteItem> 这样的一个 Map 数据,目的是当执行下面操作时,取到对应的路由数据

flatMenuPathNameMap.get('/system')
// 最终取到这样的数据
{
    "name": "系统配置",
    "id": 10,
    "parent_id": -1,
    "type": 1,
    "path": "/system",
    "component": "",
    "visible": 1,
    "redirect": "",
    "perm": "",
    "sort": 100,
    "icon": "Setting",
    "created_at": "2024-02-26T14:55:12+08:00",
    "updated_at": "2024-02-26T16:12:34+08:00",
}

平化树形数据、生成对应的 Map 数据结构:

/**
 * 菜单相关
 * 这里是单独放到了 pinia 中
 */
export const useMenuStore = defineStore('menuStore', {
    state: () => ({
        menus: [] as Array<RouteRecordRaw>,
        flatMenuArray: [] as Array<MenuEntity>,
        flatMenuPathNameMap: new Map<string, string>()
    }),
    actions: {
        generateMenuArrayAndMap(){
            let menuString = localStorage.getItem('dataMenu')
            let menusCache = menuString ? JSON.parse(menuString) as Array<MenuEntity> : [] as Array<MenuEntity>
            let flatMenuArray = recursionMenuData(menusCache)
            this.flatMenuArray = flatMenuArray
            this.flatMenuPathNameMap = new Map(flatMenuArray.map(item => [item.path, item.name]))
            // 递归方法,平化菜单数据
            function recursionMenuData(menuArray: Array<MenuEntity>){
                let tempArray: Array<MenuEntity> = []
                menuArray.forEach(item => {
                    if (item.children && item.children.length > 0){
                        tempArray = tempArray.concat(recursionMenuData(item.children))
                        // 添加本身,并去除 children 属性
                        delete item.children
                        tempArray.push(item)
                    } else {
                        tempArray.push(item)
                    }
                })
                return tempArray
            }
        },
     }
})

使用的时候

import {useMenuStore, useProjectStore} from "./pinia";
const storeMenu = useMenuStore()
// 当执行下面的操作时就会补全  storeMenu.flatMenuArray 和  storeMenu.flatMenuPathNameMap
storeMenu.generateMenuArrayAndMap()

路由树的基础数据是这样的:

平化后的路由数组是这样的:

最终生成的 Map 数据是这样的:

2. 拆分当前路由 path,并匹配

比如当前路由是 /system/normal-setup/auto-login,把它通过 / 拆分之后就是这样的结果

import {useRoute} from "vue-router";
const route = useRoute()
let routeSectionArray = route.path.split('/').filter(item => item !== '')
// 这样拆分之后,前面会多出一个空白的 "" ,所以这里剔除了它

接下来要做的就是通过上面的 routerSectionArray 生成下面的几个路由组合,再去之前生成的 Map 中匹配对应的路由名即可

  • /system
  • /system/normal-setup
  • /system/normal-setup/auto-login

匹配之后就是这样的结果

  • /system 系统配置
  • /system/normal-setup
  • 基础配置/system/normal-setup/auto-login 自动登录

代码是这样的:

import {useRoute} from "vue-router";
import {onMounted, ref} from "vue";
import {useMenuStore} from "@/pinia";
const storeMenu = useMenuStore()
const route = useRoute()
const breadCrumbArray = ref<Array<{name: string, path: string}>>([])
onMounted(()=>{
    let routeSectionArray = route.path.split('/').filter(item => item !== '')
    console.log(routeSectionArray)
    routeSectionArray.forEach((_, index) => {
        let path = `/${routeSectionArray.slice(0,index + 1).join('/')}`
        let pathName = storeMenu.flatMenuPathNameMap.get(path)
        console.log('---',pathName, path)
        if (pathName){
            breadCrumbArray.value.push({name: pathName, path: path})
        }
    })
})

四、搭配其它组件构建页面

弄好上面的 BreadCrumb 组件之后,就可以不用再管它内部的内容了,它会自动根据当前路由值生成对应的内容。
这样我们就可以放心的把它放到页面结构中了。

比如我的页面主要结构是这样的:

Toolerbar.vue

<template>
    <div class="tool-bar">
        <div class="left">
            <Breadcrumb/>
            <slot name="left"/>
        </div>
        <div class="center">
            <slot name="center"/>
        </div>
        <div class="right">
            <slot name="right"/>
        </div>
    </div>
</template>
<script setup lang="ts">
import Breadcrumb from "@/layout/Breadcrumb.vue";
</script>
<style scoped lang="scss">
.tool-bar{
    padding: 0 20px;
    align-items: center;
    min-height: 50px;
    display: flex;
    flex-flow: row wrap;
    justify-content: space-between;
    .left{
        display: flex;
        flex-flow: row nowrap;
        justify-content: flex-start;
        align-items: center;
        flex-shrink: 0;
    }
    .center{
        display: flex;
        flex-flow: row nowrap;
        justify-content: flex-start;
        align-items: center;
        flex-grow: 1;
        flex-shrink: 0;
    }
    .right{
        display: flex;
        flex-flow: row nowrap;
        justify-content: flex-start;
        align-items: center;
        flex-shrink: 0;
    }
}
</style>

Breadcrumb.vue

<template>
    <el-breadcrumb separator="/">
        <el-breadcrumb-item :to="{ path: '/' }">主页</el-breadcrumb-item>
        <el-breadcrumb-item
            v-for="item in breadCrumbArray"
            :key="item">{{ item.name }}</el-breadcrumb-item>
    </el-breadcrumb>
</template>
<script setup lang="ts">
import {useRoute} from "vue-router";
import {onMounted, ref} from "vue";
import {useMenuStore} from "@/pinia";
const storeMenu = useMenuStore()
const route = useRoute()
defineProps( {
    height: { // 高度
        type: Number,
        default: 100
    }
})
const breadCrumbArray = ref<Array<{name: string, path: string}>>([])
onMounted(()=>{
    let routeSectionArray = route.path.split('/').filter(item => item !== '')
    routeSectionArray.forEach((_, index) => {
        let path = `/${routeSectionArray.slice(0,index + 1).join('/')}`
        let pathName = storeMenu.flatMenuPathNameMap.get(path)
        console.log('---',pathName, path)
        if (pathName){
            breadCrumbArray.value.push({name: pathName, path: path})
        }
    })
})
</script>
<style lang="scss" scoped>
@import "../assets/scss/plugin";
</style>

实际页面中使用时这样:

<template>
    <Container>
        <Toolbar>
            <template #left>
            </template>
            <template #center>
            </template>
            <template #right>
            </template>
        </Toolbar>
        <Content>
        </Content>
    </Container>
<template>

五、结果

到此这篇关于vue 如何做一个动态的 BreadCrumb 组件,el-breadcrumb ElementUI的文章就介绍到这了,更多相关vue动态的 BreadCrumb 组件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue虚拟滚动性能优化方式详解

    vue虚拟滚动性能优化方式详解

    这篇文章主要为大家介绍了vue虚拟滚动性能优化方式详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • Vue.extend实现组件库message组件示例详解

    Vue.extend实现组件库message组件示例详解

    这篇文章主要为大家介绍了Vue.extend实现组件库message组件示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Vue用v-for给循环标签自身属性添加属性值的方法

    Vue用v-for给循环标签自身属性添加属性值的方法

    这篇文章主要介绍了Vue用v-for给循环标签自身属性添加属性值的方法,文中大家给大家列举了三种方法 ,需要的朋友可以参考下
    2018-10-10
  • Vue实现万年日历的示例详解

    Vue实现万年日历的示例详解

    又是一个老生常谈的功能,接下来会从零实现一个万年日历,从布局到逻辑,再到随处可见的打卡功能。文中的示例代码简洁易懂,需要的可以参考一下
    2023-01-01
  • vue3 element-plus实现图片预览功能实例

    vue3 element-plus实现图片预览功能实例

    这篇文章主要给大家介绍了关于vue3 element-plus实现图片预览功能的相关资料,在项目中我们经常会碰到图片预览的功能需求,文中通过代码示例介绍的非常详细,需要的朋友可以参考下
    2023-09-09
  • Vue3+Element-Plus 实现点击左侧菜单时显示不同内容组件展示在Main区域功能

    Vue3+Element-Plus 实现点击左侧菜单时显示不同内容组件展示在Main区域功能

    这篇文章主要介绍了Vue3+Element-Plus 实现点击左侧菜单时显示不同内容组件展示在Main区域功能,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-01-01
  • 基于vue封装一个安全键盘组件

    基于vue封装一个安全键盘组件

    大部分中文应用弹出的默认键盘是简体中文输入法键盘,在输入用户名和密码的时候,如果使用简体中文输入法键盘,用户的输入记录会被缓存下来所以我们需要一个安全键盘,本文给大家介绍了如何基于vue封装一个安全键盘组件,需要的朋友可以参考下
    2023-12-12
  • 解决vuejs项目里css引用背景图片不能显示的问题

    解决vuejs项目里css引用背景图片不能显示的问题

    今天小编就为大家分享一篇解决vuejs项目里css引用背景图片不能显示的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-09-09
  • vue组件实现可搜索下拉框扩展

    vue组件实现可搜索下拉框扩展

    这篇文章主要为大家详细介绍了vue组件实现可搜索下拉框的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-06-06
  • 详解vue2和vue3如何定义响应式数据

    详解vue2和vue3如何定义响应式数据

    这篇文章主要是来和大家一起讨论一下vue2和vue3是如何定义响应式数据的,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以了解下
    2023-11-11

最新评论