@liujitcn/kratos-admin-core 0.0.29 → 0.0.32

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 (147) hide show
  1. package/README.md +15 -8
  2. package/build/auto-imports.json +73 -0
  3. package/build/plugins.ts +2 -58
  4. package/dist/package/declarations/src/api/base/{config.d.ts → v1/config.d.ts} +1 -1
  5. package/dist/package/declarations/src/api/base/{file.d.ts → v1/file.d.ts} +3 -1
  6. package/dist/package/declarations/src/api/base/{language.d.ts → v1/language.d.ts} +1 -1
  7. package/dist/package/declarations/src/api/base/{login.d.ts → v1/login.d.ts} +2 -2
  8. package/dist/package/declarations/src/api/base/v1/mfa.d.ts +26 -0
  9. package/dist/package/declarations/src/api/base/{oauth.d.ts → v1/oauth.d.ts} +1 -1
  10. package/dist/package/declarations/src/api/system/{auth.d.ts → admin/v1/auth.d.ts} +2 -2
  11. package/dist/package/declarations/src/api/system/{base_dict.d.ts → admin/v1/base_dict.d.ts} +1 -1
  12. package/dist/package/declarations/src/api/system/{base_tenant.d.ts → admin/v1/base_tenant.d.ts} +2 -2
  13. package/dist/package/declarations/src/components/Dialog/FormDialog.vue.d.ts +3 -3
  14. package/dist/package/declarations/src/components/Dialog/PasswordVerifyDialog.vue.d.ts +65 -0
  15. package/dist/package/declarations/src/components/Dialog/ProDialog.vue.d.ts +1 -1
  16. package/dist/package/declarations/src/components/Grid/components/GridItem.vue.d.ts +1 -1
  17. package/dist/package/declarations/src/components/Mfa/MfaRecoveryCodesDialog.vue.d.ts +19 -0
  18. package/dist/package/declarations/src/components/Mfa/MfaSetupPanel.vue.d.ts +8 -0
  19. package/dist/package/declarations/src/components/ProForm/components/ProFormItem.vue.d.ts +4 -4
  20. package/dist/package/declarations/src/components/ProForm/index.vue.d.ts +1 -1
  21. package/dist/package/declarations/src/components/ProForm/interface/index.d.ts +4 -1
  22. package/dist/package/declarations/src/components/ProTable/index.vue.d.ts +114 -0
  23. package/dist/package/declarations/src/components/RichTextPreview/index.vue.d.ts +8 -0
  24. package/dist/package/declarations/src/components/TreeFilter/index.vue.d.ts +84 -76
  25. package/dist/package/declarations/src/components/Upload/File.vue.d.ts +1 -1
  26. package/dist/package/declarations/src/components/Upload/Files.vue.d.ts +1 -1
  27. package/dist/package/declarations/src/components/Upload/Img.vue.d.ts +2 -2
  28. package/dist/package/declarations/src/components/Upload/Imgs.vue.d.ts +2 -2
  29. package/dist/package/declarations/src/components/WangEditor/index.vue.d.ts +2 -0
  30. package/dist/package/declarations/src/documentTitle.d.ts +2 -0
  31. package/dist/package/declarations/src/format.d.ts +1 -1
  32. package/dist/package/declarations/src/index.d.ts +2 -1
  33. package/dist/package/declarations/src/layouts/components/LockScreen.vue.d.ts +3 -0
  34. package/dist/package/declarations/src/locales/generated.d.ts +208 -0
  35. package/dist/package/declarations/src/locales/index.d.ts +2 -0
  36. package/dist/package/declarations/src/modules/index.d.ts +1 -3
  37. package/dist/package/declarations/src/request.d.ts +1 -1
  38. package/dist/package/declarations/src/routers/index.d.ts +1 -1
  39. package/dist/package/declarations/src/rpc/base/v1/login.d.ts +37 -5
  40. package/dist/package/declarations/src/rpc/base/v1/mfa.d.ts +152 -0
  41. package/dist/package/declarations/src/rpc/base/v1/oauth.d.ts +25 -0
  42. package/dist/package/declarations/src/rpc/base/v1/oauth_client.d.ts +23 -0
  43. package/dist/package/declarations/src/rpc/system/admin/v1/auth.d.ts +13 -0
  44. package/dist/package/declarations/src/rpc/system/admin/v1/base_dict.d.ts +0 -101
  45. package/dist/package/declarations/src/rpc/system/admin/v1/base_i18n.d.ts +10 -6
  46. package/dist/package/declarations/src/rpc/system/common/v1/common.d.ts +26 -0
  47. package/dist/package/declarations/src/security.d.ts +2 -0
  48. package/dist/package/declarations/src/stores/interface/index.d.ts +19 -1
  49. package/dist/package/declarations/src/stores/modules/lockScreen.d.ts +20 -0
  50. package/dist/package/declarations/src/stores/modules/user.d.ts +7 -6
  51. package/dist/package/declarations/src/stores/runtime.d.ts +1 -0
  52. package/dist/package/declarations/src/utils/clipboard.d.ts +2 -0
  53. package/dist/package/declarations/src/utils/passwordStrength.d.ts +1 -12
  54. package/dist/package/declarations/src/utils/request.d.ts +13 -2
  55. package/dist/package/declarations/src/utils/utils.d.ts +4 -0
  56. package/dist/package/declarations/src/utils/webauthn.d.ts +4 -0
  57. package/dist/package/declarations/vite.config.d.ts +2 -0
  58. package/dist/package/src/api/base/{config.ts → v1/config.ts} +2 -2
  59. package/dist/package/src/api/base/{file.ts → v1/file.ts} +20 -24
  60. package/dist/package/src/api/base/{language.ts → v1/language.ts} +2 -2
  61. package/dist/package/src/api/base/{login.ts → v1/login.ts} +3 -3
  62. package/dist/package/src/api/base/v1/mfa.ts +114 -0
  63. package/dist/package/src/api/base/{oauth.ts → v1/oauth.ts} +2 -2
  64. package/dist/package/src/api/system/{auth.ts → admin/v1/auth.ts} +3 -3
  65. package/dist/package/src/api/system/{base_dict.ts → admin/v1/base_dict.ts} +2 -2
  66. package/dist/package/src/api/system/{base_tenant.ts → admin/v1/base_tenant.ts} +3 -3
  67. package/dist/package/src/bootstrap.ts +6 -5
  68. package/dist/package/src/components/CronExpression/index.vue +4 -3
  69. package/dist/package/src/components/Dialog/PasswordVerifyDialog.vue +176 -0
  70. package/dist/package/src/components/Dialog/ProDialog.vue +1 -0
  71. package/dist/package/src/components/ImportExcel/index.vue +4 -2
  72. package/dist/package/src/components/Loading/fullScreen.ts +2 -1
  73. package/dist/package/src/components/MarkdownPreview/index.vue +22 -2
  74. package/dist/package/src/components/Mfa/MfaRecoveryCodesDialog.vue +91 -0
  75. package/dist/package/src/components/Mfa/MfaSetupPanel.vue +81 -0
  76. package/dist/package/src/components/PasswordStrength/index.vue +1 -1
  77. package/dist/package/src/components/ProForm/components/ProFormItem.vue +16 -1
  78. package/dist/package/src/components/ProForm/interface/index.ts +4 -0
  79. package/dist/package/src/components/ProTable/components/ColSetting.vue +1 -1
  80. package/dist/package/src/components/ProTable/components/TableColumn.vue +14 -6
  81. package/dist/package/src/components/ProTable/index.vue +9 -5
  82. package/dist/package/src/components/RichTextPreview/index.vue +56 -0
  83. package/dist/package/src/components/SelectIcon/index.vue +3 -2
  84. package/dist/package/src/components/Upload/File.vue +2 -2
  85. package/dist/package/src/components/Upload/Files.vue +2 -2
  86. package/dist/package/src/components/Upload/Img.vue +7 -5
  87. package/dist/package/src/components/Upload/Imgs.vue +6 -5
  88. package/dist/package/src/components/WangEditor/index.vue +10 -7
  89. package/dist/package/src/config/index.ts +1 -1
  90. package/dist/package/src/documentTitle.ts +8 -0
  91. package/dist/package/src/format.ts +1 -1
  92. package/dist/package/src/index.ts +2 -1
  93. package/dist/package/src/layouts/LayoutClassic/index.vue +3 -1
  94. package/dist/package/src/layouts/LayoutColumns/index.vue +3 -1
  95. package/dist/package/src/layouts/LayoutTransverse/index.vue +3 -1
  96. package/dist/package/src/layouts/LayoutVertical/index.vue +9 -1
  97. package/dist/package/src/layouts/components/Header/components/Avatar.vue +26 -6
  98. package/dist/package/src/layouts/components/Header/components/InfoDialog.vue +4 -3
  99. package/dist/package/src/layouts/components/Header/components/PasswordDialog.vue +4 -3
  100. package/dist/package/src/layouts/components/Header/components/SearchMenu.vue +16 -8
  101. package/dist/package/src/layouts/components/LockScreen.vue +573 -0
  102. package/dist/package/src/layouts/components/Tabs/index.vue +12 -3
  103. package/dist/package/src/layouts/components/ThemeDrawer/index.scss +4 -4
  104. package/dist/package/src/layouts/index.vue +2 -0
  105. package/dist/package/src/layouts/indexAsync.vue +2 -0
  106. package/dist/package/src/locales/README.md +2 -1
  107. package/dist/package/src/locales/en-US.json +51 -3
  108. package/dist/package/src/locales/index.ts +16 -12
  109. package/dist/package/src/locales/ja-JP.json +51 -3
  110. package/dist/package/src/locales/zh-CN.json +51 -3
  111. package/dist/package/src/locales/zh-TW.json +51 -3
  112. package/dist/package/src/modules/index.ts +2 -4
  113. package/dist/package/src/request.ts +3 -0
  114. package/dist/package/src/routers/index.ts +15 -14
  115. package/dist/package/src/rpc/base/v1/login.ts +37 -4
  116. package/dist/package/src/rpc/base/v1/mfa.ts +180 -0
  117. package/dist/package/src/rpc/base/v1/oauth.ts +25 -0
  118. package/dist/package/src/rpc/base/v1/oauth_client.ts +33 -0
  119. package/dist/package/src/rpc/system/admin/v1/auth.ts +13 -0
  120. package/dist/package/src/rpc/system/admin/v1/base_dict.ts +0 -112
  121. package/dist/package/src/rpc/system/admin/v1/base_i18n.ts +10 -6
  122. package/dist/package/src/rpc/system/common/v1/common.ts +35 -0
  123. package/dist/package/src/security.ts +2 -0
  124. package/dist/package/src/stores/interface/index.ts +20 -1
  125. package/dist/package/src/stores/modules/auth.ts +1 -1
  126. package/dist/package/src/stores/modules/config.ts +2 -2
  127. package/dist/package/src/stores/modules/dict.ts +1 -1
  128. package/dist/package/src/stores/modules/lockScreen.ts +78 -0
  129. package/dist/package/src/stores/modules/user.ts +34 -22
  130. package/dist/package/src/stores/runtime.ts +1 -0
  131. package/dist/package/src/styles/element.scss +35 -4
  132. package/dist/package/src/utils/clipboard.ts +22 -0
  133. package/dist/package/src/utils/passwordCrypto.ts +1 -1
  134. package/dist/package/src/utils/passwordStrength.ts +7 -24
  135. package/dist/package/src/utils/request.ts +175 -40
  136. package/dist/package/src/utils/tenant.ts +1 -1
  137. package/dist/package/src/utils/utils.ts +25 -6
  138. package/dist/package/src/utils/webauthn.ts +90 -0
  139. package/dist/package/src/views/login/components/LoginForm.vue +257 -24
  140. package/dist/package/src/views/login/index.scss +2 -2
  141. package/dist/package/src/views/login/index.vue +3 -1
  142. package/dist/package/types/generated/auto-imports.d.ts +132 -129
  143. package/dist/package/types/generated/components.d.ts +4 -72
  144. package/package.json +22 -1
  145. package/types/generated/auto-imports.d.ts +132 -129
  146. package/types/generated/components.d.ts +4 -72
  147. package/vite.config.ts +4 -1
