@liujitcn/kratos-admin-system 0.0.62 → 0.0.65

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 (101) hide show
  1. package/README.md +16 -1
  2. package/dist/package/declarations/src/api/base/v1/ai_provider.d.ts +7 -0
  3. package/dist/package/declarations/src/api/system/admin/v1/ai_provider.d.ts +20 -0
  4. package/dist/package/declarations/src/api/system/admin/v1/base_api_rate_limit_policy.d.ts +19 -0
  5. package/dist/package/declarations/src/api/system/admin/v1/base_message_provider.d.ts +21 -0
  6. package/dist/package/declarations/src/api/system/admin/v1/base_message_template.d.ts +16 -0
  7. package/dist/package/declarations/src/api/system/admin/v1/base_oauth_provider.d.ts +18 -0
  8. package/dist/package/declarations/src/api/system/admin/v1/base_rate_limit_rule.d.ts +21 -0
  9. package/dist/package/declarations/src/components/api/ApiTransfer.vue.d.ts +35 -0
  10. package/dist/package/declarations/src/components/api/apiTransfer.d.ts +13 -0
  11. package/dist/package/declarations/src/components/i18n/DynamicI18nCell.vue.d.ts +2 -2
  12. package/dist/package/declarations/src/components/i18n/dynamicI18n.d.ts +3 -4
  13. package/dist/package/declarations/src/locales/generated.d.ts +968 -0
  14. package/dist/package/declarations/src/rpc/base/v1/ai_message.d.ts +4 -0
  15. package/dist/package/declarations/src/rpc/base/v1/ai_provider.d.ts +29 -0
  16. package/dist/package/declarations/src/rpc/base/v1/oauth.d.ts +20 -0
  17. package/dist/package/declarations/src/rpc/google/protobuf/struct.d.ts +81 -0
  18. package/dist/package/declarations/src/rpc/system/admin/v1/ai_provider.d.ts +133 -0
  19. package/dist/package/declarations/src/rpc/system/admin/v1/base_api_rate_limit_policy.d.ts +137 -0
  20. package/dist/package/declarations/src/rpc/system/admin/v1/base_i18n.d.ts +4 -23
  21. package/dist/package/declarations/src/rpc/system/admin/v1/base_message.d.ts +4 -4
  22. package/dist/package/declarations/src/rpc/system/admin/v1/base_message_category.d.ts +19 -0
  23. package/dist/package/declarations/src/rpc/system/admin/v1/base_message_provider.d.ts +126 -0
  24. package/dist/package/declarations/src/rpc/system/admin/v1/base_message_template.d.ts +99 -0
  25. package/dist/package/declarations/src/rpc/system/admin/v1/base_oauth_provider.d.ts +133 -0
  26. package/dist/package/declarations/src/rpc/system/admin/v1/base_rate_limit_rule.d.ts +144 -0
  27. package/dist/package/declarations/src/utils/persisted-message.d.ts +4 -0
  28. package/dist/package/declarations/src/utils/rateLimit.d.ts +24 -0
  29. package/dist/package/declarations/src/views/ai/chat/components/ChatPanel.vue.d.ts +11 -2
  30. package/dist/package/declarations/src/views/ai/chat/components/XSender.vue.d.ts +9 -0
  31. package/dist/package/declarations/src/views/ai/chat/modelDisplay.d.ts +5 -0
  32. package/dist/package/declarations/src/views/ai/chat/modelSelection.d.ts +9 -0
  33. package/dist/package/declarations/src/views/base/ai-provider/index.vue.d.ts +3 -0
  34. package/dist/package/declarations/src/views/base/api-rate-limit-policy/index.vue.d.ts +3 -0
  35. package/dist/package/declarations/src/views/base/message-provider/index.vue.d.ts +3 -0
  36. package/dist/package/declarations/src/views/base/message-template/index.vue.d.ts +3 -0
  37. package/dist/package/declarations/src/views/base/oauth-provider/index.vue.d.ts +3 -0
  38. package/dist/package/declarations/src/views/base/rate-limit-rule/index.vue.d.ts +3 -0
  39. package/dist/package/src/api/base/v1/ai_provider.ts +22 -0
  40. package/dist/package/src/api/system/admin/v1/ai_provider.ts +57 -0
  41. package/dist/package/src/api/system/admin/v1/base_api_rate_limit_policy.ts +51 -0
  42. package/dist/package/src/api/system/admin/v1/base_message_provider.ts +57 -0
  43. package/dist/package/src/api/system/admin/v1/base_message_template.ts +44 -0
  44. package/dist/package/src/api/system/admin/v1/base_oauth_provider.ts +50 -0
  45. package/dist/package/src/api/system/admin/v1/base_rate_limit_rule.ts +58 -0
  46. package/dist/package/src/components/ai/Ai.vue +1 -1
  47. package/dist/package/src/components/api/ApiTransfer.vue +117 -0
  48. package/dist/package/src/components/api/apiTransfer.ts +27 -0
  49. package/dist/package/src/components/i18n/DynamicI18nCell.vue +8 -8
  50. package/dist/package/src/components/i18n/DynamicI18nEditor.vue +6 -3
  51. package/dist/package/src/components/i18n/dynamicI18n.ts +4 -5
  52. package/dist/package/src/components/tenant-project/TenantProjectManager.vue +2 -1
  53. package/dist/package/src/locales/en-US.json +246 -4
  54. package/dist/package/src/locales/ja-JP.json +246 -4
  55. package/dist/package/src/locales/zh-CN.json +246 -4
  56. package/dist/package/src/locales/zh-TW.json +246 -4
  57. package/dist/package/src/rpc/base/v1/ai_message.ts +7 -1
  58. package/dist/package/src/rpc/base/v1/ai_provider.ts +41 -0
  59. package/dist/package/src/rpc/base/v1/oauth.ts +16 -0
  60. package/dist/package/src/rpc/google/protobuf/struct.ts +80 -0
  61. package/dist/package/src/rpc/system/admin/v1/ai_provider.ts +155 -0
  62. package/dist/package/src/rpc/system/admin/v1/base_api_rate_limit_policy.ts +160 -0
  63. package/dist/package/src/rpc/system/admin/v1/base_i18n.ts +4 -24
  64. package/dist/package/src/rpc/system/admin/v1/base_message.ts +4 -4
  65. package/dist/package/src/rpc/system/admin/v1/base_message_category.ts +20 -0
  66. package/dist/package/src/rpc/system/admin/v1/base_message_provider.ts +150 -0
  67. package/dist/package/src/rpc/system/admin/v1/base_message_template.ts +117 -0
  68. package/dist/package/src/rpc/system/admin/v1/base_oauth_provider.ts +156 -0
  69. package/dist/package/src/rpc/system/admin/v1/base_rate_limit_rule.ts +167 -0
  70. package/dist/package/src/utils/persisted-message.ts +21 -0
  71. package/dist/package/src/utils/rateLimit.ts +140 -0
  72. package/dist/package/src/views/ai/chat/components/ChatPanel.vue +40 -5
  73. package/dist/package/src/views/ai/chat/components/XSender.vue +309 -3
  74. package/dist/package/src/views/ai/chat/index.vue +48 -1
  75. package/dist/package/src/views/ai/chat/modelDisplay.ts +67 -0
  76. package/dist/package/src/views/ai/chat/modelSelection.ts +31 -0
  77. package/dist/package/src/views/base/ai-provider/index.vue +613 -0
  78. package/dist/package/src/views/base/api-rate-limit-policy/index.vue +429 -0
  79. package/dist/package/src/views/base/backup-management/archive-record/index.vue +2 -0
  80. package/dist/package/src/views/base/backup-management/archive-restore/index.vue +2 -1
  81. package/dist/package/src/views/base/backup-management/backup-record/index.vue +2 -0
  82. package/dist/package/src/views/base/backup-management/backup-restore/index.vue +2 -1
  83. package/dist/package/src/views/base/config/index.vue +12 -13
  84. package/dist/package/src/views/base/dict/index.vue +2 -3
  85. package/dist/package/src/views/base/dict/item.vue +2 -3
  86. package/dist/package/src/views/base/job/index.vue +2 -3
  87. package/dist/package/src/views/base/job/log.vue +3 -2
  88. package/dist/package/src/views/base/menu/index.vue +27 -83
  89. package/dist/package/src/views/base/message/index.vue +9 -3
  90. package/dist/package/src/views/base/message-category/index.vue +34 -3
  91. package/dist/package/src/views/base/message-provider/index.vue +168 -0
  92. package/dist/package/src/views/base/message-template/index.vue +105 -0
  93. package/dist/package/src/views/base/migration/index.vue +1 -1
  94. package/dist/package/src/views/base/oauth-client/index.vue +6 -6
  95. package/dist/package/src/views/base/oauth-provider/index.vue +274 -0
  96. package/dist/package/src/views/base/rate-limit-rule/index.vue +327 -0
  97. package/dist/package/src/views/base/redact-output-policy/index.vue +1 -1
  98. package/dist/package/src/views/base/redact-rule/index.vue +12 -2
  99. package/dist/package/src/views/base/redact-storage-policy/index.vue +4 -0
  100. package/dist/package/src/views/base/tenant/index.vue +3 -3
  101. package/package.json +2 -2
