diff --git a/vue-admin-wonderful-next/src/router/index.ts b/vue-admin-wonderful-next/src/router/index.ts index 2bc435e..86793df 100644 --- a/vue-admin-wonderful-next/src/router/index.ts +++ b/vue-admin-wonderful-next/src/router/index.ts @@ -1,8 +1,11 @@ import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router" +import { store } from "/@/store/index.ts"; +// 定义动态路由 export const dynamicRoutes = [ { path: '/', + name: '/', component: () => import('/@/views/layout/index.vue'), redirect: '/home', meta: { @@ -14,6 +17,7 @@ export const dynamicRoutes = [ }, children: [{ path: '/home', + name: 'home', component: () => import('/@/views/home/index.vue'), meta: { title: '首页', @@ -32,8 +36,8 @@ export const dynamicRoutes = [ title: '系统设置', isLink: '', isHide: false, - icon: 'iconfont icon-xitongshezhi', - isKeepAlive: true + isKeepAlive: true, + icon: 'iconfont icon-xitongshezhi' }, children: [ { @@ -44,8 +48,8 @@ export const dynamicRoutes = [ title: '菜单管理', isLink: '', isHide: false, - icon: 'iconfont icon-caidan', - isKeepAlive: true + isKeepAlive: true, + icon: 'iconfont icon-caidan' } }, { @@ -56,78 +60,92 @@ export const dynamicRoutes = [ title: '用户管理', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-icon-', - isKeepAlive: false } } ] }, { path: '/limits', + name: 'limits', component: () => import('/@/views/layout/routerView/parent.vue'), redirect: '/limits/frontEnd', meta: { title: '权限管理', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-quanxian' }, children: [ { path: '/limits/frontEnd', + name: 'limitsFrontEnd', component: () => import('/@/views/layout/routerView/parent.vue'), redirect: '/limits/frontEnd/page', meta: { title: '前端控制', isLink: '', - isHide: false + isHide: false, + isKeepAlive: true }, children: [ { path: '/limits/frontEnd/page', + name: 'limitsFrontEndPage', component: () => import('/@/views/limits/frontEnd/page/index.vue'), meta: { title: '页面权限', isLink: '', - isHide: false + isHide: false, + isKeepAlive: true } }, { path: '/limits/frontEnd/btn', + name: 'limitsFrontEndBtn', component: () => import('/@/views/limits/frontEnd/btn/index.vue'), meta: { title: '按钮权限', isLink: '', - isHide: false + isHide: false, + isKeepAlive: true } } ] }, { path: '/limits/backEnd', + name: 'limitsBackEnd', component: () => import('/@/views/layout/routerView/parent.vue'), meta: { title: '后端控制', isLink: '', - isHide: false + isHide: false, + isKeepAlive: true }, children: [ { path: '/limits/backEnd/page', + name: 'limitsBackEndEndPage', component: () => import('/@/views/limits/backEnd/page/index.vue'), meta: { title: '页面权限', isLink: '', - isHide: false + isHide: false, + isKeepAlive: true } }, { path: '/limits/backEnd/btn', + name: 'limitsBackEndEndBtn', component: () => import('/@/views/limits/backEnd/btn/index.vue'), meta: { title: '按钮权限', isLink: '', - isHide: false + isHide: false, + isKeepAlive: true } } ] @@ -136,64 +154,76 @@ export const dynamicRoutes = [ }, { path: '/menu', + name: 'menu', component: () => import('/@/views/layout/routerView/parent.vue'), redirect: '/menu/menu1', meta: { title: '路由嵌套', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-xitongshezhi' }, children: [ { path: '/menu/menu1', + name: 'menu1', component: () => import('/@/views/layout/routerView/parent.vue'), redirect: '/menu/menu1/menu11', meta: { title: '菜单1', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-caidan' }, children: [ { path: '/menu/menu1/menu11', + name: 'menu11', component: () => import('/@/views/menu/menu1/menu11/index.vue'), meta: { title: '菜单11', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-caidan' } }, { path: '/menu/menu1/menu12', + name: 'menu12', component: () => import('/@/views/layout/routerView/parent.vue'), redirect: '/menu/menu1/menu12/menu121', meta: { title: '菜单12', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-caidan' }, children: [ { path: '/menu/menu1/menu12/menu121', + name: 'menu121', component: () => import('/@/views/menu/menu1/menu12/menu121/index.vue'), meta: { title: '菜单121', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-caidan' } }, { path: '/menu/menu1/menu12/menu122', + name: 'menu122', component: () => import('/@/views/menu/menu1/menu12/menu122/index.vue'), meta: { title: '菜单122', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-caidan' } } @@ -201,11 +231,13 @@ export const dynamicRoutes = [ }, { path: '/menu/menu1/menu13', + name: 'menu13', component: () => import('/@/views/menu/menu1/menu13/index.vue'), meta: { title: '菜单13', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-caidan' } } @@ -213,11 +245,13 @@ export const dynamicRoutes = [ }, { path: '/menu/menu2', + name: 'menu2', component: () => import('/@/views/menu/menu2/index.vue'), meta: { title: '菜单2', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-caidan' } } @@ -225,87 +259,104 @@ export const dynamicRoutes = [ }, { path: '/fun', + name: 'funIndex', component: () => import('/@/views/fun/index.vue'), meta: { title: '功能', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-crew_feature' } }, { path: '/pages', + name: 'pagesIndex', component: () => import('/@/views/pages/index.vue'), meta: { title: '页面', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-fuzhiyemian' } }, { path: '/components', + name: 'componentsIndex', component: () => import('/@/views/components/index.vue'), meta: { title: '组件', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-zujian' } }, { path: '/chart', + name: 'chartIndex', component: () => import('/@/views/chart/index.vue'), meta: { title: '大数据图表', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-ico_shuju' } }, { path: '/personal', + name: 'personal', component: () => import('/@/views/personal/index.vue'), meta: { title: '个人中心', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-gerenzhongxin' } }, { path: '/tools', + name: 'tools', component: () => import('/@/views/tools/index.vue'), meta: { title: '工具类集合', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-gongju' } }, { path: '/link', + name: 'link', component: () => import('/@/views/layout/routerView/parent.vue'), meta: { title: '外链', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-caozuo-wailian' } }, { - path: '/iframe', - component: () => import('/@/views/layout/routerView/iframe.vue'), + path: '/iframes', + name: 'iframes', + component: () => import('/@/views/layout/routerView/iframes.vue'), meta: { title: '内嵌 iframe', isLink: '', isHide: false, + isKeepAlive: true, icon: 'iconfont icon-neiqianshujuchucun' } }] } ] +// 定义静态路由 const staticRoutes: Array = [ { path: '/login', @@ -337,14 +388,61 @@ const staticRoutes: Array = [ } ] +// 添加静态路由 const router = createRouter({ history: createWebHashHistory(), routes: staticRoutes }) -dynamicRoutes.map((route) => { - router.addRoute(route as RouteRecordRaw); -}); +// 多级嵌套数组处理成一维数组 +export function formatFlatteningRoutes(arr: any) { + if (arr.length < 0) return false + for (let i = 0; i < arr.length; i++) { + if (arr[i].children) { + arr = arr.slice(0, i + 1).concat(arr[i].children, arr.slice(i + 1)) + } + } + return arr +} + +// 多级嵌套数组处理后的一维数组,再处理成 `定义动态路由` 的格式 +// 只保留二级:也就是二级以上全部处理成只有二级,keep-alive 支持二级缓存 +// isKeepAlive 处理 `name` 值,进行缓存。顶级关闭,全部不缓存 +export function formatTwoStageRoutes(arr: any) { + if (arr.length < 0) return false + const newArr: any = [] + const cacheList: Array = [] + arr.map((v: any) => { + if (v.path === '/') { + newArr.push({ component: v.component, name: v.name, path: v.path, redirect: v.redirect, meta: v.meta, children: [] }) + } else { + newArr[0].children.push({ ...v }) + if (newArr[0].meta.isKeepAlive && v.meta.isKeepAlive) { + cacheList.push(v.name) + store.dispatch('setCacheKeepAlive', cacheList) + } + } + }) + return newArr +} + +// 添加动态路由 +export function setAddRoute() { + formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes)).map((route: any) => { + router.addRoute(route as RouteRecordRaw) + }) +} + +// 删除/重置路由 +export function resetRoute() { + formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes)).map((route: any) => { + const { name } = route + router.hasRoute(name) && router.removeRoute(name) + }) +} + +// 初始化执行函数 +setAddRoute() // router.afterEach((to, from) => { diff --git a/vue-admin-wonderful-next/src/store/index.ts b/vue-admin-wonderful-next/src/store/index.ts index a980000..17dd8f8 100644 --- a/vue-admin-wonderful-next/src/store/index.ts +++ b/vue-admin-wonderful-next/src/store/index.ts @@ -1,7 +1,6 @@ import { InjectionKey } from 'vue' import { createStore, useStore as baseUseStore, Store } from 'vuex' import themeConfig from '/@/utils/themeConfig.ts' -import { dynamicRoutes } from '/@/router/index.ts' export interface RootStateTypes { themeConfig: { isDrawer: boolean, @@ -42,7 +41,8 @@ export interface RootStateTypes { columnsAsideStyle: string, layout: string }, - routes: Array + routes: Array, + caches: Array } export const key: InjectionKey> = Symbol() @@ -50,7 +50,8 @@ export const key: InjectionKey> = Symbol() export const store = createStore({ state: { themeConfig, - routes: [] + routes: [], + caches: [] }, mutations: { getThemeConfig(state: any, data: object) { @@ -58,15 +59,21 @@ export const store = createStore({ }, getRoutes(state: any, data: Array) { state.routes = data + }, + getCacheKeepAlive(state: any, data: Array) { + state.caches = data } }, actions: { setThemeConfig({ commit }, data: object) { commit('getThemeConfig', data) }, - async setRoutes({ commit }) { - commit('getRoutes', dynamicRoutes) - } + async setRoutes({ commit }, data: any) { + commit('getRoutes', data) + }, + async setCacheKeepAlive({ commit }, data: Array) { + commit('getCacheKeepAlive', data) + }, } }) diff --git a/vue-admin-wonderful-next/src/theme/app.scss b/vue-admin-wonderful-next/src/theme/app.scss index cdb9c3a..0dfc972 100644 --- a/vue-admin-wonderful-next/src/theme/app.scss +++ b/vue-admin-wonderful-next/src/theme/app.scss @@ -73,6 +73,11 @@ body, .layout-mian-height-50 { height: calc(100vh - 50px); } + .layout-columns-warp { + flex: 1; + display: flex; + overflow: hidden; + } } /* element plus 全局样式 diff --git a/vue-admin-wonderful-next/src/theme/element.scss b/vue-admin-wonderful-next/src/theme/element.scss index 8e63dfb..4d1c342 100644 --- a/vue-admin-wonderful-next/src/theme/element.scss +++ b/vue-admin-wonderful-next/src/theme/element.scss @@ -786,10 +786,6 @@ width: 24px; text-align: center; } -// 获取焦点时 -.el-menu-item:focus { - background: transparent !important; -} /* Tabs 标签页 ------------------------------- */ diff --git a/vue-admin-wonderful-next/src/views/layout/component/aside.vue b/vue-admin-wonderful-next/src/views/layout/component/aside.vue index f3dc3f3..21691e6 100644 --- a/vue-admin-wonderful-next/src/views/layout/component/aside.vue +++ b/vue-admin-wonderful-next/src/views/layout/component/aside.vue @@ -18,6 +18,7 @@ import { onBeforeMount, } from "vue"; import { useStore } from "/@/store/index.ts"; +import { dynamicRoutes } from "/@/router/index.ts"; import Logo from "/@/views/layout/logo/index.vue"; import Vertical from "/@/views/layout/navMenu/vertical.vue"; export default { @@ -31,8 +32,8 @@ export default { }); // 设置/过滤路由(非静态路由/是否显示在菜单中) const setFilterRoutes = () => { - store.dispatch("setRoutes"); - state.menuList = filterRoutesFun(store.state.routes[0].children); + store.dispatch("setRoutes", dynamicRoutes[0].children); + state.menuList = filterRoutesFun(store.state.routes); }; // 路由过滤递归函数 const filterRoutesFun = (arr: Array) => { diff --git a/vue-admin-wonderful-next/src/views/layout/main/columns.vue b/vue-admin-wonderful-next/src/views/layout/main/columns.vue index 5e183e0..70527d4 100644 --- a/vue-admin-wonderful-next/src/views/layout/main/columns.vue +++ b/vue-admin-wonderful-next/src/views/layout/main/columns.vue @@ -1,7 +1,7 @@