@liujitcn/kratos-admin-system 0.0.38 → 0.0.40

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 +22 -0
  2. package/dist/package/declarations/src/api/system/admin/v1/base_config.d.ts +1 -5
  3. package/dist/package/declarations/src/api/system/admin/v1/base_login_log.d.ts +3 -1
  4. package/dist/package/declarations/src/api/system/admin/v1/base_session.d.ts +7 -1
  5. package/dist/package/declarations/src/components/DynamicI18nCell.vue.d.ts +2 -8
  6. package/dist/package/declarations/src/components/DynamicI18nEditor.vue.d.ts +20 -3
  7. package/dist/package/declarations/src/components/dynamicI18n.d.ts +2 -2
  8. package/dist/package/declarations/src/locales/generated.d.ts +5770 -5686
  9. package/dist/package/declarations/src/rpc/base/v1/login.d.ts +2 -0
  10. package/dist/package/declarations/src/rpc/base/v1/notification.d.ts +1 -1
  11. package/dist/package/declarations/src/rpc/base/v1/oauth.d.ts +4 -0
  12. package/dist/package/declarations/src/rpc/base/v1/oauth_client.d.ts +1 -1
  13. package/dist/package/declarations/src/rpc/system/admin/v1/base_api_log.d.ts +1 -1
  14. package/dist/package/declarations/src/rpc/system/admin/v1/base_config.d.ts +4 -40
  15. package/dist/package/declarations/src/rpc/system/admin/v1/base_data_access_log.d.ts +1 -1
  16. package/dist/package/declarations/src/rpc/system/admin/v1/base_i18n.d.ts +1 -1
  17. package/dist/package/declarations/src/rpc/system/admin/v1/base_log.d.ts +1 -1
  18. package/dist/package/declarations/src/rpc/system/admin/v1/base_login_log.d.ts +10 -1
  19. package/dist/package/declarations/src/rpc/system/admin/v1/base_login_policy.d.ts +8 -0
  20. package/dist/package/declarations/src/rpc/system/admin/v1/base_message.d.ts +1 -1
  21. package/dist/package/declarations/src/rpc/system/admin/v1/base_message_category.d.ts +1 -1
  22. package/dist/package/declarations/src/rpc/system/admin/v1/base_migration.d.ts +1 -1
  23. package/dist/package/declarations/src/rpc/system/admin/v1/base_operation_log.d.ts +1 -1
  24. package/dist/package/declarations/src/rpc/system/admin/v1/base_permission_log.d.ts +1 -1
  25. package/dist/package/declarations/src/rpc/system/admin/v1/base_policy_evaluation_log.d.ts +1 -1
  26. package/dist/package/declarations/src/rpc/system/admin/v1/base_redact_output_policy.d.ts +1 -1
  27. package/dist/package/declarations/src/rpc/system/admin/v1/base_redact_storage_policy.d.ts +1 -1
  28. package/dist/package/declarations/src/rpc/system/admin/v1/base_session.d.ts +41 -0
  29. package/dist/package/declarations/src/rpc/system/admin/v1/base_table_archive.d.ts +1 -1
  30. package/dist/package/declarations/src/rpc/system/admin/v1/base_table_archive_record.d.ts +1 -1
  31. package/dist/package/declarations/src/rpc/system/admin/v1/base_table_archive_restore.d.ts +1 -1
  32. package/dist/package/declarations/src/rpc/system/admin/v1/base_table_backup.d.ts +1 -1
  33. package/dist/package/declarations/src/rpc/system/admin/v1/base_table_backup_record.d.ts +1 -1
  34. package/dist/package/declarations/src/rpc/system/admin/v1/base_table_backup_restore.d.ts +1 -1
  35. package/dist/package/declarations/src/rpc/system/admin/v1/base_table_source.d.ts +1 -1
  36. package/dist/package/declarations/src/rpc/system/admin/v1/cache.d.ts +1 -1
  37. package/dist/package/declarations/src/rpc/system/admin/v1/oauth_client.d.ts +1 -1
  38. package/dist/package/declarations/src/rpc/system/admin/v1/ops_monitoring.d.ts +1 -1
  39. package/dist/package/declarations/src/utils/code-gen-progress.d.ts +3 -0
  40. package/dist/package/declarations/src/views/profile/components/login-log.vue.d.ts +3 -0
  41. package/dist/package/declarations/src/views/tool/code-gen/components/CodeGenGenerateConfirm.vue.d.ts +11 -0
  42. package/dist/package/declarations/src/views/tool/code-gen/components/CodeGenLocaleEditor.vue.d.ts +5 -9
  43. package/dist/package/declarations/src/views/tool/code-gen/config.d.ts +3 -0
  44. package/dist/package/src/api/base/v1/sse.ts +11 -2
  45. package/dist/package/src/api/system/admin/v1/base_config.ts +0 -20
  46. package/dist/package/src/api/system/admin/v1/base_login_log.ts +10 -0
  47. package/dist/package/src/api/system/admin/v1/base_session.ts +32 -1
  48. package/dist/package/src/components/DynamicI18nCell.vue +55 -220
  49. package/dist/package/src/components/DynamicI18nEditor.vue +150 -146
  50. package/dist/package/src/components/dynamicI18n.ts +10 -6
  51. package/dist/package/src/config/index.ts +1 -3
  52. package/dist/package/src/locales/en-US.json +1444 -1435
  53. package/dist/package/src/locales/ja-JP.json +1448 -1439
  54. package/dist/package/src/locales/zh-CN.json +1444 -1435
  55. package/dist/package/src/locales/zh-TW.json +1445 -1436
  56. package/dist/package/src/rpc/base/v1/ai_message.ts +1 -1
  57. package/dist/package/src/rpc/base/v1/ai_session.ts +1 -1
  58. package/dist/package/src/rpc/base/v1/ai_tool.ts +1 -1
  59. package/dist/package/src/rpc/base/v1/config.ts +1 -1
  60. package/dist/package/src/rpc/base/v1/login.ts +3 -1
  61. package/dist/package/src/rpc/base/v1/notification.ts +2 -2
  62. package/dist/package/src/rpc/base/v1/oauth.ts +5 -1
  63. package/dist/package/src/rpc/base/v1/oauth_client.ts +2 -2
  64. package/dist/package/src/rpc/base/v1/sse.ts +1 -1
  65. package/dist/package/src/rpc/common/v1/analytics.ts +1 -1
  66. package/dist/package/src/rpc/common/v1/common.ts +1 -1
  67. package/dist/package/src/rpc/common/v1/enum.ts +1 -1
  68. package/dist/package/src/rpc/common/v1/types.ts +1 -1
  69. package/dist/package/src/rpc/google/protobuf/empty.ts +1 -1
  70. package/dist/package/src/rpc/google/protobuf/timestamp.ts +1 -1
  71. package/dist/package/src/rpc/system/admin/v1/auth.ts +1 -1
  72. package/dist/package/src/rpc/system/admin/v1/base_api.ts +1 -1
  73. package/dist/package/src/rpc/system/admin/v1/base_api_log.ts +2 -2
  74. package/dist/package/src/rpc/system/admin/v1/base_area.ts +1 -1
  75. package/dist/package/src/rpc/system/admin/v1/base_config.ts +4 -44
  76. package/dist/package/src/rpc/system/admin/v1/base_dashboard.ts +1 -1
  77. package/dist/package/src/rpc/system/admin/v1/base_data_access_log.ts +2 -2
  78. package/dist/package/src/rpc/system/admin/v1/base_dept.ts +1 -1
  79. package/dist/package/src/rpc/system/admin/v1/base_dict.ts +1 -1
  80. package/dist/package/src/rpc/system/admin/v1/base_dict_item.ts +1 -1
  81. package/dist/package/src/rpc/system/admin/v1/base_file.ts +1 -1
  82. package/dist/package/src/rpc/system/admin/v1/base_i18n.ts +2 -2
  83. package/dist/package/src/rpc/system/admin/v1/base_job.ts +1 -1
  84. package/dist/package/src/rpc/system/admin/v1/base_job_log.ts +1 -1
  85. package/dist/package/src/rpc/system/admin/v1/base_language.ts +1 -1
  86. package/dist/package/src/rpc/system/admin/v1/base_log.ts +2 -2
  87. package/dist/package/src/rpc/system/admin/v1/base_login_log.ts +12 -2
  88. package/dist/package/src/rpc/system/admin/v1/base_login_policy.ts +11 -1
  89. package/dist/package/src/rpc/system/admin/v1/base_menu.ts +1 -1
  90. package/dist/package/src/rpc/system/admin/v1/base_message.ts +2 -2
  91. package/dist/package/src/rpc/system/admin/v1/base_message_category.ts +2 -2
  92. package/dist/package/src/rpc/system/admin/v1/base_migration.ts +2 -2
  93. package/dist/package/src/rpc/system/admin/v1/base_operation_log.ts +2 -2
  94. package/dist/package/src/rpc/system/admin/v1/base_permission_log.ts +2 -2
  95. package/dist/package/src/rpc/system/admin/v1/base_policy_evaluation_log.ts +2 -2
  96. package/dist/package/src/rpc/system/admin/v1/base_post.ts +1 -1
  97. package/dist/package/src/rpc/system/admin/v1/base_redact_common.ts +1 -1
  98. package/dist/package/src/rpc/system/admin/v1/base_redact_output_policy.ts +2 -2
  99. package/dist/package/src/rpc/system/admin/v1/base_redact_rule.ts +1 -1
  100. package/dist/package/src/rpc/system/admin/v1/base_redact_storage_policy.ts +2 -2
  101. package/dist/package/src/rpc/system/admin/v1/base_role.ts +1 -1
  102. package/dist/package/src/rpc/system/admin/v1/base_session.ts +47 -1
  103. package/dist/package/src/rpc/system/admin/v1/base_table_archive.ts +2 -2
  104. package/dist/package/src/rpc/system/admin/v1/base_table_archive_record.ts +2 -2
  105. package/dist/package/src/rpc/system/admin/v1/base_table_archive_restore.ts +2 -2
  106. package/dist/package/src/rpc/system/admin/v1/base_table_backup.ts +2 -2
  107. package/dist/package/src/rpc/system/admin/v1/base_table_backup_record.ts +2 -2
  108. package/dist/package/src/rpc/system/admin/v1/base_table_backup_restore.ts +2 -2
  109. package/dist/package/src/rpc/system/admin/v1/base_table_source.ts +2 -2
  110. package/dist/package/src/rpc/system/admin/v1/base_tenant.ts +1 -1
  111. package/dist/package/src/rpc/system/admin/v1/base_third_account.ts +1 -1
  112. package/dist/package/src/rpc/system/admin/v1/base_user.ts +1 -1
  113. package/dist/package/src/rpc/system/admin/v1/cache.ts +2 -2
  114. package/dist/package/src/rpc/system/admin/v1/code_gen.ts +1 -1
  115. package/dist/package/src/rpc/system/admin/v1/code_gen_column.ts +1 -1
  116. package/dist/package/src/rpc/system/admin/v1/code_gen_proto.ts +1 -1
  117. package/dist/package/src/rpc/system/admin/v1/code_gen_table.ts +1 -1
  118. package/dist/package/src/rpc/system/admin/v1/common.ts +1 -1
  119. package/dist/package/src/rpc/system/admin/v1/oauth_client.ts +2 -2
  120. package/dist/package/src/rpc/system/admin/v1/ops_monitoring.ts +2 -2
  121. package/dist/package/src/rpc/system/admin/v1/runtime_log.ts +1 -1
  122. package/dist/package/src/rpc/system/common/v1/common.ts +1 -1
  123. package/dist/package/src/rpc/system/config/v1/system_config.ts +1 -1
  124. package/dist/package/src/utils/code-gen-progress.ts +15 -0
  125. package/dist/package/src/views/base/config/index.vue +183 -48
  126. package/dist/package/src/views/base/dict/index.vue +6 -9
  127. package/dist/package/src/views/base/dict/item.vue +6 -15
  128. package/dist/package/src/views/base/job/index.vue +16 -17
  129. package/dist/package/src/views/base/login-policy/index.vue +28 -0
  130. package/dist/package/src/views/base/menu/index.vue +78 -15
  131. package/dist/package/src/views/base/online-session/index.vue +116 -0
  132. package/dist/package/src/views/base/user/index.vue +20 -8
  133. package/dist/package/src/views/profile/components/login-log.vue +123 -0
  134. package/dist/package/src/views/profile/components/session.vue +46 -61
  135. package/dist/package/src/views/profile/index.vue +13 -9
  136. package/dist/package/src/views/tool/code-gen/code-preview/index.vue +5 -15
  137. package/dist/package/src/views/tool/code-gen/columns/index.vue +17 -25
  138. package/dist/package/src/views/tool/code-gen/components/CodeGenGenerateConfirm.vue +115 -0
  139. package/dist/package/src/views/tool/code-gen/components/CodeGenLocaleEditor.vue +27 -186
  140. package/dist/package/src/views/tool/code-gen/components/CodeGenProgressDialog.vue +2 -0
  141. package/dist/package/src/views/tool/code-gen/config.ts +15 -1
  142. package/dist/package/src/views/tool/code-gen/table/index.vue +100 -45
  143. package/package.json +3 -2
  144. package/dist/package/declarations/src/config/database-backup.d.ts +0 -12
  145. package/dist/package/src/config/database-backup.ts +0 -57
  146. package/dist/package/src/views/base/runtime-config/index.vue +0 -479
  147. /package/dist/package/declarations/src/views/base/{runtime-config → online-session}/index.vue.d.ts +0 -0
