Vue中的面包屑联动导航栏Tab点击 实现方式

563 阅读3分钟

面包屑

面包屑,准确的说叫面包屑导航(BreadcrumbNavigation),这个概念源自与一个童话故事,大体就是两个傻小子在森林里走迷路了,发现道路沿边的面包屑,最后通过这些面包屑走出了森林,找到了回家的路,总之就是面包屑就是有一个导航的作用.

在前端领域,面包屑效果是很常见的,特别是一些 官网B端产品等等。

分享一下我做的项目demo吧

如图:

-0evzhofkrkl27e8d.gif

设计

面包屑组件效果肯定是要跟随侧边菜单的,侧边菜单的数据一般也是后端根据权限不同返回的。

侧边菜单数据结构大体如下:

menuData: [
        {
          path: '/',
          name: 'home',
          label: '首页',
          icon: 's-home',
          url: 'Home/Home',
        },
        {
          path: '/mall',
          name: 'mall',
          label: '商品管理',
          icon: 'video-play',
          url: 'MallManage/MallManage',
        },
        {
          path: '/user',
          name: 'user',
          label: '用户管理',
          icon: 'user',
          url: 'UserManage/UserManage',
        },
        {
          label: '其他',
          name: 'other',
          icon: 'location',
          path: '/other',
          children: [
            {
              path: '/other/page1',
              name: 'page1',
              label: '其他1',
              icon: 'setting',
              url: 'Other/PageOne',
            },
            {
              path: '/other/page2',
              name: 'page2',
              label: '其他2',
              icon: 'setting',
              url: 'Other/PageTwo',
            },
          ],

整体思路:

  1. 获取当前路由path
  2. 通过侧边菜单数据结构可以看出是一个的物理结构,当前路由就是一个子节点
  3. 只要从树root一直找到对应的那个子节点,这一段就是我们所需要的数据

比如:

当前路由: /home/other/otherPage1

面包屑导航: 首页>其他>Page1

只需要将当前路由拆分,在菜单树中从上往下依次查找就可以了 对应关系如: /home => 一级菜单/mult/three => 一级菜单/二级菜单3/mult/three/threeChild1 => 一级菜单/二级菜单3/三级菜单1

步骤:

  1. 由于在vue项目中,导航栏和面包屑常常不同组件中,比如导航栏为Aside组件,面包屑在Header中。我这里用vuex进行状态存储,存储一个面包屑数据List(我们叫做breadcrumbList
  2. 首先,点击导航栏会触发一个click事件(代码中为clickMenu),在事件中对面包屑数据List进行更新。
  3. 如何更新List,上面已经说明了思路,切分+遍历树,把每一层节点当作一个数据放进breadcrumbList
  4. 其次在面包屑展示模块中,对breadcrumbList遍历,每个节点为一个item,中间用 /划分

代码

export default {
  data() {
    return {
      //导航栏Tab
      menuData: [
        {
          path: '/',
          name: 'home',
          label: '首页',
          icon: 's-home',
          url: 'Home/Home',
        },
        {
          path: '/mall',
          name: 'mall',
          label: '商品管理',
          icon: 'video-play',
          url: 'MallManage/MallManage',
        },
        {
          path: '/user',
          name: 'user',
          label: '用户管理',
          icon: 'user',
          url: 'UserManage/UserManage',
        },
        {
          label: '其他',
          name: 'other',
          icon: 'location',
          path: '/other',
          children: [
            {
              path: '/other/page1',
              name: 'page1',
              label: '其他1',
              icon: 'setting',
              url: 'Other/PageOne',
            },
            {
              path: '/other/page2',
              name: 'page2',
              label: '其他2',
              icon: 'setting',
              url: 'Other/PageTwo',
            },
          ],
        },
      ],
    };
  },
  methods: {
    getCurrentBreadcrumbData(currentPath) {
      //这里将当前路径进行切分
      //如“/other/otherPage1” 转换成数组["other","otherPage1"]
      const currentPathSplit = currentPath.path.split('/').filter((c) => !!c);
      console.log('currentPathSplit', currentPathSplit);
      //二次转换,把除了一级节点之后的节点都拼接上之前的节点
      //["other","otherPage1"] 转成 ["other","other/otherPage1"]
      currentPathSplit.forEach((c, i) => {
        if (i) {
          currentPathSplit[i] = currentPathSplit[i - 1] + '/' + c;
        } else {
          currentPathSplit[i] = '/' + currentPathSplit[i];
        }
      });

      //初始化一个面包屑列表,默认包含首页tab
      const newBreadTabs = [
        {
          path: '/',
          name: 'home',
          label: '首页',
          icon: 's-home',
          url: 'Home/Home',
        },
      ];
      //导航栏menuData可以看成一棵树,每个数据可以看作一个节点
      let Tree = this.menuData;
      currentPathSplit.forEach((c) => {
        //console.log(c);
        //匹配path,获取menu tab
        const findItem = Tree.find((tab) => tab.path == c);
        if (findItem) {
          //放进面包屑列表中
          newBreadTabs.push(findItem);
          //匹配到导航栏的节点之后,如果有chilren节点,则把这个chilren节点赋值给Tree,下次遍历就是去新的Tree里匹配
          if (findItem.children) {
            Tree = findItem.children;
          }
        }
      });
      //console.log('newBreadTabs', newBreadTabs);
      return newBreadTabs;
    },

    //点击导航栏Tab事件
    clickMenu(item) {
      //console.log(item);
      //console.log(this.$route);
      
      //使用router进行页面跳转,这里的逻辑可以不用管,和面包屑无关
      if (this.$route.path == item.path || (this.$route.path == '/home' && item.path == '/')) {
        return;
      }
      
      const newBreadTabs = this.getCurrentBreadcrumbData(item);
      //更新面包屑list全局数据-这样面包屑数据才能被面包屑导航栏读取到展示和更新
      this.$store.commit('addBreadTabs', newBreadTabs);
      this.$router.push(item.path);
    },

html我这里使用了element-ui组件,自己写原生html也是用遍历的思想

<!--面包屑-->
<el-breadcrumb separator="/" class="breadcrumb">
   <el-breadcrumb-item v-for="tab in getTabsBreadList" :key="tab.name" :to="{ path: tab.path }">{{ tab.label }}</el-breadcrumb-item>
</el-breadcrumb>

最后附上该项目完整github: github.com/Chenhanyi38…