Commit ecc7a8be46475f5d7235073214857e241d4741dd
1 parent
e23ad201
优化多级菜单之间切换无法缓存的问题
Showing
8 changed files
with
55 additions
and
135 deletions
ruoyi-ui/src/layout/components/AppMain.vue
| 1 | -<!-- @author ruoyi 20201128 支持三级以上菜单缓存 --> | |
| 2 | 1 | <template> |
| 3 | 2 | <section class="app-main"> |
| 4 | 3 | <transition name="fade-transform" mode="out-in"> |
| 5 | - <keep-alive :max="20" :exclude="notCacheName"> | |
| 4 | + <keep-alive :include="cachedViews"> | |
| 6 | 5 | <router-view :key="key" /> |
| 7 | 6 | </keep-alive> |
| 8 | 7 | </transition> |
| ... | ... | @@ -10,119 +9,17 @@ |
| 10 | 9 | </template> |
| 11 | 10 | |
| 12 | 11 | <script> |
| 13 | -import Global from "@/layout/components/global.js"; | |
| 14 | - | |
| 15 | 12 | export default { |
| 16 | 13 | name: 'AppMain', |
| 17 | 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 | 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; | |
| 49 | - } | |
| 50 | - } | |
| 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 | -}; | |
| 19 | + return this.$route.path | |
| 20 | + } | |
| 21 | + } | |
| 22 | +} | |
| 126 | 23 | </script> |
| 127 | 24 | |
| 128 | 25 | <style lang="scss" scoped> |
| ... | ... | @@ -134,7 +31,7 @@ export default { |
| 134 | 31 | overflow: hidden; |
| 135 | 32 | } |
| 136 | 33 | |
| 137 | -.fixed-header + .app-main { | |
| 34 | +.fixed-header+.app-main { | |
| 138 | 35 | padding-top: 50px; |
| 139 | 36 | } |
| 140 | 37 | |
| ... | ... | @@ -144,7 +41,7 @@ export default { |
| 144 | 41 | min-height: calc(100vh - 84px); |
| 145 | 42 | } |
| 146 | 43 | |
| 147 | - .fixed-header + .app-main { | |
| 44 | + .fixed-header+.app-main { | |
| 148 | 45 | padding-top: 84px; |
| 149 | 46 | } |
| 150 | 47 | } | ... | ... |
ruoyi-ui/src/layout/components/Sidebar/index.vue
| ... | ... | @@ -13,7 +13,7 @@ |
| 13 | 13 | mode="vertical" |
| 14 | 14 | > |
| 15 | 15 | <sidebar-item |
| 16 | - v-for="(route, index) in permission_routes" | |
| 16 | + v-for="(route, index) in sidebarRouters" | |
| 17 | 17 | :key="route.path + index" |
| 18 | 18 | :item="route" |
| 19 | 19 | :base-path="route.path" |
| ... | ... | @@ -33,7 +33,7 @@ export default { |
| 33 | 33 | components: { SidebarItem, Logo }, |
| 34 | 34 | computed: { |
| 35 | 35 | ...mapState(["settings"]), |
| 36 | - ...mapGetters(["permission_routes", "sidebar"]), | |
| 36 | + ...mapGetters(["sidebarRouters", "sidebar"]), | |
| 37 | 37 | activeMenu() { |
| 38 | 38 | const route = this.$route; |
| 39 | 39 | const { meta, path } = route; | ... | ... |
ruoyi-ui/src/layout/components/TagsView/index.vue
| ... | ... | @@ -29,7 +29,6 @@ |
| 29 | 29 | <script> |
| 30 | 30 | import ScrollPane from './ScrollPane' |
| 31 | 31 | import path from 'path' |
| 32 | -import Global from "@/layout/components/global.js"; | |
| 33 | 32 | |
| 34 | 33 | export default { |
| 35 | 34 | components: { ScrollPane }, |
| ... | ... | @@ -145,7 +144,6 @@ export default { |
| 145 | 144 | }) |
| 146 | 145 | }) |
| 147 | 146 | }) |
| 148 | - Global.$emit("removeCache", "refreshSelectedTag", this.selectedTag); | |
| 149 | 147 | }, |
| 150 | 148 | closeSelectedTag(view) { |
| 151 | 149 | this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => { |
| ... | ... | @@ -153,14 +151,12 @@ export default { |
| 153 | 151 | this.toLastView(visitedViews, view) |
| 154 | 152 | } |
| 155 | 153 | }) |
| 156 | - Global.$emit("removeCache", "closeSelectedTag", view); | |
| 157 | 154 | }, |
| 158 | 155 | closeOthersTags() { |
| 159 | 156 | this.$router.push(this.selectedTag) |
| 160 | 157 | this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => { |
| 161 | 158 | this.moveToCurrentTag() |
| 162 | 159 | }) |
| 163 | - Global.$emit("removeCache", "closeOthersTags", this.selectedTag); | |
| 164 | 160 | }, |
| 165 | 161 | closeAllTags(view) { |
| 166 | 162 | this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => { |
| ... | ... | @@ -169,7 +165,6 @@ export default { |
| 169 | 165 | } |
| 170 | 166 | this.toLastView(visitedViews, view) |
| 171 | 167 | }) |
| 172 | - Global.$emit("removeCache", "closeAllTags"); | |
| 173 | 168 | }, |
| 174 | 169 | toLastView(visitedViews, view) { |
| 175 | 170 | const latestView = visitedViews.slice(-1)[0] | ... | ... |
ruoyi-ui/src/store/getters.js
| ... | ... | @@ -10,6 +10,7 @@ const getters = { |
| 10 | 10 | introduction: state => state.user.introduction, |
| 11 | 11 | roles: state => state.user.roles, |
| 12 | 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 | 16 | export default getters | ... | ... |
ruoyi-ui/src/store/modules/permission.js
| ... | ... | @@ -6,13 +6,17 @@ import ParentView from '@/components/ParentView'; |
| 6 | 6 | const permission = { |
| 7 | 7 | state: { |
| 8 | 8 | routes: [], |
| 9 | - addRoutes: [] | |
| 9 | + addRoutes: [], | |
| 10 | + sidebarRouters: [] | |
| 10 | 11 | }, |
| 11 | 12 | mutations: { |
| 12 | 13 | SET_ROUTES: (state, routes) => { |
| 13 | 14 | state.addRoutes = routes |
| 14 | 15 | state.routes = constantRoutes.concat(routes) |
| 15 | - } | |
| 16 | + }, | |
| 17 | + SET_SIDEBAR_ROUTERS: (state, routers) => { | |
| 18 | + state.sidebarRouters = routers | |
| 19 | + }, | |
| 16 | 20 | }, |
| 17 | 21 | actions: { |
| 18 | 22 | // 生成路由 |
| ... | ... | @@ -20,10 +24,14 @@ const permission = { |
| 20 | 24 | return new Promise(resolve => { |
| 21 | 25 | // 向后端请求路由数据 |
| 22 | 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 | 39 | } |
| 32 | 40 | |
| 33 | 41 | // 遍历后台传来的路由字符串,转换为组件对象 |
| 34 | -function filterAsyncRouter(asyncRouterMap) { | |
| 42 | +function filterAsyncRouter(asyncRouterMap, isRewrite = false) { | |
| 35 | 43 | return asyncRouterMap.filter(route => { |
| 44 | + if (isRewrite && route.children) { | |
| 45 | + route.children = filterChildren(route.children) | |
| 46 | + } | |
| 36 | 47 | if (route.component) { |
| 37 | 48 | // Layout ParentView 组件特殊处理 |
| 38 | 49 | if (route.component === 'Layout') { |
| ... | ... | @@ -44,14 +55,36 @@ function filterAsyncRouter(asyncRouterMap) { |
| 44 | 55 | } |
| 45 | 56 | } |
| 46 | 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 | 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 | 86 | export const loadView = (view) => { // 路由懒加载 |
| 54 | - return (resolve) => require([`@/views/${view}`], resolve) | |
| 87 | + return (resolve) => require([`@/views/${view}`], resolve) | |
| 55 | 88 | } |
| 56 | 89 | |
| 57 | 90 | export default permission | ... | ... |
ruoyi-ui/src/views/system/user/profile/resetPwd.vue
| ... | ... | @@ -18,7 +18,6 @@ |
| 18 | 18 | |
| 19 | 19 | <script> |
| 20 | 20 | import { updateUserPwd } from "@/api/system/user"; |
| 21 | -import Global from "@/layout/components/global.js"; | |
| 22 | 21 | |
| 23 | 22 | export default { |
| 24 | 23 | data() { |
| ... | ... | @@ -65,7 +64,6 @@ export default { |
| 65 | 64 | }); |
| 66 | 65 | }, |
| 67 | 66 | close() { |
| 68 | - Global.$emit("removeCache", "closeSelectedTag", this.$route); | |
| 69 | 67 | this.$store.dispatch("tagsView/delView", this.$route); |
| 70 | 68 | this.$router.push({ path: "/index" }); |
| 71 | 69 | } | ... | ... |
ruoyi-ui/src/views/system/user/profile/userInfo.vue
| ... | ... | @@ -24,7 +24,6 @@ |
| 24 | 24 | |
| 25 | 25 | <script> |
| 26 | 26 | import { updateUserProfile } from "@/api/system/user"; |
| 27 | -import Global from "@/layout/components/global.js"; | |
| 28 | 27 | |
| 29 | 28 | export default { |
| 30 | 29 | props: { |
| ... | ... | @@ -69,7 +68,6 @@ export default { |
| 69 | 68 | }); |
| 70 | 69 | }, |
| 71 | 70 | close() { |
| 72 | - Global.$emit("removeCache", "closeSelectedTag", this.$route); | |
| 73 | 71 | this.$store.dispatch("tagsView/delView", this.$route); |
| 74 | 72 | this.$router.push({ path: "/index" }); |
| 75 | 73 | } | ... | ... |
ruoyi-ui/src/views/tool/gen/editTable.vue
| ... | ... | @@ -127,7 +127,6 @@ |
| 127 | 127 | import { getGenTable, updateGenTable } from "@/api/tool/gen"; |
| 128 | 128 | import { optionselect as getDictOptionselect } from "@/api/system/dict/type"; |
| 129 | 129 | import { listMenu as getMenuTreeselect } from "@/api/system/menu"; |
| 130 | -import Global from "@/layout/components/global.js"; | |
| 131 | 130 | import basicInfoForm from "./basicInfoForm"; |
| 132 | 131 | import genInfoForm from "./genInfoForm"; |
| 133 | 132 | import Sortable from 'sortablejs' |
| ... | ... | @@ -208,7 +207,6 @@ export default { |
| 208 | 207 | }, |
| 209 | 208 | /** 关闭按钮 */ |
| 210 | 209 | close() { |
| 211 | - Global.$emit("removeCache", "closeSelectedTag", this.$route); | |
| 212 | 210 | this.$store.dispatch("tagsView/delView", this.$route); |
| 213 | 211 | this.$router.push({ path: "/tool/gen", query: { t: Date.now()}}) |
| 214 | 212 | } | ... | ... |