@@ -47,7 +47,6 @@ import type {
47
47
  } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_job";
48
48
  import router, { navigateTo } from "@liujitcn/kratos-admin-core/navigation";
49
49
  import { Status } from "@liujitcn/kratos-admin-system/rpc/common/v1/enum";
50
- import { I18nTargetType } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_i18n";
51
50
  import { buildPageRequest, normalizeSelectedIds } from "@liujitcn/kratos-admin-core/table";
52
51
  import { t } from "@liujitcn/kratos-admin-core";
53
52
  import DynamicI18nCell from "@liujitcn/kratos-admin-system/components/i18n/DynamicI18nCell.vue";
@@ -168,7 +167,7 @@ function renderJobNameCell(scope: RenderScope<BaseJob>) {
168
167
  const row = scope.row;
169
168
  return h(DynamicI18nCell, {
170
169
  source: row.name,
171
- targetType: I18nTargetType.I18N_TARGET_TYPE_BASE_JOB_NAME,
170
+ targetKey: "base_job.name",
172
171
  targetId: row.id,
173
172
  i18ns: row.i18ns
174
173
  });
@@ -419,7 +418,7 @@ function handleSubmit() {
419
418
  const submitData = JSON.parse(JSON.stringify(formData)) as BaseJobForm;
420
419
  submitData.i18ns = serializeDynamicI18ns(
421
420
  i18nValues.value,
422
- I18nTargetType.I18N_TARGET_TYPE_BASE_JOB_NAME,
421
+ "base_job.name",
423
422
  submitData.id,
424
423
  submitData.name
425
424
  );
@@ -17,7 +17,7 @@
17
17
  <pre class="code-block">{{ formatJson(detail.input) }}</pre>
18
18
  </el-descriptions-item>
19
19
  <el-descriptions-item :label="t('system.base.job.log.field.output')">
20
- <pre class="code-block">{{ formatJson(detail.output) }}</pre>
20
+ <pre class="code-block">{{ formatJson(resolvePersistedTaskOutput(detail.output)) }}</pre>
21
21
  </el-descriptions-item>
22
22
  </el-descriptions>
23
23
 
@@ -25,7 +25,7 @@
25
25
  v-if="detail.status === BaseJobLogStatus.BASE_JOB_LOG_STATUS_FAIL"
26
26
  :title="t('system.base.job.log.field.error')"
27
27
  type="error"
28
- :description="detail.error"
28
+ :description="resolvePersistedMessage(detail.error)"
29
29
  class="mt-4"
30
30
  show-icon
31
31
  />
@@ -52,6 +52,7 @@ import { formatDateTime, formatJson } from "@liujitcn/kratos-admin-core/format";
52
52
  import type { BaseJobLog, PageBaseJobLogRequest } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_job_log";
53
53
  import { BaseJobLogStatus } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_job_log";
54
54
  import { t } from "@liujitcn/kratos-admin-core";
55
+ import { resolvePersistedMessage, resolvePersistedTaskOutput } from "../../../utils/persisted-message";
55
56
 
56
57
  defineOptions({
57
58
  name: "BaseJobLog",
@@ -38,15 +38,23 @@
38
38
  <DynamicI18nEditor v-model="i18nValues" :source="formData.meta.title" :maxlength="100" />
39
39
  </template>
40
40
 
41
- <template #apiTransferItem="slotScope">
42
- <el-popover effect="light" trigger="hover" placement="top" width="auto">
43
- <template #default>
44
- <div>{{ t("system.base.api.field.operation") }}:{{ slotScope.option.operation }}</div>
45
- <div>{{ t("system.base.api.field.method") }}:{{ slotScope.option.method }}</div>
46
- <div>{{ t("system.base.api.field.path") }}:{{ slotScope.option.path }}</div>
41
+ <template #apiTransfer>
42
+ <ApiTransfer
43
+ v-model="formData.api"
44
+ :options="apiOptions"
45
+ :titles="[t('system.base.menu.value.available_api'), t('system.base.menu.value.selected_api')]"
46
+ >
47
+ <template #default="{ option }">
48
+ <el-popover effect="light" trigger="hover" placement="top" width="auto">
49
+ <template #default>
50
+ <div>{{ t("system.base.api.field.operation") }}:{{ option.operation }}</div>
51
+ <div>{{ t("system.base.api.field.method") }}:{{ option.method }}</div>
52
+ <div>{{ t("system.base.api.field.path") }}:{{ option.path }}</div>
53
+ </template>
54
+ <template #reference>{{ option.label }}</template>
55
+ </el-popover>
47
56
  </template>
48
- <template #reference>{{ slotScope.option.label }}</template>
49
- </el-popover>
57
+ </ApiTransfer>
50
58
  </template>
51
59
  </FormDialog>
52
60
  </div>
@@ -65,9 +73,10 @@ import type {
65
73
  } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
66
74
  import type { ProFormField, ProFormOption } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
67
75
  import SelectIcon from "@liujitcn/kratos-admin-core/components/SelectIcon/index.vue";
76
+ import ApiTransfer from "@liujitcn/kratos-admin-system/components/api/ApiTransfer.vue";
77
+ import { loadApiTransferCatalog, type ApiTransferOption } from "@liujitcn/kratos-admin-system/components/api/apiTransfer";
68
78
  import { defBaseMenuService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_menu";
69
79
  import { loadEnabledBaseLanguages } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_language";
70
- import { defBaseApiService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_api";
71
80
  import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
72
81
  import type { BaseApi } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_api";
73
82
  import type {
@@ -79,7 +88,6 @@ import type {
79
88
  } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_menu";
80
89
  import { Status } from "@liujitcn/kratos-admin-system/rpc/common/v1/enum";
81
90
  import { BaseMenuType } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/common";
82
- import { I18nTargetType } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_i18n";
83
91
  import { normalizeSelectedIds } from "@liujitcn/kratos-admin-core/table";
84
92
  import { t } from "@liujitcn/kratos-admin-core";
85
93
  import DynamicI18nEditor from "@liujitcn/kratos-admin-system/components/i18n/DynamicI18nEditor.vue";
@@ -113,6 +121,7 @@ const menuOptions = ref<ProFormOption[]>([]);
113
121
  const parentMenuTypeMap = ref(new Map<number, BaseMenuType>());
114
122
  const appMenuIds = ref(new Set<number>([APP_MENU_ROOT_ID]));
115
123
  const apiList = ref<BaseApi[]>([]);
124
+ const apiOptions = ref<ApiTransferOption[]>([]);
116
125
  const i18nValues = ref<DynamicI18nValue[]>(normalizeDynamicI18ns(undefined));
117
126
 
118
127
  const dialog = reactive({
@@ -296,7 +305,7 @@ function renderMenuTitleCell(scope: RenderScope<BaseMenu>) {
296
305
  const row = scope.row;
297
306
  return h(DynamicI18nCell, {
298
307
  source: row.meta?.title ?? "",
299
- targetType: I18nTargetType.I18N_TARGET_TYPE_BASE_MENU_META_TITLE,
308
+ targetKey: "base_menu.meta.title",
300
309
  targetId: row.id,
301
310
  i18ns: row.i18ns
302
311
  });
@@ -411,15 +420,6 @@ const headerActions = computed<HeaderActionProps[]>(() => [
411
420
  }
412
421
  ]);
413
422
 
414
- /** API 穿梭框可选数据。 */
415
- const transferData = computed<ProFormOption[]>(() => {
416
- return apiList.value.map(item => ({
417
- ...item,
418
- value: item.operation,
419
- label: `${item.service_desc}/${item.desc}`
420
- }));
421
- });
422
-
423
423
  /** 菜单表单字段配置。 */
424
424
  const formFields = computed<ProFormField[]>(() => [
425
425
  {
@@ -653,14 +653,8 @@ const formFields = computed<ProFormField[]>(() => [
653
653
  {
654
654
  prop: "api",
655
655
  label: t("system.base.menu.field.api_list"),
656
- component: "transfer",
657
- slotName: "apiTransferItem",
658
- options: transferData.value,
659
- props: {
660
- class: "menu-api-transfer",
661
- filterable: true,
662
- titles: [t("system.base.menu.value.available_api"), t("system.base.menu.value.selected_api")]
663
- },
656
+ component: "slot",
657
+ slotName: "apiTransfer",
664
658
  visible: model =>
665
659
  model.id === APP_MENU_ROOT_ID ||
666
660
  model.type === BaseMenuType.BASE_MENU_TYPE_MENU ||
@@ -993,7 +987,7 @@ function buildSubmitPayload(): BaseMenuForm {
993
987
  const payload = normalizeMenuForm(formData);
994
988
  payload.i18ns = serializeDynamicI18ns(
995
989
  i18nValues.value,
996
- I18nTargetType.I18N_TARGET_TYPE_BASE_MENU_META_TITLE,
990
+ "base_menu.meta.title",
997
991
  payload.id,
998
992
  payload.meta?.title ?? ""
999
993
  );
@@ -1054,10 +1048,11 @@ function buildSubmitPayload(): BaseMenuForm {
1054
1048
 
1055
1049
  /** 加载菜单树选项和 API 列表,确保弹窗打开时相关数据已可用。 */
1056
1050
  async function loadDialogResources() {
1057
- const [menuData, apiData] = await Promise.all([defBaseMenuService.TreeBaseMenu({}), defBaseApiService.OptionBaseApi({})]);
1051
+ const [menuData, apiCatalog] = await Promise.all([defBaseMenuService.TreeBaseMenu({}), loadApiTransferCatalog()]);
1058
1052
  return {
1059
1053
  menuOptions: buildMenuOptions(menuData.base_menus ?? []),
1060
- apiList: apiData.base_apis ?? []
1054
+ apiList: apiCatalog.apis,
1055
+ apiOptions: apiCatalog.options
1061
1056
  };
1062
1057
  }
1063
1058
 
@@ -1137,6 +1132,7 @@ async function handleOpenDialog(parentMenu?: BaseMenu, menuId?: number) {
1137
1132
  commit: ({ resources, data }) => {
1138
1133
  menuOptions.value = resources.menuOptions;
1139
1134
  apiList.value = resources.apiList;
1135
+ apiOptions.value = resources.apiOptions;
1140
1136
  dialog.parentLocked = Boolean(parentMenu || menuId);
1141
1137
  dialog.editing = Boolean(menuId);
1142
1138
  dialog.parentType = parentMenu?.type ?? BaseMenuType.BASE_MENU_TYPE_UNSPECIFIED;
@@ -1264,51 +1260,6 @@ function handleDeleteMenu(selected?: number | string | Array<number | string> |
1264
1260
  </style>
1265
1261
 
1266
1262
  <style lang="scss">
1267
- .menu-form-dialog {
1268
- .menu-api-transfer {
1269
- display: grid;
1270
- grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
1271
- align-items: center;
1272
- gap: 16px;
1273
- width: 100%;
1274
- .el-transfer-panel {
1275
- width: 100%;
1276
- min-width: 0;
1277
- }
1278
- .el-transfer__buttons {
1279
- display: flex;
1280
- flex-direction: column;
1281
- gap: 12px;
1282
- padding: 0;
1283
- .el-button {
1284
- margin: 0;
1285
- }
1286
- }
1287
- .el-transfer-panel__body {
1288
- height: 360px;
1289
- }
1290
- .el-transfer-panel__list.is-filterable {
1291
- height: 300px;
1292
- }
1293
- .el-transfer-panel__item.el-checkbox {
1294
- align-items: flex-start;
1295
- height: auto;
1296
- min-height: 36px;
1297
- padding-top: 8px;
1298
- padding-bottom: 8px;
1299
- .el-checkbox__input {
1300
- top: 3px;
1301
- }
1302
- .el-checkbox__label {
1303
- display: block;
1304
- white-space: normal;
1305
- overflow-wrap: anywhere;
1306
- line-height: 20px;
1307
- }
1308
- }
1309
- }
1310
- }
1311
-
1312
1263
  @media (max-width: 767px) {
1313
1264
  .menu-form-dialog {
1314
1265
  .el-col {
@@ -1322,13 +1273,6 @@ function handleDeleteMenu(selected?: number | string | Array<number | string> |
1322
1273
  justify-content: flex-start;
1323
1274
  width: auto !important;
1324
1275
  }
1325
- .menu-api-transfer {
1326
- grid-template-columns: minmax(0, 1fr);
1327
- .el-transfer__buttons {
1328
- flex-direction: row;
1329
- justify-content: center;
1330
- }
1331
- }
1332
1276
  }
1333
1277
  }
1334
1278
  </style>
@@ -87,7 +87,9 @@
87
87
  optionLabel(statusOptions, detail.data.base_message?.status)
88
88
  }}</el-descriptions-item>
89
89
  <el-descriptions-item :label="t('system.base.message.field.sender')">{{
90
- detail.data.base_message?.sender_name
90
+ detail.data.base_message?.sender_name === "系统"
91
+ ? t("system.notification.sender.system")
92
+ : resolvePersistedMessage(detail.data.base_message?.sender_name)
91
93
  }}</el-descriptions-item>
92
94
  <el-descriptions-item :label="t('system.base.message.field.delivered_total')">{{
93
95
  detail.data.base_message?.delivered_total
@@ -125,7 +127,9 @@
125
127
  :label="t('system.base.message.field.last_error')"
126
128
  min-width="220"
127
129
  show-overflow-tooltip
128
- />
130
+ >
131
+ <template #default="scope">{{ resolvePersistedMessage(scope.row.last_error) }}</template>
132
+ </el-table-column>
129
133
  <el-table-column :label="t('common.field.operation')">
130
134
  <template #default="scope">
131
135
  <el-button
@@ -156,8 +160,10 @@ import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
156
160
  import { buildPageRequest, normalizeSelectedIds } from "@liujitcn/kratos-admin-core/table";
157
161
  import { useTenantScope } from "@liujitcn/kratos-admin-core/tenant";
158
162
  import { t } from "@liujitcn/kratos-admin-core";
163
+ import { resolvePersistedMessage } from "../../../utils/persisted-message";
159
164
  import { defBaseMessageService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_message";
160
165
  import { defBaseMessageCategoryService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_message_category";
166
+ import { BaseMessageCategoryInboxEnabled } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_message_category";
161
167
  import type {
162
168
  BaseMessage,
163
169
  BaseMessageDetail,
@@ -570,7 +576,7 @@ async function loadCategoryOptions() {
570
576
 
571
577
  /** 请求消息分类选项。 */
572
578
  async function requestCategoryOptions() {
573
- const result = await defBaseMessageCategoryService.OptionBaseMessageCategory({});
579
+ const result = await defBaseMessageCategoryService.OptionBaseMessageCategory({ inbox_enabled: BaseMessageCategoryInboxEnabled.BASE_MESSAGE_CATEGORY_INBOX_ENABLED_ENABLE });
574
580
  return result.list.map(item => ({ label: item.label, value: item.value, disabled: item.disabled }));
575
581
  }
576
582
 
@@ -16,7 +16,7 @@
16
16
  </template>
17
17
 
18
18
  <script setup lang="ts">
19
- import { computed, h, reactive, ref, type Component } from "vue";
19
+ import { computed, h, onMounted, reactive, ref, type Component } from "vue";
20
20
  import ProTable from "@liujitcn/kratos-admin-core/components/ProTable";
21
21
  import FormDialog from "@liujitcn/kratos-admin-core/components/Dialog/FormDialog.vue";
22
22
  import type { ColumnProps, HeaderActionProps, ProTableInstance } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
@@ -26,11 +26,13 @@ import { buildPageRequest, normalizeSelectedIds } from "@liujitcn/kratos-admin-c
26
26
  import { useTenantScope } from "@liujitcn/kratos-admin-core/tenant";
27
27
  import { t } from "@liujitcn/kratos-admin-core";
28
28
  import { defBaseMessageCategoryService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_message_category";
29
+ import { defBaseMessageProviderService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_message_provider";
29
30
  import type {
30
31
  BaseMessageCategory,
31
32
  BaseMessageCategoryForm,
32
33
  PageBaseMessageCategoryRequest
33
34
  } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_message_category";
35
+ import { BaseMessageCategoryInboxEnabled } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_message_category";
34
36
  import { MessagePriority } from "@liujitcn/kratos-admin-system/rpc/base/v1/notification";
35
37
  import { Status } from "@liujitcn/kratos-admin-system/rpc/common/v1/enum";
36
38
 
@@ -43,11 +45,16 @@ const proTable = ref<ProTableInstance>();
43
45
  const formDialogRef = ref<InstanceType<typeof FormDialog>>();
44
46
  const dialog = reactive({ visible: false, titleKey: "common.action.create" });
45
47
  const formData = reactive<BaseMessageCategoryForm>(defaultForm());
48
+ const providerOptions = ref<ProFormOption[]>([]);
46
49
 
47
50
  const statusOptions = computed<ProFormOption[]>(() => [
48
51
  { label: t("common.status.enabled"), value: Status.STATUS_ENABLE },
49
52
  { label: t("common.status.disabled"), value: Status.STATUS_DISABLE }
50
53
  ]);
54
+ const inboxEnabledOptions = computed<ProFormOption[]>(() => [
55
+ { label: t("common.status.enabled"), value: BaseMessageCategoryInboxEnabled.BASE_MESSAGE_CATEGORY_INBOX_ENABLED_ENABLE },
56
+ { label: t("common.status.disabled"), value: BaseMessageCategoryInboxEnabled.BASE_MESSAGE_CATEGORY_INBOX_ENABLED_DISABLE }
57
+ ]);
51
58
  const priorityOptions = computed<ProFormOption[]>(() => [
52
59
  { label: t("system.base.message.priority.normal"), value: MessagePriority.MESSAGE_PRIORITY_NORMAL },
53
60
  { label: t("system.base.message.priority.important"), value: MessagePriority.MESSAGE_PRIORITY_IMPORTANT },
@@ -175,6 +182,20 @@ const formFields = computed<ProFormField[]>(() => [
175
182
  labelTooltip: t("system.base.message_category.tooltip.allow_delete"),
176
183
  component: "switch"
177
184
  },
185
+ {
186
+ prop: "inbox_enabled",
187
+ label: t("system.base.message_category.field.inbox"),
188
+ labelTooltip: t("system.base.message_category.tooltip.inbox"),
189
+ component: "radio-group",
190
+ options: inboxEnabledOptions.value
191
+ },
192
+ {
193
+ prop: "provider_id",
194
+ label: t("system.base.message_category.field.provider"),
195
+ labelTooltip: t("system.base.message_category.tooltip.provider"),
196
+ component: "checkbox-group",
197
+ options: providerOptions.value
198
+ },
178
199
  { prop: "status", label: t("common.field.status"), component: "radio-group", options: statusOptions.value }
179
200
  ]);
180
201
 
@@ -288,10 +309,20 @@ function defaultForm(): BaseMessageCategoryForm {
288
309
  retention_days: 180,
289
310
  allow_archive: true,
290
311
  allow_delete: true,
291
- status: Status.STATUS_ENABLE
312
+ status: Status.STATUS_ENABLE,
313
+ provider_id: [],
314
+ inbox_enabled: BaseMessageCategoryInboxEnabled.BASE_MESSAGE_CATEGORY_INBOX_ENABLED_ENABLE
292
315
  };
293
316
  }
294
317
 
318
+ onMounted(loadProviderOptions);
319
+
320
+ /** 加载消息 Provider 复选项。 */
321
+ async function loadProviderOptions() {
322
+ const data = await defBaseMessageProviderService.OptionBaseMessageProvider({});
323
+ providerOptions.value = (data.list ?? []).map(item => ({ label: item.label, value: Number(item.value), disabled: item.disabled }));
324
+ }
325
+
295
326
  /** 请求消息分类表格数据。 */
296
327
  async function requestTable(params: Record<string, unknown>) {
297
328
  const data = await defBaseMessageCategoryService.PageBaseMessageCategory(
@@ -316,7 +347,7 @@ async function openDialog(id?: number) {
316
347
  async function handleSubmit() {
317
348
  const valid = await formDialogRef.value?.validate();
318
349
  if (!valid) return;
319
- const payload = formData as BaseMessageCategoryForm;
350
+ const payload = JSON.parse(JSON.stringify(formData)) as BaseMessageCategoryForm;
320
351
  if (payload.id) await defBaseMessageCategoryService.UpdateBaseMessageCategory({ base_message_category: payload });
321
352
  else await defBaseMessageCategoryService.CreateBaseMessageCategory({ base_message_category: payload });
322
353
  ElMessage.success(t("common.message.operation_success"));
@@ -0,0 +1,168 @@
1
+ <template>
2
+ <div class="table-box">
3
+ <ProTable ref="proTable" row-key="id" :columns="columns" :header-actions="headerActions" :request-api="requestTable" />
4
+ <FormDialog
5
+ v-model="dialog.visible"
6
+ ref="formDialogRef"
7
+ :title="t(dialog.editing ? 'common.action.edit' : 'common.action.create')"
8
+ width="min(1000px, calc(100vw - 32px))"
9
+ :model="formData"
10
+ :fields="formFields"
11
+ :rules="rules"
12
+ @confirm="handleSubmit"
13
+ @close="handleClose"
14
+ />
15
+ </div>
16
+ </template>
17
+
18
+ <script setup lang="ts">
19
+ import { computed, reactive, ref } from "vue";
20
+ import { ElMessage, ElMessageBox } from "element-plus";
21
+ import { CirclePlus, Delete, EditPen } from "@element-plus/icons-vue";
22
+ import ProTable from "@liujitcn/kratos-admin-core/components/ProTable";
23
+ import FormDialog from "@liujitcn/kratos-admin-core/components/Dialog/FormDialog.vue";
24
+ import type { ColumnProps, HeaderActionProps, ProTableInstance } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
25
+ import type { ProFormField, ProFormOption } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
26
+ import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
27
+ import { buildPageRequest, normalizeSelectedIds } from "@liujitcn/kratos-admin-core/table";
28
+ import { useTenantScope } from "@liujitcn/kratos-admin-core/tenant";
29
+ import { t } from "@liujitcn/kratos-admin-core";
30
+ import { defBaseMessageProviderService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_message_provider";
31
+ import type {
32
+ BaseMessageProvider,
33
+ BaseMessageProviderForm,
34
+ PageBaseMessageProviderRequest
35
+ } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_message_provider";
36
+ import { Status } from "@liujitcn/kratos-admin-system/rpc/common/v1/enum";
37
+
38
+ defineOptions({ name: "BaseMessageProvider", inheritAttrs: false });
39
+
40
+ interface ConfigItem {
41
+ key: string;
42
+ value: string;
43
+ }
44
+
45
+ interface FormState extends BaseMessageProviderForm {
46
+ config_items: ConfigItem[];
47
+ }
48
+
49
+ const { BUTTONS } = useAuthButtons();
50
+ const { isDefaultTenant } = useTenantScope();
51
+ const proTable = ref<ProTableInstance>();
52
+ const formDialogRef = ref<InstanceType<typeof FormDialog>>();
53
+ const dialog = reactive({ visible: false, editing: false });
54
+ const formData = reactive<FormState>(defaultForm());
55
+
56
+ const statusOptions = computed<ProFormOption[]>(() => [
57
+ { label: t("common.status.enabled"), value: Status.STATUS_ENABLE },
58
+ { label: t("common.status.disabled"), value: Status.STATUS_DISABLE }
59
+ ]);
60
+
61
+ const rules = computed(() => ({
62
+ provider: [{ required: true, message: t("system.base.message_provider.validation.provider"), trigger: "blur" }],
63
+ name: [{ required: true, message: t("system.base.message_provider.validation.name"), trigger: "blur" }]
64
+ }));
65
+
66
+ const formFields = computed<ProFormField[]>(() => [
67
+ { prop: "provider", label: t("system.base.message_provider.field.provider"), component: "input", props: { disabled: dialog.editing, maxlength: 32 } },
68
+ { prop: "name", label: t("system.base.message_provider.field.name"), component: "input", props: { maxlength: 50 } },
69
+ { prop: "description", label: t("system.base.message_provider.field.description"), component: "input", colSpan: 24, props: { maxlength: 255 } },
70
+ { prop: "icon", label: t("system.base.message_provider.field.icon"), component: "input", props: { maxlength: 100 } },
71
+ { prop: "client_id", label: t("system.base.message_provider.field.client_id"), component: "input", props: { maxlength: 255 } },
72
+ { prop: "client_secret", label: t("system.base.message_provider.field.client_secret"), component: "input", props: { type: "password", showPassword: true, maxlength: 1024, placeholder: dialog.editing ? t("system.base.message_provider.placeholder.keep_secret") : "" } },
73
+ { prop: "config_items", label: t("system.base.message_provider.field.config"), component: "kv-list", props: { keyInputProps: { maxlength: 128 } } },
74
+ { prop: "sort", label: t("common.field.sort"), component: "input-number", props: { min: 0, precision: 0 } },
75
+ { prop: "status", label: t("common.field.status"), component: "radio-group", options: statusOptions.value }
76
+ ]);
77
+
78
+ const columns = computed<ColumnProps[]>(() => [
79
+ { type: "selection", width: 55 },
80
+ { prop: "name", label: t("system.base.message_provider.field.name"), minWidth: 150, search: { el: "input" } },
81
+ { prop: "provider", label: t("system.base.message_provider.field.provider"), minWidth: 130, search: { el: "input" } },
82
+ { prop: "client_id", label: t("system.base.message_provider.field.client_id"), minWidth: 180 },
83
+ { prop: "secret_configured", label: t("system.base.message_provider.field.secret_configured"), width: 120, render: scope => (scope.row as BaseMessageProvider).secret_configured ? t("common.status.configured") : t("common.status.not_configured") },
84
+ { prop: "sort", label: t("common.field.sort"), width: 80, align: "right" },
85
+ { prop: "status", label: t("common.field.status"), width: 110, search: { el: "select", enum: statusOptions.value }, cellType: "status", statusProps: {
86
+ activeValue: Status.STATUS_ENABLE, inactiveValue: Status.STATUS_DISABLE, activeText: t("common.status.enabled"), inactiveText: t("common.status.disabled"),
87
+ disabled: () => !isDefaultTenant.value || !BUTTONS.value["base:message-provider:status"], beforeChange: scope => handleSetStatus(scope.row as BaseMessageProvider)
88
+ } },
89
+ { prop: "updated_at", label: t("common.field.updated_at"), minWidth: 180, align: "center" },
90
+ { prop: "operation", label: t("common.field.operation"), cellType: "actions", actions: [
91
+ { label: t("common.action.edit"), link: true, icon: EditPen, hidden: () => !isDefaultTenant.value || !BUTTONS.value["base:message-provider:update"], onClick: scope => openDialog((scope.row as BaseMessageProvider).id) },
92
+ { label: t("common.action.delete"), type: "danger", link: true, icon: Delete, hidden: () => !isDefaultTenant.value || !BUTTONS.value["base:message-provider:delete"], onClick: scope => handleDelete(scope.row as BaseMessageProvider) }
93
+ ] }
94
+ ]);
95
+
96
+ const headerActions = computed<HeaderActionProps[]>(() => [
97
+ { label: t("common.action.create"), type: "primary", icon: CirclePlus, hidden: !isDefaultTenant.value || !BUTTONS.value["base:message-provider:create"], onClick: () => openDialog() },
98
+ { label: t("common.action.delete"), type: "danger", icon: Delete, hidden: !isDefaultTenant.value || !BUTTONS.value["base:message-provider:delete"], disabled: scope => !scope.isSelected, onClick: scope => handleDelete(scope.selectedListIds as number[]) }
99
+ ]);
100
+
101
+ function defaultForm(): FormState {
102
+ return { id: 0, provider: "", name: "", description: "", icon: "", client_id: "", client_secret: "", secret_configured: false, config: {}, config_items: [], sort: 0, status: Status.STATUS_DISABLE };
103
+ }
104
+
105
+ async function requestTable(params: Record<string, unknown>) {
106
+ const data = await defBaseMessageProviderService.PageBaseMessageProvider(buildPageRequest<PageBaseMessageProviderRequest>(params as unknown as PageBaseMessageProviderRequest));
107
+ return { data: { list: data.base_message_providers ?? [], total: data.total } };
108
+ }
109
+
110
+ async function openDialog(id?: number) {
111
+ await formDialogRef.value?.open({
112
+ load: () => (id ? defBaseMessageProviderService.GetBaseMessageProvider({ id }) : undefined),
113
+ commit: data => { Object.assign(formData, defaultForm(), data ?? {}); dialog.editing = Boolean(id); formData.config_items = configToItems(formData.config); }
114
+ });
115
+ }
116
+
117
+ async function handleSubmit() {
118
+ if (!(await formDialogRef.value?.validate())) return;
119
+ const config = configItemsToMap(formData.config_items);
120
+ if (!config) return;
121
+ const payload = JSON.parse(JSON.stringify(formData)) as BaseMessageProviderForm & { config_items?: ConfigItem[] };
122
+ delete payload.config_items;
123
+ payload.config = config;
124
+ if (payload.id) await defBaseMessageProviderService.UpdateBaseMessageProvider({ base_message_provider: payload });
125
+ else await defBaseMessageProviderService.CreateBaseMessageProvider({ base_message_provider: payload });
126
+ ElMessage.success(t("common.message.operation_success"));
127
+ dialog.visible = false;
128
+ await proTable.value?.getTableList();
129
+ }
130
+
131
+ async function handleDelete(value: BaseMessageProvider | number | number[]) {
132
+ const ids = (Array.isArray(value) ? value : typeof value === "object" ? [value.id] : normalizeSelectedIds(value)).join(",");
133
+ if (!ids) return;
134
+ try {
135
+ await ElMessageBox.confirm(t("common.confirm.delete"), t("common.title.warning"), { type: "warning" });
136
+ } catch {
137
+ return;
138
+ }
139
+ await defBaseMessageProviderService.DeleteBaseMessageProvider({ id: ids });
140
+ await proTable.value?.getTableList();
141
+ ElMessage.success(t("common.message.operation_success"));
142
+ }
143
+
144
+ async function handleSetStatus(row: BaseMessageProvider) {
145
+ const status = row.status === Status.STATUS_ENABLE ? Status.STATUS_DISABLE : Status.STATUS_ENABLE;
146
+ try {
147
+ await ElMessageBox.confirm(t("common.dialog.status_change", { action: t(status === Status.STATUS_ENABLE ? "common.status.enabled" : "common.status.disabled"), resource: t("system.base.message_provider.resource"), field: t("system.base.message_provider.field.name"), value: row.name }), t("common.title.notice"), { type: "warning" });
148
+ await defBaseMessageProviderService.SetBaseMessageProviderStatus({ id: row.id, status });
149
+ return true;
150
+ } catch { return false; }
151
+ }
152
+
153
+ function handleClose() { Object.assign(formData, defaultForm()); formDialogRef.value?.resetFields(); }
154
+
155
+ function configToItems(config: Record<string, any> | undefined): ConfigItem[] { return Object.entries(config ?? {}).map(([key, value]) => ({ key, value: typeof value === "string" ? value : JSON.stringify(value) })); }
156
+
157
+ function configItemsToMap(items: ConfigItem[]): Record<string, unknown> | undefined {
158
+ const result: Record<string, unknown> = {};
159
+ for (const item of items) {
160
+ const key = item.key.trim();
161
+ if (!key || Object.prototype.hasOwnProperty.call(result, key)) { ElMessage.error(t("system.base.message_provider.message.config_invalid")); return undefined; }
162
+ const value = item.value.trim();
163
+ if (!value) { result[key] = ""; continue; }
164
+ try { result[key] = JSON.parse(value); } catch { result[key] = value; }
165
+ }
166
+ return result;
167
+ }
168
+ </script>