1.Test.vue代码
1.1
<template>
<div>
<div>
<div class="m-tab-container">
<div>
<router-link
class="m-tab-item"
:to="item"
:key="item.path"
:class="isActive(item) ? 'active' : ''"
v-for="item in Array.from(visitedMenus)"
>
{{ item.titleText }}
<span
class="m-icon-close el-icon-close"
@click.prevent.stop="closeSelectedMenu(item)"
></span>
</router-link>
</div>
</div>
</div>
</div>
</template>
1.2js
<script>
export default {
name: "",
data() {
return {
visitedMenus: [],
};
},
created() {
this.visitedMenus = this.$store.state.visitedMenus;
},
methods: {
isActive(route) {
// 判断当前地址栏地址,并激活样式
return route.path == this.$route.path;
},
closeSelectedMenu(page) {
this.$store.dispatch({ type: "closeMenu", page }).then((pages) => {
let deletekey = pages.deletekey;
let visitedMenus = pages.visitedMenus;
if (this.isActive(page)) {
// slice() 方法可从已有的数组中返回选定的元素。
// slice() 方法可提取字符串的某个部分,并以新的字符串返回被提取的部分。
// 注意: slice() 方法不会改变原始数组。
let latestPage;
if (visitedMenus.length != 0) {
// 删除最后一个,默认前一个。不是最后一个,默认后一个
if (deletekey == visitedMenus.length) {
latestPage = visitedMenus.slice(-1)[0];
} else {
latestPage = visitedMenus.slice(deletekey, deletekey + 1)[0];
}
this.$router.push(latestPage);
} else {
// 已删除完,默认登陆成功后的页面。由于动态设置的接口返回导航数据,没有中文名称,不做限制
this.$message.warning("请至少保留一个!");
}
}
});
},
},
};
</script>
1.3scss
<style rel="stylesheet/scss" lang="scss">
.m-tab-container {
background: #fff;
height: 45px;
border-top: 1px solid #d8dce5;
border-bottom: 1px solid #d8dce5;
overflow: hidden;
white-space: nowrap;
& > div {
overflow: auto;
overflow-y: hidden;
}
a.m-tab-item {
text-decoration: none;
}
.m-tab-item {
display: inline-block;
position: relative;
height: 32px;
line-height: 32px;
border: 1px solid #d8dce5;
color: #495060;
background: #fff;
padding: 0 8px;
font-size: 14px;
margin-left: 5px;
margin-top: 6px;
&:first-of-type {
margin-left: 18px;
}
&.active {
background-color: #1c5ffd;
color: #fff;
border-color: #1c5ffd;
&::before {
content: "";
background: #fff;
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
position: relative;
margin-right: 2px;
}
}
.m-icon-close {
width: 20px;
height: 20px;
vertical-align: 2px;
border-radius: 50%;
text-align: center;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
transform-origin: 100% 50%;
&:before {
display: inline-block;
vertical-align: -3px;
}
&:hover {
background-color: #b4bccc;
color: #fff;
}
}
}
}
</style>
2.路由导航页面里面举例。可根据需要修改格式
{
path: ‘test’,
name: ‘T’,
component: Test,
meta: { titleText: ‘测试’ }
}
3.vuex文件。该文件需要在main.js中引用,才能生效
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
visitedMenus: []
},
mutations: {
setVisitedMenus(state, value) {
state.visitedMenus = value
},
addRouteMenus(state, routeMenu) {
if (routeMenu.route.path != '/login' && routeMenu.route.path != '/register') {
let hasMenu = state.visitedMenus.some(item => item.path == routeMenu.route.path)//判断此路由是否在已存储的路由数据中
if (!hasMenu) {
// 如果存储的路由数据中,没有此路由就添加
state.visitedMenus.push(Object.assign({}, {
path: routeMenu.route.path,
titleText: routeMenu.route.meta.titleText,
query: routeMenu.route.query
}))
}
}
},
},
actions: {
closeMenu({ commit, state }, routeMenu) {
let deletekey;
for (const [key, item] of state.visitedMenus.entries()) {
if (item.path == routeMenu.page.path) {
deletekey = key;
state.visitedMenus.splice(key, 1);
break;
}
}
return Promise.resolve({
visitedMenus: state.visitedMenus,
deletekey: deletekey
})
}
}
})
export default store
4.App.vue
<script>
export default {
name: "",
data() {
return {};
},
created() {
// 防止页面刷新,vuex里面的菜单数据消失
if (sessionStorage.getItem("visitedMenus")) {
this.$store.commit(
"setVisitedMenus",
JSON.parse(sessionStorage.getItem("visitedMenus"))
);
}
window.addEventListener("beforeunload", () => {
sessionStorage.setItem(
"visitedMenus",
JSON.stringify(this.$store.state.visitedMenus)
);
});
},
watch: {
$route() {
this.addRouteMenu();
},
},
methods: {
addRouteMenu() {
const route = this.$route;
this.$store.commit({
type: "addRouteMenus",
route,
});
},
},
};
</script>