@liujitcn/kratos-admin-core 0.0.20 → 0.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +2 -2
  2. package/dist/package/declarations/src/api/base/language.d.ts +8 -0
  3. package/dist/package/declarations/src/components/Dialog/ProDialog.vue.d.ts +1 -0
  4. package/dist/package/declarations/src/components/Dict/index.vue.d.ts +4 -3
  5. package/dist/package/declarations/src/components/Dict/value.d.ts +11 -0
  6. package/dist/package/declarations/src/components/LocaleSwitch/index.vue.d.ts +3 -0
  7. package/dist/package/declarations/src/index.d.ts +2 -0
  8. package/dist/package/declarations/src/locales/generated.d.ts +3382 -0
  9. package/dist/package/declarations/src/locales/index.d.ts +53 -0
  10. package/dist/package/declarations/src/modules/index.d.ts +7 -2
  11. package/dist/package/declarations/src/request.d.ts +1 -0
  12. package/dist/package/declarations/src/routers/modules/staticRouter.d.ts +1 -0
  13. package/dist/package/declarations/src/rpc/base/v1/config.d.ts +13 -1
  14. package/dist/package/declarations/src/rpc/base/v1/language.d.ts +20 -0
  15. package/dist/package/declarations/src/rpc/base/v1/login.d.ts +13 -1
  16. package/dist/package/declarations/src/rpc/system/admin/v1/auth.d.ts +1 -1
  17. package/dist/package/declarations/src/rpc/system/admin/v1/base_dict.d.ts +9 -0
  18. package/dist/package/declarations/src/rpc/system/admin/v1/base_translation.d.ts +75 -0
  19. package/dist/package/declarations/src/rpc/system/admin/v1/common.d.ts +13 -0
  20. package/dist/package/declarations/src/stores/interface/index.d.ts +2 -0
  21. package/dist/package/declarations/src/stores/modules/auth.d.ts +6 -6
  22. package/dist/package/declarations/src/stores/runtime.d.ts +1 -0
  23. package/dist/package/declarations/src/utils/dict.d.ts +2 -2
  24. package/dist/package/declarations/src/utils/index.d.ts +1 -1
  25. package/dist/package/declarations/src/utils/oauthProvider.d.ts +2 -0
  26. package/dist/package/declarations/src/utils/passwordCrypto.d.ts +1 -1
  27. package/dist/package/declarations/src/utils/passwordStrength.d.ts +0 -7
  28. package/dist/package/src/App.vue +5 -2
  29. package/dist/package/src/api/base/language.ts +20 -0
  30. package/dist/package/src/api/base/oauth.ts +0 -1
  31. package/dist/package/src/api/system/auth.ts +0 -1
  32. package/dist/package/src/bootstrap.ts +57 -2
  33. package/dist/package/src/components/CronExpression/index.vue +80 -82
  34. package/dist/package/src/components/Dialog/FormDialog.vue +10 -5
  35. package/dist/package/src/components/Dialog/ProDialog.vue +10 -4
  36. package/dist/package/src/components/Dict/index.vue +26 -9
  37. package/dist/package/src/components/Dict/value.ts +18 -0
  38. package/dist/package/src/components/ECharts/config/index.ts +6 -1
  39. package/dist/package/src/components/ErrorMessage/index.vue +3 -1
  40. package/dist/package/src/components/ImportExcel/index.vue +29 -18
  41. package/dist/package/src/components/LocaleSwitch/index.vue +28 -0
  42. package/dist/package/src/components/PasswordStrength/index.vue +8 -5
  43. package/dist/package/src/components/ProForm/components/DynamicList.vue +8 -3
  44. package/dist/package/src/components/ProForm/components/KvList.vue +14 -6
  45. package/dist/package/src/components/ProForm/index.vue +2 -2
  46. package/dist/package/src/components/ProTable/components/ColSetting.vue +7 -5
  47. package/dist/package/src/components/ProTable/components/TableColumn.vue +2 -1
  48. package/dist/package/src/components/ProTable/index.vue +36 -11
  49. package/dist/package/src/components/SearchForm/components/SearchFormItem.vue +9 -4
  50. package/dist/package/src/components/SearchForm/index.vue +6 -3
  51. package/dist/package/src/components/SelectFilter/index.vue +4 -1
  52. package/dist/package/src/components/SelectIcon/index.vue +8 -4
  53. package/dist/package/src/components/TreeFilter/index.vue +14 -6
  54. package/dist/package/src/components/Upload/File.vue +18 -13
  55. package/dist/package/src/components/Upload/Files.vue +22 -15
  56. package/dist/package/src/components/Upload/Img.vue +14 -11
  57. package/dist/package/src/components/Upload/Imgs.vue +15 -12
  58. package/dist/package/src/components/WangEditor/index.vue +12 -4
  59. package/dist/package/src/directives/modules/copy.ts +2 -1
  60. package/dist/package/src/hooks/useDownload.ts +3 -2
  61. package/dist/package/src/hooks/useHandleData.ts +5 -4
  62. package/dist/package/src/hooks/useTheme.ts +2 -1
  63. package/dist/package/src/hooks/useTime.ts +11 -2
  64. package/dist/package/src/index.ts +2 -0
  65. package/dist/package/src/layouts/components/Header/ToolBarRight.vue +2 -0
  66. package/dist/package/src/layouts/components/Header/components/AssemblySize.vue +8 -6
  67. package/dist/package/src/layouts/components/Header/components/Avatar.vue +11 -9
  68. package/dist/package/src/layouts/components/Header/components/Breadcrumb.vue +11 -3
  69. package/dist/package/src/layouts/components/Header/components/Fullscreen.vue +4 -2
  70. package/dist/package/src/layouts/components/Header/components/InfoDialog.vue +6 -3
  71. package/dist/package/src/layouts/components/Header/components/PasswordDialog.vue +6 -3
  72. package/dist/package/src/layouts/components/Header/components/SearchMenu.vue +5 -3
  73. package/dist/package/src/layouts/components/Header/components/ThemeSetting.vue +3 -1
  74. package/dist/package/src/layouts/components/Main/index.vue +1 -0
  75. package/dist/package/src/layouts/components/Tabs/components/MoreButton.vue +9 -7
  76. package/dist/package/src/layouts/components/Tabs/index.vue +15 -1
  77. package/dist/package/src/layouts/components/ThemeDrawer/index.vue +27 -24
  78. package/dist/package/src/locales/README.md +15 -0
  79. package/dist/package/src/locales/en-US.json +283 -0
  80. package/dist/package/src/locales/es-ES.json +283 -0
  81. package/dist/package/src/locales/fr-FR.json +283 -0
  82. package/dist/package/src/locales/generated.ts +57 -0
  83. package/dist/package/src/locales/index.ts +222 -0
  84. package/dist/package/src/locales/ja-JP.json +283 -0
  85. package/dist/package/src/locales/ko-KR.json +283 -0
  86. package/dist/package/src/locales/zh-CN.json +283 -0
  87. package/dist/package/src/locales/zh-TW.json +283 -0
  88. package/dist/package/src/modules/index.ts +7 -2
  89. package/dist/package/src/modules/kratosAdmin.ts +5 -1
  90. package/dist/package/src/request.ts +8 -1
  91. package/dist/package/src/routers/index.ts +4 -1
  92. package/dist/package/src/routers/modules/dynamicRouter.ts +5 -4
  93. package/dist/package/src/routers/modules/staticRouter.ts +8 -4
  94. package/dist/package/src/rpc/base/v1/config.ts +14 -1
  95. package/dist/package/src/rpc/base/v1/language.ts +31 -0
  96. package/dist/package/src/rpc/base/v1/login.ts +14 -1
  97. package/dist/package/src/rpc/system/admin/v1/auth.ts +1 -1
  98. package/dist/package/src/rpc/system/admin/v1/base_dict.ts +9 -0
  99. package/dist/package/src/rpc/system/admin/v1/base_translation.ts +90 -0
  100. package/dist/package/src/rpc/system/admin/v1/common.ts +21 -0
  101. package/dist/package/src/stores/interface/index.ts +2 -0
  102. package/dist/package/src/stores/modules/config.ts +7 -3
  103. package/dist/package/src/stores/modules/user.ts +2 -1
  104. package/dist/package/src/stores/runtime.ts +1 -0
  105. package/dist/package/src/styles/element.scss +2 -0
  106. package/dist/package/src/utils/color.ts +5 -4
  107. package/dist/package/src/utils/dict.ts +27 -4
  108. package/dist/package/src/utils/eleValidate.ts +3 -2
  109. package/dist/package/src/utils/errorHandler.ts +9 -8
  110. package/dist/package/src/utils/index.ts +9 -8
  111. package/dist/package/src/utils/oauthProvider.ts +15 -10
  112. package/dist/package/src/utils/passwordCrypto.ts +4 -3
  113. package/dist/package/src/utils/passwordStrength.ts +1 -16
  114. package/dist/package/src/utils/request.ts +13 -8
  115. package/dist/package/src/views/error/403.vue +3 -1
  116. package/dist/package/src/views/error/404.vue +3 -1
  117. package/dist/package/src/views/error/500.vue +3 -1
  118. package/dist/package/src/views/error/pending.vue +5 -2
  119. package/dist/package/src/views/login/components/LoginForm.vue +58 -29
  120. package/dist/package/src/views/login/index.scss +6 -0
  121. package/dist/package/src/views/login/index.vue +2 -0
  122. package/dist/package/types/generated/auto-imports.d.ts +129 -111
  123. package/dist/package/types/generated/components.d.ts +67 -67
  124. package/package.json +4 -2
  125. package/types/generated/auto-imports.d.ts +129 -111
  126. package/types/generated/components.d.ts +67 -67
  127. package/vite.config.ts +12 -0
  128. package/dist/package/declarations/src/rpc/base/v1/enum.d.ts +0 -43
  129. package/dist/package/declarations/src/rpc/system/common/v1/enum.d.ts +0 -67
  130. package/dist/package/src/rpc/base/v1/enum.ts +0 -54
  131. package/dist/package/src/rpc/system/common/v1/enum.ts +0 -80
