diff --git a/frontend/src/components/bussiness/ms-breadcrumb/index.vue b/frontend/src/components/bussiness/ms-breadcrumb/index.vue index 422bbfb00c..0ebcd9dd07 100644 --- a/frontend/src/components/bussiness/ms-breadcrumb/index.vue +++ b/frontend/src/components/bussiness/ms-breadcrumb/index.vue @@ -26,8 +26,14 @@ */ listenerRouteChange((newRoute) => { const { name } = newRoute; + + // 顶部菜单层级会全等 if (name === appStore.currentTopMenu.name) { appStore.setBreadcrumbList(appStore.currentTopMenu?.meta?.breadcrumbs); + } else if ((name as string).includes(appStore.currentTopMenu.name as string)) { + // 顶部菜单内下钻的父子路由命名是包含关系,子路由会携带完整的父路由名称 + const currentRoute = router.currentRoute.value.matched[1].children.find((e) => e.name === name); + appStore.setBreadcrumbList(currentRoute?.meta?.breadcrumbs); } else { appStore.setBreadcrumbList([]); } @@ -51,7 +57,7 @@ color: var(--color-text-brand); } &:last-child { - @apply cursor-auto; + @apply cursor-auto font-normal; color: var(--color-text-2); } diff --git a/frontend/src/components/bussiness/ms-breadcrumb/types.ts b/frontend/src/components/bussiness/ms-breadcrumb/types.ts new file mode 100644 index 0000000000..82f609b544 --- /dev/null +++ b/frontend/src/components/bussiness/ms-breadcrumb/types.ts @@ -0,0 +1,6 @@ +import type { RouteRecordName } from 'vue-router'; + +export interface BreadcrumbItem { + name: RouteRecordName; + locale: string; +} diff --git a/frontend/src/components/bussiness/ms-top-menu/index.vue b/frontend/src/components/bussiness/ms-top-menu/index.vue index 634cf5cc2a..5a9a06d9a5 100644 --- a/frontend/src/components/bussiness/ms-top-menu/index.vue +++ b/frontend/src/components/bussiness/ms-top-menu/index.vue @@ -37,12 +37,20 @@ ); function setCurrentTopMenu(key: string) { - const secParent = appStore.topMenus.find((el: RouteRecordRaw) => { - return (el?.name as string).includes(key); + // 先判断全等,避免同级路由出现命名包含情况 + const secParentFullSame = appStore.topMenus.find((el: RouteRecordRaw) => { + return key === el?.name; }); - if (secParent) { - appStore.setCurrentTopMenu(secParent); + // 非全等的情况下,一定是父子路由包含关系 + const secParentLike = appStore.topMenus.find((el: RouteRecordRaw) => { + return key.includes(el?.name as string); + }); + + if (secParentFullSame) { + appStore.setCurrentTopMenu(secParentFullSame); + } else if (secParentLike) { + appStore.setCurrentTopMenu(secParentLike); } } diff --git a/frontend/src/components/pure/breadcrumb/index.vue b/frontend/src/components/pure/breadcrumb/index.vue deleted file mode 100644 index e4c9f13907..0000000000 --- a/frontend/src/components/pure/breadcrumb/index.vue +++ /dev/null @@ -1,35 +0,0 @@ - - - - - diff --git a/frontend/src/layout/page-layout.vue b/frontend/src/layout/page-layout.vue index caac23d8c1..b65017d39c 100644 --- a/frontend/src/layout/page-layout.vue +++ b/frontend/src/layout/page-layout.vue @@ -23,9 +23,10 @@ diff --git a/frontend/src/locale/en-US/index.ts b/frontend/src/locale/en-US/index.ts index cb39b9c7fa..e24c3d66be 100644 --- a/frontend/src/locale/en-US/index.ts +++ b/frontend/src/locale/en-US/index.ts @@ -1,33 +1,34 @@ import dayjsLocale from 'dayjs/locale/en'; import localeSettings from './settings'; import sys from './sys'; -import localeMessageBox from '@/components/pure/message-box/locale/en-US'; -import minder from '@/components/pure/minder-editor/locale/en-US'; -import localeLogin from '@/views/login/locale/en-US'; -import localeTable from '@/components/pure/ms-table/locale/en-US'; -import localeApiTest from '@/views/api-test/locale/en-US'; -import localeSystem from '@/views/system/locale/en-US'; -import baseLocale from '@/views/base/locale/en-US'; +const _Cmodules: any = import.meta.glob('../../components/**/locale/en-US.ts', { eager: true }); +const _Vmodules: any = import.meta.glob('../../views/**/locale/en-US.ts', { eager: true }); +let result = {}; +Object.keys(_Cmodules).forEach((key) => { + const defaultModule = _Cmodules[key as any].default; + if (!defaultModule) return; + result = { ...result, ...defaultModule }; +}); +Object.keys(_Vmodules).forEach((key) => { + const defaultModule = _Vmodules[key as any].default; + if (!defaultModule) return; + result = { ...result, ...defaultModule }; +}); export default { message: { 'menu.apiTest': 'Api Test', 'menu.settings': 'System Settings', 'menu.settings.system': 'System', 'menu.settings.organization': 'Organization', - 'menu.settings.usergroup': 'User Group', - 'menu.settings.user': 'User', - 'menu.settings.organizationAndProject': 'Org & Project', + 'menu.settings.system.usergroup': 'User Group', + 'menu.settings.system.user': 'User', + 'menu.settings.system.organizationAndProject': 'Org & Project', + 'menu.settings.system.resourcePool': 'Resource Pool', 'navbar.action.locale': 'Switch to English', ...sys, ...localeSettings, - ...localeMessageBox, - ...localeLogin, - ...minder, - ...localeTable, - ...localeApiTest, - ...localeSystem, - ...baseLocale, + ...result, }, dayjsLocale, dayjsLocaleName: 'en-US', diff --git a/frontend/src/locale/en-US/system.ts b/frontend/src/locale/en-US/system.ts deleted file mode 100644 index ff8b4c5632..0000000000 --- a/frontend/src/locale/en-US/system.ts +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/frontend/src/locale/zh-CN/index.ts b/frontend/src/locale/zh-CN/index.ts index 60000624d4..e8aa688b8f 100644 --- a/frontend/src/locale/zh-CN/index.ts +++ b/frontend/src/locale/zh-CN/index.ts @@ -1,34 +1,37 @@ import dayjsLocale from 'dayjs/locale/zh-cn'; import localeSettings from './settings'; import sys from './sys'; -import localeMessageBox from '@/components/pure/message-box/locale/zh-CN'; -import minder from '@/components/pure/minder-editor/locale/zh-CN'; -import localeLogin from '@/views/login/locale/zh-CN'; -import localeTable from '@/components/pure/ms-table/locale/zh-CN'; -import localeApiTest from '@/views/api-test/locale/zh-CN'; -import localeSystem from '@/views/system/locale/zh-CN'; -import baseLocale from '@/views/base/locale/zh-CN'; +const _Cmodules: any = import.meta.glob('../../components/**/locale/zh-CN.ts', { eager: true }); +const _Vmodules: any = import.meta.glob('../../views/**/locale/zh-CN.ts', { eager: true }); +let result = {}; +Object.keys(_Cmodules).forEach((key) => { + const defaultModule = _Cmodules[key as any].default; + if (!defaultModule) return; + result = { ...result, ...defaultModule }; +}); +Object.keys(_Vmodules).forEach((key) => { + const defaultModule = _Vmodules[key as any].default; + if (!defaultModule) return; + result = { ...result, ...defaultModule }; +}); export default { message: { 'menu.apiTest': '接口测试', 'menu.settings': '系统设置', 'menu.settings.system': '系统', 'menu.settings.organization': '组织', - 'menu.settings.user': '用户', - 'menu.settings.usergroup': '用户组', - 'menu.settings.organizationAndProject': '组织与项目', + 'menu.settings.system.user': '用户', + 'menu.settings.system.usergroup': '用户组', + 'menu.settings.system.organizationAndProject': '组织与项目', + 'menu.settings.system.resourcePool': '资源池', + 'menu.settings.system.resourcePoolDetail': '添加资源池', + 'menu.settings.system.resourcePoolEdit': '编辑资源池', 'menu.user': '个人中心', 'navbar.action.locale': '切换为中文', ...sys, ...localeSettings, - ...localeMessageBox, - ...localeLogin, - ...minder, - ...localeTable, - ...localeApiTest, - ...localeSystem, - ...baseLocale, + ...result, }, dayjsLocale, dayjsLocaleName: 'zh-CN', diff --git a/frontend/src/locale/zh-CN/system.ts b/frontend/src/locale/zh-CN/system.ts deleted file mode 100644 index ff8b4c5632..0000000000 --- a/frontend/src/locale/zh-CN/system.ts +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/frontend/src/main.ts b/frontend/src/main.ts index d68392654e..01078c2d69 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -2,7 +2,6 @@ import { createApp } from 'vue'; import FormCreate from '@form-create/arco-design'; import ArcoVueIcon from '@arco-design/web-vue/es/icon'; import SvgIcon from '@/components/pure/svg-icon/index.vue'; -import Breadcrumb from '@/components/pure/breadcrumb/index.vue'; import router from './router'; import store from './store'; import { setupI18n } from './locale'; @@ -25,7 +24,6 @@ async function bootstrap() { app.use(ArcoVueIcon); app.component('SvgIcon', SvgIcon); - app.component('Breadcrumb', Breadcrumb); app.use(router); app.use(directive); diff --git a/frontend/src/mock/user.ts b/frontend/src/mock/user.ts index cc8f8c3f45..5b9d05f6e1 100644 --- a/frontend/src/mock/user.ts +++ b/frontend/src/mock/user.ts @@ -88,7 +88,7 @@ setupMock({ path: 'user', name: 'settingSystemUser', meta: { - locale: 'menu.settings.user', + locale: 'menu.settings.system.user', roles: ['*'], isTopMenu: true, }, @@ -97,11 +97,28 @@ setupMock({ path: 'usergroup', name: 'settingSystemUsergroup', meta: { - locale: 'menu.settings.usergroup', + locale: 'menu.settings.system.usergroup', roles: ['*'], isTopMenu: true, }, }, + { + path: 'resourcePool', + name: 'settingSystemResourcePool', + meta: { + locale: 'menu.settings.system.resourcePool', + roles: ['*'], + isTopMenu: true, + }, + }, + { + path: 'resourcePoolDetail', + name: 'settingSystemResourcePoolDetail', + meta: { + locale: 'menu.settings.system.resourcePoolDetail', + roles: ['*'], + }, + }, ], }, ], diff --git a/frontend/src/router/routes/modules/system.ts b/frontend/src/router/routes/modules/system.ts index 344053f5fe..f337df1970 100644 --- a/frontend/src/router/routes/modules/system.ts +++ b/frontend/src/router/routes/modules/system.ts @@ -27,7 +27,7 @@ const System: AppRouteRecordRaw = { name: 'settingSystemUser', component: () => import('@/views/system/user/index.vue'), meta: { - locale: 'menu.settings.user', + locale: 'menu.settings.system.user', roles: ['*'], isTopMenu: true, }, @@ -37,11 +37,46 @@ const System: AppRouteRecordRaw = { name: 'settingSystemUsergroup', component: () => import('@/views/system/usergroup/index.vue'), meta: { - locale: 'menu.settings.usergroup', + locale: 'menu.settings.system.usergroup', roles: ['*'], isTopMenu: true, }, }, + { + path: 'resourcePool', + name: 'settingSystemResourcePool', + component: () => import('@/views/system/resourcePool/index.vue'), + meta: { + locale: 'menu.settings.system.resourcePool', + roles: ['*'], + isTopMenu: true, + breadcrumbs: [ + { + name: 'settingSystemResourcePool', + locale: 'menu.settings.system.resourcePool', + }, + ], + }, + }, + { + path: 'resourcePoolDetail', + name: 'settingSystemResourcePoolDetail', + component: () => import('@/views/system/resourcePool/detail.vue'), + meta: { + locale: 'menu.settings.system.resourcePoolDetail', + roles: ['*'], + breadcrumbs: [ + { + name: 'settingSystemResourcePool', + locale: 'menu.settings.system.resourcePool', + }, + { + name: 'settingSystemResourcePoolDetail', + locale: 'menu.settings.system.resourcePoolDetail', + }, + ], + }, + }, ], }, ], diff --git a/frontend/src/router/routes/types.ts b/frontend/src/router/routes/types.ts index 6b8e8d7783..f924e3b6b6 100644 --- a/frontend/src/router/routes/types.ts +++ b/frontend/src/router/routes/types.ts @@ -1,11 +1,26 @@ import { defineComponent } from 'vue'; -import type { RouteMeta, NavigationGuard } from 'vue-router'; +import type { NavigationGuard } from 'vue-router'; +import type { BreadcrumbItem } from '@/components/bussiness/ms-breadcrumb/types'; export type Component = | ReturnType | (() => Promise) | (() => Promise); +export interface RouteMeta { + roles?: string[]; // 角色数组 + requiresAuth?: boolean; // 是否需要权限,默认需要 + icon?: string; // 菜单icon + locale?: string; // 国际化语言单词 + hideInMenu?: boolean; // 此路由不在菜单展示 + hideChildrenInMenu?: boolean; // 子路由不展示在菜单 + activeMenu?: string; // 激活状态 + order?: number; // 排序权重 + noAffix?: boolean; // tab展示设置,设置为true则不在tab列表展示激活页面的tab + ignoreCache?: boolean; // 缓存设置,true则不缓存 + isTopMenu?: boolean; // 是否为顶部菜单 + breadcrumbs?: BreadcrumbItem[]; // 面包屑 +} export interface AppRouteRecordRaw { path: string; name?: string | symbol; diff --git a/frontend/src/store/modules/app/index.ts b/frontend/src/store/modules/app/index.ts index 8f345218c7..ca2a718c21 100644 --- a/frontend/src/store/modules/app/index.ts +++ b/frontend/src/store/modules/app/index.ts @@ -5,9 +5,10 @@ import { getMenuList } from '@/api/modules/user'; import { useI18n } from '@/hooks/useI18n'; import { cloneDeep } from 'lodash-es'; -import type { AppState, BreadcrumbItem } from './types'; import type { NotificationReturn } from '@arco-design/web-vue/es/notification/interface'; import type { RouteRecordNormalized, RouteRecordRaw } from 'vue-router'; +import type { AppState } from './types'; +import type { BreadcrumbItem } from '@/components/bussiness/ms-breadcrumb/types'; const useAppStore = defineStore('app', { state: (): AppState => ({ @@ -134,7 +135,7 @@ const useAppStore = defineStore('app', { this.topMenus = menus ? [...menus] : []; }, /** - * 设置顶部菜单组 + * 设置激活的顶部菜单 */ setCurrentTopMenu(menu: RouteRecordRaw) { this.currentTopMenu = cloneDeep(menu); diff --git a/frontend/src/store/modules/app/types.ts b/frontend/src/store/modules/app/types.ts index 36dfd7fd5f..0dc975b682 100644 --- a/frontend/src/store/modules/app/types.ts +++ b/frontend/src/store/modules/app/types.ts @@ -1,9 +1,5 @@ -import type { RouteRecordNormalized, RouteRecordRaw, RouteRecordName } from 'vue-router'; - -export interface BreadcrumbItem { - name: RouteRecordName; - locale: string; -} +import type { RouteRecordNormalized, RouteRecordRaw } from 'vue-router'; +import type { BreadcrumbItem } from '@/components/bussiness/ms-breadcrumb/types'; export interface AppState { theme: string; diff --git a/frontend/src/views/base/locale/en-US.js b/frontend/src/views/base/locale/en-US.ts similarity index 100% rename from frontend/src/views/base/locale/en-US.js rename to frontend/src/views/base/locale/en-US.ts diff --git a/frontend/src/views/base/locale/zh-CN.js b/frontend/src/views/base/locale/zh-CN.ts similarity index 100% rename from frontend/src/views/base/locale/zh-CN.js rename to frontend/src/views/base/locale/zh-CN.ts diff --git a/frontend/src/views/system/user/index.vue b/frontend/src/views/system/user/index.vue index 54b6a07292..d077f3dbff 100644 --- a/frontend/src/views/system/user/index.vue +++ b/frontend/src/views/system/user/index.vue @@ -1,5 +1,5 @@