@liujitcn/kratos-admin-system 0.0.29 → 0.0.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/README.md +18 -1
  2. package/dist/package/declarations/src/api/base/notification.d.ts +24 -0
  3. package/dist/package/declarations/src/api/system/base_audit_log.d.ts +55 -0
  4. package/dist/package/declarations/src/api/system/base_dashboard.d.ts +14 -0
  5. package/dist/package/declarations/src/api/system/base_file.d.ts +13 -0
  6. package/dist/package/declarations/src/api/system/base_login_policy.d.ts +10 -0
  7. package/dist/package/declarations/src/api/system/base_message.d.ts +24 -0
  8. package/dist/package/declarations/src/api/system/base_message_category.d.ts +21 -0
  9. package/dist/package/declarations/src/api/system/base_session.d.ts +11 -0
  10. package/dist/package/declarations/src/api/system/oauth_client.d.ts +24 -0
  11. package/dist/package/declarations/src/components/AuditLogTable.vue.d.ts +45 -0
  12. package/dist/package/declarations/src/components/auditLog.d.ts +9 -0
  13. package/dist/package/declarations/src/locales/generated.d.ts +1312 -16
  14. package/dist/package/declarations/src/rpc/base/v1/login.d.ts +149 -0
  15. package/dist/package/declarations/src/rpc/base/v1/notification.d.ts +281 -0
  16. package/dist/package/declarations/src/rpc/base/v1/oauth.d.ts +25 -0
  17. package/dist/package/declarations/src/rpc/base/v1/oauth_client.d.ts +23 -0
  18. package/dist/package/declarations/src/rpc/system/admin/v1/base_audit_log.d.ts +640 -0
  19. package/dist/package/declarations/src/rpc/system/admin/v1/base_dashboard.d.ts +60 -0
  20. package/dist/package/declarations/src/rpc/system/admin/v1/base_file.d.ts +73 -0
  21. package/dist/package/declarations/src/rpc/system/admin/v1/base_i18n.d.ts +10 -6
  22. package/dist/package/declarations/src/rpc/system/admin/v1/base_job.d.ts +5 -0
  23. package/dist/package/declarations/src/rpc/system/admin/v1/base_login_policy.d.ts +51 -0
  24. package/dist/package/declarations/src/rpc/system/admin/v1/base_message.d.ts +193 -0
  25. package/dist/package/declarations/src/rpc/system/admin/v1/base_message_category.d.ts +125 -0
  26. package/dist/package/declarations/src/rpc/system/admin/v1/base_session.d.ts +33 -0
  27. package/dist/package/declarations/src/rpc/system/admin/v1/base_user.d.ts +3 -3
  28. package/dist/package/declarations/src/rpc/system/admin/v1/oauth_client.d.ts +145 -0
  29. package/dist/package/declarations/src/views/base/api-log/index.vue.d.ts +3 -0
  30. package/dist/package/declarations/src/views/base/dashboard/index.vue.d.ts +3 -0
  31. package/dist/package/declarations/src/views/base/data-access-log/index.vue.d.ts +3 -0
  32. package/dist/package/declarations/src/views/base/file/index.vue.d.ts +3 -0
  33. package/dist/package/declarations/src/views/base/login-log/index.vue.d.ts +3 -0
  34. package/dist/package/declarations/src/views/base/login-policy/index.vue.d.ts +3 -0
  35. package/dist/package/declarations/src/views/base/message/index.vue.d.ts +3 -0
  36. package/dist/package/declarations/src/views/base/message-category/index.vue.d.ts +3 -0
  37. package/dist/package/declarations/src/views/base/oauth-client/index.vue.d.ts +3 -0
  38. package/dist/package/declarations/src/views/base/operation-log/index.vue.d.ts +3 -0
  39. package/dist/package/declarations/src/views/base/permission-log/index.vue.d.ts +3 -0
  40. package/dist/package/declarations/src/views/base/policy-evaluation-log/index.vue.d.ts +3 -0
  41. package/dist/package/declarations/src/views/message/inbox/index.vue.d.ts +3 -0
  42. package/dist/package/declarations/src/views/profile/components/session.vue.d.ts +3 -0
  43. package/dist/package/src/api/base/notification.ts +69 -0
  44. package/dist/package/src/api/base/sse.ts +4 -4
  45. package/dist/package/src/api/system/auth.ts +1 -0
  46. package/dist/package/src/api/system/base_audit_log.ts +168 -0
  47. package/dist/package/src/api/system/base_dashboard.ts +55 -0
  48. package/dist/package/src/api/system/base_file.ts +43 -0
  49. package/dist/package/src/api/system/base_login_policy.ts +32 -0
  50. package/dist/package/src/api/system/base_message.ts +70 -0
  51. package/dist/package/src/api/system/base_message_category.ts +57 -0
  52. package/dist/package/src/api/system/base_session.ts +33 -0
  53. package/dist/package/src/api/system/oauth_client.ts +102 -0
  54. package/dist/package/src/components/AuditLogTable.vue +105 -0
  55. package/dist/package/src/components/Notification.vue +196 -0
  56. package/dist/package/src/components/auditLog.ts +46 -0
  57. package/dist/package/src/locales/README.md +4 -2
  58. package/dist/package/src/locales/en-US.json +334 -10
  59. package/dist/package/src/locales/ja-JP.json +338 -14
  60. package/dist/package/src/locales/zh-CN.json +334 -10
  61. package/dist/package/src/locales/zh-TW.json +337 -13
  62. package/dist/package/src/module.ts +5 -2
  63. package/dist/package/src/rpc/base/v1/login.ts +172 -0
  64. package/dist/package/src/rpc/base/v1/notification.ts +315 -0
  65. package/dist/package/src/rpc/base/v1/oauth.ts +25 -0
  66. package/dist/package/src/rpc/base/v1/oauth_client.ts +33 -0
  67. package/dist/package/src/rpc/system/admin/v1/base_audit_log.ts +737 -0
  68. package/dist/package/src/rpc/system/admin/v1/base_dashboard.ts +81 -0
  69. package/dist/package/src/rpc/system/admin/v1/base_file.ts +88 -0
  70. package/dist/package/src/rpc/system/admin/v1/base_i18n.ts +10 -6
  71. package/dist/package/src/rpc/system/admin/v1/base_job.ts +5 -0
  72. package/dist/package/src/rpc/system/admin/v1/base_login_policy.ts +63 -0
  73. package/dist/package/src/rpc/system/admin/v1/base_message.ts +236 -0
  74. package/dist/package/src/rpc/system/admin/v1/base_message_category.ts +149 -0
  75. package/dist/package/src/rpc/system/admin/v1/base_session.ts +44 -0
  76. package/dist/package/src/rpc/system/admin/v1/base_user.ts +3 -3
  77. package/dist/package/src/rpc/system/admin/v1/oauth_client.ts +174 -0
  78. package/dist/package/src/views/ai/chat/components/ChatPanel.vue +2 -2
  79. package/dist/package/src/views/ai/chat/message.ts +7 -3
  80. package/dist/package/src/views/base/api-log/index.vue +83 -0
  81. package/dist/package/src/views/base/config/index.vue +3 -3
  82. package/dist/package/src/views/base/dashboard/index.vue +200 -0
  83. package/dist/package/src/views/base/data-access-log/index.vue +84 -0
  84. package/dist/package/src/views/base/dict/index.vue +2 -2
  85. package/dist/package/src/views/base/dict/item.vue +2 -2
  86. package/dist/package/src/views/base/file/index.vue +119 -0
  87. package/dist/package/src/views/base/job/index.vue +62 -7
  88. package/dist/package/src/views/base/language/index.vue +2 -1
  89. package/dist/package/src/views/base/login-log/index.vue +92 -0
  90. package/dist/package/src/views/base/login-policy/index.vue +219 -0
  91. package/dist/package/src/views/base/menu/index.vue +2 -2
  92. package/dist/package/src/views/base/message/index.vue +582 -0
  93. package/dist/package/src/views/base/message-category/index.vue +356 -0
  94. package/dist/package/src/views/base/oauth-client/index.vue +411 -0
  95. package/dist/package/src/views/base/operation-log/index.vue +82 -0
  96. package/dist/package/src/views/base/permission-log/index.vue +85 -0
  97. package/dist/package/src/views/base/policy-evaluation-log/index.vue +82 -0
  98. package/dist/package/src/views/message/inbox/index.vue +249 -0
  99. package/dist/package/src/views/profile/components/base.vue +6 -5
  100. package/dist/package/src/views/profile/components/password.vue +2 -2
  101. package/dist/package/src/views/profile/components/security.vue +475 -62
  102. package/dist/package/src/views/profile/components/session.vue +153 -0
  103. package/dist/package/src/views/profile/index.vue +35 -22
  104. package/dist/package/src/views/tool/code-gen/config.ts +1 -0
  105. package/dist/package/src/views/tool/ops-monitoring/index.vue +25 -21
  106. package/package.json +2 -2
  107. package/dist/package/declarations/src/api/system/base_log.d.ts +0 -9
  108. package/dist/package/declarations/src/rpc/system/admin/v1/base_log.d.ts +0 -87
  109. package/dist/package/src/api/system/base_log.ts +0 -32
  110. package/dist/package/src/rpc/system/admin/v1/base_log.ts +0 -101
  111. package/dist/package/src/views/base/log/index.vue +0 -339
  112. /package/dist/package/declarations/src/{views/base/log/index.vue.d.ts → components/Notification.vue.d.ts} +0 -0