@@ -0,0 +1,78 @@
1
+ import { defineStore } from "pinia";
2
+ import type { LockScreenState } from "../../stores/interface";
3
+ import piniaPersistConfig from "../../stores/helper/persist";
4
+
5
+ /** 将锁屏密码转换为不可逆摘要,避免在本地持久化明文密码。 */
6
+ async function hashPassword(password: string): Promise<string> {
7
+ const data = new TextEncoder().encode(password);
8
+ const digest = await crypto.subtle.digest("SHA-256", data);
9
+ return Array.from(new Uint8Array(digest), byte => byte.toString(16).padStart(2, "0")).join("");
10
+ }
11
+
12
+ /** 管理端锁屏状态仓库。 */
13
+ export const useLockScreenStore = defineStore("admin-lock-screen", {
14
+ state: (): LockScreenState => ({
15
+ isLocked: false,
16
+ passwordHash: "",
17
+ setupVisible: false,
18
+ unlockVisible: false,
19
+ unlockError: false
20
+ }),
21
+ getters: {
22
+ /** 当前是否已配置可用于解锁的密码。 */
23
+ isConfigured: state => Boolean(state.passwordHash)
24
+ },
25
+ actions: {
26
+ /** 打开锁屏密码设置界面。 */
27
+ openSetup() {
28
+ this.setupVisible = true;
29
+ this.unlockVisible = false;
30
+ this.unlockError = false;
31
+ },
32
+ /** 关闭锁屏密码设置界面。 */
33
+ closeSetup() {
34
+ this.setupVisible = false;
35
+ },
36
+ /** 设置锁屏密码并立即进入锁屏状态。 */
37
+ async lock(password: string) {
38
+ this.passwordHash = await hashPassword(password);
39
+ this.isLocked = true;
40
+ this.setupVisible = false;
41
+ this.unlockVisible = false;
42
+ this.unlockError = false;
43
+ },
44
+ /** 打开锁屏解锁输入界面。 */
45
+ openUnlock() {
46
+ this.unlockVisible = true;
47
+ this.unlockError = false;
48
+ },
49
+ /** 返回锁屏首页。 */
50
+ closeUnlock() {
51
+ this.unlockVisible = false;
52
+ this.unlockError = false;
53
+ },
54
+ /** 校验锁屏密码,成功后解除当前锁定。 */
55
+ async unlock(password: string): Promise<boolean> {
56
+ const passwordHash = await hashPassword(password);
57
+ if (!this.passwordHash || passwordHash !== this.passwordHash) {
58
+ this.unlockError = true;
59
+ return false;
60
+ }
61
+
62
+ this.isLocked = false;
63
+ this.passwordHash = "";
64
+ this.unlockVisible = false;
65
+ this.unlockError = false;
66
+ return true;
67
+ },
68
+ /** 清理锁屏状态,供退出登录时使用。 */
69
+ clearLock() {
70
+ this.isLocked = false;
71
+ this.passwordHash = "";
72
+ this.setupVisible = false;
73
+ this.unlockVisible = false;
74
+ this.unlockError = false;
75
+ }
76
+ },
77
+ persist: piniaPersistConfig("admin-lock-screen", ["isLocked", "passwordHash"])
78
+ });
@@ -1,17 +1,22 @@
1
1
  import { defineStore } from "pinia";
