@liujitcn/kratos-admin-system 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.
Files changed (86) hide show
  1. package/README.md +51 -50
  2. package/dist/package/declarations/src/api/system/base_language.d.ts +51 -0
  3. package/dist/package/declarations/src/api/system/base_translation.d.ts +7 -0
  4. package/dist/package/declarations/src/api/system/code_gen_i18n.d.ts +8 -0
  5. package/dist/package/declarations/src/components/DynamicTranslationCell.vue.d.ts +39 -0
  6. package/dist/package/declarations/src/components/DynamicTranslationEditor.vue.d.ts +23 -0
  7. package/dist/package/declarations/src/components/dynamicTranslation.d.ts +44 -0
  8. package/dist/package/declarations/src/rpc/system/admin/v1/base_config.d.ts +5 -0
  9. package/dist/package/declarations/src/rpc/system/admin/v1/base_dict.d.ts +79 -0
  10. package/dist/package/declarations/src/rpc/system/admin/v1/base_language.d.ts +114 -0
  11. package/dist/package/declarations/src/rpc/system/admin/v1/base_menu.d.ts +40 -0
  12. package/dist/package/declarations/src/rpc/system/admin/v1/base_translation.d.ts +102 -0
  13. package/dist/package/declarations/src/rpc/system/admin/v1/code_gen.d.ts +2 -0
  14. package/dist/package/declarations/src/rpc/system/admin/v1/code_gen_column.d.ts +7 -0
  15. package/dist/package/declarations/src/rpc/system/admin/v1/code_gen_table.d.ts +13 -0
  16. package/dist/package/declarations/src/views/tool/code-gen/components/CodeGenLocaleEditor.vue.d.ts +23 -0
  17. package/dist/package/declarations/src/views/tool/code-gen/config.d.ts +7 -7
  18. package/dist/package/declarations/src/views/tool/language/index.vue.d.ts +3 -0
  19. package/dist/package/src/api/base/ai_message.ts +7 -12
  20. package/dist/package/src/api/base/sse.ts +3 -1
  21. package/dist/package/src/api/system/base_language.ts +137 -0
  22. package/dist/package/src/api/system/base_translation.ts +22 -0
  23. package/dist/package/src/api/system/code_gen_column.ts +17 -3
  24. package/dist/package/src/api/system/code_gen_i18n.ts +18 -0
  25. package/dist/package/src/api/system/code_gen_table.ts +12 -4
  26. package/dist/package/src/components/DynamicTranslationCell.vue +176 -0
  27. package/dist/package/src/components/DynamicTranslationEditor.vue +166 -0
  28. package/dist/package/src/components/dynamicTranslation.ts +86 -0
  29. package/dist/package/src/locales/README.md +28 -0
  30. package/dist/package/src/locales/en-US.json +929 -0
  31. package/dist/package/src/locales/es-ES.json +928 -0
  32. package/dist/package/src/locales/fr-FR.json +928 -0
  33. package/dist/package/src/locales/ja-JP.json +929 -0
  34. package/dist/package/src/locales/ko-KR.json +928 -0
  35. package/dist/package/src/locales/zh-CN.json +929 -0
  36. package/dist/package/src/locales/zh-TW.json +928 -0
  37. package/dist/package/src/module.ts +19 -1
  38. package/dist/package/src/rpc/system/admin/v1/base_config.ts +5 -0
  39. package/dist/package/src/rpc/system/admin/v1/base_dict.ts +81 -0
  40. package/dist/package/src/rpc/system/admin/v1/base_language.ts +135 -0
  41. package/dist/package/src/rpc/system/admin/v1/base_menu.ts +41 -0
  42. package/dist/package/src/rpc/system/admin/v1/base_translation.ts +117 -0
  43. package/dist/package/src/rpc/system/admin/v1/code_gen.ts +5 -1
  44. package/dist/package/src/rpc/system/admin/v1/code_gen_column.ts +8 -0
  45. package/dist/package/src/rpc/system/admin/v1/code_gen_table.ts +15 -0
  46. package/dist/package/src/views/ai/chat/components/AiMarkdown.vue +1 -6
  47. package/dist/package/src/views/ai/chat/components/ChatPanel.vue +70 -53
  48. package/dist/package/src/views/ai/chat/components/SessionPanel.vue +20 -9
  49. package/dist/package/src/views/ai/chat/components/XSender.vue +36 -23
  50. package/dist/package/src/views/ai/chat/index.vue +55 -39
  51. package/dist/package/src/views/ai/chat/message.ts +18 -17
  52. package/dist/package/src/views/base/api/index.vue +121 -83
  53. package/dist/package/src/views/base/area/index.vue +81 -31
  54. package/dist/package/src/views/base/config/index.vue +272 -78
  55. package/dist/package/src/views/base/dept/index.vue +110 -61
  56. package/dist/package/src/views/base/dict/index.vue +137 -58
  57. package/dist/package/src/views/base/dict/item.vue +147 -61
  58. package/dist/package/src/views/base/job/index.vue +134 -81
  59. package/dist/package/src/views/base/job/log.vue +21 -22
  60. package/dist/package/src/views/base/log/index.vue +68 -46
  61. package/dist/package/src/views/base/menu/index.vue +257 -156
  62. package/dist/package/src/views/base/migration/index.vue +49 -29
  63. package/dist/package/src/views/base/post/index.vue +128 -59
  64. package/dist/package/src/views/base/role/index.vue +145 -78
  65. package/dist/package/src/views/base/tenant/index.vue +132 -63
  66. package/dist/package/src/views/base/user/components/dept-tree.vue +2 -1
  67. package/dist/package/src/views/base/user/index.vue +192 -112
  68. package/dist/package/src/views/profile/components/base.vue +44 -27
  69. package/dist/package/src/views/profile/components/password.vue +43 -27
  70. package/dist/package/src/views/profile/components/security.vue +89 -53
  71. package/dist/package/src/views/profile/index.vue +10 -9
  72. package/dist/package/src/views/tool/api-doc/index.vue +6 -2
  73. package/dist/package/src/views/tool/code-gen/code-preview/index.vue +30 -14
  74. package/dist/package/src/views/tool/code-gen/columns/index.vue +382 -273
  75. package/dist/package/src/views/tool/code-gen/columns/option-copy.ts +1 -6
  76. package/dist/package/src/views/tool/code-gen/components/CodeGenLocaleEditor.vue +118 -0
  77. package/dist/package/src/views/tool/code-gen/components/CodeGenProgressDialog.vue +65 -36
  78. package/dist/package/src/views/tool/code-gen/components/CodePreviewPane.vue +8 -5
  79. package/dist/package/src/views/tool/code-gen/config.ts +114 -77
  80. package/dist/package/src/views/tool/code-gen/preview/data.ts +19 -17
  81. package/dist/package/src/views/tool/code-gen/preview/index.vue +96 -58
  82. package/dist/package/src/views/tool/code-gen/proto/index.vue +110 -62
  83. package/dist/package/src/views/tool/code-gen/table/index.vue +197 -117
  84. package/dist/package/src/views/tool/language/index.vue +256 -0
  85. package/dist/package/src/views/tool/project-doc/index.vue +17 -15
  86. package/package.json +6 -2
