vue 后台面包屑导航

效果图

gitee 项目地址可以直接下载项目。
在这里插入图片描述
如图效果需要用到Element-ui中的Element-ui面包屑Element-ui Tag组件,再使用vuex进行和sessionStorage做到持久化储存。
先看一下页面结构
在这里插入图片描述

1、创建路由

脚手架项目创建的时候选择vue-router或者直接使用命令 npm install vue-router 注意npm下载的话vue2要使用vue-router3版本,vue3使用vue-router4版本,npm install vue-router @3。

import Vue from 'vue'
import VueRouter from 'vue-router'
import Index from '../views/index.vue'
import IndexView from '../views/IndexViews/IndexView'
import EchartsZx from '../views/EchartsViews/EchartsZx'
import EchartsZz from '../views/EchartsViews/EchartsZz'
import EchartsBz from '../views/EchartsViews/EchartsBz'
Vue.use(VueRouter)
const routes = [
  {
   path:'/',
   name:'',
   component:Index,
   redirect:'/IndexView',
   children:[
    {
      path: '/IndexView',
      name: '首页',
      component:IndexView
    },
    {
      path: '/EchartsZx',
      name: '折线图',
      component:EchartsZx
    },
    {
      path: '/EchartsZz',
      name: '柱状图',
      component:EchartsZz
    },
    {
      path: '/EchartsBz',
      name: '饼状图',
      component:EchartsBz
    }
   ]
  },

]
//解决重复点击路由跳转报错
const VueRouterPush = VueRouter.prototype.push
VueRouter.prototype.push = function push (to) {
  return VueRouterPush.call(this, to).catch(err => err)
}
const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})
//修改浏览器标题
router.beforeEach((to,from,next)=>{
  if(to.name){
    document.title = to.name
  }
  next()
})
export default router

2、创建组件

leftView.vue左侧菜单栏,HeaderView.vue头部面包屑,BreadcrumbTag.vue公共的tag。

2.1 左侧菜单栏

左侧菜单栏用到了Element-ui 的NavMenu 导航菜单。主要操作模拟后端数据,点击菜单触发vuex方法将路由数据储存到vuex用于面包屑和tag组件展示。

<template>
  <div>
    <div class="scrollbar">
      <div class="title">
        vue2后台系统
      </div>
      <el-menu :collapse-transition="false" class="el-menu-vertical-demo" router :default-active="defaultActive"
        background-color="#FFFFFF" text-color="#000" active-text-color="#4080FF" :collapse="isCollapse">
        <el-menu-item @click="goli(menu)" v-if="menu.alwaysShow" :index="menu.url" v-for="(menu, index) in allmenu"
          :key="index">
          <i :class="[menu.icon]" class="icons"></i>
          <span style="margin-left: 10px">{{ menu.menuname }}</span>
        </el-menu-item>
        <el-submenu v-if="!menu.alwaysShow" :index="menu.url" v-for="(menu, index) in allmenu" :key="index">
          <template slot="title">
            <i :class="[menu.icon]" class="icons"></i>
            <span style="margin-left: 10px">{{ menu.menuname }}</span>
          </template>
          <el-menu-item-group>
            <el-menu-item @click="goli(chmenu)" v-for="chmenu in menu.menus" :index="chmenu.url" :key="chmenu.menuid">
              <i :class="chmenu.icon"></i>
              <span style="margin-left: 20px">{{ chmenu.menuname }}</span>
            </el-menu-item>
          </el-menu-item-group>
        </el-submenu>
      </el-menu>
    </div>

  </div>
