diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java index d56e09da..9a6fee4b 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/RouterVo.java @@ -59,4 +59,9 @@ public class RouterVo { */ private List children; + /** + * 系统编码(用于前端按菜单组过滤) + */ + private String systemCode; + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java index 554e8dd5..8a309e99 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java @@ -217,6 +217,7 @@ public class SysMenuServiceImpl implements ISysMenuService { router.setPath(menu.getRouterPath()); router.setComponent(menu.getComponentInfo()); router.setQuery(menu.getQueryParam()); + router.setSystemCode(menu.getSystemCode()); router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath(), menu.getRemark())); List cMenus = menu.getChildren(); if (CollUtil.isNotEmpty(cMenus) && SystemConstants.TYPE_DIR.equals(menu.getMenuType())) { diff --git a/zdxt-web-client/zdxt-admin-plus-ui/src/api/menu.ts b/zdxt-web-client/zdxt-admin-plus-ui/src/api/menu.ts index c0f4f29a..a3ae80e1 100644 --- a/zdxt-web-client/zdxt-admin-plus-ui/src/api/menu.ts +++ b/zdxt-web-client/zdxt-admin-plus-ui/src/api/menu.ts @@ -3,10 +3,9 @@ import { AxiosPromise } from 'axios'; import { RouteRecordRaw } from 'vue-router'; // 获取路由 -export function getRouters(systemCode?: string): AxiosPromise { +export function getRouters(): AxiosPromise { return request({ - url: '/system/menu/getRoutersBySystemCode', - method: 'get', - params: { systemCode } + url: '/system/menu/getRouters', + method: 'get' }); } diff --git a/zdxt-web-client/zdxt-admin-plus-ui/src/permission.ts b/zdxt-web-client/zdxt-admin-plus-ui/src/permission.ts index 7387e8c5..9524f1c2 100644 --- a/zdxt-web-client/zdxt-admin-plus-ui/src/permission.ts +++ b/zdxt-web-client/zdxt-admin-plus-ui/src/permission.ts @@ -7,7 +7,7 @@ import { isHttp, isPathMatch } from '@/utils/validate'; import { isRelogin } from '@/utils/request'; import { useUserStore } from '@/store/modules/user'; import { useSettingsStore } from '@/store/modules/settings'; -import { usePermissionStore } from '@/store/modules/permission'; +import { usePermissionStore, CATCH_ALL_ROUTE_NAME } from '@/store/modules/permission'; import { ElMessage } from 'element-plus/es'; NProgress.configure({ showSpinner: false }); @@ -46,7 +46,7 @@ router.beforeEach(async (to, from, next) => { } }); // 所有动态路由加载完成后,注入404通配路由,避免刷新时提前匹配 - router.addRoute({ path: '/:pathMatch(.*)*', component: () => import('@/views/error/404.vue') }); + router.addRoute({ name: CATCH_ALL_ROUTE_NAME, path: '/:pathMatch(.*)*', component: () => import('@/views/error/404.vue') }); // @ts-expect-error hack方法 确保addRoutes已完成 next({ path: to.path, replace: true, params: to.params, query: to.query, hash: to.hash, name: to.name as string }); // hack方法 确保addRoutes已完成 } diff --git a/zdxt-web-client/zdxt-admin-plus-ui/src/store/modules/permission.ts b/zdxt-web-client/zdxt-admin-plus-ui/src/store/modules/permission.ts index 7a003aed..9c2c0147 100644 --- a/zdxt-web-client/zdxt-admin-plus-ui/src/store/modules/permission.ts +++ b/zdxt-web-client/zdxt-admin-plus-ui/src/store/modules/permission.ts @@ -9,6 +9,7 @@ import ParentView from '@/components/ParentView/index.vue'; import InnerLink from '@/layout/components/InnerLink/index.vue'; import { ref } from 'vue'; import { createCustomNameComponent } from '@/utils/createCustomNameComponent'; +import { isHttp } from '@/utils/validate'; // 匹配views里面所有的.vue文件 const modules = import.meta.glob('./../../views/**/*.vue'); @@ -20,6 +21,9 @@ export interface SystemCodeOption { /** 当设置了 iframeUrl 时,切换到该选项会以全屏 iframe 展示,而非加载菜单路由 */ iframeUrl?: string; } +/** 404 兜底路由名称,方便动态移除和重新添加 */ +export const CATCH_ALL_ROUTE_NAME = 'CatchAll404'; + export const systemCodeOptions: SystemCodeOption[] = [ { label: '首页', value: 'planindex', icon: 'HomeFilled', iframeUrl: '/enforcement-code-v1/statistical/home.html' }, { label: '执法监督码', value: 'supervisionCode', icon: 'Document' }, @@ -32,6 +36,8 @@ export const usePermissionStore = defineStore('permission', () => { const defaultRoutes = ref([]); const topbarRouters = ref([]); const sidebarRouters = ref([]); + /** 缓存后端返回的全量路由原始数据(未经组件转换) */ + const allRouteDataCache = ref([]); /** sessionStorage key */ const SYSTEM_CODE_KEY = 'currentSystemCode'; /** 当前选中的系统编码(从 sessionStorage 恢复,默认 planindex) */ @@ -71,12 +77,18 @@ export const usePermissionStore = defineStore('permission', () => { }; const generateRoutes = async (systemCode?: string): Promise => { const code = systemCode || currentSystemCode.value; - const res = await getRouters(code); - const { data } = res; - console.log('[路由调试] systemCode =', code, ', 后端返回路由数据 =', JSON.parse(JSON.stringify(data))); - const sdata = JSON.parse(JSON.stringify(data)); - const rdata = JSON.parse(JSON.stringify(data)); - const defaultData = JSON.parse(JSON.stringify(data)); + // 仅首次加载时请求后端,后续从缓存取 + if (allRouteDataCache.value.length === 0) { + const res = await getRouters(); + allRouteDataCache.value = res.data as any[]; + console.log('[路由调试] 后端返回全量路由数据 =', JSON.parse(JSON.stringify(allRouteDataCache.value))); + } + // 按当前 systemCode 过滤出匹配的顶层路由 + const filteredData = allRouteDataCache.value.filter((route: any) => route.systemCode === code); + console.log('[路由调试] systemCode =', code, ', 过滤后路由数据 =', JSON.parse(JSON.stringify(filteredData))); + const sdata = JSON.parse(JSON.stringify(filteredData)); + const rdata = JSON.parse(JSON.stringify(filteredData)); + const defaultData = JSON.parse(JSON.stringify(filteredData)); const sidebarRoutes = filterAsyncRouter(sdata); const rewriteRoutes = filterAsyncRouter(rdata, undefined, true); const defaultRoutes = filterAsyncRouter(defaultData); @@ -152,19 +164,34 @@ export const usePermissionStore = defineStore('permission', () => { } iframeMode.value = false; iframeUrl.value = ''; - // 移除之前动态添加的路由 - addRoutes.value.forEach((route) => { - if (route.name) { + + // ====== 彻底清理所有动态路由,保证路由表干净 ====== + // 1. 收集所有静态路由名称(包括子路由) + const constantNames = new Set(); + const collectNames = (routeList: RouteRecordRaw[]) => { + routeList.forEach((r) => { + if (r.name) constantNames.add(r.name); + if (r.children) collectNames(r.children); + }); + }; + collectNames(constantRoutes); + // 2. 移除所有非静态路由(包括之前的动态业务路由和404兜底路由) + router.getRoutes().forEach((route) => { + if (route.name && !constantNames.has(route.name)) { router.removeRoute(route.name); } }); - // 重新生成路由 + + // ====== 重新生成并注册路由 ====== const accessRoutes = await generateRoutes(systemCode); accessRoutes.forEach((route) => { - if (!router.hasRoute(route.name!)) { + if (!isHttp(route.path)) { router.addRoute(route); } }); + // 重新添加404兜底路由,确保放在所有动态路由之后 + router.addRoute({ name: CATCH_ALL_ROUTE_NAME, path: '/:pathMatch(.*)*', component: () => import('@/views/error/404.vue') }); + console.log('[路由调试] switchSystemCode 完成, 当前路由列表 =', router.getRoutes().map((r) => ({ name: r.name, path: r.path }))); }; return {