Commit 737b1b190cf288d7ab7ad7e9bd21acb9da16cc6c
1 parent
b49950a3
wip: multi-language support
Showing
59 changed files
with
512 additions
and
272 deletions
CHANGELOG.zh_CN.md
package.json
@@ -26,6 +26,7 @@ | @@ -26,6 +26,7 @@ | ||
26 | "ant-design-vue": "2.0.0-beta.15", | 26 | "ant-design-vue": "2.0.0-beta.15", |
27 | "apexcharts": "3.22.0", | 27 | "apexcharts": "3.22.0", |
28 | "axios": "^0.21.0", | 28 | "axios": "^0.21.0", |
29 | + "crypto-es": "^1.2.6", | ||
29 | "echarts": "^4.9.0", | 30 | "echarts": "^4.9.0", |
30 | "lodash-es": "^4.17.15", | 31 | "lodash-es": "^4.17.15", |
31 | "mockjs": "^1.1.0", | 32 | "mockjs": "^1.1.0", |
src/App.vue
1 | <template> | 1 | <template> |
2 | - <ConfigProvider v-bind="lockEvent" :locale="zhCN" :transform-cell-text="transformCellText"> | 2 | + <ConfigProvider |
3 | + v-bind="lockEvent" | ||
4 | + :locale="antConfigLocale" | ||
5 | + :transform-cell-text="transformCellText" | ||
6 | + > | ||
3 | <router-view /> | 7 | <router-view /> |
4 | </ConfigProvider> | 8 | </ConfigProvider> |
5 | </template> | 9 | </template> |
@@ -7,16 +11,12 @@ | @@ -7,16 +11,12 @@ | ||
7 | <script lang="ts"> | 11 | <script lang="ts"> |
8 | import { defineComponent } from 'vue'; | 12 | import { defineComponent } from 'vue'; |
9 | import { ConfigProvider } from 'ant-design-vue'; | 13 | import { ConfigProvider } from 'ant-design-vue'; |
10 | - import { createBreakpointListen } from '/@/hooks/event/useBreakpoint'; | ||
11 | - | ||
12 | - import zhCN from 'ant-design-vue/es/locale/zh_CN'; | ||
13 | - import moment from 'moment'; | ||
14 | - import 'moment/dist/locale/zh-cn'; | ||
15 | 14 | ||
16 | import { getConfigProvider, initAppConfigStore } from '/@/setup/App'; | 15 | import { getConfigProvider, initAppConfigStore } from '/@/setup/App'; |
17 | - import { useLockPage } from '/@/hooks/web/useLockPage'; | ||
18 | 16 | ||
19 | - moment.locale('zh-cn'); | 17 | + import { useLockPage } from '/@/hooks/web/useLockPage'; |
18 | + import { useLocale } from '/@/hooks/web/useLocale'; | ||
19 | + import { createBreakpointListen } from '/@/hooks/event/useBreakpoint'; | ||
20 | 20 | ||
21 | export default defineComponent({ | 21 | export default defineComponent({ |
22 | name: 'App', | 22 | name: 'App', |
@@ -34,9 +34,12 @@ | @@ -34,9 +34,12 @@ | ||
34 | // Create a lock screen monitor | 34 | // Create a lock screen monitor |
35 | const lockEvent = useLockPage(); | 35 | const lockEvent = useLockPage(); |
36 | 36 | ||
37 | + // support Multi-language | ||
38 | + const { antConfigLocale } = useLocale(); | ||
39 | + | ||
37 | return { | 40 | return { |
38 | transformCellText, | 41 | transformCellText, |
39 | - zhCN, | 42 | + antConfigLocale, |
40 | lockEvent, | 43 | lockEvent, |
41 | }; | 44 | }; |
42 | }, | 45 | }, |
src/components/Application/index.ts
0 → 100644
src/components/Application/src/AppLocalPicker.vue
0 → 100644
1 | +<template> | ||
2 | + <Dropdown | ||
3 | + :trigger="['click']" | ||
4 | + :dropMenuList="localeList" | ||
5 | + :selectedKeys="selectedKeys" | ||
6 | + @menuEvent="handleMenuEvent" | ||
7 | + > | ||
8 | + <GlobalOutlined class="app-locale" /> | ||
9 | + </Dropdown> | ||
10 | +</template> | ||
11 | +<script lang="ts"> | ||
12 | + import { defineComponent, ref, watchEffect, unref } from 'vue'; | ||
13 | + | ||
14 | + import { Dropdown, DropMenu } from '/@/components/Dropdown'; | ||
15 | + import { GlobalOutlined } from '@ant-design/icons-vue'; | ||
16 | + | ||
17 | + import { useLocale } from '/@/hooks/web/useLocale'; | ||
18 | + import { useLocaleSetting } from '/@/settings/use/useLocaleSetting'; | ||
19 | + | ||
20 | + import { LocaleType } from '/@/locales/types'; | ||
21 | + | ||
22 | + export default defineComponent({ | ||
23 | + name: 'AppLocalPicker', | ||
24 | + components: { GlobalOutlined, Dropdown }, | ||
25 | + setup() { | ||
26 | + const { localeList } = useLocaleSetting(); | ||
27 | + const selectedKeys = ref<string[]>([]); | ||
28 | + | ||
29 | + const { changeLocale, getLang } = useLocale(); | ||
30 | + | ||
31 | + watchEffect(() => { | ||
32 | + selectedKeys.value = [unref(getLang)]; | ||
33 | + }); | ||
34 | + | ||
35 | + function toggleLocale(lang: LocaleType | string) { | ||
36 | + changeLocale(lang as LocaleType); | ||
37 | + selectedKeys.value = [lang as string]; | ||
38 | + } | ||
39 | + | ||
40 | + function handleMenuEvent(menu: DropMenu) { | ||
41 | + toggleLocale(menu.event as string); | ||
42 | + } | ||
43 | + | ||
44 | + return { localeList, handleMenuEvent, selectedKeys }; | ||
45 | + }, | ||
46 | + }); | ||
47 | +</script> | ||
48 | + | ||
49 | +<style lang="less" scoped> | ||
50 | + .app-locale { | ||
51 | + cursor: pointer; | ||
52 | + } | ||
53 | +</style> |
src/components/Dropdown/index.ts
src/components/Dropdown/Dropdown.tsx renamed to src/components/Dropdown/src/Dropdown.tsx
1 | import { defineComponent, computed, unref } from 'vue'; | 1 | import { defineComponent, computed, unref } from 'vue'; |
2 | -import { Dropdown, Menu } from 'ant-design-vue'; | 2 | +import { Dropdown, Menu, Divider } from 'ant-design-vue'; |
3 | 3 | ||
4 | import Icon from '/@/components/Icon/index'; | 4 | import Icon from '/@/components/Icon/index'; |
5 | 5 | ||
6 | import { basicDropdownProps } from './props'; | 6 | import { basicDropdownProps } from './props'; |
7 | import { getSlot } from '/@/utils/helper/tsxHelper'; | 7 | import { getSlot } from '/@/utils/helper/tsxHelper'; |
8 | +import { Trigger } from './types'; | ||
8 | 9 | ||
9 | export default defineComponent({ | 10 | export default defineComponent({ |
10 | name: 'Dropdown', | 11 | name: 'Dropdown', |
11 | props: basicDropdownProps, | 12 | props: basicDropdownProps, |
13 | + emits: ['menuEvent'], | ||
12 | setup(props, { slots, emit, attrs }) { | 14 | setup(props, { slots, emit, attrs }) { |
13 | const getMenuList = computed(() => props.dropMenuList); | 15 | const getMenuList = computed(() => props.dropMenuList); |
14 | 16 | ||
15 | function handleClickMenu({ key }: any) { | 17 | function handleClickMenu({ key }: any) { |
16 | - const menu = unref(getMenuList)[key]; | 18 | + const menu = unref(getMenuList).find((item) => item.event === key); |
17 | emit('menuEvent', menu); | 19 | emit('menuEvent', menu); |
18 | } | 20 | } |
19 | 21 | ||
20 | function renderMenus() { | 22 | function renderMenus() { |
21 | return ( | 23 | return ( |
22 | - <Menu onClick={handleClickMenu}> | 24 | + <Menu onClick={handleClickMenu} selectedKeys={props.selectedKeys}> |
23 | {() => ( | 25 | {() => ( |
24 | <> | 26 | <> |
25 | {unref(getMenuList).map((item, index) => { | 27 | {unref(getMenuList).map((item, index) => { |
26 | - const { disabled, icon, text, divider } = item; | ||
27 | - | 28 | + const { disabled, icon, text, divider, event } = item; |
28 | return [ | 29 | return [ |
29 | - <Menu.Item key={`${index}`} disabled={disabled}> | 30 | + <Menu.Item key={`${event}`} disabled={disabled}> |
30 | {() => ( | 31 | {() => ( |
31 | <> | 32 | <> |
32 | {icon && <Icon icon={icon} />} | 33 | {icon && <Icon icon={icon} />} |
@@ -34,8 +35,7 @@ export default defineComponent({ | @@ -34,8 +35,7 @@ export default defineComponent({ | ||
34 | </> | 35 | </> |
35 | )} | 36 | )} |
36 | </Menu.Item>, | 37 | </Menu.Item>, |
37 | - // @ts-ignore | ||
38 | - divider && <Menu.Divider key={`d-${index}`} />, | 38 | + divider && <Divider key={`d-${index}`} />, |
39 | ]; | 39 | ]; |
40 | })} | 40 | })} |
41 | </> | 41 | </> |
@@ -45,7 +45,7 @@ export default defineComponent({ | @@ -45,7 +45,7 @@ export default defineComponent({ | ||
45 | } | 45 | } |
46 | 46 | ||
47 | return () => ( | 47 | return () => ( |
48 | - <Dropdown trigger={props.trigger as any} {...attrs}> | 48 | + <Dropdown trigger={props.trigger as Trigger[]} {...attrs}> |
49 | {{ | 49 | {{ |
50 | default: () => <span>{getSlot(slots)}</span>, | 50 | default: () => <span>{getSlot(slots)}</span>, |
51 | overlay: () => renderMenus(), | 51 | overlay: () => renderMenus(), |
src/components/Dropdown/props.ts renamed to src/components/Dropdown/src/props.ts
1 | import type { PropType } from 'vue'; | 1 | import type { PropType } from 'vue'; |
2 | +import type { DropMenu } from './types'; | ||
2 | 3 | ||
3 | export const dropdownProps = { | 4 | export const dropdownProps = { |
4 | /** | 5 | /** |
@@ -15,7 +16,11 @@ export const dropdownProps = { | @@ -15,7 +16,11 @@ export const dropdownProps = { | ||
15 | }; | 16 | }; |
16 | export const basicDropdownProps = Object.assign({}, dropdownProps, { | 17 | export const basicDropdownProps = Object.assign({}, dropdownProps, { |
17 | dropMenuList: { | 18 | dropMenuList: { |
18 | - type: Array as PropType<any[]>, | 19 | + type: Array as PropType<DropMenu[]>, |
20 | + default: () => [], | ||
21 | + }, | ||
22 | + selectedKeys: { | ||
23 | + type: Array as PropType<string[]>, | ||
19 | default: () => [], | 24 | default: () => [], |
20 | }, | 25 | }, |
21 | }); | 26 | }); |
src/components/Dropdown/types.ts renamed to src/components/Dropdown/src/types.ts
src/hooks/web/useI18n.ts deleted
100644 → 0
1 | -import { createI18n } from 'vue-i18n'; | ||
2 | -import { ref, watch } from 'vue'; | ||
3 | -import type { I18nOptions } from 'vue-i18n'; | ||
4 | -export function useI18n(options?: I18nOptions) { | ||
5 | - const i18n = createI18n(options); | ||
6 | - | ||
7 | - const localeRef = ref(i18n.global.locale); | ||
8 | - | ||
9 | - watch(localeRef, () => { | ||
10 | - i18n.global.locale = localeRef.value as any; | ||
11 | - }); | ||
12 | - return { | ||
13 | - t: i18n.global.t, | ||
14 | - localeRef, | ||
15 | - }; | ||
16 | -} |
src/hooks/web/useLocale.ts
1 | +/** | ||
2 | + * Multi-language related operations | ||
3 | + */ | ||
1 | import type { LocaleType } from '/@/locales/types'; | 4 | import type { LocaleType } from '/@/locales/types'; |
2 | -import { appStore } from '/@/store/modules/app'; | 5 | + |
6 | +import { unref, ref } from 'vue'; | ||
7 | + | ||
8 | +import { getI18n } from '/@/setup/i18n'; | ||
9 | + | ||
10 | +import { useLocaleSetting } from '/@/settings/use/useLocaleSetting'; | ||
11 | + | ||
12 | +import moment from 'moment'; | ||
13 | + | ||
14 | +import 'moment/dist/locale/zh-cn'; | ||
15 | + | ||
16 | +moment.locale('zh-cn'); | ||
17 | + | ||
18 | +const antConfigLocaleRef = ref<any>(null); | ||
3 | 19 | ||
4 | export function useLocale() { | 20 | export function useLocale() { |
5 | - /** | ||
6 | - * | ||
7 | - */ | ||
8 | - function getLocale(): string { | ||
9 | - return appStore.getProjectConfig.locale; | 21 | + const { getLang, getLocale, setLocale: setLocalSetting } = useLocaleSetting(); |
22 | + | ||
23 | + // Switching the language will change the locale of useI18n | ||
24 | + // And submit to configuration modification | ||
25 | + function changeLocale(lang: LocaleType): void { | ||
26 | + (getI18n().global.locale as any).value = lang; | ||
27 | + setLocalSetting({ lang }); | ||
28 | + // i18n.global.setLocaleMessage(locale, messages); | ||
29 | + | ||
30 | + antConfigLocaleRef.value = { a: 1 }; | ||
31 | + switch (lang) { | ||
32 | + // Simplified Chinese | ||
33 | + case 'zh_CN': | ||
34 | + import('ant-design-vue/es/locale/zh_CN').then((locale) => { | ||
35 | + antConfigLocaleRef.value = locale.default; | ||
36 | + }); | ||
37 | + | ||
38 | + moment.locale('cn'); | ||
39 | + break; | ||
40 | + // English | ||
41 | + case 'en': | ||
42 | + import('ant-design-vue/es/locale/en_US').then((locale) => { | ||
43 | + antConfigLocaleRef.value = locale.default; | ||
44 | + }); | ||
45 | + moment.locale('en-us'); | ||
46 | + break; | ||
47 | + | ||
48 | + // other | ||
49 | + default: | ||
50 | + break; | ||
51 | + } | ||
10 | } | 52 | } |
11 | 53 | ||
12 | - /** | ||
13 | - * | ||
14 | - * @param locale | ||
15 | - */ | ||
16 | - async function changeLocale(locale: LocaleType): Promise<void> { | ||
17 | - appStore.commitProjectConfigState({ locale: locale }); | 54 | + // initialization |
55 | + function setupLocale() { | ||
56 | + const lang = unref(getLang); | ||
57 | + lang && changeLocale(lang); | ||
18 | } | 58 | } |
19 | 59 | ||
20 | - return { getLocale, changeLocale }; | 60 | + return { |
61 | + setupLocale, | ||
62 | + getLocale, | ||
63 | + getLang, | ||
64 | + changeLocale, | ||
65 | + antConfigLocale: antConfigLocaleRef, | ||
66 | + }; | ||
67 | +} | ||
68 | + | ||
69 | +/** | ||
70 | + * For non-setup use | ||
71 | + */ | ||
72 | +export function useExternalI18n() { | ||
73 | + return getI18n().global; | ||
21 | } | 74 | } |
src/hooks/web/useMessage.tsx
@@ -3,7 +3,6 @@ import type { ModalFunc, ModalFuncProps } from 'ant-design-vue/lib/modal/Modal'; | @@ -3,7 +3,6 @@ import type { ModalFunc, ModalFuncProps } from 'ant-design-vue/lib/modal/Modal'; | ||
3 | import { Modal, message as Message, notification } from 'ant-design-vue'; | 3 | import { Modal, message as Message, notification } from 'ant-design-vue'; |
4 | import { InfoCircleFilled, CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons-vue'; | 4 | import { InfoCircleFilled, CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons-vue'; |
5 | 5 | ||
6 | -import { useSetting } from '/@/hooks/core/useSetting'; | ||
7 | import { ArgsProps, ConfigProps } from 'ant-design-vue/lib/notification'; | 6 | import { ArgsProps, ConfigProps } from 'ant-design-vue/lib/notification'; |
8 | 7 | ||
9 | export interface NotifyApi { | 8 | export interface NotifyApi { |
@@ -33,8 +32,6 @@ interface ConfirmOptions { | @@ -33,8 +32,6 @@ interface ConfirmOptions { | ||
33 | warning: ModalFunc; | 32 | warning: ModalFunc; |
34 | } | 33 | } |
35 | 34 | ||
36 | -const { projectSetting } = useSetting(); | ||
37 | - | ||
38 | function getIcon(iconType: string) { | 35 | function getIcon(iconType: string) { |
39 | if (iconType === 'warning') { | 36 | if (iconType === 'warning') { |
40 | return <InfoCircleFilled class="modal-icon-warning" />; | 37 | return <InfoCircleFilled class="modal-icon-warning" />; |
@@ -60,7 +57,6 @@ function createConfirm(options: ModalOptionsEx): ConfirmOptions { | @@ -60,7 +57,6 @@ function createConfirm(options: ModalOptionsEx): ConfirmOptions { | ||
60 | const opt: ModalFuncProps = { | 57 | const opt: ModalFuncProps = { |
61 | centered: true, | 58 | centered: true, |
62 | icon: getIcon(iconType), | 59 | icon: getIcon(iconType), |
63 | - ...projectSetting.messageSetting, | ||
64 | ...options, | 60 | ...options, |
65 | }; | 61 | }; |
66 | return Modal.confirm(opt) as any; | 62 | return Modal.confirm(opt) as any; |
src/layouts/default/setting/SettingDrawer.tsx
@@ -5,7 +5,6 @@ import Button from '/@/components/Button/index.vue'; | @@ -5,7 +5,6 @@ import Button from '/@/components/Button/index.vue'; | ||
5 | import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; | 5 | import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; |
6 | import { CopyOutlined, RedoOutlined, CheckOutlined } from '@ant-design/icons-vue'; | 6 | import { CopyOutlined, RedoOutlined, CheckOutlined } from '@ant-design/icons-vue'; |
7 | import { appStore } from '/@/store/modules/app'; | 7 | import { appStore } from '/@/store/modules/app'; |
8 | -import { userStore } from '/@/store/modules/user'; | ||
9 | import { ProjectConfig } from '/@/types/config'; | 8 | import { ProjectConfig } from '/@/types/config'; |
10 | 9 | ||
11 | import { useMessage } from '/@/hooks/web/useMessage'; | 10 | import { useMessage } from '/@/hooks/web/useMessage'; |
@@ -97,7 +96,7 @@ export default defineComponent({ | @@ -97,7 +96,7 @@ export default defineComponent({ | ||
97 | 96 | ||
98 | function handleClearAndRedo() { | 97 | function handleClearAndRedo() { |
99 | localStorage.clear(); | 98 | localStorage.clear(); |
100 | - userStore.resumeAllState(); | 99 | + appStore.resumeAllState(); |
101 | location.reload(); | 100 | location.reload(); |
102 | } | 101 | } |
103 | 102 |
src/layouts/logo/index.vue
@@ -7,7 +7,7 @@ | @@ -7,7 +7,7 @@ | ||
7 | <script lang="ts"> | 7 | <script lang="ts"> |
8 | import { computed, defineComponent, PropType, ref, watch } from 'vue'; | 8 | import { computed, defineComponent, PropType, ref, watch } from 'vue'; |
9 | // hooks | 9 | // hooks |
10 | - import { useSetting } from '/@/hooks/core/useSetting'; | 10 | + import { useGlobSetting } from '/@/settings/use'; |
11 | import { useTimeoutFn } from '/@/hooks/core/useTimeout'; | 11 | import { useTimeoutFn } from '/@/hooks/core/useTimeout'; |
12 | import { useGo } from '/@/hooks/web/usePage'; | 12 | import { useGo } from '/@/hooks/web/usePage'; |
13 | 13 | ||
@@ -30,7 +30,7 @@ | @@ -30,7 +30,7 @@ | ||
30 | }, | 30 | }, |
31 | setup(props) { | 31 | setup(props) { |
32 | const showRef = ref<boolean>(!!props.showTitle); | 32 | const showRef = ref<boolean>(!!props.showTitle); |
33 | - const { globSetting } = useSetting(); | 33 | + const globSetting = useGlobSetting(); |
34 | const go = useGo(); | 34 | const go = useGo(); |
35 | 35 | ||
36 | function handleGoHome() { | 36 | function handleGoHome() { |
src/layouts/page/index.tsx
@@ -4,7 +4,7 @@ import { RouterView, RouteLocation } from 'vue-router'; | @@ -4,7 +4,7 @@ import { RouterView, RouteLocation } from 'vue-router'; | ||
4 | import FrameLayout from '/@/layouts/iframe/index.vue'; | 4 | import FrameLayout from '/@/layouts/iframe/index.vue'; |
5 | 5 | ||
6 | import { useTransition } from './useTransition'; | 6 | import { useTransition } from './useTransition'; |
7 | -import { useSetting } from '/@/hooks/core/useSetting'; | 7 | +import { useProjectSetting } from '/@/settings/use'; |
8 | 8 | ||
9 | import { tabStore } from '/@/store/modules/tab'; | 9 | import { tabStore } from '/@/store/modules/tab'; |
10 | import { appStore } from '/@/store/modules/app'; | 10 | import { appStore } from '/@/store/modules/app'; |
@@ -29,7 +29,7 @@ export default defineComponent({ | @@ -29,7 +29,7 @@ export default defineComponent({ | ||
29 | const { on: transitionOn } = useTransition(); | 29 | const { on: transitionOn } = useTransition(); |
30 | on = transitionOn; | 30 | on = transitionOn; |
31 | } | 31 | } |
32 | - const { projectSetting } = useSetting(); | 32 | + const projectSetting = useProjectSetting(); |
33 | return () => { | 33 | return () => { |
34 | const { | 34 | const { |
35 | routerTransition, | 35 | routerTransition, |
src/locales/index.ts
1 | import messages from 'globby?locale!/@/locales/lang/**/*.@(ts)'; | 1 | import messages from 'globby?locale!/@/locales/lang/**/*.@(ts)'; |
2 | 2 | ||
3 | +import type { DropMenu } from '/@/components/Dropdown'; | ||
4 | + | ||
5 | +// locale list | ||
6 | +export const localeList: DropMenu[] = [ | ||
7 | + { | ||
8 | + text: '简体中文', | ||
9 | + event: 'zh_CN', | ||
10 | + }, | ||
11 | + { | ||
12 | + text: 'English', | ||
13 | + event: 'en', | ||
14 | + }, | ||
15 | +]; | ||
16 | + | ||
3 | export default messages; | 17 | export default messages; |
src/locales/lang/en/sys/errorLog.ts
0 → 100644
1 | +export default { | ||
2 | + tableTitle: 'Error log list', | ||
3 | + tableColumnType: 'Type', | ||
4 | + tableColumnDate: 'Time', | ||
5 | + tableColumnFile: 'File', | ||
6 | + tableColumnMsg: 'Error message', | ||
7 | + tableColumnStackMsg: 'Stack info', | ||
8 | + | ||
9 | + tableActionDesc: 'Details', | ||
10 | + | ||
11 | + modalTitle: 'Error details', | ||
12 | + | ||
13 | + fireVueError: 'Fire vue error', | ||
14 | + fireResourceError: 'Fire resource error', | ||
15 | + fireAjaxError: 'Fire ajax error', | ||
16 | + | ||
17 | + enableMessage: 'Only effective when useErrorHandle=true in `/src/settings/projectSetting.ts`.', | ||
18 | +}; |
src/locales/lang/en/sys/exception.ts
0 → 100644
1 | +export default { | ||
2 | + backLogin: 'Back Login', | ||
3 | + backHome: 'Back Home', | ||
4 | + redo: 'Refresh', | ||
5 | + subTitle403: "Sorry, you don't have access to this page.", | ||
6 | + subTitle404: 'Sorry, the page you visited does not exist.', | ||
7 | + subTitle500: 'Sorry, the server is reporting an error.', | ||
8 | + noDataTitle: 'No data on the current page.', | ||
9 | + networkErrorTitle: 'Network Error', | ||
10 | + networkErrorSubTitle: | ||
11 | + 'Sorry,Your network connection has been disconnected, please check your network!', | ||
12 | +}; |
src/locales/lang/en/sys/lock.ts
0 → 100644
src/locales/lang/en/sys/login.ts
0 → 100644
1 | +export default { | ||
2 | + loginButton: 'Login', | ||
3 | + autoLogin: 'AutoLogin', | ||
4 | + forgetPassword: 'Forget Password', | ||
5 | + | ||
6 | + // notify | ||
7 | + loginSuccessTitle: 'Login successful', | ||
8 | + loginSuccessDesc: 'Welcome back', | ||
9 | + | ||
10 | + // placeholder | ||
11 | + accountPlaceholder: 'Please input Username', | ||
12 | + passwordPlaceholder: 'Please input Password', | ||
13 | +}; |
src/locales/lang/en/system/basic.ts deleted
100644 → 0
src/locales/lang/en/system/login.ts deleted
100644 → 0
src/locales/lang/ru/routes/menus/dashboard.ts deleted
100644 → 0
src/locales/lang/ru/system/basic.ts deleted
100644 → 0
src/locales/lang/ru/system/login.ts deleted
100644 → 0
src/locales/lang/zhCN/system/basic.ts deleted
100644 → 0
src/locales/lang/zhCN/system/login.ts deleted
100644 → 0
src/locales/lang/zhCN/routes/menus/dashboard.ts renamed to src/locales/lang/zh_CN/routes/menus/dashboard.ts
src/locales/lang/zh_CN/sys/errorLog.ts
0 → 100644
1 | +export default { | ||
2 | + tableTitle: '错误日志列表', | ||
3 | + tableColumnType: '类型', | ||
4 | + tableColumnDate: '时间', | ||
5 | + tableColumnFile: '文件', | ||
6 | + tableColumnMsg: '错误信息', | ||
7 | + tableColumnStackMsg: 'stack信息', | ||
8 | + | ||
9 | + tableActionDesc: '详情', | ||
10 | + | ||
11 | + modalTitle: '错误详情', | ||
12 | + | ||
13 | + fireVueError: '点击触发vue错误', | ||
14 | + fireResourceError: '点击触发资源加载错误', | ||
15 | + fireAjaxError: '点击触发ajax错误', | ||
16 | + | ||
17 | + enableMessage: '只在`/src/settings/projectSetting.ts` 内的useErrorHandle=true时生效.', | ||
18 | +}; |
src/locales/lang/zh_CN/sys/exception.ts
0 → 100644
src/locales/lang/zh_CN/sys/lock.ts
0 → 100644
src/locales/lang/zh_CN/sys/login.ts
0 → 100644
src/locales/types.ts
src/router/guard/index.ts
@@ -6,7 +6,7 @@ import { createProgressGuard } from './progressGuard'; | @@ -6,7 +6,7 @@ import { createProgressGuard } from './progressGuard'; | ||
6 | import { createPermissionGuard } from './permissionGuard'; | 6 | import { createPermissionGuard } from './permissionGuard'; |
7 | import { createPageLoadingGuard } from './pageLoadingGuard'; | 7 | import { createPageLoadingGuard } from './pageLoadingGuard'; |
8 | 8 | ||
9 | -import { useSetting } from '/@/hooks/core/useSetting'; | 9 | +import { useGlobSetting, useProjectSetting } from '/@/settings/use'; |
10 | 10 | ||
11 | import { getIsOpenTab, setCurrentTo } from '/@/utils/helper/routeHelper'; | 11 | import { getIsOpenTab, setCurrentTo } from '/@/utils/helper/routeHelper'; |
12 | import { setTitle } from '/@/utils/browser'; | 12 | import { setTitle } from '/@/utils/browser'; |
@@ -14,9 +14,9 @@ import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel'; | @@ -14,9 +14,9 @@ import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel'; | ||
14 | 14 | ||
15 | import { tabStore } from '/@/store/modules/tab'; | 15 | import { tabStore } from '/@/store/modules/tab'; |
16 | 16 | ||
17 | -const { projectSetting, globSetting } = useSetting(); | 17 | +const globSetting = useGlobSetting(); |
18 | export function createGuard(router: Router) { | 18 | export function createGuard(router: Router) { |
19 | - const { openNProgress, closeMessageOnSwitch, removeAllHttpPending } = projectSetting; | 19 | + const { openNProgress, closeMessageOnSwitch, removeAllHttpPending } = useProjectSetting(); |
20 | let axiosCanceler: AxiosCanceler | null; | 20 | let axiosCanceler: AxiosCanceler | null; |
21 | if (removeAllHttpPending) { | 21 | if (removeAllHttpPending) { |
22 | axiosCanceler = new AxiosCanceler(); | 22 | axiosCanceler = new AxiosCanceler(); |
src/router/guard/permissionGuard.ts
1 | import type { Router, RouteRecordRaw } from 'vue-router'; | 1 | import type { Router, RouteRecordRaw } from 'vue-router'; |
2 | 2 | ||
3 | -import { userStore } from '/@/store/modules/user'; | 3 | +import { appStore } from '/@/store/modules/app'; |
4 | import { permissionStore } from '/@/store/modules/permission'; | 4 | import { permissionStore } from '/@/store/modules/permission'; |
5 | 5 | ||
6 | import { PageEnum } from '/@/enums/pageEnum'; | 6 | import { PageEnum } from '/@/enums/pageEnum'; |
@@ -72,7 +72,7 @@ export function createPermissionGuard(router: Router) { | @@ -72,7 +72,7 @@ export function createPermissionGuard(router: Router) { | ||
72 | router.afterEach((to) => { | 72 | router.afterEach((to) => { |
73 | // Just enter the login page and clear the authentication information | 73 | // Just enter the login page and clear the authentication information |
74 | if (to.path === LOGIN_PATH) { | 74 | if (to.path === LOGIN_PATH) { |
75 | - userStore.resumeAllState(); | 75 | + appStore.resumeAllState(); |
76 | } | 76 | } |
77 | }); | 77 | }); |
78 | } | 78 | } |
src/router/menus/modules/demo/feat.ts
src/router/routes/modules/demo/feat.ts
@@ -81,14 +81,6 @@ const feat: AppRouteModule = { | @@ -81,14 +81,6 @@ const feat: AppRouteModule = { | ||
81 | }, | 81 | }, |
82 | }, | 82 | }, |
83 | { | 83 | { |
84 | - path: '/i18n', | ||
85 | - name: 'I18nDemo', | ||
86 | - component: () => import('/@/views/demo/feat/i18n/index.vue'), | ||
87 | - meta: { | ||
88 | - title: '国际化', | ||
89 | - }, | ||
90 | - }, | ||
91 | - { | ||
92 | path: '/watermark', | 84 | path: '/watermark', |
93 | name: 'WatermarkDemo', | 85 | name: 'WatermarkDemo', |
94 | component: () => import('/@/views/demo/feat/watermark/index.vue'), | 86 | component: () => import('/@/views/demo/feat/watermark/index.vue'), |
src/settings/projectSetting.ts
@@ -7,7 +7,16 @@ import { isProdMode } from '/@/utils/env'; | @@ -7,7 +7,16 @@ import { isProdMode } from '/@/utils/env'; | ||
7 | 7 | ||
8 | // ! You need to clear the browser cache after the change | 8 | // ! You need to clear the browser cache after the change |
9 | const setting: ProjectConfig = { | 9 | const setting: ProjectConfig = { |
10 | - locale: 'en', | 10 | + // locale setting |
11 | + locale: { | ||
12 | + // Locales | ||
13 | + lang: 'zh_CN', | ||
14 | + // Default locale | ||
15 | + fallback: 'zh_CN', | ||
16 | + // available Locales | ||
17 | + availableLocales: ['zh_CN', 'en'], | ||
18 | + }, | ||
19 | + | ||
11 | // color | 20 | // color |
12 | // TODO 主题色 | 21 | // TODO 主题色 |
13 | themeColor: primaryColor, | 22 | themeColor: primaryColor, |
@@ -87,15 +96,7 @@ const setting: ProjectConfig = { | @@ -87,15 +96,7 @@ const setting: ProjectConfig = { | ||
87 | // 开启手风琴模式,只显示一个菜单 | 96 | // 开启手风琴模式,只显示一个菜单 |
88 | accordion: true, | 97 | accordion: true, |
89 | }, | 98 | }, |
90 | - // 消息配置 | ||
91 | - messageSetting: { | ||
92 | - // 弹窗title | ||
93 | - title: '操作提示', | ||
94 | - // 取消按钮的文子, | ||
95 | - cancelText: '取消', | ||
96 | - // 确认按钮的文字 | ||
97 | - okText: '确定', | ||
98 | - }, | 99 | + |
99 | // 多标签 | 100 | // 多标签 |
100 | multiTabsSetting: { | 101 | multiTabsSetting: { |
101 | // 开启 | 102 | // 开启 |
src/hooks/core/useSetting.ts renamed to src/settings/use/index.ts
1 | -import type { ProjectConfig, GlobConfig, SettingWrap, GlobEnvConfig } from '/@/types/config'; | 1 | +import type { ProjectConfig, GlobConfig, GlobEnvConfig } from '/@/types/config'; |
2 | 2 | ||
3 | import getProjectSetting from '/@/settings/projectSetting'; | 3 | import getProjectSetting from '/@/settings/projectSetting'; |
4 | 4 | ||
5 | -import { getGlobEnvConfig, isDevMode } from '/@/utils/env'; | ||
6 | import { getShortName } from '../../../build/getShortName'; | 5 | import { getShortName } from '../../../build/getShortName'; |
7 | import { warn } from '/@/utils/log'; | 6 | import { warn } from '/@/utils/log'; |
7 | +import { getGlobEnvConfig, isDevMode } from '/@/utils/env'; | ||
8 | 8 | ||
9 | const reg = /[a-zA-Z\_]*/; | 9 | const reg = /[a-zA-Z\_]*/; |
10 | 10 | ||
@@ -12,6 +12,7 @@ const ENV_NAME = getShortName(import.meta.env); | @@ -12,6 +12,7 @@ const ENV_NAME = getShortName(import.meta.env); | ||
12 | const ENV = ((isDevMode() | 12 | const ENV = ((isDevMode() |
13 | ? getGlobEnvConfig() | 13 | ? getGlobEnvConfig() |
14 | : window[ENV_NAME as any]) as unknown) as GlobEnvConfig; | 14 | : window[ENV_NAME as any]) as unknown) as GlobEnvConfig; |
15 | + | ||
15 | const { | 16 | const { |
16 | VITE_GLOB_APP_TITLE, | 17 | VITE_GLOB_APP_TITLE, |
17 | VITE_GLOB_API_URL, | 18 | VITE_GLOB_API_URL, |
@@ -25,7 +26,7 @@ if (!reg.test(VITE_GLOB_APP_SHORT_NAME)) { | @@ -25,7 +26,7 @@ if (!reg.test(VITE_GLOB_APP_SHORT_NAME)) { | ||
25 | ); | 26 | ); |
26 | } | 27 | } |
27 | 28 | ||
28 | -export const useSetting = (): SettingWrap => { | 29 | +export const useGlobSetting = (): Readonly<GlobConfig> => { |
29 | // Take global configuration | 30 | // Take global configuration |
30 | const glob: Readonly<GlobConfig> = { | 31 | const glob: Readonly<GlobConfig> = { |
31 | title: VITE_GLOB_APP_TITLE, | 32 | title: VITE_GLOB_APP_TITLE, |
@@ -33,9 +34,10 @@ export const useSetting = (): SettingWrap => { | @@ -33,9 +34,10 @@ export const useSetting = (): SettingWrap => { | ||
33 | shortName: VITE_GLOB_APP_SHORT_NAME, | 34 | shortName: VITE_GLOB_APP_SHORT_NAME, |
34 | urlPrefix: VITE_GLOB_API_URL_PREFIX, | 35 | urlPrefix: VITE_GLOB_API_URL_PREFIX, |
35 | }; | 36 | }; |
36 | - const projectSetting: Readonly<ProjectConfig> = getProjectSetting; | ||
37 | - return { | ||
38 | - globSetting: glob as Readonly<GlobConfig>, | ||
39 | - projectSetting, | ||
40 | - }; | 37 | + return glob as Readonly<GlobConfig>; |
38 | +}; | ||
39 | + | ||
40 | +export const useProjectSetting = (): ProjectConfig => { | ||
41 | + // TODO computed | ||
42 | + return getProjectSetting; | ||
41 | }; | 43 | }; |
src/settings/use/useLocaleSetting.ts
0 → 100644
1 | +import type { LocaleSetting } from '/@/types/config'; | ||
2 | + | ||
3 | +import { computed } from 'vue'; | ||
4 | +import { appStore } from '/@/store/modules/app'; | ||
5 | + | ||
6 | +import getProjectSetting from '/@/settings/projectSetting'; | ||
7 | +import { localeList } from '/@/locales'; | ||
8 | + | ||
9 | +export function useLocaleSetting() { | ||
10 | + // Get locale configuration | ||
11 | + const getLocale = computed(() => { | ||
12 | + return appStore.getProjectConfig.locale || getProjectSetting.locale; | ||
13 | + }); | ||
14 | + | ||
15 | + // get current language | ||
16 | + const getLang = computed(() => { | ||
17 | + return getLocale.value.lang; | ||
18 | + }); | ||
19 | + | ||
20 | + // get Available Locales | ||
21 | + const getAvailableLocales = computed((): string[] => { | ||
22 | + return getLocale.value.availableLocales; | ||
23 | + }); | ||
24 | + | ||
25 | + // get Fallback Locales | ||
26 | + const getFallbackLocale = computed((): string => { | ||
27 | + return getLocale.value.fallback; | ||
28 | + }); | ||
29 | + | ||
30 | + // Set locale configuration | ||
31 | + function setLocale(locale: Partial<LocaleSetting>): void { | ||
32 | + appStore.commitProjectConfigState({ locale }); | ||
33 | + } | ||
34 | + | ||
35 | + return { getLocale, getLang, localeList, setLocale, getAvailableLocales, getFallbackLocale }; | ||
36 | +} |
src/setup/directives/permission.ts
@@ -14,9 +14,7 @@ function isAuth(el: Element, binding: any) { | @@ -14,9 +14,7 @@ function isAuth(el: Element, binding: any) { | ||
14 | const value = binding.value; | 14 | const value = binding.value; |
15 | if (!value) return; | 15 | if (!value) return; |
16 | if (!hasPermission(value)) { | 16 | if (!hasPermission(value)) { |
17 | - if (el.parentNode) { | ||
18 | - el.parentNode.removeChild(el); | ||
19 | - } | 17 | + el.parentNode?.removeChild(el); |
20 | } | 18 | } |
21 | } | 19 | } |
22 | 20 |
src/setup/directives/repeatClick.ts
@@ -9,7 +9,7 @@ const repeatDirective: Directive = { | @@ -9,7 +9,7 @@ const repeatDirective: Directive = { | ||
9 | beforeMount(el: Element, binding: DirectiveBinding<any>) { | 9 | beforeMount(el: Element, binding: DirectiveBinding<any>) { |
10 | let interval: Nullable<IntervalHandle> = null; | 10 | let interval: Nullable<IntervalHandle> = null; |
11 | let startTime = 0; | 11 | let startTime = 0; |
12 | - const handler = (): void => binding.value && binding.value(); | 12 | + const handler = (): void => binding?.value(); |
13 | const clear = (): void => { | 13 | const clear = (): void => { |
14 | if (Date.now() - startTime < 100) { | 14 | if (Date.now() - startTime < 100) { |
15 | handler(); | 15 | handler(); |
src/setup/error-handle/index.ts
@@ -3,7 +3,7 @@ | @@ -3,7 +3,7 @@ | ||
3 | */ | 3 | */ |
4 | 4 | ||
5 | import { errorStore, ErrorInfo } from '/@/store/modules/error'; | 5 | import { errorStore, ErrorInfo } from '/@/store/modules/error'; |
6 | -import { useSetting } from '/@/hooks/core/useSetting'; | 6 | +import { useProjectSetting } from '/@/settings/use'; |
7 | import { ErrorTypeEnum } from '/@/enums/exceptionEnum'; | 7 | import { ErrorTypeEnum } from '/@/enums/exceptionEnum'; |
8 | import { App } from 'vue'; | 8 | import { App } from 'vue'; |
9 | 9 | ||
@@ -89,7 +89,7 @@ export function scriptErrorHandler( | @@ -89,7 +89,7 @@ export function scriptErrorHandler( | ||
89 | const errorInfo: Partial<ErrorInfo> = {}; | 89 | const errorInfo: Partial<ErrorInfo> = {}; |
90 | colno = colno || (window.event && (window.event as any).errorCharacter) || 0; | 90 | colno = colno || (window.event && (window.event as any).errorCharacter) || 0; |
91 | errorInfo.message = event as string; | 91 | errorInfo.message = event as string; |
92 | - if (error && error.stack) { | 92 | + if (error?.stack) { |
93 | errorInfo.stack = error.stack; | 93 | errorInfo.stack = error.stack; |
94 | } else { | 94 | } else { |
95 | errorInfo.stack = ''; | 95 | errorInfo.stack = ''; |
@@ -160,8 +160,7 @@ function registerResourceErrorHandler() { | @@ -160,8 +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 { projectSetting } = useSetting(); | ||
164 | - const { useErrorHandle } = projectSetting; | 163 | + const { useErrorHandle } = useProjectSetting(); |
165 | if (!useErrorHandle) return; | 164 | if (!useErrorHandle) return; |
166 | // Vue exception monitoring; | 165 | // Vue exception monitoring; |
167 | app.config.errorHandler = vueErrorHandler; | 166 | app.config.errorHandler = vueErrorHandler; |
src/setup/i18n/index.ts
1 | -import type { App } from 'vue'; | ||
2 | -import type { I18n, Locale, I18nOptions } from 'vue-i18n'; | 1 | +import { App, unref } from 'vue'; |
2 | +import type { I18n, I18nOptions } from 'vue-i18n'; | ||
3 | 3 | ||
4 | import { createI18n } from 'vue-i18n'; | 4 | import { createI18n } from 'vue-i18n'; |
5 | import localeMessages from '/@/locales'; | 5 | import localeMessages from '/@/locales'; |
6 | import { useLocale } from '/@/hooks/web/useLocale'; | 6 | import { useLocale } from '/@/hooks/web/useLocale'; |
7 | +import { useLocaleSetting } from '/@/settings/use/useLocaleSetting'; | ||
7 | 8 | ||
8 | -const { getLocale } = useLocale(); | 9 | +const { setupLocale } = useLocale(); |
9 | 10 | ||
11 | +const { getLang, getAvailableLocales, getFallbackLocale } = useLocaleSetting(); | ||
10 | const localeData: I18nOptions = { | 12 | const localeData: I18nOptions = { |
11 | legacy: false, | 13 | legacy: false, |
12 | - locale: getLocale(), | ||
13 | - // TODO: setting fallback inside settings | ||
14 | - fallbackLocale: 'en', | 14 | + locale: unref(getLang), |
15 | + fallbackLocale: unref(getFallbackLocale), | ||
15 | messages: localeMessages, | 16 | messages: localeMessages, |
16 | - // availableLocales: ['ru'], | 17 | + availableLocales: unref(getAvailableLocales), |
17 | sync: true, //If you don’t want to inherit locale from global scope, you need to set sync of i18n component option to false. | 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. |
18 | silentTranslationWarn: false, // true - warning off | 19 | silentTranslationWarn: false, // true - warning off |
19 | silentFallbackWarn: true, | 20 | silentFallbackWarn: true, |
@@ -24,12 +25,10 @@ let i18n: I18n; | @@ -24,12 +25,10 @@ let i18n: I18n; | ||
24 | // setup i18n instance with glob | 25 | // setup i18n instance with glob |
25 | export function setupI18n(app: App) { | 26 | export function setupI18n(app: App) { |
26 | i18n = createI18n(localeData) as I18n; | 27 | i18n = createI18n(localeData) as I18n; |
27 | - setI18nLanguage(getLocale()); | 28 | + setupLocale(); |
28 | app.use(i18n); | 29 | app.use(i18n); |
29 | } | 30 | } |
30 | 31 | ||
31 | -export function setI18nLanguage(locale: Locale): void { | ||
32 | - // @ts-ignore | ||
33 | - i18n.global.locale.value = locale; | ||
34 | - // i18n.global.setLocaleMessage(locale, messages); | 32 | +export function getI18n(): I18n { |
33 | + return i18n; | ||
35 | } | 34 | } |
src/store/modules/app.ts
@@ -6,9 +6,19 @@ import store from '/@/store'; | @@ -6,9 +6,19 @@ import store from '/@/store'; | ||
6 | import { PROJ_CFG_KEY, LOCK_INFO_KEY } from '/@/enums/cacheEnum'; | 6 | import { PROJ_CFG_KEY, LOCK_INFO_KEY } from '/@/enums/cacheEnum'; |
7 | 7 | ||
8 | import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper'; | 8 | import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper'; |
9 | -import { setLocal, getLocal, removeLocal } from '/@/utils/helper/persistent'; | 9 | +import { |
10 | + setLocal, | ||
11 | + getLocal, | ||
12 | + removeLocal, | ||
13 | + clearSession, | ||
14 | + clearLocal, | ||
15 | +} from '/@/utils/helper/persistent'; | ||
10 | import { deepMerge } from '/@/utils'; | 16 | import { deepMerge } from '/@/utils'; |
11 | 17 | ||
18 | +import { resetRouter } from '/@/router'; | ||
19 | +import { permissionStore } from './permission'; | ||
20 | +import { tabStore } from './tab'; | ||
21 | + | ||
12 | import { userStore } from './user'; | 22 | import { userStore } from './user'; |
13 | 23 | ||
14 | export interface LockInfo { | 24 | export interface LockInfo { |
@@ -78,6 +88,17 @@ class App extends VuexModule { | @@ -78,6 +88,17 @@ class App extends VuexModule { | ||
78 | } | 88 | } |
79 | 89 | ||
80 | @Action | 90 | @Action |
91 | + async resumeAllState() { | ||
92 | + resetRouter(); | ||
93 | + clearSession(); | ||
94 | + clearLocal(); | ||
95 | + | ||
96 | + permissionStore.commitResetState(); | ||
97 | + tabStore.commitResetState(); | ||
98 | + userStore.commitResetState(); | ||
99 | + } | ||
100 | + | ||
101 | + @Action | ||
81 | public async setPageLoadingAction(loading: boolean): Promise<void> { | 102 | public async setPageLoadingAction(loading: boolean): Promise<void> { |
82 | if (loading) { | 103 | if (loading) { |
83 | clearTimeout(timeId); | 104 | clearTimeout(timeId); |
src/store/modules/error.ts
@@ -4,7 +4,7 @@ import { VuexModule, getModule, Module, Mutation, Action } from 'vuex-module-dec | @@ -4,7 +4,7 @@ import { VuexModule, getModule, Module, Mutation, Action } from '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 { useSetting } from '/@/hooks/core/useSetting'; | 7 | +import { useProjectSetting } from '/@/settings/use'; |
8 | 8 | ||
9 | export interface ErrorInfo { | 9 | export interface ErrorInfo { |
10 | type: ErrorTypeEnum; | 10 | type: ErrorTypeEnum; |
@@ -16,6 +16,7 @@ export interface ErrorInfo { | @@ -16,6 +16,7 @@ export interface ErrorInfo { | ||
16 | url: string; | 16 | url: string; |
17 | time?: string; | 17 | time?: string; |
18 | } | 18 | } |
19 | + | ||
19 | export interface ErrorState { | 20 | export interface ErrorState { |
20 | errorInfoState: ErrorInfo[] | null; | 21 | errorInfoState: ErrorInfo[] | null; |
21 | errorListCountState: number; | 22 | errorListCountState: number; |
@@ -56,8 +57,7 @@ class Error extends VuexModule implements ErrorState { | @@ -56,8 +57,7 @@ class Error extends VuexModule implements ErrorState { | ||
56 | 57 | ||
57 | @Action | 58 | @Action |
58 | setupErrorHandle(error: any) { | 59 | setupErrorHandle(error: any) { |
59 | - const { projectSetting } = useSetting(); | ||
60 | - const { useErrorHandle } = projectSetting; | 60 | + const { useErrorHandle } = useProjectSetting(); |
61 | if (!useErrorHandle) return; | 61 | if (!useErrorHandle) return; |
62 | 62 | ||
63 | const errInfo: Partial<ErrorInfo> = { | 63 | const errInfo: Partial<ErrorInfo> = { |
src/store/modules/user.ts
@@ -15,13 +15,11 @@ import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY } from '/@/enums/cacheEnum'; | @@ -15,13 +15,11 @@ import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY } from '/@/enums/cacheEnum'; | ||
15 | 15 | ||
16 | import { useMessage } from '/@/hooks/web/useMessage'; | 16 | import { useMessage } from '/@/hooks/web/useMessage'; |
17 | 17 | ||
18 | -import router, { resetRouter } from '/@/router'; | ||
19 | -import { permissionStore } from './permission'; | ||
20 | -import { tabStore } from './tab'; | 18 | +import router from '/@/router'; |
21 | 19 | ||
22 | import { loginApi, getUserInfoById } from '/@/api/sys/user'; | 20 | import { loginApi, getUserInfoById } from '/@/api/sys/user'; |
23 | 21 | ||
24 | -import { setLocal, getLocal, clearSession, clearLocal } from '/@/utils/helper/persistent'; | 22 | +import { setLocal, getLocal } from '/@/utils/helper/persistent'; |
25 | // import { FULL_PAGE_NOT_FOUND_ROUTE } from '/@/router/constant'; | 23 | // import { FULL_PAGE_NOT_FOUND_ROUTE } from '/@/router/constant'; |
26 | 24 | ||
27 | export type UserInfo = Omit<GetUserInfoByUserIdModel, 'roles'>; | 25 | export type UserInfo = Omit<GetUserInfoByUserIdModel, 'roles'>; |
@@ -52,7 +50,7 @@ class User extends VuexModule { | @@ -52,7 +50,7 @@ class User extends VuexModule { | ||
52 | } | 50 | } |
53 | 51 | ||
54 | @Mutation | 52 | @Mutation |
55 | - resetState(): void { | 53 | + commitResetState(): void { |
56 | this.userInfoState = null; | 54 | this.userInfoState = null; |
57 | this.tokenState = ''; | 55 | this.tokenState = ''; |
58 | this.roleListState = []; | 56 | this.roleListState = []; |
@@ -128,16 +126,6 @@ class User extends VuexModule { | @@ -128,16 +126,6 @@ class User extends VuexModule { | ||
128 | goLogin && router.push(PageEnum.BASE_LOGIN); | 126 | goLogin && router.push(PageEnum.BASE_LOGIN); |
129 | } | 127 | } |
130 | 128 | ||
131 | - @Action | ||
132 | - async resumeAllState() { | ||
133 | - resetRouter(); | ||
134 | - clearSession(); | ||
135 | - clearLocal(); | ||
136 | - permissionStore.commitResetState(); | ||
137 | - tabStore.commitResetState(); | ||
138 | - this.resetState(); | ||
139 | - } | ||
140 | - | ||
141 | /** | 129 | /** |
142 | * @description: Confirm before logging out | 130 | * @description: Confirm before logging out |
143 | */ | 131 | */ |
src/types/config.d.ts
@@ -2,13 +2,7 @@ | @@ -2,13 +2,7 @@ | ||
2 | import { MenuTypeEnum, MenuModeEnum, TriggerEnum } from '/@/enums/menuEnum'; | 2 | import { MenuTypeEnum, MenuModeEnum, TriggerEnum } from '/@/enums/menuEnum'; |
3 | import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from '/@/enums/appEnum'; | 3 | import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from '/@/enums/appEnum'; |
4 | import type { LocaleType } from '/@/locales/types'; | 4 | import type { LocaleType } from '/@/locales/types'; |
5 | -export interface MessageSetting { | ||
6 | - title: string; | ||
7 | - // 取消按钮的文字, | ||
8 | - cancelText: string; | ||
9 | - // 确认按钮的文字 | ||
10 | - okText: string; | ||
11 | -} | 5 | + |
12 | export interface MenuSetting { | 6 | export interface MenuSetting { |
13 | collapsed: boolean; | 7 | collapsed: boolean; |
14 | collapsedShowTitle: boolean; | 8 | collapsedShowTitle: boolean; |
@@ -54,8 +48,18 @@ export interface HeaderSetting { | @@ -54,8 +48,18 @@ export interface HeaderSetting { | ||
54 | // 显示消息中心按钮 | 48 | // 显示消息中心按钮 |
55 | showNotice: boolean; | 49 | showNotice: boolean; |
56 | } | 50 | } |
51 | + | ||
52 | +export interface LocaleSetting { | ||
53 | + // Current language | ||
54 | + lang: LocaleType; | ||
55 | + // default language | ||
56 | + fallback: LocaleType; | ||
57 | + // available Locales | ||
58 | + availableLocales: LocaleType[]; | ||
59 | +} | ||
60 | + | ||
57 | export interface ProjectConfig { | 61 | export interface ProjectConfig { |
58 | - locale: LocaleType; | 62 | + locale: LocaleSetting; |
59 | // header背景色 | 63 | // header背景色 |
60 | headerBgColor: string; | 64 | headerBgColor: string; |
61 | // 左侧菜单背景色 | 65 | // 左侧菜单背景色 |
@@ -81,8 +85,6 @@ export interface ProjectConfig { | @@ -81,8 +85,6 @@ export interface ProjectConfig { | ||
81 | // menuType: MenuTypeEnum; | 85 | // menuType: MenuTypeEnum; |
82 | menuSetting: MenuSetting; | 86 | menuSetting: MenuSetting; |
83 | 87 | ||
84 | - messageSetting: MessageSetting; | ||
85 | - | ||
86 | // 多标签页设置 | 88 | // 多标签页设置 |
87 | multiTabsSetting: MultiTabsSetting; | 89 | multiTabsSetting: MultiTabsSetting; |
88 | // pageLayout是否开启keep-alive | 90 | // pageLayout是否开启keep-alive |
@@ -133,12 +135,6 @@ export interface GlobEnvConfig { | @@ -133,12 +135,6 @@ export interface GlobEnvConfig { | ||
133 | VITE_GLOB_APP_SHORT_NAME: string; | 135 | VITE_GLOB_APP_SHORT_NAME: string; |
134 | } | 136 | } |
135 | 137 | ||
136 | -// 修改配置 | ||
137 | -export type SetProjectSettingFn = <T extends keyof ProjectConfig>( | ||
138 | - key: T, | ||
139 | - value: ProjectConfig[T] | ||
140 | -) => void; | ||
141 | - | ||
142 | interface GlobWrap { | 138 | interface GlobWrap { |
143 | globSetting: Readonly<GlobConfig>; | 139 | globSetting: Readonly<GlobConfig>; |
144 | } | 140 | } |
@@ -146,5 +142,3 @@ interface GlobWrap { | @@ -146,5 +142,3 @@ interface GlobWrap { | ||
146 | interface ProjectSettingWrap { | 142 | interface ProjectSettingWrap { |
147 | projectSetting: Readonly<ProjectConfig>; | 143 | projectSetting: Readonly<ProjectConfig>; |
148 | } | 144 | } |
149 | - | ||
150 | -export type SettingWrap = GlobWrap & ProjectSettingWrap; |
src/utils/helper/envHelper.ts
1 | import { getEnv } from '/@/utils/env'; | 1 | import { getEnv } from '/@/utils/env'; |
2 | -import { useSetting } from '/@/hooks/core/useSetting'; | 2 | +import { useGlobSetting } from '/@/settings/use'; |
3 | import pkg from '../../../package.json'; | 3 | import pkg from '../../../package.json'; |
4 | -const { globSetting } = useSetting(); | 4 | +const globSetting = useGlobSetting(); |
5 | 5 | ||
6 | // Generate cache key according to version | 6 | // Generate cache key according to version |
7 | export const getStorageShortName = () => { | 7 | export const getStorageShortName = () => { |
src/utils/http/axios/index.ts
@@ -10,7 +10,7 @@ import { AxiosTransform } from './axiosTransform'; | @@ -10,7 +10,7 @@ import { AxiosTransform } from './axiosTransform'; | ||
10 | 10 | ||
11 | import { checkStatus } from './checkStatus'; | 11 | import { checkStatus } from './checkStatus'; |
12 | 12 | ||
13 | -import { useSetting } from '/@/hooks/core/useSetting'; | 13 | +import { useGlobSetting } from '/@/settings/use'; |
14 | import { useMessage } from '/@/hooks/web/useMessage'; | 14 | import { useMessage } from '/@/hooks/web/useMessage'; |
15 | 15 | ||
16 | import { RequestEnum, ResultEnum, ContentTypeEnum } from '/@/enums/httpEnum'; | 16 | import { RequestEnum, ResultEnum, ContentTypeEnum } from '/@/enums/httpEnum'; |
@@ -21,7 +21,7 @@ import { setObjToUrlParams, deepMerge } from '/@/utils'; | @@ -21,7 +21,7 @@ import { setObjToUrlParams, deepMerge } from '/@/utils'; | ||
21 | import { errorStore } from '/@/store/modules/error'; | 21 | import { errorStore } from '/@/store/modules/error'; |
22 | import { errorResult } from './const'; | 22 | import { errorResult } from './const'; |
23 | 23 | ||
24 | -const { globSetting } = useSetting(); | 24 | +const globSetting = useGlobSetting(); |
25 | const prefix = globSetting.urlPrefix; | 25 | const prefix = globSetting.urlPrefix; |
26 | const { createMessage, createErrorModal } = useMessage(); | 26 | const { createMessage, createErrorModal } = useMessage(); |
27 | 27 |
src/views/demo/feat/i18n/index.vue deleted
100644 → 0
1 | -<template> | ||
2 | - <div class="p-4"> | ||
3 | - <Alert message="国际化方式,没有进行全局国际化,有需要可以自行处理。" type="info" /> | ||
4 | - <Divider /> | ||
5 | - 国际化信息: {{ t('hello') }} | ||
6 | - <Divider /> | ||
7 | - <a-button :type="localeRef === 'zhCN' ? 'primary' : 'default'" @click="localeRef = 'zhCN'"> | ||
8 | - 中文 | ||
9 | - </a-button> | ||
10 | - <a-button :type="localeRef === 'en' ? 'primary' : 'default'" @click="localeRef = 'en'"> | ||
11 | - 英文 | ||
12 | - </a-button> | ||
13 | - <Divider /> | ||
14 | - </div> | ||
15 | -</template> | ||
16 | -<script lang="ts"> | ||
17 | - import { defineComponent } from 'vue'; | ||
18 | - import { Alert, Divider } from 'ant-design-vue'; | ||
19 | - | ||
20 | - import { useI18n } from '/@/hooks/web/useI18n'; | ||
21 | - export default defineComponent({ | ||
22 | - components: { Alert, Divider }, | ||
23 | - setup() { | ||
24 | - const { t, localeRef } = useI18n({ | ||
25 | - locale: 'zhCN', | ||
26 | - messages: { | ||
27 | - en: { | ||
28 | - hello: 'hello', | ||
29 | - }, | ||
30 | - zhCN: { | ||
31 | - hello: '你好', | ||
32 | - }, | ||
33 | - }, | ||
34 | - }); | ||
35 | - return { localeRef, t }; | ||
36 | - }, | ||
37 | - }); | ||
38 | -</script> |
src/views/sys/error-log/DetailModal.vue
1 | <template> | 1 | <template> |
2 | - <BasicModal :width="800" title="错误详情" v-bind="$attrs"> | 2 | + <BasicModal :width="800" :title="t('sys.errorLog.tableActionDesc')" v-bind="$attrs"> |
3 | <Description :data="info" @register="register" /> | 3 | <Description :data="info" @register="register" /> |
4 | </BasicModal> | 4 | </BasicModal> |
5 | </template> | 5 | </template> |
6 | <script lang="ts"> | 6 | <script lang="ts"> |
7 | import { defineComponent, PropType } from 'vue'; | 7 | import { defineComponent, PropType } from 'vue'; |
8 | + import { useI18n } from 'vue-i18n'; | ||
9 | + | ||
8 | import { BasicModal } from '/@/components/Modal/index'; | 10 | import { BasicModal } from '/@/components/Modal/index'; |
9 | import { ErrorInfo } from '/@/store/modules/error'; | 11 | import { ErrorInfo } from '/@/store/modules/error'; |
10 | import { Description, useDescription } from '/@/components/Description/index'; | 12 | import { Description, useDescription } from '/@/components/Description/index'; |
13 | + | ||
11 | import { getDescSchema } from './data'; | 14 | import { getDescSchema } from './data'; |
12 | 15 | ||
13 | export default defineComponent({ | 16 | export default defineComponent({ |
@@ -20,12 +23,15 @@ | @@ -20,12 +23,15 @@ | ||
20 | }, | 23 | }, |
21 | }, | 24 | }, |
22 | setup() { | 25 | setup() { |
26 | + const { t } = useI18n(); | ||
23 | const [register] = useDescription({ | 27 | const [register] = useDescription({ |
24 | column: 2, | 28 | column: 2, |
25 | schema: getDescSchema(), | 29 | schema: getDescSchema(), |
26 | }); | 30 | }); |
27 | return { | 31 | return { |
28 | register, | 32 | register, |
33 | + useI18n, | ||
34 | + t, | ||
29 | }; | 35 | }; |
30 | }, | 36 | }, |
31 | }); | 37 | }); |
src/views/sys/error-log/data.tsx
@@ -2,11 +2,15 @@ import { Tag } from 'ant-design-vue'; | @@ -2,11 +2,15 @@ import { Tag } from 'ant-design-vue'; | ||
2 | import { BasicColumn } from '/@/components/Table/index'; | 2 | import { BasicColumn } from '/@/components/Table/index'; |
3 | import { ErrorTypeEnum } from '/@/enums/exceptionEnum'; | 3 | import { ErrorTypeEnum } from '/@/enums/exceptionEnum'; |
4 | 4 | ||
5 | +import { useExternalI18n } from '/@/hooks/web/useLocale'; | ||
6 | + | ||
7 | +const { t } = useExternalI18n(); | ||
8 | + | ||
5 | export function getColumns(): BasicColumn[] { | 9 | export function getColumns(): BasicColumn[] { |
6 | return [ | 10 | return [ |
7 | { | 11 | { |
8 | dataIndex: 'type', | 12 | dataIndex: 'type', |
9 | - title: '类型', | 13 | + title: t('sys.errorLog.tableColumnType'), |
10 | width: 80, | 14 | width: 80, |
11 | customRender: ({ text }) => { | 15 | customRender: ({ text }) => { |
12 | const color = | 16 | const color = |
@@ -24,17 +28,17 @@ export function getColumns(): BasicColumn[] { | @@ -24,17 +28,17 @@ export function getColumns(): BasicColumn[] { | ||
24 | }, | 28 | }, |
25 | { | 29 | { |
26 | dataIndex: 'url', | 30 | dataIndex: 'url', |
27 | - title: '地址', | 31 | + title: 'URL', |
28 | width: 200, | 32 | width: 200, |
29 | }, | 33 | }, |
30 | { | 34 | { |
31 | dataIndex: 'time', | 35 | dataIndex: 'time', |
32 | - title: '时间', | 36 | + title: t('sys.errorLog.tableColumnDate'), |
33 | width: 160, | 37 | width: 160, |
34 | }, | 38 | }, |
35 | { | 39 | { |
36 | dataIndex: 'file', | 40 | dataIndex: 'file', |
37 | - title: '文件', | 41 | + title: t('sys.errorLog.tableColumnFile'), |
38 | width: 200, | 42 | width: 200, |
39 | }, | 43 | }, |
40 | { | 44 | { |
@@ -44,12 +48,12 @@ export function getColumns(): BasicColumn[] { | @@ -44,12 +48,12 @@ export function getColumns(): BasicColumn[] { | ||
44 | }, | 48 | }, |
45 | { | 49 | { |
46 | dataIndex: 'message', | 50 | dataIndex: 'message', |
47 | - title: '错误信息', | 51 | + title: t('sys.errorLog.tableColumnMsg'), |
48 | width: 300, | 52 | width: 300, |
49 | }, | 53 | }, |
50 | { | 54 | { |
51 | dataIndex: 'stack', | 55 | dataIndex: 'stack', |
52 | - title: 'stack信息', | 56 | + title: t('sys.errorLog.tableColumnStackMsg'), |
53 | width: 300, | 57 | width: 300, |
54 | }, | 58 | }, |
55 | ]; | 59 | ]; |
src/views/sys/error-log/index.vue
@@ -6,12 +6,22 @@ | @@ -6,12 +6,22 @@ | ||
6 | <DetailModal :info="rowInfoRef" @register="registerModal" /> | 6 | <DetailModal :info="rowInfoRef" @register="registerModal" /> |
7 | <BasicTable @register="register" class="error-handle-table"> | 7 | <BasicTable @register="register" class="error-handle-table"> |
8 | <template #toolbar> | 8 | <template #toolbar> |
9 | - <a-button @click="fireVueError" type="primary"> 点击触发vue错误 </a-button> | ||
10 | - <a-button @click="fireResourceError" type="primary"> 点击触发resource错误 </a-button> | ||
11 | - <a-button @click="fireAjaxError" type="primary"> 点击触发ajax错误 </a-button> | 9 | + <a-button @click="fireVueError" type="primary"> |
10 | + {{ t('sys.errorLog.fireVueError') }} | ||
11 | + </a-button> | ||
12 | + <a-button @click="fireResourceError" type="primary"> | ||
13 | + {{ t('sys.errorLog.fireResourceError') }} | ||
14 | + </a-button> | ||
15 | + <a-button @click="fireAjaxError" type="primary"> | ||
16 | + {{ t('sys.errorLog.fireAjaxError') }} | ||
17 | + </a-button> | ||
12 | </template> | 18 | </template> |
13 | <template #action="{ record }"> | 19 | <template #action="{ record }"> |
14 | - <TableAction :actions="[{ label: '详情', onClick: handleDetail.bind(null, record) }]" /> | 20 | + <TableAction |
21 | + :actions="[ | ||
22 | + { label: t('sys.errorLog.tableActionDesc'), onClick: handleDetail.bind(null, record) }, | ||
23 | + ]" | ||
24 | + /> | ||
15 | </template> | 25 | </template> |
16 | </BasicTable> | 26 | </BasicTable> |
17 | </div> | 27 | </div> |
@@ -21,10 +31,11 @@ | @@ -21,10 +31,11 @@ | ||
21 | import { defineComponent, watch, ref, nextTick } from 'vue'; | 31 | import { defineComponent, watch, ref, nextTick } from 'vue'; |
22 | 32 | ||
23 | import DetailModal from './DetailModal.vue'; | 33 | import DetailModal from './DetailModal.vue'; |
34 | + import { BasicTable, useTable, TableAction } from '/@/components/Table/index'; | ||
35 | + | ||
24 | import { useModal } from '/@/components/Modal/index'; | 36 | import { useModal } from '/@/components/Modal/index'; |
25 | import { useMessage } from '/@/hooks/web/useMessage'; | 37 | import { useMessage } from '/@/hooks/web/useMessage'; |
26 | - | ||
27 | - import { BasicTable, useTable, TableAction } from '/@/components/Table/index'; | 38 | + import { useI18n } from 'vue-i18n'; |
28 | 39 | ||
29 | import { errorStore, ErrorInfo } from '/@/store/modules/error'; | 40 | import { errorStore, ErrorInfo } from '/@/store/modules/error'; |
30 | 41 | ||
@@ -42,12 +53,14 @@ | @@ -42,12 +53,14 @@ | ||
42 | const rowInfoRef = ref<ErrorInfo>(); | 53 | const rowInfoRef = ref<ErrorInfo>(); |
43 | const imgListRef = ref<string[]>([]); | 54 | const imgListRef = ref<string[]>([]); |
44 | 55 | ||
56 | + const { t } = useI18n(); | ||
57 | + | ||
45 | const [register, { setTableData }] = useTable({ | 58 | const [register, { setTableData }] = useTable({ |
46 | - title: '错误日志列表', | 59 | + title: t('sys.errorLog.tableTitle'), |
47 | columns: getColumns(), | 60 | columns: getColumns(), |
48 | actionColumn: { | 61 | actionColumn: { |
49 | width: 80, | 62 | width: 80, |
50 | - title: '操作', | 63 | + title: 'Action', |
51 | dataIndex: 'action', | 64 | dataIndex: 'action', |
52 | slots: { customRender: 'action' }, | 65 | slots: { customRender: 'action' }, |
53 | }, | 66 | }, |
@@ -67,7 +80,7 @@ | @@ -67,7 +80,7 @@ | ||
67 | ); | 80 | ); |
68 | const { createMessage } = useMessage(); | 81 | const { createMessage } = useMessage(); |
69 | if (isDevMode()) { | 82 | if (isDevMode()) { |
70 | - createMessage.info('只在`/src/settings/projectSetting.ts` 内的useErrorHandle=true时生效!'); | 83 | + createMessage.info(t('sys.errorLog.enableMessage')); |
71 | } | 84 | } |
72 | // 查看详情 | 85 | // 查看详情 |
73 | function handleDetail(row: ErrorInfo) { | 86 | function handleDetail(row: ErrorInfo) { |
@@ -96,6 +109,7 @@ | @@ -96,6 +109,7 @@ | ||
96 | fireAjaxError, | 109 | fireAjaxError, |
97 | imgListRef, | 110 | imgListRef, |
98 | rowInfoRef, | 111 | rowInfoRef, |
112 | + t, | ||
99 | }; | 113 | }; |
100 | }, | 114 | }, |
101 | }); | 115 | }); |
src/views/sys/exception/Exception.tsx
@@ -12,6 +12,7 @@ import { useRoute } from 'vue-router'; | @@ -12,6 +12,7 @@ import { useRoute } from 'vue-router'; | ||
12 | 12 | ||
13 | import { useGo, useRedo } from '/@/hooks/web/usePage'; | 13 | import { useGo, useRedo } from '/@/hooks/web/usePage'; |
14 | import { PageEnum } from '/@/enums/pageEnum'; | 14 | import { PageEnum } from '/@/enums/pageEnum'; |
15 | +import { useI18n } from 'vue-i18n'; | ||
15 | 16 | ||
16 | import './exception.less'; | 17 | import './exception.less'; |
17 | interface MapValue { | 18 | interface MapValue { |
@@ -47,9 +48,12 @@ export default defineComponent({ | @@ -47,9 +48,12 @@ export default defineComponent({ | ||
47 | }, | 48 | }, |
48 | setup(props) { | 49 | setup(props) { |
49 | const statusMapRef = ref(new Map<string | number, MapValue>()); | 50 | const statusMapRef = ref(new Map<string | number, MapValue>()); |
51 | + | ||
50 | const { query } = useRoute(); | 52 | const { query } = useRoute(); |
51 | const go = useGo(); | 53 | const go = useGo(); |
52 | const redo = useRedo(); | 54 | const redo = useRedo(); |
55 | + const { t } = useI18n(); | ||
56 | + | ||
53 | const getStatus = computed(() => { | 57 | const getStatus = computed(() => { |
54 | const { status: routeStatus } = query; | 58 | const { status: routeStatus } = query; |
55 | const { status } = props; | 59 | const { status } = props; |
@@ -62,41 +66,44 @@ export default defineComponent({ | @@ -62,41 +66,44 @@ export default defineComponent({ | ||
62 | } | 66 | } |
63 | ); | 67 | ); |
64 | 68 | ||
69 | + const backLoginI18n = t('sys.exception.backLogin'); | ||
70 | + const backHomeI18n = t('sys.exception.backHome'); | ||
71 | + | ||
65 | unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_ACCESS, { | 72 | unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_ACCESS, { |
66 | title: '403', | 73 | title: '403', |
67 | status: `${ExceptionEnum.PAGE_NOT_ACCESS}`, | 74 | status: `${ExceptionEnum.PAGE_NOT_ACCESS}`, |
68 | - subTitle: `Sorry, you don't have access to this page.!`, | ||
69 | - btnText: props.full ? 'Back Login' : 'Back Home', | 75 | + subTitle: t('sys.exception.subTitle403'), |
76 | + btnText: props.full ? backLoginI18n : backHomeI18n, | ||
70 | handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()), | 77 | handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()), |
71 | }); | 78 | }); |
72 | 79 | ||
73 | unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_FOUND, { | 80 | unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_FOUND, { |
74 | title: '404', | 81 | title: '404', |
75 | status: `${ExceptionEnum.PAGE_NOT_FOUND}`, | 82 | status: `${ExceptionEnum.PAGE_NOT_FOUND}`, |
76 | - subTitle: `Sorry, the page you visited does not exist.`, | ||
77 | - btnText: props.full ? 'Back Login' : 'Back Home', | 83 | + subTitle: t('sys.exception.subTitle404'), |
84 | + btnText: props.full ? backLoginI18n : backHomeI18n, | ||
78 | handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()), | 85 | handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()), |
79 | }); | 86 | }); |
80 | 87 | ||
81 | unref(statusMapRef).set(ExceptionEnum.ERROR, { | 88 | unref(statusMapRef).set(ExceptionEnum.ERROR, { |
82 | title: '500', | 89 | title: '500', |
83 | status: `${ExceptionEnum.ERROR}`, | 90 | status: `${ExceptionEnum.ERROR}`, |
84 | - subTitle: `Sorry, the server is reporting an error.`, | ||
85 | - btnText: 'Back Home', | 91 | + subTitle: t('sys.exception.subTitle500'), |
92 | + btnText: backHomeI18n, | ||
86 | handler: () => go(), | 93 | handler: () => go(), |
87 | }); | 94 | }); |
88 | 95 | ||
89 | unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_DATA, { | 96 | unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_DATA, { |
90 | - title: 'No data on the current page', | 97 | + title: t('sys.exception.noDataTitle'), |
91 | subTitle: '', | 98 | subTitle: '', |
92 | - btnText: 'Refresh', | 99 | + btnText: t('sys.exception.redo'), |
93 | handler: () => redo(), | 100 | handler: () => redo(), |
94 | icon: notDataImg, | 101 | icon: notDataImg, |
95 | }); | 102 | }); |
96 | 103 | ||
97 | unref(statusMapRef).set(ExceptionEnum.NET_WORK_ERROR, { | 104 | unref(statusMapRef).set(ExceptionEnum.NET_WORK_ERROR, { |
98 | - title: 'Network Error', | ||
99 | - subTitle: 'Sorry,Your network connection has been disconnected, please check your network!', | 105 | + title: t('sys.exception.networkErrorTitle'), |
106 | + subTitle: t('sys.exception.networkErrorSubTitle'), | ||
100 | btnText: 'Refresh', | 107 | btnText: 'Refresh', |
101 | handler: () => redo(), | 108 | handler: () => redo(), |
102 | icon: netWorkImg, | 109 | icon: netWorkImg, |
src/views/sys/lock/index.vue
@@ -6,36 +6,38 @@ | @@ -6,36 +6,38 @@ | ||
6 | <p class="lock-page__header-name">{{ realName }}</p> | 6 | <p class="lock-page__header-name">{{ realName }}</p> |
7 | </div> | 7 | </div> |
8 | <BasicForm @register="register" v-if="!getIsNotPwd" /> | 8 | <BasicForm @register="register" v-if="!getIsNotPwd" /> |
9 | - <Alert v-if="errMsgRef" type="error" message="锁屏密码错误" banner /> | 9 | + <Alert v-if="errMsgRef" type="error" :message="t('sys.lock.alert')" banner /> |
10 | <div class="lock-page__footer"> | 10 | <div class="lock-page__footer"> |
11 | <a-button type="default" class="mt-2 mr-2" @click="goLogin" v-if="!getIsNotPwd"> | 11 | <a-button type="default" class="mt-2 mr-2" @click="goLogin" v-if="!getIsNotPwd"> |
12 | - 返回登录 | 12 | + {{ t('sys.lock.backToLogin') }} |
13 | </a-button> | 13 | </a-button> |
14 | <a-button type="primary" class="mt-2" @click="unLock(!getIsNotPwd)" :loading="loadingRef"> | 14 | <a-button type="primary" class="mt-2" @click="unLock(!getIsNotPwd)" :loading="loadingRef"> |
15 | - 进入系统 | 15 | + {{ t('sys.lock.entry') }} |
16 | </a-button> | 16 | </a-button> |
17 | </div> | 17 | </div> |
18 | </div> | 18 | </div> |
19 | </div> | 19 | </div> |
20 | </template> | 20 | </template> |
21 | <script lang="ts"> | 21 | <script lang="ts"> |
22 | - // 组件相关 | ||
23 | import { defineComponent, ref, computed } from 'vue'; | 22 | import { defineComponent, ref, computed } from 'vue'; |
24 | import { Alert } from 'ant-design-vue'; | 23 | import { Alert } from 'ant-design-vue'; |
25 | - // hook | 24 | + |
26 | import { BasicForm, useForm } from '/@/components/Form'; | 25 | import { BasicForm, useForm } from '/@/components/Form'; |
27 | 26 | ||
28 | import { userStore } from '/@/store/modules/user'; | 27 | import { userStore } from '/@/store/modules/user'; |
29 | import { appStore } from '/@/store/modules/app'; | 28 | import { appStore } from '/@/store/modules/app'; |
29 | + | ||
30 | + import { useI18n } from 'vue-i18n'; | ||
31 | + | ||
30 | export default defineComponent({ | 32 | export default defineComponent({ |
31 | name: 'LockPage', | 33 | name: 'LockPage', |
32 | components: { Alert, BasicForm }, | 34 | components: { Alert, BasicForm }, |
33 | 35 | ||
34 | setup() { | 36 | setup() { |
35 | - // 获取配置文件 | ||
36 | - // 样式前缀 | ||
37 | const loadingRef = ref(false); | 37 | const loadingRef = ref(false); |
38 | const errMsgRef = ref(false); | 38 | const errMsgRef = ref(false); |
39 | + | ||
40 | + const { t } = useI18n(); | ||
39 | const [register, { validateFields }] = useForm({ | 41 | const [register, { validateFields }] = useForm({ |
40 | showActionButtonGroup: false, | 42 | showActionButtonGroup: false, |
41 | schemas: [ | 43 | schemas: [ |
@@ -45,7 +47,7 @@ | @@ -45,7 +47,7 @@ | ||
45 | component: 'InputPassword', | 47 | component: 'InputPassword', |
46 | componentProps: { | 48 | componentProps: { |
47 | style: { width: '100%' }, | 49 | style: { width: '100%' }, |
48 | - placeholder: '请输入锁屏密码或者用户密码', | 50 | + placeholder: t('sys.lock.placeholder'), |
49 | }, | 51 | }, |
50 | rules: [{ required: true }], | 52 | rules: [{ required: true }], |
51 | }, | 53 | }, |
@@ -55,6 +57,14 @@ | @@ -55,6 +57,14 @@ | ||
55 | const { realName } = userStore.getUserInfoState || {}; | 57 | const { realName } = userStore.getUserInfoState || {}; |
56 | return realName; | 58 | return realName; |
57 | }); | 59 | }); |
60 | + | ||
61 | + const getIsNotPwd = computed(() => { | ||
62 | + if (!appStore.getLockInfo) { | ||
63 | + return true; | ||
64 | + } | ||
65 | + return appStore.getLockInfo.pwd === undefined; | ||
66 | + }); | ||
67 | + | ||
58 | /** | 68 | /** |
59 | * @description: unLock | 69 | * @description: unLock |
60 | */ | 70 | */ |
@@ -76,17 +86,12 @@ | @@ -76,17 +86,12 @@ | ||
76 | loadingRef.value = false; | 86 | loadingRef.value = false; |
77 | } | 87 | } |
78 | } | 88 | } |
89 | + | ||
79 | function goLogin() { | 90 | function goLogin() { |
80 | userStore.loginOut(true); | 91 | userStore.loginOut(true); |
81 | appStore.resetLockInfo(); | 92 | appStore.resetLockInfo(); |
82 | } | 93 | } |
83 | - const getIsNotPwd = computed(() => { | ||
84 | - if (!appStore.getLockInfo) { | ||
85 | - return true; | ||
86 | - } | ||
87 | - return appStore.getLockInfo.pwd === undefined; | ||
88 | - }); | ||
89 | - // 账号密码登录 | 94 | + |
90 | return { | 95 | return { |
91 | register, | 96 | register, |
92 | getIsNotPwd, | 97 | getIsNotPwd, |
@@ -95,6 +100,7 @@ | @@ -95,6 +100,7 @@ | ||
95 | unLock, | 100 | unLock, |
96 | errMsgRef, | 101 | errMsgRef, |
97 | loadingRef, | 102 | loadingRef, |
103 | + t, | ||
98 | }; | 104 | }; |
99 | }, | 105 | }, |
100 | }); | 106 | }); |
src/views/sys/login/Login.vue
@@ -4,6 +4,7 @@ | @@ -4,6 +4,7 @@ | ||
4 | <div class="login-form-wrap"> | 4 | <div class="login-form-wrap"> |
5 | <div class="login-form mx-6"> | 5 | <div class="login-form mx-6"> |
6 | <div class="login-form__content px-2 py-10"> | 6 | <div class="login-form__content px-2 py-10"> |
7 | + <AppLocalPicker class="login-form__locale" /> | ||
7 | <header> | 8 | <header> |
8 | <img :src="logo" class="mr-4" /> | 9 | <img :src="logo" class="mr-4" /> |
9 | <h1>{{ title }}</h1> | 10 | <h1>{{ title }}</h1> |
@@ -29,13 +30,15 @@ | @@ -29,13 +30,15 @@ | ||
29 | <a-col :span="12"> | 30 | <a-col :span="12"> |
30 | <a-form-item> | 31 | <a-form-item> |
31 | <!-- No logic, you need to deal with it yourself --> | 32 | <!-- No logic, you need to deal with it yourself --> |
32 | - <a-checkbox v-model:checked="autoLogin" size="small">自动登录</a-checkbox> | 33 | + <a-checkbox v-model:checked="autoLogin" size="small">{{ |
34 | + t('sys.login.autoLogin') | ||
35 | + }}</a-checkbox> | ||
33 | </a-form-item> | 36 | </a-form-item> |
34 | </a-col> | 37 | </a-col> |
35 | <a-col :span="12"> | 38 | <a-col :span="12"> |
36 | <a-form-item :style="{ 'text-align': 'right' }"> | 39 | <a-form-item :style="{ 'text-align': 'right' }"> |
37 | <!-- No logic, you need to deal with it yourself --> | 40 | <!-- No logic, you need to deal with it yourself --> |
38 | - <a-button type="link" size="small">忘记密码</a-button> | 41 | + <a-button type="link" size="small">{{ t('sys.login.forgetPassword') }}</a-button> |
39 | </a-form-item> | 42 | </a-form-item> |
40 | </a-col> | 43 | </a-col> |
41 | </a-row> | 44 | </a-row> |
@@ -47,7 +50,7 @@ | @@ -47,7 +50,7 @@ | ||
47 | :block="true" | 50 | :block="true" |
48 | @click="login" | 51 | @click="login" |
49 | :loading="formState.loading" | 52 | :loading="formState.loading" |
50 | - >{{ t('system.login.button') }}</a-button | 53 | + >{{ t('sys.login.loginButton') }}</a-button |
51 | > | 54 | > |
52 | </a-form-item> | 55 | </a-form-item> |
53 | </a-form> | 56 | </a-form> |
@@ -61,6 +64,7 @@ | @@ -61,6 +64,7 @@ | ||
61 | import { Checkbox } from 'ant-design-vue'; | 64 | import { Checkbox } from 'ant-design-vue'; |
62 | 65 | ||
63 | import Button from '/@/components/Button/index.vue'; | 66 | import Button from '/@/components/Button/index.vue'; |
67 | + import { AppLocalPicker } from '/@/components/Application'; | ||
64 | // import { BasicDragVerify, DragVerifyActionType } from '/@/components/Verify/index'; | 68 | // import { BasicDragVerify, DragVerifyActionType } from '/@/components/Verify/index'; |
65 | 69 | ||
66 | import { userStore } from '/@/store/modules/user'; | 70 | import { userStore } from '/@/store/modules/user'; |
@@ -68,7 +72,7 @@ | @@ -68,7 +72,7 @@ | ||
68 | 72 | ||
69 | // import { appStore } from '/@/store/modules/app'; | 73 | // import { appStore } from '/@/store/modules/app'; |
70 | import { useMessage } from '/@/hooks/web/useMessage'; | 74 | import { useMessage } from '/@/hooks/web/useMessage'; |
71 | - import { useSetting } from '/@/hooks/core/useSetting'; | 75 | + import { useGlobSetting } from '/@/settings/use'; |
72 | import logo from '/@/assets/images/logo.png'; | 76 | import logo from '/@/assets/images/logo.png'; |
73 | 77 | ||
74 | export default defineComponent({ | 78 | export default defineComponent({ |
@@ -76,14 +80,16 @@ | @@ -76,14 +80,16 @@ | ||
76 | // BasicDragVerify, | 80 | // BasicDragVerify, |
77 | AButton: Button, | 81 | AButton: Button, |
78 | ACheckbox: Checkbox, | 82 | ACheckbox: Checkbox, |
83 | + AppLocalPicker, | ||
79 | }, | 84 | }, |
80 | setup() { | 85 | setup() { |
81 | const formRef = ref<any>(null); | 86 | const formRef = ref<any>(null); |
82 | const autoLoginRef = ref(false); | 87 | const autoLoginRef = ref(false); |
83 | // const verifyRef = ref<RefInstanceType<DragVerifyActionType>>(null); | 88 | // const verifyRef = ref<RefInstanceType<DragVerifyActionType>>(null); |
84 | 89 | ||
85 | - const { globSetting } = useSetting(); | 90 | + const globSetting = useGlobSetting(); |
86 | const { notification } = useMessage(); | 91 | const { notification } = useMessage(); |
92 | + const { t } = useI18n(); | ||
87 | 93 | ||
88 | // const openLoginVerifyRef = computed(() => appStore.getProjectConfig.openLoginVerify); | 94 | // const openLoginVerifyRef = computed(() => appStore.getProjectConfig.openLoginVerify); |
89 | 95 | ||
@@ -97,8 +103,10 @@ | @@ -97,8 +103,10 @@ | ||
97 | }); | 103 | }); |
98 | 104 | ||
99 | const formRules = reactive({ | 105 | const formRules = reactive({ |
100 | - account: [{ required: true, message: '请输入账号', trigger: 'blur' }], | ||
101 | - password: [{ required: true, message: '请输入密码', trigger: 'blur' }], | 106 | + account: [{ required: true, message: t('sys.login.accountPlaceholder'), trigger: 'blur' }], |
107 | + password: [ | ||
108 | + { required: true, message: t('sys.login.passwordPlaceholder'), trigger: 'blur' }, | ||
109 | + ], | ||
102 | // verify: unref(openLoginVerifyRef) ? [{ required: true, message: '请通过验证码校验' }] : [], | 110 | // verify: unref(openLoginVerifyRef) ? [{ required: true, message: '请通过验证码校验' }] : [], |
103 | }); | 111 | }); |
104 | 112 | ||
@@ -123,8 +131,8 @@ | @@ -123,8 +131,8 @@ | ||
123 | ); | 131 | ); |
124 | if (userInfo) { | 132 | if (userInfo) { |
125 | notification.success({ | 133 | notification.success({ |
126 | - message: '登录成功', | ||
127 | - description: `欢迎回来: ${userInfo.realName}`, | 134 | + message: t('sys.login.loginSuccessTitle'), |
135 | + description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realName}`, | ||
128 | duration: 3, | 136 | duration: 3, |
129 | }); | 137 | }); |
130 | } | 138 | } |
@@ -134,7 +142,6 @@ | @@ -134,7 +142,6 @@ | ||
134 | formState.loading = false; | 142 | formState.loading = false; |
135 | } | 143 | } |
136 | } | 144 | } |
137 | - const { t } = useI18n(); | ||
138 | return { | 145 | return { |
139 | formRef, | 146 | formRef, |
140 | // verifyRef, | 147 | // verifyRef, |
@@ -195,7 +202,14 @@ | @@ -195,7 +202,14 @@ | ||
195 | .respond-to(xlarge, { width: 540px; right:0}); | 202 | .respond-to(xlarge, { width: 540px; right:0}); |
196 | } | 203 | } |
197 | 204 | ||
205 | + &__locale { | ||
206 | + position: absolute; | ||
207 | + top: 10px; | ||
208 | + right: 10px; | ||
209 | + } | ||
210 | + | ||
198 | &__content { | 211 | &__content { |
212 | + position: relative; | ||
199 | width: 100%; | 213 | width: 100%; |
200 | height: 100%; | 214 | height: 100%; |
201 | border: 1px solid #999; | 215 | border: 1px solid #999; |
vite.config.ts
@@ -126,7 +126,12 @@ const viteConfig: UserConfig = { | @@ -126,7 +126,12 @@ const viteConfig: UserConfig = { | ||
126 | }, | 126 | }, |
127 | // The package will be recompiled using rollup, and the new package compiled into the esm module specification will be put into node_modules/.vite_opt_cache | 127 | // The package will be recompiled using rollup, and the new package compiled into the esm module specification will be put into node_modules/.vite_opt_cache |
128 | optimizeDeps: { | 128 | optimizeDeps: { |
129 | - include: ['echarts/map/js/china', 'ant-design-vue/es/locale/zh_CN', '@ant-design/icons-vue'], | 129 | + include: [ |
130 | + 'echarts/map/js/china', | ||
131 | + 'ant-design-vue/es/locale/zh_CN', | ||
132 | + 'ant-design-vue/es/locale/en_US', | ||
133 | + '@ant-design/icons-vue', | ||
134 | + ], | ||
130 | }, | 135 | }, |
131 | 136 | ||
132 | // Local cross-domain proxy | 137 | // Local cross-domain proxy |
yarn.lock
@@ -2926,6 +2926,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2: | @@ -2926,6 +2926,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2: | ||
2926 | shebang-command "^2.0.0" | 2926 | shebang-command "^2.0.0" |
2927 | which "^2.0.1" | 2927 | which "^2.0.1" |
2928 | 2928 | ||
2929 | +crypto-es@^1.2.6: | ||
2930 | + version "1.2.6" | ||
2931 | + resolved "https://registry.npmjs.org/crypto-es/-/crypto-es-1.2.6.tgz#468f3573a5d7b82e3b63b0004f55f905a6d3b12c" | ||
2932 | + integrity sha512-PQnrovdr5ibmOxqAh/Vy+A30RokHom7kb9Z61EPwfASfbcJCrCG4+vNNegmebNVHiXvS7WjYpHDePxnE/biEbA== | ||
2933 | + | ||
2929 | crypto-random-string@^1.0.0: | 2934 | crypto-random-string@^1.0.0: |
2930 | version "1.0.0" | 2935 | version "1.0.0" |
2931 | resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" | 2936 | resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" |