mirror of
				https://github.com/soybeanjs/soybean-admin.git
				synced 2025-11-04 07:43:42 +08:00 
			
		
		
		
	feat(projects): 增加i18n支持翻译菜单,tab,title
This commit is contained in:
		
							
								
								
									
										12
									
								
								src/App.vue
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/App.vue
									
									
									
									
									
								
							@@ -13,14 +13,26 @@
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { watch } from 'vue';
 | 
			
		||||
import { useRoute } from 'vue-router';
 | 
			
		||||
import { dateZhCN, zhCN } from 'naive-ui';
 | 
			
		||||
import { useI18n } from 'vue-i18n';
 | 
			
		||||
import { subscribeStore, useThemeStore } from '@/store';
 | 
			
		||||
import { useGlobalEvents } from '@/composables';
 | 
			
		||||
 | 
			
		||||
const theme = useThemeStore();
 | 
			
		||||
const { locale, t } = useI18n();
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
 | 
			
		||||
subscribeStore();
 | 
			
		||||
useGlobalEvents();
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  () => locale.value,
 | 
			
		||||
  () => {
 | 
			
		||||
    document.title = route.meta.i18nTitle ? t(route.meta.i18nTitle) : route.meta.title;
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
              v-if="theme.header.crumb.showIcon"
 | 
			
		||||
              class="inline-block align-text-bottom mr-4px text-16px"
 | 
			
		||||
            />
 | 
			
		||||
            <span>{{ breadcrumb.label }}</span>
 | 
			
		||||
            <span>{{ breadcrumb.i18nTitle ? t(breadcrumb.i18nTitle) : breadcrumb.label }}</span>
 | 
			
		||||
          </span>
 | 
			
		||||
        </n-dropdown>
 | 
			
		||||
        <template v-else>
 | 
			
		||||
@@ -19,7 +19,9 @@
 | 
			
		||||
            class="inline-block align-text-bottom mr-4px text-16px"
 | 
			
		||||
            :class="{ 'text-#BBBBBB': theme.header.inverted }"
 | 
			
		||||
          />
 | 
			
		||||
          <span :class="{ 'text-#BBBBBB': theme.header.inverted }">{{ breadcrumb.label }}</span>
 | 
			
		||||
          <span :class="{ 'text-#BBBBBB': theme.header.inverted }">{{
 | 
			
		||||
            breadcrumb.i18nTitle ? t(breadcrumb.i18nTitle) : breadcrumb.label
 | 
			
		||||
          }}</span>
 | 
			
		||||
        </template>
 | 
			
		||||
      </n-breadcrumb-item>
 | 
			
		||||
    </template>
 | 
			
		||||
@@ -33,6 +35,7 @@ import { routePath } from '@/router';
 | 
			
		||||
import { useRouteStore, useThemeStore } from '@/store';
 | 
			
		||||
import { useRouterPush } from '@/composables';
 | 
			
		||||
import { getBreadcrumbByRouteKey } from '@/utils';
 | 
			
		||||
import { t } from '@/locales';
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'GlobalBreadcrumb' });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ import { useRoute } from 'vue-router';
 | 
			
		||||
import type { MenuOption } from 'naive-ui';
 | 
			
		||||
import { useRouteStore, useThemeStore } from '@/store';
 | 
			
		||||
import { useRouterPush } from '@/composables';
 | 
			
		||||
import { translateMenuLabel } from '@/utils';
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'HeaderMenu' });
 | 
			
		||||
 | 
			
		||||
@@ -28,7 +29,7 @@ const routeStore = useRouteStore();
 | 
			
		||||
const theme = useThemeStore();
 | 
			
		||||
const { routerPush } = useRouterPush();
 | 
			
		||||
 | 
			
		||||
const menus = computed(() => routeStore.menus as App.GlobalMenuOption[]);
 | 
			
		||||
const menus = computed(() => translateMenuLabel(routeStore.menus as App.GlobalMenuOption[]));
 | 
			
		||||
const activeKey = computed(() => (route.meta?.activeMenu ? route.meta.activeMenu : route.name) as string);
 | 
			
		||||
 | 
			
		||||
