@liujitcn/kratos-admin-core 0.0.28 → 0.0.31

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 (84) hide show
  1. package/README.md +7 -6
  2. package/build/auto-imports.json +73 -0
  3. package/build/plugins.ts +2 -58
  4. package/dist/package/declarations/src/api/base/mfa.d.ts +26 -0
  5. package/dist/package/declarations/src/components/Dialog/FormDialog.vue.d.ts +3 -3
  6. package/dist/package/declarations/src/components/Dialog/PasswordVerifyDialog.vue.d.ts +65 -0
  7. package/dist/package/declarations/src/components/Dialog/ProDialog.vue.d.ts +1 -1
  8. package/dist/package/declarations/src/components/Grid/components/GridItem.vue.d.ts +1 -1
  9. package/dist/package/declarations/src/components/Mfa/MfaRecoveryCodesDialog.vue.d.ts +19 -0
  10. package/dist/package/declarations/src/components/Mfa/MfaSetupPanel.vue.d.ts +8 -0
  11. package/dist/package/declarations/src/components/ProForm/components/ProFormItem.vue.d.ts +4 -4
  12. package/dist/package/declarations/src/components/ProForm/index.vue.d.ts +1 -1
  13. package/dist/package/declarations/src/components/ProForm/interface/index.d.ts +4 -1
  14. package/dist/package/declarations/src/components/RichTextPreview/index.vue.d.ts +8 -0
  15. package/dist/package/declarations/src/components/Upload/Img.vue.d.ts +1 -1
  16. package/dist/package/declarations/src/components/Upload/Imgs.vue.d.ts +1 -1
  17. package/dist/package/declarations/src/layouts/components/LockScreen.vue.d.ts +3 -0
  18. package/dist/package/declarations/src/locales/generated.d.ts +168 -0
  19. package/dist/package/declarations/src/locales/index.d.ts +2 -0
  20. package/dist/package/declarations/src/rpc/base/v1/login.d.ts +33 -3
  21. package/dist/package/declarations/src/rpc/base/v1/mfa.d.ts +152 -0
  22. package/dist/package/declarations/src/rpc/base/v1/oauth.d.ts +25 -0
  23. package/dist/package/declarations/src/rpc/base/v1/oauth_client.d.ts +23 -0
  24. package/dist/package/declarations/src/rpc/system/admin/v1/base_dict.d.ts +4 -4
  25. package/dist/package/declarations/src/rpc/system/admin/v1/base_i18n.d.ts +27 -23
  26. package/dist/package/declarations/src/security.d.ts +2 -0
  27. package/dist/package/declarations/src/stores/interface/index.d.ts +14 -1
  28. package/dist/package/declarations/src/stores/modules/lockScreen.d.ts +20 -0
  29. package/dist/package/declarations/src/stores/modules/user.d.ts +5 -2
  30. package/dist/package/declarations/src/stores/runtime.d.ts +1 -0
  31. package/dist/package/declarations/src/utils/clipboard.d.ts +2 -0
  32. package/dist/package/declarations/src/utils/webauthn.d.ts +4 -0
  33. package/dist/package/declarations/vite.config.d.ts +2 -0
  34. package/dist/package/src/api/base/mfa.ts +114 -0
  35. package/dist/package/src/components/Dialog/PasswordVerifyDialog.vue +176 -0
  36. package/dist/package/src/components/Loading/fullScreen.ts +2 -1
  37. package/dist/package/src/components/MarkdownPreview/index.vue +21 -1
  38. package/dist/package/src/components/Mfa/MfaRecoveryCodesDialog.vue +91 -0
  39. package/dist/package/src/components/Mfa/MfaSetupPanel.vue +81 -0
  40. package/dist/package/src/components/ProForm/components/ProFormItem.vue +16 -1
  41. package/dist/package/src/components/ProForm/interface/index.ts +4 -0
  42. package/dist/package/src/components/ProTable/components/ColSetting.vue +1 -1
  43. package/dist/package/src/components/ProTable/index.vue +6 -2
  44. package/dist/package/src/components/RichTextPreview/index.vue +45 -0
  45. package/dist/package/src/config/index.ts +1 -1
  46. package/dist/package/src/layouts/LayoutClassic/index.vue +3 -1
  47. package/dist/package/src/layouts/LayoutColumns/index.vue +3 -1
  48. package/dist/package/src/layouts/LayoutTransverse/index.vue +3 -1
  49. package/dist/package/src/layouts/LayoutVertical/index.vue +9 -1
  50. package/dist/package/src/layouts/components/Header/components/Avatar.vue +23 -4
  51. package/dist/package/src/layouts/components/Header/components/InfoDialog.vue +1 -1
  52. package/dist/package/src/layouts/components/Header/components/PasswordDialog.vue +1 -1
  53. package/dist/package/src/layouts/components/LockScreen.vue +450 -0
  54. package/dist/package/src/layouts/index.vue +2 -0
  55. package/dist/package/src/layouts/indexAsync.vue +2 -0
  56. package/dist/package/src/locales/README.md +2 -1
  57. package/dist/package/src/locales/en-US.json +43 -1
  58. package/dist/package/src/locales/index.ts +16 -12
  59. package/dist/package/src/locales/ja-JP.json +43 -1
  60. package/dist/package/src/locales/zh-CN.json +43 -1
  61. package/dist/package/src/locales/zh-TW.json +43 -1
  62. package/dist/package/src/rpc/base/v1/login.ts +33 -2
  63. package/dist/package/src/rpc/base/v1/mfa.ts +180 -0
  64. package/dist/package/src/rpc/base/v1/oauth.ts +25 -0
  65. package/dist/package/src/rpc/base/v1/oauth_client.ts +33 -0
  66. package/dist/package/src/rpc/system/admin/v1/base_dict.ts +4 -4
  67. package/dist/package/src/rpc/system/admin/v1/base_i18n.ts +27 -23
  68. package/dist/package/src/security.ts +2 -0
  69. package/dist/package/src/stores/interface/index.ts +15 -1
  70. package/dist/package/src/stores/modules/config.ts +4 -4
  71. package/dist/package/src/stores/modules/lockScreen.ts +78 -0
  72. package/dist/package/src/stores/modules/user.ts +16 -3
  73. package/dist/package/src/stores/runtime.ts +1 -0
  74. package/dist/package/src/utils/clipboard.ts +22 -0
  75. package/dist/package/src/utils/request.ts +26 -10
  76. package/dist/package/src/utils/webauthn.ts +90 -0
  77. package/dist/package/src/views/login/components/LoginForm.vue +237 -16
  78. package/dist/package/src/views/login/index.vue +3 -1
  79. package/dist/package/types/generated/auto-imports.d.ts +132 -129
  80. package/dist/package/types/generated/components.d.ts +4 -72
  81. package/package.json +22 -1
  82. package/types/generated/auto-imports.d.ts +132 -129
  83. package/types/generated/components.d.ts +4 -72
  84. package/vite.config.ts +3 -1