@@ -143,7 +143,7 @@ function renderDictNameCell(scope: RenderScope<BaseDict>) {
143
143
  const row = scope.row;
144
144
  return h(DynamicI18nCell, {
145
145
  source: row.name,
146
- targetType: I18nTargetType.I18N_TARGET_TYPE_BASE_DICT,
146
+ targetType: I18nTargetType.I18N_TARGET_TYPE_BASE_DICT_NAME,
147
147
  targetId: row.id,
148
148
  i18ns: row.i18ns
149
149
  });
@@ -296,7 +296,7 @@ function handleSubmit() {
296
296
  const submitData = JSON.parse(JSON.stringify(formData)) as BaseDictForm;
297
297
  submitData.i18ns = serializeDynamicI18ns(
298
298
  i18nValues.value,
299
- I18nTargetType.I18N_TARGET_TYPE_BASE_DICT,
299
+ I18nTargetType.I18N_TARGET_TYPE_BASE_DICT_NAME,
300
300
  submitData.id
301
301
  );
302
302
  const request = submitData.id
@@ -204,7 +204,7 @@ function renderDictItemLabelCell(scope: RenderScope<BaseDictItem>) {
204
204
  const row = scope.row;
205
205
  return h(DynamicI18nCell, {
206
206
  source: row.label,
207
- targetType: I18nTargetType.I18N_TARGET_TYPE_BASE_DICT_ITEM,
207
+ targetType: I18nTargetType.I18N_TARGET_TYPE_BASE_DICT_ITEM_LABEL,
208
208
  targetId: row.id,
209
209
  i18ns: row.i18ns
210
210
  });
@@ -369,7 +369,7 @@ function handleSubmit() {
369
369
  const submitData = JSON.parse(JSON.stringify(formData)) as BaseDictItemForm;
370
370
  submitData.i18ns = serializeDynamicI18ns(
371
371
  i18nValues.value,
372
- I18nTargetType.I18N_TARGET_TYPE_BASE_DICT_ITEM,
372
+ I18nTargetType.I18N_TARGET_TYPE_BASE_DICT_ITEM_LABEL,
373
373
  submitData.id
374
374
  );
375
375
  const request = submitData.id
@@ -0,0 +1,119 @@
1
+ <template>
2
+ <div class="table-box">
3
+ <ProTable
4
+ ref="proTable"
5
+ row-key="id"
6
+ :columns="columns"
7
+ :request-api="requestBaseFileTable"
8
+ />
9
+ </div>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { computed, h, ref } from "vue";
14
+ import ProTable from "@liujitcn/kratos-admin-core/components/ProTable";
15
+ import type { ColumnProps, ProTableInstance } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
16
+ import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
17
+ import { buildPageRequest } from "@liujitcn/kratos-admin-core/table";
18
+ import { t } from "@liujitcn/kratos-admin-core";
19
+ import { defBaseFileService } from "@liujitcn/kratos-admin-system/api/system/base_file";
20
+ import type { BaseFile, PageBaseFileRequest } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_file";
21
+
22
+ defineOptions({ name: "BaseFile", inheritAttrs: false });
23
+
24
+ const { BUTTONS } = useAuthButtons();
25
+ const proTable = ref<ProTableInstance>();
26
+
27
+ const columns = computed<ColumnProps[]>(() => [
28
+ { prop: "file_name", label: t("system.base.file.field.name"), minWidth: 220, search: { el: "input" } },
29
+ { prop: "extension", label: t("system.base.file.field.extension"), width: 100, search: { el: "input" } },
30
+ { prop: "mime_type", label: t("system.base.file.field.mime"), minWidth: 180 },
31
+ { prop: "size", label: t("system.base.file.field.size"), width: 120, render: scope => formatFileSize((scope.row as BaseFile).size) },
32
+ { prop: "content_hash", label: t("system.base.file.field.hash"), minWidth: 220 },
33
+ { prop: "tenant_id", label: t("system.base.file.field.tenant"), width: 100 },
34
+ { prop: "created_at", label: t("common.field.created_at"), minWidth: 180 },
35
+ {
36
+ prop: "operation",
37
+ label: t("common.field.operation"),
38
+ width: 150,
39
+ fixed: "right",
40
+ cellType: "actions",
41
+ actions: [
42
+ {
43
+ label: t("common.action.view"),
44
+ type: "primary",
45
+ link: true,
46
+ icon: View,
47
+ hidden: () => !BUTTONS.value["base:file:info"],
48
+ onClick: scope => handleView(scope.row as BaseFile)
49
+ },
50
+ {
51
+ label: t("common.action.delete"),
52
+ type: "danger",
53
+ link: true,
54
+ icon: Delete,
55
+ hidden: () => !BUTTONS.value["base:file:delete"],
56
+ onClick: scope => handleDelete(scope.row as BaseFile)
57
+ }
58
+ ]
59
+ }
60
+ ]);
61
+
62
+ /** 请求文件资产分页列表。 */
63
+ async function requestBaseFileTable(params: PageBaseFileRequest) {
64
+ const data = await defBaseFileService.PageBaseFile(buildPageRequest(params));
65
+ return { data: { list: data.base_files ?? [], total: data.total } };
66
+ }
67
+
68
+ /** 查看文件资产详情。 */
69
+ async function handleView(row: BaseFile) {
70
+ const detail = await defBaseFileService.GetBaseFile({ id: row.id });
71
+ const lines = [
72
+ `${t("system.base.file.field.name")}:${detail.file_name}`,
73
+ `${t("system.base.file.field.path")}:${detail.file_directory}/${detail.save_file_name}`,
74
+ `${t("system.base.file.field.mime")}:${detail.mime_type}`,
75
+ `${t("system.base.file.field.size")}:${formatFileSize(detail.size)}`,
76
+ `${t("system.base.file.field.hash")}:${detail.content_hash}`,
77
+ `${t("system.base.file.field.url")}:${detail.link_url}`
78
+ ];
79
+ await ElMessageBox.alert(h("pre", { class: "file-detail" }, lines.join("\n")), t("system.base.file.detail"), { dangerouslyUseHTMLString: false });
80
+ }
81
+
82
+ /** 删除文件资产。 */
83
+ async function handleDelete(row: BaseFile) {
84
+ try {
85
+ await ElMessageBox.confirm(
86
+ t("system.base.file.confirm_delete", { name: row.file_name }),
87
+ t("common.title.warning"),
88
+ { type: "warning", confirmButtonText: t("common.action.confirm"), cancelButtonText: t("common.action.cancel") }
89
+ );
90
+ await defBaseFileService.DeleteBaseFile({ id: row.id });
91
+ ElMessage.success(t("common.message.delete_success", { resource: t("system.base.file.resource") }));
92
+ proTable.value?.getTableList();
93
+ } catch {
94
+ // 用户取消确认或接口失败时由请求层展示错误。
95
+ }
96
+ }
97
+
98
+ function formatFileSize(size: number) {
99
+ if (!size) return "0 B";
100
+ const units = ["B", "KB", "MB", "GB"];
101
+ let value = size;
102
+ let index = 0;
103
+ while (value >= 1024 && index < units.length - 1) {
104
+ value /= 1024;
105
+ index += 1;
106
+ }
107
+ return `${value.toFixed(index === 0 ? 0 : 2)} ${units[index]}`;
108
+ }
109
+ </script>
110
+
111
+ <style scoped>
112
+ .file-detail {
113
+ max-width: 720px;
114
+ margin: 0;
115
+ overflow-wrap: anywhere;
116
+ white-space: pre-wrap;
117
+ text-align: left;
118
+ }
119
+ </style>
@@ -13,7 +13,15 @@
13
13
  :rules="rules"
14
14
  @confirm="handleSubmit"
15
15
  @close="handleCloseDialog"
16
- />
16
+ >
17
+ <template #i18ns>
18
+ <DynamicI18nEditor
19
+ v-model="i18nValues"
20
+ :source="formData.name"
21
+ :maxlength="50"
22
+ />
23
+ </template>
24
+ </FormDialog>
17
25
  </div>
18
26
  </template>
19
27
 
@@ -32,6 +40,7 @@ import type { ProFormField, ProFormOption } from "@liujitcn/kratos-admin-core/co
32
40
  import ProTable from "@liujitcn/kratos-admin-core/components/ProTable";
33
41
  import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
34
42
  import { defBaseJobService } from "@liujitcn/kratos-admin-system/api/system/base_job";
43
+ import { loadEnabledBaseLanguages } from "@liujitcn/kratos-admin-system/api/system/base_language";
35
44
  import type {
36
45
  BaseJob,
37
46
  BaseJobArgs,
@@ -40,8 +49,17 @@ import type {
40
49
  } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_job";
41
50
  import router, { navigateTo } from "@liujitcn/kratos-admin-core/navigation";
42
51
  import { Status } from "@liujitcn/kratos-admin-system/rpc/common/v1/enum";
52
+ import { I18nTargetType } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_i18n";
43
53
  import { buildPageRequest, normalizeSelectedIds } from "@liujitcn/kratos-admin-core/table";
44
54
  import { t } from "@liujitcn/kratos-admin-core";
55
+ import DynamicI18nCell from "@liujitcn/kratos-admin-system/components/DynamicI18nCell.vue";
56
+ import DynamicI18nEditor from "@liujitcn/kratos-admin-system/components/DynamicI18nEditor.vue";
57
+ import {
58
+ normalizeDynamicI18ns,
59
+ serializeDynamicI18ns,
60
+ type DynamicI18nRecord,
61
+ type DynamicI18nValue
62
+ } from "@liujitcn/kratos-admin-system/components/dynamicI18n";
45
63
 
46
64
  defineOptions({
47
65
  name: "BaseJob",
@@ -51,6 +69,7 @@ defineOptions({
51
69
  const { BUTTONS } = useAuthButtons();
52
70
  const proTable = ref<ProTableInstance>();
53
71
  const formDialogRef = ref<InstanceType<typeof FormDialog>>();
72
+ const i18nValues = ref<DynamicI18nValue[]>(normalizeDynamicI18ns(undefined, "name"));
54
73
 
55
74
  const dialog = reactive({
56
75
  editing: false,
@@ -69,7 +88,9 @@ const formData = reactive<BaseJobForm>({
69
88
  /** cron表达式 */
70
89
  cron_expression: "",
71
90
  /** 状态 */
72
- status: Status.STATUS_ENABLE
91
+ status: Status.STATUS_ENABLE,
92
+ /** 定时任务名称多语言翻译。 */
93
+ i18ns: []
73
94
  });
74
95
 
75
96
  const rules = computed(() => ({
@@ -144,6 +165,17 @@ function renderArgsCell(scope: RenderScope<BaseJob>) {
144
165
  );
145
166
  }
146
167
 
168
+ /** 渲染定时任务名称翻译预览。 */
169
+ function renderJobNameCell(scope: RenderScope<BaseJob>) {
170
+ const row = scope.row;
171
+ return h(DynamicI18nCell, {
172
+ source: row.name,
173
+ targetType: I18nTargetType.I18N_TARGET_TYPE_BASE_JOB_NAME,
174
+ targetId: row.id,
175
+ i18ns: row.i18ns
176
+ });
177
+ }
178
+
147
179
  /**
148
180
  * 渲染定时任务操作列。
149
181
  */
@@ -270,6 +302,13 @@ const formFields = computed<ProFormField[]>(() => [
270
302
  component: "input",
271
303
  props: { placeholder: t("system.base.job.placeholder.name") }
272
304
  },
305
+ {
306
+ prop: "i18ns",
307
+ label: t("system.base.i18n.field.i18ns"),
308
+ component: "slot",
309
+ slotName: "i18ns",
310
+ colSpan: 24
311
+ },
273
312
  {
274
313
  prop: "invoke_target",
275
314
  label: t("system.base.job.field.invoke_target"),
@@ -298,7 +337,14 @@ const formFields = computed<ProFormField[]>(() => [
298
337
  /** 定时任务表格列配置。 */
299
338
  const columns = computed<ColumnProps[]>(() => [
300
339
  { type: "selection", width: 55 },
301
- { prop: "name", label: t("system.base.job.field.name"), minWidth: 140, search: { el: "input" } },
340
+ {
341
+ prop: "name",
342
+ label: t("system.base.job.field.name"),
343
+ minWidth: 140,
344
+ search: { el: "input" },
345
+ showOverflowTooltip: false,
346
+ render: scope => renderJobNameCell(scope as unknown as RenderScope<BaseJob>)
347
+ },
302
348
  { prop: "invoke_target", label: t("system.base.job.field.invoke_target"), minWidth: 180, search: { el: "input" } },
303
349
  {
304
350
  prop: "args",
@@ -357,6 +403,7 @@ const headerActions = computed<HeaderActionProps[]>(() => [
357
403
  * 请求定时任务列表,并由 ProTable 统一维护分页与搜索参数。
358
404
  */
359
405
  async function requestBaseJobTable(params: PageBaseJobRequest) {
406
+ await loadEnabledBaseLanguages();
360
407
  const data = await defBaseJobService.PageBaseJob(buildPageRequest(params));
361
408
  return { data: { list: data.base_jobs ?? [], total: data.total } };
362
409
  }
@@ -371,15 +418,16 @@ function refreshTable() {
371
418
  /**
372
419
  * 打开定时任务弹窗。
373
420
  */
374
- function handleOpenDialog(jobId?: number) {
421
+ async function handleOpenDialog(jobId?: number) {
422
+ await loadEnabledBaseLanguages();
375
423
  resetForm();
376
424
  dialog.editing = Boolean(jobId);
377
425
  dialog.visible = true;
378
426
  if (!jobId) return;
379
427
 
380
- defBaseJobService.GetBaseJob({ id: jobId }).then(data => {
381
- Object.assign(formData, data);
382
- });
428
+ const data = await defBaseJobService.GetBaseJob({ id: jobId });
429
+ Object.assign(formData, data);
430
+ i18nValues.value = normalizeDynamicI18ns(data.i18ns as DynamicI18nRecord[], "name");
383
431
  }
384
432
 
385
433
  /**
@@ -402,6 +450,8 @@ function resetForm() {
402
450
  formData.args = [];
403
451
  formData.cron_expression = "";
404
452
  formData.status = Status.STATUS_ENABLE;
453
+ formData.i18ns = [];
454
+ i18nValues.value = normalizeDynamicI18ns(undefined, "name");
405
455
  }
406
456
 
407
457
  /**
@@ -412,6 +462,11 @@ function handleSubmit() {
412
462
  if (!valid) return;
413
463
 
414
464
  const submitData = JSON.parse(JSON.stringify(formData)) as BaseJobForm;
465
+ submitData.i18ns = serializeDynamicI18ns(
466
+ i18nValues.value,
467
+ I18nTargetType.I18N_TARGET_TYPE_BASE_JOB_NAME,
468
+ submitData.id
469
+ );
415
470
  const request = submitData.id
416
471
  ? defBaseJobService.UpdateBaseJob({ base_job: submitData })
417
472
  : defBaseJobService.CreateBaseJob({ base_job: submitData });
@@ -138,7 +138,8 @@ const headerActions = computed<HeaderActionProps[]>(() => [
138
138
  /** 请求语言分页列表。 */
139
139
  async function requestBaseLanguageTable(params: PageBaseLanguageRequest) {
140
140
  const data = await defBaseLanguageService.PageBaseLanguage(buildPageRequest(params));
141
- return { data: { list: data.base_languages ?? [], total: data.total } };
141
+ const list = (data.base_languages ?? []).map(item => ({ ...item, is_primary: item.is_primary ?? false }));
142
+ return { data: { list, total: data.total } };
142
143
  }
143
144
 
144
145
  /** 打开语言编辑弹窗。 */
@@ -0,0 +1,92 @@
1
+ <template>
2
+ <AuditLogTable ref="page" :config="config" />
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ import { computed, ref } from "vue";
7
+ import type { ColumnProps } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
8
+ import { t } from "@liujitcn/kratos-admin-core";
9
+ import { buildPageRequest } from "@liujitcn/kratos-admin-core/table";
10
+ import AuditLogTable, { type AuditLogTableConfig } from "@liujitcn/kratos-admin-system/components/AuditLogTable.vue";
11
+ import { auditDateSearch, auditDetailColumn, auditEnumLabel, createAuditEnumOptions } from "@liujitcn/kratos-admin-system/components/auditLog";
12
+ import { defBaseLoginLogService } from "@liujitcn/kratos-admin-system/api/system/base_audit_log";
13
+ import { BaseAuditResult, BaseLoginLogType } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_audit_log";
14
+ import type { BaseLoginLog, PageBaseLoginLogRequest } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_audit_log";
15
+
16
+ defineOptions({ name: "BaseLoginLog", inheritAttrs: false });
17
+
18
+ const page = ref<InstanceType<typeof AuditLogTable>>();
19
+ const resultOptions = computed(() =>
20
+ createAuditEnumOptions([
21
+ [BaseAuditResult.BASE_AUDIT_RESULT_SUCCESS, t("system.base.audit.result.success")],
22
+ [BaseAuditResult.BASE_AUDIT_RESULT_FAILURE, t("system.base.audit.result.failure")],
23
+ [BaseAuditResult.BASE_AUDIT_RESULT_ERROR, t("system.base.audit.result.error")]
24
+ ])
25
+ );
26
+ const loginTypeOptions = computed(() =>
27
+ createAuditEnumOptions([
28
+ [BaseLoginLogType.BASE_LOGIN_LOG_TYPE_PASSWORD, t("system.base.audit.login_type.password")],
29
+ [BaseLoginLogType.BASE_LOGIN_LOG_TYPE_OAUTH, t("system.base.audit.login_type.oauth")],
30
+ [BaseLoginLogType.BASE_LOGIN_LOG_TYPE_MFA, t("system.base.audit.login_type.mfa")],
31
+ [BaseLoginLogType.BASE_LOGIN_LOG_TYPE_TOKEN_REFRESH, t("system.base.audit.login_type.token_refresh")],
32
+ [BaseLoginLogType.BASE_LOGIN_LOG_TYPE_LOGOUT, t("system.base.audit.login_type.logout")]
33
+ ])
34
+ );
35
+
36
+ const columns = computed<ColumnProps[]>(() => [
37
+ { prop: "user_name", label: t("system.base.audit.field.user_name"), minWidth: 130 },
38
+ { prop: "tenant_code", label: t("system.base.audit.field.tenant_code"), minWidth: 120 },
39
+ {
40
+ prop: "login_type",
41
+ label: t("system.base.audit.field.login_type"),
42
+ minWidth: 130,
43
+ search: { el: "select", enum: loginTypeOptions.value },
44
+ render: scope => auditEnumLabel(loginTypeOptions.value, (scope.row as BaseLoginLog).login_type)
45
+ },
46
+ {
47
+ prop: "result",
48
+ label: t("system.base.audit.field.result"),
49
+ minWidth: 110,
50
+ search: { el: "select", enum: resultOptions.value },
51
+ render: scope => auditEnumLabel(resultOptions.value, (scope.row as BaseLoginLog).result)
52
+ },
53
+ { prop: "client_ip", label: t("system.base.audit.field.client_ip"), minWidth: 140 },
54
+ { prop: "occurred_at", label: t("system.base.audit.field.occurred_at"), minWidth: 190, search: auditDateSearch(t) },
55
+ auditDetailColumn(t("common.action.view"), id => page.value?.handleOpenDialog(id))
56
+ ]);
57
+
58
+ const config = computed<AuditLogTableConfig>(() => ({
59
+ columns: columns.value,
60
+ detailTitle: t("system.base.audit.login.title.detail"),
61
+ closeText: t("common.action.close"),
62
+ detailFields: [
63
+ { key: "id", label: t("system.base.audit.field.id") },
64
+ { key: "tenant_id", label: t("system.base.audit.field.tenant_id") },
65
+ { key: "tenant_code", label: t("system.base.audit.field.tenant_code") },
66
+ { key: "user_id", label: t("system.base.audit.field.user_id") },
67
+ { key: "user_name", label: t("system.base.audit.field.user_name") },
68
+ { key: "login_type", label: t("system.base.audit.field.login_type") },
69
+ { key: "result", label: t("system.base.audit.field.result") },
70
+ { key: "reason_code", label: t("system.base.audit.field.reason_code") },
71
+ { key: "reason", label: t("system.base.audit.field.reason"), span: 2 },
72
+ { key: "client_ip", label: t("system.base.audit.field.client_ip") },
73
+ { key: "device_id", label: t("system.base.audit.field.device_id") },
74
+ { key: "user_agent", label: t("system.base.audit.field.user_agent"), span: 2 },
75
+ { key: "request_id", label: t("system.base.audit.field.request_id") },
76
+ { key: "trace_id", label: t("system.base.audit.field.trace_id") },
77
+ { key: "occurred_at", label: t("system.base.audit.field.occurred_at") },
78
+ { key: "created_at", label: t("system.base.audit.field.created_at") }
79
+ ],
80
+ request: requestTable,
81
+ get: getDetail
82
+ }));
83
+
84
+ async function requestTable(params: Record<string, unknown>) {
85
+ const response = await defBaseLoginLogService.PageBaseLoginLog(buildPageRequest(params as unknown as PageBaseLoginLogRequest));
86
+ return { list: response.base_login_logs as unknown as Record<string, unknown>[], total: response.total };
87
+ }
88
+
89
+ async function getDetail(id: number) {
90
+ return (await defBaseLoginLogService.GetBaseLoginLog({ id })) as unknown as Record<string, unknown>;
91
+ }
92
+ </script>
@@ -0,0 +1,219 @@
1
+ <template>
2
+ <div class="login-policy-page">
3
+ <el-card shadow="never">
4
+ <template #header>
5
+ <div class="policy-header">
6
+ <div>
7
+ <h3>{{ t("system.base.login_policy.title") }}</h3>
8
+ <p>{{ t("system.base.login_policy.description") }}</p>
9
+ </div>
10
+ <el-switch v-model="form.enabled" :active-text="t('system.base.login_policy.enabled')" />
11
+ </div>
12
+ </template>
13
+
14
+ <el-alert :title="t('system.base.login_policy.hint')" type="info" :closable="false" show-icon />
15
+ <el-form label-position="top" class="policy-form" @submit.prevent="save">
16
+ <el-form-item :label="t('system.base.login_policy.field.ip_blacklist')">
17
+ <el-input v-model="form.ip_blacklist" type="textarea" :rows="3" :placeholder="t('system.base.login_policy.placeholder.ip')" />
18
+ </el-form-item>
19
+ <el-form-item :label="t('system.base.login_policy.field.ip_whitelist')">
20
+ <el-input v-model="form.ip_whitelist" type="textarea" :rows="3" :placeholder="t('system.base.login_policy.placeholder.ip')" />
21
+ </el-form-item>
22
+ <el-form-item :label="t('system.base.login_policy.field.time_windows')">
23
+ <el-input v-model="form.time_windows" type="textarea" :rows="2" :placeholder="t('system.base.login_policy.placeholder.time')" />
24
+ </el-form-item>
25
+ <el-form-item :label="t('system.base.login_policy.field.device_blacklist')">
26
+ <el-input v-model="form.device_blacklist" type="textarea" :rows="2" :placeholder="t('system.base.login_policy.placeholder.device')" />
27
+ </el-form-item>
28
+ <el-form-item :label="t('system.base.login_policy.field.device_whitelist')">
29
+ <el-input v-model="form.device_whitelist" type="textarea" :rows="2" :placeholder="t('system.base.login_policy.placeholder.device')" />
30
+ </el-form-item>
31
+ <el-divider />
32
+ <div class="rules-header">
33
+ <div>
34
+ <h4>{{ t("system.base.login_policy.rules.title") }}</h4>
35
+ <p>{{ t("system.base.login_policy.rules.description") }}</p>
36
+ </div>
37
+ <el-button type="primary" plain @click="addRule">{{ t("system.base.login_policy.rules.add") }}</el-button>
38
+ </div>
39
+ <el-table v-if="form.rules.length" :data="form.rules" border class="rules-table">
40
+ <el-table-column :label="t('system.base.login_policy.rules.target_type')" width="130">
41
+ <template #default="{ row }">
42
+ <el-select v-model="row.target_type" class="rule-control">
43
+ <el-option label="TENANT" value="TENANT" />
44
+ <el-option label="USER" value="USER" />
45
+ </el-select>
46
+ </template>
47
+ </el-table-column>
48
+ <el-table-column :label="t('system.base.login_policy.rules.target_value')" min-width="160">
49
+ <template #default="{ row }"><el-input v-model="row.target_value" class="rule-control" /></template>
50
+ </el-table-column>
51
+ <el-table-column :label="t('system.base.login_policy.field.ip_blacklist')" min-width="180">
52
+ <template #default="{ row }"><el-input v-model="row.ip_blacklist" :placeholder="t('system.base.login_policy.placeholder.ip')" class="rule-control" /></template>
53
+ </el-table-column>
54
+ <el-table-column :label="t('system.base.login_policy.field.ip_whitelist')" min-width="180">
55
+ <template #default="{ row }"><el-input v-model="row.ip_whitelist" :placeholder="t('system.base.login_policy.placeholder.ip')" class="rule-control" /></template>
56
+ </el-table-column>
57
+ <el-table-column :label="t('system.base.login_policy.field.time_windows')" min-width="160">
58
+ <template #default="{ row }"><el-input v-model="row.time_windows" :placeholder="t('system.base.login_policy.placeholder.time')" class="rule-control" /></template>
59
+ </el-table-column>
60
+ <el-table-column :label="t('system.base.login_policy.rules.enabled')" width="90">
61
+ <template #default="{ row }"><el-switch v-model="row.enabled" /></template>
62
+ </el-table-column>
63
+ <el-table-column :label="t('common.field.operation')" width="90" fixed="right">
64
+ <template #default="{ $index }"><el-button link type="danger" @click="removeRule($index)">{{ t("common.action.delete") }}</el-button></template>
65
+ </el-table-column>
66
+ </el-table>
67
+ <div class="policy-actions">
68
+ <el-button type="primary" :loading="saving" @click="save">{{ t("common.action.save") }}</el-button>
69
+ </div>
70
+ </el-form>
71
+ </el-card>
72
+ </div>
73
+ </template>
74
+
75
+ <script setup lang="ts">
76
+ import { onMounted, reactive, ref } from "vue";
77
+ import { t } from "@liujitcn/kratos-admin-core";
78
+ import { defBaseLoginPolicyService } from "@liujitcn/kratos-admin-system/api/system/base_login_policy";
79
+ import type { BaseLoginPolicy, BaseLoginPolicyRule } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_login_policy";
80
+
81
+ const saving = ref(false);
82
+ /** 登录来源定向规则表单。 */
83
+ type RuleForm = { target_type: string; target_value: string; enabled: boolean; ip_blacklist: string; ip_whitelist: string; time_windows: string; device_blacklist: string; device_whitelist: string };
84
+ const form = reactive({
85
+ enabled: false,
86
+ ip_blacklist: "",
87
+ ip_whitelist: "",
88
+ time_windows: "",
89
+ device_blacklist: "",
90
+ device_whitelist: "",
91
+ rules: [] as RuleForm[]
92
+ });
93
+
94
+ /** 将接口数组转换为逐行编辑文本。 */
95
+ function assignForm(policy: BaseLoginPolicy) {
96
+ form.enabled = policy.enabled;
97
+ form.ip_blacklist = policy.ip_blacklist.join("\n");
98
+ form.ip_whitelist = policy.ip_whitelist.join("\n");
99
+ form.time_windows = policy.time_windows.join("\n");
100
+ form.device_blacklist = policy.device_blacklist.join("\n");
101
+ form.device_whitelist = policy.device_whitelist.join("\n");
102
+ form.rules = (policy.rules ?? []).map(rule => ({
103
+ target_type: rule.target_type,
104
+ target_value: rule.target_value,
105
+ enabled: rule.enabled,
106
+ ip_blacklist: rule.ip_blacklist.join(","),
107
+ ip_whitelist: rule.ip_whitelist.join(","),
108
+ time_windows: rule.time_windows.join(","),
109
+ device_blacklist: rule.device_blacklist.join(","),
110
+ device_whitelist: rule.device_whitelist.join(",")
111
+ }));
112
+ }
113
+
114
+ /** 将逐行编辑文本转换为接口数组。 */
115
+ function splitLines(value: string) {
116
+ return value.split(/[\n,]/).map(item => item.trim()).filter(Boolean);
117
+ }
118
+
119
+ /** 加载登录来源策略。 */
120
+ async function load() {
121
+ assignForm(await defBaseLoginPolicyService.GetBaseLoginPolicy({}));
122
+ }
123
+
124
+ /** 保存登录来源策略。 */
125
+ async function save() {
126
+ saving.value = true;
127
+ try {
128
+ const policy = await defBaseLoginPolicyService.UpdateBaseLoginPolicy({
129
+ policy: {
130
+ enabled: form.enabled,
131
+ ip_blacklist: splitLines(form.ip_blacklist),
132
+ ip_whitelist: splitLines(form.ip_whitelist),
133
+ time_windows: splitLines(form.time_windows),
134
+ device_blacklist: splitLines(form.device_blacklist),
135
+ device_whitelist: splitLines(form.device_whitelist),
136
+ rules: form.rules.map(rule => ({
137
+ target_type: rule.target_type,
138
+ target_value: rule.target_value,
139
+ enabled: rule.enabled,
140
+ ip_blacklist: splitLines(rule.ip_blacklist),
141
+ ip_whitelist: splitLines(rule.ip_whitelist),
142
+ time_windows: splitLines(rule.time_windows),
143
+ device_blacklist: splitLines(rule.device_blacklist),
144
+ device_whitelist: splitLines(rule.device_whitelist)
145
+ } as BaseLoginPolicyRule))
146
+ }
147
+ });
148
+ assignForm(policy);
149
+ ElMessage.success(t("common.message.update_success", { resource: t("system.base.login_policy.title") }));
150
+ } finally {
151
+ saving.value = false;
152
+ }
153
+ }
154
+
155
+ /** 新增一条定向登录策略。 */
156
+ function addRule() {
157
+ form.rules.push({ target_type: "TENANT", target_value: "", enabled: true, ip_blacklist: "", ip_whitelist: "", time_windows: "", device_blacklist: "", device_whitelist: "" });
158
+ }
159
+
160
+ /** 删除一条定向登录策略。 */
161
+ function removeRule(index: number) {
162
+ form.rules.splice(index, 1);
163
+ }
164
+
165
+ onMounted(load);
166
+ </script>
167
+
168
+ <style scoped lang="scss">
169
+ .login-policy-page {
170
+ padding: 20px;
171
+ }
172
+ .policy-header {
173
+ display: flex;
174
+ align-items: center;
175
+ justify-content: space-between;
176
+ gap: 16px;
177
+ }
178
+ .policy-header h3 {
179
+ margin: 0;
180
+ color: var(--el-text-color-primary);
181
+ font-size: 18px;
182
+ }
183
+ .policy-header p {
184
+ margin: 6px 0 0;
185
+ color: var(--el-text-color-secondary);
186
+ font-size: 13px;
187
+ }
188
+ .policy-form {
189
+ max-width: 920px;
190
+ margin-top: 20px;
191
+ }
192
+ .policy-actions {
193
+ display: flex;
194
+ justify-content: flex-end;
195
+ }
196
+ .rules-header {
197
+ display: flex;
198
+ align-items: center;
199
+ justify-content: space-between;
200
+ gap: 16px;
201
+ margin: 20px 0 12px;
202
+ }
203
+ .rules-header h4 {
204
+ margin: 0;
205
+ color: var(--el-text-color-primary);
206
+ font-size: 15px;
207
+ }
208
+ .rules-header p {
209
+ margin: 4px 0 0;
210
+ color: var(--el-text-color-secondary);
211
+ font-size: 12px;
212
+ }
213
+ .rules-table {
214
+ width: 100%;
215
+ }
216
+ .rule-control {
217
+ width: 100%;
218
+ }
219
+ </style>
@@ -296,7 +296,7 @@ function renderMenuTitleCell(scope: RenderScope<BaseMenu>) {
296
296
  const row = scope.row;
297
297
  return h(DynamicI18nCell, {
298
298
  source: row.meta?.title ?? "",
299
- targetType: I18nTargetType.I18N_TARGET_TYPE_BASE_MENU,
299
+ targetType: I18nTargetType.I18N_TARGET_TYPE_BASE_MENU_META_TITLE,
300
300
  targetId: row.id,
301
301
  i18ns: row.i18ns
302
302
  });
@@ -1001,7 +1001,7 @@ function buildSubmitPayload(): BaseMenuForm {
1001
1001
  const payload = normalizeMenuForm(formData);
1002
1002
  payload.i18ns = serializeDynamicI18ns(
1003
1003
  i18nValues.value,
1004
- I18nTargetType.I18N_TARGET_TYPE_BASE_MENU,
1004
+ I18nTargetType.I18N_TARGET_TYPE_BASE_MENU_META_TITLE,
1005
1005
  payload.id
1006
1006
  );
1007
1007
  // 一级菜单在表单中保持空白,提交时仍按接口约定传回根节点标识。