@liujitcn/kratos-admin-core 0.0.20 → 0.0.21

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 (110) 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/LocaleSwitch/index.vue.d.ts +3 -0
  5. package/dist/package/declarations/src/index.d.ts +2 -0
  6. package/dist/package/declarations/src/locales/index.d.ts +63 -0
  7. package/dist/package/declarations/src/modules/index.d.ts +7 -2
  8. package/dist/package/declarations/src/request.d.ts +1 -0
  9. package/dist/package/declarations/src/routers/modules/staticRouter.d.ts +1 -0
  10. package/dist/package/declarations/src/rpc/base/v1/config.d.ts +2 -0
  11. package/dist/package/declarations/src/rpc/base/v1/language.d.ts +26 -0
  12. package/dist/package/declarations/src/rpc/system/admin/v1/base_dict.d.ts +79 -0
  13. package/dist/package/declarations/src/rpc/system/admin/v1/base_translation.d.ts +102 -0
  14. package/dist/package/declarations/src/stores/interface/index.d.ts +2 -0
  15. package/dist/package/declarations/src/stores/runtime.d.ts +1 -0
  16. package/dist/package/declarations/src/utils/dict.d.ts +2 -2
  17. package/dist/package/declarations/src/utils/index.d.ts +1 -1
  18. package/dist/package/declarations/src/utils/oauthProvider.d.ts +2 -0
  19. package/dist/package/declarations/src/utils/passwordStrength.d.ts +0 -7
  20. package/dist/package/src/App.vue +10 -1
  21. package/dist/package/src/api/base/language.ts +20 -0
  22. package/dist/package/src/api/base/oauth.ts +0 -1
  23. package/dist/package/src/api/system/auth.ts +0 -1
  24. package/dist/package/src/bootstrap.ts +57 -2
  25. package/dist/package/src/components/CronExpression/index.vue +80 -82
  26. package/dist/package/src/components/Dialog/FormDialog.vue +9 -4
  27. package/dist/package/src/components/Dialog/ProDialog.vue +10 -4
  28. package/dist/package/src/components/Dict/index.vue +5 -2
  29. package/dist/package/src/components/ECharts/config/index.ts +6 -1
  30. package/dist/package/src/components/ErrorMessage/index.vue +3 -1
  31. package/dist/package/src/components/ImportExcel/index.vue +29 -18
  32. package/dist/package/src/components/LocaleSwitch/index.vue +28 -0
  33. package/dist/package/src/components/PasswordStrength/index.vue +8 -5
  34. package/dist/package/src/components/ProForm/components/DynamicList.vue +8 -3
  35. package/dist/package/src/components/ProForm/components/KvList.vue +14 -6
  36. package/dist/package/src/components/ProTable/components/ColSetting.vue +7 -5
  37. package/dist/package/src/components/ProTable/components/TableColumn.vue +2 -1
  38. package/dist/package/src/components/ProTable/index.vue +36 -11
  39. package/dist/package/src/components/SearchForm/components/SearchFormItem.vue +9 -4
  40. package/dist/package/src/components/SearchForm/index.vue +6 -3
  41. package/dist/package/src/components/SelectFilter/index.vue +4 -1
  42. package/dist/package/src/components/SelectIcon/index.vue +8 -4
  43. package/dist/package/src/components/TreeFilter/index.vue +14 -6
  44. package/dist/package/src/components/Upload/File.vue +18 -13
  45. package/dist/package/src/components/Upload/Files.vue +22 -15
  46. package/dist/package/src/components/Upload/Img.vue +14 -11
  47. package/dist/package/src/components/Upload/Imgs.vue +15 -12
  48. package/dist/package/src/components/WangEditor/index.vue +12 -4
  49. package/dist/package/src/directives/modules/copy.ts +2 -1
  50. package/dist/package/src/hooks/useDownload.ts +3 -2
  51. package/dist/package/src/hooks/useHandleData.ts +5 -4
  52. package/dist/package/src/hooks/useTheme.ts +2 -1
  53. package/dist/package/src/hooks/useTime.ts +11 -2
  54. package/dist/package/src/index.ts +2 -0
  55. package/dist/package/src/layouts/components/Header/ToolBarRight.vue +2 -0
  56. package/dist/package/src/layouts/components/Header/components/AssemblySize.vue +8 -6
  57. package/dist/package/src/layouts/components/Header/components/Avatar.vue +11 -9
  58. package/dist/package/src/layouts/components/Header/components/Breadcrumb.vue +10 -2
  59. package/dist/package/src/layouts/components/Header/components/Fullscreen.vue +4 -2
  60. package/dist/package/src/layouts/components/Header/components/InfoDialog.vue +6 -3
  61. package/dist/package/src/layouts/components/Header/components/PasswordDialog.vue +6 -3
  62. package/dist/package/src/layouts/components/Header/components/SearchMenu.vue +5 -3
  63. package/dist/package/src/layouts/components/Header/components/ThemeSetting.vue +3 -1
  64. package/dist/package/src/layouts/components/Tabs/components/MoreButton.vue +9 -7
  65. package/dist/package/src/layouts/components/ThemeDrawer/index.vue +27 -24
  66. package/dist/package/src/locales/README.md +15 -0
  67. package/dist/package/src/locales/en-US.json +258 -0
  68. package/dist/package/src/locales/es-ES.json +258 -0
  69. package/dist/package/src/locales/fr-FR.json +258 -0
  70. package/dist/package/src/locales/index.ts +223 -0
  71. package/dist/package/src/locales/ja-JP.json +258 -0
  72. package/dist/package/src/locales/ko-KR.json +258 -0
  73. package/dist/package/src/locales/zh-CN.json +258 -0
  74. package/dist/package/src/locales/zh-TW.json +258 -0
  75. package/dist/package/src/modules/index.ts +7 -2
  76. package/dist/package/src/modules/kratosAdmin.ts +19 -1
  77. package/dist/package/src/request.ts +8 -1
  78. package/dist/package/src/routers/index.ts +4 -1
  79. package/dist/package/src/routers/modules/dynamicRouter.ts +3 -2
  80. package/dist/package/src/routers/modules/staticRouter.ts +8 -4
  81. package/dist/package/src/rpc/base/v1/config.ts +2 -0
  82. package/dist/package/src/rpc/base/v1/language.ts +37 -0
  83. package/dist/package/src/rpc/system/admin/v1/base_dict.ts +81 -0
  84. package/dist/package/src/rpc/system/admin/v1/base_translation.ts +117 -0
  85. package/dist/package/src/stores/interface/index.ts +2 -0
  86. package/dist/package/src/stores/modules/config.ts +5 -1
  87. package/dist/package/src/stores/modules/user.ts +2 -1
  88. package/dist/package/src/stores/runtime.ts +1 -0
  89. package/dist/package/src/utils/color.ts +5 -4
  90. package/dist/package/src/utils/dict.ts +27 -4
  91. package/dist/package/src/utils/eleValidate.ts +3 -2
  92. package/dist/package/src/utils/errorHandler.ts +9 -8
  93. package/dist/package/src/utils/index.ts +7 -6
  94. package/dist/package/src/utils/oauthProvider.ts +15 -10
  95. package/dist/package/src/utils/passwordCrypto.ts +3 -2
  96. package/dist/package/src/utils/passwordStrength.ts +1 -16
  97. package/dist/package/src/utils/request.ts +13 -8
  98. package/dist/package/src/views/error/403.vue +3 -1
  99. package/dist/package/src/views/error/404.vue +3 -1
  100. package/dist/package/src/views/error/500.vue +3 -1
  101. package/dist/package/src/views/error/pending.vue +5 -2
  102. package/dist/package/src/views/login/components/LoginForm.vue +53 -27
  103. package/dist/package/src/views/login/index.scss +6 -0
  104. package/dist/package/src/views/login/index.vue +2 -0
  105. package/dist/package/types/generated/auto-imports.d.ts +129 -111
  106. package/dist/package/types/generated/components.d.ts +67 -67
  107. package/package.json +3 -1
  108. package/types/generated/auto-imports.d.ts +129 -111
  109. package/types/generated/components.d.ts +67 -67
  110. package/vite.config.ts +8 -0