function handleUpdateMenu(_key: string, item: MenuOption) {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import ThemeMode from './theme-mode.vue';
 | 
			
		||||
import UserAvatar from './user-avatar.vue';
 | 
			
		||||
import SystemMessage from './system-message.vue';
 | 
			
		||||
import SettingButton from './setting-button.vue';
 | 
			
		||||
import ToggleLang from './toggle-lang.vue';
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
  MenuCollapse,
 | 
			
		||||
@@ -17,5 +18,6 @@ export {
 | 
			
		||||
  ThemeMode,
 | 
			
		||||
  UserAvatar,
 | 
			
		||||
  SystemMessage,
 | 
			
		||||
  SettingButton
 | 
			
		||||
  SettingButton,
 | 
			
		||||
  ToggleLang
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								src/layouts/common/global-header/components/toggle-lang.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/layouts/common/global-header/components/toggle-lang.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <hover-container class="w-40px h-full">
 | 
			
		||||
    <n-dropdown :options="options" trigger="hover" :value="language" @select="handleSelect">
 | 
			
		||||
      <icon-cil:language class="text-18px outline-transparent" />
 | 
			
		||||
    </n-dropdown>
 | 
			
		||||
  </hover-container>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { ref } from 'vue';
 | 
			
		||||
import { useI18n } from 'vue-i18n';
 | 
			
		||||
import { localStg } from '@/utils';
 | 
			
		||||
 | 
			
		||||
const { locale } = useI18n();
 | 
			
		||||
 | 
			
		||||
const language = ref<I18nType.langType>(localStg.get('lang') || 'zh-CN');
 | 
			
		||||
const options = [
 | 
			
		||||
  {
 | 
			
		||||
    label: '中文',
 | 
			
		||||
    key: 'zh-CN'
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    label: 'English',
 | 
			
		||||
    key: 'en'
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
const handleSelect = (key: string) => {
 | 
			
		||||
  language.value = key as I18nType.langType;
 | 
			
		||||
  locale.value = key;
 | 
			
		||||
  localStg.set('lang', key as I18nType.langType);
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
@@ -11,6 +11,7 @@
 | 
			
		||||
      <github-site />
 | 
			
		||||
      <full-screen />
 | 
			
		||||
      <theme-mode />
 | 
			
		||||
      <toggle-lang />
 | 
			
		||||
      <system-message />
 | 
			
		||||
      <setting-button v-if="showButton" />
 | 
			
		||||
      <user-avatar />
 | 
			
		||||
@@ -32,7 +33,8 @@ import {
 | 
			
		||||
  SettingButton,
 | 
			
		||||
  SystemMessage,
 | 
			
		||||
  ThemeMode,
 | 
			
		||||
  UserAvatar
 | 
			
		||||
  UserAvatar,
 | 
			
		||||
  ToggleLang
 | 
			
		||||
} from './components';
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'GlobalHeader' });
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <router-link :to="routeHomePath" class="flex-center w-full nowrap-hidden">
 | 
			
		||||
    <system-logo class="text-32px text-primary" />
 | 
			
		||||
    <h2
 | 
			
		||||
      v-show="showTitle"
 | 
			
		||||
      class="pl-8px text-16px font-bold text-primary transition duration-300 ease-in-out"
 | 
			
		||||
      @click="toggleLocal"
 | 
			
		||||
    >
 | 
			
		||||
    <h2 v-show="showTitle" class="pl-8px text-16px font-bold text-primary transition duration-300 ease-in-out">
 | 
			
		||||
      {{ t('message.system.title') }}
 | 
			
		||||
    </h2>
 | 
			
		||||
  </router-link>
 | 
			
		||||
@@ -13,7 +9,7 @@
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { routePath } from '@/router';
 | 
			
		||||
import { t, setLocale } from '@/locales';
 | 
			
		||||
import { t } from '@/locales';
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'GlobalLogo' });
 | 
			
		||||
 | 
			
		||||
@@ -25,12 +21,6 @@ interface Props {
 | 
			
		||||
defineProps<Props>();
 | 
			
		||||
 | 
			
		||||
const routeHomePath = routePath('root');
 | 
			
		||||
 | 
			
		||||
let flag = true;
 | 
			
		||||
function toggleLocal() {
 | 
			
		||||
  flag = !flag;
 | 
			
		||||
  setLocale(flag ? 'en' : 'zh-CN');
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style scoped></style>
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,9 @@ import { useRoute } from 'vue-router';
 | 
			
		||||
import { useAppStore, useRouteStore, useThemeStore } from '@/store';
 | 
			
		||||
import { useRouterPush } from '@/composables';
 | 
			
		||||
import { useBoolean } from '@/hooks';
 | 
			
		||||
import { translateMenuLabel } from '@/utils';
 | 
			
		||||
import { GlobalLogo } from '@/layouts/common';
 | 
			
		||||
import { t } from '@/locales';
 | 
			
		||||
import { MixMenuCollapse, MixMenuDetail, MixMenuDrawer } from './components';
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'VerticalMixSider' });
 | 
			
		||||
@@ -45,13 +47,13 @@ function setActiveParentRouteName(routeName: string) {
 | 
			
		||||
 | 
			
		||||
const firstDegreeMenus = computed(() =>
 | 
			
		||||
  routeStore.menus.map(item => {
 | 
			
		||||
    const { routeName, label } = item;
 | 
			
		||||
    const { routeName, label, i18nTitle } = item;
 | 
			
		||||
    const icon = item?.icon;
 | 
			
		||||
    const hasChildren = Boolean(item.children && item.children.length);
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      routeName,
 | 
			
		||||
      label,
 | 
			
		||||
      label: i18nTitle ? t(i18nTitle) : label,
 | 
			
		||||
      icon,
 | 
			
		||||
      hasChildren
 | 
			
		||||
    };
 | 
			
		||||
@@ -88,7 +90,7 @@ const activeChildMenus = computed(() => {
 | 
			
		||||
  routeStore.menus.some(item => {
 | 
			
		||||
    const flag = item.routeName === activeParentRouteName.value && Boolean(item.children?.length);
 | 
			
		||||
    if (flag) {
 | 
			
		||||
      menus.push(...(item.children || []));
 | 
			
		||||
      menus.push(...translateMenuLabel((item.children || []) as App.GlobalMenuOption[]));
 | 
			
		||||
    }
 | 
			
		||||
    return flag;
 | 
			
		||||
  });
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ import { useRoute } from 'vue-router';
 | 
			
		||||
import type { MenuOption } from 'naive-ui';
 | 
			
		||||
import { useAppStore, useRouteStore, useThemeStore } from '@/store';
 | 
			
		||||
import { useRouterPush } from '@/composables';
 | 
			
		||||
import { getActiveKeyPathsOfMenus } from '@/utils';
 | 
			
		||||
import { getActiveKeyPathsOfMenus, translateMenuLabel } from '@/utils';
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'VerticalMenu' });
 | 
			
		||||
 | 
			
		||||
@@ -31,7 +31,7 @@ const theme = useThemeStore();
 | 
			
		||||
const routeStore = useRouteStore();
 | 
			
		||||
const { routerPush } = useRouterPush();
 | 
			
		||||
 | 
			
		||||
const menus = computed(() => routeStore.menus as App.GlobalMenuOption[]);
 | 
			
		||||
const menus = computed(() => translateMenuLabel(routeStore.menus as App.GlobalMenuOption[]));
 | 
			
		||||
 | 
			
		||||
const activeKey = computed(() => (route.meta?.activeMenu ? route.meta.activeMenu : route.name) as string);
 | 
			
		||||
const expandedKeys = ref<string[]>([]);
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@
 | 
			
		||||
          class="inline-block align-text-bottom text-16px"
 | 
			
		||||
        />
 | 
			
		||||
      </template>
 | 
			
		||||
      {{ item.meta.title }}
 | 
			
		||||
      {{ item.meta.i18nTitle ? t(item.meta.i18nTitle) : item.meta.title }}
 | 
			
		||||
    </AdminTab>
 | 
			
		||||
  </div>
 | 
			
		||||
  <context-menu
 | 
			
		||||
@@ -36,6 +36,7 @@
 | 
			
		||||
import { computed, nextTick, reactive, ref, watch } from 'vue';
 | 
			
		||||
import { AdminTab } from '@soybeanjs/vue-materials';
 | 
			
		||||
import { useTabStore, useThemeStore } from '@/store';
 | 
			
		||||
import { t } from '@/locales';
 | 
			
		||||
import { ContextMenu } from './components';
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'TabDetail' });
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,14 @@
 | 
			
		||||
import type { App } from 'vue';
 | 
			
		||||
import { createI18n } from 'vue-i18n';
 | 
			
		||||
import { localStg } from '@/utils';
 | 
			
		||||
import messages from './lang';
 | 
			
		||||
import type { LocaleKey } from './lang';
 | 
			
		||||
 | 
			
		||||
const i18n = createI18n({
 | 
			
		||||
  locale: 'zh-CN',
 | 
			
		||||
  locale: localStg.get('lang') || 'zh-CN',
 | 
			
		||||
  fallbackLocale: 'en',
 | 
			
		||||
  messages
 | 
			
		||||
  messages,
 | 
			
		||||
  legacy: false
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export function setupI18n(app: App) {
 | 
			
		||||
@@ -18,5 +20,5 @@ export function t(key: string) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function setLocale(locale: LocaleKey) {
 | 
			
		||||
  i18n.global.locale = locale;
 | 
			
		||||
  i18n.global.locale.value = locale;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import type { Router } from 'vue-router';
 | 
			
		||||
import { useTitle } from '@vueuse/core';
 | 
			
		||||
import { t } from '@/locales';
 | 
			
		||||
import { createPermissionGuard } from './permission';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -15,7 +16,7 @@ export function createRouterGuard(router: Router) {
 | 
			
		||||
  });
 | 
			
		||||
  router.afterEach(to => {
 | 
			
		||||
    // 设置document title
 | 
			
		||||
    useTitle(to.meta.title);
 | 
			
		||||
    useTitle(to.meta.i18nTitle ? t(to.meta.i18nTitle) : to.meta.title);
 | 
			
		||||
    // 结束 loadingBar
 | 
			
		||||
    window.$loadingBar?.finish();
 | 
			
		||||
  });
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,8 @@ const dashboard: AuthRoute.Route = {
 | 
			
		||||
      meta: {
 | 
			
		||||
        title: '分析页',
 | 
			
		||||
        requiresAuth: true,
 | 
			
		||||
        icon: 'icon-park-outline:analysis'
 | 
			
		||||
        icon: 'icon-park-outline:analysis',
 | 
			
		||||
        i18nTitle: 'message.routes.dashboard.analysis'
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -20,14 +21,16 @@ const dashboard: AuthRoute.Route = {
 | 
			
		||||
      meta: {
 | 
			
		||||
        title: '工作台',
 | 
			
		||||
        requiresAuth: true,
 | 
			
		||||
        icon: 'icon-park-outline:workbench'
 | 
			
		||||
        icon: 'icon-park-outline:workbench',
 | 
			
		||||
        i18nTitle: 'message.routes.dashboard.workbench'
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  meta: {
 | 
			
		||||
    title: '仪表盘',
 | 
			
		||||
    icon: 'mdi:monitor-dashboard',
 | 
			
		||||
    order: 1
 | 
			
		||||
    order: 1,
 | 
			
		||||
    i18nTitle: 'message.routes.dashboard.dashboard'
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,6 @@ import { localStg } from '@/utils';
 | 
			
		||||
 */
 | 
			
		||||
export function getTabRouteByVueRoute(route: RouteRecordNormalized | RouteLocationNormalizedLoaded) {
 | 
			
		||||
  const fullPath = hasFullPath(route) ? route.fullPath : route.path;
 | 
			
		||||
 | 
			
		||||
  const tabRoute: App.GlobalTabRoute = {
 | 
			
		||||
    name: route.name,
 | 
			
		||||
    fullPath,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								src/typings/route.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/typings/route.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -31,6 +31,8 @@ declare namespace AuthRoute {
 | 
			
		||||
  interface RouteMeta<K extends AuthRoute.RoutePath> {
 | 
			
		||||
    /** 路由标题(可用来作document.title或者菜单的名称) */
 | 
			
		||||
    title: string;
 | 
			
		||||
    /** 用来支持多国语言 如果i18nTitle和title同时存在优先使用i18nTitle */
 | 
			
		||||
    i18nTitle?: string;
 | 
			
		||||
    /** 路由的动态路径(需要动态路径的页面需要将path添加进范型参数) */
 | 
			
		||||
    dynamicPath?: AuthRouteUtils.GetDynamicPath<K>;
 | 
			
		||||
    /** 作为单级路由的父级路由布局组件 */
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								src/typings/storage.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/typings/storage.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -18,5 +18,7 @@ declare namespace StorageInterface {
 | 
			
		||||
    themeSettings: Theme.Setting;
 | 
			
		||||
    /** 多页签路由信息 */
 | 
			
		||||
    multiTabRoutes: App.GlobalTabRoute[];
 | 
			
		||||
    /** 本地语言缓存 */
 | 
			
		||||
    lang: I18nType.langType;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								src/typings/system.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								src/typings/system.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -242,6 +242,7 @@ declare namespace App {
 | 
			
		||||
    routePath: string;
 | 
			
		||||
    icon?: () => import('vue').VNodeChild;
 | 
			
		||||
    children?: GlobalMenuOption[];
 | 
			
		||||
    i18nTitle?: string;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /** 面包屑 */
 | 
			
		||||
@@ -252,6 +253,7 @@ declare namespace App {
 | 
			
		||||
    routeName: string;
 | 
			
		||||
    hasChildren: boolean;
 | 
			
		||||
    icon?: import('vue').Component;
 | 
			
		||||
    i18nTitle?: string;
 | 
			
		||||
    options?: import('naive-ui/es/dropdown/src/interface').DropdownMixedOption[];
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@@ -300,6 +302,7 @@ declare namespace App {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare namespace I18nType {
 | 
			
		||||
  type langType = 'en' | 'zh-CN';
 | 
			
		||||
  interface Schema {
 | 
			
		||||
    system: {
 | 
			
		||||
      title: string;
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,8 @@ function transformBreadcrumbMenuToBreadcrumb(menu: App.GlobalMenuOption, rootPat
 | 
			
		||||
    label: menu.label as string,
 | 
			
		||||
    routeName: menu.routeName,
 | 
			
		||||
    disabled: menu.routePath === rootPath,
 | 
			
		||||
    hasChildren
 | 
			
		||||
    hasChildren,
 | 
			
		||||
    i18nTitle: menu.i18nTitle
 | 
			
		||||
  };
 | 
			
		||||
  if (menu.icon) {
 | 
			
		||||
    breadcrumb.icon = menu.icon;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import { useIconRender } from '@/composables';
 | 
			
		||||
import { t } from '@/locales';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 将权限路由转换成菜单
 | 
			
		||||
@@ -18,7 +19,8 @@ export function transformAuthRouteToMenu(routes: AuthRoute.Route[]): App.GlobalM
 | 
			
		||||
        key: routeName,
 | 
			
		||||
        label: meta.title,
 | 
			
		||||
        routeName,
 | 
			
		||||
        routePath: path
 | 
			
		||||
        routePath: path,
 | 
			
		||||
        i18nTitle: meta.i18nTitle
 | 
			
		||||
      },
 | 
			
		||||
      icon: meta.icon,
 | 
			
		||||
      localIcon: meta.localIcon,
 | 
			
		||||
@@ -33,6 +35,28 @@ export function transformAuthRouteToMenu(routes: AuthRoute.Route[]): App.GlobalM
 | 
			
		||||
  return globalMenu;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 翻译菜单
 | 
			
		||||
 * @param menus
 | 
			
		||||
 * @returns
 | 
			
		||||
 */
 | 
			
		||||
export function translateMenuLabel(menus: App.GlobalMenuOption[]): App.GlobalMenuOption[] {
 | 
			
		||||
  const globalMenu: App.GlobalMenuOption[] = [];
 | 
			
		||||
  menus.forEach(menu => {
 | 
			
		||||
    let menuChildren: App.GlobalMenuOption[] | undefined;
 | 
			
		||||
    if (menu.children && menu.children.length > 0) {
 | 
			
		||||
      menuChildren = translateMenuLabel(menu.children);
 | 
			
		||||
    }
 | 
			
		||||
    const menuItem: App.GlobalMenuOption = {
 | 
			
		||||
      ...menu,
 | 
			
		||||
      children: menuChildren,
 | 
			
		||||
      label: menu.i18nTitle ? t(menu.i18nTitle) : menu.label
 | 
			
		||||
    };
 | 
			
		||||
    globalMenu.push(menuItem);
 | 
			
		||||
  });
 | 
			
		||||
  return globalMenu;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 获取当前路由所在菜单数据的paths
 | 
			
		||||
 * @param activeKey - 当前路由的key
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user