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>