Commit 737b1b190cf288d7ab7ad7e9bd21acb9da16cc6c

Authored by vben
1 parent b49950a3

wip: multi-language support

Showing 59 changed files with 512 additions and 272 deletions
CHANGELOG.zh_CN.md
  1 +## Wip
  2 +
  3 +### 🎫 Chores
  4 +
  5 +- 移除 messageSetting 配置
  6 +
1 7 ## 2.0.0-rc.11 (2020-11-18)
2 8  
3 9 ### ✨ Features
... ...
package.json
... ... @@ -26,6 +26,7 @@
26 26 "ant-design-vue": "2.0.0-beta.15",
27 27 "apexcharts": "3.22.0",
28 28 "axios": "^0.21.0",
  29 + "crypto-es": "^1.2.6",
29 30 "echarts": "^4.9.0",
30 31 "lodash-es": "^4.17.15",
31 32 "mockjs": "^1.1.0",
... ...
src/App.vue
1 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 7 <router-view />
4 8 </ConfigProvider>
5 9 </template>
... ... @@ -7,16 +11,12 @@
7 11 <script lang="ts">
8 12 import { defineComponent } from 'vue';
9 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 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 21 export default defineComponent({
22 22 name: 'App',
... ... @@ -34,9 +34,12 @@
34 34 // Create a lock screen monitor
35 35 const lockEvent = useLockPage();
36 36  
  37 + // support Multi-language
  38 + const { antConfigLocale } = useLocale();
  39 +
37 40 return {
38 41 transformCellText,
39   - zhCN,
  42 + antConfigLocale,
40 43 lockEvent,
41 44 };
42 45 },
... ...
src/components/Application/index.ts 0 → 100644
  1 +import AppLocalPicker from './src/AppLocalPicker.vue';
  2 +
  3 +export { AppLocalPicker };
... ...
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
1   -export { default as Dropdown } from './Dropdown';
2   -export * from './types';
  1 +export { default as Dropdown } from './src/Dropdown';
  2 +export * from './src/types';
... ...
src/components/Dropdown/Dropdown.tsx renamed to src/components/Dropdown/src/Dropdown.tsx
1 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 4 import Icon from '/@/components/Icon/index';
5 5  
6 6 import { basicDropdownProps } from './props';
7 7 import { getSlot } from '/@/utils/helper/tsxHelper';
  8 +import { Trigger } from './types';
8 9  
9 10 export default defineComponent({
10 11 name: 'Dropdown',
11 12 props: basicDropdownProps,
  13 + emits: ['menuEvent'],
12 14 setup(props, { slots, emit, attrs }) {
13 15 const getMenuList = computed(() => props.dropMenuList);
14 16  
15 17 function handleClickMenu({ key }: any) {
16   - const menu = unref(getMenuList)[key];
  18 + const menu = unref(getMenuList).find((item) => item.event === key);
17 19 emit('menuEvent', menu);
18 20 }
19 21  
20 22 function renderMenus() {
21 23 return (
22   - <Menu onClick={handleClickMenu}>
  24 + <Menu onClick={handleClickMenu} selectedKeys={props.selectedKeys}>
23 25 {() => (
24 26 <>
25 27 {unref(getMenuList).map((item, index) => {
26   - const { disabled, icon, text, divider } = item;
27   -
  28 + const { disabled, icon, text, divider, event } = item;
28 29 return [
29   - <Menu.Item key={`${index}`} disabled={disabled}>
  30 + <Menu.Item key={`${event}`} disabled={disabled}>
30 31 {() => (
31 32 <>
32 33 {icon && <Icon icon={icon} />}
... ... @@ -34,8 +35,7 @@ export default defineComponent({
34 35 </>
35 36 )}
36 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 45 }
46 46  
47 47 return () => (
48   - <Dropdown trigger={props.trigger as any} {...attrs}>
  48 + <Dropdown trigger={props.trigger as Trigger[]} {...attrs}>
49 49 {{
50 50 default: () => <span>{getSlot(slots)}</span>,
51 51 overlay: () => renderMenus(),
... ...
src/components/Dropdown/props.ts renamed to src/components/Dropdown/src/props.ts
1 1 import type { PropType } from 'vue';
  2 +import type { DropMenu } from './types';
2 3  
3 4 export const dropdownProps = {
4 5 /**
... ... @@ -15,7 +16,11 @@ export const dropdownProps = {
15 16 };
16 17 export const basicDropdownProps = Object.assign({}, dropdownProps, {
17 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 24 default: () => [],
20 25 },
21 26 });
... ...
src/components/Dropdown/types.ts renamed to src/components/Dropdown/src/types.ts
... ... @@ -6,3 +6,5 @@ export interface DropMenu {
6 6 disabled?: boolean;
7 7 divider?: boolean;
8 8 }
  9 +
  10 +export type Trigger = 'click' | 'hover' | 'contextMenu';
... ...
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 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 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 &#39;ant-design-vue/lib/modal/Modal&#39;;
3 3 import { Modal, message as Message, notification } from 'ant-design-vue';
4 4 import { InfoCircleFilled, CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons-vue';
5 5  
6   -import { useSetting } from '/@/hooks/core/useSetting';
7 6 import { ArgsProps, ConfigProps } from 'ant-design-vue/lib/notification';
8 7  
9 8 export interface NotifyApi {
... ... @@ -33,8 +32,6 @@ interface ConfirmOptions {
33 32 warning: ModalFunc;
34 33 }
35 34  
36   -const { projectSetting } = useSetting();
37   -
38 35 function getIcon(iconType: string) {
39 36 if (iconType === 'warning') {
40 37 return <InfoCircleFilled class="modal-icon-warning" />;
... ... @@ -60,7 +57,6 @@ function createConfirm(options: ModalOptionsEx): ConfirmOptions {
60 57 const opt: ModalFuncProps = {
61 58 centered: true,
62 59 icon: getIcon(iconType),
63   - ...projectSetting.messageSetting,
64 60 ...options,
65 61 };
66 62 return Modal.confirm(opt) as any;
... ...
src/layouts/default/setting/SettingDrawer.tsx
... ... @@ -5,7 +5,6 @@ import Button from &#39;/@/components/Button/index.vue&#39;;
5 5 import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
6 6 import { CopyOutlined, RedoOutlined, CheckOutlined } from '@ant-design/icons-vue';
7 7 import { appStore } from '/@/store/modules/app';
8   -import { userStore } from '/@/store/modules/user';
9 8 import { ProjectConfig } from '/@/types/config';
10 9  
11 10 import { useMessage } from '/@/hooks/web/useMessage';
... ... @@ -97,7 +96,7 @@ export default defineComponent({
97 96  
98 97 function handleClearAndRedo() {
99 98 localStorage.clear();
100   - userStore.resumeAllState();
  99 + appStore.resumeAllState();
101 100 location.reload();
102 101 }
103 102  
... ...
src/layouts/logo/index.vue
... ... @@ -7,7 +7,7 @@
7 7 <script lang="ts">
8 8 import { computed, defineComponent, PropType, ref, watch } from 'vue';
9 9 // hooks
10   - import { useSetting } from '/@/hooks/core/useSetting';
  10 + import { useGlobSetting } from '/@/settings/use';
11 11 import { useTimeoutFn } from '/@/hooks/core/useTimeout';
12 12 import { useGo } from '/@/hooks/web/usePage';
13 13  
... ... @@ -30,7 +30,7 @@
30 30 },
31 31 setup(props) {
32 32 const showRef = ref<boolean>(!!props.showTitle);
33   - const { globSetting } = useSetting();
  33 + const globSetting = useGlobSetting();
34 34 const go = useGo();
35 35  
36 36 function handleGoHome() {
... ...
src/layouts/page/index.tsx
... ... @@ -4,7 +4,7 @@ import { RouterView, RouteLocation } from &#39;vue-router&#39;;
4 4 import FrameLayout from '/@/layouts/iframe/index.vue';
5 5  
6 6 import { useTransition } from './useTransition';
7   -import { useSetting } from '/@/hooks/core/useSetting';
  7 +import { useProjectSetting } from '/@/settings/use';
8 8  
9 9 import { tabStore } from '/@/store/modules/tab';
10 10 import { appStore } from '/@/store/modules/app';
... ... @@ -29,7 +29,7 @@ export default defineComponent({
29 29 const { on: transitionOn } = useTransition();
30 30 on = transitionOn;
31 31 }
32   - const { projectSetting } = useSetting();
  32 + const projectSetting = useProjectSetting();
33 33 return () => {
34 34 const {
35 35 routerTransition,
... ...
src/locales/index.ts
1 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 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
  1 +export default {
  2 + alert: 'Lock screen password error',
  3 + backToLogin: 'Back to login',
  4 + entry: 'Enter the system',
  5 + placeholder: 'Please enter the lock screen password or user password',
  6 +};
... ...
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
1   -export default {
2   - some: 'Get Out',
3   -};
src/locales/lang/en/system/login.ts deleted 100644 → 0
1   -export default {
2   - button: 'Login',
3   -};
src/locales/lang/ru/routes/menus/dashboard.ts deleted 100644 → 0
1   -export default {
2   - someentry: 'some text',
3   -};
src/locales/lang/ru/system/basic.ts deleted 100644 → 0
1   -export default {
2   - some: 'Get Out',
3   -};
src/locales/lang/ru/system/login.ts deleted 100644 → 0
1   -export default {
2   - button: 'Login',
3   - validation: {
4   - account: 'Required Field account',
5   - password: 'Required Field password',
6   - },
7   -};
src/locales/lang/zhCN/system/basic.ts deleted 100644 → 0
1   -export default {
2   - some: '出去',
3   -};
src/locales/lang/zhCN/system/login.ts deleted 100644 → 0
1   -export default {
2   - button: '登录',
3   -};
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
  1 +export default {
  2 + backLogin: '返回登录',
  3 + backHome: '返回首页',
  4 + redo: '刷新',
  5 + subTitle403: '抱歉,您无权访问此页面。',
  6 + subTitle404: '抱歉,您访问的页面不存在。',
  7 + subTitle500: '抱歉,服务器报告错误。',
  8 + noDataTitle: '当前页无数据',
  9 + networkErrorTitle: '网络错误',
  10 + networkErrorSubTitle: '抱歉,您的网络连接已断开,请检查您的网络!',
  11 +};
... ...
src/locales/lang/zh_CN/sys/lock.ts 0 → 100644
  1 +export default {
  2 + alert: '锁屏密码错误',
  3 + backToLogin: '返回登录',
  4 + entry: '进入系统',
  5 + placeholder: '请输入锁屏密码或者用户密码',
  6 +};
... ...
src/locales/lang/zh_CN/sys/login.ts 0 → 100644
  1 +export default {
  2 + loginButton: '登录',
  3 + autoLogin: '自动登录',
  4 + forgetPassword: '忘记密码',
  5 +
  6 + // notify
  7 + loginSuccessTitle: '登录成功',
  8 + loginSuccessDesc: '欢迎回来',
  9 +
  10 + // placeholder
  11 + accountPlaceholder: '请输入账号',
  12 + passwordPlaceholder: '请输入密码',
  13 +};
... ...
src/locales/types.ts
1   -export type LocaleType = 'zhCN' | 'en' | 'ru' | 'ja';
  1 +export type LocaleType = 'zh_CN' | 'en' | 'ru' | 'ja';
... ...
src/router/guard/index.ts
... ... @@ -6,7 +6,7 @@ import { createProgressGuard } from &#39;./progressGuard&#39;;
6 6 import { createPermissionGuard } from './permissionGuard';
7 7 import { createPageLoadingGuard } from './pageLoadingGuard';
8 8  
9   -import { useSetting } from '/@/hooks/core/useSetting';
  9 +import { useGlobSetting, useProjectSetting } from '/@/settings/use';
10 10  
11 11 import { getIsOpenTab, setCurrentTo } from '/@/utils/helper/routeHelper';
12 12 import { setTitle } from '/@/utils/browser';
... ... @@ -14,9 +14,9 @@ import { AxiosCanceler } from &#39;/@/utils/http/axios/axiosCancel&#39;;
14 14  
15 15 import { tabStore } from '/@/store/modules/tab';
16 16  
17   -const { projectSetting, globSetting } = useSetting();
  17 +const globSetting = useGlobSetting();
18 18 export function createGuard(router: Router) {
19   - const { openNProgress, closeMessageOnSwitch, removeAllHttpPending } = projectSetting;
  19 + const { openNProgress, closeMessageOnSwitch, removeAllHttpPending } = useProjectSetting();
20 20 let axiosCanceler: AxiosCanceler | null;
21 21 if (removeAllHttpPending) {
22 22 axiosCanceler = new AxiosCanceler();
... ...
src/router/guard/permissionGuard.ts
1 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 4 import { permissionStore } from '/@/store/modules/permission';
5 5  
6 6 import { PageEnum } from '/@/enums/pageEnum';
... ... @@ -72,7 +72,7 @@ export function createPermissionGuard(router: Router) {
72 72 router.afterEach((to) => {
73 73 // Just enter the login page and clear the authentication information
74 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
... ... @@ -36,10 +36,6 @@ const menu: MenuModule = {
36 36 name: '图片预览',
37 37 },
38 38 {
39   - path: 'i18n',
40   - name: '国际化',
41   - },
42   - {
43 39 path: 'copy',
44 40 name: '剪切板',
45 41 },
... ...
src/router/routes/modules/demo/feat.ts
... ... @@ -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 84 path: '/watermark',
93 85 name: 'WatermarkDemo',
94 86 component: () => import('/@/views/demo/feat/watermark/index.vue'),
... ...
src/settings/projectSetting.ts
... ... @@ -7,7 +7,16 @@ import { isProdMode } from &#39;/@/utils/env&#39;;
7 7  
8 8 // ! You need to clear the browser cache after the change
9 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 20 // color
12 21 // TODO 主题色
13 22 themeColor: primaryColor,
... ... @@ -87,15 +96,7 @@ const setting: ProjectConfig = {
87 96 // 开启手风琴模式,只显示一个菜单
88 97 accordion: true,
89 98 },
90   - // 消息配置
91   - messageSetting: {
92   - // 弹窗title
93   - title: '操作提示',
94   - // 取消按钮的文子,
95   - cancelText: '取消',
96   - // 确认按钮的文字
97   - okText: '确定',
98   - },
  99 +
99 100 // 多标签
100 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 3 import getProjectSetting from '/@/settings/projectSetting';
4 4  
5   -import { getGlobEnvConfig, isDevMode } from '/@/utils/env';
6 5 import { getShortName } from '../../../build/getShortName';
7 6 import { warn } from '/@/utils/log';
  7 +import { getGlobEnvConfig, isDevMode } from '/@/utils/env';
8 8  
9 9 const reg = /[a-zA-Z\_]*/;
10 10  
... ... @@ -12,6 +12,7 @@ const ENV_NAME = getShortName(import.meta.env);
12 12 const ENV = ((isDevMode()
13 13 ? getGlobEnvConfig()
14 14 : window[ENV_NAME as any]) as unknown) as GlobEnvConfig;
  15 +
15 16 const {
16 17 VITE_GLOB_APP_TITLE,
17 18 VITE_GLOB_API_URL,
... ... @@ -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 30 // Take global configuration
30 31 const glob: Readonly<GlobConfig> = {
31 32 title: VITE_GLOB_APP_TITLE,
... ... @@ -33,9 +34,10 @@ export const useSetting = (): SettingWrap =&gt; {
33 34 shortName: VITE_GLOB_APP_SHORT_NAME,
34 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 14 const value = binding.value;
15 15 if (!value) return;
16 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 9 beforeMount(el: Element, binding: DirectiveBinding<any>) {
10 10 let interval: Nullable<IntervalHandle> = null;
11 11 let startTime = 0;
12   - const handler = (): void => binding.value && binding.value();
  12 + const handler = (): void => binding?.value();
13 13 const clear = (): void => {
14 14 if (Date.now() - startTime < 100) {
15 15 handler();
... ...
src/setup/error-handle/index.ts
... ... @@ -3,7 +3,7 @@
3 3 */
4 4  
5 5 import { errorStore, ErrorInfo } from '/@/store/modules/error';
6   -import { useSetting } from '/@/hooks/core/useSetting';
  6 +import { useProjectSetting } from '/@/settings/use';
7 7 import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
8 8 import { App } from 'vue';
9 9  
... ... @@ -89,7 +89,7 @@ export function scriptErrorHandler(
89 89 const errorInfo: Partial<ErrorInfo> = {};
90 90 colno = colno || (window.event && (window.event as any).errorCharacter) || 0;
91 91 errorInfo.message = event as string;
92   - if (error && error.stack) {
  92 + if (error?.stack) {
93 93 errorInfo.stack = error.stack;
94 94 } else {
95 95 errorInfo.stack = '';
... ... @@ -160,8 +160,7 @@ function registerResourceErrorHandler() {
160 160 * @param app
161 161 */
162 162 export function setupErrorHandle(app: App) {
163   - const { projectSetting } = useSetting();
164   - const { useErrorHandle } = projectSetting;
  163 + const { useErrorHandle } = useProjectSetting();
165 164 if (!useErrorHandle) return;
166 165 // Vue exception monitoring;
167 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 4 import { createI18n } from 'vue-i18n';
5 5 import localeMessages from '/@/locales';
6 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 12 const localeData: I18nOptions = {
11 13 legacy: false,
12   - locale: getLocale(),
13   - // TODO: setting fallback inside settings
14   - fallbackLocale: 'en',
  14 + locale: unref(getLang),
  15 + fallbackLocale: unref(getFallbackLocale),
15 16 messages: localeMessages,
16   - // availableLocales: ['ru'],
  17 + availableLocales: unref(getAvailableLocales),
17 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 19 silentTranslationWarn: false, // true - warning off
19 20 silentFallbackWarn: true,
... ... @@ -24,12 +25,10 @@ let i18n: I18n;
24 25 // setup i18n instance with glob
25 26 export function setupI18n(app: App) {
26 27 i18n = createI18n(localeData) as I18n;
27   - setI18nLanguage(getLocale());
  28 + setupLocale();
28 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 &#39;/@/store&#39;;
6 6 import { PROJ_CFG_KEY, LOCK_INFO_KEY } from '/@/enums/cacheEnum';
7 7  
8 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 16 import { deepMerge } from '/@/utils';
11 17  
  18 +import { resetRouter } from '/@/router';
  19 +import { permissionStore } from './permission';
  20 +import { tabStore } from './tab';
  21 +
12 22 import { userStore } from './user';
13 23  
14 24 export interface LockInfo {
... ... @@ -78,6 +88,17 @@ class App extends VuexModule {
78 88 }
79 89  
80 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 102 public async setPageLoadingAction(loading: boolean): Promise<void> {
82 103 if (loading) {
83 104 clearTimeout(timeId);
... ...
src/store/modules/error.ts
... ... @@ -4,7 +4,7 @@ import { VuexModule, getModule, Module, Mutation, Action } from &#39;vuex-module-dec
4 4  
5 5 import { formatToDateTime } from '/@/utils/dateUtil';
6 6 import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
7   -import { useSetting } from '/@/hooks/core/useSetting';
  7 +import { useProjectSetting } from '/@/settings/use';
8 8  
9 9 export interface ErrorInfo {
10 10 type: ErrorTypeEnum;
... ... @@ -16,6 +16,7 @@ export interface ErrorInfo {
16 16 url: string;
17 17 time?: string;
18 18 }
  19 +
19 20 export interface ErrorState {
20 21 errorInfoState: ErrorInfo[] | null;
21 22 errorListCountState: number;
... ... @@ -56,8 +57,7 @@ class Error extends VuexModule implements ErrorState {
56 57  
57 58 @Action
58 59 setupErrorHandle(error: any) {
59   - const { projectSetting } = useSetting();
60   - const { useErrorHandle } = projectSetting;
  60 + const { useErrorHandle } = useProjectSetting();
61 61 if (!useErrorHandle) return;
62 62  
63 63 const errInfo: Partial<ErrorInfo> = {
... ...
src/store/modules/user.ts
... ... @@ -15,13 +15,11 @@ import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY } from &#39;/@/enums/cacheEnum&#39;;
15 15  
16 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 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 23 // import { FULL_PAGE_NOT_FOUND_ROUTE } from '/@/router/constant';
26 24  
27 25 export type UserInfo = Omit<GetUserInfoByUserIdModel, 'roles'>;
... ... @@ -52,7 +50,7 @@ class User extends VuexModule {
52 50 }
53 51  
54 52 @Mutation
55   - resetState(): void {
  53 + commitResetState(): void {
56 54 this.userInfoState = null;
57 55 this.tokenState = '';
58 56 this.roleListState = [];
... ... @@ -128,16 +126,6 @@ class User extends VuexModule {
128 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 130 * @description: Confirm before logging out
143 131 */
... ...
src/types/config.d.ts
... ... @@ -2,13 +2,7 @@
2 2 import { MenuTypeEnum, MenuModeEnum, TriggerEnum } from '/@/enums/menuEnum';
3 3 import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from '/@/enums/appEnum';
4 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 6 export interface MenuSetting {
13 7 collapsed: boolean;
14 8 collapsedShowTitle: boolean;
... ... @@ -54,8 +48,18 @@ export interface HeaderSetting {
54 48 // 显示消息中心按钮
55 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 61 export interface ProjectConfig {
58   - locale: LocaleType;
  62 + locale: LocaleSetting;
59 63 // header背景色
60 64 headerBgColor: string;
61 65 // 左侧菜单背景色
... ... @@ -81,8 +85,6 @@ export interface ProjectConfig {
81 85 // menuType: MenuTypeEnum;
82 86 menuSetting: MenuSetting;
83 87  
84   - messageSetting: MessageSetting;
85   -
86 88 // 多标签页设置
87 89 multiTabsSetting: MultiTabsSetting;
88 90 // pageLayout是否开启keep-alive
... ... @@ -133,12 +135,6 @@ export interface GlobEnvConfig {
133 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 138 interface GlobWrap {
143 139 globSetting: Readonly<GlobConfig>;
144 140 }
... ... @@ -146,5 +142,3 @@ interface GlobWrap {
146 142 interface ProjectSettingWrap {
147 143 projectSetting: Readonly<ProjectConfig>;
148 144 }
149   -
150   -export type SettingWrap = GlobWrap & ProjectSettingWrap;
... ...
src/utils/helper/envHelper.ts
1 1 import { getEnv } from '/@/utils/env';
2   -import { useSetting } from '/@/hooks/core/useSetting';
  2 +import { useGlobSetting } from '/@/settings/use';
3 3 import pkg from '../../../package.json';
4   -const { globSetting } = useSetting();
  4 +const globSetting = useGlobSetting();
5 5  
6 6 // Generate cache key according to version
7 7 export const getStorageShortName = () => {
... ...
src/utils/http/axios/index.ts
... ... @@ -10,7 +10,7 @@ import { AxiosTransform } from &#39;./axiosTransform&#39;;
10 10  
11 11 import { checkStatus } from './checkStatus';
12 12  
13   -import { useSetting } from '/@/hooks/core/useSetting';
  13 +import { useGlobSetting } from '/@/settings/use';
14 14 import { useMessage } from '/@/hooks/web/useMessage';
15 15  
16 16 import { RequestEnum, ResultEnum, ContentTypeEnum } from '/@/enums/httpEnum';
... ... @@ -21,7 +21,7 @@ import { setObjToUrlParams, deepMerge } from &#39;/@/utils&#39;;
21 21 import { errorStore } from '/@/store/modules/error';
22 22 import { errorResult } from './const';
23 23  
24   -const { globSetting } = useSetting();
  24 +const globSetting = useGlobSetting();
25 25 const prefix = globSetting.urlPrefix;
26 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 1 <template>
2   - <BasicModal :width="800" title="错误详情" v-bind="$attrs">
  2 + <BasicModal :width="800" :title="t('sys.errorLog.tableActionDesc')" v-bind="$attrs">
3 3 <Description :data="info" @register="register" />
4 4 </BasicModal>
5 5 </template>
6 6 <script lang="ts">
7 7 import { defineComponent, PropType } from 'vue';
  8 + import { useI18n } from 'vue-i18n';
  9 +
8 10 import { BasicModal } from '/@/components/Modal/index';
9 11 import { ErrorInfo } from '/@/store/modules/error';
10 12 import { Description, useDescription } from '/@/components/Description/index';
  13 +
11 14 import { getDescSchema } from './data';
12 15  
13 16 export default defineComponent({
... ... @@ -20,12 +23,15 @@
20 23 },
21 24 },
22 25 setup() {
  26 + const { t } = useI18n();
23 27 const [register] = useDescription({
24 28 column: 2,
25 29 schema: getDescSchema(),
26 30 });
27 31 return {
28 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 &#39;ant-design-vue&#39;;
2 2 import { BasicColumn } from '/@/components/Table/index';
3 3 import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
4 4  
  5 +import { useExternalI18n } from '/@/hooks/web/useLocale';
  6 +
  7 +const { t } = useExternalI18n();
  8 +
5 9 export function getColumns(): BasicColumn[] {
6 10 return [
7 11 {
8 12 dataIndex: 'type',
9   - title: '类型',
  13 + title: t('sys.errorLog.tableColumnType'),
10 14 width: 80,
11 15 customRender: ({ text }) => {
12 16 const color =
... ... @@ -24,17 +28,17 @@ export function getColumns(): BasicColumn[] {
24 28 },
25 29 {
26 30 dataIndex: 'url',
27   - title: '地址',
  31 + title: 'URL',
28 32 width: 200,
29 33 },
30 34 {
31 35 dataIndex: 'time',
32   - title: '时间',
  36 + title: t('sys.errorLog.tableColumnDate'),
33 37 width: 160,
34 38 },
35 39 {
36 40 dataIndex: 'file',
37   - title: '文件',
  41 + title: t('sys.errorLog.tableColumnFile'),
38 42 width: 200,
39 43 },
40 44 {
... ... @@ -44,12 +48,12 @@ export function getColumns(): BasicColumn[] {
44 48 },
45 49 {
46 50 dataIndex: 'message',
47   - title: '错误信息',
  51 + title: t('sys.errorLog.tableColumnMsg'),
48 52 width: 300,
49 53 },
50 54 {
51 55 dataIndex: 'stack',
52   - title: 'stack信息',
  56 + title: t('sys.errorLog.tableColumnStackMsg'),
53 57 width: 300,
54 58 },
55 59 ];
... ...
src/views/sys/error-log/index.vue
... ... @@ -6,12 +6,22 @@
6 6 <DetailModal :info="rowInfoRef" @register="registerModal" />
7 7 <BasicTable @register="register" class="error-handle-table">
8 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 18 </template>
13 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 25 </template>
16 26 </BasicTable>
17 27 </div>
... ... @@ -21,10 +31,11 @@
21 31 import { defineComponent, watch, ref, nextTick } from 'vue';
22 32  
23 33 import DetailModal from './DetailModal.vue';
  34 + import { BasicTable, useTable, TableAction } from '/@/components/Table/index';
  35 +
24 36 import { useModal } from '/@/components/Modal/index';
25 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 40 import { errorStore, ErrorInfo } from '/@/store/modules/error';
30 41  
... ... @@ -42,12 +53,14 @@
42 53 const rowInfoRef = ref<ErrorInfo>();
43 54 const imgListRef = ref<string[]>([]);
44 55  
  56 + const { t } = useI18n();
  57 +
45 58 const [register, { setTableData }] = useTable({
46   - title: '错误日志列表',
  59 + title: t('sys.errorLog.tableTitle'),
47 60 columns: getColumns(),
48 61 actionColumn: {
49 62 width: 80,
50   - title: '操作',
  63 + title: 'Action',
51 64 dataIndex: 'action',
52 65 slots: { customRender: 'action' },
53 66 },
... ... @@ -67,7 +80,7 @@
67 80 );
68 81 const { createMessage } = useMessage();
69 82 if (isDevMode()) {
70   - createMessage.info('只在`/src/settings/projectSetting.ts` 内的useErrorHandle=true时生效!');
  83 + createMessage.info(t('sys.errorLog.enableMessage'));
71 84 }
72 85 // 查看详情
73 86 function handleDetail(row: ErrorInfo) {
... ... @@ -96,6 +109,7 @@
96 109 fireAjaxError,
97 110 imgListRef,
98 111 rowInfoRef,
  112 + t,
99 113 };
100 114 },
101 115 });
... ...
src/views/sys/exception/Exception.tsx
... ... @@ -12,6 +12,7 @@ import { useRoute } from &#39;vue-router&#39;;
12 12  
13 13 import { useGo, useRedo } from '/@/hooks/web/usePage';
14 14 import { PageEnum } from '/@/enums/pageEnum';
  15 +import { useI18n } from 'vue-i18n';
15 16  
16 17 import './exception.less';
17 18 interface MapValue {
... ... @@ -47,9 +48,12 @@ export default defineComponent({
47 48 },
48 49 setup(props) {
49 50 const statusMapRef = ref(new Map<string | number, MapValue>());
  51 +
50 52 const { query } = useRoute();
51 53 const go = useGo();
52 54 const redo = useRedo();
  55 + const { t } = useI18n();
  56 +
53 57 const getStatus = computed(() => {
54 58 const { status: routeStatus } = query;
55 59 const { status } = props;
... ... @@ -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 72 unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_ACCESS, {
66 73 title: '403',
67 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 77 handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()),
71 78 });
72 79  
73 80 unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_FOUND, {
74 81 title: '404',
75 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 85 handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()),
79 86 });
80 87  
81 88 unref(statusMapRef).set(ExceptionEnum.ERROR, {
82 89 title: '500',
83 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 93 handler: () => go(),
87 94 });
88 95  
89 96 unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_DATA, {
90   - title: 'No data on the current page',
  97 + title: t('sys.exception.noDataTitle'),
91 98 subTitle: '',
92   - btnText: 'Refresh',
  99 + btnText: t('sys.exception.redo'),
93 100 handler: () => redo(),
94 101 icon: notDataImg,
95 102 });
96 103  
97 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 107 btnText: 'Refresh',
101 108 handler: () => redo(),
102 109 icon: netWorkImg,
... ...
src/views/sys/lock/index.vue
... ... @@ -6,36 +6,38 @@
6 6 <p class="lock-page__header-name">{{ realName }}</p>
7 7 </div>
8 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 10 <div class="lock-page__footer">
11 11 <a-button type="default" class="mt-2 mr-2" @click="goLogin" v-if="!getIsNotPwd">
12   - 返回登录
  12 + {{ t('sys.lock.backToLogin') }}
13 13 </a-button>
14 14 <a-button type="primary" class="mt-2" @click="unLock(!getIsNotPwd)" :loading="loadingRef">
15   - 进入系统
  15 + {{ t('sys.lock.entry') }}
16 16 </a-button>
17 17 </div>
18 18 </div>
19 19 </div>
20 20 </template>
21 21 <script lang="ts">
22   - // 组件相关
23 22 import { defineComponent, ref, computed } from 'vue';
24 23 import { Alert } from 'ant-design-vue';
25   - // hook
  24 +
26 25 import { BasicForm, useForm } from '/@/components/Form';
27 26  
28 27 import { userStore } from '/@/store/modules/user';
29 28 import { appStore } from '/@/store/modules/app';
  29 +
  30 + import { useI18n } from 'vue-i18n';
  31 +
30 32 export default defineComponent({
31 33 name: 'LockPage',
32 34 components: { Alert, BasicForm },
33 35  
34 36 setup() {
35   - // 获取配置文件
36   - // 样式前缀
37 37 const loadingRef = ref(false);
38 38 const errMsgRef = ref(false);
  39 +
  40 + const { t } = useI18n();
39 41 const [register, { validateFields }] = useForm({
40 42 showActionButtonGroup: false,
41 43 schemas: [
... ... @@ -45,7 +47,7 @@
45 47 component: 'InputPassword',
46 48 componentProps: {
47 49 style: { width: '100%' },
48   - placeholder: '请输入锁屏密码或者用户密码',
  50 + placeholder: t('sys.lock.placeholder'),
49 51 },
50 52 rules: [{ required: true }],
51 53 },
... ... @@ -55,6 +57,14 @@
55 57 const { realName } = userStore.getUserInfoState || {};
56 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 69 * @description: unLock
60 70 */
... ... @@ -76,17 +86,12 @@
76 86 loadingRef.value = false;
77 87 }
78 88 }
  89 +
79 90 function goLogin() {
80 91 userStore.loginOut(true);
81 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 95 return {
91 96 register,
92 97 getIsNotPwd,
... ... @@ -95,6 +100,7 @@
95 100 unLock,
96 101 errMsgRef,
97 102 loadingRef,
  103 + t,
98 104 };
99 105 },
100 106 });
... ...
src/views/sys/login/Login.vue
... ... @@ -4,6 +4,7 @@
4 4 <div class="login-form-wrap">
5 5 <div class="login-form mx-6">
6 6 <div class="login-form__content px-2 py-10">
  7 + <AppLocalPicker class="login-form__locale" />
7 8 <header>
8 9 <img :src="logo" class="mr-4" />
9 10 <h1>{{ title }}</h1>
... ... @@ -29,13 +30,15 @@
29 30 <a-col :span="12">
30 31 <a-form-item>
31 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 36 </a-form-item>
34 37 </a-col>
35 38 <a-col :span="12">
36 39 <a-form-item :style="{ 'text-align': 'right' }">
37 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 42 </a-form-item>
40 43 </a-col>
41 44 </a-row>
... ... @@ -47,7 +50,7 @@
47 50 :block="true"
48 51 @click="login"
49 52 :loading="formState.loading"
50   - >{{ t('system.login.button') }}</a-button
  53 + >{{ t('sys.login.loginButton') }}</a-button
51 54 >
52 55 </a-form-item>
53 56 </a-form>
... ... @@ -61,6 +64,7 @@
61 64 import { Checkbox } from 'ant-design-vue';
62 65  
63 66 import Button from '/@/components/Button/index.vue';
  67 + import { AppLocalPicker } from '/@/components/Application';
64 68 // import { BasicDragVerify, DragVerifyActionType } from '/@/components/Verify/index';
65 69  
66 70 import { userStore } from '/@/store/modules/user';
... ... @@ -68,7 +72,7 @@
68 72  
69 73 // import { appStore } from '/@/store/modules/app';
70 74 import { useMessage } from '/@/hooks/web/useMessage';
71   - import { useSetting } from '/@/hooks/core/useSetting';
  75 + import { useGlobSetting } from '/@/settings/use';
72 76 import logo from '/@/assets/images/logo.png';
73 77  
74 78 export default defineComponent({
... ... @@ -76,14 +80,16 @@
76 80 // BasicDragVerify,
77 81 AButton: Button,
78 82 ACheckbox: Checkbox,
  83 + AppLocalPicker,
79 84 },
80 85 setup() {
81 86 const formRef = ref<any>(null);
82 87 const autoLoginRef = ref(false);
83 88 // const verifyRef = ref<RefInstanceType<DragVerifyActionType>>(null);
84 89  
85   - const { globSetting } = useSetting();
  90 + const globSetting = useGlobSetting();
86 91 const { notification } = useMessage();
  92 + const { t } = useI18n();
87 93  
88 94 // const openLoginVerifyRef = computed(() => appStore.getProjectConfig.openLoginVerify);
89 95  
... ... @@ -97,8 +103,10 @@
97 103 });
98 104  
99 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 110 // verify: unref(openLoginVerifyRef) ? [{ required: true, message: '请通过验证码校验' }] : [],
103 111 });
104 112  
... ... @@ -123,8 +131,8 @@
123 131 );
124 132 if (userInfo) {
125 133 notification.success({
126   - message: '登录成功',
127   - description: `欢迎回来: ${userInfo.realName}`,
  134 + message: t('sys.login.loginSuccessTitle'),
  135 + description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realName}`,
128 136 duration: 3,
129 137 });
130 138 }
... ... @@ -134,7 +142,6 @@
134 142 formState.loading = false;
135 143 }
136 144 }
137   - const { t } = useI18n();
138 145 return {
139 146 formRef,
140 147 // verifyRef,
... ... @@ -195,7 +202,14 @@
195 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 211 &__content {
  212 + position: relative;
199 213 width: 100%;
200 214 height: 100%;
201 215 border: 1px solid #999;
... ...
vite.config.ts
... ... @@ -126,7 +126,12 @@ const viteConfig: UserConfig = {
126 126 },
127 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 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 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 2926 shebang-command "^2.0.0"
2927 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 2934 crypto-random-string@^1.0.0:
2930 2935 version "1.0.0"
2931 2936 resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
... ...