@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
@@ -12,20 +12,22 @@
12
12
  :on-error="uploadError"
13
13
  :accept="fileType.join(',')"
14
14
  >
15
- <el-button type="primary" :disabled="selfDisabled">上传文件</el-button>
15
+ <el-button type="primary" :disabled="selfDisabled">{{ t("core.upload.action") }}</el-button>
16
16
  </el-upload>
17
17
 
18
18
  <div v-if="fileInfo?.url" class="file-card">
19
19
  <div class="file-card__main">
20
20
  <el-icon class="file-card__icon"><Document /></el-icon>
21
21
  <div class="file-card__meta">
22
- <div class="file-card__name">{{ fileInfo.name || "未命名文件" }}</div>
22
+ <div class="file-card__name">{{ fileInfo.name || t("core.upload.unnamed_file") }}</div>
23
23
  <div class="file-card__url">{{ fileInfo.url }}</div>
24
24
  </div>
25
25
  </div>
26
26
  <div class="file-card__action">
27
- <el-button link type="primary" :icon="Download" @click.stop="handleDownload">下载</el-button>
28
- <el-button v-if="!selfDisabled" link type="danger" :icon="Delete" @click.stop="handleDelete">删除</el-button>
27
+ <el-button link type="primary" :icon="Download" @click.stop="handleDownload">{{ t("common.action.download") }}</el-button>
28
+ <el-button v-if="!selfDisabled" link type="danger" :icon="Delete" @click.stop="handleDelete">
29
+ {{ t("common.action.delete") }}
30
+ </el-button>
29
31
  </div>
30
32
  </div>
31
33
  </div>
@@ -38,6 +40,9 @@ import { ElNotification, formContextKey, formItemContextKey } from "element-plus
38
40
  import type { UploadProps, UploadRequestOptions, UploadUserFile } from "element-plus";
39
41
  import { defFileService } from "../../api/base/file";
40
42
  import type { FileInfo } from "../../rpc/base/v1/file";
43
+ import { useLocaleStore } from "../../locales";
44
+
45
+ const { t } = useLocaleStore();
41
46
 
42
47
  /** 单文件上传组件属性。 */
