@liujitcn/kratos-admin-system 0.0.1

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 (208) hide show
  1. package/README.md +137 -0
  2. package/dist/package/declarations/index.d.ts +1 -0
  3. package/dist/package/declarations/src/ai.d.ts +10 -0
  4. package/dist/package/declarations/src/api/base/ai_message.d.ts +27 -0
  5. package/dist/package/declarations/src/api/base/ai_session.d.ts +17 -0
  6. package/dist/package/declarations/src/api/base/ai_tool.d.ts +7 -0
  7. package/dist/package/declarations/src/api/base/oauth.d.ts +13 -0
  8. package/dist/package/declarations/src/api/base/sse.d.ts +31 -0
  9. package/dist/package/declarations/src/api/system/auth.d.ts +17 -0
  10. package/dist/package/declarations/src/api/system/base_api.d.ts +22 -0
  11. package/dist/package/declarations/src/api/system/base_area.d.ts +19 -0
  12. package/dist/package/declarations/src/api/system/base_config.d.ts +20 -0
  13. package/dist/package/declarations/src/api/system/base_dept.d.ts +21 -0
  14. package/dist/package/declarations/src/api/system/base_dict.d.ts +32 -0
  15. package/dist/package/declarations/src/api/system/base_job.d.ts +28 -0
  16. package/dist/package/declarations/src/api/system/base_log.d.ts +9 -0
  17. package/dist/package/declarations/src/api/system/base_menu.d.ts +21 -0
  18. package/dist/package/declarations/src/api/system/base_migration.d.ts +10 -0
  19. package/dist/package/declarations/src/api/system/base_post.d.ts +21 -0
  20. package/dist/package/declarations/src/api/system/base_role.d.ts +23 -0
  21. package/dist/package/declarations/src/api/system/base_tenant.d.ts +21 -0
  22. package/dist/package/declarations/src/api/system/base_user.d.ts +23 -0
  23. package/dist/package/declarations/src/api/system/code_gen.d.ts +14 -0
  24. package/dist/package/declarations/src/api/system/code_gen_column.d.ts +12 -0
  25. package/dist/package/declarations/src/api/system/code_gen_proto.d.ts +10 -0
  26. package/dist/package/declarations/src/api/system/code_gen_sse.d.ts +8 -0
  27. package/dist/package/declarations/src/api/system/code_gen_table.d.ts +18 -0
  28. package/dist/package/declarations/src/api/system/project_document.d.ts +9 -0
  29. package/dist/package/declarations/src/components/Ai.vue.d.ts +3 -0
  30. package/dist/package/declarations/src/index.d.ts +3 -0
  31. package/dist/package/declarations/src/module.d.ts +2 -0
  32. package/dist/package/declarations/src/rpc/base/v1/ai_message.d.ts +82 -0
  33. package/dist/package/declarations/src/rpc/base/v1/ai_session.d.ts +176 -0
  34. package/dist/package/declarations/src/rpc/base/v1/ai_tool.d.ts +59 -0
  35. package/dist/package/declarations/src/rpc/base/v1/oauth.d.ts +167 -0
  36. package/dist/package/declarations/src/rpc/base/v1/sse.d.ts +13 -0
  37. package/dist/package/declarations/src/rpc/common/v1/common.d.ts +57 -0
  38. package/dist/package/declarations/src/rpc/common/v1/enum.d.ts +58 -0
  39. package/dist/package/declarations/src/rpc/common/v1/types.d.ts +60 -0
  40. package/dist/package/declarations/src/rpc/google/protobuf/empty.d.ts +11 -0
  41. package/dist/package/declarations/src/rpc/google/protobuf/timestamp.d.ts +107 -0
  42. package/dist/package/declarations/src/rpc/system/admin/v1/auth.d.ts +156 -0
  43. package/dist/package/declarations/src/rpc/system/admin/v1/base_api.d.ts +193 -0
  44. package/dist/package/declarations/src/rpc/system/admin/v1/base_area.d.ts +82 -0
  45. package/dist/package/declarations/src/rpc/system/admin/v1/base_config.d.ts +112 -0
  46. package/dist/package/declarations/src/rpc/system/admin/v1/base_dept.d.ts +112 -0
  47. package/dist/package/declarations/src/rpc/system/admin/v1/base_dict.d.ts +215 -0
  48. package/dist/package/declarations/src/rpc/system/admin/v1/base_job.d.ts +177 -0
  49. package/dist/package/declarations/src/rpc/system/admin/v1/base_log.d.ts +87 -0
  50. package/dist/package/declarations/src/rpc/system/admin/v1/base_menu.d.ts +151 -0
  51. package/dist/package/declarations/src/rpc/system/admin/v1/base_migration.d.ts +64 -0
  52. package/dist/package/declarations/src/rpc/system/admin/v1/base_post.d.ts +112 -0
  53. package/dist/package/declarations/src/rpc/system/admin/v1/base_role.d.ts +128 -0
  54. package/dist/package/declarations/src/rpc/system/admin/v1/base_tenant.d.ts +112 -0
  55. package/dist/package/declarations/src/rpc/system/admin/v1/base_user.d.ts +161 -0
  56. package/dist/package/declarations/src/rpc/system/admin/v1/code_gen.d.ts +155 -0
  57. package/dist/package/declarations/src/rpc/system/admin/v1/code_gen_column.d.ts +143 -0
  58. package/dist/package/declarations/src/rpc/system/admin/v1/code_gen_proto.d.ts +86 -0
  59. package/dist/package/declarations/src/rpc/system/admin/v1/code_gen_table.d.ts +149 -0
  60. package/dist/package/declarations/src/rpc/system/admin/v1/project_document.d.ts +64 -0
  61. package/dist/package/declarations/src/rpc/system/common/v1/enum.d.ts +67 -0
  62. package/dist/package/declarations/src/views/ai/chat/attachment.d.ts +14 -0
  63. package/dist/package/declarations/src/views/ai/chat/components/AiMarkdown.vue.d.ts +13 -0
  64. package/dist/package/declarations/src/views/ai/chat/components/ChatPanel.vue.d.ts +45 -0
  65. package/dist/package/declarations/src/views/ai/chat/components/SessionPanel.vue.d.ts +34 -0
  66. package/dist/package/declarations/src/views/ai/chat/components/XSender.vue.d.ts +12 -0
  67. package/dist/package/declarations/src/views/ai/chat/index.vue.d.ts +4 -0
  68. package/dist/package/declarations/src/views/ai/chat/message.d.ts +59 -0
  69. package/dist/package/declarations/src/views/ai/chat/stream.d.ts +10 -0
  70. package/dist/package/declarations/src/views/ai/chat/types.d.ts +115 -0
  71. package/dist/package/declarations/src/views/base/api/index.vue.d.ts +3 -0
  72. package/dist/package/declarations/src/views/base/area/index.vue.d.ts +3 -0
  73. package/dist/package/declarations/src/views/base/config/index.vue.d.ts +3 -0
  74. package/dist/package/declarations/src/views/base/dept/index.vue.d.ts +3 -0
  75. package/dist/package/declarations/src/views/base/dict/index.vue.d.ts +3 -0
  76. package/dist/package/declarations/src/views/base/dict/item.vue.d.ts +3 -0
  77. package/dist/package/declarations/src/views/base/job/index.vue.d.ts +3 -0
  78. package/dist/package/declarations/src/views/base/job/log.vue.d.ts +3 -0
  79. package/dist/package/declarations/src/views/base/log/index.vue.d.ts +3 -0
  80. package/dist/package/declarations/src/views/base/menu/index.vue.d.ts +3 -0
  81. package/dist/package/declarations/src/views/base/migration/index.vue.d.ts +4 -0
  82. package/dist/package/declarations/src/views/base/post/index.vue.d.ts +3 -0
  83. package/dist/package/declarations/src/views/base/role/index.vue.d.ts +3 -0
  84. package/dist/package/declarations/src/views/base/tenant/index.vue.d.ts +3 -0
  85. package/dist/package/declarations/src/views/base/user/components/dept-tree.vue.d.ts +19 -0
  86. package/dist/package/declarations/src/views/base/user/index.vue.d.ts +3 -0
  87. package/dist/package/declarations/src/views/profile/components/base.vue.d.ts +13 -0
  88. package/dist/package/declarations/src/views/profile/components/password.vue.d.ts +3 -0
  89. package/dist/package/declarations/src/views/profile/components/security.vue.d.ts +15 -0
  90. package/dist/package/declarations/src/views/profile/index.vue.d.ts +3 -0
  91. package/dist/package/declarations/src/views/tool/api-doc/index.vue.d.ts +4 -0
  92. package/dist/package/declarations/src/views/tool/code-gen/code-preview/index.vue.d.ts +3 -0
  93. package/dist/package/declarations/src/views/tool/code-gen/columns/index.vue.d.ts +3 -0
  94. package/dist/package/declarations/src/views/tool/code-gen/columns/option-copy.d.ts +25 -0
  95. package/dist/package/declarations/src/views/tool/code-gen/components/CodeGenProgressDialog.vue.d.ts +16 -0
  96. package/dist/package/declarations/src/views/tool/code-gen/components/CodePreviewPane.vue.d.ts +9 -0
  97. package/dist/package/declarations/src/views/tool/code-gen/config.d.ts +32 -0
  98. package/dist/package/declarations/src/views/tool/code-gen/preview/capabilities.d.ts +20 -0
  99. package/dist/package/declarations/src/views/tool/code-gen/preview/data.d.ts +42 -0
  100. package/dist/package/declarations/src/views/tool/code-gen/preview/index.vue.d.ts +3 -0
  101. package/dist/package/declarations/src/views/tool/code-gen/proto/index.vue.d.ts +3 -0
  102. package/dist/package/declarations/src/views/tool/code-gen/table/index.vue.d.ts +3 -0
  103. package/dist/package/declarations/src/views/tool/project-doc/index.vue.d.ts +4 -0
  104. package/dist/package/src/ai.ts +16 -0
  105. package/dist/package/src/api/base/ai_message.ts +139 -0
  106. package/dist/package/src/api/base/ai_session.ts +77 -0
  107. package/dist/package/src/api/base/ai_tool.ts +22 -0
  108. package/dist/package/src/api/base/oauth.ts +47 -0
  109. package/dist/package/src/api/base/sse.ts +192 -0
  110. package/dist/package/src/api/system/auth.ts +65 -0
  111. package/dist/package/src/api/system/base_api.ts +95 -0
  112. package/dist/package/src/api/system/base_area.ts +73 -0
  113. package/dist/package/src/api/system/base_config.ts +82 -0
  114. package/dist/package/src/api/system/base_dept.ts +83 -0
  115. package/dist/package/src/api/system/base_dict.ts +144 -0
  116. package/dist/package/src/api/system/base_job.ts +125 -0
  117. package/dist/package/src/api/system/base_log.ts +32 -0
  118. package/dist/package/src/api/system/base_menu.ts +83 -0
  119. package/dist/package/src/api/system/base_migration.ts +35 -0
  120. package/dist/package/src/api/system/base_post.ts +83 -0
  121. package/dist/package/src/api/system/base_role.ts +93 -0
  122. package/dist/package/src/api/system/base_tenant.ts +83 -0
  123. package/dist/package/src/api/system/base_user.ts +93 -0
  124. package/dist/package/src/api/system/code_gen.ts +56 -0
  125. package/dist/package/src/api/system/code_gen_column.ts +43 -0
  126. package/dist/package/src/api/system/code_gen_proto.ts +32 -0
  127. package/dist/package/src/api/system/code_gen_sse.ts +33 -0
  128. package/dist/package/src/api/system/code_gen_table.ts +74 -0
  129. package/dist/package/src/api/system/project_document.ts +33 -0
  130. package/dist/package/src/components/Ai.vue +57 -0
  131. package/dist/package/src/index.ts +3 -0
  132. package/dist/package/src/module.ts +17 -0
  133. package/dist/package/src/rpc/base/v1/ai_message.ts +98 -0
  134. package/dist/package/src/rpc/base/v1/ai_session.ts +214 -0
  135. package/dist/package/src/rpc/base/v1/ai_tool.ts +74 -0
  136. package/dist/package/src/rpc/base/v1/oauth.ts +199 -0
  137. package/dist/package/src/rpc/base/v1/sse.ts +22 -0
  138. package/dist/package/src/rpc/common/v1/common.ts +71 -0
  139. package/dist/package/src/rpc/common/v1/enum.ts +71 -0
  140. package/dist/package/src/rpc/common/v1/types.ts +77 -0
  141. package/dist/package/src/rpc/google/protobuf/empty.ts +19 -0
  142. package/dist/package/src/rpc/google/protobuf/timestamp.ts +115 -0
  143. package/dist/package/src/rpc/system/admin/v1/auth.ts +208 -0
  144. package/dist/package/src/rpc/system/admin/v1/base_api.ts +241 -0
  145. package/dist/package/src/rpc/system/admin/v1/base_area.ts +105 -0
  146. package/dist/package/src/rpc/system/admin/v1/base_config.ts +140 -0
  147. package/dist/package/src/rpc/system/admin/v1/base_dept.ts +140 -0
  148. package/dist/package/src/rpc/system/admin/v1/base_dict.ts +249 -0
  149. package/dist/package/src/rpc/system/admin/v1/base_job.ts +206 -0
  150. package/dist/package/src/rpc/system/admin/v1/base_log.ts +101 -0
  151. package/dist/package/src/rpc/system/admin/v1/base_menu.ts +195 -0
  152. package/dist/package/src/rpc/system/admin/v1/base_migration.ts +81 -0
  153. package/dist/package/src/rpc/system/admin/v1/base_post.ts +134 -0
  154. package/dist/package/src/rpc/system/admin/v1/base_role.ts +151 -0
  155. package/dist/package/src/rpc/system/admin/v1/base_tenant.ts +134 -0
  156. package/dist/package/src/rpc/system/admin/v1/base_user.ts +190 -0
  157. package/dist/package/src/rpc/system/admin/v1/code_gen.ts +176 -0
  158. package/dist/package/src/rpc/system/admin/v1/code_gen_column.ts +170 -0
  159. package/dist/package/src/rpc/system/admin/v1/code_gen_proto.ts +104 -0
  160. package/dist/package/src/rpc/system/admin/v1/code_gen_table.ts +179 -0
  161. package/dist/package/src/rpc/system/admin/v1/project_document.ts +79 -0
  162. package/dist/package/src/rpc/system/common/v1/enum.ts +80 -0
  163. package/dist/package/src/typings/swagger-ui-dist.d.ts +28 -0
  164. package/dist/package/src/typings/x-markdown-vue.d.ts +1 -0
  165. package/dist/package/src/views/ai/chat/attachment.ts +65 -0
  166. package/dist/package/src/views/ai/chat/components/AiMarkdown.vue +162 -0
  167. package/dist/package/src/views/ai/chat/components/ChatPanel.vue +1861 -0
  168. package/dist/package/src/views/ai/chat/components/SessionPanel.vue +316 -0
  169. package/dist/package/src/views/ai/chat/components/XSender.vue +554 -0
  170. package/dist/package/src/views/ai/chat/index.vue +848 -0
  171. package/dist/package/src/views/ai/chat/message.ts +416 -0
  172. package/dist/package/src/views/ai/chat/stream.ts +115 -0
  173. package/dist/package/src/views/ai/chat/types.ts +126 -0
  174. package/dist/package/src/views/base/api/index.vue +455 -0
  175. package/dist/package/src/views/base/area/index.vue +269 -0
  176. package/dist/package/src/views/base/config/index.vue +530 -0
  177. package/dist/package/src/views/base/dept/index.vue +461 -0
  178. package/dist/package/src/views/base/dict/index.vue +304 -0
  179. package/dist/package/src/views/base/dict/item.vue +373 -0
  180. package/dist/package/src/views/base/job/index.vue +535 -0
  181. package/dist/package/src/views/base/job/log.vue +202 -0
  182. package/dist/package/src/views/base/log/index.vue +317 -0
  183. package/dist/package/src/views/base/menu/index.vue +944 -0
  184. package/dist/package/src/views/base/migration/index.vue +791 -0
  185. package/dist/package/src/views/base/post/index.vue +324 -0
  186. package/dist/package/src/views/base/role/index.vue +684 -0
  187. package/dist/package/src/views/base/tenant/index.vue +328 -0
  188. package/dist/package/src/views/base/user/components/dept-tree.vue +75 -0
  189. package/dist/package/src/views/base/user/index.vue +783 -0
  190. package/dist/package/src/views/profile/components/base.vue +320 -0
  191. package/dist/package/src/views/profile/components/password.vue +269 -0
  192. package/dist/package/src/views/profile/components/security.vue +443 -0
  193. package/dist/package/src/views/profile/index.vue +199 -0
  194. package/dist/package/src/views/tool/api-doc/index.vue +275 -0
  195. package/dist/package/src/views/tool/code-gen/code-preview/index.vue +233 -0
  196. package/dist/package/src/views/tool/code-gen/columns/index.vue +1456 -0
  197. package/dist/package/src/views/tool/code-gen/columns/option-copy.ts +125 -0
  198. package/dist/package/src/views/tool/code-gen/components/CodeGenProgressDialog.vue +328 -0
  199. package/dist/package/src/views/tool/code-gen/components/CodePreviewPane.vue +42 -0
  200. package/dist/package/src/views/tool/code-gen/config.ts +175 -0
  201. package/dist/package/src/views/tool/code-gen/preview/capabilities.ts +34 -0
  202. package/dist/package/src/views/tool/code-gen/preview/data.ts +331 -0
  203. package/dist/package/src/views/tool/code-gen/preview/index.vue +831 -0
  204. package/dist/package/src/views/tool/code-gen/proto/index.vue +703 -0
  205. package/dist/package/src/views/tool/code-gen/table/index.vue +864 -0
  206. package/dist/package/src/views/tool/project-doc/index.vue +561 -0
  207. package/package.json +60 -0
  208. package/tsconfig.json +13 -0