</template>
<script>
import { mapState,mapMutations } from 'vuex'
export default {
  name: 'index',
  data() {
    return {
      allmenu: [],
    }
  },
  watch:{
    //监听路由跳转
    $route(to, from) {
      if(to.meta.childrenUrl){
        this.SetDefaultActive(to.meta.childrenUrl)
      }else{
        this.SetDefaultActive(this.$route.path)
      }
    }
  },
  computed: {
    //从vuex拿state的数据
    ...mapState(['isCollapse','defaultActive']),
  },
  mounted() {
    // 获取图形验证码
    let res = {
      success: true,
      data: [
        {
          menuid: 21,
          icon: 'el-icon-user-solid',
          menuname: '首页',
          url: "/IndexView",
          alwaysShow: true
        },
        {
          menuid: 31,
          icon: 'el-icon-s-order',
          menuname: 'Echarts展示',
          url: "null",
          menus: [
            {
              menuid: 32,
              menuname: "折线图",
              url: "/EchartsZx",
              menus: null,
            },
            {
              menuid: 33,
              menuname: "柱状图",
              url: "/EchartsZz",
              menus: null,
            },
            {
              menuid: 34,
              menuname: "饼状图",
              url: "/EchartsBz",
              menus: null,
            }
          ],
        }
      ],
      msg: "success",
    };
    this.allmenu = res.data;
  },
  methods: {
    ...mapMutations(['SetDefaultActive']),
    goli(item) {
      this.$store.commit('SetMenu', item)
    }
  }
};
</script>
<style lang="scss">
.title {
  width: auto;
  height: 60px;
  text-align: center;
  line-height: 60px;
}

.scrollbar {
  height: calc(100vh - 50px);
  position: relative;
  overflow: auto;
}

.scrollbar::-webkit-scrollbar {
  width: 6px;
  height: 4px;
}

.scrollbar::-webkit-scrollbar-thumb {
  border-radius: 5px;
  background: #ffffff;
}

.scrollbar::-webkit-scrollbar-button {
  display: none;
}

.scrollbar::-webkit-scrollbar-track {
  border-radius: 5px;
  background: #e1e1e4;
}

.el-aside {
  width: 100%;
}

.el-menu-item:hover {
  background-color: #ffffff !important;
  color: #4080ff !important;
}

.el-submenu__title:hover {
  background-color: #ffffff !important;
  color: #4080ff !important;
}

.icons {
  vertical-align: middle;
  margin-right: 5px;
  width: 24px;
  text-align: center;
  font-size: 18px;
}

.el-submenu__title:hover .icons {
  color: #4080ff !important;
}

.el-submenu.is-active>.el-submenu__title,
.el-submenu.is-active>.el-submenu__title .icons {
  color: #4080ff !important;
}
</style>

2.2 头部面包屑

<template>
    <div>
      <div class="heades">
         //点击展开收起
          <i class="icons" :class="isCollapse ? 'el-icon-s-unfold' :'el-icon-s-fold' " @click="SetisCollapse"></i>
          <el-breadcrumb separator="/">
            <el-breadcrumb-item  :to="{path:item.url}" v-for="(item,index) in tabslist" :key="index">{{item.menuname}}</el-breadcrumb-item>
          </el-breadcrumb>
      </div>
    </div>
  </template>
  <script>
  import { mapState,mapMutations } from 'vuex'
  export default {
    name: 'index',
    data() {
      return {
      }
    },
    computed:{
      //从vuex拿数据
      ...mapState(['tabslist','isCollapse'])
    },
    methods: {
      //点击展开收起方法
      ...mapMutations(['SetisCollapse'])
    },
  };
  </script>
  <style lang="scss" scoped>
  .heades{
    display: flex;
    align-items: center;
    height: 60px;
    border-bottom: 1px solid #eee;
  }
  .icons{
    font-size: 28px;
    cursor: pointer;
    margin-right: 20px;
  }
  .heades ::v-deep .el-breadcrumb__item{
    .el-breadcrumb__inner.is-link{
      font-weight: 400;
    }
  }
  .heades ::v-deep .el-breadcrumb__item:last-child{
    .el-breadcrumb__inner.is-link{
      font-weight: 500;
      color: #409EFF;
    }
  }
  </style>
  

2.3 公共的tag

<template>
  <div>
    <el-tag
    v-for="(item,index) in tabslist"
    :key="index"
    :closable="item.menuname !='首页' "
    :effect="$route.name === item.menuname ?'dark' :'plain' " 
    size="small" 
    style="margin-right: 10px;" 
    @close="handleClose(item,index)"
    @click="gotoLink(item)"
    >
    {{ item.menuname }}
  </el-tag>
  </div>