@@ -18,7 +18,8 @@
18
18
  ref="formDialogRef"
19
19
  class="menu-form-dialog"
20
20
  :title="t(dialog.editing ? 'system.base.menu.action.edit' : 'system.base.menu.action.create')"
21
- width="1180px"
21
+ width="min(1440px, calc(100vw - 32px))"
22
+ top="4vh"
22
23
  label-width="180px"
23
24
  :model="formData"
24
25
  :fields="formFields"
@@ -32,11 +33,7 @@
32
33
  </template>
33
34
 
34
35
  <template #i18ns>
35
- <DynamicI18nEditor
36
- v-model="i18nValues"
37
- :source="formData.meta.title"
38
- :maxlength="100"
39
- />
36
+ <DynamicI18nEditor v-model="i18nValues" :source="formData.meta.title" :maxlength="100" />
40
37
  </template>
41
38
 
42
39
  <template #apiTransferItem="slotScope">
@@ -452,6 +449,7 @@ const formFields = computed<ProFormField[]>(() => [
452
449
  },
453
450
  {
454
451
  prop: "meta.title",
452
+ suffixSlotName: "i18ns",
455
453
  label: t(
456
454
  formData.type === BaseMenuType.BASE_MENU_TYPE_BUTTON ? "system.base.menu.field.button_name" : "system.base.menu.field.title"
457
455
  ),
@@ -467,13 +465,6 @@ const formFields = computed<ProFormField[]>(() => [
467
465
  )
468
466
  })
469
467
  },
