Commit e5f8ce3fd8ec25c6fdb122867cd33e4e84a6f43f
1 parent
cedba37e
feat: multi-language layout
Showing
44 changed files
with
504 additions
and
386 deletions
mock/sys/user.ts
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
src/design/var/index.less
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
src/layouts/default/footer/index.tsx
... | ... | @@ -8,18 +8,21 @@ import { GithubFilled } from '@ant-design/icons-vue'; |
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 ©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 '/@/hooks/event/useWindowSizeFn'; |
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 '/@/store/modules/error'; |
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 '/@/utils'; |
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<MenuItemProps> = (props) => { |
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
src/layouts/default/lock/LockAction.tsx
... | ... | @@ -9,11 +9,13 @@ import headerImg from '/@/assets/images/header.jpg'; |
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 './useTabDropdown'; |
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 '/@/components/Dropdown/index'; |
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 '/@/hooks/setting/useMenuSetting'; |
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 = () => { |
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 = () => { |
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 = () => { |
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 = () => { |
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 = () => { |
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
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
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
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
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
src/types/config.d.ts
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 'vue-router'; |
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 | }); | ... | ... |