</template>

<script>
import { mapState,mapMutations } from 'vuex';
export default {
  name:'BreadcrumbTag',
  data() {
    return {
    }
  },
  computed:{
    ...mapState(['tabslist'])
  },
  mounted(){
     console.log(this.$route);
  },
  methods: {
    ...mapMutations(['ClearTag']),
    //点击跳转
    gotoLink(item){
       this.$router.push(item.url)
    },
    //点击删除
    handleClose(item,index){
        this.ClearTag(item)
        const length = this.tabslist.length
        //删除之后跳转到前一个页面
        if(item.menuname != this.$route.name){
            return
        }
        if(index == length){
            this.$router.push(this.tabslist[index -1].url)
        }else{
            this.$router.push(this.tabslist[index].url)
        }

    }
  },
}
</script>
<style lang='scss' scoped>
.el-tag{
  cursor: pointer;
}
</style>

3、vuex使用

页面新建store文件夹下创建index

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    defaultActive:sessionStorage.getItem("isactive") || '' ,//菜单高亮的值
    isCollapse:false, //菜单收起关闭
    tabslist:JSON.parse(sessionStorage.getItem("tabslist")) || [{
        menuid: 21,
        icon: 'el-icon-user-solid',
        menuname: '首页',
        url: "/IndexView",
        alwaysShow:true
      }] //面包屑数据
  },
  getters: {
  },
  mutations: {
    SetisCollapse(state){
       state.isCollapse =!state.isCollapse 
    },
    //修改面包屑数据
    SetMenu(state,value){
      //判断添加的数据是否存在
      if(value.menuname != '首页'){
        const index = state.tabslist.findIndex(item => item.menuname === value.menuname)
        //如果点击的菜单不存在 state.tabslist
        if(index === -1){
           state.tabslist.push(value)
           //增加完面包屑把数据存到本地做持久化储存
           sessionStorage.setItem("tabslist", JSON.stringify(state.tabslist))
        }
      }
    },
    //删除指定面包屑数据
    ClearTag(state,value){
      const index = state.tabslist.findIndex(item => item.menuname === value.menuname)
      state.tabslist.splice(index,1)
    },
    //修改菜单高亮的值
    SetDefaultActive(state,value){
      state.defaultActive = value
      sessionStorage.setItem('isactive', value);
    }
  },
  actions: {
  },
  modules: {
  }
})

4、整体使用

第一创建页面也就是我的Index.vue作为项目的主页面,然后把组件引入。App记得渲染路由数据。
App.vue内容


<template>
  <div id="app">
    <router-view />
  </div>
</template>

<script>
import { mapState } from 'vuex';
export default {
  name: '',
  data() {
    return {

    }
  },
  computed:{
   ...mapState(['tabslist'])
  },
  created() {
    //浏览器刷新重新获取面包屑数据
    window.addEventListener("beforeunload", () => {
      sessionStorage.setItem("tabslist", JSON.stringify(this.tabslist));
    });
  },
}
</script>
<style lang='scss'>
body {
  margin: 0;
  padding: 0;
}
</style>

index.vue内容,记得创建路由时要设置重定向到这里。

   path:'/',
   name:'',
   component:Index,
   redirect:'/IndexView', //重定向
<template>
    <div>
      <el-container>
        <el-aside width="auto">
          <LeftView></LeftView>
        </el-aside>
        <el-container>
          <el-header style="padding:0px;">
             <HeaderView></HeaderView>
          </el-header>
          <el-main style="padding:20px;">
              <BreadcrumbTag></BreadcrumbTag>
              <router-view></router-view>
          </el-main>
        </el-container>
      </el-container>
    </div>
 </template>
 <script>
import LeftView from '@/components/leftView.vue';
import HeaderView from '@/components/HeaderView.vue';
import BreadcrumbTag from '@/components/BreadcrumbTag.vue'
 export default {
    name: 'index',
    data() {
        return {};
    },
    components: { LeftView,HeaderView,BreadcrumbTag}
};
 </script>
 <style>
 </style>