@@ -3,10 +3,10 @@
3
3
  <template #header>
4
4
  <div class="panel-header">
5
5
  <div>
6
- <h3>账号信息</h3>
7
- <p>维护头像、昵称和基础账号资料。</p>
6
+ <h3>{{ t("system.profile.account.title") }}</h3>
7
+ <p>{{ t("system.profile.account.description") }}</p>
8
8
  </div>
9
- <el-button type="primary" plain @click="openAccountDialog">修改基本资料</el-button>
9
+ <el-button type="primary" plain @click="openAccountDialog">{{ t("system.profile.account.action.edit") }}</el-button>
10
10
  </div>
11
11
  </template>
12
12
 
@@ -18,49 +18,56 @@
18
18
  <input ref="fileInputRef" type="file" class="hidden-input" accept="image/*" @change="handleFileChange" />
19
19
  </div>
20
20
  <div class="avatar-copy">
21
- <strong>头像</strong>
22
- <p>点击右下角可更换头像</p>
21
+ <strong>{{ t("system.profile.account.field.avatar") }}</strong>
22
+ <p>{{ t("system.profile.account.message.avatarHint") }}</p>
23
23
  </div>
24
24
  </div>
25
25
 
26
26
  <div class="detail-grid">
27
27
  <div class="detail-item">
28
- <span class="detail-label">登录账号</span>
28
+ <span class="detail-label">{{ t("system.profile.account.field.userName") }}</span>
29
29
  <span class="detail-value">{{ profile.user_name || "--" }}</span>
30
30
  </div>
31
31
  <div class="detail-item">
32
- <span class="detail-label">昵称</span>
32
+ <span class="detail-label">{{ t("system.profile.account.field.nickName") }}</span>
33
33
  <span class="detail-value">{{ profile.nick_name || "--" }}</span>
34
34
  </div>
35
35
  <div class="detail-item">
36
- <span class="detail-label">性别</span>
36
+ <span class="detail-label">{{ t("system.profile.account.field.gender") }}</span>
37
37
  <span class="detail-value">{{ genderText }}</span>
38
38
  </div>
39
39
  <div class="detail-item">
40
- <span class="detail-label">绑定手机</span>
41
- <span class="detail-value">{{ profile.phone || "未绑定" }}</span>
40
+ <span class="detail-label">{{ t("system.profile.account.field.phone") }}</span>
41
+ <span class="detail-value">{{ profile.phone || t("system.profile.value.unbound") }}</span>
42
42
  </div>
43
43
  <div class="detail-item">
44
- <span class="detail-label">所属角色</span>
44
+ <span class="detail-label">{{ t("system.profile.account.field.role") }}</span>
45
45
  <span class="detail-value">{{ profile.role_name || "--" }}</span>
