Trim locale bundles and filtered menus
This commit is contained in:
@@ -8,54 +8,54 @@ import { ref } from 'vue';
|
||||
|
||||
import {
|
||||
$t,
|
||||
setupI18n as coreSetup,
|
||||
getSystemLanguage,
|
||||
i18n,
|
||||
loadLocalesMapFromDir,
|
||||
setupI18n as coreSetup,
|
||||
} from '@vben/locales';
|
||||
import { preferences } from '@vben/preferences';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import 'dayjs/locale/zh-cn';
|
||||
import defaultLocale from 'element-plus/es/locale/lang/zh-cn';
|
||||
|
||||
const APP_LOCALE: SupportedLanguagesType = 'zh-CN';
|
||||
const elementLocale = ref<Language>(defaultLocale);
|
||||
|
||||
const baseModules = import.meta.glob([
|
||||
'./langs/*/about.json',
|
||||
'./langs/*/authentication.json',
|
||||
'./langs/*/common.json',
|
||||
'./langs/*/menu-title.json',
|
||||
'./langs/*/page.json',
|
||||
'./langs/*/ui.json',
|
||||
'./langs/zh-CN/about.json',
|
||||
'./langs/zh-CN/authentication.json',
|
||||
'./langs/zh-CN/common.json',
|
||||
'./langs/zh-CN/menu-title.json',
|
||||
'./langs/zh-CN/page.json',
|
||||
'./langs/zh-CN/ui.json',
|
||||
]);
|
||||
|
||||
const businessModules = import.meta.glob([
|
||||
'./langs/*/account-settings.json',
|
||||
'./langs/*/api-token.json',
|
||||
'./langs/*/ai-platform.json',
|
||||
'./langs/*/announcement.json',
|
||||
'./langs/*/application.json',
|
||||
'./langs/*/chat.json',
|
||||
'./langs/*/dept.json',
|
||||
'./langs/*/dashboard-design.json',
|
||||
'./langs/*/dingtalk-sync.json',
|
||||
'./langs/*/dict.json',
|
||||
'./langs/*/feishu-sync.json',
|
||||
'./langs/*/file-manager.json',
|
||||
'./langs/*/loginLog.json',
|
||||
'./langs/*/menu.json',
|
||||
'./langs/*/message.json',
|
||||
'./langs/*/org-chart.json',
|
||||
'./langs/*/page-manager.json',
|
||||
'./langs/*/permission.json',
|
||||
'./langs/*/post.json',
|
||||
'./langs/*/role.json',
|
||||
'./langs/*/system-config.json',
|
||||
'./langs/*/system.json',
|
||||
'./langs/*/ui-config.json',
|
||||
'./langs/*/user-avatar.json',
|
||||
'./langs/*/user.json',
|
||||
'./langs/*/wecom-sync.json',
|
||||
'./langs/zh-CN/account-settings.json',
|
||||
'./langs/zh-CN/api-token.json',
|
||||
'./langs/zh-CN/ai-platform.json',
|
||||
'./langs/zh-CN/announcement.json',
|
||||
'./langs/zh-CN/application.json',
|
||||
'./langs/zh-CN/chat.json',
|
||||
'./langs/zh-CN/dept.json',
|
||||
'./langs/zh-CN/dashboard-design.json',
|
||||
'./langs/zh-CN/dingtalk-sync.json',
|
||||
'./langs/zh-CN/dict.json',
|
||||
'./langs/zh-CN/feishu-sync.json',
|
||||
'./langs/zh-CN/file-manager.json',
|
||||
'./langs/zh-CN/loginLog.json',
|
||||
'./langs/zh-CN/menu.json',
|
||||
'./langs/zh-CN/message.json',
|
||||
'./langs/zh-CN/org-chart.json',
|
||||
'./langs/zh-CN/page-manager.json',
|
||||
'./langs/zh-CN/permission.json',
|
||||
'./langs/zh-CN/post.json',
|
||||
'./langs/zh-CN/role.json',
|
||||
'./langs/zh-CN/system-config.json',
|
||||
'./langs/zh-CN/system.json',
|
||||
'./langs/zh-CN/ui-config.json',
|
||||
'./langs/zh-CN/user-avatar.json',
|
||||
'./langs/zh-CN/user.json',
|
||||
'./langs/zh-CN/wecom-sync.json',
|
||||
]);
|
||||
|
||||
const baseLocalesMap = loadLocalesMapFromDir(
|
||||
@@ -70,11 +70,6 @@ const businessLocalesMap = loadLocalesMapFromDir(
|
||||
|
||||
const loadedBusinessLocales = new Set<SupportedLanguagesType>();
|
||||
|
||||
/**
|
||||
* 加载应用特有的语言包
|
||||
* 这里也可以改造为从服务端获取翻译数据
|
||||
* @param lang
|
||||
*/
|
||||
async function loadMessages(lang: SupportedLanguagesType) {
|
||||
const [appLocaleMessages] = await Promise.all([
|
||||
baseLocalesMap[lang]?.(),
|
||||
@@ -84,8 +79,7 @@ async function loadMessages(lang: SupportedLanguagesType) {
|
||||
}
|
||||
|
||||
function getActualLocale(): SupportedLanguagesType {
|
||||
const locale = preferences.app.locale;
|
||||
return locale === 'auto' ? getSystemLanguage() : locale;
|
||||
return APP_LOCALE;
|
||||
}
|
||||
|
||||
async function loadBusinessMessages(lang = getActualLocale()) {
|
||||
@@ -100,76 +94,25 @@ async function loadBusinessMessages(lang = getActualLocale()) {
|
||||
loadedBusinessLocales.add(lang);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载第三方组件库的语言包
|
||||
* @param lang
|
||||
*/
|
||||
async function loadThirdPartyMessage(lang: SupportedLanguagesType) {
|
||||
await Promise.all([loadElementLocale(lang), loadDayjsLocale(lang)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载dayjs的语言包
|
||||
* @param lang
|
||||
*/
|
||||
async function loadDayjsLocale(lang: SupportedLanguagesType) {
|
||||
let locale;
|
||||
switch (lang) {
|
||||
case 'en-US': {
|
||||
locale = await import('dayjs/locale/en');
|
||||
break;
|
||||
}
|
||||
case 'zh-CN': {
|
||||
locale = await import('dayjs/locale/zh-cn');
|
||||
break;
|
||||
}
|
||||
case 'zh-TW': {
|
||||
locale = await import('dayjs/locale/zh-tw');
|
||||
break;
|
||||
}
|
||||
// 默认使用英语
|
||||
default: {
|
||||
locale = await import('dayjs/locale/en');
|
||||
}
|
||||
}
|
||||
if (locale) {
|
||||
dayjs.locale(locale);
|
||||
} else {
|
||||
console.error(`Failed to load dayjs locale for ${lang}`);
|
||||
if (lang === APP_LOCALE) {
|
||||
dayjs.locale('zh-cn');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载element-plus的语言包
|
||||
* @param lang
|
||||
*/
|
||||
async function loadElementLocale(lang: SupportedLanguagesType) {
|
||||
switch (lang) {
|
||||
case 'en-US': {
|
||||
elementLocale.value = (
|
||||
await import('element-plus/es/locale/lang/en')
|
||||
).default;
|
||||
break;
|
||||
}
|
||||
case 'zh-CN': {
|
||||
if (lang === APP_LOCALE) {
|
||||
elementLocale.value = defaultLocale;
|
||||
break;
|
||||
}
|
||||
case 'zh-TW': {
|
||||
elementLocale.value = (
|
||||
await import('element-plus/es/locale/lang/zh-tw')
|
||||
).default;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function setupI18n(app: App, options: LocaleSetupOptions = {}) {
|
||||
// 如果是跟随系统,获取系统语言
|
||||
const actualLocale = getActualLocale();
|
||||
|
||||
await coreSetup(app, {
|
||||
defaultLocale: actualLocale,
|
||||
defaultLocale: getActualLocale(),
|
||||
loadMessages,
|
||||
missingWarn: !import.meta.env.PROD,
|
||||
...options,
|
||||
|
||||
@@ -13,6 +13,67 @@ import { useAppContextStore } from '#/store/app-context';
|
||||
|
||||
const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue');
|
||||
|
||||
function normalizeViewPath(path: string) {
|
||||
const normalizedPath = path.replace(/^(\.\/|\.\.\/)+/, '');
|
||||
const viewPath = normalizedPath.startsWith('/')
|
||||
? normalizedPath
|
||||
: `/${normalizedPath}`;
|
||||
return viewPath.replace(/^\/views/, '');
|
||||
}
|
||||
|
||||
function hasPageComponent(
|
||||
component: string | undefined,
|
||||
pageMap: ComponentRecordType,
|
||||
layoutMap: ComponentRecordType,
|
||||
) {
|
||||
if (!component) return true;
|
||||
if (layoutMap[component]) return true;
|
||||
|
||||
const normalizedPath = normalizeViewPath(component);
|
||||
const pageKey = normalizedPath.endsWith('.vue')
|
||||
? normalizedPath
|
||||
: `${normalizedPath}.vue`;
|
||||
|
||||
return Boolean(pageMap[pageKey]);
|
||||
}
|
||||
|
||||
function normalizePageMap(pageMap: ComponentRecordType) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(pageMap).map(([key, value]) => [
|
||||
normalizeViewPath(key),
|
||||
value,
|
||||
]),
|
||||
) as ComponentRecordType;
|
||||
}
|
||||
|
||||
function filterAvailableRoutes<T extends { children?: T[]; component?: string }>(
|
||||
routes: T[],
|
||||
pageMap: ComponentRecordType,
|
||||
layoutMap: ComponentRecordType,
|
||||
): T[] {
|
||||
return routes
|
||||
.map((route) => {
|
||||
const children = route.children
|
||||
? filterAvailableRoutes(route.children, pageMap, layoutMap)
|
||||
: undefined;
|
||||
|
||||
if (!route.component && route.children && !children?.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!hasPageComponent(route.component, pageMap, layoutMap)) {
|
||||
if (!children?.length) return undefined;
|
||||
|
||||
const groupRoute = { ...route, children };
|
||||
delete groupRoute.component;
|
||||
return groupRoute;
|
||||
}
|
||||
|
||||
return children ? { ...route, children } : route;
|
||||
})
|
||||
.filter(Boolean) as T[];
|
||||
}
|
||||
|
||||
async function generateAccess(options: GenerateMenuAndRoutesOptions) {
|
||||
const pageMap: ComponentRecordType = import.meta.glob([
|
||||
'../views/_core/agent-chat/**/*.vue',
|
||||
@@ -54,7 +115,11 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
|
||||
const layoutMap: ComponentRecordType = {
|
||||
BasicLayout,
|
||||
IFrameView,
|
||||
Layout: BasicLayout,
|
||||
'Layout.vue': BasicLayout,
|
||||
'/Layout.vue': BasicLayout,
|
||||
};
|
||||
const routePageMap = normalizePageMap(pageMap);
|
||||
|
||||
return await generateAccessible(preferences.app.accessMode, {
|
||||
...options,
|
||||
@@ -72,11 +137,13 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
|
||||
|
||||
// 如果是子应用模式,传递 appCode 过滤应用专属菜单
|
||||
if (appContextStore.appCode) {
|
||||
return await getAllMenusApi(appContextStore.appCode);
|
||||
const menus = await getAllMenusApi(appContextStore.appCode);
|
||||
return filterAvailableRoutes(menus, routePageMap, layoutMap);
|
||||
}
|
||||
|
||||
// 主应用模式,不传参数
|
||||
return await getAllMenusApi();
|
||||
const menus = await getAllMenusApi();
|
||||
return filterAvailableRoutes(menus, routePageMap, layoutMap);
|
||||
},
|
||||
// 可以指定没有权限跳转403页面
|
||||
forbiddenComponent,
|
||||
|
||||
Reference in New Issue
Block a user