提交 f6cef108 编写于 作者: V Vben

refactor: refactored multi-language modules to support lazy loading and remote loading

上级 f57eb944
......@@ -21,3 +21,4 @@ ignore:
- dist
- .local
- .husky
- src/locales/lang
## Wip
### ✨ Refactor
- 重构多语言模块,支持懒加载及远程加载
### ✨ Features
- axios 支持 form-data 格式请求
......
<template>
<ConfigProvider v-bind="lockEvent" :locale="antConfigLocale">
<ConfigProvider v-bind="lockEvent" :locale="getAntdLocale">
<AppProvider>
<RouterView />
</AppProvider>
......@@ -21,9 +21,7 @@
components: { ConfigProvider, AppProvider },
setup() {
// support Multi-language
const { antConfigLocale, setLocale } = useLocale();
setLocale();
const { getAntdLocale } = useLocale();
// Initialize vuex internal system configuration
initAppConfigStore();
......@@ -31,10 +29,7 @@
// Create a lock screen monitor
const lockEvent = useLockPage();
return {
antConfigLocale,
lockEvent,
};
return { getAntdLocale, lockEvent };
},
});
</script>
......@@ -18,7 +18,7 @@
</Dropdown>
</template>
<script lang="ts">
import type { LocaleType } from '/@/locales/types';
import type { LocaleType } from '/#/config';
import type { DropMenu } from '/@/components/Dropdown';
import { defineComponent, ref, watchEffect, unref, computed } from 'vue';
......@@ -26,7 +26,7 @@
import Icon from '/@/components/Icon';
import { useLocale } from '/@/locales/useLocale';
import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting';
import { localeList } from '/@/settings/localeSetting';
import { useDesign } from '/@/hooks/web/useDesign';
import { propTypes } from '/@/utils/propTypes';
......@@ -44,9 +44,7 @@
const { prefixCls } = useDesign('app-locale-picker');
const { localeList } = useLocaleSetting();
const { changeLocale, getLang } = useLocale();
const { changeLocale, getLocale } = useLocale();
const getLangText = computed(() => {
const key = selectedKeys.value[0];
......@@ -55,17 +53,17 @@
});
watchEffect(() => {
selectedKeys.value = [unref(getLang)];
selectedKeys.value = [unref(getLocale)];
});
function toggleLocale(lang: LocaleType | string) {
changeLocale(lang as LocaleType);
async function toggleLocale(lang: LocaleType | string) {
await changeLocale(lang as LocaleType);
selectedKeys.value = [lang as string];
props.reload && location.reload();
}
function handleMenuEvent(menu: DropMenu) {
if (unref(getLang) === menu.event) return;
if (unref(getLocale) === menu.event) return;
toggleLocale(menu.event as string);
}
......
......@@ -34,13 +34,13 @@
const modalFn = useModalContext();
const { getLang } = useLocale();
const { getLocale } = useLocale();
watchEffect(() => {});
const getCurrentLang = computed((): 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' => {
let lang: Lang;
switch (unref(getLang)) {
switch (unref(getLocale)) {
case 'en':
lang = 'en_US';
break;
......
......@@ -52,7 +52,6 @@
import { propTypes } from '/@/utils/propTypes';
import { useI18n } from '/@/hooks/web/useI18n';
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
const { t } = useI18n();
export default defineComponent({
name: 'SimpleSubMenu',
......@@ -73,6 +72,7 @@
theme: propTypes.oneOf(['dark', 'light']),
},
setup(props) {
const { t } = useI18n();
const { prefixCls } = useDesign('simple-menu');
const getShowMenu = computed(() => {
......
// token key
export const TOKEN_KEY = 'TOKEN__';
export const LOCALE_KEY = 'LOCALE__';
// user info key
export const USER_INFO_KEY = 'USER__INFO__';
......@@ -14,16 +16,10 @@ export const PROJ_CFG_KEY = 'PROJ__CFG__KEY__';
export const LOCK_INFO_KEY = 'LOCK__INFO__KEY__';
// base global local key
export const BASE_LOCAL_CACHE_KEY = 'LOCAL__CACHE__KEY__';
// base global session key
export const BASE_SESSION_CACHE_KEY = 'SESSION__CACHE__KEY__';
// base global local key
export const APP_LOCAL_CACHE_KEY = 'LOCAL__CACHE__KEY__';
export const APP_LOCAL_CACHE_KEY = 'COMMON__LOCAL__KEY__';
// base global session key
export const APP_SESSION_CACHE_KEY = 'SESSION__CACHE__KEY__';
export const APP_SESSION_CACHE_KEY = 'COMMON__SESSION__KEY__';
export enum CacheTypeEnum {
SESSION,
......
import type { ProjectConfig, GlobConfig, GlobEnvConfig } from '/#/config';
import { getConfigFileName } from '../../../build/getConfigFileName';
import getProjectSetting from '/@/settings/projectSetting';
import type { GlobConfig } from '/#/config';
import { warn } from '/@/utils/log';
import { getGlobEnvConfig, isDevMode } from '/@/utils/env';
import { getAppEnvConfig } from '/@/utils/env';
export const useGlobSetting = (): Readonly<GlobConfig> => {
const ENV_NAME = getConfigFileName(import.meta.env);
const ENV = ((isDevMode()
? getGlobEnvConfig()
: window[ENV_NAME as any]) as unknown) as GlobEnvConfig;
const {
VITE_GLOB_APP_TITLE,
VITE_GLOB_API_URL,
VITE_GLOB_APP_SHORT_NAME,
VITE_GLOB_API_URL_PREFIX,
VITE_GLOB_UPLOAD_URL,
} = ENV;
} = getAppEnvConfig();
if (!/[a-zA-Z\_]*/.test(VITE_GLOB_APP_SHORT_NAME)) {
warn(
......@@ -38,8 +28,3 @@ export const useGlobSetting = (): Readonly<GlobConfig> => {
};
return glob as Readonly<GlobConfig>;
};
export const useProjectSetting = (): ProjectConfig => {
// TODO computed
return getProjectSetting;
};
import type { LocaleSetting } from '/#/config';
import { computed, unref } from 'vue';
import { appStore } from '/@/store/modules/app';
import getProjectSetting from '/@/settings/projectSetting';
import { localeList } from '/@/locales/constant';
// Get locale configuration
const getLocale = computed(() => appStore.getProjectConfig.locale || getProjectSetting.locale);
// get current language
const getLang = computed(() => unref(getLocale).lang);
// get Available Locales
const getAvailableLocales = computed((): string[] => unref(getLocale).availableLocales);
// get Fallback Locales
const getFallbackLocale = computed((): string => unref(getLocale).fallback);
const getShowLocale = computed(() => unref(getLocale).show);
// Set locale configuration
function setLocale(locale: Partial<LocaleSetting>): void {
appStore.commitProjectConfigState({ locale });
}
export function useLocaleSetting() {
return {
getLocale,
getLang,
localeList,
setLocale,
getShowLocale,
getAvailableLocales,
getFallbackLocale,
};
}
......@@ -40,6 +40,7 @@ export function useI18n(
const tFn: I18nGlobalTranslation = (key: string, ...arg: any[]) => {
if (!key) return '';
if (!key.includes('.')) return key;
return t(getKey(namespace, key), ...(arg as I18nTranslationRestParameters));
};
return {
......
......@@ -58,7 +58,7 @@ export function usePermission() {
return def;
}
if (!isArray(value)) {
return userStore.getRoleListState.includes(value as RoleEnum);
return userStore.getRoleListState?.includes(value as RoleEnum);
}
return (intersection(value, userStore.getRoleListState) as RoleEnum[]).length > 0;
}
......
......@@ -18,7 +18,6 @@
import { defineComponent, ref, toRaw, watchEffect } from 'vue';
import { Breadcrumb } from 'ant-design-vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import { filter } from '/@/utils/helper/treeHelper';
......@@ -33,6 +32,7 @@
import { propTypes } from '/@/utils/propTypes';
import { useGo } from '/@/hooks/web/usePage';
import { isString } from '/@/utils/is';
import { useI18n } from '/@/hooks/web/useI18n';
export default defineComponent({
name: 'LayoutBreadcrumb',
......
......@@ -17,7 +17,7 @@
icon="ion:document-text-outline"
v-if="getShowDoc"
/>
<MenuDivider />
<MenuDivider v-if="getShowDoc" />
<MenuItem
key="lock"
:text="t('layout.header.tooltipLock')"
......
......@@ -42,7 +42,7 @@
<FullScreen v-if="getShowFullScreen" :class="`${prefixCls}-action__item fullscreen-item`" />
<AppLocalePicker
v-if="getShowLocale"
v-if="getShowLocalePicker"
:reload="true"
:showText="false"
:class="`${prefixCls}-action__item`"
......@@ -69,7 +69,6 @@
import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting';
import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
import { SettingButtonPositionEnum } from '/@/enums/appEnum';
......@@ -80,6 +79,7 @@
import { useDesign } from '/@/hooks/web/useDesign';
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
import { useLocale } from '/@/locales/useLocale';
export default defineComponent({
name: 'LayoutHeader',
......@@ -112,7 +112,6 @@
getMenuWidth,
getIsMixSidebar,
} = useMenuSetting();
const { getShowLocale } = useLocaleSetting();
const {
getUseErrorHandle,
getShowSettingButton,
......@@ -130,6 +129,8 @@
getShowHeader,
} = useHeaderSetting();
const { getShowLocalePicker } = useLocale();
const { getIsMobile } = useAppInject();
const getHeaderClass = computed(() => {
......@@ -185,7 +186,7 @@
getSplit,
getMenuMode,
getShowTopMenu,
getShowLocale,
getShowLocalePicker,
getShowFullScreen,
getShowNotice,
getUseLockPage,
......
import { toRaw, ref, nextTick } from 'vue';
import { RouteLocationNormalized } from 'vue-router';
import { useProjectSetting } from '/@/hooks/setting';
import { useDesign } from '/@/hooks/web/useDesign';
import { useSortable } from '/@/hooks/web/useSortable';
import router from '/@/router';
import { tabStore } from '/@/store/modules/tab';
import { isNullAndUnDef } from '/@/utils/is';
import projectSetting from '/@/settings/projectSetting';
export function initAffixTabs(): string[] {
const affixList = ref<RouteLocationNormalized[]>([]);
......@@ -47,7 +47,7 @@ export function initAffixTabs(): string[] {
}
export function useTabsDrag(affixTextList: string[]) {
const { multiTabsSetting } = useProjectSetting();
const { multiTabsSetting } = projectSetting;
const { prefixCls } = useDesign('multiple-tabs');
nextTick(() => {
......
import type { DropMenu } from '/@/components/Dropdown';
// locale list
export const localeList: DropMenu[] = [
{
text: '简体中文',
event: 'zh_CN',
},
{
text: 'English',
event: 'en',
},
];
import { genMessage } from './helper';
const modules = import.meta.globEager('./lang/**/*.ts');
export default genMessage(modules);
......@@ -4,16 +4,21 @@ export function genMessage(langs: Record<string, Record<string, any>>, prefix =
const obj: Recordable = {};
Object.keys(langs).forEach((key) => {
const mod = langs[key].default;
let k = key.replace(`./${prefix}/`, '').replace(/^\.\//, '');
const lastIndex = k.lastIndexOf('.');
k = k.substring(0, lastIndex);
const keyList = k.split('/');
const lang = keyList.shift();
const langFileModule = langs[key].default;
let fileName = key.replace(`./${prefix}/`, '').replace(/^\.\//, '');
const lastIndex = fileName.lastIndexOf('.');
fileName = fileName.substring(0, lastIndex);
const keyList = fileName.split('/');
const moduleName = keyList.shift();
const objKey = keyList.join('.');
if (lang) {
set(obj, lang, obj[lang] || {});
set(obj[lang], objKey, mod);
if (moduleName) {
if (objKey) {
set(obj, moduleName, obj[moduleName] || {});
set(obj[moduleName], objKey, langFileModule);
} else {
set(obj, moduleName, langFileModule || {});
}
}
});
return obj;
......
import { genMessage } from '../helper';
const modules = import.meta.globEager('./en/**/*.ts');
import antdLocale from 'ant-design-vue/es/locale/en_US';
import momentLocale from 'moment/dist/locale/eu';
export default {
message: {
...genMessage(modules, 'en'),
antdLocale,
},
momentLocale,
momentLocaleName: 'eu',
};
export default {
redo: 'Refresh current',
reload: 'Refresh current',
close: 'Close current',
closeLeft: 'Close Left',
closeRight: 'Close Right',
......
......@@ -25,4 +25,6 @@ export default {
list: 'List page',
listCard: 'Card list',
basic: 'Basic list',
listBasic: 'Basic list',
listSearch: 'Search list',
};
import { genMessage } from '../helper';
const modules = import.meta.globEager('./zh_CN/**/*.ts');
import antdLocale from 'ant-design-vue/es/locale/zh_CN';
import momentLocale from 'moment/dist/locale/zh-cn';
export default {
message: {
...genMessage(modules, 'zh_CN'),
antdLocale,
},
momentLocale,
momentLocaleName: 'zh-cn',
};
......@@ -3,27 +3,36 @@ import type { I18n, I18nOptions } from 'vue-i18n';
import { createI18n } from 'vue-i18n';
import projectSetting from '/@/settings/projectSetting';
import { localeStore } from '/@/store/modules/locale';
import { localeSetting } from '/@/settings/localeSetting';
import messages from './getMessage';
const { fallback, availableLocales } = localeSetting;
const { lang, availableLocales, fallback } = projectSetting?.locale;
export let i18n: ReturnType<typeof createI18n>;
const localeData: I18nOptions = {
legacy: false,
locale: lang,
fallbackLocale: fallback,
messages,
availableLocales: availableLocales,
sync: true, //If you don’t want to inherit locale from global scope, you need to set sync of i18n component option to false.
silentTranslationWarn: true, // true - warning off
missingWarn: false,
silentFallbackWarn: true,
};
export let i18n: I18n;
async function createI18nOptions(): Promise<I18nOptions> {
const locale = localeStore.getLocale;
const defaultLocal = await import(`./lang/${locale}.ts`);
const message = defaultLocal.default?.message;
return {
legacy: false,
locale,
fallbackLocale: fallback,
messages: {
[locale]: message,
},
availableLocales: availableLocales,
sync: true, //If you don’t want to inherit locale from global scope, you need to set sync of i18n component option to false.
silentTranslationWarn: true, // true - warning off
missingWarn: false,
silentFallbackWarn: true,
};
}
// setup i18n instance with glob
export function setupI18n(app: App) {
i18n = createI18n(localeData) as I18n;
export async function setupI18n(app: App) {
const options = await createI18nOptions();
i18n = createI18n(options) as I18n;
app.use(i18n);
}
export type LocaleType = 'zh_CN' | 'en' | 'ru' | 'ja' | 'ko';
/**
* Multi-language related operations
*/
import type { LocaleType } from '/@/locales/types';
import type { Ref } from 'vue';
import type { LocaleType } from '/#/config';
import { unref, ref } from 'vue';
import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting';
import { ref } from 'vue';
import moment from 'moment';
import { computed } from 'vue';
import { i18n } from './setupI18n';
import { localeStore } from '/@/store/modules/locale';
import { unref } from 'vue';
import 'moment/dist/locale/zh-cn';
interface LangModule {
message: Recordable;
momentLocale: Recordable;
momentLocaleName: string;
}
const antConfigLocale = ref<Nullable<Recordable>>(null);
const loadLocalePool: LocaleType[] = [];
const antConfigLocaleRef = ref<any>(null);
function setI18nLanguage(locale: LocaleType) {
if (i18n.mode === 'legacy') {
i18n.global.locale = locale;
} else {
(i18n.global.locale as any).value = locale;
}
localeStore.setLocaleInfo({ locale });
document.querySelector('html')?.setAttribute('lang', locale);
}
export function useLocale() {
const { getLang, getLocale, setLocale: setLocalSetting } = useLocaleSetting();
const getLocale = computed(() => localeStore.getLocale);
const getShowLocalePicker = computed(() => localeStore.getShowPicker);
const getAntdLocale = computed(() => {
return i18n.global.getLocaleMessage(unref(getLocale))?.antdLocale;
});
// Switching the language will change the locale of useI18n
// And submit to configuration modification
function changeLocale(lang: LocaleType): void {
if (i18n.mode === 'legacy') {
i18n.global.locale = lang;
} else {
((i18n.global.locale as unknown) as Ref<string>).value = lang;
}
setLocalSetting({ lang });
// i18n.global.setLocaleMessage(locale, messages);
async function changeLocale(locale: LocaleType) {
const globalI18n = i18n.global;
const currentLocale = unref(globalI18n.locale);
if (currentLocale === locale) return locale;
switch (lang) {
// Simplified Chinese
case 'zh_CN':
import('ant-design-vue/es/locale/zh_CN').then((locale) => {
antConfigLocaleRef.value = locale.default;
});
if (loadLocalePool.includes(locale)) {
setI18nLanguage(locale);
return locale;
}
const langModule = ((await import(`./lang/${locale}.ts`)) as any).default as LangModule;
if (!langModule) return;
break;
// English
case 'en':
import('ant-design-vue/es/locale/en_US').then((locale) => {
antConfigLocaleRef.value = locale.default;
});
break;
const { message, momentLocale, momentLocaleName } = langModule;
// other
default:
break;
}
}
globalI18n.setLocaleMessage(locale, message);
moment.updateLocale(momentLocaleName, momentLocale);
loadLocalePool.push(locale);
// initialization
function setLocale() {
const lang = unref(getLang);
lang && changeLocale(lang);
setI18nLanguage(locale);
return locale;
}
return {
setLocale,
getLocale,
getLang,
getShowLocalePicker,
changeLocale,
antConfigLocale: antConfigLocaleRef,
antConfigLocale,
getAntdLocale,
};
}
......@@ -3,9 +3,9 @@
*/
import { errorStore, ErrorInfo } from '/@/store/modules/error';
import { useProjectSetting } from '/@/hooks/setting';
import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
import { App } from 'vue';
import projectSetting from '/@/settings/projectSetting';
/**
* Handling error stack information
......@@ -160,7 +160,7 @@ function registerResourceErrorHandler() {
* @param app
*/
export function setupErrorHandle(app: App) {
const { useErrorHandle } = useProjectSetting();
const { useErrorHandle } = projectSetting;
if (!useErrorHandle) return;
// Vue exception monitoring;
app.config.errorHandler = vueErrorHandler;
......
......@@ -2,25 +2,22 @@
* Application configuration
*/
import type { ProjectConfig } from '/#/config';
import { PROJ_CFG_KEY } from '/@/enums/cacheEnum';
import projectSetting from '/@/settings/projectSetting';
import { Persistent } from '/@/utils/cache/persistent';
import { updateHeaderBgColor, updateSidebarBgColor } from '/@/logics/theme/updateBackground';
import { updateColorWeak } from '/@/logics/theme/updateColorWeak';
import { updateGrayMode } from '/@/logics/theme/updateGrayMode';
import { changeTheme } from '/@/logics/theme';
import { appStore } from '/@/store/modules/app';
import { deepMerge } from '/@/utils';
import { localeStore } from '/@/store/modules/locale';
import { getCommonStoragePrefix, getStorageShortName } from '/@/utils/env';
import { primaryColor } from '../../build/config/themeConfig';
// Initial project configuration
export function initAppConfigStore() {
let projCfg: ProjectConfig = Persistent.getLocal(PROJ_CFG_KEY) as ProjectConfig;
projCfg = deepMerge(projectSetting, projCfg || {});
try {
const {
colorWeak,
......@@ -28,7 +25,7 @@ export function initAppConfigStore() {
themeColor,
headerSetting: { bgColor: headerBgColor } = {},
menuSetting: { bgColor } = {},
} = projCfg;
} = projectSetting;
if (themeColor && themeColor !== primaryColor) {
changeTheme(themeColor);
}
......@@ -39,5 +36,27 @@ export function initAppConfigStore() {
} catch (error) {
console.log(error);
}
appStore.commitProjectConfigState(projCfg);
appStore.commitProjectConfigState(projectSetting);
localeStore.initLocale();
setTimeout(() => {
clearObsoleteStorage();
}, 16);
}
/**
* As the version continues to iterate, there will be more and more cache keys stored in localStorage.
* This method is used to delete useless keys
*/
export function clearObsoleteStorage() {
const commonPrefix = getCommonStoragePrefix();
const shortPrefix = getStorageShortName();
[localStorage, sessionStorage].forEach((item: Storage) => {
Object.keys(item).forEach((key) => {
if (key && key.startsWith(commonPrefix) && !key.startsWith(shortPrefix)) {
item.removeItem(key);
}
});
});
}
......@@ -14,33 +14,36 @@ import { registerGlobComp } from '/@/components/registerGlobComp';
import { isDevMode } from '/@/utils/env';
const app = createApp(App);
(async () => {
const app = createApp(App);
// Register global components
registerGlobComp(app);
// Register global components
registerGlobComp(app);
// Multilingual configuration
setupI18n(app);
// Configure routing
setupRouter(app);
// Configure routing
setupRouter(app);
// Configure vuex store
setupStore(app);
// Configure vuex store
setupStore(app);
// Register global directive
setupGlobDirectives(app);
// Register global directive
setupGlobDirectives(app);
// Configure global error handling
setupErrorHandle(app);
// Configure global error handling
setupErrorHandle(app);
await Promise.all([
// Multilingual configuration
setupI18n(app),
// Mount when the route is ready
router.isReady(),
]);
// Mount when the route is ready
router.isReady().then(() => {
app.mount('#app', true);
});
// The development environment takes effect
if (isDevMode()) {
app.config.performance = true;
window.__APP__ = app;
}
// The development environment takes effect
if (isDevMode()) {
app.config.performance = true;
window.__APP__ = app;
}
})();
import type { Router } from 'vue-router';
import { useProjectSetting } from '/@/hooks/setting';
import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel';
import projectSetting from '/@/settings/projectSetting';
/**
* The interface used to close the current page to complete the request when the route is switched
* @param router
*/
export function createHttpGuard(router: Router) {
const { removeAllHttpPending } = useProjectSetting();
const { removeAllHttpPending } = projectSetting;
let axiosCanceler: Nullable<AxiosCanceler>;
if (removeAllHttpPending) {
axiosCanceler = new AxiosCanceler();
......
import type { Router } from 'vue-router';
import { useProjectSetting } from '/@/hooks/setting';
import { Modal, notification } from 'ant-design-vue';
import projectSetting from '/@/settings/projectSetting';
import { warn } from '/@/utils/log';
/**
......@@ -9,7 +8,7 @@ import { warn } from '/@/utils/log';
* @param router
*/
export function createMessageGuard(router: Router) {
const { closeMessageOnSwitch } = useProjectSetting();
const { closeMessageOnSwitch } = projectSetting;
router.beforeEach(async () => {
try {
......
......@@ -4,9 +4,11 @@ import type { App } from 'vue';
import { createRouter, createWebHashHistory } from 'vue-router';
import { createGuard } from './guard';
import { basicRoutes } from './routes';
import { basicRoutes, LoginRoute } from './routes';
import { REDIRECT_NAME } from './constant';
const WHITE_NAME_LIST = [LoginRoute.name, REDIRECT_NAME];
// app router
const router = createRouter({
history: createWebHashHistory(),
......@@ -17,10 +19,9 @@ const router = createRouter({
// reset router
export function resetRouter() {
const resetWhiteNameList = ['Login', REDIRECT_NAME];
router.getRoutes().forEach((route) => {
const { name } = route;
if (name && !resetWhiteNameList.includes(name as string)) {
if (name && !WHITE_NAME_LIST.includes(name as string)) {
router.hasRoute(name) && router.removeRoute(name);
}
});
......
/**
The routing of this file will not show the layout.
It is an independent new page.
the contents of the file still need to log in to access
*/
import type { AppRouteModule } from '/@/router/types';
// test
......
import type { DropMenu } from '/@/components/Dropdown/src/types';
import type { LocaleSetting, LocaleType } from '/#/config';
export const LOCALE: { [key: string]: LocaleType } = {
ZH_CN: 'zh_CN',
EN_US: 'en',
};
export const localeSetting: LocaleSetting = {
showPicker: true,
// Locale
locale: LOCALE.ZH_CN,
// Default locale
fallback: LOCALE.ZH_CN,
// available Locales
availableLocales: [LOCALE.ZH_CN, LOCALE.EN_US],
};
// locale list
export const localeList: DropMenu[] = [
{
text: '简体中文',
event: LOCALE.ZH_CN,
},
{
text: 'English',
event: LOCALE.EN_US,
},
];
import type { ProjectConfig } from '/#/config';
import { MenuTypeEnum, MenuModeEnum, TriggerEnum, MixSidebarTriggerEnum } from '/@/enums/menuEnum';
import { CacheTypeEnum } from '/@/enums/cacheEnum';
import {
......@@ -26,8 +25,8 @@ const setting: ProjectConfig = {
permissionCacheType: CacheTypeEnum.LOCAL,
// color
// TODO Theme color
themeColor: primaryColor,
// TODO dark theme
themeMode: themeMode,
......@@ -49,17 +48,6 @@ const setting: ProjectConfig = {
// Whether to show footer
showFooter: false,
// locale setting
locale: {
show: true,
// Locale
lang: 'zh_CN',
// Default locale
fallback: 'zh_CN',
// available Locales
availableLocales: ['zh_CN', 'en'],
},
// Header configuration
headerSetting: {
// header bg color
......
......@@ -4,7 +4,7 @@ import { VuexModule, getModule, Module, Mutation, Action } from 'vuex-module-dec
import { formatToDateTime } from '/@/utils/dateUtil';
import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
import { useProjectSetting } from '/@/hooks/setting';
import projectSetting from '/@/settings/projectSetting';
export interface ErrorInfo {
type: ErrorTypeEnum;
......@@ -57,7 +57,7 @@ class Error extends VuexModule implements ErrorState {
@Action
setupErrorHandle(error: any) {
const { useErrorHandle } = useProjectSetting();
const { useErrorHandle } = projectSetting;
if (!useErrorHandle) return;
const errInfo: Partial<ErrorInfo> = {
......
import store from '/@/store';
import { VuexModule, getModule, Module, Mutation, Action } from 'vuex-module-decorators';
import { LOCALE_KEY } from '/@/enums/cacheEnum';
import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
import { LocaleSetting, LocaleType } from '/#/config';
import { createLocalStorage } from '/@/utils/cache';
import { localeSetting } from '/@/settings/localeSetting';
const ls = createLocalStorage();
const lsSetting = (ls.get(LOCALE_KEY) || localeSetting) as LocaleSetting;
const NAME = 'locale';
hotModuleUnregisterModule(NAME);
@Module({ dynamic: true, namespaced: true, store, name: NAME })
class Locale extends VuexModule {
private info: LocaleSetting = lsSetting;
get getShowPicker(): boolean {
return !!this.info?.showPicker;
}
get getLocale(): LocaleType {
return this.info?.locale;
}
@Mutation
setLocaleInfo(info: Partial<LocaleSetting>): void {
this.info = { ...this.info, ...info };
ls.set(LOCALE_KEY, this.info);
}
@Action
initLocale(): void {
this.setLocaleInfo({
...localeSetting,
...this.info,
});
}
}
export const localeStore = getModule<Locale>(Locale);
......@@ -88,7 +88,7 @@ class Permission extends VuexModule {
let routes: AppRouteRecordRaw[] = [];
const roleList = toRaw(userStore.getRoleListState);
const { permissionMode } = appStore.getProjectConfig;
const { permissionMode = PermissionModeEnum.ROLE } = appStore.getProjectConfig;
// role permissions
if (permissionMode === PermissionModeEnum.ROLE) {
......
......@@ -11,7 +11,6 @@ const createOptions = (storage: Storage, options: Options = {}): Options => {
hasEncrypt: enableStorageEncryption,
storage,
prefixKey: getStorageShortName(),
...options,
};
};
......@@ -22,11 +21,12 @@ export const createStorage = (storage: Storage = sessionStorage, options: Option
return create(createOptions(storage, options));
};
export const createPersistentStorage = (
storage: Storage = sessionStorage,
options: Options = {}
) => {
return createStorage(storage, { ...options, timeout: DEFAULT_CACHE_TIME });
export const createSessionStorage = (options: Options = {}) => {
return createStorage(sessionStorage, { ...options, timeout: DEFAULT_CACHE_TIME });
};
export const createLocalStorage = (options: Options = {}) => {
return createStorage(localStorage, { ...options, timeout: DEFAULT_CACHE_TIME });
};
export default WebStorage;
......@@ -57,7 +57,7 @@ export class Memory<T = any, V = any> {
if (!expires) {
return value;
}
item.time = new Date().getTime() + this.alive * 1000;
item.time = new Date().getTime() + this.alive;
item.timeoutId = setTimeout(() => {
this.remove(key);
}, expires);
......@@ -80,7 +80,7 @@ export class Memory<T = any, V = any> {
const item = cache[k];
if (item && item.time) {
const now = new Date().getTime();
const expire = now + item.time * 1000;
const expire = item.time;
if (expire > now) {
this.set(k, item.value, expire);
}
......
import { createPersistentStorage } from '/@/utils/cache';
import { createLocalStorage, createSessionStorage } from '/@/utils/cache';
import { Memory } from './memory';
import {
TOKEN_KEY,
......@@ -28,19 +28,19 @@ export type BasicKeys = keyof BasicStore;
type LocalKeys = keyof LocalStore;
type SessionKeys = keyof SessionStore;
const ls = createPersistentStorage(localStorage);
const ss = createPersistentStorage(sessionStorage);
const ls = createLocalStorage();
const ss = createSessionStorage();
const localMemory = new Memory(DEFAULT_CACHE_TIME);
const sessionMemory = new Memory(DEFAULT_CACHE_TIME);
function initMemory() {
function initPersistentMemory() {
const localCache = ls.get(APP_LOCAL_CACHE_KEY);
const sessionCache = ls.get(APP_SESSION_CACHE_KEY);
localCache && localMemory.resetCache(localCache);
sessionCache && sessionMemory.resetCache(sessionCache);
}
initMemory();
export class Persistent {
static getLocal<T>(key: LocalKeys) {
return localMemory.get(key)?.value as Nullable<T>;
......@@ -106,4 +106,4 @@ function storageChange(e: any) {
window.addEventListener('storage', storageChange);
export default {};
initPersistentMemory();
......@@ -2,19 +2,26 @@ import type { GlobEnvConfig } from '/#/config';
import { useGlobSetting } from '/@/hooks/setting';
import pkg from '../../package.json';
import { getConfigFileName } from '../../build/getConfigFileName';
/**
* Get the global configuration (the configuration will be extracted independently when packaging)
*/
export function getGlobEnvConfig(): GlobEnvConfig {
const env = import.meta.env;
return (env as unknown) as GlobEnvConfig;
export function getCommonStoragePrefix() {
const globSetting = useGlobSetting();
return `${globSetting.shortName}__${getEnv()}`.toUpperCase();
}
// Generate cache key according to version
export function getStorageShortName() {
const globSetting = useGlobSetting();
return `${globSetting.shortName}__${getEnv()}${`__${pkg.version}`}__`.toUpperCase();
return `${getCommonStoragePrefix()}${`__${pkg.version}`}__`.toUpperCase();
}
export function getAppEnvConfig() {
const ENV_NAME = getConfigFileName(import.meta.env);
const ENV = ((isDevMode()
? // Get the global configuration (the configuration will be extracted independently when packaging)
((import.meta.env as unknown) as GlobEnvConfig)
: window[ENV_NAME as any]) as unknown) as GlobEnvConfig;
return ENV;
}
/**
......
import { dateUtil } from '/@/utils/dateUtil';
import { reactive, toRefs } from 'vue';
import { tryOnMounted, tryOnUnmounted } from '/@/utils/helper/vueHelper';
import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting';
import { localeStore } from '/@/store/modules/locale';
export function useNow(immediate = true) {
const { getLang } = useLocaleSetting();
const localData = dateUtil.localeData(getLang.value);
const localData = dateUtil.localeData(localeStore.getLocale);
let timer: IntervalHandle;
const state = reactive({
......
......@@ -53,9 +53,10 @@
import MobileForm from './MobileForm.vue';
import QrCodeForm from './QrCodeForm.vue';
import { useGlobSetting, useProjectSetting } from '/@/hooks/setting';
import { useGlobSetting } from '/@/hooks/setting';
import { useI18n } from '/@/hooks/web/useI18n';
import { useDesign } from '/@/hooks/web/useDesign';
import { localeStore } from '/@/store/modules/locale';
export default defineComponent({
name: 'Login',
......@@ -71,14 +72,13 @@
setup() {
const globSetting = useGlobSetting();
const { prefixCls } = useDesign('login');
const { locale } = useProjectSetting();
const { t } = useI18n();
return {
t,
prefixCls,
title: computed(() => globSetting?.title ?? ''),
showLocale: computed(() => locale.show),
showLocale: localeStore.getShowPicker,
};
},
});
......
......@@ -8,9 +8,10 @@ import {
} from '/@/enums/appEnum';
import { CacheTypeEnum } from '/@/enums/cacheEnum';
import type { LocaleType } from '/@/locales/types';
import { ThemeMode } from '../build/config/themeConfig';
export type LocaleType = 'zh_CN' | 'en' | 'ru' | 'ja' | 'ko';
export interface MenuSetting {
bgColor: string;
fixed: boolean;
......@@ -57,9 +58,9 @@ export interface HeaderSetting {
}
export interface LocaleSetting {
show: boolean;
showPicker: boolean;
// Current language
lang: LocaleType;
locale: LocaleType;
// default language
fallback: LocaleType;
// available Locales
......@@ -78,8 +79,6 @@ export interface TransitionSetting {
}
export interface ProjectConfig {
// Multilingual configuration
locale: LocaleSetting;
// Storage location of permission related information
permissionCacheType: CacheTypeEnum;
// Whether to show the configuration button
......
......@@ -4,7 +4,7 @@ declare module 'ant-design-vue/es/locale/*' {
export default locale as Locale & ReadonlyRecordable;
}
declare module 'moment/locale/*' {
declare module 'moment/dist/locale/*' {
import { LocaleSpecification } from 'moment';
const locale: LocaleSpecification & ReadonlyRecordable;
export default locale;
......
......@@ -1718,10 +1718,10 @@
dependencies:
vue-demi latest
"@windicss/plugin-utils@0.5.4":
version "0.5.4"
resolved "https://registry.npmjs.org/@windicss/plugin-utils/-/plugin-utils-0.5.4.tgz#69476a9d1fee92046695766bf7fbfe48e48809a7"
integrity sha512-zxpHdTsVZl7TF8A3uAymJCqMRlG83dMRAXf//fXonluoLDSJCuGBJyxN3NdkAyNZZR1L1DvoUUtkZLYOba+ElQ==
"@windicss/plugin-utils@0.6.0":
version "0.6.0"
resolved "https://registry.npmjs.org/@windicss/plugin-utils/-/plugin-utils-0.6.0.tgz#34eb852b7ff338bb933b0079112318a30d2aee00"
integrity sha512-CpXn3CRrAaDrpTjenidVfBz0JONLuGTFP6qjrwZ2tmhsKOuvTWw8Ic9JQ2a9L0AkYBH33lTso1qk70/PjnE6WQ==
dependencies:
esbuild "^0.8.52"
esbuild-register "^2.0.0"
......@@ -1849,10 +1849,10 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
dependencies:
color-convert "^2.0.1"
ant-design-vue@2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-2.0.0.tgz#d30ec06938dc3b43b08a117818fab91d7b083e5f"
integrity sha512-Uv35Z9V+8iT1PBO0QOqWHaVE4Gju94UfikL8NGxtAqy/yZDnTn8K2gz5n7PfQbB5oBqkEyn2O0mtOpUBUEXZ+g==
ant-design-vue@2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-2.0.1.tgz#3a5964523aac10fd2b16d84d651145cd2b65f1d5"
integrity sha512-CFIF+srTui4ZwdKPBXNoFA9/0fkSpypanQeOts0PAq1vEuMLxUoZHapDDn7wzsxZH3sYLF+mvMp8gYMRkaNn+w==
dependencies:
"@ant-design-vue/use" "^0.0.1-0"
"@ant-design/icons-vue" "^6.0.0"
......@@ -8968,12 +8968,12 @@ vite-plugin-theme@^0.4.8:
es-module-lexer "^0.3.26"
tinycolor2 "^1.4.2"
vite-plugin-windicss@0.5.4:
version "0.5.4"
resolved "https://registry.npmjs.org/vite-plugin-windicss/-/vite-plugin-windicss-0.5.4.tgz#35764e91536d596ac2c9266c3e16c546915d8b3e"
integrity sha512-iPLoqfpZdnRIY1AzweumpdE8ILQQnyhywZwJDqFpj8SZ3h43e5tfQFnJb5nS6FLccOsBcCV9JFugD2w6pGyfqg==
vite-plugin-windicss@0.6.0:
version "0.6.0"
resolved "https://registry.npmjs.org/vite-plugin-windicss/-/vite-plugin-windicss-0.6.0.tgz#ac8f24e70439904b67adc1f133e692fb6257ecaf"
integrity sha512-PSFdm0hrAGaKFzkFOiz31+dODoKNbh9wo/3m/7/012WwV9oJ1mX/9OxDxACykW7hMR0YvWHFmC0UwtvMra+InQ==
dependencies:
"@windicss/plugin-utils" "0.5.4"
"@windicss/plugin-utils" "0.6.0"
windicss "^2.2.0"
vite@2.0.4:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册