VUE中二级路由(router)实现多级面包屑(breadcrumb)跳转

3,569 阅读1分钟

概述

常用vue 开发路由都是二级管理,面包屑常规是按照路由的 this.$route.matched获取到的路由列表匹配,无法实现每个面包屑节点都能点。经过本文改造得以优化。初到掘金,请指教。

最终效果

image.png

路由改造

重点思路

  • parentBreadcrumb: '['父级路由path']' 如果设置了会在面包屑中作为父级路由,添加到面包屑中间。
  • noredirect: true 如果设置了true,择控制面包屑点击路由不跳转。
  • 路由构造LEVELONE、A1、A2、A3 ,LEVELONE、B1、B2、B3 分别行程两条三级路由,并且每一级路由都能跳转。
import Router from 'vue-router'
Vue.use(Router)
/* Layout */
import Layout from '@/layout'
/**
 * hidden: true    如果设置为true则不会显示在侧边菜单
 * alwaysShow: true   设置为true一直显示根路由
 * redirect: noRedirect  当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
 * name:'router-name'    设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
 * meta : {
    roles: ['admin','editor']    设置该路由进入的权限,支持多个权限叠加
    title: 'title'               设置该路由在侧边栏和面包屑中展示的名字
    icon: 'svg-name'            设置该路由的图标
    breadcrumb: false             如果设置为false,则不会在breadcrumb面包屑中显示
    activeMenu: '/example/list'    如果设置了激活菜单,则侧边菜单会默认选中
    parentBreadcrumb: '['父级路由path']'    如果设置了会在面包屑中作为父级路由,添加到面包屑中间
    noredirect: true   如果设置了true,择控制面包屑点击路由不跳转
  }
 */
// 固定路由,所有的人都可以访问
export const constantRoutes = [
  {
    path: '/LEVELONE',
    component: Layout,
    noredirect: true,
    redirect: 'bloodClinical/aggregateAnalysis',
    alwaysShow: true,
    meta: { title: 'LEVELONE', icon: 'dashboard', noredirect: true },
    // component: () =>
    //   import('@/views/aggregateAnalysis'),
    children: [{
      path: 'A1',
      name: 'A1',
      component: () =>
        import('@/views/A/A1.vue'),
      meta: {
        title: 'A1', icon: 'dashboard', keepAlive: true, parentBreadcrumb: [
        ]
      }
    },
    {
      path: 'A2',
      name: 'A2',
      // hidden: true,
      component: () =>
        import('@/views/A/A2.vue'),
      meta: {
        title: 'A2', icon: 'dashboard', parentBreadcrumb: [
          'A1'
        ]
      }, hidden: true
    },
    {
      path: 'A3',
      name: 'A3',
      // hidden: true,
      component: () =>
        import('@/views/A/A3.vue'),
      meta: {
        title: '患者分析', icon: 'dashboard', parentBreadcrumb: [
          'A1',
          'A2'
        ]
      }, hidden: true

    },
    {
      path: 'B1',
      name: 'B1',
      component: () =>
        import('@/views/B/B1.vue'),
      meta: {
        title: 'B1', icon: 'dashboard', keepAlive: true, parentBreadcrumb: [
        ]
      }
    },
    {
      path: 'B2',
      name: 'B2',
      // hidden: true,
      component: () =>
        import('@/views/B/B2.vue'),
      meta: {
        title: 'B2', icon: 'dashboard', parentBreadcrumb: [
          'B1'
        ]
      }, hidden: true
    },
    {
      path: 'B3',
      name: 'B3',
      // hidden: true,
      component: () =>
        import('@/views/B/B3.vue'),
      meta: {
        title: '患者分析', icon: 'dashboard', parentBreadcrumb: [
          'B1',
          'B2'
        ]
      }, hidden: true

    },
    ]
  },
]
const createRouter = () =>
  new Router({
    // mode: 'history', // 上线前可以设置为history,但是需要后台支持配置
    // 切换路由回到顶部 (默认)
    scrollBehavior: (to, from, savedPosition) => ({ y: 0 }),
    routes: constantRoutes
  })
const router = createRouter()
// 重置路由
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}
export default router

面包屑组件

重点代码

  • 根据findRoute() 找到路由中的parentBreadcrumb配置,返回路由数据。添加到面包屑数组中, this.levelList.splice(1, 0, ...pushlist);
<template>
  <el-breadcrumb class="app-breadcrumb" separator="/">
    <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
      <span v-if="item.redirect==='noRedirect'||index==levelList.length-1"
        class="no-redirect">{{ item.meta.title }}</span>
      <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
    </el-breadcrumb-item>
  </el-breadcrumb>
</template>
<script>
import pathToRegexp from 'path-to-regexp';
export default {
  data() {
    return {
      levelList: null,
    };
  },
  computed: {
    routes() {
      return this.$router.options.routes;
    },
  },
  watch: {
    $route() {
      this.getBreadcrumb();
    },
  },
  created() {
    this.getBreadcrumb();
  },
  methods: {
    getBreadcrumb() {
      // debugger;
      // only show routes with meta.title
      let matched = this.$route.matched.filter(
        (item) => item.meta && item.meta.title
      );
      const first = matched[0];
      const last = matched[matched.length - 1];
      if (!this.isDashboard(first)) {
        matched = [
          // { path: '/dashboard', meta: { title: '临床用血' } }
        ].concat(matched);
      }
      this.levelList = matched.filter(
        (item) => item.meta && item.meta.title && item.meta.breadcrumb !== false
      );
      const pushlist = [];
      if (last.meta.parentBreadcrumb && last.meta.parentBreadcrumb.length > 0) {
        last.meta.parentBreadcrumb.forEach((element) => {
          const selectroute = this.findRoute(element);
          if (selectroute != null) {
            pushlist.push(selectroute);
          }
        });
      }
      this.levelList.splice(1, 0, ...pushlist);
    },
    isDashboard(route) {
      const name = route && route.name;
      if (!name) {
        return false;
      }
      return (
        name.trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase()
      );
    },
    pathCompile(path) {
      // To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
      const { params } = this.$route;
      var toPath = pathToRegexp.compile(path);
      return toPath(params);
    },
    handleLink(item) {
      const { redirect, path } = item;
      if (item.meta.noredirect || item.meta.noredirect != undefined) {
        return;
      }
      if (redirect) {
        this.$router.push(redirect);
        return;
      }
      this.$router.push(this.pathCompile(path));
    },
    findRoute(name) {
      let routes = null;
      for (var i = 0; i < this.routes.length - 1; i++) {
        if (this.routes[i].children) {
          routes = this.routes[i].children.find((e) => e.name == name);
        }
      }
      return routes;
    },
  },
};
</script>
<style lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
  display: inline-block;
  font-size: 14px;
  line-height: 50px;
  margin-left: 8px;

  .no-redirect {
    color: #97a8be;
    cursor: text;
  }
}
</style>