基本思路:
父组件:
此页面基本是整个项目的最上层组件 包含左侧菜单+右侧上方页签+右侧下方内容区 主要需要设置个动态缓存
参考:vue keep-alive 缓存 不生效解决方案_keep-alive keepalive:true, 缓存不生效-CSDN博客
左侧菜单树:
1 接口接收当前用户允许查看的路由树并渲染上
2 点击对应导航时 将相关数据传到vuex里保存起来 一会儿页签里面用
右侧页签:
操作vuex的列表数据 左侧导航新增相关页签到vuex后 页签直接使用vuex的页签列表 会自动更新 页签组件里负责点击删除后删除相关页签
vuex里:
设置新增与删除页签列表相关代码就行
代码(未注释版 暂时没时间仔细梳理注释 有问题评论或者私聊我)
父组件
<template>
<div style="width:100%;height:100vh;display: flex;justify-content: flex-start;align-items: flex-start;" class="flex_SB_H">
<left_navigation style="width:20%;max-width: 30rem;"></left_navigation>
<article style="flex: 1;height: 100%; overflow: scroll;">
<top_subtab></top_subtab>
<keep-alive :max="40">
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</article>
</div>
</template>
<script>
import left_navigation from '@/components/layout/left_navigation.vue'; /*左侧导航动态版(接口请求)组件*/
import top_subtab from '@/components/layout/top_subtab.vue';/*右侧顶部页签缓存版组件*/
// import { } from 'element-ui';
export default ({
name:'layout',
components:{ left_navigation,top_subtab },
data:()=>{
return {
// state:store.state.keepAliveList
}
},
mounted(){
// console.log('32131',store,store.state.keepAliveList)
},
})
</script>
<style>
</style>
左侧菜单树
<template>
<div class="left_nav" style="overflow-y: scroll;height:100%;">
<el-menu style="height: 100%;" ref="layout"
active-text-color="#ffd04b"
background-color="#545c64"
class="el-menu-vertical-demo"
:default-active="menuActive"
text-color="#fff"
:unique-opened="fatherData.menu_uniqueOpened"
>
<div v-for="(item,index) in fatherData.list" :index="index">
<!-- 有子集菜单 -->
<template v-if="item.list">
<el-submenu :index="item.name" :disabled="item.disabled">
<template #title><el-icon><component :is="item.icon"></component></el-icon>{{ item.label }}</template>
<el-menu-item v-for="(item2) in item.list" :index="item2.name" :disabled="item2.disabled" @click="childClickFn">
<template #title><el-icon><component :is="item2.icon"></component></el-icon>{{ item2.label }}</template>
</el-menu-item>
</el-submenu>
</template>
<!-- 无子集菜单的渲染 -->
<template v-else>
<el-menu-item :index="item.name" :disabled="item.disabled" @click="childClickFn">
<template #title><el-icon><component :is="item.icon"></component></el-icon>{{ item.label }}</template>
</el-menu-item>
</template>
</div>
</el-menu>
</div>
</template>
<script>
import store from '@/store'
import { Menu,Submenu,MenuItem,Icon,} from 'element-ui';
import { arrayMethod } from '@/assets/public.js'
export default({
components:{
store,
[Menu.name]:Menu,
[Submenu.name]:Submenu,
[MenuItem.name]:MenuItem,
[Icon.name]:Icon,
arrayMethod,
},
data:()=>{
return {
fatherData:{
menu_uniqueOpened: false,/* 是否只保持一个子菜单的展开 */
list:[]
},
menuActive:store.state.menuActive
}
},
created() {
this.dataListFn()
},
methods:{
dataListFn(){
setTimeout(()=>{
this.fatherData = {
menu_uniqueOpened:false,
list: [
{label:'人物管理',icon:'Search',name:'person',
list:[
{label:'人物管理',icon:'Search',name:'renwu',},
{label:'种族管理',icon:'Search',name:'zhongzu',},
{label:'特性管理',icon:'Search',name:'dict',},
]
},
{label:'组件使用示例',icon:'Search',name:'examples',
list:[
{label:'table示例',icon:'Search',name:'et_table',},
{label:'et_form里的et_inputTableSelect示例',icon:'Search',name:'et_inputTableSelect',},
]
},
{label:'测试2-1',icon:'Female',name:'pageD',},
{label:'表格-动态增删列表项',icon:'Search',name:'table_userList', },
{label:'测试4',icon:'Female',name:'layouA_4',},
{label:'测试2',icon:'Search',name:'layouA_2', },
{label:'测试2',icon:'Search',name:'layouA_2', },
{label:'测试2',icon:'Search',name:'layouA_2', },
{label:'测试2',icon:'Search',name:'layouA_2', },
{label:'测试2',icon:'Search',name:'layouA_2', },
{label:'测试2',icon:'Search',name:'layouA_2', },
{label:'测试2',icon:'Search',name:'layouA_2', },
{label:'测试2',icon:'Search',name:'layouA_2', },
{label:'测试1',icon:'Search',name:'layouA',
list:[
{label:'测试1-1',icon:'Search',name:'pageA',},
{label:'测试1-2',icon:'Female',name:'pageB',}]
},
{label:'测试1',icon:'Search',name:'layouA',
list:[
{label:'测试1-1',icon:'Search',name:'pageA',},
{label:'测试1-2',icon:'Female',name:'pageB',}]
},
]
};
},2000)
},
childClickFn(event){
/* 添加keepAliveList数据到缓存 */
let state_status = true
store.state.keepAliveList.forEach((item)=>{
if(item === event.index){ state_status = false }
})
if(state_status){ store.dispatch('keepAliveList_add',event.index) }
/* 添加路由数据到二级页签 */
let status = true
store.state.subtab.forEach((item)=>{
if(item.routerName === event.index){ status = false }
})
if(status){
store.dispatch('subtab_add',{
name:event.index,
type:event.index ==='pageA' ? 'info':'success',
routerName:event.index,
path:arrayMethod.connectListFn(event.indexPath,{ start:'/',connect:'/' })
})
}
if(event.index !== this.$route.name){ this.$router.push({ name:event.index }) }
},
},
})
</script>
<style scoped>
/* 滚动条美化 */
.left_nav ::-webkit-scrollbar{ display:none }
/*html body ::-webkit-scrollbar-button{ display:none }
html body ::-webkit-scrollbar-track{ display:none }
::-webkit-scrollbar-track-piece{ display:none }
::-webkit-scrollbar-thumb{ display:none }
::-webkit-scrollbar-corner{ display:none }
::-webkit-resizer{ display:none }*/
</style>
右侧页签
<template>
<div class="flex_S_C">
<div v-if="state.length > 0">
<el-tag
v-for="tag in state"
:key="tag.name"
:class="abcdFn(tag)"
closable
:type="tag.type"
@click="tagClickFn(tag)"
@close="subtab_delete(tag)"
>
{{ tag.name }}
</el-tag>
</div>
</div>
</template>
<script>
import { toRefs,defineComponent,ref,reactive} from 'vue';
import { useRouter } from 'vue-router'
import { useStore } from "vuex"
import store from '@/store'
import { Tag } from 'element-ui';
export default({
components:{
store,
[Tag.name]:Tag,
},
props: {},
data:()=>{
return {
state:store.state.subtab,
}
},
mounted(){
},
methods:{
abcdFn(tag){
return tag.name === this.$route.name ? 'class1':'class2'
},
subtab_delete(tag){
store.dispatch('subtab_delete',tag.routerName)
store.dispatch('keepAliveList_delete',tag.routerName)
if(tag.name === this.$route.name){ this.$router.go(-1); }
},
tagClickFn(tag){
if(tag.name !== this.$route.name){ this.$router.push({ path:tag.path }) }
},
},
})
</script>
<style scoped>
.class1{ color: red; }
.class2{ color: blueviolet;}
</style>
vuex:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state(){
return{
keepAliveList:[], /*页签功能配套的缓存变化集*/
subtab:[],/*三级页签集*/
menuActive:'pageB',/*默认选中的menu标签*/
}
},
mutations:{
subtab_add(state,args){
/* @ts-ignore*/
state.subtab.push(args)
},
/* subtab_update_type(state,args){
/!* @ts-ignore*!/
state.subtab.forEach((item,index)=>{
item.type = 'warning'
})
},*/
subtab_delete(state,routerName){
/* @ts-ignore*/ /*注: forEach会改变原有长度 导致少循环一次*/
state.subtab.forEach((item,index)=>{
if(item.routerName === routerName){
/* @ts-ignore*/
state.subtab.splice(index,1)
}
})
},
/* 路由缓存的功能设置 */
keepAliveList_add(state,args){
/* @ts-ignore*/
state.keepAliveList.push(args)
},
keepAliveList_delete(state,name){
/* @ts-ignore*/ /*注: forEach会改变原有长度 导致少循环一次*/
state.keepAliveList.forEach((item,index)=>{
if(item === name){
/* @ts-ignore*/
state.keepAliveList.splice(index,1)
}
})
}
},
actions:{
subtab_add(context,args){ context.commit('subtab_add',args) },
subtab_delete(context,routerName){
if( routerName ){ context.commit('subtab_delete',routerName) }
else { console.log('vuex删除路由功能缺少routerName属性') }
},
/* 路由缓存的功能设置 */
keepAliveList_add(context,args){ context.commit('keepAliveList_add',args) },
keepAliveList_delete(context,name){
if( name ){ context.commit('keepAliveList_delete',name) }
else { console.log('vuex删除路由功能2缺少name属性') }
},
},
getters:{
/* doubleNum(state){
// @ts-ignore
return state.num*2
}*/
},
modules:{}
})