主要是利用vue 路由 route.matched属性 matched文档说明

本节目标
image.png

2-1 route.matched

与给定路由地址匹配的标准化的路由记录数组。

当路由路径是 /system/menu时 会匹配到两条路由记录
image.png
每条记录里 有我们所需要的meta字段
image.png
route.meta.title会作为路由面包屑导航名称,如果没有title就不显示在面包屑导航

2-2 导入element的面包屑组件 ElBreadcrumb和 ElBreadcrumbItem

image.png

2-3 安装path-to-regexp(这里有坑 不明确)

path-to-regexp可以将动态路由路径/user/:id 转换成正则表达式。Path-to-RegExp

  1. # 安装固定版本
  2. npm install path-to-regexp@6.2.0 --save
  3. # npm默认安装使用时
  4. # import { compile } from 'path-to-regexp'
  5. # 老报错compile函数是undefined 时好时坏

这里我们要用它解决 面包屑导航是动态路由的情况下,点击面包屑导航router.push跳转时 怎么把动态参数填充进去 拼接成正常路由 /user/:class/role/:id + {id: 2, class: 3} => /user/3/role/2 然后正确跳转

  1. 例如:动态路径是 /user/:class/role/:id 我们要将{id: 2, class: 3}填充为完整路径 然后才能正确跳转
  2. const { compile } = require('path-to-regexp')
  3. const toPath = compile('/user/:class/role/:id')
  4. const path = toPath({ id: 2, class: 3 })
  5. console.log('path:', path) // path: /user/3/role/2

2-4 创建Breadcrumb组件

src/components/Breadcrumb/index.vue

  1. <template>
  2. <el-breadcrumb class="app-breadcrumb breadcrumb-container" separator="/">
  3. <el-breadcrumb-item
  4. v-for="(item, index) in levelList"
  5. :key="item.path"
  6. >
  7. <!-- 面包屑导航最后一个是不可点击的 因为最后一个正是当前所显示的路由 -->
  8. <span
  9. v-if="index == levelList.length - 1"
  10. class="no-redirect"
  11. >
  12. {{ item.meta.title }}
  13. </span>
  14. <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
  15. </el-breadcrumb-item>
  16. </el-breadcrumb>
  17. </template>
  18. <script lang="ts">
  19. import { defineComponent, ref, watch, onBeforeMount } from 'vue'
  20. import { useRoute, useRouter, RouteLocationMatched, RouteLocationRaw } from 'vue-router'
  21. import { compile } from 'path-to-regexp'
  22. type PartialRouteLocationMatched = Partial<RouteLocationMatched>
  23. export default defineComponent({
  24. name: 'Breadcrumb',
  25. setup() {
  26. const route = useRoute() // 相当于this.$route对象
  27. const router = useRouter() // 相当于this.$router对象
  28. const levelList = ref<Array<PartialRouteLocationMatched>>([]) // 导航列表 存放matched里筛选的路由记录
  29. // 判断是不是Dashboard路由
  30. const isDashboard = (route?: PartialRouteLocationMatched) => {
  31. const name = route && route.name
  32. if (!name) {
  33. return false
  34. }
  35. return (name as string).trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase()
  36. }
  37. // 获取面包屑导航
  38. const getBreadcrumb = () => {
  39. // 对匹配的路由进行过滤 过滤掉没有title属性的路由,没有title就无法作为面包屑导航
  40. let matched = route.matched.filter(item => item.meta && item.meta.title) as PartialRouteLocationMatched[]
  41. // 获取第一个匹配路由记录
  42. const first = matched[0]
  43. // 我们要把dashboard作为首页 始终固定在面包屑导航第一个 Dashboard/System/Menu Management
  44. // 如果第一个匹配到的路由记录不是dashboard 我们自己就把它放在记录数组的第一项
  45. if (!isDashboard(first)) {
  46. matched = ([{
  47. path: '/dashboard',
  48. meta: {
  49. title: 'Dashboard'
  50. }
  51. }] as PartialRouteLocationMatched[]).concat(matched)
  52. }
  53. // route.meta.breadcrumb自定义属性 如果为false 匹配到时 将不会再面包屑导航显示该路由
  54. // {
  55. // path: 'menu',
  56. // meta: {
  57. // title: 'Menu Management',
  58. // breadcrumb: false // 不显示在面包屑导航 默认true
  59. // }
  60. // }
  61. levelList.value = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
  62. }
  63. onBeforeMount(() => {
  64. getBreadcrumb()
  65. })
  66. watch(() => route.path, () => {
  67. getBreadcrumb()
  68. })
  69. // 主要是针对 动态路由 /user/:id 进行动态参数填充
  70. // path-to-regexp 文档说明 https://www.npmjs.com/package/path-to-regexp
  71. const pathCompile = (path: string) => {
  72. // 根据路径变编译成正则函数 并接收具体参数 比如根据正则/user/:id 帮你将:id替换成具体路径
  73. const toPath = compile(path) // 比如 path /user/:id
  74. const params = route.params // { id: 10 }
  75. return toPath(params) // toPath({ id: 10 }) => /user/10 返回填充后的路径
  76. }
  77. // 点击面包屑导航可跳转
  78. const handleLink = (route: RouteLocationMatched) => {
  79. const { path, redirect } = route
  80. // 如果是重定向路由 就走重定向路径
  81. if (redirect) {
  82. router.push(redirect as RouteLocationRaw)
  83. return
  84. }
  85. router.push(pathCompile(path))
  86. }
  87. return {
  88. levelList,
  89. handleLink
  90. }
  91. }
  92. })
  93. </script>
  94. <style lang="scss" scoped>
  95. .app-breadcrumb.el-breadcrumb {
  96. display: inline-block;
  97. /* float: left; */
  98. line-height: 50px;
  99. font-size: 14px;
  100. margin-left: 8px;
  101. }
  102. .no-redirect {
  103. color: #97a8be;
  104. cursor: text;
  105. }
  106. </style>
  107. <style lang="scss">
  108. .breadcrumb-enter-active,
  109. .breadcrumb-leave-active {
  110. transition: all .5s;
  111. }
  112. .breadcrumb-enter,
  113. .breadcrumb-leave-active {
  114. opacity: 0;
  115. transform: translateX(20px);
  116. }
  117. .breadcrumb-leave-active {
  118. position: absolute;
  119. }
  120. .breadcrumb-move {
  121. transition: all .5s;
  122. }
  123. </style>

本节参考源码

https://gitee.com/brolly/vue3-element-admin/commit/45c652324fed0f105d02ae40d61614d158a2c2b8