43
48
  interface UploadFileProps {
@@ -68,7 +73,7 @@ type UploadRequestError = Parameters<NonNullable<UploadRequestOptions["onError"]
68
73
 
69
74
  /** 兼容 Element Plus 上传组件要求的错误对象结构。 */
70
75
  function buildUploadError(error: unknown): UploadRequestError {
71
- const uploadError = error instanceof Error ? error : new Error("文件上传失败");
76
+ const uploadError = error instanceof Error ? error : new Error(t("core.upload.file_failed"));
72
77
  return Object.assign(uploadError, {
73
78
  status: 500,
74
79
  method: "POST",
@@ -88,16 +93,16 @@ const beforeUpload: UploadProps["beforeUpload"] = rawFile => {
88
93
 
89
94
  if (!fileTypeValid) {
90
95
  ElNotification({
91
- title: "温馨提示",
92
- message: "上传文件不符合所需的格式!",
96
+ title: t("common.title.warning"),
97
+ message: t("core.upload.file_format_invalid"),
93
98
  type: "warning"
94
99
  });
95
100
  }
96
101
 
97
102
  if (!fileSizeValid) {
98
103
  ElNotification({
99
- title: "温馨提示",
100
- message: `上传文件大小不能超过 ${props.fileSize}M!`,
104
+ title: t("common.title.warning"),
105
+ message: t("core.upload.file_size_exceeded", { size: props.fileSize }),
101
106
  type: "warning"
102
107
  });
103
108
  }
@@ -125,8 +130,8 @@ const uploadSuccess = (response: FileInfo | undefined) => {
125
130
  });
126
131
  formItemContext?.prop && formContext?.validateField([formItemContext.prop as string]);
127
132
  ElNotification({
128
- title: "温馨提示",
129
- message: "文件上传成功!",
133
+ title: t("common.title.notice"),
134
+ message: t("core.upload.file_success"),
130
135
  type: "success"
131
136
  });
132
137
  };
@@ -134,8 +139,8 @@ const uploadSuccess = (response: FileInfo | undefined) => {
134
139
  /** 处理文件上传失败提示。 */
135
140
  const uploadError = () => {
136
141
  ElNotification({
137
- title: "温馨提示",
138
- message: "文件上传失败,请您重新上传!",
142
+ title: t("common.title.warning"),
143
+ message: t("core.upload.file_failed"),
139
144
  type: "error"
140
145
  });
141
146
  };
@@ -14,7 +14,7 @@
14
14
  :on-exceed="handleExceed"
15
15
  :accept="fileType.join(',')"
16
16
  >
17
- <el-button type="primary" :disabled="selfDisabled">上传文件</el-button>
17
+ <el-button type="primary" :disabled="selfDisabled">{{ t("core.upload.action") }}</el-button>
18
18
  </el-upload>
19
19
 
20
20
  <div v-if="_fileList.length" class="file-list">
@@ -22,13 +22,17 @@
22
22
  <div class="file-card__main">
23
23
  <el-icon class="file-card__icon"><Document /></el-icon>
24
24
  <div class="file-card__meta">
25
- <div class="file-card__name">{{ file.name || "未命名文件" }}</div>
25
+ <div class="file-card__name">{{ file.name || t("core.upload.unnamed_file") }}</div>
26
26
  <div class="file-card__url">{{ file.url }}</div>
27
27
  </div>
28
28
  </div>
29
29
  <div class="file-card__action">
30
- <el-button link type="primary" :icon="Download" @click.stop="handleDownload(file)">下载</el-button>
31
- <el-button v-if="!selfDisabled" link type="danger" :icon="Delete" @click.stop="handleRemove(file)">删除</el-button>
30
+ <el-button link type="primary" :icon="Download" @click.stop="handleDownload(file)">
31
+ {{ t("common.action.download") }}
32
+ </el-button>
33
+ <el-button v-if="!selfDisabled" link type="danger" :icon="Delete" @click.stop="handleRemove(file)">
34
+ {{ t("common.action.delete") }}
35
+ </el-button>
32
36
  </div>
33
37
  </div>
34
38
  </div>
@@ -42,6 +46,9 @@ import { ElNotification, formContextKey, formItemContextKey } from "element-plus
42
46
  import type { UploadProps, UploadRequestOptions, UploadUserFile } from "element-plus";
43
47
  import { defFileService } from "../../api/base/file";
44
48
  import type { FileInfo } from "../../rpc/base/v1/file";
49
+ import { useLocaleStore } from "../../locales";
50
+
51
+ const { t } = useLocaleStore();
45
52
 
46
53
  /** 多文件上传组件属性。 */
47
54
  interface UploadFilesProps {
@@ -75,7 +82,7 @@ type UploadRequestError = Parameters<NonNullable<UploadRequestOptions["onError"]
75
82
 
76
83
  /** 兼容 Element Plus 上传组件要求的错误对象结构。 */
77
84
  function buildUploadError(error: unknown): UploadRequestError {
78
- const uploadError = error instanceof Error ? error : new Error("文件上传失败");
85
+ const uploadError = error instanceof Error ? error : new Error(t("core.upload.file_failed"));
79
86
  return Object.assign(uploadError, {
80
87
  status: 500,
81
88
  method: "POST",
@@ -102,16 +109,16 @@ const beforeUpload: UploadProps["beforeUpload"] = rawFile => {
102
109
 
103
110
  if (!fileTypeValid) {
104
111
  ElNotification({
105
- title: "温馨提示",
106
- message: "上传文件不符合所需的格式!",
112
+ title: t("common.title.warning"),
113
+ message: t("core.upload.file_format_invalid"),
107
114
  type: "warning"
108
115
  });
109
116
  }
110
117
 
111
118
  if (!fileSizeValid) {
112
119
  ElNotification({
113
- title: "温馨提示",
114
- message: `上传文件大小不能超过 ${props.fileSize}M!`,
120
+ title: t("common.title.warning"),
121
+ message: t("core.upload.file_size_exceeded", { size: props.fileSize }),
115
122
  type: "warning"
116
123
  });
117
124
  }
@@ -143,8 +150,8 @@ const uploadSuccess = (response: FileInfo | undefined) => {
143
150
  emit("update:fileList", _fileList.value);
144
151
  formItemContext?.prop && formContext?.validateField([formItemContext.prop as string]);
145
152
  ElNotification({
146
- title: "温馨提示",
147
- message: "文件上传成功!",
153
+ title: t("common.title.notice"),
154
+ message: t("core.upload.file_success"),
148
155
  type: "success"
149
156
  });
150
157
  };
@@ -152,8 +159,8 @@ const uploadSuccess = (response: FileInfo | undefined) => {
152
159
  /** 处理文件上传失败提示。 */
153
160
  const uploadError = () => {
154
161
  ElNotification({
155
- title: "温馨提示",
156
- message: "文件上传失败,请您重新上传!",
162
+ title: t("common.title.warning"),
163
+ message: t("core.upload.file_failed"),
157
164
  type: "error"
158
165
  });
159
166
  };
@@ -161,8 +168,8 @@ const uploadError = () => {
161
168
  /** 超出上传数量限制时给出提示。 */
162
169
  function handleExceed() {
163
170
  ElNotification({
164
- title: "温馨提示",
165
- message: `当前最多只能上传 ${props.limit} 个文件,请移除后上传!`,
171
+ title: t("common.title.warning"),
172
+ message: t("core.upload.limit_exceeded", { limit: props.limit }),
166
173
  type: "warning"
167
174
  });
168
175
  }
@@ -19,15 +19,15 @@
19
19
  <div class="upload-handle" @click.stop>
20
20
  <div v-if="!self_disabled" class="handle-icon" @click="editImg">
21
21
  <el-icon><Edit /></el-icon>
22
- <span>编辑</span>
22
+ <span>{{ t("common.action.edit") }}</span>
23
23
  </div>
24
24
  <div class="handle-icon" @click="imgViewVisible = true">
25
25
  <el-icon><ZoomIn /></el-icon>
26
- <span>查看</span>
26
+ <span>{{ t("common.action.view") }}</span>
27
27
  </div>
28
28
  <div v-if="!self_disabled" class="handle-icon" @click="deleteImg">
29
29
  <el-icon><Delete /></el-icon>
30
- <span>删除</span>
30
+ <span>{{ t("common.action.delete") }}</span>
31
31
  </div>
32
32
  </div>
33
33
  </template>
@@ -54,6 +54,9 @@ import { defFileService } from "../../api/base/file";
54
54
  import { ElNotification, formContextKey, formItemContextKey } from "element-plus";
55
55
  import type { UploadProps, UploadRequestOptions } from "element-plus";
56
56
  import type { FileInfo } from "../../rpc/base/v1/file";
57
+ import { useLocaleStore } from "../../locales";
58
+
59
+ const { t } = useLocaleStore();
57
60
 
58
61
  /** 单图上传组件属性。 */
59
62
  interface UploadFileProps {
@@ -141,15 +144,15 @@ const beforeUpload: UploadProps["beforeUpload"] = rawFile => {
141
144
  const imgType = props.fileType.includes(rawFile.type as File.ImageMimeType);
142
145
  if (!imgType)
143
146
  ElNotification({
144
- title: "温馨提示",
145
- message: "上传图片不符合所需的格式!",
147
+ title: t("common.title.warning"),
148
+ message: t("core.upload.image_format_invalid"),
146
149
  type: "warning"
147
150
  });
148
151
  if (!imgSize)
149
152
  setTimeout(() => {
150
153
  ElNotification({
151
- title: "温馨提示",
152
- message: `上传图片大小不能超过 ${props.fileSize}M!`,
154
+ title: t("common.title.warning"),
155
+ message: t("core.upload.image_size_exceeded", { size: props.fileSize }),
153
156
  type: "warning"
154
157
  });
155
158
  }, 0);
@@ -161,8 +164,8 @@ const beforeUpload: UploadProps["beforeUpload"] = rawFile => {
161
164
  * */
162
165
  const uploadSuccess = () => {
163
166
  ElNotification({
164
- title: "温馨提示",
165
- message: "图片上传成功!",
167
+ title: t("common.title.notice"),
168
+ message: t("core.upload.image_success"),
166
169
  type: "success"
167
170
  });
168
171
  };
@@ -172,8 +175,8 @@ const uploadSuccess = () => {
172
175
  * */
173
176
  const uploadError = () => {
174
177
  ElNotification({
175
- title: "温馨提示",
176
- message: "图片上传失败,请您重新上传!",
178
+ title: t("common.title.warning"),
179
+ message: t("core.upload.image_failed"),
177
180
  type: "error"
178
181
  });
179
182
  };
@@ -27,11 +27,11 @@
27
27
  <div class="upload-handle" @click.stop>
28
28
  <div class="handle-icon" @click="handlePictureCardPreview(file)">
29
29
  <el-icon><ZoomIn /></el-icon>
30
- <span>查看</span>
30
+ <span>{{ t("common.action.view") }}</span>
31
31
  </div>
32
32
  <div v-if="!self_disabled" class="handle-icon" @click="handleRemove(file)">
33
33
  <el-icon><Delete /></el-icon>
34
- <span>删除</span>
34
+ <span>{{ t("common.action.delete") }}</span>
35
35
  </div>
36
36
  </div>
37
37
  </template>
@@ -50,6 +50,9 @@ import { defFileService } from "../../api/base/file";
50
50
  import type { UploadProps, UploadFile, UploadUserFile, UploadRequestOptions } from "element-plus";
51
51
  import { ElNotification, formContextKey, formItemContextKey } from "element-plus";
52
52
  import type { FileInfo } from "../../rpc/base/v1/file";
53
+ import { useLocaleStore } from "../../locales";
54
+
55
+ const { t } = useLocaleStore();
53
56
 
54
57
  /** 多图上传组件属性。 */
55
58
  interface UploadFileProps {
@@ -107,15 +110,15 @@ const beforeUpload: UploadProps["beforeUpload"] = rawFile => {
107
110
  const imgType = props.fileType.includes(rawFile.type as File.ImageMimeType);
108
111
  if (!imgType)
109
112
  ElNotification({
110
- title: "温馨提示",
111
- message: "上传图片不符合所需的格式!",
113
+ title: t("common.title.warning"),
114
+ message: t("core.upload.image_format_invalid"),
112
115
  type: "warning"
113
116
  });
114
117
  if (!imgSize)
115
118
  setTimeout(() => {
116
119
  ElNotification({
117
- title: "温馨提示",
118
- message: `上传图片大小不能超过 ${props.fileSize}M!`,
120
+ title: t("common.title.warning"),
121
+ message: t("core.upload.image_size_exceeded", { size: props.fileSize }),
119
122
  type: "warning"
120
123
  });
121
124
  }, 0);
@@ -153,8 +156,8 @@ const uploadSuccess = (response: FileInfo | undefined, uploadFile: UploadFile) =
153
156
  // 调用 el-form 内部的校验方法(可自动校验)
154
157
  formItemContext?.prop && formContext?.validateField([formItemContext.prop as string]);
155
158
  ElNotification({
156
- title: "温馨提示",
157
- message: "图片上传成功!",
159
+ title: t("common.title.notice"),
160
+ message: t("core.upload.image_success"),
158
161
  type: "success"
159
162
  });
160
163
  };
@@ -173,8 +176,8 @@ const handleRemove = (file: UploadFile) => {
173
176
  * */
174
177
  const uploadError = () => {
175
178
  ElNotification({
176
- title: "温馨提示",
177
- message: "图片上传失败,请您重新上传!",
179
+ title: t("common.title.warning"),
180
+ message: t("core.upload.image_failed"),
178
181
  type: "error"
179
182
  });
180
183
  };
@@ -184,8 +187,8 @@ const uploadError = () => {
184
187
  * */
185
188
  const handleExceed = () => {
186
189
  ElNotification({
187
- title: "温馨提示",
188
- message: `当前最多只能上传 ${props.limit} 张图片,请移除后上传!`,
190
+ title: t("common.title.warning"),
191
+ message: t("core.upload.image_limit_exceeded", { limit: props.limit }),
189
192
  type: "warning"
190
193
  });
191
194
  };
@@ -6,7 +6,7 @@
6
6
  class="editor-content"
7
7
  :style="{ height }"
8
8
  :mode="mode"
9
- :default-config="editorConfig"
9
+ :default-config="resolvedEditorConfig"
10
10
  @on-created="handleCreated"
11
11
  @on-blur="handleBlur"
12
12
  />
@@ -20,6 +20,9 @@ import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
20
20
  import { defFileService } from "../../api/base/file";
21
21
  import "@wangeditor/editor/dist/css/style.css";
22
22
  import { formContextKey, formItemContextKey } from "element-plus";
23
+ import { useLocaleStore } from "../../locales";
24
+
25
+ const { t } = useLocaleStore();
23
26
 
24
27
  // 富文本 DOM 元素
25
28
  const editorRef = shallowRef();
@@ -47,7 +50,6 @@ const props = withDefaults(defineProps<RichEditorProps>(), {
47
50
  },
48
51
  editorConfig: () => {
49
52
  return {
50
- placeholder: "请输入内容...",
51
53
  MENU_CONF: {}
52
54
  };
53
55
  },
@@ -56,6 +58,12 @@ const props = withDefaults(defineProps<RichEditorProps>(), {
56
58
  hideToolBar: false,
57
59
  disabled: false
58
60
  });
61
+ const editorMenuConfig = props.editorConfig.MENU_CONF ?? {};
62
+ const resolvedEditorConfig = computed(() => ({
63
+ ...props.editorConfig,
64
+ placeholder: props.editorConfig.placeholder || t("common.placeholder.input_content"),
65
+ MENU_CONF: editorMenuConfig
66
+ }));
59
67
 
60
68
  // 获取 el-form 组件上下文
61
69
  const formContext = inject(formContextKey, void 0);
@@ -91,7 +99,7 @@ const valueHtml = computed({
91
99
  * @param insertFn 上传成功后的回调函数(插入到富文本编辑器中)
92
100
  * */
93
101
  type InsertFnTypeImg = (url: string, alt?: string, href?: string) => void;
94
- props.editorConfig.MENU_CONF!["uploadImage"] = {
102
+ editorMenuConfig["uploadImage"] = {
95
103
  async customUpload(file: File, insertFn: InsertFnTypeImg) {
96
104
  if (!uploadImgValidate(file)) return;
97
105
  try {
@@ -115,7 +123,7 @@ const uploadImgValidate = (file: File): boolean => {
115
123
  * @param insertFn 上传成功后的回调函数(插入到富文本编辑器中)
116
124
  * */
117
125
  type InsertFnTypeVideo = (url: string, poster?: string) => void;
118
- props.editorConfig.MENU_CONF!["uploadVideo"] = {
126
+ editorMenuConfig["uploadVideo"] = {
119
127
  async customUpload(file: File, insertFn: InsertFnTypeVideo) {
120
128
  if (!uploadVideoValidate(file)) return;
121
129
  try {
@@ -6,6 +6,7 @@
6
6
 
7
7
  import type { Directive, DirectiveBinding } from "vue";
8
8
  import { ElMessage } from "element-plus";
9
+ import { t } from "../../locales";
9
10
  /** 复制指令绑定后挂载复制内容的元素类型。 */
10
11
  interface ElType extends HTMLElement {
11
12
  copyData: string | number;
@@ -29,7 +30,7 @@ async function handleClick(this: any) {
29
30
  await navigator.clipboard.writeText(this.copyData);
30
31
  ElMessage({
31
32
  type: "success",
32
- message: "复制成功"
33
+ message: t("core.clipboard.success")
33
34
  });
34
35
  } catch (err) {
35
36
  console.error("复制操作不被支持或失败: ", err);
@@ -1,4 +1,5 @@
1
1
  import { ElNotification } from "element-plus";
2
+ import { t } from "../locales";
2
3
 
3
4
  /**
4
5
  * @description 接收数据流生成 blob,创建链接,下载文件
@@ -17,8 +18,8 @@ export const useDownload = async (
17
18
  ) => {
18
19
  if (isNotify) {
19
20
  ElNotification({
20
- title: "温馨提示",
21
- message: "如果数据庞大会导致下载缓慢哦,请您耐心等待!",
21
+ title: t("common.title.notice"),
22
+ message: t("core.download.large_file_notice"),
22
23
  type: "info",
23
24
  duration: 3000
24
25
  });
@@ -1,5 +1,6 @@
1
1
  import { ElMessageBox, ElMessage } from "element-plus";
2
2
  import { HandleData } from "./interface";
3
+ import { t } from "../locales";
3
4
 
4
5
  /**
5
6
  * @description 操作单条数据信息 (二次确认【删除、禁用、启用、重置密码】)
@@ -16,9 +17,9 @@ export const useHandleData = (
16
17
  confirmType: HandleData.MessageType = "warning"
17
18
  ) => {
18
19
  return new Promise((resolve, reject) => {
19
- ElMessageBox.confirm(`是否${message}?`, "温馨提示", {
20
- confirmButtonText: "确定",
21
- cancelButtonText: "取消",
20
+ ElMessageBox.confirm(t("core.data.confirm_action", { action: message }), t("common.title.warning"), {
21
+ confirmButtonText: t("common.action.confirm"),
22
+ cancelButtonText: t("common.action.cancel"),
22
23
  type: confirmType,
23
24
  draggable: true
24
25
  })
@@ -27,7 +28,7 @@ export const useHandleData = (
27
28
  if (!res) return reject(false);
28
29
  ElMessage({
29
30
  type: "success",
30
- message: `${message}成功!`
31
+ message: t("core.data.operation_success", { action: message })
31
32
  });
32
33
  resolve(true);
33
34
  })
@@ -7,6 +7,7 @@ import { getLightColor, getDarkColor } from "../utils/color";
7
7
  import { menuTheme } from "../styles/theme/menu";
8
8
  import { asideTheme } from "../styles/theme/aside";
9
9
  import { headerTheme } from "../styles/theme/header";
10
+ import { t } from "../locales";
10
11
 
11
12
  /**
12
13
  * @description 全局主题 hooks
@@ -29,7 +30,7 @@ export const useTheme = () => {
29
30
  const changePrimary = (val: string | null) => {
30
31
  if (!val) {
31
32
  val = DEFAULT_PRIMARY;
32
- ElMessage({ type: "success", message: `主题颜色已重置为 ${DEFAULT_PRIMARY}` });
33
+ ElMessage({ type: "success", message: t("core.theme.primary_reset", { color: DEFAULT_PRIMARY }) });
33
34
  }
34
35
  // 计算主题颜色变化
35
36
  document.documentElement.style.setProperty("--el-color-primary", val);
@@ -1,4 +1,5 @@
1
1
  import { ref } from "vue";
2
+ import { getCurrentLocale } from "../locales";
2
3
 
3
4
  /**
4
5
  * @description 获取本地时间
@@ -18,7 +19,7 @@ export const useTime = () => {
18
19
  const date = new Date();
19
20
  year.value = date.getFullYear();
20
21
  month.value = date.getMonth() + 1;
21
- week.value = "日一二三四五六".charAt(date.getDay());
22
+ week.value = new Intl.DateTimeFormat(getCurrentLocale(), { weekday: "short" }).format(date);
22
23
  day.value = date.getDate();
23
24
  hour.value =
24
25
  (date.getHours() + "")?.padStart(2, "0") ||
@@ -29,7 +30,15 @@ export const useTime = () => {
29
30
  second.value =
30
31
  (date.getSeconds() + "")?.padStart(2, "0") ||
31
32
  new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getSeconds());
32
- nowTime.value = `${year.value}年${month.value}月${day.value} ${hour.value}:${minute.value}:${second.value}`;
33
+ nowTime.value = new Intl.DateTimeFormat(getCurrentLocale(), {
34
+ year: "numeric",
35
+ month: "2-digit",
36
+ day: "2-digit",
37
+ hour: "2-digit",
38
+ minute: "2-digit",
39
+ second: "2-digit",
40
+ hour12: false
41
+ }).format(date);
33
42
  };
34
43
 
35
44
  updateTime();
@@ -5,5 +5,7 @@
5
5
  /// <reference path="../types/generated/components.d.ts" />
6
6
 
7
7
  export * from "./modules";
8
+ export * from "./locales";
9
+ export { defLanguageService } from "./api/base/language";
8
10
  export { kratosAdminModule } from "./modules/kratosAdmin";
9
11
  export { bootstrapAdminApp } from "./bootstrap";
@@ -2,6 +2,7 @@
2
2
  <div class="tool-bar-ri">
3
3
  <div class="header-icon">
4
4
  <component :is="tool.component" v-for="tool in headerTools" :id="tool.name" :key="tool.name" />
5
+ <LocaleSwitch id="localeSwitch" />
5
6
  <AssemblySize id="assemblySize" />
6
7
  <SearchMenu id="searchMenu" />
7
8
  <ThemeSetting id="themeSetting" />
@@ -18,6 +19,7 @@ import SearchMenu from "./components/SearchMenu.vue";
18
19
  import ThemeSetting from "./components/ThemeSetting.vue";
19
20
  import Fullscreen from "./components/Fullscreen.vue";
20
21
  import Avatar from "./components/Avatar.vue";
22
+ import LocaleSwitch from "../../../components/LocaleSwitch/index.vue";
21
23
 
22
24
  const headerTools = getAdminHeaderTools();
23
25
  </script>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <el-tooltip effect="dark" content="组件大小" placement="bottom" :show-after="200">
2
+ <el-tooltip effect="dark" :content="t('core.header.size_select')" placement="bottom" :show-after="200">
3
3
  <el-dropdown trigger="click" @command="setAssemblySize">
4
4
  <i :class="'iconfont icon-contentright'" class="toolBar-icon"></i>
5
5
  <template #dropdown>
@@ -22,15 +22,17 @@
22
22
  import { computed } from "vue";
23
23
  import { useGlobalStore } from "../../../../stores/modules/global";
24
24
  import { AssemblySizeType } from "../../../../stores/interface";
25
+ import { useLocaleStore } from "../../../../locales";
25
26
 
26
27
  const globalStore = useGlobalStore();
27
28
  const assemblySize = computed(() => globalStore.assemblySize);
29
+ const { t } = useLocaleStore();
28
30
 
29
- const assemblySizeList = [
30
- { label: "默认", value: "default" },
31
- { label: "大型", value: "large" },
32
- { label: "小型", value: "small" }
33
- ];
31
+ const assemblySizeList = computed(() => [
32
+ { label: t("core.header.size.default"), value: "default" },
33
+ { label: t("core.header.size.large"), value: "large" },
34
+ { label: t("core.header.size.small"), value: "small" }
35
+ ]);
34
36
 
35
37
  const setAssemblySize = (item: AssemblySizeType) => {
36
38
  if (assemblySize.value === item) return;
@@ -23,11 +23,11 @@
23
23
  @click.stop="handleUserMenuAction(action.path)"
24
24
  >
25
25
  <el-icon v-if="action.icon"><component :is="action.icon" /></el-icon>
26
- <span>{{ action.label }}</span>
26
+ <span>{{ action.labelKey ? t(action.labelKey) : action.label }}</span>
27
27
  </button>
28
28
  <button class="action-btn action-btn--danger" type="button" @click="logout">
29
29
  <el-icon><SwitchButton /></el-icon>
30
- <span>退出登录</span>
30
+ <span>{{ t("common.action.logout") }}</span>
31
31
  </button>
32
32
  </div>
33
33
  </div>
@@ -46,16 +46,18 @@ import type { DropdownInstance } from "element-plus";
46
46
  import defaultAvatar from "../../../../assets/images/avatar.png";
47
47
  import { navigateTo } from "../../../../utils/router";
48
48
  import { getAdminUserMenuActions } from "../../../../modules";
49
+ import { useLocaleStore } from "../../../../locales";
49
50
 
50
51
  const router = useRouter();
51
52
  const userStore = useUserStore();
52
53
  const authStore = useAuthStore();
53
54
  const userMenuActions = getAdminUserMenuActions();
54
55
  const dropdownRef = ref<DropdownInstance>();
56
+ const { t } = useLocaleStore();
55
57
  const avatarSrc = ref(defaultAvatar);
56
- const displayName = computed(() => userStore.userInfo.nick_name || userStore.userInfo.user_name || "未设置");
57
- const roleName = computed(() => userStore.userInfo.role_name || "未分配角色");
58
- const deptName = computed(() => userStore.userInfo.dept_name || "未分配部门");
58
+ const displayName = computed(() => userStore.userInfo.nick_name || userStore.userInfo.user_name || t("core.layout.not_set"));
59
+ const roleName = computed(() => userStore.userInfo.role_name || t("core.layout.role_unassigned"));
60
+ const deptName = computed(() => userStore.userInfo.dept_name || t("core.layout.department_unassigned"));
59
61
  const profileSummary = computed(() => `${roleName.value} / ${deptName.value}`);
60
62
  const availableUserMenuActions = computed(() => {
61
63
  return userMenuActions.flatMap(action => {
@@ -84,9 +86,9 @@ watch(
84
86
 
85
87
  /** 退出登录并清理当前登录态。 */
86
88
  const logout = () => {
87
- ElMessageBox.confirm("您是否确认退出登录?", "温馨提示", {
88
- confirmButtonText: "确定",
89
- cancelButtonText: "取消",
89
+ ElMessageBox.confirm(t("core.layout.logout_confirm"), t("common.title.warning"), {
90
+ confirmButtonText: t("common.action.confirm"),
91
+ cancelButtonText: t("common.action.cancel"),
90
92
  type: "warning"
91
93
  }).then(async () => {
92
94
  // 1.执行退出登录接口并清理本地状态
@@ -94,7 +96,7 @@ const logout = () => {
94
96
 
95
97
  // 2.重定向到登录页
96
98
  router.replace(LOGIN_URL);
97
- ElMessage.success("退出登录成功!");
99
+ ElMessage.success(t("core.layout.logout_success"));
98
100
  });
99
101
  };
100
102
 
@@ -27,20 +27,28 @@ import { ArrowRight } from "@element-plus/icons-vue";
27
27
  import { useAuthStore } from "../../../../stores/modules/auth";
28
28
  import { useGlobalStore } from "../../../../stores/modules/global";
29
29
  import type { RouteItem } from "../../../../rpc/system/admin/v1/auth";
30
- import { BaseMenuType } from "../../../../rpc/system/common/v1/enum";
30
+ import { BaseMenuType } from "../../../../rpc/system/admin/v1/common";
31
31
  import { getRouteMetaIcon, getRouteMetaTitle, getRouteTarget, isExternalPath } from "../../../../utils";
32
+ import { useLocaleStore } from "../../../../locales";
32
33
 
33
34
  const route = useRoute();
34
35
  const router = useRouter();
35
36
  const authStore = useAuthStore();
36
37
  const globalStore = useGlobalStore();
38
+ const { t } = useLocaleStore();
37
39
 
38
40
  const breadcrumbList = computed(() => {
39
41
  let breadcrumbData = authStore.breadcrumbListGet[route.matched[route.matched.length - 1].path] ?? [];
40
42
  // 🙅‍♀️不需要首页面包屑可删除以下判断
41
- if (breadcrumbData[0]?.path !== HOME_URL && getRouteMetaTitle(breadcrumbData[0]?.meta) !== "首页") {
43
+ if (breadcrumbData[0]?.path !== HOME_URL) {
42
44
  breadcrumbData = [
43
- { path: HOME_URL, name: "home", type: BaseMenuType.MENU, meta: { icon: "HomeFilled", title: "首页", params: [] }, children: [] },
45
+ {
46
+ path: HOME_URL,
47
+ name: "home",
48
+ type: BaseMenuType.BASE_MENU_TYPE_MENU,
49
+ meta: { icon: "HomeFilled", title: t("core.layout.home"), params: [] },
50
+ children: []
51
+ },
44
52
  ...breadcrumbData
45
53
  ];
46
54
  }