vue+element-ui实现el-tab标签的动态面包屑
以下内容仅供学习使用
前言: 下面是最终实现的效果图
- 首先在router.js里面配置meta:
- 封装一个el-tab面包屑的子组件 通过v-for指令和tags数组中的数据进行渲染
<template>
<div>
<el-tag :key="index" v-for="(tag, index) in tags" :closable="tag.name !== 'users'" :disable-transitions="false" @close="handleClose(tag, index)" @click="changeMenu(tag)" :effect="isActive(tag) ? 'dark' : 'plain'">
{{ tag.label }}
</el-tag>
</div>
</template>
- 组件实例的data中定义了一个tags数组,用于存储标签页的数据
data () {
return {
tags: [],
};
},
- watch监听$route变量的变化来添加新的标签页,同时使用localStorage保存了当前打开的标签页信息,以便刷新页面后仍能保持原来的状态。
watch: {
tags: {
handler (newTags) {
localStorage.setItem('tags', JSON.stringify(newTags));
},
//deep: true 表示在监听属性变化时,将会递归遍历所有嵌套的子属性,而不仅仅是监听对象的直接属性变化。
deep: true,(可以不用)
},
$route (to) {
if (to.matched.some((record) => record.meta.requiresAuth)) {
// 如果需要验证身份,则在异步组件加载完成后再添加标签
const tag = { name: to.name, label: to.meta.title };
About().then(() => {
const isTagExist = this.tags.some((t) => t.name === tag.name);
if (!isTagExist) {
this.tags.push(tag);
}
});
} else {
// 否则直接添加标签
const tag = { name: to.name, label: to.meta.title };
const isTagExist = this.tags.some((t) => t.name === tag.name);
if (!isTagExist) {
this.tags.push(tag);
}
}
},
},
- mounted从本地存储中获取之前保存的标签数组,如果有值就将其赋给组件的tags属性,否则就添加一个默认的首页标签。
mounted () {
//从本地存储中获取tags,如果不存在则返回一个空数组[]。
const tags = JSON.parse(localStorage.getItem('tags') || '[]');
//判断获取到的tags数组的长度,如果大于0,则将它赋值给组件的tags属性。
if (tags.length) {
this.tags = tags;
} else {
//如果获取到的tags数组的长度等于0,则添加一个默认的首页标签,包括其路径、名称和标签文字,然后将它添加到tags数组中。
this.tags.push({ path: '/users', name: 'users', label: '首页' });
}
},
- methods
methods: {
//handleClose(tag, index):处理关闭标签的逻辑。如果标签不是首页('users'),则从标签数组中移除该标签。
//如果关闭的标签不是当前路由的话,则不进行路由跳转。如果关闭的标签是最右边的标签,则往左边跳转一个,否则往右边跳转。
handleClose (tag, index) {
// 处理关闭标签的逻辑
if (tag.name !== 'users') {
this.tags.splice(index, 1);
}
// 如果关闭的标签不是当前路由的话,就不跳转
if (tag.name !== this.$route.name) {
return;
}
const length = this.tags.length;
// 关闭的标签是最右边的话,往左边跳转一个
if (index === length) {
this.$router.push({ name: this.tags[index - 1].name });
} else {
// 否则往右边跳转
this.$router.push({ name: this.tags[index].name });
}
},
//changeMenu(tag):处理点击标签的逻辑。如果点击的是首页标签,则跳转到首页。
//如果点击的不是首页标签,则根据标签的 name 属性获取该标签对应的路由路径,并跳转到该路径。如果点击的标签已经是当前路由的话,则不进行跳转。
changeMenu (tag) {
// 跳转首页
if (tag.name === 'users') {
if (this.$route.path !== '/users') {
this.$router.push({ path: '/users' });
}
return;
}
// 处理点击标签的逻辑
if (tag.name !== 'users') {
const toPath = this.$router.resolve({ name: tag.name }).href;
if (this.$route.path !== toPath) {
this.$router.push({ name: tag.name });
}
}
},
//isActive(tag):判断当前标签是否为当前路由。如果是,则返回 true,否则返回 false。
isActive (tag) {
// 判断当前标签是否为当前路由
return tag.name === this.$route.name;
},
},
- 样式
<style lang="scss" scoped>
.el-tag--dark,
.el-tag--plain {
cursor: pointer;
margin-left: 10px;
}
.el-tag:nth-child(2n) {
cursor: pointer;
margin-left: 10px;
}
</style>
- 在点击退出的时候清除localStorage.clear('tags')
localStorage.clear('tags')
-
调用封装好的子组件
import MyTag from '../../components/MyTag.vue'
components: {MyTag},
-
效果图
-
完整的子组件代码,需要自取
<template>
<div>
<el-tag :key="index" v-for="(tag, index) in tags" :closable="tag.name !== 'users'" :disable-transitions="false" @close="handleClose(tag, index)" @click="changeMenu(tag)" :effect="isActive(tag) ? 'dark' : 'plain'">
{{ tag.label }}
</el-tag>
</div>
</template>
<script>
export default {
data () {
return {
tags: [],
};
},
watch: {
tags: {
handler (newTags) {
localStorage.setItem('tags', JSON.stringify(newTags));
},
deep: true,
},
$route (to) {
if (to.matched.some((record) => record.meta.requiresAuth)) {
// 如果需要验证身份,则在异步组件加载完成后再添加标签
const tag = { name: to.name, label: to.meta.title };
About().then(() => {
const isTagExist = this.tags.some((t) => t.name === tag.name);
if (!isTagExist) {
this.tags.push(tag);
}
});
} else {
// 否则直接添加标签
const tag = { name: to.name, label: to.meta.title };
const isTagExist = this.tags.some((t) => t.name === tag.name);
if (!isTagExist) {
this.tags.push(tag);
}
}
},
},
mounted () {
const tags = JSON.parse(localStorage.getItem('tags') || '[]');
if (tags.length) {
this.tags = tags;
} else {
this.tags.push({ path: '/users', name: 'users', label: '首页' });
}
},
methods: {
handleClose (tag, index) {
// 处理关闭标签的逻辑
if (tag.name !== 'users') {
this.tags.splice(index, 1);
}
// 如果关闭的标签不是当前路由的话,就不跳转
if (tag.name !== this.$route.name) {
return;
}
const length = this.tags.length;
// 关闭的标签是最右边的话,往左边跳转一个
if (index === length) {
this.$router.push({ name: this.tags[index - 1].name });
} else {
// 否则往右边跳转
this.$router.push({ name: this.tags[index].name });
}
},
changeMenu (tag) {
// 跳转首页
if (tag.name === 'users') {
if (this.$route.path !== '/users') {
this.$router.push({ path: '/users' });
}
return;
}
// 处理点击标签的逻辑
if (tag.name !== 'users') {
const toPath = this.$router.resolve({ name: tag.name }).href;
if (this.$route.path !== toPath) {
this.$router.push({ name: tag.name });
}
}
},
isActive (tag) {
// 判断当前标签是否为当前路由
return tag.name === this.$route.name;
},
},
};
</script>
<style lang="scss" scoped>
.el-tag--dark,
.el-tag--plain {
cursor: pointer;
margin-left: 10px;
}
.el-tag:nth-child(2n) {
cursor: pointer;
margin-left: 10px;
}
</style>