46
46
  </div>
47
47
  <div class="detail-item">
48
- <span class="detail-label">所属部门</span>
48
+ <span class="detail-label">{{ t("system.profile.account.field.dept") }}</span>
49
49
  <span class="detail-value">{{ profile.dept_name || "--" }}</span>
50
50
  </div>
51
51
  <div class="detail-item detail-item--wide">
52
- <span class="detail-label">创建时间</span>
52
+ <span class="detail-label">{{ t("system.common.field.createdAt") }}</span>
53
53
  <span class="detail-value">{{ profile.created_at || "--" }}</span>
54
54
  </div>
55
55
  </div>
56
56
  </div>
57
57
 
58
- <ProDialog v-model="accountDialogVisible" title="修改基本资料" :width="520" @closed="handleDialogClosed">
58
+ <ProDialog
59
+ v-model="accountDialogVisible"
60
+ :title="t('system.profile.account.action.edit')"
61
+ :width="520"
62
+ @closed="handleDialogClosed"
63
+ >
59
64
  <ProForm ref="accountFormRef" :model="accountForm" :fields="accountFormFields" label-width="96px" />
60
65
  <template #footer>
61
66
  <div class="dialog-footer">
62
- <el-button @click="accountDialogVisible = false">取消</el-button>
63
- <el-button type="primary" :loading="submitLoading" @click="handleSubmitAccount">保存</el-button>
67
+ <el-button @click="accountDialogVisible = false">{{ t("common.action.cancel") }}</el-button>
68
+ <el-button type="primary" :loading="submitLoading" @click="handleSubmitAccount">
69
+ {{ t("common.action.save") }}
70
+ </el-button>
64
71
  </div>
65
72
  </template>
66
73
  </ProDialog>
@@ -69,6 +76,7 @@
69
76
 
70
77
  <script setup lang="ts">
71
78
  import { computed, reactive, ref, watch } from "vue";
79
+ import { t } from "@liujitcn/kratos-admin-core";
72
80
  import { defProfileAuthService } from "@liujitcn/kratos-admin-system/api/system/auth";
73
81
  import type { UserProfileForm } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/auth";
74
82
  import { defFileService } from "@liujitcn/kratos-admin-core/api/base/file";
@@ -76,7 +84,6 @@ import defaultAvatar from "@liujitcn/kratos-admin-core/assets/images/avatar.png"
76
84
  import ProDialog from "@liujitcn/kratos-admin-core/components/Dialog/ProDialog.vue";
77
85
  import ProForm from "@liujitcn/kratos-admin-core/components/ProForm/index.vue";
78
86
  import type { ProFormField, ProFormInstance } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
79
- import { ElMessage } from "element-plus";
80
87
  import { Camera } from "@element-plus/icons-vue";
81
88
 
82
89
  /** 个人中心基础资料组件属性。 */
@@ -101,16 +108,26 @@ const accountForm = reactive<Pick<UserProfileForm, "nick_name" | "gender">>({
101
108
  gender: 3
102
109
  });
103
110
 
104
- const accountFormFields: ProFormField[] = [
105
- { prop: "nick_name", label: "昵称", component: "input", props: { placeholder: "请输入昵称" } },
106
- { prop: "gender", label: "性别", component: "dict", props: { code: "base_user_gender" } }
107
- ];
111
+ const accountFormFields = computed<ProFormField[]>(() => [
112
+ {
113
+ prop: "nick_name",
114
+ label: t("system.profile.account.field.nickName"),
115
+ component: "input",
116
+ props: { placeholder: t("system.profile.account.placeholder.nickName") }
117
+ },
118
+ {
119
+ prop: "gender",
120
+ label: t("system.profile.account.field.gender"),
121
+ component: "dict",
122
+ props: { code: "base_user_gender" }
123
+ }
124
+ ]);
108
125
 
109
126
  /** 根据资料中的性别值输出展示文案。 */
110
127
  const genderText = computed(() => {
111
- if (props.profile.gender === 1) return "男";
112
- if (props.profile.gender === 2) return "女";
113
- return "保密";
128
+ if (props.profile.gender === 1) return t("common.value.male");
129
+ if (props.profile.gender === 2) return t("common.value.female");
130
+ return t("system.profile.value.private");
114
131
  });
115
132
 