@@ -0,0 +1,117 @@
1
+ // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
2
+ // versions:
3
+ // protoc-gen-ts_proto v2.11.8
4
+ // protoc unknown
5
+ // source: system/admin/v1/base_translation.proto
6
+
7
+ /* eslint-disable */
8
+
9
+ /** 翻译状态 */
10
+ export enum TranslationStatus {
11
+ TRANSLATION_STATUS_UNSPECIFIED = 0,
12
+ TRANSLATION_STATUS_PENDING = 1,
13
+ TRANSLATION_STATUS_MACHINE = 2,
14
+ TRANSLATION_STATUS_REVIEWED = 3,
15
+ }
16
+
17
+ /** 翻译资源类型 */
18
+ export enum TranslationResourceType {
19
+ TRANSLATION_RESOURCE_TYPE_UNSPECIFIED = 0,
20
+ TRANSLATION_RESOURCE_TYPE_MENU = 1,
21
+ TRANSLATION_RESOURCE_TYPE_DICT = 2,
22
+ TRANSLATION_RESOURCE_TYPE_DICT_ITEM = 3,
23
+ TRANSLATION_RESOURCE_TYPE_CONFIG = 4,
24
+ }
25
+
26
+ /** 系统配置翻译字段 */
27
+ export enum BaseConfigTranslationField {
28
+ BASE_CONFIG_TRANSLATION_FIELD_UNSPECIFIED = 0,
29
+ BASE_CONFIG_TRANSLATION_FIELD_NAME = 1,
30
+ BASE_CONFIG_TRANSLATION_FIELD_VALUE = 2,
31
+ }
32
+
33
+ /** 生成翻译草稿请求 */
34
+ export interface GenerateTranslationDraftRequest {
35
+ /** 资源类型 */
36
+ resource_type: TranslationResourceType;
37
+ /** 已保存资源ID */
38
+ resource_id: number;
39
+ /** 目标语言代码 */
40
+ target_locale: string;
41
+ /** 系统配置翻译字段,配置资源必填 */
42
+ field: BaseConfigTranslationField;
43
+ }
44
+
45
+ /** 生成翻译草稿响应 */
46
+ export interface GenerateTranslationDraftResponse {
47
+ /** 资源类型 */
48
+ resource_type: TranslationResourceType;
49
+ /** 资源ID */
50
+ resource_id: number;
51
+ /** 语言区域 */
52
+ locale: string;
53
+ /** 机器翻译草稿 */
54
+ translation: string;
55
+ /** 翻译状态 */
56
+ translation_status: TranslationStatus;
57
+ /** 中文源文SHA-256 */
58
+ source_hash: string;
59
+ /** 机器翻译提供方 */
60
+ translation_provider: string;
61
+ /** 最近机器翻译时间 */
62
+ translated_at: string;
63
+ /** 系统配置翻译字段 */
64
+ field: BaseConfigTranslationField;
65
+ }
66
+
67
+ /** 系统配置单语言翻译 */
68
+ export interface BaseConfigTranslation {
69
+ /** 翻译记录ID */
70
+ id: number;
71
+ /** 系统配置ID */
72
+ config_id: number;
73
+ /** 语言区域 */
74
+ locale: string;
75
+ /** 翻译字段:name或value */
76
+ field: BaseConfigTranslationField;
77
+ /** 翻译文本 */
78
+ text: string;
79
+ /** 翻译审核状态 */
80
+ translation_status: TranslationStatus;
81
+ /** 中文源文SHA-256 */
82
+ source_hash: string;
83
+ /** 中文源文是否已变化 */
84
+ source_changed: boolean;
85
+ /** 机器翻译提供方 */
86
+ translation_provider: string;
87
+ /** 最近机器翻译时间 */
88
+ translated_at: string;
89
+ /** 审核人ID */
90
+ reviewed_by: number;
91
+ /** 审核时间 */
92
+ reviewed_at: string;
93
+ /** 创建人ID */
94
+ created_by: number;
95
+ /** 更新人ID */
96
+ updated_by: number;
97
+ /** 创建时间 */
98
+ created_at: string;
99
+ /** 更新时间 */
100
+ updated_at: string;
101
+ /** 删除时间 */
102
+ deleted_at: number;
103
+ }
104
+
105
+ /** 代码生成单语言国际化配置 */
106
+ export interface CodeGenLocaleConfig {
107
+ /** 业务或字段描述 */
108
+ comment: string;
109
+ /** 左树描述 */
110
+ left_tree_comment: string;
111
+ }
112
+
113
+ /** Admin翻译草稿服务 */
114
+ export interface BaseTranslationService {
115
+ /** 为单个已保存资源生成机器翻译草稿 */
116
+ GenerateTranslationDraft(request: GenerateTranslationDraftRequest): Promise<GenerateTranslationDraftResponse>;
117
+ }
@@ -99,4 +99,6 @@ export interface SiteConfigState {
99
99
  display: SiteDisplayConfig;
100
100
  /** 当前登录验证码配置 */
101
101
  captcha: LoginCaptchaConfig;
102
+ /** 是否允许显式生成机器翻译草稿。 */
103
+ translationDraftEnabled: boolean;
102
104
  }