@@ -1,44 +1,48 @@
1
1
  import type { Empty } from "../../../google/protobuf/empty";
2
2
  /** 统一翻译表目标类型,和 base_i18n.target_type 的值一一对应。 */
3
- export declare enum TranslationTargetType {
4
- /** TRANSLATION_TARGET_TYPE_UNSPECIFIED - 未指定统一翻译表目标类型。 */
5
- TRANSLATION_TARGET_TYPE_UNSPECIFIED = 0,
6
- /** TRANSLATION_TARGET_TYPE_BASE_CONFIG_VALUE - 系统配置值。 */
7
- TRANSLATION_TARGET_TYPE_BASE_CONFIG_VALUE = 1,
8
- /** TRANSLATION_TARGET_TYPE_BASE_CONFIG_NAME - 系统配置名称。 */
9
- TRANSLATION_TARGET_TYPE_BASE_CONFIG_NAME = 2,
10
- /** TRANSLATION_TARGET_TYPE_BASE_DICT - 字典名称。 */
11
- TRANSLATION_TARGET_TYPE_BASE_DICT = 3,
12
- /** TRANSLATION_TARGET_TYPE_BASE_DICT_ITEM - 字典项标签。 */
13
- TRANSLATION_TARGET_TYPE_BASE_DICT_ITEM = 4,
14
- /** TRANSLATION_TARGET_TYPE_BASE_MENU - 菜单标题。 */
15
- TRANSLATION_TARGET_TYPE_BASE_MENU = 5
3
+ export declare enum I18nTargetType {
4
+ /** I18N_TARGET_TYPE_UNSPECIFIED - 未指定统一翻译表目标类型。 */
5
+ I18N_TARGET_TYPE_UNSPECIFIED = 0,
6
+ /** I18N_TARGET_TYPE_BASE_CONFIG_VALUE - 系统配置值。 */
7
+ I18N_TARGET_TYPE_BASE_CONFIG_VALUE = 1,
8
+ /** I18N_TARGET_TYPE_BASE_CONFIG_NAME - 系统配置名称。 */
9
+ I18N_TARGET_TYPE_BASE_CONFIG_NAME = 2,
10
+ /** I18N_TARGET_TYPE_BASE_DICT_NAME - 字典名称。 */
11
+ I18N_TARGET_TYPE_BASE_DICT_NAME = 3,
12
+ /** I18N_TARGET_TYPE_BASE_DICT_ITEM_LABEL - 字典项标签。 */
13
+ I18N_TARGET_TYPE_BASE_DICT_ITEM_LABEL = 4,
14
+ /** I18N_TARGET_TYPE_BASE_MENU_META_TITLE - 菜单标题。 */
15
+ I18N_TARGET_TYPE_BASE_MENU_META_TITLE = 5,
16
+ /** I18N_TARGET_TYPE_BASE_JOB_NAME - 定时任务名称。 */
17
+ I18N_TARGET_TYPE_BASE_JOB_NAME = 6,
18
+ /** I18N_TARGET_TYPE_BASE_MIGRATION_DESCRIPTION - 数据库迁移说明。 */
19
+ I18N_TARGET_TYPE_BASE_MIGRATION_DESCRIPTION = 7
16
20
  }
17
21
  /** 翻译单个文本请求。 */
18
- export interface DraftBaseTranslationRequest {
22
+ export interface DraftBaseI18nRequest {
19
23
  /** 待翻译文本 */
20
24
  source: string;
21
25
  /** 目标语言区域,不传时翻译所有启用的非主语言 */
22
26
  locale?: string | undefined;
23
27
  }
24
28
  /** 翻译单个文本响应。 */
25
- export interface DraftBaseTranslationResponse {
29
+ export interface DraftBaseI18nResponse {
26
30
  /** 翻译结果 */
27
- translations: DraftBaseTranslationItem[];
31
+ i18ns: DraftBaseI18nItem[];
28
32
  }
29
33
  /** 翻译单个文本响应。 */
30
- export interface DraftBaseTranslationItem {
34
+ export interface DraftBaseI18nItem {
31
35
  /** 目标语言区域 */
32
36
  locale: string;
33
37
  /** 翻译文本 */
34
- translation: string;
38
+ i18n: string;
35
39
  }
36
40
  /** 修改单个翻译信息请求。 */
37
- export interface UpdateBaseTranslationRequest {
41
+ export interface UpdateBaseI18nRequest {
38
42
  /** 翻译记录ID,大于零时优先按ID更新;ID不存在时根据目标信息更新或新增 */
39
43
  id: number;
40
44
  /** 翻译目标类型,ID为零时用于查询或新增 */
41
- target_type: TranslationTargetType;
45
+ target_type: I18nTargetType;
42
46
  /** 目标资源ID,ID为零时用于查询或新增 */
43
47
  target_id: number;
44
48
  /** 目标语言区域,ID为零时用于查询或新增 */
@@ -51,7 +55,7 @@ export interface BaseI18n {
51
55
  /** 翻译记录ID */
52
56
  id: number;
53
57
  /** 翻译目标类型 */
54
- target_type: TranslationTargetType;
58
+ target_type: I18nTargetType;
55
59
  /** 目标资源ID */
56
60
  target_id: number;
57
61
  /** 语言区域 */
@@ -69,7 +73,7 @@ export interface CodeGenLocaleConfig {
69
73
  /** 国际化翻译信息服务。 */
70
74
  export interface BaseI18nService {
71
75
  /** 翻译单个文本。 */
72
- DraftBaseTranslation(request: DraftBaseTranslationRequest): Promise<DraftBaseTranslationResponse>;
76
+ DraftBaseI18n(request: DraftBaseI18nRequest): Promise<DraftBaseI18nResponse>;
73
77
  /** 修改国际化翻译信息 */
74
- UpdateBaseTranslation(request: UpdateBaseTranslationRequest): Promise<Empty>;
78
+ UpdateBaseI18n(request: UpdateBaseI18nRequest): Promise<Empty>;
75
79
  }
@@ -1,3 +1,5 @@
1
1
  export * from "./utils/oauthProvider";
2
2
  export * from "./utils/passwordCrypto";
3
3
  export * from "./utils/passwordStrength";
4
+ export * from "./utils/webauthn";
5
+ export * from "./utils/clipboard";
@@ -52,6 +52,19 @@ export interface UserState {
52
52
  tokenExpiresAt: number;
53
53
  userInfo: UserInfoForm;
54
54
  }
55
+ /** 当前管理端锁屏状态。 */
56
+ export interface LockScreenState {
57
+ /** 是否处于锁屏状态。 */
58
+ isLocked: boolean;
59
+ /** 当前锁屏密码的摘要,仅在锁屏期间持久化。 */
60
+ passwordHash: string;
61
+ /** 是否显示设置锁屏密码界面。 */
62
+ setupVisible: boolean;
63
+ /** 是否显示解锁输入界面。 */
64
+ unlockVisible: boolean;
65
+ /** 最近一次解锁是否失败。 */
66
+ unlockError: boolean;
67
+ }
55
68
  /** 标签页菜单项状态。 */
56
69
  export interface TabsMenuProps {
57
70
  icon: string;
@@ -88,5 +101,5 @@ export interface SiteConfigState {
88
101
  /** 当前登录验证码配置 */
89
102
  captcha: LoginCaptchaConfig;
90
103
  /** 是否允许显式生成机器翻译草稿。 */
91
- translationDraftEnabled: boolean;
104
+ i18nDraftEnabled: boolean;
92
105
  }
@@ -0,0 +1,20 @@
1
+ /** 管理端锁屏状态仓库。 */
2
+ export declare const useLockScreenStore: import("pinia").StoreDefinition<"admin-lock-screen", import("pinia").StateTree, {
3
+ /** 当前是否已配置可用于解锁的密码。 */
4
+ isConfigured: (state: import("pinia").StateTree & import("pinia").PiniaCustomStateProperties<import("pinia").StateTree>) => boolean;
5
+ }, {
6
+ /** 打开锁屏密码设置界面。 */
7
+ openSetup(): void;
8
+ /** 关闭锁屏密码设置界面。 */
9
+ closeSetup(): void;
10
+ /** 设置锁屏密码并立即进入锁屏状态。 */
11
+ lock(password: string): Promise<void>;
12
+ /** 打开锁屏解锁输入界面。 */
13
+ openUnlock(): void;
14
+ /** 返回锁屏首页。 */
15
+ closeUnlock(): void;
16
+ /** 校验锁屏密码,成功后解除当前锁定。 */
17
+ unlock(password: string): Promise<boolean>;
18
+ /** 清理锁屏状态,供退出登录时使用。 */
19
+ clearLock(): void;
20
+ }>;
@@ -1,4 +1,5 @@
1
- import type { LoginRequest } from "../../rpc/base/v1/login";
1
+ import type { LoginRequest, LoginResponse } from "../../rpc/base/v1/login";
2
+ import type { VerifyMfaRequest } from "../../rpc/base/v1/mfa";
2
3
  import type { UserInfoForm } from "../../rpc/system/admin/v1/auth";
3
4
  import { UserState } from "../../stores/interface";
4
5
  export declare const useUserStore: import("pinia").StoreDefinition<"admin-user", import("pinia").StateTree, {}, {
@@ -17,7 +18,9 @@ export declare const useUserStore: import("pinia").StoreDefinition<"admin-user",
17
18
  /** 判断当前访问令牌是否仍然可用 */
18
19
  isAuthenticated(): boolean;
19
20
  /** 登录 */
20
- login(loginRequest: LoginRequest): Promise<void>;
21
+ login(loginRequest: LoginRequest): Promise<LoginResponse>;
22
+ /** 校验登录阶段的多因素认证并保存正式令牌。 */
23
+ verifyMfa(request: VerifyMfaRequest): Promise<LoginResponse>;
21
24
  /** 刷新认证令牌 */
22
25
  refreshAccessToken(): Promise<import("../../rpc/base/v1/login").RefreshTokenResponse>;
23
26
  /** 获取用户信息 */
@@ -1,5 +1,6 @@
1
1
  export { useAuthStore } from "./modules/auth";
2
2
  export { useConfigStore } from "./modules/config";
3
3
  export { useGlobalStore } from "./modules/global";
4
+ export { useLockScreenStore } from "./modules/lockScreen";
4
5
  export { useTabsStore } from "./modules/tabs";
5
6
  export { useUserStore } from "./modules/user";
@@ -0,0 +1,2 @@
1
+ /** 将文本复制到系统剪贴板,并兼容非安全上下文的管理端开发地址。 */
2
+ export declare function copyText(content: string): Promise<void>;
@@ -0,0 +1,4 @@
1
+ /** 执行 WebAuthn 注册 ceremony。 */
2
+ export declare function createWebAuthnCredential(optionsJson: string): Promise<string>;
3
+ /** 执行 WebAuthn 登录 ceremony。 */
4
+ export declare function getWebAuthnAssertion(optionsJson: string): Promise<string>;
@@ -6,6 +6,8 @@ export interface AdminViteConfigOptions {
6
6
  optimizeDependencies?: string[];
7
7
  /** 静态资源构建输出目录。 */
8
8
  outputDirectory?: string;
9
+ /** 自动组件声明输出位置,外部宿主默认不写入依赖包。 */
10
+ componentDts?: string | false;
9
11
  }
10
12
  /** 创建管理端宿主 Vite 配置。 */
11
13
  export declare function defineAdminViteConfig(options?: AdminViteConfigOptions): import("vite").UserConfigFnObject;
@@ -0,0 +1,114 @@
1
+ import service from "../../utils/request";
2
+ import type { LoginResponse } from "../../rpc/base/v1/login";
3
+ import type {
4
+ BeginMfaDisableRequest,
5
+ BeginMfaDisableResponse,
6
+ BeginMfaEnrollmentRequest,
7
+ BeginMfaEnrollmentResponse,
8
+ BeginMfaSetupRequest,
9
+ BeginMfaSetupResponse,
10
+ ConfirmMfaEnrollmentRequest,
11
+ ConfirmMfaEnrollmentResponse,
12
+ ConfirmMfaSetupRequest,
13
+ ConfirmMfaSetupResponse,
14
+ DisableMfaRequest,
15
+ GetMfaStatusRequest,
16
+ MfaService,
17
+ MfaStatusResponse,
18
+ RecoveryCodesResponse,
19
+ RegenerateMfaRecoveryCodesRequest,
20
+ VerifyMfaRequest
21
+ } from "../../rpc/base/v1/mfa";
22
+ import type { Empty } from "../../rpc/google/protobuf/empty";
23
+
24
+ const MFA_URL = "/v1/base/mfa";
25
+
26
+ /** 多因素认证公共服务。 */
27
+ export class MfaServiceImpl implements MfaService {
28
+ /** 校验登录阶段多因素认证。 */
29
+ VerifyMfa(request: VerifyMfaRequest): Promise<LoginResponse> {
30
+ return service<VerifyMfaRequest, LoginResponse>({
31
+ url: `${MFA_URL}/verify`,
32
+ method: "post",
33
+ data: request,
34
+ headers: { Authorization: "no-auth" }
35
+ });
36
+ }
37
+
38
+ /** 开始登录阶段的强制多因素认证绑定。 */
39
+ BeginMfaEnrollment(request: BeginMfaEnrollmentRequest): Promise<BeginMfaEnrollmentResponse> {
40
+ return service<BeginMfaEnrollmentRequest, BeginMfaEnrollmentResponse>({
41
+ url: `${MFA_URL}/enrollment`,
42
+ method: "post",
43
+ data: request,
44
+ headers: { Authorization: "no-auth" }
45
+ });
46
+ }
47
+
48
+ /** 确认登录阶段的强制多因素认证绑定。 */
49
+ ConfirmMfaEnrollment(request: ConfirmMfaEnrollmentRequest): Promise<ConfirmMfaEnrollmentResponse> {
50
+ return service<ConfirmMfaEnrollmentRequest, ConfirmMfaEnrollmentResponse>({
51
+ url: `${MFA_URL}/enrollment/confirm`,
52
+ method: "post",
53
+ data: request,
54
+ headers: { Authorization: "no-auth" }
55
+ });
56
+ }
57
+
58
+ /** 查询当前用户多因素认证状态。 */
59
+ GetMfaStatus(request: GetMfaStatusRequest): Promise<MfaStatusResponse> {
60
+ return service<GetMfaStatusRequest, MfaStatusResponse>({
61
+ url: MFA_URL,
62
+ method: "get",
63
+ params: request
64
+ });
65
+ }
66
+
67
+ /** 开始绑定当前用户的多因素认证方式。 */
68
+ BeginMfaSetup(request: BeginMfaSetupRequest): Promise<BeginMfaSetupResponse> {
69
+ return service<BeginMfaSetupRequest, BeginMfaSetupResponse>({
70
+ url: `${MFA_URL}/setup`,
71
+ method: "post",
72
+ data: request
73
+ });
74
+ }
75
+
76
+ /** 确认绑定当前用户的多因素认证方式。 */
77
+ ConfirmMfaSetup(request: ConfirmMfaSetupRequest): Promise<ConfirmMfaSetupResponse> {
78
+ return service<ConfirmMfaSetupRequest, ConfirmMfaSetupResponse>({
79
+ url: `${MFA_URL}/setup/confirm`,
80
+ method: "post",
81
+ data: request
82
+ });
83
+ }
84
+
85
+ /** 开始禁用 WebAuthn 多因素认证的 Passkey 验证。 */
86
+ BeginMfaDisable(request: BeginMfaDisableRequest): Promise<BeginMfaDisableResponse> {
87
+ return service<BeginMfaDisableRequest, BeginMfaDisableResponse>({
88
+ url: `${MFA_URL}/disable/challenge`,
89
+ method: "post",
90
+ data: request
91
+ });
92
+ }
93
+
94
+ /** 禁用当前用户的多因素认证。 */
95
+ DisableMfa(request: DisableMfaRequest): Promise<Empty> {
96
+ return service<DisableMfaRequest, Empty>({
97
+ url: `${MFA_URL}/disable`,
98
+ method: "post",
99
+ data: request
100
+ });
101
+ }
102
+
103
+ /** 重新生成当前用户的多因素认证恢复码。 */
104
+ RegenerateMfaRecoveryCodes(request: RegenerateMfaRecoveryCodesRequest): Promise<RecoveryCodesResponse> {
105
+ return service<RegenerateMfaRecoveryCodesRequest, RecoveryCodesResponse>({
106
+ url: `${MFA_URL}/recovery-codes`,
107
+ method: "post",
108
+ data: request
109
+ });
110
+ }
111
+ }
112
+
113
+ /** defMfaService 多因素认证公共服务实例。 */
114
+ export const defMfaService = new MfaServiceImpl();
@@ -0,0 +1,176 @@
1
+ <template>
2
+ <ProDialog
3
+ :model-value="modelValue"
4
+ :title="dialogTitle"
5
+ :width="width"
6
+ :top="top"
7
+ :confirm-text="confirmText"
8
+ :cancel-text="cancelText"
9
+ :confirm-loading="submitting"
10
+ :destroy-on-close="destroyOnClose"
11
+ :close-on-click-modal="closeOnClickModal"
12
+ :close-on-press-escape="closeOnPressEscape"
13
+ @update:model-value="handleVisibleChange"
14
+ @confirm="handleConfirm"
15
+ @cancel="handleCancel"
16
+ @close="handleClose"
17
+ @closed="handleClosed"
18
+ >
19
+ <p v-if="description" class="password-verify-dialog__description">{{ description }}</p>
20
+ <ProForm
21
+ ref="formRef"
22
+ :model="form"
23
+ :fields="fields"
24
+ :rules="rules"
25
+ :label-width="labelWidth"
26
+ @keyup.enter.prevent="handleConfirm"
27
+ />
28
+ </ProDialog>
29
+ </template>
30
+
31
+ <script setup lang="ts" name="PasswordVerifyDialog">
32
+ import { computed, reactive, ref } from "vue";
33
+ import type { FormRules } from "element-plus";
34
+ import ProDialog from "../../components/Dialog/ProDialog.vue";
35
+ import ProForm from "../../components/ProForm/index.vue";
36
+ import type { ProFormField, ProFormInstance } from "../../components/ProForm/interface";
37
+ import { useLocaleStore } from "../../locales";
38
+ import { PASSWORD_CRYPTO_SCENE, encryptPassword } from "../../utils/passwordCrypto";
39
+ import type { PasswordCryptoScene } from "../../utils/passwordCrypto";
40
+ import type { PasswordCrypto } from "../../rpc/common/v1/types";
41
+
42
+ const { t } = useLocaleStore();
43
+
44
+ /** 通用密码验证弹窗属性。 */
45
+ interface PasswordVerifyDialogProps {
46
+ /** 弹窗显示状态。 */
47
+ modelValue: boolean;
48
+ /** 弹窗标题。 */
49
+ title?: string;
50
+ /** 密码输入前的可选补充说明。 */
51
+ description?: string;
52
+ /** 弹窗宽度。 */
53
+ width?: string | number;
54
+ /** 弹窗顶部距离。 */
55
+ top?: string;
56
+ /** 密码字段标题。 */
57
+ passwordLabel?: string;
58
+ /** 密码输入提示。 */
59
+ passwordPlaceholder?: string;
60
+ /** 确认按钮文案。 */
61
+ confirmText?: string;
62
+ /** 取消按钮文案。 */
63
+ cancelText?: string;
64
+ /** 外部业务提交状态。 */
65
+ confirmLoading?: boolean;
66
+ /** 密码加密场景。 */
67
+ scene?: PasswordCryptoScene;
68
+ /** 是否关闭时销毁内容。 */
69
+ destroyOnClose?: boolean;
70
+ /** 是否允许点击遮罩关闭。 */
71
+ closeOnClickModal?: boolean;
72
+ /** 是否允许按 Esc 关闭。 */
73
+ closeOnPressEscape?: boolean;
74
+ /** 表单标签宽度。 */
75
+ labelWidth?: string;
76
+ }
77
+
78
+ const props = withDefaults(defineProps<PasswordVerifyDialogProps>(), {
79
+ title: "",
80
+ description: "",
81
+ width: "500px",
82
+ top: "8vh",
83
+ passwordLabel: "",
84
+ passwordPlaceholder: "",
85
+ confirmText: "",
86
+ cancelText: "",
87
+ confirmLoading: false,
88
+ scene: PASSWORD_CRYPTO_SCENE.PASSWORD_CRYPTO_SCENE_MFA,
89
+ destroyOnClose: false,
90
+ closeOnClickModal: false,
91
+ closeOnPressEscape: true,
92
+ labelWidth: "auto"
93
+ });
94
+
95
+ const emit = defineEmits<{
96
+ "update:modelValue": [value: boolean];
97
+ confirm: [password: PasswordCrypto];
98
+ cancel: [];
99
+ close: [];
100
+ closed: [];
101
+ }>();
102
+
103
+ const formRef = ref<ProFormInstance>();
104
+ const form = reactive({ password: "" });
105
+ const encrypting = ref(false);
106
+ const submitting = computed(() => props.confirmLoading || encrypting.value);
107
+ const dialogTitle = computed(() => props.title || t("core.password.verify_title"));
108
+ const passwordLabel = computed(() => props.passwordLabel || t("core.password.verify_label"));
109
+ const passwordPlaceholder = computed(() => props.passwordPlaceholder || t("core.password.required"));
110
+ const confirmText = computed(() => props.confirmText || t("common.action.confirm"));
111
+ const cancelText = computed(() => props.cancelText || t("common.action.cancel"));
112
+
113
+ const fields = computed<ProFormField[]>(() => [
114
+ {
115
+ prop: "password",
116
+ label: passwordLabel.value,
117
+ component: "password",
118
+ props: {
119
+ autocomplete: "current-password",
120
+ placeholder: passwordPlaceholder.value,
121
+ showPassword: true
122
+ }
123
+ }
124
+ ]);
125
+
126
+ const rules = computed<FormRules>(() => ({
127
+ password: [{ required: true, message: t("core.password.required"), trigger: "blur" }]
128
+ }));
129
+
130
+ /** 同步弹窗显示状态到外部。 */
131
+ function handleVisibleChange(value: boolean) {
132
+ emit("update:modelValue", value);
133
+ }
134
+
135
+ /** 校验密码并向业务方返回加密结果。 */
136
+ async function handleConfirm() {
137
+ if (submitting.value) return;
138
+ if (!(await formRef.value?.validate())) return;
139
+ encrypting.value = true;
140
+ try {
141
+ const password = await encryptPassword(form.password, props.scene);
142
+ emit("confirm", password);
143
+ } finally {
144
+ encrypting.value = false;
145
+ }
146
+ }
147
+
148
+ /** 对外透传取消事件。 */
149
+ function handleCancel() {
150
+ emit("cancel");
151
+ }
152
+
153
+ /** 对外透传关闭事件。 */
154
+ function handleClose() {
155
+ emit("close");
156
+ }
157
+
158
+ /** 关闭弹窗后清理密码和校验状态。 */
159
+ function handleClosed() {
160
+ form.password = "";
161
+ formRef.value?.clearValidate();
162
+ emit("closed");
163
+ }
164
+ </script>
165
+
166
+ <style scoped lang="scss">
167
+ .password-verify-dialog__description {
168
+ margin: 0 0 16px;
169
+ color: var(--el-text-color-regular);
170
+ line-height: 1.6;
171
+ }
172
+
173
+ .password-verify-dialog__description + :deep(.pro-form) {
174
+ margin-top: 0;
175
+ }
176
+ </style>
@@ -1,4 +1,5 @@
1
1
  import { ElLoading } from "element-plus";
2
+ import { t } from "../../locales";
2
3
 
3
4
  /* 全局请求 loading */
4
5
  let loadingInstance: ReturnType<typeof ElLoading.service>;
@@ -10,7 +11,7 @@ const startLoading = () => {
10
11
  loadingInstance = ElLoading.service({
11
12
  fullscreen: true,
12
13
  lock: true,
13
- text: "Loading",
14
+ text: t("core.loading.text"),
14
15
  background: "rgba(0, 0, 0, 0.7)"
15
16
  });
16
17
  };
@@ -7,7 +7,7 @@
7
7
  :theme="props.isDark ? 'dark' : 'light'"
8
8
  preview-theme="github"
9
9
  code-theme="github"
10
- language="zh-CN"
10
+ :language="locale"
11
11
  :show-code-row-number="false"
12
12
  :code-foldable="false"
13
13
  :no-mermaid="true"
@@ -26,6 +26,7 @@ import DOMPurify from "dompurify";
26
26
  import hljs from "highlight.js";
27
27
  import { config as configureMarkdownPreview, MdPreview } from "md-editor-v3";
28
28
  import type { MdHeadingId } from "md-editor-v3";
29
+ import { getLocaleText, useLocaleStore } from "../../locales";
29
30
  import "md-editor-v3/lib/preview.css";
30
31
 
31
32
  defineOptions({
@@ -53,12 +54,31 @@ const props = withDefaults(defineProps<MarkdownPreviewProps>(), {
53
54
  const generatedId = useId();
54
55
  const resolvedId = computed(() => props.id || `core-markdown-preview-${generatedId.replaceAll(":", "")}`);
55
56
  const markdownHeadingIdCounts = new Map<string, number>();
57
+ const { locale } = useLocaleStore();
56
58
 
57
59
  configureMarkdownPreview({
58
60
  editorExtensions: {
59
61
  highlight: {
60
62
  instance: hljs
61
63
  }
64
+ },
65
+ editorConfig: {
66
+ languageUserDefined: {
67
+ "zh-TW": {
68
+ copyCode: {
69
+ text: getLocaleText("zh-TW", "core.markdown.action.copy_code"),
70
+ successTips: getLocaleText("zh-TW", "core.markdown.message.copy_success"),
71
+ failTips: getLocaleText("zh-TW", "core.markdown.message.copy_failed")
72
+ }
73
+ },
74
+ "ja-JP": {
75
+ copyCode: {
76
+ text: getLocaleText("ja-JP", "core.markdown.action.copy_code"),
77
+ successTips: getLocaleText("ja-JP", "core.markdown.message.copy_success"),
78
+ failTips: getLocaleText("ja-JP", "core.markdown.message.copy_failed")
79
+ }
80
+ }
81
+ }
62
82
  }
63
83
  });
64
84
 
@@ -0,0 +1,91 @@
1
+ <template>
2
+ <ProDialog
3
+ :model-value="modelValue"
4
+ :title="t('core.login.mfa_recovery_codes_title')"
5
+ width="420px"
6
+ :close-on-click-modal="false"
7
+ @update:model-value="emit('update:modelValue', $event)"
8
+ >
9
+ <div class="mfa-recovery-dialog__codes">
10
+ <span>{{ codesText }}</span>
11
+ <el-tooltip :content="t('core.login.mfa_copy_recovery_codes')" placement="top">
12
+ <el-button text circle :aria-label="t('core.login.mfa_copy_recovery_codes')" @click="copyRecoveryCodes">
13
+ <el-icon class="mfa-recovery-dialog__copy"><CopyDocument /></el-icon>
14
+ </el-button>
15
+ </el-tooltip>
16
+ </div>
17
+ <p class="mfa-recovery-dialog__warning">{{ t("core.login.mfa_recovery_codes_warning") }}</p>
18
+ <template #footer>
19
+ <el-button type="primary" @click="handleConfirm">{{ t("core.login.mfa_recovery_codes_confirm") }}</el-button>
20
+ </template>
21
+ </ProDialog>
22
+ </template>
23
+
24
+ <script setup lang="ts" name="MfaRecoveryCodesDialog">
25
+ import { computed } from "vue";
26
+ import ProDialog from "../../components/Dialog/ProDialog.vue";
27
+ import { useLocaleStore } from "../../locales";
28
+ import { copyText } from "../../utils/clipboard";
29
+
30
+ /** MFA 恢复码弹窗属性。 */
31
+ interface MfaRecoveryCodesDialogProps {
32
+ /** 弹窗显示状态。 */
33
+ modelValue: boolean;
34
+ /** 本次生成的一次性恢复码。 */
35
+ codes: string[];
36
+ }
37
+
38
+ const props = withDefaults(defineProps<MfaRecoveryCodesDialogProps>(), {
39
+ modelValue: false,
40
+ codes: () => []
41
+ });
42
+
43
+ const emit = defineEmits<{
44
+ "update:modelValue": [value: boolean];
45
+ confirm: [];
46
+ }>();
47
+
48
+ const { t } = useLocaleStore();
49
+ const codesText = computed(() => props.codes.join("\n"));
50
+
51
+ /** 复制当前显示的恢复码。 */
52
+ async function copyRecoveryCodes() {
53
+ await copyText(codesText.value);
54
+ ElMessage.success(t("core.login.mfa_recovery_codes_copied"));
55
+ }
56
+
57
+ /** 关闭弹窗并通知调用方完成绑定流程。 */
58
+ function handleConfirm() {
59
+ emit("update:modelValue", false);
60
+ emit("confirm");
61
+ }
62
+ </script>
63
+
64
+ <style scoped lang="scss">
65
+ .mfa-recovery-dialog__codes {
66
+ display: flex;
67
+ gap: 8px;
68
+ align-items: flex-start;
69
+ color: var(--el-text-color-regular);
70
+ line-height: 1.6;
71
+ overflow-wrap: anywhere;
72
+ }
73
+
74
+ .mfa-recovery-dialog__codes span {
75
+ flex: 1;
76
+ min-width: 0;
77
+ white-space: pre-wrap;
78
+ }
79
+
80
+ .mfa-recovery-dialog__copy {
81
+ color: var(--el-color-primary);
82
+ font-size: 18px;
83
+ }
84
+
85
+ .mfa-recovery-dialog__warning {
86
+ margin: 8px 0 0;
87
+ color: var(--el-color-warning);
88
+ font-size: 13px;
89
+ line-height: 1.6;
90
+ }
91
+ </style>