Commit f6cef1088d499acd7d5124d8e1a83d454ba648cb

Authored by Vben
1 parent f57eb944

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

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