@liujitcn/kratos-admin-core 0.0.19 → 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.
- package/README.md +2 -2
- package/dist/package/declarations/src/api/base/language.d.ts +8 -0
- package/dist/package/declarations/src/components/Dialog/ProDialog.vue.d.ts +1 -0
- package/dist/package/declarations/src/components/LocaleSwitch/index.vue.d.ts +3 -0
- package/dist/package/declarations/src/index.d.ts +2 -0
- package/dist/package/declarations/src/locales/index.d.ts +63 -0
- package/dist/package/declarations/src/modules/index.d.ts +7 -2
- package/dist/package/declarations/src/request.d.ts +1 -0
- package/dist/package/declarations/src/routers/modules/staticRouter.d.ts +1 -0
- package/dist/package/declarations/src/rpc/base/v1/config.d.ts +2 -0
- package/dist/package/declarations/src/rpc/base/v1/language.d.ts +26 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/base_dict.d.ts +79 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/base_translation.d.ts +102 -0
- package/dist/package/declarations/src/stores/interface/index.d.ts +2 -0
- package/dist/package/declarations/src/stores/runtime.d.ts +1 -0
- package/dist/package/declarations/src/utils/dict.d.ts +2 -2
- package/dist/package/declarations/src/utils/index.d.ts +1 -1
- package/dist/package/declarations/src/utils/oauthProvider.d.ts +2 -0
- package/dist/package/declarations/src/utils/passwordStrength.d.ts +0 -7
- package/dist/package/src/App.vue +10 -1
- package/dist/package/src/api/base/language.ts +20 -0
- package/dist/package/src/api/base/oauth.ts +0 -1
- package/dist/package/src/api/system/auth.ts +0 -1
- package/dist/package/src/bootstrap.ts +57 -2
- package/dist/package/src/components/CronExpression/index.vue +80 -82
- package/dist/package/src/components/Dialog/FormDialog.vue +9 -4
- package/dist/package/src/components/Dialog/ProDialog.vue +10 -4
- package/dist/package/src/components/Dict/index.vue +5 -2
- package/dist/package/src/components/ECharts/config/index.ts +6 -1
- package/dist/package/src/components/ErrorMessage/index.vue +3 -1
- package/dist/package/src/components/ImportExcel/index.vue +29 -18
- package/dist/package/src/components/LocaleSwitch/index.vue +28 -0
- package/dist/package/src/components/PasswordStrength/index.vue +8 -5
- package/dist/package/src/components/ProForm/components/DynamicList.vue +8 -3
- package/dist/package/src/components/ProForm/components/KvList.vue +14 -6
- package/dist/package/src/components/ProTable/components/ColSetting.vue +7 -5
- package/dist/package/src/components/ProTable/components/TableColumn.vue +2 -1
- package/dist/package/src/components/ProTable/index.vue +36 -11
- package/dist/package/src/components/SearchForm/components/SearchFormItem.vue +9 -4
- package/dist/package/src/components/SearchForm/index.vue +6 -3
- package/dist/package/src/components/SelectFilter/index.vue +4 -1
- package/dist/package/src/components/SelectIcon/index.vue +8 -4
- package/dist/package/src/components/TreeFilter/index.vue +14 -6
- package/dist/package/src/components/Upload/File.vue +18 -13
- package/dist/package/src/components/Upload/Files.vue +22 -15
- package/dist/package/src/components/Upload/Img.vue +14 -11
- package/dist/package/src/components/Upload/Imgs.vue +15 -12
- package/dist/package/src/components/WangEditor/index.vue +12 -4
- package/dist/package/src/directives/modules/copy.ts +2 -1
- package/dist/package/src/hooks/useDownload.ts +3 -2
- package/dist/package/src/hooks/useHandleData.ts +5 -4
- package/dist/package/src/hooks/useTheme.ts +2 -1
- package/dist/package/src/hooks/useTime.ts +11 -2
- package/dist/package/src/index.ts +2 -0
- package/dist/package/src/layouts/components/Header/ToolBarRight.vue +2 -0
- package/dist/package/src/layouts/components/Header/components/AssemblySize.vue +8 -6
- package/dist/package/src/layouts/components/Header/components/Avatar.vue +11 -9
- package/dist/package/src/layouts/components/Header/components/Breadcrumb.vue +10 -2
- package/dist/package/src/layouts/components/Header/components/Fullscreen.vue +4 -2
- package/dist/package/src/layouts/components/Header/components/InfoDialog.vue +6 -3
- package/dist/package/src/layouts/components/Header/components/PasswordDialog.vue +6 -3
- package/dist/package/src/layouts/components/Header/components/SearchMenu.vue +5 -3
- package/dist/package/src/layouts/components/Header/components/ThemeSetting.vue +3 -1
- package/dist/package/src/layouts/components/Tabs/components/MoreButton.vue +9 -7
- package/dist/package/src/layouts/components/ThemeDrawer/index.vue +27 -24
- package/dist/package/src/locales/README.md +15 -0
- package/dist/package/src/locales/en-US.json +258 -0
- package/dist/package/src/locales/es-ES.json +258 -0
- package/dist/package/src/locales/fr-FR.json +258 -0
- package/dist/package/src/locales/index.ts +223 -0
- package/dist/package/src/locales/ja-JP.json +258 -0
- package/dist/package/src/locales/ko-KR.json +258 -0
- package/dist/package/src/locales/zh-CN.json +258 -0
- package/dist/package/src/locales/zh-TW.json +258 -0
- package/dist/package/src/modules/index.ts +7 -2
- package/dist/package/src/modules/kratosAdmin.ts +19 -1
- package/dist/package/src/request.ts +8 -1
- package/dist/package/src/routers/index.ts +4 -1
- package/dist/package/src/routers/modules/dynamicRouter.ts +3 -2
- package/dist/package/src/routers/modules/staticRouter.ts +8 -4
- package/dist/package/src/rpc/base/v1/config.ts +2 -0
- package/dist/package/src/rpc/base/v1/language.ts +37 -0
- package/dist/package/src/rpc/system/admin/v1/base_dict.ts +81 -0
- package/dist/package/src/rpc/system/admin/v1/base_translation.ts +117 -0
- package/dist/package/src/stores/interface/index.ts +2 -0
- package/dist/package/src/stores/modules/config.ts +5 -1
- package/dist/package/src/stores/modules/user.ts +2 -1
- package/dist/package/src/stores/runtime.ts +1 -0
- package/dist/package/src/utils/color.ts +5 -4
- package/dist/package/src/utils/dict.ts +27 -4
- package/dist/package/src/utils/eleValidate.ts +3 -2
- package/dist/package/src/utils/errorHandler.ts +9 -8
- package/dist/package/src/utils/index.ts +7 -6
- package/dist/package/src/utils/oauthProvider.ts +15 -10
- package/dist/package/src/utils/passwordCrypto.ts +3 -2
- package/dist/package/src/utils/passwordStrength.ts +1 -16
- package/dist/package/src/utils/request.ts +13 -8
- package/dist/package/src/views/error/403.vue +3 -1
- package/dist/package/src/views/error/404.vue +3 -1
- package/dist/package/src/views/error/500.vue +3 -1
- package/dist/package/src/views/error/pending.vue +5 -2
- package/dist/package/src/views/login/components/LoginForm.vue +53 -27
- package/dist/package/src/views/login/index.scss +6 -0
- package/dist/package/src/views/login/index.vue +2 -0
- package/dist/package/types/generated/auto-imports.d.ts +129 -111
- package/dist/package/types/generated/components.d.ts +67 -67
- package/package.json +3 -1
- package/types/generated/auto-imports.d.ts +129 -111
- package/types/generated/components.d.ts +67 -67
- 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
|
+
}
|
|
@@ -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("
|
|
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
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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
|
-
{
|
|
8
|
-
|
|
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
|
-
{
|
|
16
|
-
|
|
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: "
|
|
11
|
-
ReferenceError: "
|
|
12
|
-
TypeError: "
|
|
13
|
-
RangeError: "
|
|
14
|
-
SyntaxError: "
|
|
15
|
-
EvalError: "
|
|
16
|
-
URIError: "
|
|
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
|
-
|
|
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: {
|
|
26
|
-
wechat: {
|
|
27
|
-
wechatmp: {
|
|
28
|
-
gitee: {
|
|
29
|
-
google: {
|
|
30
|
-
dingtalk: {
|
|
31
|
-
feishu: {
|
|
32
|
-
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
|
-
|
|
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("
|
|
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"
|
|
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
|
|