@@ -1,6 +1,7 @@
1
1
  import { defLoginService } from "../api/base/login";
2
- import { PasswordCryptoScene } from "../rpc/base/v1/enum";
2
+ import { PasswordCryptoScene } from "../rpc/base/v1/login";
3
3
  import type { PasswordCrypto } from "../rpc/common/v1/types";
4
+ import { t } from "../locales";
4
5
 
5
6
  export const PASSWORD_CRYPTO_SCENE = PasswordCryptoScene;
6
7
  export type { PasswordCryptoScene };
@@ -33,7 +34,7 @@ function arrayBufferToBase64(buffer: ArrayBuffer) {
33
34
  function getSubtleCrypto() {
34
35
  const cryptoApi = globalThis.crypto;
35
36
  if (!cryptoApi?.subtle) {
36
- throw new Error("当前环境不支持密码加密");
37
+ throw new Error(t("core.password.crypto_unsupported"));
37
38
  }
38
39
  return cryptoApi;
39
40
  }
@@ -42,7 +43,7 @@ function getSubtleCrypto() {
42
43
  export async function encryptPassword(password: string, scene: PasswordCryptoScene): Promise<PasswordCrypto> {
43
44
  const plainPassword = password.trim();
44
45
  if (!plainPassword) {
45
- throw new Error("密码不能为空");
46
+ throw new Error(t("core.password.required"));
46
47
  }
47
48
 
48
49
  const cryptoApi = getSubtleCrypto();
@@ -9,18 +9,10 @@ export interface PasswordStrengthResult {
9
9
  strengthScore: number;
10
10
  /** 强度等级。 */
11
11
  level: PasswordStrengthLevel;
12
- /** 强度文案。 */
13
- text: string;
14
12
  /** 是否达到允许提交的最高强度。 */
15
13
  isValid: boolean;
16
14
  }
17
15
 
18
- /** 密码强度错误提示。 */
19
- export const PASSWORD_STRENGTH_ERROR_MESSAGE = "密码需同时包含大小写字母、数字、特殊字符,且长度不少于 8 位";
20
-
21
- /** 密码强度说明文案。 */
22
- export const PASSWORD_STRENGTH_TIP = "需同时包含大小写字母、数字、特殊字符,且长度不少于 8 位,达到最高强度后才可提交。";
23
-
24
16
  /**
25
17
  * 计算密码强度结果,供表单展示和校验统一复用。
26
18
  *
@@ -33,7 +25,6 @@ export function getPasswordStrength(password?: string): PasswordStrengthResult {
33
25
  ruleScore: 0,
34
26
  strengthScore: 0,
35
27
  level: "empty",
36
- text: "未输入",
37
28
  isValid: false
38
29
  };
39
30
  }
@@ -49,7 +40,6 @@ export function getPasswordStrength(password?: string): PasswordStrengthResult {
49
40
  ruleScore,
50
41
  strengthScore: 3,
51
42
  level: "high",
52
- text: "高",
53
43
  isValid: true
54
44
  };
55
45
  }
@@ -58,7 +48,6 @@ export function getPasswordStrength(password?: string): PasswordStrengthResult {
58
48
  ruleScore,
59
49
  strengthScore: 2,
60
50
  level: "medium",
61
- text: "中",
62
51
  isValid: false
63
52
  };
64
53
  }
@@ -66,7 +55,6 @@ export function getPasswordStrength(password?: string): PasswordStrengthResult {
66
55
  ruleScore,
67
56
  strengthScore: 1,
68
57
  level: "low",
69
- text: "低",
70
58
  isValid: false
71
59
  };
72
60
  }
@@ -79,8 +67,5 @@ export function getPasswordStrength(password?: string): PasswordStrengthResult {
79
67
  */
80
68
  export function validatePasswordStrengthValue(password?: string) {
81
69
  const result = getPasswordStrength(password);
82
- return {
83
- valid: result.isValid,
84
- message: result.isValid ? "" : PASSWORD_STRENGTH_ERROR_MESSAGE
85
- };
70
+ return { valid: result.isValid };
86
71
  }
@@ -5,6 +5,7 @@ import router from "../routers";
5
5
  import { LOGIN_URL } from "../config";
6
6
  import pinia from "../stores";
7
7
  import { useUserStore } from "../stores/modules/user";
8
+ import { getLocaleRequestHeaders, t } from "../locales";
8
9
 
9
10
  const apiBasePath = import.meta.env.VITE_APP_BASE_API || "";
10
11
  const apiTargetUrl = import.meta.env.VITE_API_URL || import.meta.env.VITE_APP_API_URL || "";
@@ -13,6 +14,7 @@ const SESSION_URL = "/v1/base/session";
13
14
  const TOKEN_URL = "/v1/base/token";
14
15
  const CAPTCHA_URL = "/v1/base/captcha";
15
16
  const CONFIG_URL = "/v1/base/config";
17
+ const LANGUAGE_URL = "/v1/base/language";
16
18
  const PASSWORD_PUBLIC_KEY_URL = "/v1/base/password-public-key";
17
19
  const OAUTH_PROVIDER_URL = "/v1/base/oauth/provider";
18
20
  const OAUTH_AUTHORIZATION_URL = "/v1/base/oauth/authorization";
@@ -26,6 +28,7 @@ const NO_AUTH_URL_SET = new Set([
26
28
  TOKEN_URL,
27
29
  CAPTCHA_URL,
28
30
  CONFIG_URL,
31
+ LANGUAGE_URL,
29
32
  PASSWORD_PUBLIC_KEY_URL,
30
33
  OAUTH_PROVIDER_URL,
31
34
  OAUTH_AUTHORIZATION_URL,
@@ -39,6 +42,7 @@ const AUTH_EXPIRED_EXCLUDED_URL_SET = new Set([
39
42
  TOKEN_URL,
40
43
  CAPTCHA_URL,
41
44
  CONFIG_URL,
45
+ LANGUAGE_URL,
42
46
  PASSWORD_PUBLIC_KEY_URL,
43
47
  OAUTH_PROVIDER_URL,
44
48
  OAUTH_AUTHORIZATION_URL,
@@ -156,9 +160,9 @@ export function handleAuthExpired() {
156
160
  }
157
161
 
158
162
  isHandlingAuthExpired = true;
159
- ElMessageBox.confirm("登录状态已失效,请重新登录", "提示", {
160
- confirmButtonText: "重新登录",
161
- cancelButtonText: "取消",
163
+ ElMessageBox.confirm(t("core.auth.session_expired"), t("common.title.notice"), {
164
+ confirmButtonText: t("core.auth.login_again"),
165
+ cancelButtonText: t("common.action.cancel"),
162
166
  type: "warning",
163
167
  closeOnClickModal: false,
164
168
  closeOnPressEscape: false
@@ -183,6 +187,7 @@ service.interceptors.request.use(
183
187
  async (config: InternalAxiosRequestConfig) => {
184
188
  const skipAuth = shouldSkipAuth(config);
185
189
  const accessToken = skipAuth ? "" : await getRequestAccessToken();
190
+ Object.assign(config.headers, getLocaleRequestHeaders());
186
191
  // 登录、验证码、刷新令牌接口不携带旧 token,避免请求头污染。
187
192
  if (!skipAuth && accessToken) {
188
193
  config.headers.Authorization = accessToken;
@@ -207,7 +212,7 @@ service.interceptors.response.use(
207
212
  return response.data;
208
213
  }
209
214
 
210
- ElMessage.error(message || "系统出错");
215
+ ElMessage.error(message || t("common.message.system_error"));
211
216
  return Promise.reject(new Error(message || "Error"));
212
217
  },
213
218
  async (error: AxiosError) => {
@@ -235,11 +240,11 @@ service.interceptors.response.use(
235
240
  if (message) {
236
241
  ElMessage.error(message);
237
242
  } else {
238
- ElMessage.error("系统出错");
243
+ ElMessage.error(t("common.message.system_error"));
239
244
  }
240
245
  }
241
246
  } else if (!shouldSkipErrorMessage(requestConfig)) {
242
- ElMessage.error(error.message || "系统出错");
247
+ ElMessage.error(error.message || t("common.message.system_error"));
243
248
  }
244
249
  return Promise.reject(error.message);
245
250
  }
@@ -278,13 +283,13 @@ async function handleTokenRefresh(promptOnFailure = true) {
278
283
  async function refreshAccessToken() {
279
284
  const userStore = getUserStore();
280
285
  if (!userStore.refreshToken) {
281
- return Promise.reject(new Error("refresh token 不存在"));
286
+ return Promise.reject(new Error(t("core.auth.refresh_token_missing")));
282
287
  }
283
288
 
284
289
  const response = await refreshService.post(
285
290
  TOKEN_URL,
286
291
  { refresh_token: userStore.refreshToken },
287
- { headers: { Authorization: "no-auth" } }
292
+ { headers: { Authorization: "no-auth", ...getLocaleRequestHeaders() } }
288
293
  );
289
294
  const data = response.data;
290
295
  userStore.updateTokenAuth(
@@ -1,8 +1,10 @@
1
1
  <script setup lang="ts" name="403">
2
2
  import errorImage from "../../assets/images/403.png";
3
3
  import ErrorMessage from "../../components/ErrorMessage/index.vue";
4
+ import { useLocaleStore } from "../../locales";
5
+ const { t } = useLocaleStore();
4
6
  </script>
5
7
 
6
8
  <template>
7
- <ErrorMessage code="403" :image="errorImage" message="抱歉,您无权访问该页面~🙅‍♂️🙅‍♀️" />
9
+ <ErrorMessage code="403" :image="errorImage" :message="t('core.error.403.detail')" />
8
10
  </template>
@@ -1,8 +1,10 @@
1
1
  <script setup lang="ts" name="404">
2
2
  import errorImage from "../../assets/images/404.png";
3
3
  import ErrorMessage from "../../components/ErrorMessage/index.vue";
4
+ import { useLocaleStore } from "../../locales";
5
+ const { t } = useLocaleStore();
4
6
  </script>
5
7
 
6
8
  <template>
7
- <ErrorMessage code="404" :image="errorImage" message="抱歉,您访问的页面不存在~🤷‍♂️🤷‍♀️" />
9
+ <ErrorMessage code="404" :image="errorImage" :message="t('core.error.404.detail')" />
8
10
  </template>
@@ -1,8 +1,10 @@
1
1
  <script setup lang="ts" name="500">
2
2
  import errorImage from "../../assets/images/500.png";
3
3
  import ErrorMessage from "../../components/ErrorMessage/index.vue";
4
+ import { useLocaleStore } from "../../locales";
5
+ const { t } = useLocaleStore();
4
6
  </script>
5
7
 
6
8
  <template>
7
- <ErrorMessage code="500" :image="errorImage" message="抱歉,您的网络不见了~🤦‍♂️🤦‍♀️" />
9
+ <ErrorMessage code="500" :image="errorImage" :message="t('core.error.500.detail')" />
8
10
  </template>
@@ -1,8 +1,11 @@
1
- <script setup lang="ts" name="Pending"></script>
1
+ <script setup lang="ts" name="Pending">
2
+ import { useLocaleStore } from "../../locales";
3
+ const { t } = useLocaleStore();
4
+ </script>
2
5
 
3
6
  <template>
4
7
  <div class="route-pending page-card">
5
- <el-empty description="该页面暂不可用,请联系管理员检查菜单组件配置" />
8
+ <el-empty :description="t('core.error.pending.detail')" />
6
9
  </div>
7
10
  </template>
8
11
 
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <el-form ref="loginFormRef" :model="loginForm" :rules="loginRules" size="large">
3
3
  <el-form-item prop="tenant_code">
4
- <el-input v-model="loginForm.tenant_code" placeholder="请输入租户编码">
4
+ <el-input v-model="loginForm.tenant_code" :placeholder="t('core.login.tenant_code')">
5
5
  <template #prefix>
6
6
  <el-icon class="el-input__icon">
7
7
  <office-building />
@@ -10,7 +10,7 @@
10
10
  </el-input>
11
11
  </el-form-item>
12
12
  <el-form-item prop="user_name">
13
- <el-input v-model="loginForm.user_name" placeholder="请输入用户名">
13
+ <el-input v-model="loginForm.user_name" :placeholder="t('core.login.user_name')">
14
14
  <template #prefix>
15
15
  <el-icon class="el-input__icon">
16
16
  <user />
@@ -19,7 +19,13 @@
19
19
  </el-input>
20
20
  </el-form-item>
21
21
  <el-form-item prop="password">
22
- <el-input v-model="loginForm.password" type="password" placeholder="请输入密码" show-password autocomplete="new-password">
22
+ <el-input
23
+ v-model="loginForm.password"
24
+ type="password"
25
+ :placeholder="t('core.login.password')"
26
+ show-password
27
+ autocomplete="new-password"
28
+ >
23
29
  <template #prefix>
24
30
  <el-icon class="el-input__icon">
25
31
  <lock />
@@ -29,7 +35,11 @@
29
35
  </el-form-item>
30
36
  <el-form-item v-if="!isBehaviorCaptcha" prop="captcha_code">
31
37
  <div class="captcha-row">
32
- <el-input v-model="loginForm.captcha_code" placeholder="请输入验证码" @keyup.enter="handleLogin(loginFormRef)">
38
+ <el-input
39
+ v-model="loginForm.captcha_code"
40
+ :placeholder="t('core.login.captcha')"
41
+ @keyup.enter="handleLogin(loginFormRef)"
42
+ >
33
43
  <template #prefix>
34
44
  <el-icon class="el-input__icon">
35
45
  <Key />
@@ -41,7 +51,7 @@
41
51
  class="captcha-image"
42
52
  :style="{ width: captchaImageWidth }"
43
53
  :src="captcha_base64"
44
- alt="验证码"
54
+ :alt="t('core.login.captcha_alt')"
45
55
  @load="handleCaptchaImageLoad"
46
56
  @click="getCaptcha"
47
57
  />
@@ -49,28 +59,35 @@
49
59
  </el-form-item>
50
60
  </el-form>
51
61
  <div class="login-btn">
52
- <el-button :icon="CircleClose" round size="large" @click="resetForm(loginFormRef)"> 重置 </el-button>
53
- <el-button :icon="UserFilled" round size="large" type="primary" :loading="loading || oauthTicketLoading" @click="handleLogin(loginFormRef)">
54
- 登录
62
+ <el-button :icon="CircleClose" round size="large" @click="resetForm(loginFormRef)">{{ t("common.action.reset") }}</el-button>
63
+ <el-button
64
+ :icon="UserFilled"
65
+ round
66
+ size="large"
67
+ type="primary"
68
+ :loading="loading || oauthTicketLoading"
69
+ @click="handleLogin(loginFormRef)"
70
+ >
71
+ {{ t("common.action.login") }}
55
72
  </el-button>
56
73
  </div>
57
74
  <div v-if="oauthProviders.length" class="oauth-login">
58
75
  <div class="oauth-divider">
59
- <span>其他登录方式</span>
76
+ <span>{{ t("core.login.other_methods") }}</span>
60
77
  </div>
61
78
  <div class="oauth-provider-list">
62
79
  <el-tooltip
63
80
  v-for="provider in oauthProviders"
64
81
  :key="provider.provider"
65
- :content="provider.name"
82
+ :content="provider.nameKey.includes('.') ? t(provider.nameKey) : provider.nameKey"
66
83
  placement="top"
67
84
  :trigger="['hover', 'focus']"
68
85
  >
69
86
  <button
70
87
  class="oauth-provider-button"
71
88
  type="button"
72
- :aria-label="provider.name"
73
- :title="provider.name"
89
+ :aria-label="provider.nameKey.includes('.') ? t(provider.nameKey) : provider.nameKey"
90
+ :title="provider.nameKey.includes('.') ? t(provider.nameKey) : provider.nameKey"
74
91
  :disabled="oauthTicketLoading || oauthLoadingProvider === provider.provider"
75
92
  @click="handleOauthLogin(provider)"
76
93
  >
@@ -133,6 +150,7 @@ import { PASSWORD_CRYPTO_SCENE, encryptPassword } from "../../../utils/passwordC
133
150
  import { useConfigStore } from "../../../stores/modules/config";
134
151
  import { Click as GoCaptchaClick, Rotate as GoCaptchaRotate, Slide as GoCaptchaSlide } from "go-captcha-vue";
135
152
  import "go-captcha-vue/dist/style.css";
153
+ import { useLocaleStore } from "../../../locales";
136
154
 
137
155
  const router = useRouter();
138
156
  const route = useRoute();
@@ -141,6 +159,7 @@ const configStore = useConfigStore();
141
159
  const dictStore = useDictStore();
142
160
  const tabsStore = useTabsStore();
143
161
  const keepAliveStore = useKeepAliveStore();
162
+ const { t } = useLocaleStore();
144
163
 
145
164
  /** 登录表单实例类型。 */
146
165
  type FormInstance = InstanceType<typeof ElForm>;
@@ -150,12 +169,12 @@ const defaultCaptchaImageWidth = 96;
150
169
  const captchaImageWidth = ref(`${defaultCaptchaImageWidth}px`);
151
170
  const behaviorDialogVisible = ref(false);
152
171
  const behaviorLoading = ref(false);
153
- const loginRules = reactive({
154
- tenant_code: [{ required: true, message: "请输入租户编码", trigger: "blur" }],
155
- user_name: [{ required: true, message: "请输入用户名", trigger: "blur" }],
156
- password: [{ required: true, message: "请输入密码", trigger: "blur" }],
157
- captcha_code: [{ required: true, message: "请输入验证码", trigger: "blur" }]
158
- });
172
+ const loginRules = computed(() => ({
173
+ tenant_code: [{ required: true, message: t("core.login.tenant_code"), trigger: "blur" }],
174
+ user_name: [{ required: true, message: t("core.login.user_name"), trigger: "blur" }],
175
+ password: [{ required: true, message: t("core.login.password"), trigger: "blur" }],
176
+ captcha_code: [{ required: true, message: t("core.login.captcha"), trigger: "blur" }]
177
+ }));
159
178
 
160
179
  const loading = ref(false);
161
180
  const oauthLoadingProvider = ref("");
@@ -245,7 +264,9 @@ const behaviorCaptchaSource = reactive({
245
264
  thumbHeight: 60,
246
265
  thumbSize: 150
247
266
  });
248
- const behaviorCaptchaDisplayHeight = computed(() => Math.round((behaviorCaptchaSource.height * behaviorCaptchaDisplayWidth) / behaviorCaptchaSource.width));
267
+ const behaviorCaptchaDisplayHeight = computed(() =>
268
+ Math.round((behaviorCaptchaSource.height * behaviorCaptchaDisplayWidth) / behaviorCaptchaSource.width)
269
+ );
249
270
  const behaviorCaptchaScaleX = computed(() => behaviorCaptchaDisplayWidth / behaviorCaptchaSource.width);
250
271
  const behaviorCaptchaScaleY = computed(() => behaviorCaptchaDisplayHeight.value / behaviorCaptchaSource.height);
251
272
  const rotateCaptchaScale = computed(() => rotateCaptchaDisplaySize / behaviorCaptchaSource.width);
@@ -260,10 +281,12 @@ const toDisplayCaptchaY = (value: number) => Math.round(value * behaviorCaptchaS
260
281
  const toDisplayRotateSize = (value: number) => Math.round(value * rotateCaptchaScale.value);
261
282
 
262
283
  /** 将前端展示 X 坐标换算回服务端原始坐标。 */
263
- const toOriginalCaptchaX = (value: number) => Math.min(behaviorCaptchaSource.width, Math.max(0, Math.round(value / behaviorCaptchaScaleX.value)));
284
+ const toOriginalCaptchaX = (value: number) =>
285
+ Math.min(behaviorCaptchaSource.width, Math.max(0, Math.round(value / behaviorCaptchaScaleX.value)));
264
286
 
265
287
  /** 将前端展示 Y 坐标换算回服务端原始坐标。 */
266
- const toOriginalCaptchaY = (value: number) => Math.min(behaviorCaptchaSource.height, Math.max(0, Math.round(value / behaviorCaptchaScaleY.value)));
288
+ const toOriginalCaptchaY = (value: number) =>
289
+ Math.min(behaviorCaptchaSource.height, Math.max(0, Math.round(value / behaviorCaptchaScaleY.value)));
267
290
  const slideCaptchaConfig = computed(() => ({
268
291
  width: behaviorCaptchaDisplayWidth,
269
292
  height: behaviorCaptchaDisplayHeight.value,
@@ -273,7 +296,7 @@ const slideCaptchaConfig = computed(() => ({
273
296
  verticalPadding: 0,
274
297
  horizontalPadding: 0,
275
298
  iconSize: 20,
276
- title: "请拖动滑块完成拼图"
299
+ title: t("core.login.behavior_puzzle")
277
300
  }));
278
301
  const clickCaptchaConfig = computed(() => ({
279
302
  width: behaviorCaptchaDisplayWidth,
@@ -283,10 +306,10 @@ const clickCaptchaConfig = computed(() => ({
283
306
  showTheme: false,
284
307
  verticalPadding: 0,
285
308
  horizontalPadding: 0,
286
- buttonText: "确认",
309
+ buttonText: t("core.login.behavior_confirm"),
287
310
  iconSize: 20,
288
311
  dotSize: 20,
289
- title: "请按顺序点击文字"
312
+ title: t("core.login.behavior_click")
290
313
  }));
291
314
  const rotateCaptchaConfig = computed(() => ({
292
315
  width: behaviorCaptchaDisplayWidth,
@@ -296,7 +319,7 @@ const rotateCaptchaConfig = computed(() => ({
296
319
  verticalPadding: 0,
297
320
  horizontalPadding: 0,
298
321
  iconSize: 20,
299
- title: "拖动滑块,将内圈图片转正"
322
+ title: t("core.login.behavior_rotate")
300
323
  }));
301
324
  const slideCaptchaEvents = {
302
325
  refresh: () => getCaptcha(),
@@ -307,7 +330,10 @@ const clickCaptchaEvents = {
307
330
  refresh: () => getCaptcha(),
308
331
  close: () => closeBehaviorCaptcha(),
309
332
  confirm: (dots: ClickCaptchaPoint[], reset: () => void) =>
310
- verifyBehaviorCaptcha(JSON.stringify(dots.map(dot => ({ x: toOriginalCaptchaX(dot.x), y: toOriginalCaptchaY(dot.y) }))), reset)
333
+ verifyBehaviorCaptcha(
334
+ JSON.stringify(dots.map(dot => ({ x: toOriginalCaptchaX(dot.x), y: toOriginalCaptchaY(dot.y) }))),
335
+ reset
336
+ )
311
337
  };
312
338
  const rotateCaptchaEvents = {
313
339
  refresh: () => getCaptcha(),
@@ -402,7 +428,7 @@ const finishLogin = async () => {
402
428
  await navigateTo(router, getLoginRedirectPath());
403
429
  ElNotification({
404
430
  title: getTimeState(),
405
- message: "欢迎登录管理后台",
431
+ message: t("core.login.welcome"),
406
432
  type: "success",
407
433
  duration: 3000
408
434
  });
@@ -500,7 +526,7 @@ const verifyCaptchaToken = async (captchaCode: string) => {
500
526
  const submitLogin = async (captchaToken: string) => {
501
527
  loading.value = true;
502
528
  try {
503
- const password = await encryptPassword(loginForm.password, PASSWORD_CRYPTO_SCENE.LOGIN);
529
+ const password = await encryptPassword(loginForm.password, PASSWORD_CRYPTO_SCENE.PASSWORD_CRYPTO_SCENE_LOGIN);
504
530
  const loginRequest: LoginRequest = {
505
531
  tenant_code: loginForm.tenant_code,
506
532
  user_name: loginForm.user_name,
@@ -735,13 +761,16 @@ watch(
735
761
  height: auto;
736
762
  min-height: 24px;
737
763
  margin-bottom: 6px;
764
+ align-items: flex-start;
738
765
  }
739
766
 
740
767
  :global(.behavior-captcha-dialog .go-captcha .gc-header span),
741
768
  :global(.behavior-captcha-dialog .go-captcha .gc-header .gc-text) {
769
+ min-width: 0;
742
770
  font-size: 14px;
743
771
  line-height: 22px;
744
- white-space: nowrap;
772
+ white-space: normal;
773
+ overflow-wrap: anywhere;
745
774
  }
746
775
 
747
776
  :global(.behavior-captcha-dialog .go-captcha .gc-body) {
@@ -21,6 +21,12 @@
21
21
  top: 13px;
22
22
  right: 18px;
23
23
  }
24
+ .locale {
25
+ position: absolute;
26
+ z-index: 2;
27
+ top: 19px;
28
+ right: 72px;
29
+ }
24
30
  .login-left {
25
31
  width: 800px;
26
32
  margin-right: 10px;
@@ -2,6 +2,7 @@
2
2
  <div class="login-container flx-center">
3
3
  <div class="login-box">
4
4
  <SwitchDark class="dark" />
5
+ <LocaleSwitch class="locale" />
5
6
  <div class="login-left">
6
7
  <img class="login-left-img" :src="backgroundUrl" alt="login" />
7
8
  </div>
@@ -20,6 +21,7 @@
20
21
  import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue";
21
22
  import LoginForm from "./components/LoginForm.vue";
22
23
  import SwitchDark from "../../components/SwitchDark/index.vue";
24
+ import LocaleSwitch from "../../components/LocaleSwitch/index.vue";
23
25
  import { useConfigStore } from "../../stores/modules/config";
24
26
 
25
27
  const configStore = useConfigStore();