116
133
  /**
@@ -165,10 +182,10 @@ async function handleFileChange(event: Event) {
165
182
  avatar: uploadResult.url
166
183
  }
167
184
  });
168
- ElMessage.success("头像更新成功");
185
+ ElMessage.success(t("system.profile.account.message.avatarUpdated"));
169
186
  emit("refreshed");
170
187
  } catch (_error) {
171
- ElMessage.error("头像上传失败");
188
+ ElMessage.error(t("system.profile.account.message.avatarUploadFailed"));
172
189
  } finally {
173
190
  target.value = "";
174
191
  }
@@ -187,7 +204,7 @@ async function handleSubmitAccount() {
187
204
  gender: accountForm.gender
188
205
  }
189
206
  });
190
- ElMessage.success("基本资料已更新");
207
+ ElMessage.success(t("system.profile.account.message.updated"));
191
208
  accountDialogVisible.value = false;
192
209
  emit("refreshed");
193
210
  } finally {
@@ -4,10 +4,10 @@
4
4
  <template #header>
5
5
  <div class="panel-header">
6
6
  <div>
7
- <h3>修改密码</h3>
8
- <p>更新当前登录密码,保持账号安全。</p>
7
+ <h3>{{ t("system.profile.password.title") }}</h3>
8
+ <p>{{ t("system.profile.password.description") }}</p>
9
9
  </div>
10
- <el-tag type="warning" effect="plain">建议高强度</el-tag>
10
+ <el-tag type="warning" effect="plain">{{ t("system.profile.password.value.strongRecommended") }}</el-tag>
11
11
  </div>
12
12
  </template>
13
13
  <div class="password-layout">
@@ -21,25 +21,27 @@
21
21
  />
22
22
  <PasswordStrength :password="passwordForm.new_pwd" class="password-form-wrap__strength" />
23
23
  <div class="password-footer">
24
- <el-button @click="resetPasswordForm">重置</el-button>
25
- <el-button type="primary" :loading="submitLoading" @click="handleSubmitPassword">更新密码</el-button>
24
+ <el-button @click="resetPasswordForm">{{ t("common.action.reset") }}</el-button>
25
+ <el-button type="primary" :loading="submitLoading" @click="handleSubmitPassword">
26
+ {{ t("system.profile.password.action.update") }}
27
+ </el-button>
26
28
  </div>
27
29
  </div>
28
30
  <div class="password-tips">
29
31
  <div class="tip-card">
30
32
  <span class="tip-badge">01</span>
31
- <strong>避免重复密码</strong>
32
- <p>不要重复使用旧密码或其他系统已使用密码。</p>
33
+ <strong>{{ t("system.profile.password.tip.uniqueTitle") }}</strong>
34
+ <p>{{ t("system.profile.password.tip.uniqueDescription") }}</p>
33
35
  </div>
34
36
  <div class="tip-card">
35
37
  <span class="tip-badge">02</span>
36
- <strong>控制密码强度</strong>
37
- <p>建议使用大小写字母、数字和符号组合。</p>
38
+ <strong>{{ t("system.profile.password.tip.strengthTitle") }}</strong>
39
+ <p>{{ t("system.profile.password.tip.strengthDescription") }}</p>
38
40
  </div>
39
41
  <div class="tip-card">
40
42
  <span class="tip-badge">03</span>
41
- <strong>及时更新记录</strong>
42
- <p>如有账号交接,请同步更新保管记录。</p>
43
+ <strong>{{ t("system.profile.password.tip.recordTitle") }}</strong>
44
+ <p>{{ t("system.profile.password.tip.recordDescription") }}</p>
43
45
  </div>
44
46
  </div>
45
47
  </div>
@@ -50,6 +52,7 @@
50
52
  <script setup lang="ts">
51
53
  import { computed, reactive, ref } from "vue";
52
54
  import { useRouter } from "vue-router";
55
+ import { t } from "@liujitcn/kratos-admin-core";
53
56
  import { defProfileAuthService } from "@liujitcn/kratos-admin-system/api/system/auth";
54
57
  import type { UserPasswordForm } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/auth";
55
58
  import PasswordStrength from "@liujitcn/kratos-admin-core/components/PasswordStrength/index.vue";
@@ -57,10 +60,8 @@ import ProForm from "@liujitcn/kratos-admin-core/components/ProForm/index.vue";
57
60
  import type { ProFormField, ProFormInstance } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
58
61
  import { LOGIN_URL } from "@liujitcn/kratos-admin-core/config";
59
62
  import { useUserStore } from "@liujitcn/kratos-admin-core/stores/runtime";
60
- import { ElMessage } from "element-plus";
61
63
  import {
62
64
  PASSWORD_CRYPTO_SCENE,
63
- PASSWORD_STRENGTH_ERROR_MESSAGE,
64
65
  encryptPassword,
65
66
  getPasswordStrength,
66
67
  validatePasswordStrengthValue
@@ -86,20 +87,35 @@ const passwordForm = reactive<UserPasswordFormState>({
86
87
  confirm_pwd: ""
87
88
  });
88
89
 
89
- const passwordFormFields: ProFormField[] = [
90
- { prop: "old_pwd", label: "原密码", component: "password", props: { placeholder: "请输入原密码" } },
91
- { prop: "new_pwd", label: "新密码", component: "password", props: { placeholder: "请输入新密码" } },
92
- { prop: "confirm_pwd", label: "确认密码", component: "password", props: { placeholder: "请再次输入新密码" } }
93
- ];
90
+ const passwordFormFields = computed<ProFormField[]>(() => [
91
+ {
92
+ prop: "old_pwd",
93
+ label: t("system.profile.password.field.oldPassword"),
94
+ component: "password",
95
+ props: { placeholder: t("system.profile.password.placeholder.oldPassword") }
96
+ },
97
+ {
98
+ prop: "new_pwd",
99
+ label: t("system.profile.password.field.newPassword"),
100
+ component: "password",
101
+ props: { placeholder: t("system.profile.password.placeholder.newPassword") }
102
+ },
103
+ {
104
+ prop: "confirm_pwd",
105
+ label: t("system.profile.password.field.confirmPassword"),
106
+ component: "password",
107
+ props: { placeholder: t("system.profile.password.placeholder.confirmPassword") }
108
+ }
109
+ ]);
94
110
 
95
- const passwordFormRules = {
96
- old_pwd: [{ required: true, message: "请输入原密码", trigger: "blur" }],
111
+ const passwordFormRules = computed(() => ({
112
+ old_pwd: [{ required: true, message: t("system.profile.password.placeholder.oldPassword"), trigger: "blur" }],
97
113
  new_pwd: [
98
- { required: true, message: "请输入新密码", trigger: "blur" },
114
+ { required: true, message: t("system.profile.password.placeholder.newPassword"), trigger: "blur" },
99
115
  { validator: validatePasswordStrength, trigger: "blur" }
100
116
  ],
101
- confirm_pwd: [{ required: true, message: "请再次输入新密码", trigger: "blur" }]
102
- };
117
+ confirm_pwd: [{ required: true, message: t("system.profile.password.placeholder.confirmPassword"), trigger: "blur" }]
118
+ }));
103
119
 
104
120
  /** 统一计算当前新密码强度,供展示和提交校验复用。 */
