1:封装显示tab组件
TabNav/index
<template>
<div class="tab-nav">
<ul class="tabs">
<li
v-for="item in visitedViews"
:key="item.path"
:class="{ active: item.path === activeTab }"
@click="handleTabClick(item)"
>
<span>{{ item.meta.title }}</span>
<i class="el-icon-close" @click.stop="removeTab(item.path)" />
</li>
</ul>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
data() {
return {
activeTab: ''
}
},
computed: {
...mapState({
visitedViews: state => state.tagsView.visitedViews // 显式写法更安全
})
},
watch: {
$route(newVal) {
const exists = this.visitedViews.some(v => v.path === newVal.path)
if (!exists) {
this.addView(newVal)
}
this.activeTab = newVal.path
}
},
created() {
console.log(this.$store.state.tagsView.visitedViews, 'this.$store.state.tagsView.visitedViews')
// this.activeTab = this.$route.path
this.$nextTick(() => {
const route = this.$route
if (route.meta && route.meta.title) {
console.log('准备添加 tab:', route)
this.addView(this.$route)
this.activeTab = route.path
} else {
console.warn('当前路由缺少 meta.title,无法添加 tab')
}
})
},
methods: {
addView(view) {
this.$store.dispatch('tagsView/addView', view)
},
removeTab(targetName) {
const tabs = [...this.visitedViews]
const curIndex = tabs.findIndex(v => v.path === targetName)
if (curIndex === -1) return
// 删除前先找到下一个 tab
const nextTab = tabs[curIndex + 1] || tabs[curIndex - 1]
console.log('点击删除:', targetName)
// 分发删除 action
this.$store.dispatch('tagsView/delView', { path: targetName }).then(({ visitedViews }) => {
console.log('【组件】收到最新 visitedViews:', visitedViews)
// 强制刷新缓存 key
this.$root.$emit('update-cached-views')
// 如果当前 tab 被删除,则跳转
if (this.activeTab === targetName && nextTab) {
this.activeTab = nextTab.path
this.$router.push(nextTab.path)
} else if (visitedViews.length === 0) {
this.$router.push('/')
}
})
},
toLastView(visitedViews, view) {
console.log('最新的 visitedViews:', visitedViews)
// 先确保 visitedViews 是数组
if (!Array.isArray(visitedViews)) {
console.warn('visitedViews 不是数组', visitedViews)
this.$router.push('/')
return
}
const latestView = visitedViews.slice(-1)[0]
if (latestView) {
// 还有 tab 存在,跳转到最后一个
this.activeTab = latestView.path
this.$router.push(latestView.path)
} else {
// 没有 tab 了,跳转默认页面
this.activeTab = '/' // 或者 '/'
this.$router.push(this.activeTab)
}
},
handleTabClick(item) {
this.activeTab = item.path
this.$router.push(item.path)
}
}
}
</script>
<style scoped>.tab-nav {
margin-bottom: 10px;
}
.tabs {
display: flex;
border-bottom: 1px solid #e4e7ed;
list-style: none;
padding: 0;
margin: 10px 20px;
}
.tabs li {
border-radius: 4px 4px 0 0;
position: relative;
background-color: #fff;
border: 1px solid #e4e7ed;
border-bottom-color: transparent;
padding: 0 10px;
font-size: 14px;
cursor: pointer;
transition: all 0.3s;
margin-right: 6px;
}
.tabs li.active {
color: #409eff;
font-weight: 700;
}
.tabs li:hover {
color: #409eff;
background-color: #f5f7fa;
border-color: #c0c4cc;
}
.tabs li .el-icon-close {
display: none;
color: #909399;
font-size: 12px;
vertical-align: middle;
margin-left: 5px;
}
.tabs li:hover .el-icon-close {
display: inline-block;
}
.tabs li.active .el-icon-close {
color: #606266;
}
</style>
2.vue x 存储
store/tagsView.js
const state = {
visitedViews: [],
cachedViews: []
}
const mutations = {
ADD_VISITED_VIEW: (state, view) => {
if (!view.meta || !view.meta.title) return
console.log('【mutation】正在添加 tab:', view)
if (state.visitedViews.some(v => v.path === view.path)) return
state.visitedViews.push({
...view,
title: view.meta.title
})
// 如果该页面允许缓存,则加入 cachedViews
if (view.meta.keepAlive && !state.cachedViews.includes(view.name)) {
state.cachedViews.push(view.name)
}
},
DEL_VISITED_VIEW(state, view) {
console.log(state.cachedViews, '删除前')
const index = state.visitedViews.findIndex(v => v.path === view.path)
if (index > -1) {
console.log('正在删除:', view)
const removedView = state.visitedViews[index]
state.visitedViews.splice(index, 1)
// 只有被删除的 tab 才清除缓存
const nameIndex = state.cachedViews.indexOf(removedView.name)
if (nameIndex > -1) {
state.cachedViews.splice(nameIndex, 1)
}
}
console.log(state.cachedViews, '删除后')
}
}
const actions = {
addView({ commit }, view) {
console.log('【action】准备添加 tab:', view)
commit('ADD_VISITED_VIEW', view)
},
delView({ commit }, view) {
console.log('【action】准备删除 tab:', view)
return new Promise((resolve) => {
commit('DEL_VISITED_VIEW', view)
resolve({ visitedViews: [...state.visitedViews] }) // 返回拷贝后的数组
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
3:APP.vue
<template>
<div id="app">
<!-- 缓存组件 -->
<keep-alive>
<router-view
v-if="$route.meta.keepAlive"
:key="$route.fullPath"
/>
</keep-alive>
<!-- 不缓存的组件 -->
<router-view v-if="isRouterAlive && !$route.meta.keepAlive" :key="$route.fullPath" />
</div>
</template>
4:路由设置
// keepAlive:true缓存的页面 keepAlive:false缓存的页面
{
path: 'home',
name: 'home',
component: () => import('@/views/home/index'),
meta: { title: '首页', icon: 'dashboard', keepAlive: true }
},
{
path: 'houseStaus',
name: 'houseStaus',
component: () => import('@/views/houseStaus/index'),
meta: { title: '房态', icon: 'dashboard', keepAlive: false }
},