这节课完成面包屑和tag的布局,并且与左侧菜单联系,涉及组件间通信。
1.提前准备
首先我们先完成每个页面的路由,之前已经有home页面和user页面,缺少mail页面和其它选项下的page1和page2页面。在view文件夹下新建mail文件夹,新建index.vue,填充user页面的内容即可。在view下新建other文件夹,新建pageOne.vue和pageTwo.vue,页面内容简单填充即可。三个页面都要更改name属性,后面会用到,首字母大写。
示例:
配置路由:在路由主文件添加路由,路由位置在CommonAside的menu数组中保持偶一致。这里的name属性是小写,与上面不相同。
{
path:'/mail',
name:'mail',
component:()=>import('../view/mail')
},
{
path:'/page1',
name:'page1',
component:()=>import('../view/other/pageOne.vue')
},
{
path:'/page2',
name:'page2',
component:()=>import('../view/other/pageTwo.vue')
}
测试发现商品管理可以正常跳转,但是其他的二级菜单不能跳转,原因是没有设置点击事件。为左侧导航栏二级菜单添加点击事件。为el-menu-item添加点击事件。
<el-menu-item-group
v-for="(subItem,subIndex) in item.children"
:key="subItem.path">
<el-menu-item
@click="clickMenu(subItem)"
:index="subIndex.toString()">{{ subItem.label }}</el-menu-item>
</el-menu-item-group>
2.面包屑功能
面包屑有数据记忆存储功能,每次点击导航栏后后记录选中的导航栏并列出。这里需要用到Vuex.Store,我们前面左侧导航栏的收起与展开同样使用到Vuex.Store,store可以看做是容器,里面有四个值,由State、Getters、Mutation、Actions这四种组成。在store下的tab.js文件,默认数据定义在state中,我们定义为一个数组tabsList,数组里有多个对象,但这里知识默认对象,对象里有四个属性,path、name、label、icon。currentMenu为一个临时变量,存储点击的tab。
state:{
isCollapse:false,
tabsList:[
{
path:'/',
name:'home',
label:'首页',
icon:'home'
}
],
currentMenu:null
},
改变tabsList里的内容要用到Vuex.store的mutations。改变state数据的发都会以函数的形式写在mutations中,之后在页面调用即可。selectMenu函数接收两个参数,state和val,此时需要进行判断,当前点击的导航栏是不是首页,用name属性判断,判断传入的item.name是否等于‘home’,如果不是home,需要赋值val给currentMenu,这样currentMenu就会不断累加,最多为五个(首页、用户、商品、其他1、其他2)。此处还需要一个判断,判断当前点击的导航栏是是不是刚刚已经点击过来,点击过了不再进行累加,而是将面包屑跳转到点击过的那一个。这里用到findIndex()方法。判断item.name里是否有val.name。 有的话为1,无的话为-1,无的话那么使用push()方法将val累加到currentMenu。如果点击是‘home’的情况,currentMenu置空。
selectMenu(state,val){
if(val.name !== 'home'){
state.currentMenu = val
const result = state.tabsList.findIndex(item => item.name === val.name)
if(result === -1){
state.tabsList.push(val)
}
}else{
state.currentMenu = null
}
},
在CommonAside中调用currentMenu,并传入item,item实际就menu数组中的任一对象。
clickMenu(item){
this.$router.push({
name:item.name
})
this.$store.commit('selectMenu',item)
更改CommonHeader.vue,使用element ui的面包屑元素。
//element ui官方案例
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item><a href="/">活动管理</a></el-breadcrumb-item>
<el-breadcrumb-item>活动列表</el-breadcrumb-item>
<el-breadcrumb-item>活动详情</el-breadcrumb-item>
</el-breadcrumb>
我们需要进行数据渲染,渲染成动态数据,v-for循环tags,:key使用item.path识别。{{}}表示动态渲染数据。
这里数据比较比较长,item--tags--tabsList--menu。tabsList里的数据也是menu动态加载进去的。
<el-breadcrumb separator="/">
<el-breadcrumb-item v-for="item in tags" :key="item.path" :to="{ path: item.path }">{{item.label}}</el-breadcrumb-item>
</el-breadcrumb>
引入mapState,这个是vuex的辅助函数。
import { mapState } from 'vuex'
在computed属性中进行注入,这里起了一个别名tags。
computed:{
...mapState({
tags:state => state.tab.tabsList
})
}
效果:这里样式有问题,不过影响不大,后期可以自行更改。
页面会根据面包屑进行,路由也会自行更改。
3.tag区域
这个功能其实类似于面包屑,只是多了删除功能。使用element ui的tag组件。tag组件的close属性表示组件是否可以关闭。tag组件有两个触发事件click、close。分别是点击tag和删除tag。
tag组件为公共组件,在components下新建CommonTag.vue。v-for遍历tags,这里注意,首页tag是不能删除的,closable属性表示有删除按钮,后面可以附带条件。effect属性为主题(样式),表示为高亮显示。判断方法是根据路由地址和当前tag的名称是否一致,注意是route(不是router)一致则为dark(高亮),不一致为plain(白色)。添加改变tag事件和删除tag事件。这里用模块化,用函数实现。
<template>
<div class="tabs">
<el-tag
v-for="(tag,index) in tags"
:key="tag.name"
:closable="tag.name !== 'home'"
:effect="$route.name === tag.name ? 'dark' : 'plain'"
@click="changeMenu(tag)"
@close="handleClose(tag,index)"
size="small">
{{ tag.label }}
</el-tag>
</div>
</template>
渲染tag,同样需要用到tabsList,引入mapState,在computed属性中注入,注入为三个点(...),这里同样将tabsList赋值给tags,两个页面的tags互不干扰。
<script>
import { mapState,mapMutations } from 'vuex'
export default{
name:'CommonTag',
data(){
return{
}
},
computed:{
...mapState({
tags:state => state.tab.tabsList
}) ,
},
methods:{
...mapMutations({
close:'closeTag'
}),
changeMenu()
handleClose()
},
}
</script>
在mian.vue引入组件
import CommonTag from "../src/components/CommonTag.vue";
export default {
name: "Home",
components: {
CommonTag
},
注意在el-header和el-main之间,因为这个组件并不属于header也不属于main,所以在中间。
<el-header>
<common-header></common-header>
</el-header>
<common-tag></common-tag>
<el-main>
<router-view></router-view>
</el-main>
覆盖样式
<style lang="less" scoped>
.tabs{
padding: 20px;
.el-tag{
margin-right: 15px;
cursor: pointer;
}
}
</style>
接下来完成点击事件个删除事件(重难点!!!),点击就是路由切换。methods里更改函数。
changeMenu(item){
this.$router.push({ name: item.name })
console.log(name)
删除tag:
删除tag也会删除对应的面包屑,面包屑对应的是state.tabsList。首先拿到tag的总长度,总长度减1就是我们当前选中的tag,赋值给length,判断length和index的长度进行比较,如果这两个值相等,那么就表示此时我们选中的tag是tag中的最后一项。首先判断tag.name与当前的路由是否一致,如果不一致,不需要额外操作,直接return,即高亮在最后一个tag,删除的tag是非选中的tag,所以无需操作。
如果当前删除的tag是最后一个tag,那么高亮部分一个向左移动一格。如果当前菜单高亮才当在中间,并且你删除当前的菜单时,高亮显示向右移动一格。
handleClose(tag,index){
this.close(tag)
const length = this.tags.length - 1
if(tag.name !== this.$route.name){
return;
}
//高亮向左移动,这里视频代码错误,应该是上面不减1,下面减1.
if(index === length){
this.$router.push({
name: this.tags[index].name
})
}else{
this.$router.push({
name: this.tags[index - 1].name
})
}
}
此时需要删除state下的tabsList,修改state必须使用mutations。因此需要在mutations中声明closeTag。
closeTag(state,val){
const result = state.tabsList.findIndex(item => item.name === val.name)
state.tabsList.splice(result,1)
}
调用mutations
import { mapState,mapMutations } from 'vuex'
...mapMutations({
close:'closeTag'
}),
总体CommonTag的script代码。
<script>
import { mapState,mapMutations } from 'vuex'
export default{
name:'CommonTag',
data(){
return{
}
},
computed:{
...mapState({
tags:state => state.tab.tabsList
}) ,
},
methods:{
...mapMutations({
close:'closeTag'
}),
changeMenu(item){
this.$router.push({ name: item.name })
console.log(name)
},handleClose(tag,index){
this.close(tag)
const length = this.tags.length - 1
if(tag.name !== this.$route.name){
return;
}
//高亮向左移动,这里视频代码错误,应该是上面不减1,下面减1.
if(index === length){
this.$router.push({
name: this.tags[index].name
})
}else{
this.$router.push({
name: this.tags[index - 1].name
})
}
},
}
}
</script>