470
- {
471
- prop: "i18ns",
472
- label: t("system.base.i18n.field.i18ns"),
473
- component: "slot",
474
- slotName: "i18ns",
475
- colSpan: 24
476
- },
477
468
  {
478
469
  prop: "path",
479
470
  label: getPathFieldLabel(),
@@ -666,6 +657,7 @@ const formFields = computed<ProFormField[]>(() => [
666
657
  slotName: "apiTransferItem",
667
658
  options: transferData.value,
668
659
  props: {
660
+ class: "menu-api-transfer",
669
661
  filterable: true,
670
662
  titles: [t("system.base.menu.value.available_api"), t("system.base.menu.value.selected_api")]
671
663
  },
@@ -1002,7 +994,8 @@ function buildSubmitPayload(): BaseMenuForm {
1002
994
  payload.i18ns = serializeDynamicI18ns(
1003
995
  i18nValues.value,
1004
996
  I18nTargetType.I18N_TARGET_TYPE_BASE_MENU_META_TITLE,
1005
- payload.id
997
+ payload.id,
998
+ payload.meta?.title ?? ""
1006
999
  );
1007
1000
  // 一级菜单在表单中保持空白,提交时仍按接口约定传回根节点标识。
1008
1001
  if (payload.id > 0 && payload.parent_id === undefined) payload.parent_id = 0;
@@ -1145,7 +1138,7 @@ function resetForm(data?: Partial<BaseMenuForm>) {
1145
1138
  formDialogRef.value?.resetFields();
1146
1139
  formDialogRef.value?.clearValidate();
1147
1140
  Object.assign(formData, normalizeMenuForm(data));
1148
- i18nValues.value = normalizeDynamicI18ns(data?.i18ns as DynamicI18nRecord[] | undefined);
1141
+ i18nValues.value = normalizeDynamicI18ns(data?.i18ns as DynamicI18nRecord[] | undefined);
1149
1142
  }
1150
1143
 
1151
1144
  /** 提交菜单表单,并在成功后关闭弹窗、刷新表格。 */
@@ -1249,3 +1242,73 @@ function handleDeleteMenu(selected?: number | string | Array<number | string> |
1249
1242
  align-items: flex-start;
1250
1243
  }
1251
1244
  </style>
1245
+
1246
+ <style lang="scss">
1247
+ .menu-form-dialog {
1248
+ .menu-api-transfer {
1249
+ display: grid;
1250
+ grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
1251
+ align-items: center;
1252
+ gap: 16px;
1253
+ width: 100%;
1254
+ .el-transfer-panel {
1255
+ width: 100%;
1256
+ min-width: 0;
1257
+ }
1258
+ .el-transfer__buttons {
1259
+ display: flex;
1260
+ flex-direction: column;
1261
+ gap: 12px;
1262
+ padding: 0;
1263
+ .el-button {
1264
+ margin: 0;
1265
+ }
1266
+ }
1267
+ .el-transfer-panel__body {
1268
+ height: 360px;
1269
+ }
1270
+ .el-transfer-panel__list.is-filterable {
1271
+ height: 300px;
1272
+ }
1273
+ .el-transfer-panel__item.el-checkbox {
1274
+ align-items: flex-start;
1275
+ height: auto;
1276
+ min-height: 36px;
1277
+ padding-top: 8px;
1278
+ padding-bottom: 8px;
1279
+ .el-checkbox__input {
1280
+ top: 3px;
1281
+ }
1282
+ .el-checkbox__label {
1283
+ display: block;
1284
+ white-space: normal;
1285
+ overflow-wrap: anywhere;
1286
+ line-height: 20px;
1287
+ }
1288
+ }
1289
+ }
1290
+ }
1291
+
1292
+ @media (max-width: 767px) {
1293
+ .menu-form-dialog {
1294
+ .el-col {
1295
+ flex: 0 0 100%;
1296
+ max-width: 100%;
1297
+ }
1298
+ .el-form-item {
1299
+ flex-direction: column;
1300
+ }
1301
+ .el-form-item__label {
1302
+ justify-content: flex-start;
1303
+ width: auto !important;
1304
+ }
1305
+ .menu-api-transfer {
1306
+ grid-template-columns: minmax(0, 1fr);
1307
+ .el-transfer__buttons {
1308
+ flex-direction: row;
1309
+ justify-content: center;
1310
+ }
1311
+ }
1312
+ }
1313
+ }
1314
+ </style>
@@ -0,0 +1,116 @@
1
+ <template>
2
+ <div class="online-session-page">
3
+ <el-card class="admin-page-card">
4
+ <ProTable
5
+ :key="isDefaultTenant ? 'default-tenant' : 'current-tenant'"
6
+ ref="proTable"
7
+ row-key="session_id"
8
+ :columns="columns"
9
+ :request-api="requestTable"
10
+ />
11
+ </el-card>
12
+ </div>
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ import { computed, ref } from "vue";
17
+ import ProTable from "@liujitcn/kratos-admin-core/components/ProTable";
18
+ import type { ColumnProps, EnumProps, ProTableInstance } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
19
+ import { DEFAULT_TENANT_CODE } from "@liujitcn/kratos-admin-core/tenant";
20
+ import { useUserStore } from "@liujitcn/kratos-admin-core/stores/runtime";
21
+ import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
22
+ import { buildPageRequest } from "@liujitcn/kratos-admin-core/table";
23
+ import { t } from "@liujitcn/kratos-admin-core";
24
+ import { defBaseTenantService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_tenant";
25
+ import { defBaseSessionService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_session";
26
+ import type { BaseSession, PageOnlineBaseSessionsRequest } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_session";
27
+
28
+ defineOptions({ name: "BaseOnlineSession", inheritAttrs: false });
29
+
30
+ const { BUTTONS } = useAuthButtons();
31
+ const proTable = ref<ProTableInstance>();
32
+ const userStore = useUserStore();
33
+ const isDefaultTenant = computed(() => userStore.userInfo.tenant_code === DEFAULT_TENANT_CODE);
34
+
35
+ const columns = computed<ColumnProps[]>(() => [
36
+ {
37
+ prop: "tenant_code",
38
+ label: t("common.field.tenant"),
39
+ minWidth: 130,
40
+ search: isDefaultTenant.value ? { el: "select", props: { filterable: true }, order: 1 } : undefined,
41
+ enum: requestSessionTenantOptions
42
+ },
43
+ {
44
+ prop: "user_name",
45
+ label: t("system.base.online_session.field.user_name"),
46
+ minWidth: 140,
47
+ search: { el: "input", key: "keyword", order: 2 }
48
+ },
49
+ { prop: "client_ip", label: t("system.base.online_session.field.client_ip"), minWidth: 140 },
50
+ { prop: "device", label: t("system.base.online_session.field.device"), minWidth: 180 },
51
+ { prop: "user_agent", label: t("system.base.online_session.field.user_agent"), minWidth: 220 },
52
+ { prop: "issued_at", label: t("system.base.online_session.field.issued_at"), minWidth: 180 },
53
+ {
54
+ prop: "operation",
55
+ label: t("common.field.operation"),
56
+ width: 100,
57
+ fixed: "right",
58
+ cellType: "actions",
59
+ actions: [
60
+ {
61
+ label: t("system.base.online_session.action.revoke"),
62
+ type: "danger",
63
+ link: true,
64
+ icon: SwitchButton,
65
+ hidden: () => !BUTTONS.value["base:online-session:revoke"],
66
+ onClick: scope => revokeSession(scope.row as BaseSession)
67
+ }
68
+ ]
69
+ }
70
+ ]);
71
+
72
+ /** 将租户名称映射到会话使用的租户编码,供下拉筛选与表格展示复用。 */
73
+ async function requestSessionTenantOptions() {
74
+ if (!isDefaultTenant.value) {
75
+ return { data: [{ value: userStore.userInfo.tenant_code, label: userStore.userInfo.tenant_name }] };
76
+ }
77
+ const options: EnumProps[] = [];
78
+ const pageSize = 100;
79
+ for (let pageNum = 1; ; pageNum++) {
80
+ const response = await defBaseTenantService.PageBaseTenant({ code: "", name: "", page_num: pageNum, page_size: pageSize });
81
+ const tenants = response.base_tenants ?? [];
82
+ options.push(...tenants.map(tenant => ({ value: tenant.code, label: tenant.name })));
83
+ if (options.length >= response.total || tenants.length === 0) break;
84
+ }
85
+ return { data: options };
86
+ }
87
+
88
+ /** 查询在线用户会话列表。 */
89
+ async function requestTable(params: Record<string, unknown>) {
90
+ const response = await defBaseSessionService.PageOnlineBaseSessions(
91
+ buildPageRequest<PageOnlineBaseSessionsRequest>({
92
+ ...(params as unknown as PageOnlineBaseSessionsRequest),
93
+ tenant_code: isDefaultTenant.value ? String(params.tenant_code ?? "") : ""
94
+ })
95
+ );
96
+ return { data: { list: response.sessions ?? [], total: response.total } };
97
+ }
98
+
99
+ /** 下线指定在线用户会话。 */
100
+ async function revokeSession(row: BaseSession) {
101
+ await ElMessageBox.confirm(
102
+ t("system.base.online_session.confirm.revoke", { user_name: row.user_name }),
103
+ t("common.title.warning"),
104
+ { type: "warning" }
105
+ );
106
+ await defBaseSessionService.RevokeBaseSession({ session_id: row.session_id });
107
+ ElMessage.success(t("system.base.online_session.message.revoked"));
108
+ proTable.value?.getTableList();
109
+ }
110
+ </script>
111
+
112
+ <style scoped lang="scss">
113
+ .online-session-page {
114
+ min-width: 0;
115
+ }
116
+ </style>
@@ -27,7 +27,9 @@
27
27
  v-model="dialog.visible"
28
28
  ref="formDialogRef"
29
29
  :title="t(dialog.editing ? 'system.base.user.action.edit' : 'system.base.user.action.create')"
30
- width="800px"
30
+ width="960px"
31
+ label-width="120px"
32
+ :col-span="12"
31
33
  :model="formData"
32
34
  :fields="formFields"
33
35
  :rules="rules"
@@ -400,12 +402,6 @@ const formFields = computed<ProFormField[]>(() => [
400
402
  component: "input",
401
403
  props: { placeholder: t("common.validation.phone_required") }
402
404
  },
403
- {
404
- prop: "email",
405
- label: t("system.base.user.field.email"),
406
- component: "input",
407
- props: { placeholder: t("system.base.user.placeholder.email") }
408
- },
409
405
  {
410
406
  prop: "id_type",
411
407
  label: t("system.base.user.field.id_type"),
@@ -419,10 +415,19 @@ const formFields = computed<ProFormField[]>(() => [
419
415
  component: "input",
420
416
  props: { placeholder: t("system.base.user.placeholder.id_code") }
421
417
  },
418
+ {
419
+ prop: "email",
420
+ label: t("system.base.user.field.email"),
421
+ component: "input",
422
+ colSpan: 24,
423
+ props: { placeholder: t("system.base.user.placeholder.email") }
424
+ },
422
425
  {
423
426
  prop: "pwd",
424
427
  label: t("system.base.user.field.password"),
425
428
  component: "password",
429
+ rowBreakBefore: true,
430
+ colSpan: 24,
426
431
  props: { placeholder: t("system.base.user.placeholder.password_optional"), showPassword: true },
427
432
  visible: model => !model.id
428
433
  },
@@ -431,9 +436,15 @@ const formFields = computed<ProFormField[]>(() => [
431
436
  label: t("system.base.user.field.password_strength"),
432
437
  component: "slot",
433
438
  slotName: "passwordStrength",
439
+ colSpan: 24,
434
440
  visible: model => !model.id
435
441
  },
436
- { prop: "gender", label: t("system.base.user.field.gender"), component: "dict", props: { code: "base_user_gender" } },
442
+ {
443
+ prop: "gender",
444
+ label: t("system.base.user.field.gender"),
445
+ component: "dict",
446
+ props: { code: "base_user_gender", style: { width: "100%" } }
447
+ },
437
448
  {
438
449
  prop: "status",
439
450
  label: t("common.field.status"),
@@ -445,6 +456,7 @@ const formFields = computed<ProFormField[]>(() => [
445
456
  prop: "remark",
446
457
  label: t("common.field.remark"),
447
458
  component: "textarea",
459
+ colSpan: 24,
448
460
  props: { placeholder: t("common.placeholder.remark") }
449
461
  }
450
462
  ]);
@@ -0,0 +1,123 @@
1
+ <template>
2
+ <el-card class="login-log-card" shadow="never">
3
+ <template #header>
4
+ <div class="login-log-header">
5
+ <div>
6
+ <h3>{{ t("system.profile.login_log.title") }}</h3>
7
+ <p>{{ t("system.profile.login_log.description") }}</p>
8
+ </div>
9
+ <el-button :loading="loading" @click="refresh">
10
+ <el-icon><Refresh /></el-icon>
11
+ {{ t("system.profile.login_log.action.refresh") }}
12
+ </el-button>
13
+ </div>
14
+ </template>
15
+ <ProTable ref="proTable" row-key="id" :columns="columns" :request-api="requestTable" />
16
+ </el-card>
17
+ </template>
18
+
19
+ <script setup lang="ts">
20
+ import { computed, ref } from "vue";
21
+ import ProTable from "@liujitcn/kratos-admin-core/components/ProTable";
22
+ import type { ColumnProps, ProTableInstance } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
23
+ import { buildPageRequest } from "@liujitcn/kratos-admin-core/table";
24
+ import { t } from "@liujitcn/kratos-admin-core";
25
+ import { defBaseLoginLogService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_login_log";
26
+ import { BaseLogResult } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_log";
27
+ import { BaseLoginLogType } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_login_log";
28
+ import type { BaseLoginLog } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_login_log";
29
+
30
+ defineOptions({ name: "ProfileLoginLog", inheritAttrs: false });
31
+
32
+ const proTable = ref<ProTableInstance>();
33
+ const loading = ref(false);
34
+ const loginTypeOptions = computed<Array<[number, string]>>(() => [
35
+ [BaseLoginLogType.BASE_LOGIN_LOG_TYPE_PASSWORD, t("system.base.log.login_type.password")],
36
+ [BaseLoginLogType.BASE_LOGIN_LOG_TYPE_OAUTH, t("system.base.log.login_type.oauth")],
37
+ [BaseLoginLogType.BASE_LOGIN_LOG_TYPE_MFA, t("system.base.log.login_type.mfa")],
38
+ [BaseLoginLogType.BASE_LOGIN_LOG_TYPE_TOKEN_REFRESH, t("system.base.log.login_type.token_refresh")],
39
+ [BaseLoginLogType.BASE_LOGIN_LOG_TYPE_LOGOUT, t("system.base.log.login_type.logout")]
40
+ ]);
41
+ const resultOptions = computed<Array<[number, string]>>(() => [
42
+ [BaseLogResult.BASE_LOG_RESULT_SUCCESS, t("system.base.log.result.success")],
43
+ [BaseLogResult.BASE_LOG_RESULT_FAILURE, t("system.base.log.result.failure")],
44
+ [BaseLogResult.BASE_LOG_RESULT_ERROR, t("system.base.log.result.error")]
45
+ ]);
46
+ const columns = computed<ColumnProps[]>(() => [
47
+ { prop: "occurred_at", label: t("system.profile.login_log.field.occurred_at"), minWidth: 180 },
48
+ {
49
+ prop: "login_type",
50
+ label: t("system.profile.login_log.field.login_type"),
51
+ minWidth: 130,
52
+ render: scope => enumLabel(loginTypeOptions.value, (scope.row as BaseLoginLog).login_type)
53
+ },
54
+ {
55
+ prop: "result",
56
+ label: t("system.profile.login_log.field.result"),
57
+ minWidth: 100,
58
+ render: scope => enumLabel(resultOptions.value, (scope.row as BaseLoginLog).result)
59
+ },
60
+ { prop: "client_ip", label: t("system.profile.login_log.field.client_ip"), minWidth: 140 },
61
+ { prop: "device_id", label: t("system.profile.login_log.field.device_id"), minWidth: 160 },
62
+ { prop: "reason", label: t("system.profile.login_log.field.reason"), minWidth: 180 }
63
+ ]);
64
+
65
+ /** 查询当前用户的登录记录。 */
66
+ async function requestTable(params: Record<string, unknown>) {
67
+ const response = await defBaseLoginLogService.PageCurrentUserLoginLog(
68
+ buildPageRequest({ page_num: Number(params.page_num ?? 1), page_size: Number(params.page_size ?? 10) })
69
+ );
70
+ return { data: { list: response.base_login_logs ?? [], total: response.total } };
71
+ }
72
+
73
+ /** 刷新当前用户的登录记录。 */
74
+ async function refresh() {
75
+ loading.value = true;
76
+ try {
77
+ await proTable.value?.getTableList();
78
+ } finally {
79
+ loading.value = false;
80
+ }
81
+ }
82
+
83
+ /** 输出枚举显示文本。 */
84
+ function enumLabel(options: Array<[number, string]>, value: number) {
85
+ return options.find(option => option[0] === value)?.[1] ?? String(value);
86
+ }
87
+ </script>
88
+
89
+ <style scoped lang="scss">
90
+ .login-log-card {
91
+ border: 1px solid var(--el-border-color-light);
92
+ border-radius: var(--admin-page-radius);
93
+ }
94
+ :deep(.login-log-card .el-card__header) {
95
+ padding: 18px 20px;
96
+ border-bottom: 1px solid var(--el-border-color-light);
97
+ }
98
+ :deep(.login-log-card .el-card__body) {
99
+ padding: 20px;
100
+ }
101
+ .login-log-header {
102
+ display: flex;
103
+ align-items: center;
104
+ justify-content: space-between;
105
+ gap: 12px;
106
+ }
107
+ .login-log-header h3 {
108
+ margin: 0;
109
+ color: var(--el-text-color-primary);
110
+ font-size: 18px;
111
+ }
112
+ .login-log-header p {
113
+ margin: 6px 0 0;
114
+ color: var(--el-text-color-secondary);
115
+ font-size: 13px;
116
+ }
117
+ @media screen and (width <= 640px) {
118
+ .login-log-header {
119
+ align-items: flex-start;
120
+ flex-direction: column;
121
+ }
122
+ }
123
+ </style>
@@ -13,43 +13,20 @@
13
13
  </div>
14
14
  </template>
15
15
 
16
- <el-skeleton v-if="loading && !session" :rows="5" animated />
17
- <el-empty v-else-if="!session" :description="t('system.profile.session.empty')" />
18
- <div v-else class="session-details">
19
- <div class="session-status">
20
- <el-tag type="success" effect="plain">{{ t("system.profile.session.status.active") }}</el-tag>
21
- <span>{{ session.user_name }} · {{ session.tenant_code }}</span>
22
- </div>
23
- <dl class="session-grid">
24
- <div>
25
- <dt>{{ t("system.profile.session.field.client_ip") }}</dt>
26
- <dd>{{ session.client_ip || "-" }}</dd>
27
- </div>
28
- <div>
29
- <dt>{{ t("system.profile.session.field.device") }}</dt>
30
- <dd>{{ session.device || "-" }}</dd>
31
- </div>
32
- <div>
33
- <dt>{{ t("system.profile.session.field.issued_at") }}</dt>
34
- <dd>{{ session.issued_at || "-" }}</dd>
35
- </div>
36
- <div>
37
- <dt>{{ t("system.profile.session.field.expires_in") }}</dt>
38
- <dd>{{ formatExpires(session.expires_in) }}</dd>
39
- </div>
40
- </dl>
41
- <div class="session-actions">
42
- <el-button type="danger" plain @click="revokeAll">
43
- <el-icon><SwitchButton /></el-icon>
44
- {{ t("system.profile.session.action.revoke_all") }}
45
- </el-button>
46
- </div>
16
+ <ProTable ref="proTable" row-key="session_id" :columns="columns" :request-api="requestSessionTable" :pagination="false" />
17
+ <div class="session-actions">
18
+ <el-button type="danger" plain :disabled="!hasSessions" @click="revokeAll">
19
+ <el-icon><SwitchButton /></el-icon>
20
+ {{ t("system.profile.session.action.revoke_all") }}
21
+ </el-button>
47
22
  </div>
48
23
  </el-card>
49
24
  </template>
50
25
 
51
26
  <script setup lang="ts">
52
- import { onMounted, ref } from "vue";
27
+ import { computed, ref } from "vue";
28
+ import ProTable from "@liujitcn/kratos-admin-core/components/ProTable";
29
+ import type { ColumnProps, ProTableInstance } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
53
30
  import { useRouter } from "vue-router";
54
31
  import { t } from "@liujitcn/kratos-admin-core";
55
32
  import { LOGIN_URL } from "@liujitcn/kratos-admin-core/config";
@@ -58,15 +35,47 @@ import { defBaseSessionService } from "@liujitcn/kratos-admin-system/api/system/
58
35
  import type { BaseSession } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_session";
59
36
 
60
37
  const loading = ref(false);
61
- const session = ref<BaseSession>();
38
+ const proTable = ref<ProTableInstance>();
39
+ const hasSessions = ref(false);
40
+ const columns = computed<ColumnProps[]>(() => [
41
+ {
42
+ prop: "current",
43
+ label: t("system.profile.session.field.current"),
44
+ cellType: "status",
45
+ minWidth: 130,
46
+ statusProps: {
47
+ activeValue: true,
48
+ inactiveValue: false,
49
+ activeText: t("system.profile.session.status.current"),
50
+ inactiveText: t("system.profile.session.status.other")
51
+ }
52
+ },
53
+ { prop: "client_ip", label: t("system.profile.session.field.client_ip"), minWidth: 140 },
54
+ { prop: "device", label: t("system.profile.session.field.device"), minWidth: 200 },
55
+ { prop: "issued_at", label: t("system.profile.session.field.issued_at"), minWidth: 180 },
56
+ {
57
+ prop: "expires_in",
58
+ label: t("system.profile.session.field.expires_in"),
59
+ minWidth: 140,
60
+ render: scope => formatExpires((scope.row as BaseSession).expires_in)
61
+ }
62
+ ]);
63
+
64
+ /** 查询本人全部有效会话。 */
65
+ async function requestSessionTable() {
66
+ const response = await defBaseSessionService.ListCurrentBaseSessions({});
67
+ const sessions = response.sessions ?? [];
68
+ hasSessions.value = sessions.length > 0;
69
+ return { data: sessions };
70
+ }
62
71
  const router = useRouter();
63
72
  const userStore = useUserStore();
64
73
 
65
- /** 拉取当前会话信息。 */
74
+ /** 刷新本人会话列表。 */
66
75
  async function loadSession() {
67
76
  loading.value = true;
68
77
  try {
69
- session.value = await defBaseSessionService.GetCurrentBaseSession({});
78
+ await proTable.value?.getTableList();
70
79
  } finally {
71
80
  loading.value = false;
72
81
  }
@@ -80,7 +89,7 @@ async function revokeAll() {
80
89
  await defBaseSessionService.RevokeAllBaseSessions({});
81
90
  userStore.clearAuthData();
82
91
  ElMessage.success(t("system.profile.session.message.revoked"));
83
- session.value = undefined;
92
+ hasSessions.value = false;
84
93
  await router.replace(LOGIN_URL);
85
94
  }
86
95
 
@@ -92,8 +101,6 @@ function formatExpires(seconds: number) {
92
101
  if (hours > 0) return t("system.profile.session.value.hours_minutes", { hours, minutes });
93
102
  return t("system.profile.session.value.minutes", { minutes });
94
103
  }
95
-
96
- onMounted(loadSession);
97
104
  </script>
98
105
 
99
106
  <style scoped lang="scss">
@@ -126,36 +133,14 @@ onMounted(loadSession);
126
133
  color: var(--el-text-color-secondary);
127
134
  font-size: 13px;
128
135
  }
129
- .session-grid {
130
- display: grid;
131
- grid-template-columns: repeat(2, minmax(0, 1fr));
132
- gap: 16px;
133
- margin: 24px 0;
134
- }
135
- .session-grid div {
136
- padding: 14px;
137
- background: var(--el-fill-color-light);
138
- border-radius: var(--admin-page-radius);
139
- }
140
- .session-grid dt {
141
- color: var(--el-text-color-secondary);
142
- font-size: 12px;
143
- }
144
- .session-grid dd {
145
- margin: 6px 0 0;
146
- color: var(--el-text-color-primary);
147
- overflow-wrap: anywhere;
148
- }
149
136
  .session-actions {
150
137
  justify-content: flex-end;
138
+ margin-top: 20px;
151
139
  }
152
140
  @media screen and (width <= 640px) {
153
141
  .session-header {
154
142
  align-items: flex-start;
155
143
  flex-direction: column;
156
144
  }
157
- .session-grid {
158
- grid-template-columns: 1fr;
159
- }
160
145
  }
161
146
  </style>
@@ -27,6 +27,7 @@
27
27
  @switch-tab="handleTabChange"
28
28
  />
29
29
  <ProfileSession v-else-if="activeTab === 'session'" />
30
+ <ProfileLoginLog v-else-if="activeTab === 'login-log'" />
30
31
  <ProfilePassword v-else />
31
32
  </main>
32
33
  </section>
@@ -49,9 +50,10 @@ import ProfileBase from "./components/base.vue";
49
50
  import ProfileSecurity from "./components/security.vue";
50
51
  import ProfilePassword from "./components/password.vue";
51
52
  import ProfileSession from "./components/session.vue";
53
+ import ProfileLoginLog from "./components/login-log.vue";
52
54
 
53
55
  /** 个人中心标签页。 */
54
- type ProfileTab = "account" | "security" | "password" | "session";
56
+ type ProfileTab = "account" | "security" | "password" | "session" | "login-log";
55
57
 
56
58
  /** 左侧导航项结构。 */
57
59
  interface ProfileTabOption {
@@ -84,13 +86,16 @@ const profileTabs = computed<ProfileTabOption[]>(() => {
84
86
  description: t("system.profile.password.nav_description")
85
87
  }
86
88
  ];
87
- if (userStore.userInfo.role_code === "super") {
88
- tabs.push({
89
- value: "session",
90
- label: t("system.profile.session.title"),
91
- description: t("system.profile.session.nav_description")
92
- });
93
- }
89
+ tabs.push({
90
+ value: "session",
91
+ label: t("system.profile.session.title"),
92
+ description: t("system.profile.session.nav_description")
93
+ });
94
+ tabs.push({
95
+ value: "login-log",
96
+ label: t("system.profile.login_log.title"),
97
+ description: t("system.profile.login_log.nav_description")
98
+ });
94
99
  return tabs;
95
100
  });
96
101
  const userProfileForm = reactive<UserProfileForm>({
@@ -210,5 +215,4 @@ onMounted(async () => {
210
215
  position: static;
211
216
  }
212
217
  }
213
-
214
218
  </style>
@@ -135,21 +135,11 @@ async function handleGenerate() {
135
135
  return;
136
136
  }
137
137
  if (missingI18ns.value.length) {
138
- try {
139
- await ElMessageBox.alert(
140
- t("system.code.gen.preview.message.missing_i18ns", {
141
- items: missingI18ns.value.join(t("system.code.gen.preview.value.list_separator"))
142
- }),
143
- t("common.title.warning"),
144
- {
145
- confirmButtonText: t("common.action.close"),
146
- type: "warning"
147
- }
148
- );
149
- } catch {
150
- // 关闭提示框与点击关闭按钮语义一致。
151
- }
152
- return;
138
+ ElMessage.warning(
139
+ t("system.code.gen.preview.message.missing_i18ns", {
140
+ items: missingI18ns.value.join(t("system.code.gen.preview.value.list_separator"))
141
+ })
142
+ );
153
143
  }
154
144
  try {
155
145
  await ElMessageBox.confirm(