105
121
  const passwordStrength = computed(() => getPasswordStrength(passwordForm.new_pwd));
@@ -108,11 +124,11 @@ const passwordStrength = computed(() => getPasswordStrength(passwordForm.new_pwd
108
124
  async function handleSubmitPassword() {
109
125
  if (!(await passwordFormRef.value?.validate())) return;
110
126
  if (passwordForm.new_pwd !== passwordForm.confirm_pwd) {
111
- ElMessage.error("两次输入的密码不一致");
127
+ ElMessage.error(t("system.profile.password.message.mismatch"));
112
128
  return;
113
129
  }
114
130
  if (!passwordStrength.value.isValid) {
115
- ElMessage.error("新密码强度不足,请提升到最高强度");
131
+ ElMessage.error(t("system.profile.password.message.strengthInsufficient"));
116
132
  return;
117
133
  }
118
134
 
@@ -125,7 +141,7 @@ async function handleSubmitPassword() {
125
141
  new_pwd: newPwd
126
142
  };
127
143
  await defProfileAuthService.UpdateUserPassword({ user_password: userPassword });
128
- ElMessage.success("密码修改成功,请重新登录");
144
+ ElMessage.success(t("system.profile.password.message.updated"));
129
145
  resetPasswordForm();
130
146
  await forceRelogin();
131
147
  } finally {
@@ -167,7 +183,7 @@ function validatePasswordStrength(_rule: unknown, value: string, callback: (erro
167
183
  }
168
184
  const result = validatePasswordStrengthValue(value);
169
185
  if (!result.valid) {
170
- callback(new Error(result.message || PASSWORD_STRENGTH_ERROR_MESSAGE));
186
+ callback(new Error(t("core.password.error")));
171
187
  return;
172
188
  }
173
189
  callback();
@@ -3,36 +3,44 @@
3
3
  <el-card class="security-card" shadow="never">
4
4
  <div class="security-intro">
5
5
  <div>
6
- <h3>安全设置</h3>
7
- <p>管理登录密码、手机验证和账号完整度。</p>
6
+ <h3>{{ t("system.profile.security.title") }}</h3>
7
+ <p>{{ t("system.profile.security.description") }}</p>
8
8
  </div>
9
- <el-tag effect="plain" type="success">账号状态正常</el-tag>
9
+ <el-tag effect="plain" type="success">{{ t("system.profile.security.value.accountNormal") }}</el-tag>
10
10
  </div>
11
11
  <div class="security-list">
12
12
  <div class="security-item">
13
13
  <div class="security-item__content">
14
- <strong>登录密码</strong>
15
- <p>建议定期更新,避免长期使用同一密码。</p>
14
+ <strong>{{ t("system.profile.security.field.loginPassword") }}</strong>
15
+ <p>{{ t("system.profile.security.message.passwordHint") }}</p>
16
16
  </div>
17
- <el-button type="primary" plain @click="emit('switchTab', 'password')">前往修改</el-button>
17
+ <el-button type="primary" plain @click="emit('switchTab', 'password')">
18
+ {{ t("system.profile.security.action.changePassword") }}
19
+ </el-button>
18
20
  </div>
19
21
  <div class="security-item">
20
22
  <div class="security-item__content">
21
- <strong>绑定手机</strong>
23
+ <strong>{{ t("system.profile.security.field.phone") }}</strong>
22
24
  <p>{{ mobileTip }}</p>
23
25
  </div>
24
- <el-button plain @click="openPhoneDialog">{{ profile.phone ? "更换手机号" : "立即绑定" }}</el-button>
26
+ <el-button plain @click="openPhoneDialog">
27
+ {{ profile.phone ? t("system.profile.security.action.changePhone") : t("system.profile.security.action.bindNow") }}
28
+ </el-button>
25
29
  </div>
26
30
  <div v-for="item in oauthBindings" :key="item.provider" class="security-item">
27
31
  <div class="security-item__content security-item__content--oauth">
28
- <el-tooltip :content="item.name" placement="top" :trigger="['hover', 'focus']">
29
- <span class="oauth-icon" :aria-label="item.name" :title="item.name">
32
+ <el-tooltip :content="oauthName(item)" placement="top" :trigger="['hover', 'focus']">
33
+ <span class="oauth-icon" :aria-label="oauthName(item)" :title="oauthName(item)">
30
34
  <component :is="getOauthProviderIcon(item)" />
31
35
  </span>
32
36
  </el-tooltip>
33
37
  <div>
34
- <strong>{{ item.name }}</strong>
35
- <p>{{ item.bound ? "已绑定,可用于登录管理后台。" : "未绑定,绑定成功后可用于登录管理后台。" }}</p>
38
+ <strong>{{ oauthName(item) }}</strong>
39
+ <p>
40
+ {{
41
+ item.bound ? t("system.profile.security.message.oauthBound") : t("system.profile.security.message.oauthUnbound")
42
+ }}
43
+ </p>
36
44
  </div>
37
45
  </div>
38
46
  <el-button
@@ -42,11 +50,11 @@
42
50
  :loading="oauthLoadingProvider === item.provider"
43
51
  @click="handleUnbindOauth(item)"
44
52
  >
45
- 解绑
53
+ {{ t("system.profile.security.action.unbind") }}
46
54
  </el-button>
47
- <el-button v-else plain :loading="oauthLoadingProvider === item.provider" @click="handleBindOauth(item)"
48
- >绑定</el-button
49
- >
55
+ <el-button v-else plain :loading="oauthLoadingProvider === item.provider" @click="handleBindOauth(item)">{{
56
+ t("system.profile.security.action.bind")
57
+ }}</el-button>
50
58
  </div>
51
59
  </div>
52
60
  </el-card>
@@ -55,30 +63,39 @@
55
63
  <template #header>
56
64
  <div class="status-header">
57
65
  <div>
58
- <h3>账号状态</h3>
59
- <p>查看当前账号验证状态与资料完整度。</p>
66
+ <h3>{{ t("system.profile.security.status.title") }}</h3>
67
+ <p>{{ t("system.profile.security.status.description") }}</p>
60
68
  </div>
61
69
  </div>
62
70
  </template>
63
71
  <div class="status-grid">
64
72
  <div class="status-item">
65
- <span>手机验证</span>
66
- <strong>{{ profile.phone ? "已启用" : "未启用" }}</strong>
73
+ <span>{{ t("system.profile.security.field.phoneVerification") }}</span>
74
+ <strong>{{ profile.phone ? t("common.status.enabled") : t("common.status.disabled") }}</strong>
67
75
  </div>
68
76
  <div class="status-item">
69
- <span>资料完整度</span>
77
+ <span>{{ t("system.profile.security.field.profileCompletion") }}</span>
70
78
  <strong>{{ profileCompletion }}</strong>
71
79
  </div>
72
80
  </div>
73
81
  </el-card>
74
82
 
75
- <ProDialog v-model="phoneDialogVisible" title="绑定手机" :width="520" @closed="handleDialogClosed">
83
+ <ProDialog
84
+ v-model="phoneDialogVisible"
85
+ :title="t('system.profile.security.dialog.bindPhone')"
86
+ :width="520"
87
+ @closed="handleDialogClosed"
88
+ >
76
89
  <ProForm ref="phoneFormRef" :model="phoneForm" :fields="phoneFormFields" :rules="phoneFormRules" label-width="96px">
77
90
  <template #mobileCodeInput>
78
- <el-input v-model="phoneForm.code" placeholder="请输入验证码">
91
+ <el-input v-model="phoneForm.code" :placeholder="t('system.profile.security.placeholder.code')">
79
92
  <template #append>
80
93
  <el-button :disabled="countdown > 0" @click="handleSendCode">
81
- {{ countdown > 0 ? `${countdown}s后重试` : "发送验证码" }}
94
+ {{
95
+ countdown > 0
96
+ ? t("system.profile.security.action.retryAfter", { seconds: countdown })
97
+ : t("system.profile.security.action.sendCode")
98
+ }}
82
99
  </el-button>
83
100
  </template>
84
101
  </el-input>
@@ -86,8 +103,10 @@
86
103
  </ProForm>
87
104
  <template #footer>
88
105
  <div class="dialog-footer">
89
- <el-button @click="phoneDialogVisible = false">取消</el-button>
90
- <el-button type="primary" :loading="submitLoading" @click="handleSubmitPhone">保存</el-button>
106
+ <el-button @click="phoneDialogVisible = false">{{ t("common.action.cancel") }}</el-button>
107
+ <el-button type="primary" :loading="submitLoading" @click="handleSubmitPhone">
108
+ {{ t("common.action.save") }}
109
+ </el-button>
91
110
  </div>
92
111
  </template>
93
112
  </ProDialog>
@@ -97,6 +116,7 @@
97
116
  <script setup lang="ts">
98
117
  import { computed, onBeforeUnmount, onMounted, reactive, ref } from "vue";
99
118
  import { useRoute, useRouter } from "vue-router";
119
+ import { t } from "@liujitcn/kratos-admin-core";
100
120
  import { defProfileAuthService } from "@liujitcn/kratos-admin-system/api/system/auth";
101
121
  import { defProfileOauthService } from "@liujitcn/kratos-admin-system/api/base/oauth";
102
122
  import type { OauthBinding } from "@liujitcn/kratos-admin-system/rpc/base/v1/oauth";
@@ -108,13 +128,8 @@ import type {
108
128
  import ProDialog from "@liujitcn/kratos-admin-core/components/Dialog/ProDialog.vue";
109
129
  import ProForm from "@liujitcn/kratos-admin-core/components/ProForm/index.vue";
110
130
  import type { ProFormField, ProFormInstance } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
111
- import {
112
- getOauthProviderIcon,
113
- withOauthProviderDisplay,
114
- type OauthProviderDisplay
115
- } from "@liujitcn/kratos-admin-core/security";
131
+ import { getOauthProviderIcon, withOauthProviderDisplay, type OauthProviderDisplay } from "@liujitcn/kratos-admin-core/security";
116
132
  import { resolveFrontendRouteURL } from "@liujitcn/kratos-admin-core/navigation";
117
- import { ElMessage, ElMessageBox } from "element-plus";
118
133
 
119
134
  /** 安全中心组件属性。 */
120
135
  interface ProfileSecurityProps {
@@ -149,22 +164,34 @@ const sendPhoneCodeForm = reactive<SendPhoneCodeRequest>({
149
164
  phone: ""
150
165
  });
151
166
 
152
- const phoneFormFields: ProFormField[] = [
153
- { prop: "phone", label: "手机号码", component: "input", props: { placeholder: "请输入手机号" } },
154
- { prop: "code", label: "验证码", component: "slot", slotName: "mobileCodeInput" }
155
- ];
167
+ const phoneFormFields = computed<ProFormField[]>(() => [
168
+ {
169
+ prop: "phone",
170
+ label: t("system.profile.security.field.phoneNumber"),
171
+ component: "input",
172
+ props: { placeholder: t("system.profile.security.placeholder.phone") }
173
+ },
174
+ {
175
+ prop: "code",
176
+ label: t("system.profile.security.field.code"),
177
+ component: "slot",
178
+ slotName: "mobileCodeInput"
179
+ }
180
+ ]);
156
181
 
157
- const phoneFormRules = {
182
+ const phoneFormRules = computed(() => ({
158
183
  phone: [
159
- { required: true, message: "请输入手机号", trigger: "blur" },
160
- { pattern: /^1[3-9]\d{9}$/, message: "请输入正确的手机号码", trigger: "blur" }
184
+ { required: true, message: t("system.profile.security.placeholder.phone"), trigger: "blur" },
185
+ { pattern: /^1[3-9]\d{9}$/, message: t("system.profile.security.validation.phone"), trigger: "blur" }
161
186
  ],
162
- code: [{ required: true, message: "请输入验证码", trigger: "blur" }]
163
- };
187
+ code: [{ required: true, message: t("system.profile.security.placeholder.code"), trigger: "blur" }]
188
+ }));
164
189
 
165
190
  /** 根据当前绑定状态输出手机号说明文案。 */
166
191
  const mobileTip = computed(() => {
167
- return props.profile.phone ? `已绑定:${props.profile.phone}` : "当前未绑定手机。";
192
+ return props.profile.phone
193
+ ? t("system.profile.security.message.phoneBound", { phone: props.profile.phone })
194
+ : t("system.profile.security.message.phoneUnbound");
168
195
  });
169
196
 
170
197
  /** 根据关键资料估算当前资料完成度。 */
@@ -189,6 +216,11 @@ async function loadOauthBindings() {
189
216
  oauthBindings.value = result.bindings.map(withOauthProviderDisplay);
190
217
  }
191
218
 
219
+ /** 根据当前语言返回三方登录方式名称。 */
220
+ function oauthName(binding: SecurityOauthBinding) {
221
+ return binding.nameKey.includes(".") ? t(binding.nameKey) : binding.nameKey;
222
+ }
223
+
192
224
  /** 处理 OAuth 绑定回跳结果。 */
193
225
  async function consumeOauthBindingResult() {
194
226
  const bindError = route.query.oauth_bind_error;
@@ -196,7 +228,7 @@ async function consumeOauthBindingResult() {
196
228
  if (typeof bindError === "string" && bindError) {
197
229
  ElMessage.error(bindError);
198
230
  } else if (bindSuccess === "1") {
199
- ElMessage.success("三方账号绑定成功");
231
+ ElMessage.success(t("system.profile.security.message.oauthBindSuccess"));
200
232
  } else {
201
233
  return;
202
234
  }
@@ -230,15 +262,19 @@ async function handleBindOauth(binding: SecurityOauthBinding) {
230
262
 
231
263
  /** 解绑三方账号并刷新绑定状态。 */
232
264
  async function handleUnbindOauth(binding: SecurityOauthBinding) {
233
- await ElMessageBox.confirm(`是否确定解绑该登录方式?\n登录方式:${binding.name}`, "提示", {
234
- type: "warning",
235
- confirmButtonText: "解绑",
236
- cancelButtonText: "取消"
237
- });
265
+ await ElMessageBox.confirm(
266
+ t("system.profile.security.dialog.confirmUnbind", { provider: oauthName(binding) }),
267
+ t("common.title.warning"),
268
+ {
269
+ type: "warning",
270
+ confirmButtonText: t("system.profile.security.action.unbind"),
271
+ cancelButtonText: t("common.action.cancel")
272
+ }
273
+ );
238
274
  oauthLoadingProvider.value = binding.provider;
239
275
  try {
240
276
  await defProfileOauthService.UnbindOauthAccount({ provider: binding.provider });
241
- ElMessage.success("三方账号已解绑");
277
+ ElMessage.success(t("system.profile.security.message.oauthUnbindSuccess"));
242
278
  await loadOauthBindings();
243
279
  } finally {
244
280
  oauthLoadingProvider.value = "";
@@ -255,17 +291,17 @@ function openPhoneDialog() {
255
291
  /** 发送手机验证码并启动倒计时。 */
256
292
  async function handleSendCode() {
257
293
  if (!phoneForm.phone) {
258
- ElMessage.error("请输入手机号");
294
+ ElMessage.error(t("system.profile.security.placeholder.phone"));
259
295
  return;
260
296
  }
261
297
  if (!/^1[3-9]\d{9}$/.test(phoneForm.phone)) {
262
- ElMessage.error("手机号格式不正确");
298
+ ElMessage.error(t("system.profile.security.validation.phone"));
263
299
  return;
264
300
  }
265
301
 
266
302
  sendPhoneCodeForm.phone = phoneForm.phone;
267
303
  await defProfileAuthService.SendPhoneCode(sendPhoneCodeForm);
268
- ElMessage.success("验证码已发送");
304
+ ElMessage.success(t("system.profile.security.message.codeSent"));
269
305
  startCountdown();
270
306
  }
271
307
 
@@ -276,7 +312,7 @@ async function handleSubmitPhone() {
276
312
  submitLoading.value = true;
277
313
  try {
278
314
  await defProfileAuthService.UpdateUserPhone({ user_phone: phoneForm });
279
- ElMessage.success("手机号更新成功");
315
+ ElMessage.success(t("system.profile.security.message.phoneUpdated"));
280
316
  phoneDialogVisible.value = false;
281
317
  emit("refreshed");
282
318
  } finally {
@@ -38,8 +38,9 @@ defineOptions({
38
38
  inheritAttrs: false
39
39
  });
40
40
 
41
- import { onMounted, reactive, ref } from "vue";
41
+ import { computed, onMounted, reactive, ref } from "vue";
42
42
  import { useRoute } from "vue-router";
43
+ import { t } from "@liujitcn/kratos-admin-core";
43
44
  import { defProfileAuthService } from "@liujitcn/kratos-admin-system/api/system/auth";
44
45
  import type { UserProfileForm } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/auth";
45
46
  import { useUserStore } from "@liujitcn/kratos-admin-core/stores/runtime";
@@ -64,23 +65,23 @@ interface ProfileTabOption {
64
65
  const userStore = useUserStore();
65
66
  const route = useRoute();
66
67
  const activeTab = ref<ProfileTab>("account");
67
- const profileTabs: ProfileTabOption[] = [
68
+ const profileTabs = computed<ProfileTabOption[]>(() => [
68
69
  {
69
70
  value: "account",
70
- label: "账号信息",
71
- description: "维护头像和资料"
71
+ label: t("system.profile.account.title"),
72
+ description: t("system.profile.account.navDescription")
72
73
  },
73
74
  {
74
75
  value: "security",
75
- label: "安全设置",
76
- description: "管理验证与安全"
76
+ label: t("system.profile.security.title"),
77
+ description: t("system.profile.security.navDescription")
77
78
  },
78
79
  {
79
80
  value: "password",
80
- label: "修改密码",
81
- description: "更新登录密码"
81
+ label: t("system.profile.password.title"),
82
+ description: t("system.profile.password.navDescription")
82
83
  }
83
- ];
84
+ ]);
84
85
  const userProfileForm = reactive<UserProfileForm>({
85
86
  user_name: "",
86
87
  nick_name: "",