2
- import { defAuthService } from "../../api/system/auth";
3
- import { defLoginService } from "../../api/base/login";
4
- import type { LoginRequest } from "../../rpc/base/v1/login";
2
+ import { defAuthService } from "../../api/system/admin/v1/auth";
3
+ import { defLoginService } from "../../api/base/v1/login";
4
+ import { defMfaService } from "../../api/base/v1/mfa";
5
+ import type { LoginRequest, LoginResponse } from "../../rpc/base/v1/login";
6
+ import type { VerifyMfaRequest } from "../../rpc/base/v1/mfa";
5
7
  import type { UserInfoForm } from "../../rpc/system/admin/v1/auth";
6
8
  import { UserState } from "../../stores/interface";
7
9
  import piniaPersistConfig from "../../stores/helper/persist";
8
10
  import { useDictStoreHook } from "../../stores/modules/dict";
9
- import { t } from "../../locales";
11
+ import { useLockScreenStore } from "../../stores/modules/lockScreen";
10
12
 
11
13
  const defaultUserInfo: UserInfoForm = {
12
14
  user_name: "",
13
15
  nick_name: "",
14
16
  phone: "",
17
+ email: "",
18
+ id_type: 0,
19
+ id_code: "",
15
20
  avatar: "",
16
21
  role_code: "",
17
22
  role_name: "",
@@ -23,9 +28,11 @@ const defaultUserInfo: UserInfoForm = {
23
28
  export const useUserStore = defineStore("admin-user", {
24
29
  state: (): UserState => ({
25
30
  token: "",
26
- refreshToken: "",
27
31
  tokenType: "",
28
32
  tokenExpiresAt: 0,
33
+ isLoggingOut: false,
34
+ authVersion: 0,
35
+ authInvalidated: false,
29
36
  userInfo: defaultUserInfo
30
37
  }),
31
38
  getters: {},
@@ -34,10 +41,6 @@ export const useUserStore = defineStore("admin-user", {
34
41
  setToken(token: string) {
35
42
  this.token = token;
36
43
  },
37
- /** 设置刷新令牌 */
38
- setRefreshToken(refreshToken: string) {
39
- this.refreshToken = refreshToken;
40
- },
41
44
  /** 设置令牌类型 */
42
45
  setTokenType(tokenType: string) {
43
46
  this.tokenType = tokenType;
@@ -51,12 +54,12 @@ export const useUserStore = defineStore("admin-user", {
51
54
  this.userInfo = userInfo;
52
55
  },
53
56
  /** 根据接口返回统一更新令牌信息 */
54
- updateTokenAuth(accessToken: string, refreshToken: string, tokenType: string, expiresIn?: number) {
57
+ updateTokenAuth(accessToken: string, tokenType: string, expiresIn?: number) {
55
58
  const tokenPrefix = tokenType ? `${tokenType} ` : "";
56
59
  const expiresAt = expiresIn ? Date.now() + expiresIn * 1000 : 0;
57
60
 
61
+ this.authInvalidated = false;
58
62
  this.setToken(`${tokenPrefix}${accessToken}`.trim());
59
- this.setRefreshToken(refreshToken ?? "");
60
63
  this.setTokenType(tokenType ?? "");
61
64
  this.setTokenExpiresAt(expiresAt);
62
65
  },
@@ -65,18 +68,23 @@ export const useUserStore = defineStore("admin-user", {
65
68
  return Boolean(this.token.trim() && this.tokenExpiresAt > Date.now());
66
69
  },
67
70
  /** 登录 */
68
- async login(loginRequest: LoginRequest) {
71
+ async login(loginRequest: LoginRequest): Promise<LoginResponse> {
69
72
  const data = await defLoginService.Login(loginRequest);
70
- this.updateTokenAuth(data.access_token, data.refresh_token ?? "", data.token_type ?? "", data.expires_in);
73
+ if (data.status === 1 || data.status === 0 || data.status === 4) {
74
+ this.updateTokenAuth(data.access_token, data.token_type ?? "", data.expires_in);
75
+ }
76
+ return data;
71
77
  },
72
- /** 刷新认证令牌 */
78
+ /** 校验登录阶段的多因素认证并保存正式令牌。 */
79
+ async verifyMfa(request: VerifyMfaRequest): Promise<LoginResponse> {
80
+ const data = await defMfaService.VerifyMfa(request);
81
+ this.updateTokenAuth(data.access_token, data.token_type ?? "", data.expires_in);
82
+ return data;
83
+ },
84
+ /** 通过 HttpOnly Cookie 刷新认证令牌。 */
73
85
  async refreshAccessToken() {
74
- if (!this.refreshToken) {
75
- return Promise.reject(new Error(t("core.auth.refresh_token_missing")));
76
- }
77
-
78
- const data = await defLoginService.RefreshToken({ refresh_token: this.refreshToken });
79
- this.updateTokenAuth(data.access_token, data.refresh_token ?? this.refreshToken, data.token_type ?? "", data.expires_in);
86
+ const data = await defLoginService.RefreshToken({});
87
+ this.updateTokenAuth(data.access_token, data.token_type ?? "", data.expires_in);
80
88
  return data;
81
89
  },
82
90
  /** 获取用户信息 */
@@ -89,22 +97,26 @@ export const useUserStore = defineStore("admin-user", {
89
97
  clearAuthData() {
90
98
  // 清理登录态时同步清空字典缓存,避免切换账号后读到旧字典。
91
99
  useDictStoreHook().clearDictionaryCache();
100
+ useLockScreenStore().clearLock();
101
+ this.authVersion += 1;
102
+ this.authInvalidated = true;
92
103
  this.setToken("");
93
- this.setRefreshToken("");
94
104
  this.setTokenType("");
95
105
  this.setTokenExpiresAt(0);
96
106
  this.setUserInfo({ ...defaultUserInfo });
97
107
  },
98
108
  /** 退出登录 */
99
109
  async logout() {
110
+ this.isLoggingOut = true;
100
111
  try {
101
112
  await defLoginService.Logout({});
102
113
  } catch {
103
114
  // 退出接口返回异常时,前端仍需清理本地登录态,避免用户卡在当前会话。
104
115
  } finally {
105
116
  this.clearAuthData();
117
+ this.isLoggingOut = false;
106
118
  }
107
119
  }
108
120
  },
109
- persist: piniaPersistConfig("admin-user")
121
+ persist: piniaPersistConfig("admin-user-security-v2", ["userInfo"])
110
122
  });
@@ -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";
@@ -1,3 +1,9 @@
1
+ /* 统一 Element Plus 矩形控件与后台页面的圆角。 */
2
+ html:root {
3
+ --el-border-radius-base: var(--admin-page-radius);
4
+ --el-border-radius-small: 2px;
5
+ }
6
+
1
7
  /* el-alert */
2
8
  .el-alert {
3
9
  border: 1px solid;
@@ -27,7 +33,18 @@
27
33
  overflow-x: hidden;
28
34
  background-color: var(--el-bg-color);
29
35
  border: 1px solid var(--el-border-color-light);
30
- border-radius: 6px;
36
+ border-radius: var(--admin-page-radius);
37
+ box-shadow: 0 0 12px rgb(0 0 0 / 5%);
38
+ }
39
+
40
+ /* 非列表业务页沿用 ProTable 卡片外观 */
41
+ .el-card.admin-page-card {
42
+ background-color: var(--el-bg-color);
43
+ border: 1px solid var(--el-border-color-light);
44
+ border-radius: var(--admin-page-radius);
45
+ box-shadow: 0 0 12px rgb(0 0 0 / 5%);
46
+ }
47
+ .el-card.admin-page-card.is-always-shadow {
31
48
  box-shadow: 0 0 12px rgb(0 0 0 / 5%);
32
49
  }
33
50
 
@@ -165,10 +182,9 @@
165
182
  }
166
183
  }
167
184
 
168
- // 设置 el-table 中 header 文字不换行,并省略
185
+ // 设置 el-table 表头文字单行省略,完整内容由列标题悬浮提示展示。
169
186
  .el-table__header .el-table__cell > .cell {
170
- // white-space: nowrap;
171
- white-space: wrap;
187
+ white-space: nowrap;
172
188
  }
173
189
 
174
190
  // 空表时去掉底部那条默认分隔线,和菜单管理页保持一致。
@@ -328,6 +344,21 @@
328
344
  }
329
345
  }
330
346
 
347
+ // ProDialog 需要去掉 Element Plus 默认的外层内边距,避免异步样式重复加载后覆盖紧凑布局。
348
+ .el-dialog.pro-dialog {
349
+ padding: 0;
350
+
351
+ // 对齐原生 MessageBox 的紧凑上下间距,避免标题和操作区留白过大。
352
+ .el-dialog__header,
353
+ .el-dialog__body {
354
+ padding: 12px 20px;
355
+ }
356
+
357
+ .el-dialog__footer {
358
+ padding: 0 20px 12px;
359
+ }
360
+ }
361
+
331
362
  @media screen and (width <= 768px) {
332
363
  .el-dialog {
333
364
  margin-top: 4vh;
@@ -0,0 +1,22 @@
1
+ /** 将文本复制到系统剪贴板,并兼容非安全上下文的管理端开发地址。 */
2
+ export async function copyText(content: string): Promise<void> {
3
+ if (typeof navigator !== "undefined" && navigator.clipboard && typeof window !== "undefined" && window.isSecureContext) {
4
+ try {
5
+ await navigator.clipboard.writeText(content);
6
+ return;
7
+ } catch {
8
+ // Clipboard API 受权限限制时继续尝试传统 DOM 方案。
9
+ }
10
+ }
11
+ if (typeof document === "undefined") throw new Error("copy failed");
12
+ const textarea = document.createElement("textarea");
13
+ textarea.value = content;
14
+ textarea.style.position = "fixed";
15
+ textarea.style.opacity = "0";
16
+ document.body.appendChild(textarea);
17
+ textarea.focus();
18
+ textarea.select();
19
+ const copied = document.execCommand("copy");
20
+ document.body.removeChild(textarea);
21
+ if (!copied) throw new Error("copy failed");
22
+ }
@@ -1,4 +1,4 @@
1
- import { defLoginService } from "../api/base/login";
1
+ import { defLoginService } from "../api/base/v1/login";
2
2
  import { PasswordCryptoScene } from "../rpc/base/v1/login";
3
3
  import type { PasswordCrypto } from "../rpc/common/v1/types";
4
4
  import { t } from "../locales";
@@ -9,12 +9,10 @@ export interface PasswordStrengthResult {
9
9
  strengthScore: number;
10
10
  /** 强度等级。 */
11
11
  level: PasswordStrengthLevel;
12
- /** 是否达到允许提交的最高强度。 */
13
- isValid: boolean;
14
12
  }
15
13
 
16
14
  /**
17
- * 计算密码强度结果,供表单展示和校验统一复用。
15
+ * 计算密码强度结果,供表单展示使用;最终规则由服务端密码策略校验。
18
16
  *
19
17
  * @param password 当前密码
20
18
  * @returns 密码强度分析结果
@@ -24,14 +22,13 @@ export function getPasswordStrength(password?: string): PasswordStrengthResult {
24
22
  return {
25
23
  ruleScore: 0,
26
24
  strengthScore: 0,
27
- level: "empty",
28
- isValid: false
25
+ level: "empty"
29
26
  };
30
27
  }
31
28
 
32
29
  let ruleScore = 0;
33
- if (password.length >= 8) ruleScore += 1;
34
- if (/[a-z]/.test(password) && /[A-Z]/.test(password)) ruleScore += 1;
30
+ if (/[a-z]/.test(password)) ruleScore += 1;
31
+ if (/[A-Z]/.test(password)) ruleScore += 1;
35
32
  if (/\d/.test(password)) ruleScore += 1;
36
33
  if (/[^A-Za-z0-9]/.test(password)) ruleScore += 1;
37
34
 
@@ -39,33 +36,19 @@ export function getPasswordStrength(password?: string): PasswordStrengthResult {
39
36
  return {
40
37
  ruleScore,
41
38
  strengthScore: 3,
42
- level: "high",
43
- isValid: true
39
+ level: "high"
44
40
  };
45
41
  }
46
42
  if (ruleScore === 3) {
47
43
  return {
48
44
  ruleScore,
49
45
  strengthScore: 2,
50
- level: "medium",
51
- isValid: false
46
+ level: "medium"
52
47
  };
53
48
  }
54
49
  return {
55
50
  ruleScore,
56
51
  strengthScore: 1,
57
- level: "low",
58
- isValid: false
52
+ level: "low"
59
53
  };
60
54
  }
61
-
62
- /**
63
- * 校验密码是否达到最高强度,便于表单规则直接复用。
64
- *
65
- * @param password 当前密码
66
- * @returns 校验结果
67
- */
68
- export function validatePasswordStrengthValue(password?: string) {
69
- const result = getPasswordStrength(password);
70
- return { valid: result.isValid };
71
- }