@@ -0,0 +1,320 @@
1
+ <template>
2
+ <el-card class="base-card" shadow="never">
3
+ <template #header>
4
+ <div class="panel-header">
5
+ <div>
6
+ <h3>账号信息</h3>
7
+ <p>维护头像、昵称和基础账号资料。</p>
8
+ </div>
9
+ <el-button type="primary" plain @click="openAccountDialog">修改基本资料</el-button>
10
+ </div>
11
+ </template>
12
+
13
+ <div class="base-layout">
14
+ <div class="avatar-panel">
15
+ <div class="avatar-shell">
16
+ <el-avatar :src="avatarSrc" :size="116" @error="handleAvatarError" />
17
+ <el-button class="avatar-trigger" circle type="primary" :icon="Camera" @click="triggerFileUpload" />
18
+ <input ref="fileInputRef" type="file" class="hidden-input" accept="image/*" @change="handleFileChange" />
19
+ </div>
20
+ <div class="avatar-copy">
21
+ <strong>头像</strong>
22
+ <p>点击右下角可更换头像</p>
23
+ </div>
24
+ </div>
25
+
26
+ <div class="detail-grid">
27
+ <div class="detail-item">
28
+ <span class="detail-label">登录账号</span>
29
+ <span class="detail-value">{{ profile.user_name || "--" }}</span>
30
+ </div>
31
+ <div class="detail-item">
32
+ <span class="detail-label">昵称</span>
33
+ <span class="detail-value">{{ profile.nick_name || "--" }}</span>
34
+ </div>
35
+ <div class="detail-item">
36
+ <span class="detail-label">性别</span>
37
+ <span class="detail-value">{{ genderText }}</span>
38
+ </div>
39
+ <div class="detail-item">
40
+ <span class="detail-label">绑定手机</span>
41
+ <span class="detail-value">{{ profile.phone || "未绑定" }}</span>
42
+ </div>
43
+ <div class="detail-item">
44
+ <span class="detail-label">所属角色</span>
45
+ <span class="detail-value">{{ profile.role_name || "--" }}</span>
46
+ </div>
47
+ <div class="detail-item">
48
+ <span class="detail-label">所属部门</span>
49
+ <span class="detail-value">{{ profile.dept_name || "--" }}</span>
50
+ </div>
51
+ <div class="detail-item detail-item--wide">
52
+ <span class="detail-label">创建时间</span>
53
+ <span class="detail-value">{{ profile.created_at || "--" }}</span>
54
+ </div>
55
+ </div>
56
+ </div>
57
+
58
+ <ProDialog v-model="accountDialogVisible" title="修改基本资料" :width="520" @closed="handleDialogClosed">
59
+ <ProForm ref="accountFormRef" :model="accountForm" :fields="accountFormFields" label-width="96px" />
60
+ <template #footer>
61
+ <div class="dialog-footer">
62
+ <el-button @click="accountDialogVisible = false">取消</el-button>
63
+ <el-button type="primary" :loading="submitLoading" @click="handleSubmitAccount">保存</el-button>
64
+ </div>
65
+ </template>
66
+ </ProDialog>
67
+ </el-card>
68
+ </template>
69
+
70
+ <script setup lang="ts">
71
+ import { computed, reactive, ref, watch } from "vue";
72
+ import { defProfileAuthService } from "@liujitcn/kratos-admin-system/api/system/auth";
73
+ import type { UserProfileForm } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/auth";
74
+ import { defFileService } from "@liujitcn/kratos-admin-core/api/base/file";
75
+ import defaultAvatar from "@liujitcn/kratos-admin-core/assets/images/avatar.png";
76
+ import ProDialog from "@liujitcn/kratos-admin-core/components/Dialog/ProDialog.vue";
77
+ import ProForm from "@liujitcn/kratos-admin-core/components/ProForm/index.vue";
78
+ import type { ProFormField, ProFormInstance } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
79
+ import { ElMessage } from "element-plus";
80
+ import { Camera } from "@element-plus/icons-vue";
81
+
82
+ /** 个人中心基础资料组件属性。 */
83
+ interface ProfileBaseProps {
84
+ /** 当前用户资料。 */
85
+ profile: UserProfileForm;
86
+ }
87
+
88
+ const props = defineProps<ProfileBaseProps>();
89
+
90
+ const emit = defineEmits<{
91
+ refreshed: [];
92
+ }>();
93
+
94
+ const fileInputRef = ref<HTMLInputElement | null>(null);
95
+ const accountFormRef = ref<ProFormInstance>();
96
+ const accountDialogVisible = ref(false);
97
+ const submitLoading = ref(false);
98
+ const avatarSrc = ref(defaultAvatar);
99
+ const accountForm = reactive<Pick<UserProfileForm, "nick_name" | "gender">>({
100
+ nick_name: "",
101
+ gender: 3
102
+ });
103
+
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
+ ];
108
+
109
+ /** 根据资料中的性别值输出展示文案。 */
110
+ const genderText = computed(() => {
111
+ if (props.profile.gender === 1) return "男";
112
+ if (props.profile.gender === 2) return "女";
113
+ return "保密";
114
+ });
115
+
116
+ /**
117
+ * 同步个人中心头像展示,优先使用用户头像,为空时回退默认头像。
118
+ *
119
+ * @param avatar 用户头像地址
120
+ */
121
+ function syncAvatarSrc(avatar?: string) {
122
+ // 个人中心与头部头像保持一致,统一使用本地默认头像兜底。
123
+ avatarSrc.value = avatar || defaultAvatar;
124
+ }
125
+
126
+ watch(
127
+ () => props.profile,
128
+ profile => {
129
+ accountForm.nick_name = profile.nick_name;
130
+ accountForm.gender = profile.gender;
131
+ syncAvatarSrc(profile.avatar);
132
+ },
133
+ { immediate: true, deep: true }
134
+ );
135
+
136
+ /** 头像加载失败时回退默认头像,避免出现空白或破图。 */
137
+ function handleAvatarError() {
138
+ avatarSrc.value = defaultAvatar;
139
+ return false;
140
+ }
141
+
142
+ /** 触发头像文件选择。 */
143
+ function triggerFileUpload() {
144
+ fileInputRef.value?.click();
145
+ }
146
+
147
+ /** 打开基本资料编辑弹窗,并回填当前资料。 */
148
+ function openAccountDialog() {
149
+ accountForm.nick_name = props.profile.nick_name;
150
+ accountForm.gender = props.profile.gender;
151
+ accountDialogVisible.value = true;
152
+ }
153
+
154
+ /** 处理头像文件上传并同步到个人资料。 */
155
+ async function handleFileChange(event: Event) {
156
+ const target = event.target as HTMLInputElement;
157
+ const file = target.files?.[0];
158
+ if (!file) return;
159
+
160
+ try {
161
+ const uploadResult = await defFileService.UploadFile(file, "avatar");
162
+ await defProfileAuthService.UpdateUserProfile({
163
+ user_profile: {
164
+ ...props.profile,
165
+ avatar: uploadResult.url
166
+ }
167
+ });
168
+ ElMessage.success("头像更新成功");
169
+ emit("refreshed");
170
+ } catch (_error) {
171
+ ElMessage.error("头像上传失败");
172
+ } finally {
173
+ target.value = "";
174
+ }
175
+ }
176
+
177
+ /** 提交基本资料更新。 */
178
+ async function handleSubmitAccount() {
179
+ if (!(await accountFormRef.value?.validate())) return;
180
+
181
+ submitLoading.value = true;
182
+ try {
183
+ await defProfileAuthService.UpdateUserProfile({
184
+ user_profile: {
185
+ ...props.profile,
186
+ nick_name: accountForm.nick_name,
187
+ gender: accountForm.gender
188
+ }
189
+ });
190
+ ElMessage.success("基本资料已更新");
191
+ accountDialogVisible.value = false;
192
+ emit("refreshed");
193
+ } finally {
194
+ submitLoading.value = false;
195
+ }
196
+ }
197
+
198
+ /** 弹窗关闭后清理校验状态,并恢复表单值。 */
199
+ function handleDialogClosed() {
200
+ accountFormRef.value?.clearValidate();
201
+ accountForm.nick_name = props.profile.nick_name;
202
+ accountForm.gender = props.profile.gender;
203
+ }
204
+ </script>
205
+
206
+ <style scoped lang="scss">
207
+ .base-card {
208
+ border: 1px solid #ebeef5;
209
+ border-radius: 12px;
210
+ }
211
+ :deep(.base-card .el-card__header) {
212
+ padding: 18px 20px;
213
+ border-bottom: 1px solid #f0f2f5;
214
+ }
215
+ :deep(.base-card .el-card__body) {
216
+ padding: 20px;
217
+ }
218
+ .panel-header {
219
+ display: flex;
220
+ gap: 16px;
221
+ align-items: flex-start;
222
+ justify-content: space-between;
223
+ }
224
+ .panel-header h3 {
225
+ margin: 0;
226
+ font-size: 18px;
227
+ color: #303133;
228
+ }
229
+ .panel-header p {
230
+ margin: 6px 0 0;
231
+ font-size: 13px;
232
+ color: #909399;
233
+ }
234
+ .base-layout {
235
+ display: grid;
236
+ grid-template-columns: 280px minmax(0, 1fr);
237
+ gap: 16px;
238
+ align-items: start;
239
+ }
240
+ .avatar-panel {
241
+ padding: 24px 20px;
242
+ text-align: center;
243
+ background: #f8fafc;
244
+ border: 1px solid #eef2f6;
245
+ border-radius: 12px;
246
+ }
247
+ .avatar-shell {
248
+ position: relative;
249
+ display: inline-flex;
250
+ }
251
+ .avatar-trigger {
252
+ position: absolute;
253
+ right: 6px;
254
+ bottom: 4px;
255
+ }
256
+ .hidden-input {
257
+ display: none;
258
+ }
259
+ .avatar-copy {
260
+ margin-top: 16px;
261
+ }
262
+ .avatar-copy strong {
263
+ display: block;
264
+ font-size: 16px;
265
+ font-weight: 600;
266
+ color: #303133;
267
+ }
268
+ .avatar-copy p {
269
+ margin: 6px 0 0;
270
+ font-size: 12px;
271
+ color: #909399;
272
+ }
273
+ .detail-grid {
274
+ display: grid;
275
+ grid-template-columns: repeat(2, minmax(0, 1fr));
276
+ gap: 12px;
277
+ }
278
+ .detail-item {
279
+ padding: 16px;
280
+ background: #ffffff;
281
+ border: 1px solid #f0f2f5;
282
+ border-radius: 10px;
283
+ }
284
+ .detail-item--wide {
285
+ grid-column: 1 / -1;
286
+ }
287
+ .detail-label {
288
+ display: block;
289
+ margin-bottom: 8px;
290
+ font-size: 12px;
291
+ color: #909399;
292
+ }
293
+ .detail-value {
294
+ font-size: 14px;
295
+ font-weight: 600;
296
+ color: #303133;
297
+ word-break: break-all;
298
+ }
299
+ .dialog-footer {
300
+ display: flex;
301
+ justify-content: flex-end;
302
+ }
303
+
304
+ @media screen and (width <= 992px) {
305
+ .base-layout,
306
+ .detail-grid {
307
+ grid-template-columns: 1fr;
308
+ }
309
+ .detail-item--wide {
310
+ grid-column: auto;
311
+ }
312
+ }
313
+
314
+ @media screen and (width <= 640px) {
315
+ .panel-header {
316
+ flex-direction: column;
317
+ align-items: flex-start;
318
+ }
319
+ }
320
+ </style>
@@ -0,0 +1,269 @@
1
+ <template>
2
+ <div class="profile-password">
3
+ <el-card class="password-card" shadow="never">
4
+ <template #header>
5
+ <div class="panel-header">
6
+ <div>
7
+ <h3>修改密码</h3>
8
+ <p>更新当前登录密码,保持账号安全。</p>
9
+ </div>
10
+ <el-tag type="warning" effect="plain">建议高强度</el-tag>
11
+ </div>
12
+ </template>
13
+ <div class="password-layout">
14
+ <div class="password-form-wrap">
15
+ <ProForm
16
+ ref="passwordFormRef"
17
+ :model="passwordForm"
18
+ :fields="passwordFormFields"
19
+ :rules="passwordFormRules"
20
+ label-width="96px"
21
+ />
22
+ <PasswordStrength :password="passwordForm.new_pwd" class="password-form-wrap__strength" />
23
+ <div class="password-footer">
24
+ <el-button @click="resetPasswordForm">重置</el-button>
25
+ <el-button type="primary" :loading="submitLoading" @click="handleSubmitPassword">更新密码</el-button>
26
+ </div>
27
+ </div>
28
+ <div class="password-tips">
29
+ <div class="tip-card">
30
+ <span class="tip-badge">01</span>
31
+ <strong>避免重复密码</strong>
32
+ <p>不要重复使用旧密码或其他系统已使用密码。</p>
33
+ </div>
34
+ <div class="tip-card">
35
+ <span class="tip-badge">02</span>
36
+ <strong>控制密码强度</strong>
37
+ <p>建议使用大小写字母、数字和符号组合。</p>
38
+ </div>
39
+ <div class="tip-card">
40
+ <span class="tip-badge">03</span>
41
+ <strong>及时更新记录</strong>
42
+ <p>如有账号交接,请同步更新保管记录。</p>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </el-card>
47
+ </div>
48
+ </template>
49
+
50
+ <script setup lang="ts">
51
+ import { computed, reactive, ref } from "vue";
52
+ import { useRouter } from "vue-router";
53
+ import { defProfileAuthService } from "@liujitcn/kratos-admin-system/api/system/auth";
54
+ import type { UserPasswordForm } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/auth";
55
+ import PasswordStrength from "@liujitcn/kratos-admin-core/components/PasswordStrength/index.vue";
56
+ import ProForm from "@liujitcn/kratos-admin-core/components/ProForm/index.vue";
57
+ import type { ProFormField, ProFormInstance } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
58
+ import { LOGIN_URL } from "@liujitcn/kratos-admin-core/config";
59
+ import { useUserStore } from "@liujitcn/kratos-admin-core/stores/runtime";
60
+ import { ElMessage } from "element-plus";
61
+ import {
62
+ PASSWORD_CRYPTO_SCENE,
63
+ PASSWORD_STRENGTH_ERROR_MESSAGE,
64
+ encryptPassword,
65
+ getPasswordStrength,
66
+ validatePasswordStrengthValue
67
+ } from "@liujitcn/kratos-admin-core/security";
68
+
69
+ /** 修改密码表单状态,明文只在前端校验和加密前短暂保存。 */
70
+ interface UserPasswordFormState {
71
+ /** 原密码明文只保留在前端表单中,提交前转换为密码密文。 */
72
+ old_pwd: string;
73
+ /** 新密码明文只保留在前端表单中,提交前转换为密码密文。 */
74
+ new_pwd: string;
75
+ /** 确认密码只用于前端一致性校验,不提交后端。 */
76
+ confirm_pwd: string;
77
+ }
78
+
79
+ const router = useRouter();
80
+ const userStore = useUserStore();
81
+ const passwordFormRef = ref<ProFormInstance>();
82
+ const submitLoading = ref(false);
83
+ const passwordForm = reactive<UserPasswordFormState>({
84
+ old_pwd: "",
85
+ new_pwd: "",
86
+ confirm_pwd: ""
87
+ });
88
+
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
+ ];
94
+
95
+ const passwordFormRules = {
96
+ old_pwd: [{ required: true, message: "请输入原密码", trigger: "blur" }],
97
+ new_pwd: [
98
+ { required: true, message: "请输入新密码", trigger: "blur" },
99
+ { validator: validatePasswordStrength, trigger: "blur" }
100
+ ],
101
+ confirm_pwd: [{ required: true, message: "请再次输入新密码", trigger: "blur" }]
102
+ };
103
+
104
+ /** 统一计算当前新密码强度,供展示和提交校验复用。 */
105
+ const passwordStrength = computed(() => getPasswordStrength(passwordForm.new_pwd));
106
+
107
+ /** 提交修改密码请求,并校验两次输入的一致性。 */
108
+ async function handleSubmitPassword() {
109
+ if (!(await passwordFormRef.value?.validate())) return;
110
+ if (passwordForm.new_pwd !== passwordForm.confirm_pwd) {
111
+ ElMessage.error("两次输入的密码不一致");
112
+ return;
113
+ }
114
+ if (!passwordStrength.value.isValid) {
115
+ ElMessage.error("新密码强度不足,请提升到最高强度");
116
+ return;
117
+ }
118
+
119
+ submitLoading.value = true;
120
+ try {
121
+ const oldPwd = await encryptPassword(passwordForm.old_pwd, PASSWORD_CRYPTO_SCENE.UPDATE_USER_PASSWORD);
122
+ const newPwd = await encryptPassword(passwordForm.new_pwd, PASSWORD_CRYPTO_SCENE.UPDATE_USER_PASSWORD);
123
+ const userPassword: UserPasswordForm = {
124
+ old_pwd: oldPwd,
125
+ new_pwd: newPwd
126
+ };
127
+ await defProfileAuthService.UpdateUserPassword({ user_password: userPassword });
128
+ ElMessage.success("密码修改成功,请重新登录");
129
+ resetPasswordForm();
130
+ await forceRelogin();
131
+ } finally {
132
+ submitLoading.value = false;
133
+ }
134
+ }
135
+
136
+ /** 重置密码表单内容与校验状态。 */
137
+ function resetPasswordForm() {
138
+ passwordFormRef.value?.resetFields();
139
+ passwordFormRef.value?.clearValidate();
140
+ passwordForm.old_pwd = "";
141
+ passwordForm.new_pwd = "";
142
+ passwordForm.confirm_pwd = "";
143
+ }
144
+
145
+ /** 修改密码成功后强制重新登录,避免旧登录态继续使用。 */
146
+ async function forceRelogin() {
147
+ try {
148
+ await userStore.logout();
149
+ } catch (_error) {
150
+ // 若后端已提前让旧令牌失效,前端仍需保证本地登录态被清空。
151
+ userStore.clearAuthData();
152
+ }
153
+ await router.replace(LOGIN_URL);
154
+ }
155
+
156
+ /**
157
+ * 校验新密码强度,必须达到最高强度才允许提交。
158
+ *
159
+ * @param _rule 表单规则对象
160
+ * @param value 当前输入的新密码
161
+ * @param callback 校验回调
162
+ */
163
+ function validatePasswordStrength(_rule: unknown, value: string, callback: (error?: Error) => void) {
164
+ if (!value) {
165
+ callback();
166
+ return;
167
+ }
168
+ const result = validatePasswordStrengthValue(value);
169
+ if (!result.valid) {
170
+ callback(new Error(result.message || PASSWORD_STRENGTH_ERROR_MESSAGE));
171
+ return;
172
+ }
173
+ callback();
174
+ }
175
+ </script>
176
+
177
+ <style scoped lang="scss">
178
+ .password-card {
179
+ border: 1px solid #ebeef5;
180
+ border-radius: 12px;
181
+ }
182
+ :deep(.password-card .el-card__header) {
183
+ padding: 18px 20px;
184
+ border-bottom: 1px solid #f0f2f5;
185
+ }
186
+ :deep(.password-card .el-card__body) {
187
+ padding: 20px;
188
+ }
189
+ .panel-header {
190
+ display: flex;
191
+ gap: 16px;
192
+ align-items: flex-start;
193
+ justify-content: space-between;
194
+ }
195
+ .panel-header h3 {
196
+ margin: 0;
197
+ font-size: 18px;
198
+ color: #303133;
199
+ }
200
+ .panel-header p {
201
+ margin: 6px 0 0;
202
+ font-size: 13px;
203
+ color: #909399;
204
+ }
205
+ .password-layout {
206
+ display: grid;
207
+ grid-template-columns: minmax(0, 1.2fr) minmax(260px, 0.9fr);
208
+ gap: 16px;
209
+ }
210
+ .password-form-wrap,
211
+ .tip-card {
212
+ padding: 18px;
213
+ background: #ffffff;
214
+ border: 1px solid #f0f2f5;
215
+ border-radius: 10px;
216
+ }
217
+ .password-form-wrap__strength {
218
+ margin-top: 16px;
219
+ }
220
+ .password-footer {
221
+ display: flex;
222
+ gap: 12px;
223
+ justify-content: flex-end;
224
+ margin-top: 24px;
225
+ }
226
+ .password-tips {
227
+ display: flex;
228
+ flex-direction: column;
229
+ gap: 12px;
230
+ }
231
+ .tip-badge {
232
+ display: inline-flex;
233
+ align-items: center;
234
+ justify-content: center;
235
+ width: 28px;
236
+ height: 28px;
237
+ margin-bottom: 10px;
238
+ font-size: 12px;
239
+ font-weight: 700;
240
+ color: #409eff;
241
+ background: #ecf5ff;
242
+ border-radius: 8px;
243
+ }
244
+ .tip-card strong {
245
+ display: block;
246
+ margin-bottom: 8px;
247
+ font-size: 15px;
248
+ color: #303133;
249
+ }
250
+ .tip-card p {
251
+ margin: 0;
252
+ font-size: 13px;
253
+ line-height: 1.6;
254
+ color: #909399;
255
+ }
256
+
257
+ @media screen and (width <= 960px) {
258
+ .password-layout {
259
+ grid-template-columns: 1fr;
260
+ }
261
+ }
262
+
263
+ @media screen and (width <= 640px) {
264
+ .panel-header {
265
+ flex-direction: column;
266
+ align-items: flex-start;
267
+ }
268
+ }
269
+ </style>