@@ -6,6 +6,7 @@ import defaultLogoUrl from "../../assets/images/logo.svg";
6
6
  import defaultBackgroundUrl from "../../assets/images/login_left.png";
7
7
 
8
8
  const CAPTCHA_TYPE_KEY = "captchaType";
9
+ const TRANSLATION_DRAFT_ENABLED_KEY = "i18n.translation_draft.enabled";
9
10
 
10
11
  const DEFAULT_SITE_DISPLAY_CONFIG: SiteDisplayConfig = {
11
12
  sysName: "Admin",
@@ -76,7 +77,8 @@ function buildConfigMap(configs: Array<{ key?: string; value?: string }>) {
76
77
  export const useConfigStore = defineStore("admin-config", {
77
78
  state: (): SiteConfigState => ({
78
79
  display: { ...DEFAULT_SITE_DISPLAY_CONFIG },
79
- captcha: { ...DEFAULT_LOGIN_CAPTCHA_CONFIG }
80
+ captcha: { ...DEFAULT_LOGIN_CAPTCHA_CONFIG },
81
+ translationDraftEnabled: false
80
82
  }),
81
83
  getters: {},
82
84
  actions: {
@@ -101,6 +103,7 @@ export const useConfigStore = defineStore("admin-config", {
101
103
  resetDisplayConfig() {
102
104
  this.display = { ...DEFAULT_SITE_DISPLAY_CONFIG };
103
105
  this.captcha = { ...DEFAULT_LOGIN_CAPTCHA_CONFIG };
106
+ this.translationDraftEnabled = false;
104
107
  },
105
108
  /**
106
109
  * 加载管理端站点配置,并以服务端返回值覆盖本地默认值。
@@ -113,6 +116,7 @@ export const useConfigStore = defineStore("admin-config", {
113
116
 
114
117
  this.setDisplayConfig(normalizeSiteDisplayConfig(configMap));
115
118
  this.setLoginCaptchaConfig(normalizeLoginCaptchaConfig(configMap));
119
+ this.translationDraftEnabled = configMap[TRANSLATION_DRAFT_ENABLED_KEY] === "true";
116
120
  return this.display;
117
121
  }
118
122
  }
@@ -6,6 +6,7 @@ import type { UserInfoForm } from "../../rpc/system/admin/v1/auth";
6
6
  import { UserState } from "../../stores/interface";
7
7
  import piniaPersistConfig from "../../stores/helper/persist";
8
8
  import { useDictStoreHook } from "../../stores/modules/dict";
9
+ import { t } from "../../locales";
9
10
 
10
11
  const defaultUserInfo: UserInfoForm = {
11
12
  user_name: "",
@@ -71,7 +72,7 @@ export const useUserStore = defineStore("admin-user", {
71
72
  /** 刷新认证令牌 */
72
73
  async refreshAccessToken() {
73
74
  if (!this.refreshToken) {
74
- return Promise.reject(new Error("refresh token 不存在"));
75
+ return Promise.reject(new Error(t("core.auth.refreshTokenMissing")));
75
76
  }
76
77
 
77
78
  const data = await defLoginService.RefreshToken({ refresh_token: this.refreshToken });
@@ -1,4 +1,5 @@
1
1
  export { useAuthStore } from "./modules/auth";
2
+ export { useConfigStore } from "./modules/config";
2
3
  export { useGlobalStore } from "./modules/global";
3
4
  export { useTabsStore } from "./modules/tabs";
4
5
  export { useUserStore } from "./modules/user";
@@ -1,4 +1,5 @@
1
1
  import { ElMessage } from "element-plus";
2
+ import { t } from "../locales";
2
3
 
3
4
  /**
4
5
  * @description hex颜色转rgb颜色
@@ -8,7 +9,7 @@ import { ElMessage } from "element-plus";
8
9
  export function hexToRgb(str: any) {
9
10
  let hexs: any = "";
10
11
  let reg = /^#?[0-9A-Fa-f]{6}$/;
11
- if (!reg.test(str)) return ElMessage.warning("输入错误的hex");
12
+ if (!reg.test(str)) return ElMessage.warning(t("core.theme.colorHexInvalid"));
12
13
  str = str.replace("#", "");
13
14
  hexs = str.match(/../g);
14
15
  for (let i = 0; i < 3; i++) hexs[i] = parseInt(hexs[i], 16);
@@ -24,7 +25,7 @@ export function hexToRgb(str: any) {
24
25
  */
25
26
  export function rgbToHex(r: any, g: any, b: any) {
26
27
  let reg = /^\d{1,3}$/;
27
- if (!reg.test(r) || !reg.test(g) || !reg.test(b)) return ElMessage.warning("输入错误的rgb颜色值");
28
+ if (!reg.test(r) || !reg.test(g) || !reg.test(b)) return ElMessage.warning(t("core.theme.colorRgbInvalid"));
28
29
  let hexs = [r.toString(16), g.toString(16), b.toString(16)];
29
30
  for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`;
30
31
  return `#${hexs.join("")}`;
@@ -38,7 +39,7 @@ export function rgbToHex(r: any, g: any, b: any) {
38
39
  */
39
40
  export function getDarkColor(color: string, level: number) {
40
41
  let reg = /^#?[0-9A-Fa-f]{6}$/;
41
- if (!reg.test(color)) return ElMessage.warning("输入错误的hex颜色值");
42
+ if (!reg.test(color)) return ElMessage.warning(t("core.theme.colorHexInvalid"));
42
43
  let rgb = hexToRgb(color);
43
44
  for (let i = 0; i < 3; i++) rgb[i] = Math.round(20.5 * level + rgb[i] * (1 - level));
44
45
  return rgbToHex(rgb[0], rgb[1], rgb[2]);
@@ -52,7 +53,7 @@ export function getDarkColor(color: string, level: number) {
52
53
  */
53
54
  export function getLightColor(color: string, level: number) {
54
55
  let reg = /^#?[0-9A-Fa-f]{6}$/;
55
- if (!reg.test(color)) return ElMessage.warning("输入错误的hex颜色值");
56
+ if (!reg.test(color)) return ElMessage.warning(t("core.theme.colorHexInvalid"));
56
57
  let rgb = hexToRgb(color);
57
58
  for (let i = 0; i < 3; i++) rgb[i] = Math.round(255 * level + rgb[i] * (1 - level));
58
59
  return rgbToHex(rgb[0], rgb[1], rgb[2]);
@@ -1,17 +1,40 @@
1
1
  // ? 系统全局字典
2
+ import { t } from "../locales";
2
3
 
3
4
  /**
4
5
  * @description:用户性别
5
6
  */
6
7
  export const genderType = [
7
- { label: "男", value: 1 },
8
- { label: "女", value: 2 }
8
+ {
9
+ get label() {
10
+ return t("common.value.male");
11
+ },
12
+ value: 1
13
+ },
14
+ {
15
+ get label() {
16
+ return t("common.value.female");
17
+ },
18
+ value: 2
19
+ }
9
20
  ];
10
21
 
11
22
  /**
12
23
  * @description:用户状态
13
24
  */
14
25
  export const userStatus = [
15
- { label: "启用", value: 1, tagType: "success" },
16
- { label: "禁用", value: 0, tagType: "danger" }
26
+ {
27
+ get label() {
28
+ return t("common.status.enabled");
29
+ },
30
+ value: 1,
31
+ tagType: "success"
32
+ },
33
+ {
34
+ get label() {
35
+ return t("common.status.disabled");
36
+ },
37
+ value: 0,
38
+ tagType: "danger"
39
+ }
17
40
  ];
@@ -1,13 +1,14 @@
1
1
  // ? Element 常用表单校验规则
2
+ import { t } from "../locales";
2
3
 
3
4
  /**
4
5
  * @rule 手机号
5
6
  */
6
7
  export function checkPhoneNumber(rule: any, value: any, callback: any) {
7
8
  const regexp = /^(((13[0-9]{1})|(15[0-9]{1})|(16[0-9]{1})|(17[3-8]{1})|(18[0-9]{1})|(19[0-9]{1})|(14[5-7]{1}))+\d{8})$/;
8
- if (value === "") callback("请输入手机号码");
9
+ if (value === "") callback(t("common.validation.phoneRequired"));
9
10
  if (!regexp.test(value)) {
10
- callback(new Error("请输入正确的手机号码"));
11
+ callback(new Error(t("common.validation.phoneInvalid")));
11
12
  } else {
12
13
  return callback();
13
14
  }
@@ -1,4 +1,5 @@
1
1
  import { ElNotification } from "element-plus";
2
+ import { t } from "../locales";
2
3
 
3
4
  /**
4
5
  * @description 全局代码错误捕捉
@@ -7,15 +8,15 @@ const errorHandler = (error: any) => {
7
8
  // 过滤 HTTP 请求错误
8
9
  if (error.status || error.status == 0) return false;
9
10
  let errorMap: { [key: string]: string } = {
10
- InternalError: "Javascript引擎内部错误",
11
- ReferenceError: "未找到对象",
12
- TypeError: "使用了错误的类型或对象",
13
- RangeError: "使用内置对象时,参数超范围",
14
- SyntaxError: "语法错误",
15
- EvalError: "错误的使用了Eval",
16
- URIError: "URI错误"
11
+ InternalError: t("core.error.javascript.internal"),
12
+ ReferenceError: t("core.error.javascript.reference"),
13
+ TypeError: t("core.error.javascript.type"),
14
+ RangeError: t("core.error.javascript.range"),
15
+ SyntaxError: t("core.error.javascript.syntax"),
16
+ EvalError: t("core.error.javascript.eval"),
17
+ URIError: t("core.error.javascript.uri")
17
18
  };
18
- let errorName = errorMap[error.name] || "未知错误";
19
+ let errorName = errorMap[error.name] || t("core.error.javascript.unknown");
19
20
  ElNotification({
20
21
  title: errorName,
21
22
  message: error,
@@ -1,6 +1,7 @@
1
1
  import { isArray } from "../utils/is";
2
2
  import type { RouteItem, RouteMeta } from "../rpc/system/admin/v1/auth";
3
3
  import { BaseMenuType } from "../rpc/system/common/v1/enum";
4
+ import { t } from "../locales";
4
5
 
5
6
  const mode = import.meta.env.VITE_ROUTER_MODE;
6
7
 
@@ -22,7 +23,7 @@ export function getRouteMetaIcon(meta?: RouteMeta) {
22
23
  * 获取菜单标题。
23
24
  */
24
25
  export function getRouteMetaTitle(meta?: RouteMeta) {
25
- return meta?.title ?? "未命名菜单";
26
+ return meta?.title ?? t("core.layout.unnamedMenu");
26
27
  }
27
28
 
28
29
  /**
@@ -169,11 +170,11 @@ export function randomNum(min: number, max: number): number {
169
170
  export function getTimeState() {
170
171
  let timeNow = new Date();
171
172
  let hours = timeNow.getHours();
172
- if (hours >= 6 && hours <= 10) return `早上好 ⛅`;
173
- if (hours >= 10 && hours <= 14) return `中午好 🌞`;
174
- if (hours >= 14 && hours <= 18) return `下午好 🌞`;
175
- if (hours >= 18 && hours <= 24) return `晚上好 🌛`;
176
- if (hours >= 0 && hours <= 6) return `凌晨好 🌛`;
173
+ if (hours >= 6 && hours <= 10) return t("core.login.greeting.morning");
174
+ if (hours >= 10 && hours <= 14) return t("core.login.greeting.noon");
175
+ if (hours >= 14 && hours <= 18) return t("core.login.greeting.afternoon");
176
+ if (hours >= 18 && hours <= 24) return t("core.login.greeting.evening");
177
+ return t("core.login.greeting.night");
177
178
  }
178
179
 
179
180
  /**
@@ -6,11 +6,14 @@ import githubIconUrl from "../assets/images/oauth/github.png";
6
6
  import googleIconUrl from "../assets/images/oauth/google.png";
7
7
  import wechatIconUrl from "../assets/images/oauth/wechat.png";
8
8
  import wechatworkIconUrl from "../assets/images/oauth/wechatwork.png";
9
+ import { t } from "../locales";
9
10
 
10
11
  /** 三方登录展示信息。 */
11
12
  export interface OauthProviderDisplay {
12
13
  /** 登录方式名称。 */
13
14
  name: string;
15
+ /** 登录方式名称对应的稳定语言键。 */
16
+ nameKey: string;
14
17
  /** 登录方式图标标识。 */
15
18
  icon: string;
16
19
  }
@@ -21,15 +24,15 @@ export interface OauthProviderSource {
21
24
  provider: string;
22
25
  }
23
26
 
24
- const oauthProviderDisplayMap: Record<string, OauthProviderDisplay> = {
25
- github: { name: "GitHub", icon: "github" },
26
- wechat: { name: "微信", icon: "wechat" },
27
- wechatmp: { name: "微信公众号", icon: "wechat" },
28
- gitee: { name: "Gitee", icon: "gitee" },
29
- google: { name: "Google", icon: "google" },
30
- dingtalk: { name: "钉钉", icon: "dingtalk" },
31
- feishu: { name: "飞书", icon: "feishu" },
32
- wechatwork: { name: "企业微信", icon: "wechatwork" }
27
+ const oauthProviderDisplayMap: Record<string, Omit<OauthProviderDisplay, "name">> = {
28
+ github: { nameKey: "GitHub", icon: "github" },
29
+ wechat: { nameKey: "core.oauth.wechat", icon: "wechat" },
30
+ wechatmp: { nameKey: "core.oauth.wechatmp", icon: "wechat" },
31
+ gitee: { nameKey: "Gitee", icon: "gitee" },
32
+ google: { nameKey: "Google", icon: "google" },
33
+ dingtalk: { nameKey: "core.oauth.dingtalk", icon: "dingtalk" },
34
+ feishu: { nameKey: "core.oauth.feishu", icon: "feishu" },
35
+ wechatwork: { nameKey: "core.oauth.wechatwork", icon: "wechatwork" }
33
36
  };
34
37
 
35
38
  /** 创建本地官方图标图片组件。 */
@@ -56,7 +59,9 @@ const oauthIconMap: Record<string, Component> = {
56
59
 
57
60
  /** 根据 provider 标识获取前端展示信息。 */
58
61
  export function getOauthProviderDisplay(provider: string): OauthProviderDisplay {
59
- return oauthProviderDisplayMap[provider] || { name: provider || "未知方式", icon: provider };
62
+ const display = oauthProviderDisplayMap[provider] ?? { nameKey: "core.oauth.unknown", icon: provider };
63
+ const name = display.nameKey.includes(".") ? t(display.nameKey) : display.nameKey;
64
+ return { ...display, name: provider ? name : t("core.oauth.unknown") };
60
65
  }
61
66
 
62
67
  /** 创建未知 provider 的文字兜底图标。 */
@@ -1,6 +1,7 @@
1
1
  import { defLoginService } from "../api/base/login";
2
2
  import { PasswordCryptoScene } from "../rpc/base/v1/enum";
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.cryptoUnsupported"));
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.sessionExpired"), t("common.title.notice"), {
164
+ confirmButtonText: t("core.auth.loginAgain"),
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.systemError"));
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.systemError"));
239
244
  }
240
245
  }
241
246
  } else if (!shouldSkipErrorMessage(requestConfig)) {
242
- ElMessage.error(error.message || "系统出错");
247
+ ElMessage.error(error.message || t("common.message.systemError"));
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.refreshTokenMissing")));
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