Commit f6cef1088d499acd7d5124d8e1a83d454ba648cb
1 parent
f57eb944
refactor: refactored multi-language modules to support lazy loading and remote loading
Showing
47 changed files
with
353 additions
and
284 deletions
.ls-lint.yml
CHANGELOG.zh_CN.md
package.json
... | ... | @@ -28,7 +28,7 @@ |
28 | 28 | "@iconify/iconify": "^2.0.0-rc.6", |
29 | 29 | "@vueuse/core": "^4.3.0", |
30 | 30 | "@zxcvbn-ts/core": "^0.2.0", |
31 | - "ant-design-vue": "2.0.0", | |
31 | + "ant-design-vue": "2.0.1", | |
32 | 32 | "apexcharts": "^3.25.0", |
33 | 33 | "axios": "^0.21.1", |
34 | 34 | "crypto-js": "^4.0.0", |
... | ... | @@ -106,7 +106,7 @@ |
106 | 106 | "vite-plugin-pwa": "^0.5.5", |
107 | 107 | "vite-plugin-style-import": "^0.7.5", |
108 | 108 | "vite-plugin-theme": "^0.4.8", |
109 | - "vite-plugin-windicss": "0.5.4", | |
109 | + "vite-plugin-windicss": "0.6.0", | |
110 | 110 | "vue-eslint-parser": "^7.5.0", |
111 | 111 | "yargs": "^16.2.0" |
112 | 112 | }, | ... | ... |
src/App.vue
1 | 1 | <template> |
2 | - <ConfigProvider v-bind="lockEvent" :locale="antConfigLocale"> | |
2 | + <ConfigProvider v-bind="lockEvent" :locale="getAntdLocale"> | |
3 | 3 | <AppProvider> |
4 | 4 | <RouterView /> |
5 | 5 | </AppProvider> |
... | ... | @@ -21,9 +21,7 @@ |
21 | 21 | components: { ConfigProvider, AppProvider }, |
22 | 22 | setup() { |
23 | 23 | // support Multi-language |
24 | - const { antConfigLocale, setLocale } = useLocale(); | |
25 | - | |
26 | - setLocale(); | |
24 | + const { getAntdLocale } = useLocale(); | |
27 | 25 | |
28 | 26 | // Initialize vuex internal system configuration |
29 | 27 | initAppConfigStore(); |
... | ... | @@ -31,10 +29,7 @@ |
31 | 29 | // Create a lock screen monitor |
32 | 30 | const lockEvent = useLockPage(); |
33 | 31 | |
34 | - return { | |
35 | - antConfigLocale, | |
36 | - lockEvent, | |
37 | - }; | |
32 | + return { getAntdLocale, lockEvent }; | |
38 | 33 | }, |
39 | 34 | }); |
40 | 35 | </script> | ... | ... |
src/components/Application/src/AppLocalePicker.vue
... | ... | @@ -18,7 +18,7 @@ |
18 | 18 | </Dropdown> |
19 | 19 | </template> |
20 | 20 | <script lang="ts"> |
21 | - import type { LocaleType } from '/@/locales/types'; | |
21 | + import type { LocaleType } from '/#/config'; | |
22 | 22 | import type { DropMenu } from '/@/components/Dropdown'; |
23 | 23 | |
24 | 24 | import { defineComponent, ref, watchEffect, unref, computed } from 'vue'; |
... | ... | @@ -26,7 +26,7 @@ |
26 | 26 | import Icon from '/@/components/Icon'; |
27 | 27 | |
28 | 28 | import { useLocale } from '/@/locales/useLocale'; |
29 | - import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting'; | |
29 | + import { localeList } from '/@/settings/localeSetting'; | |
30 | 30 | import { useDesign } from '/@/hooks/web/useDesign'; |
31 | 31 | import { propTypes } from '/@/utils/propTypes'; |
32 | 32 | |
... | ... | @@ -44,9 +44,7 @@ |
44 | 44 | |
45 | 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 | 49 | const getLangText = computed(() => { |
52 | 50 | const key = selectedKeys.value[0]; |
... | ... | @@ -55,17 +53,17 @@ |
55 | 53 | }); |
56 | 54 | |
57 | 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 | 61 | selectedKeys.value = [lang as string]; |
64 | 62 | props.reload && location.reload(); |
65 | 63 | } |
66 | 64 | |
67 | 65 | function handleMenuEvent(menu: DropMenu) { |
68 | - if (unref(getLang) === menu.event) return; | |
66 | + if (unref(getLocale) === menu.event) return; | |
69 | 67 | toggleLocale(menu.event as string); |
70 | 68 | } |
71 | 69 | ... | ... |
src/components/Markdown/src/index.vue
... | ... | @@ -34,13 +34,13 @@ |
34 | 34 | |
35 | 35 | const modalFn = useModalContext(); |
36 | 36 | |
37 | - const { getLang } = useLocale(); | |
37 | + const { getLocale } = useLocale(); | |
38 | 38 | |
39 | 39 | watchEffect(() => {}); |
40 | 40 | |
41 | 41 | const getCurrentLang = computed((): 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' => { |
42 | 42 | let lang: Lang; |
43 | - switch (unref(getLang)) { | |
43 | + switch (unref(getLocale)) { | |
44 | 44 | case 'en': |
45 | 45 | lang = 'en_US'; |
46 | 46 | break; | ... | ... |
src/components/SimpleMenu/src/SimpleSubMenu.vue
... | ... | @@ -52,7 +52,6 @@ |
52 | 52 | import { propTypes } from '/@/utils/propTypes'; |
53 | 53 | import { useI18n } from '/@/hooks/web/useI18n'; |
54 | 54 | import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; |
55 | - const { t } = useI18n(); | |
56 | 55 | |
57 | 56 | export default defineComponent({ |
58 | 57 | name: 'SimpleSubMenu', |
... | ... | @@ -73,6 +72,7 @@ |
73 | 72 | theme: propTypes.oneOf(['dark', 'light']), |
74 | 73 | }, |
75 | 74 | setup(props) { |
75 | + const { t } = useI18n(); | |
76 | 76 | const { prefixCls } = useDesign('simple-menu'); |
77 | 77 | |
78 | 78 | const getShowMenu = computed(() => { | ... | ... |
src/enums/cacheEnum.ts
1 | 1 | // token key |
2 | 2 | export const TOKEN_KEY = 'TOKEN__'; |
3 | 3 | |
4 | +export const LOCALE_KEY = 'LOCALE__'; | |
5 | + | |
4 | 6 | // user info key |
5 | 7 | export const USER_INFO_KEY = 'USER__INFO__'; |
6 | 8 | |
... | ... | @@ -14,16 +16,10 @@ export const PROJ_CFG_KEY = 'PROJ__CFG__KEY__'; |
14 | 16 | export const LOCK_INFO_KEY = 'LOCK__INFO__KEY__'; |
15 | 17 | |
16 | 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 | 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 | 24 | export enum CacheTypeEnum { |
29 | 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 | 3 | import { warn } from '/@/utils/log'; |
8 | -import { getGlobEnvConfig, isDevMode } from '/@/utils/env'; | |
4 | +import { getAppEnvConfig } from '/@/utils/env'; | |
9 | 5 | |
10 | 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 | 7 | const { |
18 | 8 | VITE_GLOB_APP_TITLE, |
19 | 9 | VITE_GLOB_API_URL, |
20 | 10 | VITE_GLOB_APP_SHORT_NAME, |
21 | 11 | VITE_GLOB_API_URL_PREFIX, |
22 | 12 | VITE_GLOB_UPLOAD_URL, |
23 | - } = ENV; | |
13 | + } = getAppEnvConfig(); | |
24 | 14 | |
25 | 15 | if (!/[a-zA-Z\_]*/.test(VITE_GLOB_APP_SHORT_NAME)) { |
26 | 16 | warn( |
... | ... | @@ -38,8 +28,3 @@ export const useGlobSetting = (): Readonly<GlobConfig> => { |
38 | 28 | }; |
39 | 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 | 40 | |
41 | 41 | const tFn: I18nGlobalTranslation = (key: string, ...arg: any[]) => { |
42 | 42 | if (!key) return ''; |
43 | + if (!key.includes('.')) return key; | |
43 | 44 | return t(getKey(namespace, key), ...(arg as I18nTranslationRestParameters)); |
44 | 45 | }; |
45 | 46 | return { | ... | ... |
src/hooks/web/usePermission.ts
... | ... | @@ -58,7 +58,7 @@ export function usePermission() { |
58 | 58 | return def; |
59 | 59 | } |
60 | 60 | if (!isArray(value)) { |
61 | - return userStore.getRoleListState.includes(value as RoleEnum); | |
61 | + return userStore.getRoleListState?.includes(value as RoleEnum); | |
62 | 62 | } |
63 | 63 | return (intersection(value, userStore.getRoleListState) as RoleEnum[]).length > 0; |
64 | 64 | } | ... | ... |
src/layouts/default/header/components/Breadcrumb.vue
... | ... | @@ -18,7 +18,6 @@ |
18 | 18 | |
19 | 19 | import { defineComponent, ref, toRaw, watchEffect } from 'vue'; |
20 | 20 | import { Breadcrumb } from 'ant-design-vue'; |
21 | - import { useI18n } from 'vue-i18n'; | |
22 | 21 | |
23 | 22 | import { useRouter } from 'vue-router'; |
24 | 23 | import { filter } from '/@/utils/helper/treeHelper'; |
... | ... | @@ -33,6 +32,7 @@ |
33 | 32 | import { propTypes } from '/@/utils/propTypes'; |
34 | 33 | import { useGo } from '/@/hooks/web/usePage'; |
35 | 34 | import { isString } from '/@/utils/is'; |
35 | + import { useI18n } from '/@/hooks/web/useI18n'; | |
36 | 36 | |
37 | 37 | export default defineComponent({ |
38 | 38 | name: 'LayoutBreadcrumb', | ... | ... |
src/layouts/default/header/components/user-dropdown/index.vue
src/layouts/default/header/index.vue
... | ... | @@ -42,7 +42,7 @@ |
42 | 42 | <FullScreen v-if="getShowFullScreen" :class="`${prefixCls}-action__item fullscreen-item`" /> |
43 | 43 | |
44 | 44 | <AppLocalePicker |
45 | - v-if="getShowLocale" | |
45 | + v-if="getShowLocalePicker" | |
46 | 46 | :reload="true" |
47 | 47 | :showText="false" |
48 | 48 | :class="`${prefixCls}-action__item`" |
... | ... | @@ -69,7 +69,6 @@ |
69 | 69 | import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting'; |
70 | 70 | import { useMenuSetting } from '/@/hooks/setting/useMenuSetting'; |
71 | 71 | import { useRootSetting } from '/@/hooks/setting/useRootSetting'; |
72 | - import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting'; | |
73 | 72 | |
74 | 73 | import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum'; |
75 | 74 | import { SettingButtonPositionEnum } from '/@/enums/appEnum'; |
... | ... | @@ -80,6 +79,7 @@ |
80 | 79 | import { useDesign } from '/@/hooks/web/useDesign'; |
81 | 80 | |
82 | 81 | import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; |
82 | + import { useLocale } from '/@/locales/useLocale'; | |
83 | 83 | |
84 | 84 | export default defineComponent({ |
85 | 85 | name: 'LayoutHeader', |
... | ... | @@ -112,7 +112,6 @@ |
112 | 112 | getMenuWidth, |
113 | 113 | getIsMixSidebar, |
114 | 114 | } = useMenuSetting(); |
115 | - const { getShowLocale } = useLocaleSetting(); | |
116 | 115 | const { |
117 | 116 | getUseErrorHandle, |
118 | 117 | getShowSettingButton, |
... | ... | @@ -130,6 +129,8 @@ |
130 | 129 | getShowHeader, |
131 | 130 | } = useHeaderSetting(); |
132 | 131 | |
132 | + const { getShowLocalePicker } = useLocale(); | |
133 | + | |
133 | 134 | const { getIsMobile } = useAppInject(); |
134 | 135 | |
135 | 136 | const getHeaderClass = computed(() => { |
... | ... | @@ -185,7 +186,7 @@ |
185 | 186 | getSplit, |
186 | 187 | getMenuMode, |
187 | 188 | getShowTopMenu, |
188 | - getShowLocale, | |
189 | + getShowLocalePicker, | |
189 | 190 | getShowFullScreen, |
190 | 191 | getShowNotice, |
191 | 192 | getUseLockPage, | ... | ... |
src/layouts/default/tabs/useMultipleTabs.ts
1 | 1 | import { toRaw, ref, nextTick } from 'vue'; |
2 | 2 | import { RouteLocationNormalized } from 'vue-router'; |
3 | -import { useProjectSetting } from '/@/hooks/setting'; | |
4 | 3 | import { useDesign } from '/@/hooks/web/useDesign'; |
5 | 4 | import { useSortable } from '/@/hooks/web/useSortable'; |
6 | 5 | import router from '/@/router'; |
7 | 6 | import { tabStore } from '/@/store/modules/tab'; |
8 | 7 | import { isNullAndUnDef } from '/@/utils/is'; |
8 | +import projectSetting from '/@/settings/projectSetting'; | |
9 | 9 | |
10 | 10 | export function initAffixTabs(): string[] { |
11 | 11 | const affixList = ref<RouteLocationNormalized[]>([]); |
... | ... | @@ -47,7 +47,7 @@ export function initAffixTabs(): string[] { |
47 | 47 | } |
48 | 48 | |
49 | 49 | export function useTabsDrag(affixTextList: string[]) { |
50 | - const { multiTabsSetting } = useProjectSetting(); | |
50 | + const { multiTabsSetting } = projectSetting; | |
51 | 51 | |
52 | 52 | const { prefixCls } = useDesign('multiple-tabs'); |
53 | 53 | nextTick(() => { | ... | ... |
src/locales/constant.ts deleted
100644 → 0
src/locales/getMessage.ts deleted
100644 → 0
src/locales/helper.ts
... | ... | @@ -4,16 +4,21 @@ export function genMessage(langs: Record<string, Record<string, any>>, prefix = |
4 | 4 | const obj: Recordable = {}; |
5 | 5 | |
6 | 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 | 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 | 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
src/locales/lang/en/routes/demo/page.ts
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 'vue-i18n'; |
3 | 3 | |
4 | 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 | 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 | 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 | 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 | 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 | 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 | 42 | // Switching the language will change the locale of useI18n |
20 | 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 | 66 | return { |
58 | - setLocale, | |
59 | 67 | getLocale, |
60 | - getLang, | |
68 | + getShowLocalePicker, | |
61 | 69 | changeLocale, |
62 | - antConfigLocale: antConfigLocaleRef, | |
70 | + antConfigLocale, | |
71 | + getAntdLocale, | |
63 | 72 | }; |
64 | 73 | } | ... | ... |
src/logics/error-handle/index.ts
... | ... | @@ -3,9 +3,9 @@ |
3 | 3 | */ |
4 | 4 | |
5 | 5 | import { errorStore, ErrorInfo } from '/@/store/modules/error'; |
6 | -import { useProjectSetting } from '/@/hooks/setting'; | |
7 | 6 | import { ErrorTypeEnum } from '/@/enums/exceptionEnum'; |
8 | 7 | import { App } from 'vue'; |
8 | +import projectSetting from '/@/settings/projectSetting'; | |
9 | 9 | |
10 | 10 | /** |
11 | 11 | * Handling error stack information |
... | ... | @@ -160,7 +160,7 @@ function registerResourceErrorHandler() { |
160 | 160 | * @param app |
161 | 161 | */ |
162 | 162 | export function setupErrorHandle(app: App) { |
163 | - const { useErrorHandle } = useProjectSetting(); | |
163 | + const { useErrorHandle } = projectSetting; | |
164 | 164 | if (!useErrorHandle) return; |
165 | 165 | // Vue exception monitoring; |
166 | 166 | app.config.errorHandler = vueErrorHandler; | ... | ... |
src/logics/initAppConfig.ts
... | ... | @@ -2,25 +2,22 @@ |
2 | 2 | * Application configuration |
3 | 3 | */ |
4 | 4 | |
5 | -import type { ProjectConfig } from '/#/config'; | |
6 | - | |
7 | -import { PROJ_CFG_KEY } from '/@/enums/cacheEnum'; | |
8 | - | |
9 | 5 | import projectSetting from '/@/settings/projectSetting'; |
10 | -import { Persistent } from '/@/utils/cache/persistent'; | |
6 | + | |
11 | 7 | import { updateHeaderBgColor, updateSidebarBgColor } from '/@/logics/theme/updateBackground'; |
12 | 8 | import { updateColorWeak } from '/@/logics/theme/updateColorWeak'; |
13 | 9 | import { updateGrayMode } from '/@/logics/theme/updateGrayMode'; |
14 | 10 | import { changeTheme } from '/@/logics/theme'; |
15 | 11 | |
16 | 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 | 17 | import { primaryColor } from '../../build/config/themeConfig'; |
19 | 18 | |
20 | 19 | // Initial project configuration |
21 | 20 | export function initAppConfigStore() { |
22 | - let projCfg: ProjectConfig = Persistent.getLocal(PROJ_CFG_KEY) as ProjectConfig; | |
23 | - projCfg = deepMerge(projectSetting, projCfg || {}); | |
24 | 21 | try { |
25 | 22 | const { |
26 | 23 | colorWeak, |
... | ... | @@ -28,7 +25,7 @@ export function initAppConfigStore() { |
28 | 25 | themeColor, |
29 | 26 | headerSetting: { bgColor: headerBgColor } = {}, |
30 | 27 | menuSetting: { bgColor } = {}, |
31 | - } = projCfg; | |
28 | + } = projectSetting; | |
32 | 29 | if (themeColor && themeColor !== primaryColor) { |
33 | 30 | changeTheme(themeColor); |
34 | 31 | } |
... | ... | @@ -39,5 +36,27 @@ export function initAppConfigStore() { |
39 | 36 | } catch (error) { |
40 | 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 '/@/components/registerGlobComp'; |
14 | 14 | |
15 | 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 | 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 | 1 | import type { Router } from 'vue-router'; |
2 | -import { useProjectSetting } from '/@/hooks/setting'; | |
3 | 2 | import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel'; |
3 | +import projectSetting from '/@/settings/projectSetting'; | |
4 | 4 | |
5 | 5 | /** |
6 | 6 | * The interface used to close the current page to complete the request when the route is switched |
7 | 7 | * @param router |
8 | 8 | */ |
9 | 9 | export function createHttpGuard(router: Router) { |
10 | - const { removeAllHttpPending } = useProjectSetting(); | |
10 | + const { removeAllHttpPending } = projectSetting; | |
11 | 11 | let axiosCanceler: Nullable<AxiosCanceler>; |
12 | 12 | if (removeAllHttpPending) { |
13 | 13 | axiosCanceler = new AxiosCanceler(); | ... | ... |
src/router/guard/messageGuard.ts
1 | 1 | import type { Router } from 'vue-router'; |
2 | -import { useProjectSetting } from '/@/hooks/setting'; | |
3 | 2 | import { Modal, notification } from 'ant-design-vue'; |
4 | - | |
3 | +import projectSetting from '/@/settings/projectSetting'; | |
5 | 4 | import { warn } from '/@/utils/log'; |
6 | 5 | |
7 | 6 | /** |
... | ... | @@ -9,7 +8,7 @@ import { warn } from '/@/utils/log'; |
9 | 8 | * @param router |
10 | 9 | */ |
11 | 10 | export function createMessageGuard(router: Router) { |
12 | - const { closeMessageOnSwitch } = useProjectSetting(); | |
11 | + const { closeMessageOnSwitch } = projectSetting; | |
13 | 12 | |
14 | 13 | router.beforeEach(async () => { |
15 | 14 | try { | ... | ... |
src/router/index.ts
... | ... | @@ -4,9 +4,11 @@ import type { App } from 'vue'; |
4 | 4 | import { createRouter, createWebHashHistory } from 'vue-router'; |
5 | 5 | |
6 | 6 | import { createGuard } from './guard'; |
7 | -import { basicRoutes } from './routes'; | |
7 | +import { basicRoutes, LoginRoute } from './routes'; | |
8 | 8 | import { REDIRECT_NAME } from './constant'; |
9 | 9 | |
10 | +const WHITE_NAME_LIST = [LoginRoute.name, REDIRECT_NAME]; | |
11 | + | |
10 | 12 | // app router |
11 | 13 | const router = createRouter({ |
12 | 14 | history: createWebHashHistory(), |
... | ... | @@ -17,10 +19,9 @@ const router = createRouter({ |
17 | 19 | |
18 | 20 | // reset router |
19 | 21 | export function resetRouter() { |
20 | - const resetWhiteNameList = ['Login', REDIRECT_NAME]; | |
21 | 22 | router.getRoutes().forEach((route) => { |
22 | 23 | const { name } = route; |
23 | - if (name && !resetWhiteNameList.includes(name as string)) { | |
24 | + if (name && !WHITE_NAME_LIST.includes(name as string)) { | |
24 | 25 | router.hasRoute(name) && router.removeRoute(name); |
25 | 26 | } |
26 | 27 | }); | ... | ... |
src/router/routes/mainOut.ts
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 | 1 | import type { ProjectConfig } from '/#/config'; |
2 | - | |
3 | 2 | import { MenuTypeEnum, MenuModeEnum, TriggerEnum, MixSidebarTriggerEnum } from '/@/enums/menuEnum'; |
4 | 3 | import { CacheTypeEnum } from '/@/enums/cacheEnum'; |
5 | 4 | import { |
... | ... | @@ -26,8 +25,8 @@ const setting: ProjectConfig = { |
26 | 25 | permissionCacheType: CacheTypeEnum.LOCAL, |
27 | 26 | |
28 | 27 | // color |
29 | - // TODO Theme color | |
30 | 28 | themeColor: primaryColor, |
29 | + | |
31 | 30 | // TODO dark theme |
32 | 31 | themeMode: themeMode, |
33 | 32 | |
... | ... | @@ -49,17 +48,6 @@ const setting: ProjectConfig = { |
49 | 48 | // Whether to show footer |
50 | 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 | 51 | // Header configuration |
64 | 52 | headerSetting: { |
65 | 53 | // header bg color | ... | ... |
src/store/modules/error.ts
... | ... | @@ -4,7 +4,7 @@ import { VuexModule, getModule, Module, Mutation, Action } from 'vuex-module-dec |
4 | 4 | |
5 | 5 | import { formatToDateTime } from '/@/utils/dateUtil'; |
6 | 6 | import { ErrorTypeEnum } from '/@/enums/exceptionEnum'; |
7 | -import { useProjectSetting } from '/@/hooks/setting'; | |
7 | +import projectSetting from '/@/settings/projectSetting'; | |
8 | 8 | |
9 | 9 | export interface ErrorInfo { |
10 | 10 | type: ErrorTypeEnum; |
... | ... | @@ -57,7 +57,7 @@ class Error extends VuexModule implements ErrorState { |
57 | 57 | |
58 | 58 | @Action |
59 | 59 | setupErrorHandle(error: any) { |
60 | - const { useErrorHandle } = useProjectSetting(); | |
60 | + const { useErrorHandle } = projectSetting; | |
61 | 61 | if (!useErrorHandle) return; |
62 | 62 | |
63 | 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 | 88 | let routes: AppRouteRecordRaw[] = []; |
89 | 89 | const roleList = toRaw(userStore.getRoleListState); |
90 | 90 | |
91 | - const { permissionMode } = appStore.getProjectConfig; | |
91 | + const { permissionMode = PermissionModeEnum.ROLE } = appStore.getProjectConfig; | |
92 | 92 | |
93 | 93 | // role permissions |
94 | 94 | if (permissionMode === PermissionModeEnum.ROLE) { | ... | ... |
src/utils/cache/index.ts
... | ... | @@ -11,7 +11,6 @@ const createOptions = (storage: Storage, options: Options = {}): Options => { |
11 | 11 | hasEncrypt: enableStorageEncryption, |
12 | 12 | storage, |
13 | 13 | prefixKey: getStorageShortName(), |
14 | - | |
15 | 14 | ...options, |
16 | 15 | }; |
17 | 16 | }; |
... | ... | @@ -22,11 +21,12 @@ export const createStorage = (storage: Storage = sessionStorage, options: Option |
22 | 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 | 32 | export default WebStorage; | ... | ... |
src/utils/cache/memory.ts
... | ... | @@ -57,7 +57,7 @@ export class Memory<T = any, V = any> { |
57 | 57 | if (!expires) { |
58 | 58 | return value; |
59 | 59 | } |
60 | - item.time = new Date().getTime() + this.alive * 1000; | |
60 | + item.time = new Date().getTime() + this.alive; | |
61 | 61 | item.timeoutId = setTimeout(() => { |
62 | 62 | this.remove(key); |
63 | 63 | }, expires); |
... | ... | @@ -80,7 +80,7 @@ export class Memory<T = any, V = any> { |
80 | 80 | const item = cache[k]; |
81 | 81 | if (item && item.time) { |
82 | 82 | const now = new Date().getTime(); |
83 | - const expire = now + item.time * 1000; | |
83 | + const expire = item.time; | |
84 | 84 | if (expire > now) { |
85 | 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 | 2 | import { Memory } from './memory'; |
3 | 3 | import { |
4 | 4 | TOKEN_KEY, |
... | ... | @@ -28,19 +28,19 @@ export type BasicKeys = keyof BasicStore; |
28 | 28 | type LocalKeys = keyof LocalStore; |
29 | 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 | 34 | const localMemory = new Memory(DEFAULT_CACHE_TIME); |
35 | 35 | const sessionMemory = new Memory(DEFAULT_CACHE_TIME); |
36 | 36 | |
37 | -function initMemory() { | |
37 | +function initPersistentMemory() { | |
38 | 38 | const localCache = ls.get(APP_LOCAL_CACHE_KEY); |
39 | 39 | const sessionCache = ls.get(APP_SESSION_CACHE_KEY); |
40 | 40 | localCache && localMemory.resetCache(localCache); |
41 | 41 | sessionCache && sessionMemory.resetCache(sessionCache); |
42 | 42 | } |
43 | -initMemory(); | |
43 | + | |
44 | 44 | export class Persistent { |
45 | 45 | static getLocal<T>(key: LocalKeys) { |
46 | 46 | return localMemory.get(key)?.value as Nullable<T>; |
... | ... | @@ -106,4 +106,4 @@ function storageChange(e: any) { |
106 | 106 | |
107 | 107 | window.addEventListener('storage', storageChange); |
108 | 108 | |
109 | -export default {}; | |
109 | +initPersistentMemory(); | ... | ... |
src/utils/env.ts
... | ... | @@ -2,19 +2,26 @@ import type { GlobEnvConfig } from '/#/config'; |
2 | 2 | |
3 | 3 | import { useGlobSetting } from '/@/hooks/setting'; |
4 | 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 | 12 | // Generate cache key according to version |
15 | 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 | 1 | import { dateUtil } from '/@/utils/dateUtil'; |
2 | 2 | import { reactive, toRefs } from 'vue'; |
3 | 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 | 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 | 8 | let timer: IntervalHandle; |
10 | 9 | |
11 | 10 | const state = reactive({ | ... | ... |
src/views/sys/login/Login.vue
... | ... | @@ -53,9 +53,10 @@ |
53 | 53 | import MobileForm from './MobileForm.vue'; |
54 | 54 | import QrCodeForm from './QrCodeForm.vue'; |
55 | 55 | |
56 | - import { useGlobSetting, useProjectSetting } from '/@/hooks/setting'; | |
56 | + import { useGlobSetting } from '/@/hooks/setting'; | |
57 | 57 | import { useI18n } from '/@/hooks/web/useI18n'; |
58 | 58 | import { useDesign } from '/@/hooks/web/useDesign'; |
59 | + import { localeStore } from '/@/store/modules/locale'; | |
59 | 60 | |
60 | 61 | export default defineComponent({ |
61 | 62 | name: 'Login', |
... | ... | @@ -71,14 +72,13 @@ |
71 | 72 | setup() { |
72 | 73 | const globSetting = useGlobSetting(); |
73 | 74 | const { prefixCls } = useDesign('login'); |
74 | - const { locale } = useProjectSetting(); | |
75 | 75 | const { t } = useI18n(); |
76 | 76 | |
77 | 77 | return { |
78 | 78 | t, |
79 | 79 | prefixCls, |
80 | 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 | 8 | } from '/@/enums/appEnum'; |
9 | 9 | |
10 | 10 | import { CacheTypeEnum } from '/@/enums/cacheEnum'; |
11 | -import type { LocaleType } from '/@/locales/types'; | |
12 | 11 | import { ThemeMode } from '../build/config/themeConfig'; |
13 | 12 | |
13 | +export type LocaleType = 'zh_CN' | 'en' | 'ru' | 'ja' | 'ko'; | |
14 | + | |
14 | 15 | export interface MenuSetting { |
15 | 16 | bgColor: string; |
16 | 17 | fixed: boolean; |
... | ... | @@ -57,9 +58,9 @@ export interface HeaderSetting { |
57 | 58 | } |
58 | 59 | |
59 | 60 | export interface LocaleSetting { |
60 | - show: boolean; | |
61 | + showPicker: boolean; | |
61 | 62 | // Current language |
62 | - lang: LocaleType; | |
63 | + locale: LocaleType; | |
63 | 64 | // default language |
64 | 65 | fallback: LocaleType; |
65 | 66 | // available Locales |
... | ... | @@ -78,8 +79,6 @@ export interface TransitionSetting { |
78 | 79 | } |
79 | 80 | |
80 | 81 | export interface ProjectConfig { |
81 | - // Multilingual configuration | |
82 | - locale: LocaleSetting; | |
83 | 82 | // Storage location of permission related information |
84 | 83 | permissionCacheType: CacheTypeEnum; |
85 | 84 | // Whether to show the configuration button | ... | ... |
types/module.d.ts
... | ... | @@ -4,7 +4,7 @@ declare module 'ant-design-vue/es/locale/*' { |
4 | 4 | export default locale as Locale & ReadonlyRecordable; |
5 | 5 | } |
6 | 6 | |
7 | -declare module 'moment/locale/*' { | |
7 | +declare module 'moment/dist/locale/*' { | |
8 | 8 | import { LocaleSpecification } from 'moment'; |
9 | 9 | const locale: LocaleSpecification & ReadonlyRecordable; |
10 | 10 | export default locale; | ... | ... |
yarn.lock
... | ... | @@ -1718,10 +1718,10 @@ |
1718 | 1718 | dependencies: |
1719 | 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 | 1725 | dependencies: |
1726 | 1726 | esbuild "^0.8.52" |
1727 | 1727 | esbuild-register "^2.0.0" |
... | ... | @@ -1849,10 +1849,10 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: |
1849 | 1849 | dependencies: |
1850 | 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 | 1856 | dependencies: |
1857 | 1857 | "@ant-design-vue/use" "^0.0.1-0" |
1858 | 1858 | "@ant-design/icons-vue" "^6.0.0" |
... | ... | @@ -8968,12 +8968,12 @@ vite-plugin-theme@^0.4.8: |
8968 | 8968 | es-module-lexer "^0.3.26" |
8969 | 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 | 8975 | dependencies: |
8976 | - "@windicss/plugin-utils" "0.5.4" | |
8976 | + "@windicss/plugin-utils" "0.6.0" | |
8977 | 8977 | windicss "^2.2.0" |
8978 | 8978 | |
8979 | 8979 | vite@2.0.4: | ... | ... |