Commit e5f8ce3fd8ec25c6fdb122867cd33e4e84a6f43f

Authored by vben
1 parent cedba37e

feat: multi-language layout

Showing 44 changed files with 504 additions and 386 deletions
mock/sys/user.ts
... ... @@ -6,7 +6,7 @@ function createFakeUserList() {
6 6 {
7 7 userId: '1',
8 8 username: 'vben',
9   - realName: 'Vben',
  9 + realName: 'Vben Admin',
10 10 desc: 'manager',
11 11 password: '123456',
12 12 token: 'fakeToken1',
... ...
src/components/Application/index.ts
1   -import AppLocalPicker from './src/AppLocalPicker.vue';
  1 +import AppLocalePicker from './src/AppLocalePicker.vue';
2 2 import AppPageFooter from './src/AppPageFooter.vue';
3 3 import AppLogo from './src/AppLogo.vue';
4 4 import { withInstall } from '../util';
5 5  
6   -export { AppLocalPicker, AppPageFooter, AppLogo };
  6 +export { AppLocalePicker, AppPageFooter, AppLogo };
7 7  
8   -export default withInstall(AppLocalPicker, AppPageFooter, AppLogo);
  8 +export default withInstall(AppLocalePicker, AppPageFooter, AppLogo);
... ...
src/components/Application/src/AppLocalPicker.vue renamed to src/components/Application/src/AppLocalePicker.vue
... ... @@ -4,6 +4,7 @@
4 4 :dropMenuList="localeList"
5 5 :selectedKeys="selectedKeys"
6 6 @menuEvent="handleMenuEvent"
  7 + overlayClassName="app-locale-picker-overlay"
7 8 >
8 9 <span class="app-local-picker">
9 10 <GlobalOutlined class="app-local-picker__icon" />
... ... @@ -30,8 +31,12 @@
30 31 type: Boolean,
31 32 default: true,
32 33 },
  34 + reload: {
  35 + type: Boolean,
  36 + default: false,
  37 + },
33 38 },
34   - setup() {
  39 + setup(props) {
35 40 const { localeList } = useLocaleSetting();
36 41 const selectedKeys = ref<string[]>([]);
37 42  
... ... @@ -50,6 +55,7 @@
50 55 function toggleLocale(lang: LocaleType | string) {
51 56 changeLocale(lang as LocaleType);
52 57 selectedKeys.value = [lang as string];
  58 + props.reload && location.reload();
53 59 }
54 60  
55 61 function handleMenuEvent(menu: DropMenu) {
... ... @@ -61,7 +67,13 @@
61 67 });
62 68 </script>
63 69  
64   -<style lang="less" scoped>
  70 +<style lang="less">
  71 + .app-locale-picker-overlay {
  72 + .ant-dropdown-menu-item {
  73 + min-width: 160px;
  74 + }
  75 + }
  76 +
65 77 .app-local-picker {
66 78 display: flex;
67 79 align-items: center;
... ...
src/components/Dropdown/src/Dropdown.tsx
  1 +import type { Trigger } from './types';
  2 +
1 3 import { defineComponent, computed, unref } from 'vue';
2   -import { Dropdown, Menu, Divider } from 'ant-design-vue';
  4 +import { Dropdown, Menu } from 'ant-design-vue';
3 5  
4 6 import Icon from '/@/components/Icon/index';
5 7  
6 8 import { basicDropdownProps } from './props';
7 9 import { getSlot } from '/@/utils/helper/tsxHelper';
8   -import { Trigger } from './types';
9 10  
10 11 export default defineComponent({
11 12 name: 'Dropdown',
... ... @@ -24,7 +25,7 @@ export default defineComponent({
24 25 <Menu onClick={handleClickMenu} selectedKeys={props.selectedKeys}>
25 26 {() => (
26 27 <>
27   - {unref(getMenuList).map((item, index) => {
  28 + {unref(getMenuList).map((item) => {
28 29 const { disabled, icon, text, divider, event } = item;
29 30 return [
30 31 <Menu.Item key={`${event}`} disabled={disabled}>
... ... @@ -35,7 +36,8 @@ export default defineComponent({
35 36 </>
36 37 )}
37 38 </Menu.Item>,
38   - divider && <Divider key={`d-${index}`} />,
  39 + // @ts-ignore
  40 + divider && <Menu.Divider key={`d-${event}`} />,
39 41 ];
40 42 })}
41 43 </>
... ...
src/design/ant/index.less
... ... @@ -13,20 +13,17 @@
13 13 }
14 14 }
15 15  
16   -// .ant-form-item-label {
17   -// text-align: unset;
18   -// }
19   -
20 16 // =================================
21 17 // ==============descriptions=======
22 18 // =================================
23   -.ant-descriptions-bordered .ant-descriptions-item-label {
24   - background-color: @background-color-light;
25   -}
  19 +// .ant-descriptions-bordered .ant-descriptions-item-label {
  20 +// background-color: @background-color-light;
  21 +// }
  22 +
  23 +// .ant-descriptions .ant-descriptions-item-content {
  24 +// color: @text-color-call-out;
  25 +// }
26 26  
27   -.ant-descriptions .ant-descriptions-item-content {
28   - color: @text-color-call-out;
29   -}
30 27 // =================================
31 28 // ==============modal message======
32 29 // =================================
... ... @@ -46,68 +43,6 @@
46 43 color: @primary-color !important;
47 44 }
48 45  
49   -.ant-modal-mask {
50   - background-color: rgba(0, 0, 0, 0.2);
51   -}
52   -// =================================
53   -// ==============menu===============
54   -// =================================
55   -.ant-menu-item {
56   - &-selected {
57   - a {
58   - color: @primary-color;
59   -
60   - &:hover {
61   - color: @primary-color;
62   - }
63   - }
64   - }
65   -}
66   -// =================================
67   -// ==============dropdown===========
68   -// =================================
69   -.ant-dropdown {
70   - .ant-divider {
71   - margin: 4px 0;
72   - }
73   -
74   - &-menu-item {
75   - line-height: 30px;
76   - color: @text-color-call-out;
77   -
78   - &:hover {
79   - color: inherit;
80   - background-color: @border-color-shallow-light;
81   - }
82   - }
83   -}
84   -// =================================
85   -// ==============back-top===========
86   -// =================================
87   -.ant-back-top {
88   - right: 50px;
89   - bottom: 60px;
90   -}
91   -// =================================
92   -// ==============calendar===========
93   -// =================================
94   -.ant-calendar-picker {
95   - width: 100%;
96   -}
97   -// =================================
98   -// ==============tooltip============
99   -// =================================
100   -
101   -.ant-tooltip {
102   - &-inner {
103   - padding: 6px 16px;
104   - line-height: 20px;
105   - color: @white;
106   - background: @text-color-base;
107   - border-radius: 4px;
108   - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
109   - }
110   -}
111 46 // =================================
112 47 // ==============form===============
113 48 // =================================
... ...
src/design/ant/input.less
... ... @@ -5,24 +5,9 @@
5 5 .ant-input {
6 6 &-number {
7 7 min-width: 110px;
8   - border-color: @border-color-shallow-dark;
9 8 }
10 9 }
11 10  
12   -.ant-input-affix-wrapper:hover .ant-input:not(.ant-input-disabled) {
13   - border-color: @info-color;
14   -}
15   -
16   -.ant-input-disabled,
17   -.ant-select-disabled .ant-select-selection,
18   -.ant-cascader-picker-label {
19   - color: @text-color-base !important;
20   -}
21   -
22   -.ant-input-disabled {
23   - background-color: @background-color-light;
24   -}
25   -
26 11 .ant-input-affix-wrapper .ant-input-suffix {
27 12 right: 9px;
28 13 }
... ...
src/design/color.less
... ... @@ -13,6 +13,9 @@
13 13 }
14 14  
15 15 @white: #fff;
  16 +
  17 +@content-bg: #f0f2f5;
  18 +
16 19 @info-color: @primary-color;
17 20  
18 21 @basic-mask-color: fade(@white, 30%);
... ... @@ -24,7 +27,7 @@
24 27 @iconify-bg-color: #5551;
25 28  
26 29 // =================================
27   -// ==============border-color============
  30 +// ==============border-color=======
28 31 // =================================
29 32  
30 33 // Dark-dark
... ...
src/design/index.less
... ... @@ -54,8 +54,7 @@ input::-ms-reveal {
54 54 }
55 55  
56 56 body {
57   - // font-family: 'Microsoft YaHei,微软雅黑,Arial,sans-serif,Helvetica Neue,Helvetica,Pingfang SC,Hiragino Sans GB';
58   - font-family: '-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol,noto color emoji';
  57 + font-family: 'BlinkMacSystemFont,segoe ui,Microsoft YaHei,Arial,sans-serif,Helvetica Neue,Helvetica,Pingfang SC,Hiragino Sans GB,Roboto,helvetica neue,Arial,noto sans,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol,noto color emoji';
59 58 font-style: normal;
60 59 font-weight: normal;
61 60 line-height: 1.428571429; // 20/14
... ... @@ -159,17 +158,3 @@ embed,
159 158 object {
160 159 vertical-align: baseline !important;
161 160 }
162   -
163   -#app {
164   - width: 100%;
165   - height: 100%;
166   -}
167   -
168   -.ant-layout {
169   - background: #f0f2f5;
170   -
171   - // &-content {
172   - // position: relative;
173   - // overflow: hidden;
174   - // }
175   -}
... ...
src/design/mixins.less
... ... @@ -16,33 +16,33 @@
16 16 color: @color;
17 17 }
18 18 }
19   -// 文本截断
  19 +// Text truncation
20 20 .text-truncate() {
21 21 overflow: hidden;
22 22 text-overflow: ellipsis;
23 23 white-space: nowrap;
24 24 }
25 25  
26   -/* 强制不换行 */
  26 +/* Force no line break */
27 27 .word-nowrap() {
28 28 word-wrap: normal;
29 29 white-space: nowrap;
30 30 }
31 31  
32   -/* 强制换行 */
  32 +/* Force line break */
33 33 .break-all() {
34 34 word-break: break-all;
35 35 word-wrap: break-word;
36 36 white-space: normal;
37 37 }
38 38  
39   -// 禁止选中
  39 +// Prohibit selection
40 40 .unselect() {
41 41 cursor: pointer;
42 42 user-select: none;
43 43 }
44 44  
45   -/* 适用于webkit内核和移动端 */
  45 +/* Suitable for webkit core and mobile */
46 46 .ellipsis-multiple(@num: 1) {
47 47 display: -webkit-box;
48 48 overflow: hidden;
... ...
src/design/public.less
  1 +#app {
  2 + width: 100%;
  3 + height: 100%;
  4 +}
  5 +
1 6 .app-svg-loading {
2 7 position: relative;
3 8 width: auto;
4 9 }
5 10  
  11 +// =================================
  12 +// ==============scrollbar==========
  13 +// =================================
6 14 ::-webkit-scrollbar {
7 15 width: 6px;
8 16 height: 6px;
... ...
src/design/reset.less deleted 100644 → 0
1   -@import 'var/link';
2   -@import './mixins/reset-text.less';
3   -@import 'color/index';
4   -.reset() {
5   - html,
6   - body {
7   - width: 100%;
8   - height: 100%;
9   -
10   - &.color-weak {
11   - filter: invert(80%);
12   - }
13   -
14   - &.gray-mode {
15   - filter: grayscale(100%);
16   - filter: progid:dximagetransform.microsoft.basicimage(grayscale=1);
17   - }
18   - }
19   -
20   - input::-ms-clear,
21   - input::-ms-reveal {
22   - display: none;
23   - }
24   -
25   - body {
26   - .reset-text();
27   - }
28   -
29   - h1,
30   - h2,
31   - h3,
32   - h4,
33   - h5,
34   - h6 {
35   - margin-top: 0;
36   - margin-bottom: 0.5em;
37   - font-weight: 500;
38   - color: @heading-color;
39   - }
40   -
41   - ul,
42   - ol {
43   - list-style: none;
44   - }
45   -
46   - li {
47   - list-style-type: none;
48   - }
49   -
50   - img {
51   - vertical-align: top;
52   - border: 0;
53   - }
54   -
55   - table {
56   - border-collapse: collapse;
57   - border-spacing: 0;
58   - }
59   -
60   - a:focus,
61   - a:active {
62   - outline: none;
63   - }
64   -
65   - i,
66   - em {
67   - font-style: normal;
68   - }
69   -
70   - div:focus {
71   - outline: none;
72   - }
73   -
74   - a {
75   - color: @link-color;
76   - text-decoration: @link-decoration;
77   - cursor: pointer;
78   - background-color: transparent; // remove the gray background on active links in IE 10.
79   - outline: none;
80   - transition: color 0.3s;
81   - -webkit-text-decoration-skip: objects; // remove gaps in links underline in iOS 8+ and Safari 8+.
82   -
83   - &:hover {
84   - color: @link-hover-color;
85   - }
86   -
87   - &:active {
88   - color: @link-active-color;
89   - }
90   -
91   - &:active,
92   - &:hover {
93   - text-decoration: @link-hover-decoration;
94   - outline: 0;
95   - }
96   -
97   - &[disabled] {
98   - color: @disabled-color;
99   - pointer-events: none;
100   - cursor: not-allowed;
101   - }
102   - }
103   -}
src/design/transition/base.less
1 1 .transition-default() {
2 2 &-enter-active,
3 3 &-leave-active {
4   - transition: 0.1s cubic-bezier(0.25, 0.8, 0.5, 1) !important;
  4 + transition: 0.3s cubic-bezier(0.25, 0.8, 0.5, 1) !important;
5 5 }
6 6  
7 7 &-move {
8   - transition: transform 0.2s;
  8 + transition: transform 0.4s;
9 9 }
10 10 }
11 11  
... ...
src/design/transition/fade.less
... ... @@ -11,7 +11,7 @@
11 11 /* fade-slide */
12 12 .fade-slide-leave-active,
13 13 .fade-slide-enter-active {
14   - transition: all 0.2s;
  14 + transition: all 0.3s;
15 15 }
16 16  
17 17 .fade-slide-enter-from {
... ...
src/design/var/index.less
... ... @@ -13,6 +13,7 @@
13 13  
14 14 //
15 15 @side-drag-z-index: 200;
  16 +
16 17 @page-loading-z-index: 10000;
17 18  
18 19 // left-menu
... ...
src/hooks/setting/useLocaleSetting.ts
... ... @@ -19,10 +19,20 @@ export function useLocaleSetting() {
19 19 // get Fallback Locales
20 20 const getFallbackLocale = computed((): string => unref(getLocale).fallback);
21 21  
  22 + const getShowLocale = computed(() => unref(getLocale).show);
  23 +
22 24 // Set locale configuration
23 25 function setLocale(locale: Partial<LocaleSetting>): void {
24 26 appStore.commitProjectConfigState({ locale });
25 27 }
26 28  
27   - return { getLocale, getLang, localeList, setLocale, getAvailableLocales, getFallbackLocale };
  29 + return {
  30 + getLocale,
  31 + getLang,
  32 + localeList,
  33 + setLocale,
  34 + getShowLocale,
  35 + getAvailableLocales,
  36 + getFallbackLocale,
  37 + };
28 38 }
... ...
src/hooks/web/useI18n.ts 0 → 100644
  1 +import { getI18n } from '/@/setup/i18n';
  2 +
  3 +export function useI18n(namespace?: string) {
  4 + const { t, ...methods } = getI18n().global;
  5 +
  6 + function getKey(key: string) {
  7 + if (!namespace) {
  8 + return key;
  9 + }
  10 + if (key.startsWith(namespace)) {
  11 + return key;
  12 + }
  13 + return `${namespace}.${key}`;
  14 + }
  15 + return {
  16 + ...methods,
  17 + t: (key: string, ...arg: Parameters<typeof t>) => {
  18 + return t(getKey(key), ...arg);
  19 + },
  20 + };
  21 +}
... ...
src/hooks/web/useLocale.ts
... ... @@ -65,10 +65,3 @@ export function useLocale() {
65 65 antConfigLocale: antConfigLocaleRef,
66 66 };
67 67 }
68   -
69   -/**
70   - * For non-setup setting
71   - */
72   -export function useExternalI18n() {
73   - return getI18n().global;
74   -}
... ...
src/layouts/default/footer/index.tsx
... ... @@ -8,18 +8,21 @@ import { GithubFilled } from &#39;@ant-design/icons-vue&#39;;
8 8 import { DOC_URL, GITHUB_URL, SITE_URL } from '/@/settings/siteSetting';
9 9 import { openWindow } from '/@/utils';
10 10  
  11 +import { useI18n } from '/@/hooks/web/useI18n';
  12 +
11 13 export default defineComponent({
12 14 name: 'LayoutContent',
13 15 setup() {
  16 + const { t } = useI18n('layout.footer');
14 17 return () => {
15 18 return (
16 19 <Layout.Footer class="layout-footer">
17 20 {() => (
18 21 <>
19 22 <div class="layout-footer__links">
20   - <a onClick={() => openWindow(SITE_URL)}>在线预览</a>
  23 + <a onClick={() => openWindow(SITE_URL)}>{t('onlinePreview')}</a>
21 24 <GithubFilled onClick={() => openWindow(GITHUB_URL)} class="github" />
22   - <a onClick={() => openWindow(DOC_URL)}>在线文档</a>
  25 + <a onClick={() => openWindow(DOC_URL)}>{t('onlineDocument')}</a>
23 26 </div>
24 27 <div>Copyright &copy;2020 Vben Admin</div>
25 28 </>
... ...
src/layouts/default/header/LayoutHeader.tsx
1 1 import './index.less';
2 2  
3 3 import type { FunctionalComponent } from 'vue';
  4 +import type { Component } from '/@/components/types';
4 5  
5 6 import { defineComponent, unref, computed, ref, nextTick } from 'vue';
6 7  
... ... @@ -27,6 +28,7 @@ import { useWindowSizeFn } from &#39;/@/hooks/event/useWindowSizeFn&#39;;
27 28 import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
28 29 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
29 30 import { useRootSetting } from '/@/hooks/setting/useRootSetting';
  31 +import { useLocaleSetting } from '/@/hooks/setting/useLocaleSetting';
30 32  
31 33 import { useRouter } from 'vue-router';
32 34  
... ... @@ -34,7 +36,8 @@ import { errorStore } from &#39;/@/store/modules/error&#39;;
34 36  
35 37 import { PageEnum } from '/@/enums/pageEnum';
36 38 import { MenuModeEnum, MenuSplitTyeEnum } from '/@/enums/menuEnum';
37   -import { Component } from '/@/components/types';
  39 +import { AppLocalePicker } from '/@/components/Application';
  40 +import { useI18n } from '/@/hooks/web/useI18n';
38 41  
39 42 interface TooltipItemProps {
40 43 title: string;
... ... @@ -65,9 +68,11 @@ export default defineComponent({
65 68 const logoWidthRef = ref(200);
66 69 const logoRef = ref<ComponentRef>(null);
67 70 const { refreshPage } = useTabs();
  71 + const { t } = useI18n('layout.header');
68 72  
69 73 const { getShowTopMenu, getShowHeaderTrigger, getSplit, getTopMenuAlign } = useMenuSetting();
70 74  
  75 + const { getShowLocale } = useLocaleSetting();
71 76 const { getUseErrorHandle, getShowBreadCrumbIcon } = useRootSetting();
72 77  
73 78 const {
... ... @@ -160,8 +165,8 @@ export default defineComponent({
160 165  
161 166 function renderActionDefault(Comp: Component | any, event: Fn) {
162 167 return (
163   - <div class={`layout-header__action-item`} onClick={event}>
164   - <Comp class={`layout-header__action-icon`} />
  168 + <div class="layout-header__action-item" onClick={event}>
  169 + <Comp class="layout-header__action-icon" />
165 170 </div>
166 171 );
167 172 }
... ... @@ -170,7 +175,7 @@ export default defineComponent({
170 175 return (
171 176 <div class={`layout-header__action`}>
172 177 {unref(getUseErrorHandle) && (
173   - <TooltipItem title="错误日志">
  178 + <TooltipItem title={t('layout.header.tooltipErrorLog')}>
174 179 {() => (
175 180 <Badge
176 181 count={errorStore.getErrorListCountState}
... ... @@ -185,23 +190,31 @@ export default defineComponent({
185 190 )}
186 191  
187 192 {unref(getUseLockPage) && (
188   - <TooltipItem title="锁定屏幕">
  193 + <TooltipItem title={t('layout.header.tooltipLock')}>
189 194 {() => renderActionDefault(LockOutlined, handleLockPage)}
190 195 </TooltipItem>
191 196 )}
192 197  
193 198 {unref(getShowNotice) && (
194   - <TooltipItem title="消息通知">{() => <NoticeAction />}</TooltipItem>
  199 + <TooltipItem title={t('layout.header.tooltipNotify')}>
  200 + {() => <NoticeAction />}
  201 + </TooltipItem>
195 202 )}
196 203  
197 204 {unref(getShowRedo) && (
198   - <TooltipItem title="刷新">
  205 + <TooltipItem title={t('layout.header.tooltipRedo')}>
199 206 {() => renderActionDefault(RedoOutlined, refreshPage)}
200 207 </TooltipItem>
201 208 )}
202 209  
203 210 {unref(getShowFullScreen) && (
204   - <TooltipItem title={unref(isFullscreenRef) ? '退出全屏' : '全屏'}>
  211 + <TooltipItem
  212 + title={
  213 + unref(isFullscreenRef)
  214 + ? t('layout.header.tooltipExitFull')
  215 + : t('layout.header.tooltipEntryFull')
  216 + }
  217 + >
205 218 {() => {
206 219 const Icon = !unref(isFullscreenRef) ? (
207 220 <FullscreenOutlined />
... ... @@ -212,7 +225,14 @@ export default defineComponent({
212 225 }}
213 226 </TooltipItem>
214 227 )}
215   - <UserDropdown class={`layout-header__user-dropdown`} />
  228 + <UserDropdown class="layout-header__user-dropdown" />
  229 + {unref(getShowLocale) && (
  230 + <AppLocalePicker
  231 + reload={true}
  232 + showText={false}
  233 + class="layout-header__action-item locale"
  234 + />
  235 + )}
216 236 </div>
217 237 );
218 238 }
... ...
src/layouts/default/header/UserDropdown.tsx
1 1 // components
2   -import { Dropdown, Menu, Divider } from 'ant-design-vue';
  2 +import { Dropdown, Menu } from 'ant-design-vue';
3 3  
4 4 import { defineComponent, computed, unref } from 'vue';
5 5  
... ... @@ -16,6 +16,7 @@ import { openWindow } from &#39;/@/utils&#39;;
16 16  
17 17 import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
18 18 import { FunctionalComponent } from 'vue';
  19 +import { useI18n } from '/@/hooks/web/useI18n';
19 20  
20 21 type MenuEvent = 'loginOut' | 'doc';
21 22 interface MenuItemProps {
... ... @@ -43,6 +44,7 @@ const MenuItem: FunctionalComponent&lt;MenuItemProps&gt; = (props) =&gt; {
43 44 export default defineComponent({
44 45 name: 'UserDropdown',
45 46 setup() {
  47 + const { t } = useI18n('layout.header');
46 48 const { getShowDoc } = useHeaderSetting();
47 49  
48 50 const getUserInfo = computed(() => {
... ... @@ -89,9 +91,14 @@ export default defineComponent({
89 91 <Menu onClick={handleMenuClick}>
90 92 {() => (
91 93 <>
92   - {showDoc && <MenuItem key="doc" text="文档" icon="gg:loadbar-doc" />}
93   - {showDoc && <Divider />}
94   - <MenuItem key="loginOut" text="退出系统" icon="ant-design:poweroff-outlined" />
  94 + {showDoc && <MenuItem key="doc" text={t('dropdownItemDoc')} icon="gg:loadbar-doc" />}
  95 + {/* @ts-ignore */}
  96 + {showDoc && <Menu.Divider />}
  97 + <MenuItem
  98 + key="loginOut"
  99 + text={t('dropdownItemLoginOut')}
  100 + icon="ant-design:poweroff-outlined"
  101 + />
95 102 </>
96 103 )}
97 104 </Menu>
... ... @@ -100,7 +107,7 @@ export default defineComponent({
100 107  
101 108 return () => {
102 109 return (
103   - <Dropdown placement="bottomLeft">
  110 + <Dropdown placement="bottomLeft" overlayClassName="app-layout-header-user-dropdown-overlay">
104 111 {{
105 112 default: () => renderSlotsDefault(),
106 113 overlay: () => renderSlotOverlay(),
... ...
src/layouts/default/header/index.less
... ... @@ -92,6 +92,11 @@
92 92 &:hover {
93 93 background: @header-light-bg-hover-color;
94 94 }
  95 +
  96 + &.locale {
  97 + padding: 0 10px;
  98 + color: rgba(0, 0, 0, 0.65);
  99 + }
95 100 }
96 101  
97 102 &-icon {
... ... @@ -221,3 +226,9 @@
221 226 }
222 227 }
223 228 }
  229 +
  230 +.app-layout-header-user-dropdown-overlay {
  231 + .ant-dropdown-menu-item {
  232 + min-width: 160px;
  233 + }
  234 +}
... ...
src/layouts/default/index.less
... ... @@ -2,9 +2,10 @@
2 2  
3 3 .default-layout {
4 4 display: flex;
5   - flex-direction: column;
6 5 width: 100%;
7 6 min-height: 100%;
  7 + background: @content-bg;
  8 + flex-direction: column;
8 9  
9 10 > .ant-layout {
10 11 min-height: 100%;
... ...
src/layouts/default/lock/LockAction.tsx
... ... @@ -9,11 +9,13 @@ import headerImg from &#39;/@/assets/images/header.jpg&#39;;
9 9  
10 10 import { appStore } from '/@/store/modules/app';
11 11 import { userStore } from '/@/store/modules/user';
  12 +import { useI18n } from '/@/hooks/web/useI18n';
12 13  
13 14 const prefixCls = 'lock-modal';
14 15 export default defineComponent({
15 16 name: 'LockModal',
16 17 setup(_, { attrs }) {
  18 + const { t } = useI18n('layout.header');
17 19 const [register, { closeModal }] = useModalInner();
18 20  
19 21 const [registerForm, { validateFields, resetFields }] = useForm({
... ... @@ -21,7 +23,7 @@ export default defineComponent({
21 23 schemas: [
22 24 {
23 25 field: 'password',
24   - label: '锁屏密码',
  26 + label: t('lockScreenPassword'),
25 27 component: 'InputPassword',
26 28 required: true,
27 29 },
... ... @@ -49,7 +51,13 @@ export default defineComponent({
49 51 }
50 52  
51 53 return () => (
52   - <BasicModal footer={null} title="锁定屏幕" {...attrs} class={prefixCls} onRegister={register}>
  54 + <BasicModal
  55 + footer={null}
  56 + title={t('lockScreen')}
  57 + {...attrs}
  58 + class={prefixCls}
  59 + onRegister={register}
  60 + >
53 61 {() => (
54 62 <div class={`${prefixCls}__entry`}>
55 63 <div class={`${prefixCls}__header`}>
... ... @@ -61,10 +69,10 @@ export default defineComponent({
61 69  
62 70 <div class={`${prefixCls}__footer`}>
63 71 <Button type="primary" block class="mt-2" onClick={lock}>
64   - {() => '锁屏'}
  72 + {() => t('lockScreenBtn')}
65 73 </Button>
66 74 <Button block class="mt-2" onClick={lock.bind(null, false)}>
67   - {() => ' 不设置密码锁屏'}
  75 + {() => t('notLockScreenPassword')}
68 76 </Button>
69 77 </div>
70 78 </div>
... ...
src/layouts/default/multitabs/TabContent.tsx
... ... @@ -13,6 +13,7 @@ import { useTabDropdown } from &#39;./useTabDropdown&#39;;
13 13 import { useMenuSetting } from '/@/hooks/setting/useMenuSetting';
14 14 import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
15 15 import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
  16 +import { useI18n } from '/@/hooks/web/useI18n';
16 17  
17 18 const ExtraContent: FunctionalComponent = () => {
18 19 return (
... ... @@ -56,6 +57,7 @@ export default defineComponent({
56 57 },
57 58 },
58 59 setup(props) {
  60 + const { t } = useI18n('layout.multipleTab');
59 61 const { getShowMenu } = useMenuSetting();
60 62 const { getShowHeader } = useHeaderSetting();
61 63 const { getShowQuick } = useMultipleTabSetting();
... ... @@ -71,7 +73,10 @@ export default defineComponent({
71 73 const { getDropMenuList, handleMenuEvent } = useTabDropdown(props as TabContentProps);
72 74  
73 75 return () => {
74   - const scaleAction = getScaleAction(unref(getIsScale) ? '收起' : '展开', unref(getIsScale));
  76 + const scaleAction = getScaleAction(
  77 + unref(getIsScale) ? t('putAway') : t('unfold'),
  78 + unref(getIsScale)
  79 + );
75 80 const dropMenuList = unref(getDropMenuList) || [];
76 81  
77 82 const isTab = unref(getIsTab);
... ...
src/layouts/default/multitabs/data.ts
... ... @@ -2,6 +2,10 @@ import { DropMenu } from &#39;/@/components/Dropdown/index&#39;;
2 2 import { AppRouteRecordRaw } from '/@/router/types';
3 3 import type { TabItem } from '/@/store/modules/tab';
4 4  
  5 +import { useI18n } from '/@/hooks/web/useI18n';
  6 +
  7 +const { t } = useI18n('layout.multipleTab');
  8 +
5 9 export enum TabContentEnum {
6 10 TAB_TYPE,
7 11 EXTRA_TYPE,
... ... @@ -37,40 +41,40 @@ export function getActions() {
37 41 const REFRESH_PAGE: DropMenu = {
38 42 icon: 'ant-design:reload-outlined',
39 43 event: MenuEventEnum.REFRESH_PAGE,
40   - text: '刷新',
  44 + text: t('redo'),
41 45 disabled: false,
42 46 };
43 47 const CLOSE_CURRENT: DropMenu = {
44 48 icon: 'ant-design:close-outlined',
45 49 event: MenuEventEnum.CLOSE_CURRENT,
46   - text: '关闭',
  50 + text: t('close'),
47 51 disabled: false,
48 52 divider: true,
49 53 };
50 54 const CLOSE_LEFT: DropMenu = {
51 55 icon: 'ant-design:pic-left-outlined',
52 56 event: MenuEventEnum.CLOSE_LEFT,
53   - text: '关闭左侧',
  57 + text: t('closeLeft'),
54 58 disabled: false,
55 59 divider: false,
56 60 };
57 61 const CLOSE_RIGHT: DropMenu = {
58 62 icon: 'ant-design:pic-right-outlined',
59 63 event: MenuEventEnum.CLOSE_RIGHT,
60   - text: '关闭右侧',
  64 + text: t('closeRight'),
61 65 disabled: false,
62 66 divider: true,
63 67 };
64 68 const CLOSE_OTHER: DropMenu = {
65 69 icon: 'ant-design:pic-center-outlined',
66 70 event: MenuEventEnum.CLOSE_OTHER,
67   - text: '关闭其他',
  71 + text: t('closeOther'),
68 72 disabled: false,
69 73 };
70 74 const CLOSE_ALL: DropMenu = {
71 75 icon: 'ant-design:line-outlined',
72 76 event: MenuEventEnum.CLOSE_ALL,
73   - text: '关闭全部',
  77 + text: t('closeAll'),
74 78 disabled: false,
75 79 };
76 80 return [REFRESH_PAGE, CLOSE_CURRENT, CLOSE_LEFT, CLOSE_RIGHT, CLOSE_OTHER, CLOSE_ALL];
... ...
src/layouts/default/setting/SettingDrawer.tsx
... ... @@ -18,6 +18,7 @@ import { useMenuSetting } from &#39;/@/hooks/setting/useMenuSetting&#39;;
18 18 import { useHeaderSetting } from '/@/hooks/setting/useHeaderSetting';
19 19 import { useMultipleTabSetting } from '/@/hooks/setting/useMultipleTabSetting';
20 20 import { useTransitionSetting } from '/@/hooks/setting/useTransitionSetting';
  21 +import { useI18n } from '/@/hooks/web/useI18n';
21 22  
22 23 import { updateColorWeak, updateGrayMode } from '/@/setup/theme';
23 24  
... ... @@ -55,6 +56,7 @@ interface ThemePickerProps {
55 56 }
56 57  
57 58 const { createSuccessModal, createMessage } = useMessage();
  59 +const { t } = useI18n('layout.setting');
58 60  
59 61 /**
60 62 * Menu type Picker comp
... ... @@ -120,8 +122,8 @@ const FooterButton: FunctionalComponent = () =&gt; {
120 122 const { isSuccessRef } = useCopyToClipboard(JSON.stringify(unref(getRootSetting), null, 2));
121 123 unref(isSuccessRef) &&
122 124 createSuccessModal({
123   - title: '操作成功',
124   - content: '复制成功,请到 src/settings/projectSetting.ts 中修改配置!',
  125 + title: t('operatingTitle'),
  126 + content: t('operatingContent'),
125 127 });
126 128 }
127 129 function handleResetSetting() {
... ... @@ -131,7 +133,7 @@ const FooterButton: FunctionalComponent = () =&gt; {
131 133 // updateTheme(themeColor);
132 134 updateColorWeak(colorWeak);
133 135 updateGrayMode(grayMode);
134   - createMessage.success('重置成功!');
  136 + createMessage.success(t('resetSuccess'));
135 137 } catch (error) {
136 138 createMessage.error(error);
137 139 }
... ... @@ -149,7 +151,7 @@ const FooterButton: FunctionalComponent = () =&gt; {
149 151 {() => (
150 152 <>
151 153 <CopyOutlined class="mr-2" />
152   - 拷贝
  154 + {t('copyBtn')}
153 155 </>
154 156 )}
155 157 </Button>
... ... @@ -157,7 +159,7 @@ const FooterButton: FunctionalComponent = () =&gt; {
157 159 {() => (
158 160 <>
159 161 <RedoOutlined class="mr-2" />
160   - 重置
  162 + {t('resetBtn')}
161 163 </>
162 164 )}
163 165 </Button>
... ... @@ -165,7 +167,7 @@ const FooterButton: FunctionalComponent = () =&gt; {
165 167 {() => (
166 168 <>
167 169 <RedoOutlined class="mr-2" />
168   - 清空缓存并返回登录页
  170 + {t('clearBtn')}
169 171 </>
170 172 )}
171 173 </Button>
... ... @@ -224,7 +226,7 @@ export default defineComponent({
224 226 return (
225 227 <>
226 228 <MenuTypePicker />
227   - {renderSwitchItem('分割菜单', {
  229 + {renderSwitchItem(t('splitMenu'), {
228 230 handler: (e) => {
229 231 baseHandler(HandlerEnum.MENU_SPLIT, e);
230 232 },
... ... @@ -238,7 +240,7 @@ export default defineComponent({
238 240 function renderTheme() {
239 241 return (
240 242 <>
241   - <Divider>{() => '顶栏主题'}</Divider>
  243 + <Divider>{() => t('headerTheme')}</Divider>
242 244 <ThemePicker
243 245 colorList={HEADER_PRESET_BG_COLOR_LIST}
244 246 def={unref(getHeaderBgColor)}
... ... @@ -246,7 +248,7 @@ export default defineComponent({
246 248 baseHandler(HandlerEnum.HEADER_THEME, e);
247 249 }}
248 250 />
249   - <Divider>{() => '菜单主题'}</Divider>
  251 + <Divider>{() => t('sidebarTheme')}</Divider>
250 252 <ThemePicker
251 253 colorList={SIDE_BAR_BG_COLOR_LIST}
252 254 def={unref(getMenuBgColor)}
... ... @@ -263,56 +265,56 @@ export default defineComponent({
263 265 */
264 266 function renderFeatures() {
265 267 return [
266   - renderSwitchItem('侧边菜单拖拽', {
  268 + renderSwitchItem(t('menuDrag'), {
267 269 handler: (e) => {
268 270 baseHandler(HandlerEnum.MENU_HAS_DRAG, e);
269 271 },
270 272 def: unref(getCanDrag),
271 273 disabled: !unref(getShowMenuRef),
272 274 }),
273   - renderSwitchItem('侧边菜单搜索', {
  275 + renderSwitchItem(t('menuSearch'), {
274 276 handler: (e) => {
275 277 baseHandler(HandlerEnum.MENU_SHOW_SEARCH, e);
276 278 },
277 279 def: unref(getShowSearch),
278 280 disabled: !unref(getShowMenuRef),
279 281 }),
280   - renderSwitchItem('侧边菜单手风琴模式', {
  282 + renderSwitchItem(t('menuAccordion'), {
281 283 handler: (e) => {
282 284 baseHandler(HandlerEnum.MENU_ACCORDION, e);
283 285 },
284 286 def: unref(getAccordion),
285 287 disabled: !unref(getShowMenuRef),
286 288 }),
287   - renderSwitchItem('折叠菜单', {
  289 + renderSwitchItem(t('menuCollapse'), {
288 290 handler: (e) => {
289 291 baseHandler(HandlerEnum.MENU_COLLAPSED, e);
290 292 },
291 293 def: unref(getCollapsed),
292 294 disabled: !unref(getShowMenuRef),
293 295 }),
294   - renderSwitchItem('折叠菜单显示名称', {
  296 + renderSwitchItem(t('collapseMenuDisplayName'), {
295 297 handler: (e) => {
296 298 baseHandler(HandlerEnum.MENU_COLLAPSED_SHOW_TITLE, e);
297 299 },
298 300 def: unref(getCollapsedShowTitle),
299 301 disabled: !unref(getShowMenuRef) || !unref(getCollapsed),
300 302 }),
301   - renderSwitchItem('固定header', {
  303 + renderSwitchItem(t('fixedHeader'), {
302 304 handler: (e) => {
303 305 baseHandler(HandlerEnum.HEADER_FIXED, e);
304 306 },
305 307 def: unref(getHeaderFixed),
306 308 disabled: !unref(getShowHeader),
307 309 }),
308   - renderSwitchItem('固定Siderbar', {
  310 + renderSwitchItem(t('fixedSideBar'), {
309 311 handler: (e) => {
310 312 baseHandler(HandlerEnum.MENU_FIXED, e);
311 313 },
312 314 def: unref(getMenuFixed),
313 315 disabled: !unref(getShowMenuRef),
314 316 }),
315   - renderSelectItem('顶部菜单布局', {
  317 + renderSelectItem(t('topMenuLayout'), {
316 318 handler: (e) => {
317 319 baseHandler(HandlerEnum.MENU_TOP_ALIGN, e);
318 320 },
... ... @@ -320,7 +322,7 @@ export default defineComponent({
320 322 options: topMenuAlignOptions,
321 323 disabled: !unref(getShowHeader) || (!unref(getIsTopMenu) && !unref(getSplit)),
322 324 }),
323   - renderSelectItem('菜单折叠按钮', {
  325 + renderSelectItem(t('menuCollapseButton'), {
324 326 handler: (e) => {
325 327 baseHandler(HandlerEnum.MENU_TRIGGER, e);
326 328 },
... ... @@ -329,7 +331,7 @@ export default defineComponent({
329 331 options: menuTriggerOptions,
330 332 }),
331 333  
332   - renderSelectItem('内容区域宽度', {
  334 + renderSelectItem(t('contentMode'), {
333 335 handler: (e) => {
334 336 baseHandler(HandlerEnum.CONTENT_MODE, e);
335 337 },
... ... @@ -337,9 +339,9 @@ export default defineComponent({
337 339 options: contentModeOptions,
338 340 }),
339 341 <div class={`setting-drawer__cell-item`}>
340   - <span>自动锁屏</span>
  342 + <span>{t('autoScreenLock')}</span>
341 343 <InputNumber
342   - style="width:120px"
  344 + style="width:126px"
343 345 size="small"
344 346 min={0}
345 347 onChange={(e: any) => {
... ... @@ -348,16 +350,16 @@ export default defineComponent({
348 350 defaultValue={appStore.getProjectConfig.lockTime}
349 351 formatter={(value: string) => {
350 352 if (parseInt(value) === 0) {
351   - return '0(不自动锁屏)';
  353 + return `0(${t('notAutoScreenLock')})`;
352 354 }
353   - return `${value}分钟`;
  355 + return `${value}${t('minute')}`;
354 356 }}
355 357 />
356 358 </div>,
357 359 <div class={`setting-drawer__cell-item`}>
358   - <span>菜单展开宽度</span>
  360 + <span>{t('expandedMenuWidth')}</span>
359 361 <InputNumber
360   - style="width:120px"
  362 + style="width:126px"
361 363 size="small"
362 364 max={600}
363 365 min={100}
... ... @@ -375,27 +377,27 @@ export default defineComponent({
375 377  
376 378 function renderContent() {
377 379 return [
378   - renderSwitchItem('面包屑', {
  380 + renderSwitchItem(t('breadcrumb'), {
379 381 handler: (e) => {
380 382 baseHandler(HandlerEnum.SHOW_BREADCRUMB, e);
381 383 },
382 384 def: unref(getShowBreadCrumb),
383 385 disabled: !unref(getShowHeader),
384 386 }),
385   - renderSwitchItem('面包屑图标', {
  387 + renderSwitchItem(t('breadcrumbIcon'), {
386 388 handler: (e) => {
387 389 baseHandler(HandlerEnum.SHOW_BREADCRUMB_ICON, e);
388 390 },
389 391 def: unref(getShowBreadCrumbIcon),
390 392 disabled: !unref(getShowHeader),
391 393 }),
392   - renderSwitchItem('标签页', {
  394 + renderSwitchItem(t('tabs'), {
393 395 handler: (e) => {
394 396 baseHandler(HandlerEnum.TABS_SHOW, e);
395 397 },
396 398 def: unref(getShowMultipleTab),
397 399 }),
398   - renderSwitchItem('标签页快捷按钮', {
  400 + renderSwitchItem(t('tabsQuickBtn'), {
399 401 handler: (e) => {
400 402 baseHandler(HandlerEnum.TABS_SHOW_QUICK, e);
401 403 },
... ... @@ -403,14 +405,14 @@ export default defineComponent({
403 405 disabled: !unref(getShowMultipleTab),
404 406 }),
405 407  
406   - renderSwitchItem('左侧菜单', {
  408 + renderSwitchItem(t('sidebar'), {
407 409 handler: (e) => {
408 410 baseHandler(HandlerEnum.MENU_SHOW_SIDEBAR, e);
409 411 },
410 412 def: unref(getShowMenu),
411 413 disabled: unref(getIsHorizontal),
412 414 }),
413   - renderSwitchItem('顶栏', {
  415 + renderSwitchItem(t('header'), {
414 416 handler: (e) => {
415 417 baseHandler(HandlerEnum.HEADER_SHOW, e);
416 418 },
... ... @@ -422,25 +424,25 @@ export default defineComponent({
422 424 },
423 425 def: unref(getShowLogo),
424 426 }),
425   - renderSwitchItem('页脚', {
  427 + renderSwitchItem(t('footer'), {
426 428 handler: (e) => {
427 429 baseHandler(HandlerEnum.SHOW_FOOTER, e);
428 430 },
429 431 def: unref(getShowFooter),
430 432 }),
431   - renderSwitchItem('全屏内容', {
  433 + renderSwitchItem(t('fullContent'), {
432 434 handler: (e) => {
433 435 baseHandler(HandlerEnum.FULL_CONTENT, e);
434 436 },
435 437 def: unref(getFullContent),
436 438 }),
437   - renderSwitchItem('灰色模式', {
  439 + renderSwitchItem(t('grayMode'), {
438 440 handler: (e) => {
439 441 baseHandler(HandlerEnum.GRAY_MODE, e);
440 442 },
441 443 def: unref(getGrayMode),
442 444 }),
443   - renderSwitchItem('色弱模式', {
  445 + renderSwitchItem(t('colorWeak'), {
444 446 handler: (e) => {
445 447 baseHandler(HandlerEnum.COLOR_WEAK, e);
446 448 },
... ... @@ -452,13 +454,13 @@ export default defineComponent({
452 454 function renderTransition() {
453 455 return (
454 456 <>
455   - {renderSwitchItem('顶部进度条', {
  457 + {renderSwitchItem(t('progress'), {
456 458 handler: (e) => {
457 459 baseHandler(HandlerEnum.OPEN_PROGRESS, e);
458 460 },
459 461 def: unref(getOpenNProgress),
460 462 })}
461   - {renderSwitchItem('切换loading', {
  463 + {renderSwitchItem(t('switchLoading'), {
462 464 handler: (e) => {
463 465 baseHandler(HandlerEnum.OPEN_PAGE_LOADING, e);
464 466 },
... ... @@ -466,14 +468,14 @@ export default defineComponent({
466 468 disabled: !unref(getEnableTransition),
467 469 })}
468 470  
469   - {renderSwitchItem('切换动画', {
  471 + {renderSwitchItem(t('switchAnimation'), {
470 472 handler: (e) => {
471 473 baseHandler(HandlerEnum.OPEN_ROUTE_TRANSITION, e);
472 474 },
473 475 def: unref(getEnableTransition),
474 476 })}
475 477  
476   - {renderSelectItem('动画类型', {
  478 + {renderSelectItem(t('animationType'), {
477 479 handler: (e) => {
478 480 baseHandler(HandlerEnum.ROUTER_TRANSITION, e);
479 481 },
... ... @@ -495,7 +497,7 @@ export default defineComponent({
495 497 {...opt}
496 498 disabled={disabled}
497 499 size="small"
498   - style={{ width: '120px' }}
  500 + style={{ width: '126px' }}
499 501 onChange={(e) => {
500 502 handler && handler(e);
501 503 }}
... ... @@ -517,26 +519,26 @@ export default defineComponent({
517 519 onChange={(e: any) => {
518 520 handler && handler(e);
519 521 }}
520   - checkedChildren="开"
521   - unCheckedChildren="关"
  522 + checkedChildren={t('on')}
  523 + unCheckedChildren={t('off')}
522 524 />
523 525 </div>
524 526 );
525 527 }
526 528  
527 529 return () => (
528   - <BasicDrawer {...attrs} title="项目配置" width={300} wrapClassName="setting-drawer">
  530 + <BasicDrawer {...attrs} title={t('drawerTitle')} width={330} wrapClassName="setting-drawer">
529 531 {{
530 532 default: () => (
531 533 <>
532   - <Divider>{() => '导航栏模式'}</Divider>
  534 + <Divider>{() => t('navMode')}</Divider>
533 535 {renderSidebar()}
534 536 {renderTheme()}
535   - <Divider>{() => '界面功能'}</Divider>
  537 + <Divider>{() => t('interfaceFunction')}</Divider>
536 538 {renderFeatures()}
537   - <Divider>{() => '界面显示'}</Divider>
  539 + <Divider>{() => t('interfaceDisplay')}</Divider>
538 540 {renderContent()}
539   - <Divider>{() => '切换动画'}</Divider>
  541 + <Divider>{() => t('animation')}</Divider>
540 542 {renderTransition()}
541 543 <Divider />
542 544 <FooterButton />
... ...
src/layouts/default/setting/enum.ts
1   -import { ContentEnum, RouterTransitionEnum, ThemeEnum } from '/@/enums/appEnum';
  1 +import { ContentEnum, RouterTransitionEnum } from '/@/enums/appEnum';
2 2 import { MenuModeEnum, MenuTypeEnum, TopMenuAlignEnum, TriggerEnum } from '/@/enums/menuEnum';
3 3  
4 4 import mixImg from '/@/assets/images/layout/menu-mix.svg';
5 5 import sidebarImg from '/@/assets/images/layout/menu-sidebar.svg';
6 6 import menuTopImg from '/@/assets/images/layout/menu-top.svg';
  7 +import { useI18n } from '/@/hooks/web/useI18n';
  8 +
  9 +const { t } = useI18n('layout.setting');
7 10  
8 11 export enum HandlerEnum {
9 12 CHANGE_LAYOUT,
... ... @@ -45,55 +48,44 @@ export enum HandlerEnum {
45 48 OPEN_ROUTE_TRANSITION,
46 49 }
47 50  
48   -export const themeOptions = [
49   - {
50   - value: ThemeEnum.LIGHT,
51   - label: '亮色',
52   - },
53   - {
54   - value: ThemeEnum.DARK,
55   - label: '暗色',
56   - },
57   -];
58   -
59 51 export const contentModeOptions = [
60 52 {
61 53 value: ContentEnum.FULL,
62   - label: '流式',
  54 + label: t('contentModeFull'),
63 55 },
64 56 {
65 57 value: ContentEnum.FIXED,
66   - label: '定宽',
  58 + label: t('contentModeFixed'),
67 59 },
68 60 ];
69 61  
70 62 export const topMenuAlignOptions = [
71 63 {
72 64 value: TopMenuAlignEnum.CENTER,
73   - label: '居中',
  65 + label: t('topMenuAlignRight'),
74 66 },
75 67 {
76 68 value: TopMenuAlignEnum.START,
77   - label: '居左',
  69 + label: t('topMenuAlignLeft'),
78 70 },
79 71 {
80 72 value: TopMenuAlignEnum.END,
81   - label: '居右',
  73 + label: t('topMenuAlignCenter'),
82 74 },
83 75 ];
84 76  
85 77 export const menuTriggerOptions = [
86 78 {
87 79 value: TriggerEnum.NONE,
88   - label: '不显示',
  80 + label: t('menuTriggerNone'),
89 81 },
90 82 {
91 83 value: TriggerEnum.FOOTER,
92   - label: '底部',
  84 + label: t('menuTriggerBottom'),
93 85 },
94 86 {
95 87 value: TriggerEnum.HEADER,
96   - label: '顶部',
  88 + label: t('menuTriggerTop'),
97 89 },
98 90 ];
99 91  
... ... @@ -112,20 +104,20 @@ export const routerTransitionOptions = [
112 104  
113 105 export const menuTypeList = [
114 106 {
115   - title: '左侧菜单模式',
  107 + title: t('menuTypeSidebar'),
116 108 mode: MenuModeEnum.INLINE,
117 109 type: MenuTypeEnum.SIDEBAR,
118 110 src: sidebarImg,
119 111 },
120 112 {
121   - title: '混合模式',
  113 + title: t('menuTypeMix'),
122 114 mode: MenuModeEnum.INLINE,
123 115 type: MenuTypeEnum.MIX,
124 116 src: mixImg,
125 117 },
126 118  
127 119 {
128   - title: '顶部菜单模式',
  120 + title: t('menuTypeTopMenu'),
129 121 mode: MenuModeEnum.HORIZONTAL,
130 122 type: MenuTypeEnum.TOP_MENU,
131 123 src: menuTopImg,
... ...
src/layouts/default/sider/index.tsx
1 1 import './index.less';
2 2  
3   -import { computed, defineComponent, ref, unref, watch, nextTick } from 'vue';
  3 +import { computed, defineComponent, ref, unref, watch, nextTick, CSSProperties } from 'vue';
4 4  
5 5 import { Layout } from 'ant-design-vue';
6 6 import LayoutMenu from '../menu';
... ... @@ -91,17 +91,19 @@ export default defineComponent({
91 91 }
92 92 );
93 93  
94   - const getHiddenDomStyle = computed(() => {
95   - const width = `${unref(getRealWidth)}px`;
96   - return {
97   - width: width,
98   - overflow: 'hidden',
99   - flex: `0 0 ${width}`,
100   - 'max-width': width,
101   - 'min-width': width,
102   - transition: 'all 0.2s',
103   - };
104   - });
  94 + const getHiddenDomStyle = computed(
  95 + (): CSSProperties => {
  96 + const width = `${unref(getRealWidth)}px`;
  97 + return {
  98 + width: width,
  99 + overflow: 'hidden',
  100 + flex: `0 0 ${width}`,
  101 + maxWidth: width,
  102 + minWidth: width,
  103 + transition: 'all 0.2s',
  104 + };
  105 + }
  106 + );
105 107  
106 108 function renderDefault() {
107 109 return (
... ...
src/locales/lang/en/layout/footer.ts 0 → 100644
  1 +export default {
  2 + onlinePreview: 'Preview',
  3 + onlineDocument: 'Document',
  4 +};
... ...
src/locales/lang/en/layout/header.ts 0 → 100644
  1 +export default {
  2 + // user dropdown
  3 + dropdownItemDoc: 'Document',
  4 + dropdownItemLoginOut: 'Login Out',
  5 +
  6 + tooltipErrorLog: 'Error log',
  7 + tooltipLock: 'Lock screen',
  8 + tooltipNotify: 'Notification',
  9 + tooltipRedo: 'Refresh',
  10 + tooltipEntryFull: 'Full Screen',
  11 + tooltipExitFull: 'Exit Full Screen',
  12 +
  13 + // lock
  14 + lockScreenPassword: 'Lock screen password',
  15 + lockScreen: 'Lock screen',
  16 + lockScreenBtn: 'Locking',
  17 + notLockScreenPassword: 'No password lock screen',
  18 +};
... ...
src/locales/lang/en/layout/multipleTab.ts 0 → 100644
  1 +export default {
  2 + redo: 'Refresh',
  3 + close: 'Close',
  4 + closeLeft: 'Close Left',
  5 + closeRight: 'Close Right',
  6 + closeOther: 'Close Other',
  7 + closeAll: 'Close All',
  8 + putAway: 'PutAway',
  9 + unfold: 'Unfold',
  10 +};
... ...
src/locales/lang/en/layout/setting.ts 0 → 100644
  1 +export default {
  2 + // content mode
  3 + contentModeFull: 'Full',
  4 + contentModeFixed: 'Fixed width',
  5 + // topMenu align
  6 + topMenuAlignLeft: 'Left',
  7 + topMenuAlignRight: 'Center',
  8 + topMenuAlignCenter: 'Right',
  9 + // menu trigger
  10 + menuTriggerNone: 'Not Show',
  11 + menuTriggerBottom: 'Bottom',
  12 + menuTriggerTop: 'Top',
  13 + // menu type
  14 + menuTypeSidebar: 'Left menu mode',
  15 + menuTypeMix: 'Mixed mode',
  16 + menuTypeTopMenu: 'Top menu mode',
  17 +
  18 + on: 'On',
  19 + off: 'Off',
  20 + minute: 'Minute',
  21 +
  22 + operatingTitle: 'Successful!',
  23 + operatingContent:
  24 + 'The copy is successful, please go to src/settings/projectSetting.ts to modify the configuration!',
  25 + resetSuccess: 'Successfully reset!',
  26 +
  27 + copyBtn: 'Copy',
  28 + resetBtn: 'Reset',
  29 + clearBtn: 'Clear cache and to the login page',
  30 +
  31 + drawerTitle: 'Configuration',
  32 +
  33 + navMode: 'Navigation mode',
  34 + interfaceFunction: 'Interface function',
  35 + interfaceDisplay: 'Interface display',
  36 + animation: 'Animation',
  37 + splitMenu: 'Split menu',
  38 +
  39 + headerTheme: 'Header theme',
  40 + sidebarTheme: 'Menu theme',
  41 +
  42 + menuDrag: 'Drag Sidebar',
  43 + menuSearch: 'Sidebar search',
  44 + menuAccordion: 'Sidebar accordion',
  45 + menuCollapse: 'Collapse menu',
  46 + collapseMenuDisplayName: 'Collapse menu display name',
  47 + topMenuLayout: 'Top menu layout',
  48 + menuCollapseButton: 'Menu collapse button',
  49 + contentMode: 'Content area width',
  50 + expandedMenuWidth: 'Expanded menu width',
  51 +
  52 + breadcrumb: 'Breadcrumbs',
  53 + breadcrumbIcon: 'Breadcrumbs Icon',
  54 + tabs: 'Tabs',
  55 + tabsQuickBtn: 'Tabs quick button',
  56 + sidebar: 'Sidebar',
  57 + header: 'Header',
  58 + footer: 'Footer',
  59 + fullContent: 'Full content',
  60 + grayMode: 'Gray mode',
  61 + colorWeak: 'Color Weak Mode',
  62 +
  63 + progress: 'Progress',
  64 + switchLoading: 'Switch Loading',
  65 + switchAnimation: 'Switch animation',
  66 + animationType: 'Animation type',
  67 +
  68 + autoScreenLock: 'Auto screen lock',
  69 + notAutoScreenLock: 'Not auto lock',
  70 +
  71 + fixedHeader: 'Fixed header',
  72 + fixedSideBar: 'Fixed Sidebar',
  73 +};
... ...
src/locales/lang/zh_CN/layout/footer.ts 0 → 100644
  1 +export default {
  2 + onlinePreview: '在线预览',
  3 + onlineDocument: '在线文档',
  4 +};
... ...
src/locales/lang/zh_CN/layout/header.ts 0 → 100644
  1 +export default {
  2 + // user dropdown
  3 + dropdownItemDoc: '文档',
  4 + dropdownItemLoginOut: '退出系统',
  5 +
  6 + // tooltip
  7 + tooltipErrorLog: '错误日志',
  8 + tooltipLock: '锁定屏幕',
  9 + tooltipNotify: '消息通知',
  10 + tooltipRedo: '刷新',
  11 + tooltipEntryFull: '全屏',
  12 + tooltipExitFull: '退出全屏',
  13 +
  14 + // lock
  15 + lockScreenPassword: '锁屏密码',
  16 + lockScreen: '锁定屏幕',
  17 + lockScreenBtn: '锁定',
  18 + notLockScreenPassword: '不设置密码锁屏',
  19 +};
... ...
src/locales/lang/zh_CN/layout/multipleTab.ts 0 → 100644
  1 +export default {
  2 + redo: '刷新',
  3 + close: '关闭',
  4 + closeLeft: '关闭左侧',
  5 + closeRight: '关闭右侧',
  6 + closeOther: '关闭其他',
  7 + closeAll: '关闭全部',
  8 + putAway: '收起',
  9 + unfold: '展开',
  10 +};
... ...
src/locales/lang/zh_CN/layout/setting.ts 0 → 100644
  1 +export default {
  2 + // content mode
  3 + contentModeFull: '流式',
  4 + contentModeFixed: '定宽',
  5 + // topMenu align
  6 + topMenuAlignLeft: '居左',
  7 + topMenuAlignRight: '居中',
  8 + topMenuAlignCenter: '居右',
  9 + // menu trigger
  10 + menuTriggerNone: '不显示',
  11 + menuTriggerBottom: '底部',
  12 + menuTriggerTop: '顶部',
  13 + // menu type
  14 + menuTypeSidebar: '左侧菜单模式',
  15 + menuTypeMix: '混合模式',
  16 + menuTypeTopMenu: '顶部菜单模式',
  17 +
  18 + on: '开',
  19 + off: '关',
  20 + minute: '分钟',
  21 +
  22 + operatingTitle: '操作成功',
  23 + operatingContent: '复制成功,请到 src/settings/projectSetting.ts 中修改配置!',
  24 + resetSuccess: '重置成功!',
  25 +
  26 + copyBtn: '拷贝',
  27 + resetBtn: '重置',
  28 + clearBtn: '清空缓存并返回登录页',
  29 +
  30 + drawerTitle: '项目配置',
  31 +
  32 + navMode: '导航栏模式',
  33 + interfaceFunction: '界面功能',
  34 + interfaceDisplay: '界面显示',
  35 + animation: '动画',
  36 + splitMenu: '分割菜单',
  37 +
  38 + headerTheme: '顶栏主题',
  39 + sidebarTheme: '菜单主题',
  40 +
  41 + menuDrag: '侧边菜单拖拽',
  42 + menuSearch: '侧边菜单搜索',
  43 + menuAccordion: '侧边菜单手风琴模式',
  44 + menuCollapse: '折叠菜单',
  45 + collapseMenuDisplayName: '折叠菜单显示名称',
  46 + topMenuLayout: '顶部菜单布局',
  47 + menuCollapseButton: '菜单折叠按钮',
  48 + contentMode: '内容区域宽度',
  49 + expandedMenuWidth: '菜单展开宽度',
  50 +
  51 + breadcrumb: '面包屑',
  52 + breadcrumbIcon: '面包屑图标',
  53 + tabs: '标签页',
  54 + tabsQuickBtn: '标签页快捷按钮',
  55 + sidebar: '左侧菜单',
  56 + header: '顶栏',
  57 + footer: '页脚',
  58 + fullContent: '全屏内容',
  59 + grayMode: '灰色模式',
  60 + colorWeak: '色弱模式',
  61 +
  62 + progress: '顶部进度条',
  63 + switchLoading: '切换loading',
  64 + switchAnimation: '切换动画',
  65 + animationType: '动画类型',
  66 +
  67 + autoScreenLock: '自动锁屏',
  68 + notAutoScreenLock: '不自动锁屏',
  69 +
  70 + fixedHeader: '固定header',
  71 + fixedSideBar: '固定Sidebar',
  72 +};
... ...
src/settings/projectSetting.ts
... ... @@ -41,6 +41,7 @@ const setting: ProjectConfig = {
41 41  
42 42 // locale setting
43 43 locale: {
  44 + show: true,
44 45 // Locale
45 46 lang: 'zh_CN',
46 47 // Default locale
... ...
src/types/config.d.ts
... ... @@ -51,6 +51,7 @@ export interface HeaderSetting {
51 51 }
52 52  
53 53 export interface LocaleSetting {
  54 + show: boolean;
54 55 // Current language
55 56 lang: LocaleType;
56 57 // default language
... ...
src/views/sys/error-log/DetailModal.vue
1 1 <template>
2   - <BasicModal :width="800" :title="t('sys.errorLog.tableActionDesc')" v-bind="$attrs">
  2 + <BasicModal :width="800" :title="t('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 9 import { BasicModal } from '/@/components/Modal/index';
11 10 import { ErrorInfo } from '/@/store/modules/error';
12 11 import { Description, useDescription } from '/@/components/Description/index';
  12 + import { useI18n } from '/@/hooks/web/useI18n';
13 13  
14 14 import { getDescSchema } from './data';
15 15  
... ... @@ -23,7 +23,7 @@
23 23 },
24 24 },
25 25 setup() {
26   - const { t } = useI18n();
  26 + const { t } = useI18n('sys.errorLog');
27 27 const [register] = useDescription({
28 28 column: 2,
29 29 schema: getDescSchema(),
... ...
src/views/sys/error-log/data.tsx
1 1 import { Tag } from 'ant-design-vue';
2 2 import { BasicColumn } from '/@/components/Table/index';
3 3 import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
  4 +import { useI18n } from '/@/hooks/web/useI18n';
4 5  
5   -import { useExternalI18n } from '/@/hooks/web/useLocale';
6   -
7   -const { t } = useExternalI18n();
  6 +const { t } = useI18n('sys.errorLog');
8 7  
9 8 export function getColumns(): BasicColumn[] {
10 9 return [
11 10 {
12 11 dataIndex: 'type',
13   - title: t('sys.errorLog.tableColumnType'),
  12 + title: t('tableColumnType'),
14 13 width: 80,
15 14 customRender: ({ text }) => {
16 15 const color =
... ... @@ -33,12 +32,12 @@ export function getColumns(): BasicColumn[] {
33 32 },
34 33 {
35 34 dataIndex: 'time',
36   - title: t('sys.errorLog.tableColumnDate'),
  35 + title: t('tableColumnDate'),
37 36 width: 160,
38 37 },
39 38 {
40 39 dataIndex: 'file',
41   - title: t('sys.errorLog.tableColumnFile'),
  40 + title: t('tableColumnFile'),
42 41 width: 200,
43 42 },
44 43 {
... ... @@ -48,12 +47,12 @@ export function getColumns(): BasicColumn[] {
48 47 },
49 48 {
50 49 dataIndex: 'message',
51   - title: t('sys.errorLog.tableColumnMsg'),
  50 + title: t('tableColumnMsg'),
52 51 width: 300,
53 52 },
54 53 {
55 54 dataIndex: 'stack',
56   - title: t('sys.errorLog.tableColumnStackMsg'),
  55 + title: t('tableColumnStackMsg'),
57 56 width: 300,
58 57 },
59 58 ];
... ...
src/views/sys/error-log/index.vue
... ... @@ -35,7 +35,7 @@
35 35  
36 36 import { useModal } from '/@/components/Modal/index';
37 37 import { useMessage } from '/@/hooks/web/useMessage';
38   - import { useI18n } from 'vue-i18n';
  38 + import { useI18n } from '/@/hooks/web/useI18n';
39 39  
40 40 import { errorStore, ErrorInfo } from '/@/store/modules/error';
41 41  
... ... @@ -53,7 +53,7 @@
53 53 const rowInfoRef = ref<ErrorInfo>();
54 54 const imgListRef = ref<string[]>([]);
55 55  
56   - const { t } = useI18n();
  56 + const { t } = useI18n('sys.errorLog');
57 57  
58 58 const [register, { setTableData }] = useTable({
59 59 title: t('sys.errorLog.tableTitle'),
... ... @@ -80,7 +80,7 @@
80 80 );
81 81 const { createMessage } = useMessage();
82 82 if (isDevMode()) {
83   - createMessage.info(t('sys.errorLog.enableMessage'));
  83 + createMessage.info(t('enableMessage'));
84 84 }
85 85 // 查看详情
86 86 function handleDetail(row: ErrorInfo) {
... ...
src/views/sys/exception/Exception.tsx
  1 +import './exception.less';
  2 +
1 3 import type { PropType } from 'vue';
2 4  
3 5 import { Result, Button } from 'ant-design-vue';
... ... @@ -12,9 +14,8 @@ import { useRoute } from &#39;vue-router&#39;;
12 14  
13 15 import { useGo, useRedo } from '/@/hooks/web/usePage';
14 16 import { PageEnum } from '/@/enums/pageEnum';
15   -import { useI18n } from 'vue-i18n';
  17 +import { useI18n } from '/@/hooks/web/useI18n';
16 18  
17   -import './exception.less';
18 19 interface MapValue {
19 20 title: string;
20 21 subTitle: string;
... ... @@ -52,7 +53,7 @@ export default defineComponent({
52 53 const { query } = useRoute();
53 54 const go = useGo();
54 55 const redo = useRedo();
55   - const { t } = useI18n();
  56 + const { t } = useI18n('sys.exception');
56 57  
57 58 const getStatus = computed(() => {
58 59 const { status: routeStatus } = query;
... ... @@ -66,13 +67,13 @@ export default defineComponent({
66 67 }
67 68 );
68 69  
69   - const backLoginI18n = t('sys.exception.backLogin');
70   - const backHomeI18n = t('sys.exception.backHome');
  70 + const backLoginI18n = t('backLogin');
  71 + const backHomeI18n = t('backHome');
71 72  
72 73 unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_ACCESS, {
73 74 title: '403',
74 75 status: `${ExceptionEnum.PAGE_NOT_ACCESS}`,
75   - subTitle: t('sys.exception.subTitle403'),
  76 + subTitle: t('subTitle403'),
76 77 btnText: props.full ? backLoginI18n : backHomeI18n,
77 78 handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()),
78 79 });
... ... @@ -80,7 +81,7 @@ export default defineComponent({
80 81 unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_FOUND, {
81 82 title: '404',
82 83 status: `${ExceptionEnum.PAGE_NOT_FOUND}`,
83   - subTitle: t('sys.exception.subTitle404'),
  84 + subTitle: t('subTitle404'),
84 85 btnText: props.full ? backLoginI18n : backHomeI18n,
85 86 handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()),
86 87 });
... ... @@ -88,22 +89,22 @@ export default defineComponent({
88 89 unref(statusMapRef).set(ExceptionEnum.ERROR, {
89 90 title: '500',
90 91 status: `${ExceptionEnum.ERROR}`,
91   - subTitle: t('sys.exception.subTitle500'),
  92 + subTitle: t('subTitle500'),
92 93 btnText: backHomeI18n,
93 94 handler: () => go(),
94 95 });
95 96  
96 97 unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_DATA, {
97   - title: t('sys.exception.noDataTitle'),
  98 + title: t('noDataTitle'),
98 99 subTitle: '',
99   - btnText: t('sys.exception.redo'),
  100 + btnText: t('redo'),
100 101 handler: () => redo(),
101 102 icon: notDataImg,
102 103 });
103 104  
104 105 unref(statusMapRef).set(ExceptionEnum.NET_WORK_ERROR, {
105   - title: t('sys.exception.networkErrorTitle'),
106   - subTitle: t('sys.exception.networkErrorSubTitle'),
  106 + title: t('networkErrorTitle'),
  107 + subTitle: t('networkErrorSubTitle'),
107 108 btnText: 'Refresh',
108 109 handler: () => redo(),
109 110 icon: netWorkImg,
... ...
src/views/sys/lock/index.vue
... ... @@ -6,7 +6,7 @@
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="t('sys.lock.alert')" banner />
  9 + <Alert v-if="errMsgRef" type="error" :message="t('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') }}
... ... @@ -26,8 +26,7 @@
26 26  
27 27 import { userStore } from '/@/store/modules/user';
28 28 import { appStore } from '/@/store/modules/app';
29   -
30   - import { useI18n } from 'vue-i18n';
  29 + import { useI18n } from '/@/hooks/web/useI18n';
31 30  
32 31 export default defineComponent({
33 32 name: 'LockPage',
... ... @@ -37,7 +36,7 @@
37 36 const loadingRef = ref(false);
38 37 const errMsgRef = ref(false);
39 38  
40   - const { t } = useI18n();
  39 + const { t } = useI18n('sys.lock');
41 40 const [register, { validateFields }] = useForm({
42 41 showActionButtonGroup: false,
43 42 schemas: [
... ... @@ -47,7 +46,7 @@
47 46 component: 'InputPassword',
48 47 componentProps: {
49 48 style: { width: '100%' },
50   - placeholder: t('sys.lock.placeholder'),
  49 + placeholder: t('placeholder'),
51 50 },
52 51 rules: [{ required: true }],
53 52 },
... ...
src/views/sys/login/Login.vue
... ... @@ -4,7 +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 + <AppLocalePicker v-if="showLocale" class="login-form__locale" />
8 8 <header>
9 9 <img :src="logo" class="mr-4" />
10 10 <h1>{{ title }}</h1>
... ... @@ -64,23 +64,23 @@
64 64 import { Checkbox } from 'ant-design-vue';
65 65  
66 66 import Button from '/@/components/Button/index.vue';
67   - import { AppLocalPicker } from '/@/components/Application';
  67 + import { AppLocalePicker } from '/@/components/Application';
68 68 // import { BasicDragVerify, DragVerifyActionType } from '/@/components/Verify/index';
69 69  
70 70 import { userStore } from '/@/store/modules/user';
71   - import { useI18n } from 'vue-i18n';
72 71  
73 72 // import { appStore } from '/@/store/modules/app';
74 73 import { useMessage } from '/@/hooks/web/useMessage';
75   - import { useGlobSetting } from '/@/hooks/setting';
  74 + import { useGlobSetting, useProjectSetting } from '/@/hooks/setting';
76 75 import logo from '/@/assets/images/logo.png';
  76 + import { useI18n } from '/@/hooks/web/useI18n';
77 77  
78 78 export default defineComponent({
79 79 components: {
80 80 // BasicDragVerify,
81 81 AButton: Button,
82 82 ACheckbox: Checkbox,
83   - AppLocalPicker,
  83 + AppLocalePicker,
84 84 },
85 85 setup() {
86 86 const formRef = ref<any>(null);
... ... @@ -88,8 +88,9 @@
88 88 // const verifyRef = ref<RefInstanceType<DragVerifyActionType>>(null);
89 89  
90 90 const globSetting = useGlobSetting();
  91 + const { locale } = useProjectSetting();
91 92 const { notification } = useMessage();
92   - const { t } = useI18n();
  93 + const { t } = useI18n('sys.login');
93 94  
94 95 // const openLoginVerifyRef = computed(() => appStore.getProjectConfig.openLoginVerify);
95 96  
... ... @@ -103,10 +104,8 @@
103 104 });
104 105  
105 106 const formRules = reactive({
106   - account: [{ required: true, message: t('sys.login.accountPlaceholder'), trigger: 'blur' }],
107   - password: [
108   - { required: true, message: t('sys.login.passwordPlaceholder'), trigger: 'blur' },
109   - ],
  107 + account: [{ required: true, message: t('accountPlaceholder'), trigger: 'blur' }],
  108 + password: [{ required: true, message: t('passwordPlaceholder'), trigger: 'blur' }],
110 109 // verify: unref(openLoginVerifyRef) ? [{ required: true, message: '请通过验证码校验' }] : [],
111 110 });
112 111  
... ... @@ -131,8 +130,8 @@
131 130 );
132 131 if (userInfo) {
133 132 notification.success({
134   - message: t('sys.login.loginSuccessTitle'),
135   - description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realName}`,
  133 + message: t('loginSuccessTitle'),
  134 + description: `${t('loginSuccessDesc')}: ${userInfo.realName}`,
136 135 duration: 3,
137 136 });
138 137 }
... ... @@ -154,6 +153,7 @@
154 153 title: globSetting && globSetting.title,
155 154 logo,
156 155 t,
  156 + showLocale: locale.show,
157 157 };
158 158 },
159 159 });
... ...