作者 RuoYi

优化多级菜单之间切换无法缓存的问题

1 -<!-- @author ruoyi 20201128 支持三级以上菜单缓存 -->  
2 <template> 1 <template>
3 <section class="app-main"> 2 <section class="app-main">
4 <transition name="fade-transform" mode="out-in"> 3 <transition name="fade-transform" mode="out-in">
5 - <keep-alive :max="20" :exclude="notCacheName"> 4 + <keep-alive :include="cachedViews">
6 <router-view :key="key" /> 5 <router-view :key="key" />
7 </keep-alive> 6 </keep-alive>
8 </transition> 7 </transition>
@@ -10,119 +9,17 @@ @@ -10,119 +9,17 @@
10 </template> 9 </template>
11 10
12 <script> 11 <script>
13 -import Global from "@/layout/components/global.js";  
14 -  
15 export default { 12 export default {
16 name: 'AppMain', 13 name: 'AppMain',
17 computed: { 14 computed: {
18 - notCacheName() {  
19 - var visitedViews = this.$store.state.tagsView.visitedViews;  
20 - var noCacheViews = [];  
21 - Object.keys(visitedViews).some((index) => {  
22 - if (visitedViews[index].meta.noCache) {  
23 - noCacheViews.push(visitedViews[index].name);  
24 - }  
25 - });  
26 - return noCacheViews; 15 + cachedViews() {
  16 + return this.$store.state.tagsView.cachedViews
27 }, 17 },
28 key() { 18 key() {
29 - return this.$route.path;  
30 - },  
31 - },  
32 - mounted() {  
33 - // 关闭标签触发  
34 - Global.$on("removeCache", (name, view) => {  
35 - this.removeCache(name, view);  
36 - });  
37 - },  
38 - methods: {  
39 - // 获取有keep-alive子节点的Vnode  
40 - getVnode() {  
41 - // 判断子集非空  
42 - if (this.$children.length == 0) return false;  
43 - let vnode;  
44 - for (let item of this.$children) {  
45 - // 如果data中有key则代表找到了keep-alive下面的子集,这个key就是router-view上的key  
46 - if (item.$vnode.data.key) {  
47 - vnode = item.$vnode;  
48 - break; 19 + return this.$route.path
49 } 20 }
50 } 21 }
51 - return vnode ? vnode : false;  
52 - },  
53 - // 移除keep-alive缓存  
54 - removeCache(name, view = {}) {  
55 - let vnode = this.getVnode();  
56 - if (!vnode) return false;  
57 - let componentInstance = vnode.parent.componentInstance;  
58 - // 这个key是用来获取前缀用来后面正则匹配用的  
59 - let keyStart = vnode.key.split("/")[0];  
60 - let thisKey = `${keyStart}${view.fullPath}`;  
61 - let regKey = `${keyStart}${view.path}`;  
62 -  
63 - this[name]({ componentInstance, thisKey, regKey });  
64 - },  
65 - // 移除其他  
66 - closeOthersTags({ componentInstance, thisKey }) {  
67 - Object.keys(componentInstance.cache).forEach((key, index) => {  
68 - if (key != thisKey) {  
69 - // 销毁实例(这里存在多个key指向一个缓存的情况可能前面一个已经清除掉了所有要加判断)  
70 - if (componentInstance.cache[key]) {  
71 - componentInstance.cache[key].componentInstance.$destroy();  
72 - }  
73 - // 删除缓存  
74 - delete componentInstance.cache[key];  
75 - // 移除key中对应的key  
76 - componentInstance.keys.splice(index, 1);  
77 - }  
78 - });  
79 - },  
80 - // 移除所有缓存  
81 - closeAllTags({ componentInstance }) {  
82 - // 销毁实例  
83 - Object.keys(componentInstance.cache).forEach((key) => {  
84 - if (componentInstance.cache[key]) {  
85 - componentInstance.cache[key].componentInstance.$destroy();  
86 - }  
87 - });  
88 - // 删除缓存  
89 - componentInstance.cache = {};  
90 - // 移除key中对应的key  
91 - componentInstance.keys = [];  
92 - },  
93 - // 移除单个缓存  
94 - closeSelectedTag({ componentInstance, regKey }) {  
95 - let reg = new RegExp(`^${regKey}`);  
96 - Object.keys(componentInstance.cache).forEach((key, i) => {  
97 - if (reg.test(key)) {  
98 - // 销毁实例  
99 - if (componentInstance.cache[key]) {  
100 - componentInstance.cache[key].componentInstance.$destroy();  
101 - }  
102 - // 删除缓存  
103 - delete componentInstance.cache[key];  
104 - // 移除key中对应的key  
105 - componentInstance.keys.splice(i, 1);  
106 - }  
107 - });  
108 - },  
109 - // 刷新单个缓存  
110 - refreshSelectedTag({ componentInstance, thisKey }) {  
111 - Object.keys(componentInstance.cache).forEach((key, index) => {  
112 - if (null != thisKey && key.replace("/redirect", "") == thisKey) {  
113 - // 1 销毁实例(这里存在多个key指向一个缓存的情况可能前面一个已经清除掉了所有要加判断)  
114 - if (componentInstance.cache[key]) {  
115 - componentInstance.cache[key].componentInstance.$destroy();  
116 - }  
117 - // 2 删除缓存  
118 - delete componentInstance.cache[key];  
119 - // 3 移除key中对应的key  
120 - componentInstance.keys.splice(index, 1);  
121 - }  
122 - });  
123 - },  
124 - },  
125 -}; 22 +}
126 </script> 23 </script>
127 24
128 <style lang="scss" scoped> 25 <style lang="scss" scoped>
@@ -134,7 +31,7 @@ export default { @@ -134,7 +31,7 @@ export default {
134 overflow: hidden; 31 overflow: hidden;
135 } 32 }
136 33
137 -.fixed-header + .app-main { 34 +.fixed-header+.app-main {
138 padding-top: 50px; 35 padding-top: 50px;
139 } 36 }
140 37
@@ -144,7 +41,7 @@ export default { @@ -144,7 +41,7 @@ export default {
144 min-height: calc(100vh - 84px); 41 min-height: calc(100vh - 84px);
145 } 42 }
146 43
147 - .fixed-header + .app-main { 44 + .fixed-header+.app-main {
148 padding-top: 84px; 45 padding-top: 84px;
149 } 46 }
150 } 47 }
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 mode="vertical" 13 mode="vertical"
14 > 14 >
15 <sidebar-item 15 <sidebar-item
16 - v-for="(route, index) in permission_routes" 16 + v-for="(route, index) in sidebarRouters"
17 :key="route.path + index" 17 :key="route.path + index"
18 :item="route" 18 :item="route"
19 :base-path="route.path" 19 :base-path="route.path"
@@ -33,7 +33,7 @@ export default { @@ -33,7 +33,7 @@ export default {
33 components: { SidebarItem, Logo }, 33 components: { SidebarItem, Logo },
34 computed: { 34 computed: {
35 ...mapState(["settings"]), 35 ...mapState(["settings"]),
36 - ...mapGetters(["permission_routes", "sidebar"]), 36 + ...mapGetters(["sidebarRouters", "sidebar"]),
37 activeMenu() { 37 activeMenu() {
38 const route = this.$route; 38 const route = this.$route;
39 const { meta, path } = route; 39 const { meta, path } = route;
@@ -29,7 +29,6 @@ @@ -29,7 +29,6 @@
29 <script> 29 <script>
30 import ScrollPane from './ScrollPane' 30 import ScrollPane from './ScrollPane'
31 import path from 'path' 31 import path from 'path'
32 -import Global from "@/layout/components/global.js";  
33 32
34 export default { 33 export default {
35 components: { ScrollPane }, 34 components: { ScrollPane },
@@ -145,7 +144,6 @@ export default { @@ -145,7 +144,6 @@ export default {
145 }) 144 })
146 }) 145 })
147 }) 146 })
148 - Global.$emit("removeCache", "refreshSelectedTag", this.selectedTag);  
149 }, 147 },
150 closeSelectedTag(view) { 148 closeSelectedTag(view) {
151 this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => { 149 this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
@@ -153,14 +151,12 @@ export default { @@ -153,14 +151,12 @@ export default {
153 this.toLastView(visitedViews, view) 151 this.toLastView(visitedViews, view)
154 } 152 }
155 }) 153 })
156 - Global.$emit("removeCache", "closeSelectedTag", view);  
157 }, 154 },
158 closeOthersTags() { 155 closeOthersTags() {
159 this.$router.push(this.selectedTag) 156 this.$router.push(this.selectedTag)
160 this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => { 157 this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => {
161 this.moveToCurrentTag() 158 this.moveToCurrentTag()
162 }) 159 })
163 - Global.$emit("removeCache", "closeOthersTags", this.selectedTag);  
164 }, 160 },
165 closeAllTags(view) { 161 closeAllTags(view) {
166 this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => { 162 this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => {
@@ -169,7 +165,6 @@ export default { @@ -169,7 +165,6 @@ export default {
169 } 165 }
170 this.toLastView(visitedViews, view) 166 this.toLastView(visitedViews, view)
171 }) 167 })
172 - Global.$emit("removeCache", "closeAllTags");  
173 }, 168 },
174 toLastView(visitedViews, view) { 169 toLastView(visitedViews, view) {
175 const latestView = visitedViews.slice(-1)[0] 170 const latestView = visitedViews.slice(-1)[0]
@@ -10,6 +10,7 @@ const getters = { @@ -10,6 +10,7 @@ const getters = {
10 introduction: state => state.user.introduction, 10 introduction: state => state.user.introduction,
11 roles: state => state.user.roles, 11 roles: state => state.user.roles,
12 permissions: state => state.user.permissions, 12 permissions: state => state.user.permissions,
13 - permission_routes: state => state.permission.routes 13 + permission_routes: state => state.permission.routes,
  14 + sidebarRouters:state => state.permission.sidebarRouters,
14 } 15 }
15 export default getters 16 export default getters
@@ -6,13 +6,17 @@ import ParentView from '@/components/ParentView'; @@ -6,13 +6,17 @@ import ParentView from '@/components/ParentView';
6 const permission = { 6 const permission = {
7 state: { 7 state: {
8 routes: [], 8 routes: [],
9 - addRoutes: [] 9 + addRoutes: [],
  10 + sidebarRouters: []
10 }, 11 },
11 mutations: { 12 mutations: {
12 SET_ROUTES: (state, routes) => { 13 SET_ROUTES: (state, routes) => {
13 state.addRoutes = routes 14 state.addRoutes = routes
14 state.routes = constantRoutes.concat(routes) 15 state.routes = constantRoutes.concat(routes)
15 - } 16 + },
  17 + SET_SIDEBAR_ROUTERS: (state, routers) => {
  18 + state.sidebarRouters = routers
  19 + },
16 }, 20 },
17 actions: { 21 actions: {
18 // 生成路由 22 // 生成路由
@@ -20,10 +24,14 @@ const permission = { @@ -20,10 +24,14 @@ const permission = {
20 return new Promise(resolve => { 24 return new Promise(resolve => {
21 // 向后端请求路由数据 25 // 向后端请求路由数据
22 getRouters().then(res => { 26 getRouters().then(res => {
23 - const accessedRoutes = filterAsyncRouter(res.data)  
24 - accessedRoutes.push({ path: '*', redirect: '/404', hidden: true })  
25 - commit('SET_ROUTES', accessedRoutes)  
26 - resolve(accessedRoutes) 27 + const sdata = JSON.parse(JSON.stringify(res.data))
  28 + const rdata = JSON.parse(JSON.stringify(res.data))
  29 + const sidebarRoutes = filterAsyncRouter(sdata)
  30 + const rewriteRoutes = filterAsyncRouter(rdata, true)
  31 + rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
  32 + commit('SET_ROUTES', rewriteRoutes)
  33 + commit('SET_SIDEBAR_ROUTERS', sidebarRoutes)
  34 + resolve(rewriteRoutes)
27 }) 35 })
28 }) 36 })
29 } 37 }
@@ -31,8 +39,11 @@ const permission = { @@ -31,8 +39,11 @@ const permission = {
31 } 39 }
32 40
33 // 遍历后台传来的路由字符串,转换为组件对象 41 // 遍历后台传来的路由字符串,转换为组件对象
34 -function filterAsyncRouter(asyncRouterMap) { 42 +function filterAsyncRouter(asyncRouterMap, isRewrite = false) {
35 return asyncRouterMap.filter(route => { 43 return asyncRouterMap.filter(route => {
  44 + if (isRewrite && route.children) {
  45 + route.children = filterChildren(route.children)
  46 + }
36 if (route.component) { 47 if (route.component) {
37 // Layout ParentView 组件特殊处理 48 // Layout ParentView 组件特殊处理
38 if (route.component === 'Layout') { 49 if (route.component === 'Layout') {
@@ -44,12 +55,34 @@ function filterAsyncRouter(asyncRouterMap) { @@ -44,12 +55,34 @@ function filterAsyncRouter(asyncRouterMap) {
44 } 55 }
45 } 56 }
46 if (route.children != null && route.children && route.children.length) { 57 if (route.children != null && route.children && route.children.length) {
47 - route.children = filterAsyncRouter(route.children) 58 + route.children = filterAsyncRouter(route.children, route, isRewrite)
48 } 59 }
49 return true 60 return true
50 }) 61 })
51 } 62 }
52 63
  64 +function filterChildren(childrenMap) {
  65 + var children = []
  66 + childrenMap.forEach((el, index) => {
  67 + if (el.children && el.children.length) {
  68 + if (el.component === 'ParentView') {
  69 + el.children.forEach(c => {
  70 + c.path = el.path + '/' + c.path
  71 + if (c.children && c.children.length) {
  72 + children = children.concat(filterChildren(c.children, c))
  73 + return
  74 + }
  75 + children.push(c)
  76 + })
  77 + childrenMap.splice(index, 1)
  78 + return
  79 + }
  80 + }
  81 + children = children.concat(el)
  82 + })
  83 + return children
  84 +}
  85 +
53 export const loadView = (view) => { // 路由懒加载 86 export const loadView = (view) => { // 路由懒加载
54 return (resolve) => require([`@/views/${view}`], resolve) 87 return (resolve) => require([`@/views/${view}`], resolve)
55 } 88 }
@@ -18,7 +18,6 @@ @@ -18,7 +18,6 @@
18 18
19 <script> 19 <script>
20 import { updateUserPwd } from "@/api/system/user"; 20 import { updateUserPwd } from "@/api/system/user";
21 -import Global from "@/layout/components/global.js";  
22 21
23 export default { 22 export default {
24 data() { 23 data() {
@@ -65,7 +64,6 @@ export default { @@ -65,7 +64,6 @@ export default {
65 }); 64 });
66 }, 65 },
67 close() { 66 close() {
68 - Global.$emit("removeCache", "closeSelectedTag", this.$route);  
69 this.$store.dispatch("tagsView/delView", this.$route); 67 this.$store.dispatch("tagsView/delView", this.$route);
70 this.$router.push({ path: "/index" }); 68 this.$router.push({ path: "/index" });
71 } 69 }
@@ -24,7 +24,6 @@ @@ -24,7 +24,6 @@
24 24
25 <script> 25 <script>
26 import { updateUserProfile } from "@/api/system/user"; 26 import { updateUserProfile } from "@/api/system/user";
27 -import Global from "@/layout/components/global.js";  
28 27
29 export default { 28 export default {
30 props: { 29 props: {
@@ -69,7 +68,6 @@ export default { @@ -69,7 +68,6 @@ export default {
69 }); 68 });
70 }, 69 },
71 close() { 70 close() {
72 - Global.$emit("removeCache", "closeSelectedTag", this.$route);  
73 this.$store.dispatch("tagsView/delView", this.$route); 71 this.$store.dispatch("tagsView/delView", this.$route);
74 this.$router.push({ path: "/index" }); 72 this.$router.push({ path: "/index" });
75 } 73 }
@@ -127,7 +127,6 @@ @@ -127,7 +127,6 @@
127 import { getGenTable, updateGenTable } from "@/api/tool/gen"; 127 import { getGenTable, updateGenTable } from "@/api/tool/gen";
128 import { optionselect as getDictOptionselect } from "@/api/system/dict/type"; 128 import { optionselect as getDictOptionselect } from "@/api/system/dict/type";
129 import { listMenu as getMenuTreeselect } from "@/api/system/menu"; 129 import { listMenu as getMenuTreeselect } from "@/api/system/menu";
130 -import Global from "@/layout/components/global.js";  
131 import basicInfoForm from "./basicInfoForm"; 130 import basicInfoForm from "./basicInfoForm";
132 import genInfoForm from "./genInfoForm"; 131 import genInfoForm from "./genInfoForm";
133 import Sortable from 'sortablejs' 132 import Sortable from 'sortablejs'
@@ -208,7 +207,6 @@ export default { @@ -208,7 +207,6 @@ export default {
208 }, 207 },
209 /** 关闭按钮 */ 208 /** 关闭按钮 */
210 close() { 209 close() {
211 - Global.$emit("removeCache", "closeSelectedTag", this.$route);  
212 this.$store.dispatch("tagsView/delView", this.$route); 210 this.$store.dispatch("tagsView/delView", this.$route);
213 this.$router.push({ path: "/tool/gen", query: { t: Date.now()}}) 211 this.$router.push({ path: "/tool/gen", query: { t: Date.now()}})
214 } 212 }