@liujitcn/kratos-admin-system 0.0.61 → 0.0.64

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 (97) 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 +18 -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 +900 -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 +22 -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 +110 -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/base/ai-provider/index.vue.d.ts +3 -0
  32. package/dist/package/declarations/src/views/base/api-rate-limit-policy/index.vue.d.ts +3 -0
  33. package/dist/package/declarations/src/views/base/message-provider/index.vue.d.ts +3 -0
  34. package/dist/package/declarations/src/views/base/message-template/index.vue.d.ts +3 -0
  35. package/dist/package/declarations/src/views/base/oauth-provider/index.vue.d.ts +3 -0
  36. package/dist/package/declarations/src/views/base/rate-limit-rule/index.vue.d.ts +3 -0
  37. package/dist/package/src/api/base/v1/ai_provider.ts +22 -0
  38. package/dist/package/src/api/system/admin/v1/ai_provider.ts +50 -0
  39. package/dist/package/src/api/system/admin/v1/base_api_rate_limit_policy.ts +51 -0
  40. package/dist/package/src/api/system/admin/v1/base_message_provider.ts +57 -0
  41. package/dist/package/src/api/system/admin/v1/base_message_template.ts +44 -0
  42. package/dist/package/src/api/system/admin/v1/base_oauth_provider.ts +50 -0
  43. package/dist/package/src/api/system/admin/v1/base_rate_limit_rule.ts +58 -0
  44. package/dist/package/src/components/ai/Ai.vue +1 -1
  45. package/dist/package/src/components/api/ApiTransfer.vue +117 -0
  46. package/dist/package/src/components/api/apiTransfer.ts +27 -0
  47. package/dist/package/src/components/i18n/DynamicI18nCell.vue +8 -8
  48. package/dist/package/src/components/i18n/DynamicI18nEditor.vue +6 -3
  49. package/dist/package/src/components/i18n/dynamicI18n.ts +4 -5
  50. package/dist/package/src/components/tenant-project/TenantProjectManager.vue +2 -1
  51. package/dist/package/src/locales/en-US.json +229 -4
  52. package/dist/package/src/locales/ja-JP.json +229 -4
  53. package/dist/package/src/locales/zh-CN.json +229 -4
  54. package/dist/package/src/locales/zh-TW.json +229 -4
  55. package/dist/package/src/rpc/base/v1/ai_message.ts +7 -1
  56. package/dist/package/src/rpc/base/v1/ai_provider.ts +33 -0
  57. package/dist/package/src/rpc/base/v1/oauth.ts +16 -0
  58. package/dist/package/src/rpc/google/protobuf/struct.ts +80 -0
  59. package/dist/package/src/rpc/system/admin/v1/ai_provider.ts +129 -0
  60. package/dist/package/src/rpc/system/admin/v1/base_api_rate_limit_policy.ts +160 -0
  61. package/dist/package/src/rpc/system/admin/v1/base_i18n.ts +4 -24
  62. package/dist/package/src/rpc/system/admin/v1/base_message.ts +4 -4
  63. package/dist/package/src/rpc/system/admin/v1/base_message_category.ts +20 -0
  64. package/dist/package/src/rpc/system/admin/v1/base_message_provider.ts +150 -0
  65. package/dist/package/src/rpc/system/admin/v1/base_message_template.ts +117 -0
  66. package/dist/package/src/rpc/system/admin/v1/base_oauth_provider.ts +156 -0
  67. package/dist/package/src/rpc/system/admin/v1/base_rate_limit_rule.ts +167 -0
  68. package/dist/package/src/utils/persisted-message.ts +21 -0
  69. package/dist/package/src/utils/rateLimit.ts +140 -0
  70. package/dist/package/src/views/ai/chat/components/ChatPanel.vue +36 -4
  71. package/dist/package/src/views/ai/chat/components/XSender.vue +112 -1
  72. package/dist/package/src/views/ai/chat/index.vue +30 -1
  73. package/dist/package/src/views/base/ai-provider/index.vue +512 -0
  74. package/dist/package/src/views/base/api-rate-limit-policy/index.vue +428 -0
  75. package/dist/package/src/views/base/backup-management/archive-record/index.vue +2 -0
  76. package/dist/package/src/views/base/backup-management/archive-restore/index.vue +2 -1
  77. package/dist/package/src/views/base/backup-management/backup-record/index.vue +2 -0
  78. package/dist/package/src/views/base/backup-management/backup-restore/index.vue +2 -1
  79. package/dist/package/src/views/base/config/index.vue +12 -13
  80. package/dist/package/src/views/base/dict/index.vue +2 -3
  81. package/dist/package/src/views/base/dict/item.vue +2 -3
  82. package/dist/package/src/views/base/job/index.vue +2 -3
  83. package/dist/package/src/views/base/job/log.vue +3 -2
  84. package/dist/package/src/views/base/menu/index.vue +27 -83
  85. package/dist/package/src/views/base/message/index.vue +9 -3
  86. package/dist/package/src/views/base/message-category/index.vue +34 -3
  87. package/dist/package/src/views/base/message-provider/index.vue +164 -0
  88. package/dist/package/src/views/base/message-template/index.vue +105 -0
  89. package/dist/package/src/views/base/migration/index.vue +1 -1
  90. package/dist/package/src/views/base/oauth-client/index.vue +6 -6
  91. package/dist/package/src/views/base/oauth-provider/index.vue +274 -0
  92. package/dist/package/src/views/base/rate-limit-rule/index.vue +322 -0
  93. package/dist/package/src/views/base/redact-output-policy/index.vue +1 -1
  94. package/dist/package/src/views/base/redact-rule/index.vue +12 -2
  95. package/dist/package/src/views/base/redact-storage-policy/index.vue +4 -0
  96. package/dist/package/src/views/base/tenant/index.vue +3 -3
  97. package/package.json +2 -2
@@ -0,0 +1,428 @@
1
+ <template>
2
+ <div class="table-box">
3
+ <ProTable ref="table" row-key="id" :columns="columns" :header-actions="headerActions" :request-api="requestTable" />
4
+ <FormDialog
5
+ v-model="dialog.visible"
6
+ ref="dialogRef"
7
+ :title="t(dialog.titleKey)"
8
+ width="min(1600px, calc(100vw - 32px))"
9
+ label-position="left"
10
+ :model="form"
11
+ :fields="fields"
12
+ :rules="rules"
13
+ @confirm="submit"
14
+ @close="resetForm"
15
+ >
16
+ <template #apiTransfer>
17
+ <ApiTransfer
18
+ v-model="form.operations"
19
+ :options="apiOptions"
20
+ :titles="[t('system.base.menu.value.available_api'), t('system.base.menu.value.selected_api')]"
21
+ :panel-height="420"
22
+ />
23
+ </template>
24
+ <template #parameters>
25
+ <div v-if="form.rule_type === BaseRateLimitAlgorithm.BASE_RATE_LIMIT_ALGORITHM_TOKEN_BUCKET" class="parameter-grid">
26
+ <div class="parameter-item">
27
+ <span class="parameter-label">
28
+ <span>{{ t("system.base.rate_limit_rule.field.tokens_per_second") }}</span>
29
+ <el-tooltip :content="t('system.base.rate_limit_rule.tooltip.tokens_per_second')" placement="top" effect="light">
30
+ <el-icon class="parameter-help" tabindex="0"><QuestionFilled /></el-icon>
31
+ </el-tooltip>
32
+ </span>
33
+ <el-input-number v-model="form.params.tokens_per_second" :min="0" :max="1000000" :precision="3" :step="1" controls-position="right" />
34
+ </div>
35
+ <div class="parameter-item">
36
+ <span class="parameter-label">
37
+ <span>{{ t("system.base.rate_limit_rule.field.burst") }}</span>
38
+ <el-tooltip :content="t('system.base.rate_limit_rule.tooltip.burst')" placement="top" effect="light">
39
+ <el-icon class="parameter-help" tabindex="0"><QuestionFilled /></el-icon>
40
+ </el-tooltip>
41
+ </span>
42
+ <el-input-number v-model="form.params.burst" :min="0" :max="1000000" :precision="0" controls-position="right" />
43
+ </div>
44
+ </div>
45
+ <div v-else-if="windowedAlgorithms.includes(form.rule_type)" class="parameter-grid">
46
+ <div class="parameter-item">
47
+ <span class="parameter-label">
48
+ <span>{{ t("system.base.rate_limit_rule.field.limit") }}</span>
49
+ <el-tooltip :content="t('system.base.rate_limit_rule.tooltip.limit')" placement="top" effect="light">
50
+ <el-icon class="parameter-help" tabindex="0"><QuestionFilled /></el-icon>
51
+ </el-tooltip>
52
+ </span>
53
+ <el-input-number v-model="form.params.limit" :min="1" :max="form.rule_type === BaseRateLimitAlgorithm.BASE_RATE_LIMIT_ALGORITHM_SLIDING_WINDOW_LOG ? 10000 : 1000000" :precision="0" controls-position="right" />
54
+ </div>
55
+ <div class="parameter-item">
56
+ <span class="parameter-label">
57
+ <span>{{ t("system.base.rate_limit_rule.field.window_seconds") }}</span>
58
+ <el-tooltip :content="t('system.base.rate_limit_rule.tooltip.window_seconds')" placement="top" effect="light">
59
+ <el-icon class="parameter-help" tabindex="0"><QuestionFilled /></el-icon>
60
+ </el-tooltip>
61
+ </span>
62
+ <el-input-number v-model="form.params.window_seconds" :min="1" :max="86400" :precision="0" controls-position="right" />
63
+ </div>
64
+ </div>
65
+ <div v-else-if="form.rule_type === BaseRateLimitAlgorithm.BASE_RATE_LIMIT_ALGORITHM_LEAKY_BUCKET" class="parameter-grid">
66
+ <div class="parameter-item">
67
+ <span class="parameter-label">
68
+ <span>{{ t("system.base.rate_limit_rule.field.leak_rate_per_second") }}</span>
69
+ <el-tooltip :content="t('system.base.rate_limit_rule.tooltip.leak_rate_per_second')" placement="top" effect="light">
70
+ <el-icon class="parameter-help" tabindex="0"><QuestionFilled /></el-icon>
71
+ </el-tooltip>
72
+ </span>
73
+ <el-input-number v-model="form.params.leak_rate_per_second" :min="0.001" :max="1000000" :precision="3" :step="1" controls-position="right" />
74
+ </div>
75
+ <div class="parameter-item">
76
+ <span class="parameter-label">
77
+ <span>{{ t("system.base.rate_limit_rule.field.capacity") }}</span>
78
+ <el-tooltip :content="t('system.base.rate_limit_rule.tooltip.capacity')" placement="top" effect="light">
79
+ <el-icon class="parameter-help" tabindex="0"><QuestionFilled /></el-icon>
80
+ </el-tooltip>
81
+ </span>
82
+ <el-input-number v-model="form.params.capacity" :min="1" :max="1000000" :precision="0" controls-position="right" />
83
+ </div>
84
+ </div>
85
+ </template>
86
+ </FormDialog>
87
+ </div>
88
+ </template>
89
+
90
+ <script setup lang="ts">
91
+ import { computed, h, reactive, ref } from "vue";
92
+ import { ElMessage, ElMessageBox } from "element-plus";
93
+ import type { FormRules } from "element-plus";
94
+ import { CirclePlus, Delete, EditPen, QuestionFilled } from "@element-plus/icons-vue";
95
+ import type { ColumnProps, HeaderActionProps, ProTableInstance } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
96
+ import ProTable from "@liujitcn/kratos-admin-core/components/ProTable";
97
+ import FormDialog from "@liujitcn/kratos-admin-core/components/Dialog/FormDialog.vue";
98
+ import type { ProFormField, ProFormOption } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
99
+ import ApiTransfer from "@liujitcn/kratos-admin-system/components/api/ApiTransfer.vue";
100
+ import { loadApiTransferCatalog, type ApiTransferOption } from "@liujitcn/kratos-admin-system/components/api/apiTransfer";
101
+ import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
102
+ import { buildPageRequest, normalizeSelectedIds } from "@liujitcn/kratos-admin-core/table";
103
+ import { t } from "@liujitcn/kratos-admin-core";
104
+ import { defBaseApiRateLimitPolicyService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_api_rate_limit_policy";
105
+ import { defBaseRateLimitRuleService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_rate_limit_rule";
106
+ import type { BaseApi } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_api";
107
+ import { BaseRateLimitAlgorithm } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_rate_limit_rule";
108
+ import type {
109
+ BaseApiRateLimitPolicy,
110
+ BaseApiRateLimitPolicyForm,
111
+ PageBaseApiRateLimitPolicyRequest
112
+ } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_api_rate_limit_policy";
113
+ import { BaseApiRateLimitDimension } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_api_rate_limit_policy";
114
+ import type { BaseRateLimitRuleOption } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_rate_limit_rule";
115
+ import { Status } from "@liujitcn/kratos-admin-system/rpc/common/v1/enum";
116
+ import { defaultRateLimitParams, parseRateLimitParams, rateLimitAlgorithmLabelKey, rateLimitSummaryKey, rateLimitSummaryParams, serializeRateLimitParams, type RateLimitParams } from "../../../utils/rateLimit";
117
+
118
+ /** 接口限流策略表单状态。 */
119
+ interface PolicyFormState extends Omit<BaseApiRateLimitPolicyForm, "rule_id"> {
120
+ /** 当前选择的限流规则 ID。 */
121
+ rule_id: number | undefined;
122
+ /** 当前选择算法及其参数快照。 */
123
+ rule_type: BaseRateLimitAlgorithm;
124
+ params: RateLimitParams;
125
+ }
126
+
127
+ defineOptions({ name: "BaseApiRateLimitPolicy", inheritAttrs: false });
128
+
129
+ const { BUTTONS } = useAuthButtons();
130
+ const table = ref<ProTableInstance>();
131
+ const dialogRef = ref<InstanceType<typeof FormDialog>>();
132
+ const ruleCatalog = ref<BaseRateLimitRuleOption[]>([]);
133
+ const apiOptions = ref<ApiTransferOption[]>([]);
134
+ const ruleOptions = ref<ProFormOption[]>([]);
135
+ const dialog = reactive({ visible: false, titleKey: "common.action.create_resource" });
136
+ const form = reactive<PolicyFormState>(defaultForm());
137
+ const windowedAlgorithms = [
138
+ BaseRateLimitAlgorithm.BASE_RATE_LIMIT_ALGORITHM_FIXED_WINDOW,
139
+ BaseRateLimitAlgorithm.BASE_RATE_LIMIT_ALGORITHM_SLIDING_WINDOW_COUNTER,
140
+ BaseRateLimitAlgorithm.BASE_RATE_LIMIT_ALGORITHM_SLIDING_WINDOW_LOG
141
+ ];
142
+ const statusOptions = computed<ProFormOption[]>(() => [
143
+ { label: t("common.status.enabled"), value: Status.STATUS_ENABLE },
144
+ { label: t("common.status.disabled"), value: Status.STATUS_DISABLE }
145
+ ]);
146
+ const dimensionOptions = computed<ProFormOption[]>(() => [
147
+ { label: t("system.base.api_rate_limit_policy.dimension.global"), value: BaseApiRateLimitDimension.BASE_API_RATE_LIMIT_DIMENSION_GLOBAL },
148
+ { label: t("system.base.api_rate_limit_policy.dimension.ip"), value: BaseApiRateLimitDimension.BASE_API_RATE_LIMIT_DIMENSION_IP },
149
+ { label: t("system.base.api_rate_limit_policy.dimension.tenant"), value: BaseApiRateLimitDimension.BASE_API_RATE_LIMIT_DIMENSION_TENANT },
150
+ { label: t("system.base.api_rate_limit_policy.dimension.user"), value: BaseApiRateLimitDimension.BASE_API_RATE_LIMIT_DIMENSION_USER },
151
+ { label: t("system.base.api_rate_limit_policy.dimension.oauth_client"), value: BaseApiRateLimitDimension.BASE_API_RATE_LIMIT_DIMENSION_OAUTH_CLIENT }
152
+ ]);
153
+ const fields = computed<ProFormField[]>(() => [
154
+ { prop: "dimension", label: t("system.base.api_rate_limit_policy.field.dimension"), labelTooltip: t("system.base.api_rate_limit_policy.tooltip.dimension"), component: "select", options: dimensionOptions.value },
155
+ { prop: "rule_id", label: t("system.base.api_rate_limit_policy.field.rule"), labelTooltip: t("system.base.api_rate_limit_policy.tooltip.rule"), component: "select", options: ruleOptions.value, props: { filterable: true, placeholder: t("common.placeholder.select"), onChange: handleRuleChange } },
156
+ { prop: "parameters", label: t("system.base.rate_limit_rule.field.parameters"), labelTooltip: t("system.base.api_rate_limit_policy.tooltip.parameters"), component: "slot", slotName: "parameters", colSpan: 24 },
157
+ {
158
+ prop: "operations",
159
+ label: t("system.base.api_rate_limit_policy.field.api"),
160
+ labelTooltip: t("system.base.api_rate_limit_policy.tooltip.apis"),
161
+ component: "slot",
162
+ slotName: "apiTransfer",
163
+ colSpan: 24
164
+ },
165
+ { prop: "status", label: t("common.field.status"), component: "radio-group", options: statusOptions.value },
166
+ { prop: "remark", label: t("common.field.remark"), component: "textarea" }
167
+ ]);
168
+ const rules = computed<FormRules>(() => ({
169
+ operations: [{ required: true, type: "array", min: 1, message: t("system.base.api_rate_limit_policy.validation.api"), trigger: "change" }],
170
+ dimension: [{ required: true, message: t("system.base.api_rate_limit_policy.validation.dimension"), trigger: "change" }],
171
+ rule_id: [{ required: true, message: t("system.base.api_rate_limit_policy.validation.rule"), trigger: "change" }]
172
+ }));
173
+ const columns = computed<ColumnProps[]>(() => [
174
+ { type: "selection", width: 55 },
175
+ { prop: "dimension", label: t("system.base.api_rate_limit_policy.field.dimension"), width: 150, render: scope => dimensionLabel((scope.row as BaseApiRateLimitPolicy).dimension) },
176
+ { prop: "rule_name", label: t("system.base.api_rate_limit_policy.field.rule"), minWidth: 160 },
177
+ { prop: "rule_type", label: t("system.base.rate_limit_rule.field.rule_type"), width: 190, render: scope => algorithmLabel((scope.row as BaseApiRateLimitPolicy).rule_type) },
178
+ { prop: "rule_params", label: t("system.base.rate_limit_rule.field.parameters"), minWidth: 230, render: scope => paramsSummary((scope.row as BaseApiRateLimitPolicy).rule_type, (scope.row as BaseApiRateLimitPolicy).rule_params) },
179
+ {
180
+ prop: "apis",
181
+ label: t("system.base.api_rate_limit_policy.field.api"),
182
+ minWidth: 360,
183
+ showOverflowTooltip: { popperClass: "api-rate-limit-api-tooltip" },
184
+ tooltipFormatter: ({ row }) =>
185
+ h(
186
+ "div",
187
+ { class: "api-rate-limit-api-tooltip__content" },
188
+ (row as BaseApiRateLimitPolicy).apis.map(api => h("div", { class: "api-rate-limit-api-tooltip__item" }, formatApiLabel(api)))
189
+ ),
190
+ render: scope => (scope.row as BaseApiRateLimitPolicy).apis.map(formatApiLabel).join(" · ")
191
+ },
192
+ {
193
+ prop: "status",
194
+ label: t("common.field.status"),
195
+ width: 100,
196
+ cellType: "status",
197
+ statusProps: {
198
+ activeValue: Status.STATUS_ENABLE,
199
+ inactiveValue: Status.STATUS_DISABLE,
200
+ activeText: t("common.status.enabled"),
201
+ inactiveText: t("common.status.disabled"),
202
+ disabled: () => !BUTTONS.value["base:api-rate-limit-policy:status"],
203
+ beforeChange: scope => changeStatus(scope.row as BaseApiRateLimitPolicy)
204
+ }
205
+ },
206
+ {
207
+ prop: "actions",
208
+ label: t("common.field.operation"),
209
+ cellType: "actions",
210
+ actions: [
211
+ { label: t("common.action.edit"), type: "primary", link: true, icon: EditPen, hidden: () => !BUTTONS.value["base:api-rate-limit-policy:update"], onClick: scope => openDialog((scope.row as BaseApiRateLimitPolicy).id) },
212
+ { label: t("common.action.delete"), type: "danger", link: true, icon: Delete, hidden: () => !BUTTONS.value["base:api-rate-limit-policy:delete"], onClick: scope => deleteItems(scope.row as BaseApiRateLimitPolicy) }
213
+ ]
214
+ }
215
+ ]);
216
+ const headerActions = computed<HeaderActionProps[]>(() => [
217
+ { label: t("common.action.create"), type: "success", icon: CirclePlus, hidden: () => !BUTTONS.value["base:api-rate-limit-policy:create"], onClick: () => openDialog() },
218
+ { label: t("common.action.delete"), type: "danger", icon: Delete, hidden: () => !BUTTONS.value["base:api-rate-limit-policy:delete"], disabled: scope => !scope.selectedList.length, onClick: scope => deleteItems(scope.selectedList as BaseApiRateLimitPolicy[]) }
219
+ ]);
220
+
221
+ /** 请求接口限流策略列表。 */
222
+ async function requestTable(params: PageBaseApiRateLimitPolicyRequest) {
223
+ const data = await defBaseApiRateLimitPolicyService.PageBaseApiRateLimitPolicy(buildPageRequest(params));
224
+ return { data: { list: data.base_api_rate_limit_policies ?? [], total: data.total } };
225
+ }
226
+
227
+ /** 打开新增或编辑接口限流策略弹窗。 */
228
+ async function openDialog(id?: number) {
229
+ await loadOptions();
230
+ await dialogRef.value?.open({
231
+ load: () => (id !== undefined ? defBaseApiRateLimitPolicyService.GetBaseApiRateLimitPolicy({ id }) : undefined),
232
+ commit: data => {
233
+ resetForm();
234
+ if (data) {
235
+ Object.assign(form, data);
236
+ handleRuleChange(data.rule_id);
237
+ form.params = parseRateLimitParams(form.rule_type, data.rule_params);
238
+ }
239
+ dialog.titleKey = id !== undefined ? "common.action.edit_resource" : "common.action.create_resource";
240
+ }
241
+ });
242
+ }
243
+
244
+ /** 加载可配置接口和限流规则。 */
245
+ async function loadOptions() {
246
+ const [apiCatalog, rules] = await Promise.all([
247
+ loadApiTransferCatalog({ include_public: true }),
248
+ defBaseRateLimitRuleService.OptionBaseRateLimitRule({ keyword: "" })
249
+ ]);
250
+ apiOptions.value = apiCatalog.options;
251
+ ruleCatalog.value = rules.list ?? [];
252
+ ruleOptions.value = ruleCatalog.value.map(rule => ({ label: `${rule.label} (${rule.code}) · ${algorithmLabel(rule.rule_type)}`, value: rule.id, disabled: rule.disabled }));
253
+ }
254
+
255
+ /** 重置接口限流策略表单。 */
256
+ function resetForm() {
257
+ dialog.visible = false;
258
+ dialogRef.value?.resetFields();
259
+ Object.assign(form, defaultForm());
260
+ }
261
+
262
+ /** 根据所选模板加载默认参数。 */
263
+ function handleRuleChange(ruleId?: number) {
264
+ const selected = ruleCatalog.value.find(rule => rule.id === Number(ruleId));
265
+ form.rule_type = selected?.rule_type ?? BaseRateLimitAlgorithm.BASE_RATE_LIMIT_ALGORITHM_UNSPECIFIED;
266
+ form.params = selected ? parseRateLimitParams(form.rule_type, selected.default_params) : defaultRateLimitParams(form.rule_type);
267
+ }
268
+
269
+ /** 校验并保存接口策略参数快照。 */
270
+ async function submit() {
271
+ const valid = await dialogRef.value?.validate();
272
+ if (!valid || form.rule_id === undefined) return;
273
+
274
+ const params = serializeRateLimitParams(form.rule_type, form.params);
275
+ if (!params) {
276
+ ElMessage.warning(t("system.base.rate_limit_rule.validation.parameters"));
277
+ return;
278
+ }
279
+ form.rule_params = JSON.stringify(params);
280
+ const payload: BaseApiRateLimitPolicyForm = {
281
+ id: form.id,
282
+ operations: form.operations,
283
+ dimension: form.dimension,
284
+ rule_id: form.rule_id,
285
+ rule_params: form.rule_params,
286
+ status: form.status,
287
+ remark: form.remark
288
+ };
289
+ if (form.id) await defBaseApiRateLimitPolicyService.UpdateBaseApiRateLimitPolicy({ base_api_rate_limit_policy: payload });
290
+ else await defBaseApiRateLimitPolicyService.CreateBaseApiRateLimitPolicy({ base_api_rate_limit_policy: payload });
291
+ ElMessage.success(t(form.id ? "common.message.update_success" : "common.message.create_success", { resource: t("system.base.api_rate_limit_policy.title") }));
292
+ resetForm();
293
+ table.value?.getTableList();
294
+ }
295
+
296
+ /** 确认并切换接口策略状态。 */
297
+ async function changeStatus(row: BaseApiRateLimitPolicy) {
298
+ const next = row.status === Status.STATUS_ENABLE ? Status.STATUS_DISABLE : Status.STATUS_ENABLE;
299
+ try {
300
+ await ElMessageBox.confirm(
301
+ t("common.dialog.status_change", {
302
+ action: t(next === Status.STATUS_ENABLE ? "common.status.enabled" : "common.status.disabled"),
303
+ resource: t("system.base.api_rate_limit_policy.title"),
304
+ field: t("system.base.api_rate_limit_policy.field.api"),
305
+ value: row.apis.map(api => api.operation).join(", ")
306
+ }),
307
+ t("common.title.warning"),
308
+ { type: "warning" }
309
+ );
310
+ await defBaseApiRateLimitPolicyService.SetBaseApiRateLimitPolicyStatus({ id: row.id, status: next });
311
+ table.value?.getTableList();
312
+ return true;
313
+ } catch {
314
+ return false;
315
+ }
316
+ }
317
+
318
+ /** 删除选中的接口限流策略。 */
319
+ async function deleteItems(selected?: BaseApiRateLimitPolicy | BaseApiRateLimitPolicy[] | number | string | Array<number | string>) {
320
+ const items = Array.isArray(selected) ? selected.filter((item): item is BaseApiRateLimitPolicy => typeof item === "object") : selected && typeof selected === "object" ? [selected] : [];
321
+ const ids = items.length ? items.map(item => item.id) : normalizeSelectedIds(selected as number | string | Array<number | string>);
322
+ if (!ids.length) {
323
+ ElMessage.warning(t("common.message.select_delete_item"));
324
+ return;
325
+ }
326
+ try {
327
+ await ElMessageBox.confirm(t("common.dialog.delete_selected", { resource: t("system.base.api_rate_limit_policy.title") }), t("common.title.warning"), { type: "warning" });
328
+ await defBaseApiRateLimitPolicyService.DeleteBaseApiRateLimitPolicy({ id: ids.join(",") });
329
+ ElMessage.success(t("common.message.delete_success", { resource: t("system.base.api_rate_limit_policy.title") }));
330
+ table.value?.getTableList();
331
+ } catch {
332
+ return;
333
+ }
334
+ }
335
+
336
+ /** 格式化策略参数摘要。 */
337
+ function paramsSummary(algorithm: BaseRateLimitAlgorithm, raw: string) {
338
+ return t(rateLimitSummaryKey(algorithm), rateLimitSummaryParams(algorithm, raw));
339
+ }
340
+
341
+ /** 返回限流算法显示名称。 */
342
+ function algorithmLabel(algorithm: BaseRateLimitAlgorithm) { return t(rateLimitAlgorithmLabelKey(algorithm)); }
343
+
344
+ /** 返回限流维度的显示名称。 */
345
+ function dimensionLabel(dimension: BaseApiRateLimitDimension) {
346
+ const option = dimensionOptions.value.find(item => item.value === dimension);
347
+ return option?.label ?? String(dimension);
348
+ }
349
+
350
+ /** 格式化限流策略表格中的接口信息。 */
351
+ function formatApiLabel(api: Pick<BaseApi, "service_desc" | "service_name" | "method" | "path" | "operation">) {
352
+ return `${api.service_desc || api.service_name} · ${api.method} ${api.path} · ${api.operation}`;
353
+ }
354
+
355
+ /** 返回接口策略表单初始值。 */
356
+ function defaultForm(): PolicyFormState {
357
+ const rule_type = BaseRateLimitAlgorithm.BASE_RATE_LIMIT_ALGORITHM_UNSPECIFIED;
358
+ return {
359
+ id: 0,
360
+ operations: [],
361
+ dimension: BaseApiRateLimitDimension.BASE_API_RATE_LIMIT_DIMENSION_GLOBAL,
362
+ rule_id: undefined,
363
+ rule_params: "{}",
364
+ rule_type,
365
+ status: Status.STATUS_ENABLE,
366
+ remark: "",
367
+ params: defaultRateLimitParams(rule_type)
368
+ };
369
+ }
370
+ </script>
371
+
372
+ <style lang="scss">
373
+ .api-rate-limit-api-tooltip {
374
+ max-width: min(960px, calc(100vw - 48px));
375
+ }
376
+ .api-rate-limit-api-tooltip__content {
377
+ display: flex;
378
+ flex-direction: column;
379
+ gap: 6px;
380
+ max-height: min(420px, calc(100vh - 32px));
381
+ overflow-y: auto;
382
+ }
383
+ .api-rate-limit-api-tooltip__item {
384
+ overflow-wrap: anywhere;
385
+ white-space: normal;
386
+ }
387
+ </style>
388
+
389
+ <style scoped>
390
+ .parameter-grid {
391
+ display: grid;
392
+ grid-template-columns: repeat(2, minmax(0, 1fr));
393
+ gap: 12px;
394
+ width: 100%;
395
+ }
396
+
397
+ .parameter-item {
398
+ display: flex;
399
+ align-items: center;
400
+ gap: 8px;
401
+ min-width: 0;
402
+ }
403
+
404
+ .parameter-label {
405
+ display: inline-flex;
406
+ flex: none;
407
+ align-items: center;
408
+ gap: 4px;
409
+ color: var(--el-text-color-regular);
410
+ font-size: 12px;
411
+ }
412
+
413
+ .parameter-help {
414
+ color: var(--el-text-color-placeholder);
415
+ cursor: help;
416
+ }
417
+
418
+ .parameter-item :deep(.el-input-number) {
419
+ flex: 1;
420
+ width: 0;
421
+ min-width: 0;
422
+ }
423
+ @media (width <= 900px) {
424
+ .parameter-grid {
425
+ grid-template-columns: minmax(0, 1fr);
426
+ }
427
+ }
428
+ </style>
@@ -11,6 +11,7 @@ import type { ColumnProps, ProTableInstance } from "@liujitcn/kratos-admin-core/
11
11
  import { buildPageRequest } from "@liujitcn/kratos-admin-core/table";
12
12
  import { t } from "@liujitcn/kratos-admin-core";
13
13
  import { formatDateTime } from "@liujitcn/kratos-admin-core/format";
14
+ import { resolvePersistedMessage } from "../../../../utils/persisted-message";
14
15
  import { defBaseTableArchiveRecordService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_table_archive_record";
15
16
  import { BaseTableArchiveMode } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_table_archive";
16
17
  import { BaseTableArchiveRecordStatus, type PageBaseTableArchiveRecordRequest } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_table_archive_record";
@@ -33,6 +34,7 @@ const columns = computed<ColumnProps[]>(() => [
33
34
  { prop: "deleted_rows", label: t("system.backup.field.deleted_rows"), width: 110, align: "right" },
34
35
  { prop: "object_key", label: t("system.backup.field.object_key"), minWidth: 220 },
35
36
  { prop: "status", label: t("common.field.status"), width: 110, search: { el: "select", enum: statusOptions.value }, render: scope => statusOptions.value.find(item => item.value === scope.row.status)?.label ?? String(scope.row.status) },
37
+ { prop: "error", label: t("system.backup.field.error"), minWidth: 220, render: scope => resolvePersistedMessage(scope.row.error) },
36
38
  { prop: "started_at", align: "center", label: t("system.backup.field.started_at"), minWidth: 170, render: scope => formatDateTime(scope.row.started_at) },
37
39
  { prop: "finished_at", align: "center", label: t("system.backup.field.finished_at"), minWidth: 170, render: scope => formatDateTime(scope.row.finished_at) }
38
40
  ]);
@@ -20,6 +20,7 @@ import { buildPageRequest } from "@liujitcn/kratos-admin-core/table";
20
20
  import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
21
21
  import { t } from "@liujitcn/kratos-admin-core";
22
22
  import { formatDateTime } from "@liujitcn/kratos-admin-core/format";
23
+ import { resolvePersistedMessage } from "../../../../utils/persisted-message";
23
24
  import { defBaseTableArchiveRestoreService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_table_archive_restore";
24
25
  import { BaseTableArchiveRestoreMode, BaseTableArchiveRestoreStatus, type BaseTableArchiveRestore } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_table_archive_restore";
25
26
 
@@ -37,7 +38,7 @@ const formRules = computed(() => ({
37
38
  { type: "number", min: 1, message: t("system.backup.validation.archive_record_id_positive"), trigger: "change" }
38
39
  ]
39
40
  }));
40
- const columns = computed<ColumnProps[]>(() => [{ prop: "id", label: t("system.backup.field.id"), width: 90 }, { prop: "archive_record_id", label: t("system.backup.field.archive_record_id"), width: 130 }, { prop: "table_name", label: t("system.backup.field.table_name"), minWidth: 180 }, { prop: "restore_mode", label: t("system.backup.field.restore_mode"), width: 120, render: scope => modeOptions.value.find(item => item.value === scope.row.restore_mode)?.label ?? String(scope.row.restore_mode) }, { prop: "restored_rows", label: t("system.backup.field.restored_rows"), width: 110 }, { prop: "status", label: t("common.field.status"), width: 110 }, { prop: "error", label: t("system.backup.field.error"), minWidth: 220 }, { prop: "started_at", align: "center", label: t("system.backup.field.started_at"), minWidth: 170, render: scope => formatDateTime(scope.row.started_at) }, { prop: "finished_at", align: "center", label: t("system.backup.field.finished_at"), minWidth: 170, render: scope => formatDateTime(scope.row.finished_at) }]);
41
+ const columns = computed<ColumnProps[]>(() => [{ prop: "id", label: t("system.backup.field.id"), width: 90 }, { prop: "archive_record_id", label: t("system.backup.field.archive_record_id"), width: 130 }, { prop: "table_name", label: t("system.backup.field.table_name"), minWidth: 180 }, { prop: "restore_mode", label: t("system.backup.field.restore_mode"), width: 120, render: scope => modeOptions.value.find(item => item.value === scope.row.restore_mode)?.label ?? String(scope.row.restore_mode) }, { prop: "restored_rows", label: t("system.backup.field.restored_rows"), width: 110 }, { prop: "status", label: t("common.field.status"), width: 110 }, { prop: "error", label: t("system.backup.field.error"), minWidth: 220, render: scope => resolvePersistedMessage(scope.row.error) }, { prop: "started_at", align: "center", label: t("system.backup.field.started_at"), minWidth: 170, render: scope => formatDateTime(scope.row.started_at) }, { prop: "finished_at", align: "center", label: t("system.backup.field.finished_at"), minWidth: 170, render: scope => formatDateTime(scope.row.finished_at) }]);
41
42
 
42
43
  /** 请求归档恢复记录分页列表。 */
43
44
  async function requestTable(params: Record<string, unknown>) { const data = await defBaseTableArchiveRestoreService.PageBaseTableArchiveRestore(buildPageRequest(params)); return { data: { list: data.base_table_archive_restores ?? [], total: data.total } }; }
@@ -11,6 +11,7 @@ import type { ColumnProps, ProTableInstance } from "@liujitcn/kratos-admin-core/
11
11
  import { buildPageRequest } from "@liujitcn/kratos-admin-core/table";
12
12
  import { t } from "@liujitcn/kratos-admin-core";
13
13
  import { formatDateTime } from "@liujitcn/kratos-admin-core/format";
14
+ import { resolvePersistedMessage } from "../../../../utils/persisted-message";
14
15
  import { defBaseTableBackupRecordService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_table_backup_record";
15
16
  import { BaseTableBackupRecordStatus, type PageBaseTableBackupRecordRequest } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_table_backup_record";
16
17
 
@@ -29,6 +30,7 @@ const columns = computed<ColumnProps[]>(() => [
29
30
  { prop: "object_key", label: t("system.backup.field.object_key"), minWidth: 260 },
30
31
  { prop: "size_bytes", label: t("system.backup.field.size_bytes"), width: 110 },
31
32
  { prop: "status", label: t("common.field.status"), width: 110, search: { el: "select", enum: statusOptions.value }, render: scope => statusOptions.value.find(item => item.value === scope.row.status)?.label ?? String(scope.row.status) },
33
+ { prop: "error", label: t("system.backup.field.error"), minWidth: 220, render: scope => resolvePersistedMessage(scope.row.error) },
32
34
  { prop: "started_at", align: "center", label: t("system.backup.field.started_at"), minWidth: 170, render: scope => formatDateTime(scope.row.started_at) },
33
35
  { prop: "finished_at", align: "center", label: t("system.backup.field.finished_at"), minWidth: 170, render: scope => formatDateTime(scope.row.finished_at) },
34
36
  { prop: "verified_at", label: t("system.backup.field.verified_at"), minWidth: 170 }
@@ -20,6 +20,7 @@ import { buildPageRequest } from "@liujitcn/kratos-admin-core/table";
20
20
  import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
21
21
  import { t } from "@liujitcn/kratos-admin-core";
22
22
  import { formatDateTime } from "@liujitcn/kratos-admin-core/format";
23
+ import { resolvePersistedMessage } from "../../../../utils/persisted-message";
23
24
  import { defBaseTableBackupRestoreService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_table_backup_restore";
24
25
  import { defBaseTableSourceService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_table_source";
25
26
  import { BaseTableBackupRestoreMode, BaseTableBackupRestoreStatus, type BaseTableBackupRestore } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_table_backup_restore";
@@ -41,7 +42,7 @@ const formRules = computed(() => ({
41
42
  target_source_name: [{ required: true, message: t("system.backup.validation.target_source_name"), trigger: "blur" }],
42
43
  target_database: [{ required: true, message: t("system.backup.validation.target_database"), trigger: "blur" }]
43
44
  }));
44
- const columns = computed<ColumnProps[]>(() => [{ prop: "id", label: t("system.backup.field.id"), width: 90 }, { prop: "backup_record_id", label: t("system.backup.field.backup_record_id"), width: 130 }, { prop: "source_name", label: t("system.backup.field.source_name"), minWidth: 130 }, { prop: "target_source_name", label: t("system.backup.field.target_source_name"), minWidth: 130 }, { prop: "target_database", label: t("system.backup.field.target_database"), minWidth: 150 }, { prop: "restore_mode", label: t("system.backup.field.restore_mode"), width: 120, render: scope => modeOptions.value.find(item => item.value === scope.row.restore_mode)?.label ?? String(scope.row.restore_mode) }, { prop: "status", label: t("common.field.status"), width: 110 }, { prop: "error", label: t("system.backup.field.error"), minWidth: 220 }, { prop: "started_at", align: "center", label: t("system.backup.field.started_at"), minWidth: 170, render: scope => formatDateTime(scope.row.started_at) }, { prop: "finished_at", align: "center", label: t("system.backup.field.finished_at"), minWidth: 170, render: scope => formatDateTime(scope.row.finished_at) }]);
45
+ const columns = computed<ColumnProps[]>(() => [{ prop: "id", label: t("system.backup.field.id"), width: 90 }, { prop: "backup_record_id", label: t("system.backup.field.backup_record_id"), width: 130 }, { prop: "source_name", label: t("system.backup.field.source_name"), minWidth: 130 }, { prop: "target_source_name", label: t("system.backup.field.target_source_name"), minWidth: 130 }, { prop: "target_database", label: t("system.backup.field.target_database"), minWidth: 150 }, { prop: "restore_mode", label: t("system.backup.field.restore_mode"), width: 120, render: scope => modeOptions.value.find(item => item.value === scope.row.restore_mode)?.label ?? String(scope.row.restore_mode) }, { prop: "status", label: t("common.field.status"), width: 110 }, { prop: "error", label: t("system.backup.field.error"), minWidth: 220, render: scope => resolvePersistedMessage(scope.row.error) }, { prop: "started_at", align: "center", label: t("system.backup.field.started_at"), minWidth: 170, render: scope => formatDateTime(scope.row.started_at) }, { prop: "finished_at", align: "center", label: t("system.backup.field.finished_at"), minWidth: 170, render: scope => formatDateTime(scope.row.finished_at) }]);
45
46
 
46
47
  /** 请求备份恢复记录分页列表。 */
47
48
  async function requestTable(params: Record<string, unknown>) { const data = await defBaseTableBackupRestoreService.PageBaseTableBackupRestore(buildPageRequest(params)); return { data: { list: data.base_table_backup_restores ?? [], total: data.total } }; }
@@ -142,7 +142,6 @@ import { BaseConfigSite } from "@liujitcn/kratos-admin-system/rpc/base/v1/config
142
142
  import { BaseFileAccessMode } from "@liujitcn/kratos-admin-core/rpc/base/v1/file";
143
143
  import { Status } from "@liujitcn/kratos-admin-system/rpc/common/v1/enum";
144
144
  import { BaseConfigType } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_config";
145
- import { I18nTargetType } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_i18n";
146
145
  import { buildPageRequest, normalizeSelectedIds } from "@liujitcn/kratos-admin-core/table";
147
146
  import { t } from "@liujitcn/kratos-admin-core";
148
147
  import DynamicI18nEditor from "@liujitcn/kratos-admin-system/components/i18n/DynamicI18nEditor.vue";
@@ -257,8 +256,8 @@ watch(
257
256
  );
258
257
 
259
258
  /** 将配置值翻译记录转换为编辑器值,缺少记录时保留可编辑的空行。 */
260
- function normalizeConfigI18ns(targetType: I18nTargetType): DynamicI18nValue[] {
261
- const records = targetType === I18nTargetType.I18N_TARGET_TYPE_BASE_CONFIG_NAME ? formData.name_i18ns : formData.value_i18ns;
259
+ function normalizeConfigI18ns(targetKey: string): DynamicI18nValue[] {
260
+ const records = targetKey === "base_config.name" ? formData.name_i18ns : formData.value_i18ns;
262
261
  return getEditableLanguageOptions()
263
262
  .map(item => item.value)
264
263
  .map(locale => {
@@ -272,17 +271,17 @@ function normalizeConfigI18ns(targetType: I18nTargetType): DynamicI18nValue[] {
272
271
  }
273
272
 
274
273
  /** 保存指定目标类型的编辑器值,并保留其他字段的翻译。 */
275
- function updateConfigI18ns(targetType: I18nTargetType, values: DynamicI18nValue[]) {
274
+ function updateConfigI18ns(targetKey: string, values: DynamicI18nValue[]) {
276
275
  const next = values.map(
277
276
  item =>
278
277
  ({
279
278
  ...item,
280
- target_type: targetType,
279
+ target_key: targetKey,
281
280
  target_id: formData.id,
282
281
  name: item.text
283
282
  }) as BaseI18n
284
283
  );
285
- if (targetType === I18nTargetType.I18N_TARGET_TYPE_BASE_CONFIG_NAME) {
284
+ if (targetKey === "base_config.name") {
286
285
  formData.name_i18ns = next;
287
286
  } else {
288
287
  formData.value_i18ns = next;
@@ -290,13 +289,13 @@ function updateConfigI18ns(targetType: I18nTargetType, values: DynamicI18nValue[
290
289
  }
291
290
 
292
291
  const nameI18nValues = computed<DynamicI18nValue[]>({
293
- get: () => normalizeConfigI18ns(I18nTargetType.I18N_TARGET_TYPE_BASE_CONFIG_NAME),
294
- set: values => updateConfigI18ns(I18nTargetType.I18N_TARGET_TYPE_BASE_CONFIG_NAME, values)
292
+ get: () => normalizeConfigI18ns("base_config.name"),
293
+ set: values => updateConfigI18ns("base_config.name", values)
295
294
  });
296
295
 
297
296
  const valueI18nValues = computed<DynamicI18nValue[]>({
298
- get: () => normalizeConfigI18ns(I18nTargetType.I18N_TARGET_TYPE_BASE_CONFIG_VALUE),
299
- set: values => updateConfigI18ns(I18nTargetType.I18N_TARGET_TYPE_BASE_CONFIG_VALUE, values)
297
+ get: () => normalizeConfigI18ns("base_config.value"),
298
+ set: values => updateConfigI18ns("base_config.value", values)
300
299
  });
301
300
 
302
301
  const rules = computed(() => ({
@@ -539,7 +538,7 @@ const columns = computed<ColumnProps[]>(() => [
539
538
  function renderConfigNameCell(row: BaseConfig) {
540
539
  return h(DynamicI18nCell, {
541
540
  source: row.name,
542
- targetType: I18nTargetType.I18N_TARGET_TYPE_BASE_CONFIG_NAME,
541
+ targetKey: "base_config.name",
543
542
  targetId: row.id,
544
543
  i18ns: row.i18ns,
545
544
  editable: !!BUTTONS.value["base:config:update"]
@@ -777,7 +776,7 @@ async function handleSubmit() {
777
776
  const submitData = JSON.parse(JSON.stringify(formData)) as BaseConfigForm;
778
777
  submitData.name_i18ns = serializeDynamicI18ns(
779
778
  nameI18nValues.value,
780
- I18nTargetType.I18N_TARGET_TYPE_BASE_CONFIG_NAME,
779
+ "base_config.name",
781
780
  submitData.id,
782
781
  submitData.name
783
782
  );
@@ -785,7 +784,7 @@ async function handleSubmit() {
785
784
  submitData.type === BaseConfigType.BASE_CONFIG_TYPE_TEXT || submitData.type === BaseConfigType.BASE_CONFIG_TYPE_RICH_TEXT
786
785
  ? serializeDynamicI18ns(
787
786
  valueI18nValues.value,
788
- I18nTargetType.I18N_TARGET_TYPE_BASE_CONFIG_VALUE,
787
+ "base_config.value",
789
788
  submitData.id,
790
789
  submitData.value
791
790
  )
@@ -46,7 +46,6 @@ import { loadEnabledBaseLanguages } from "@liujitcn/kratos-admin-system/api/syst
46
46
  import type { BaseDict, BaseDictForm, PageBaseDictRequest } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_dict";
47
47
  import router, { navigateTo } from "@liujitcn/kratos-admin-core/navigation";
48
48
  import { Status } from "@liujitcn/kratos-admin-system/rpc/common/v1/enum";
49
- import { I18nTargetType } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_i18n";
50
49
  import { buildPageRequest, normalizeSelectedIds } from "@liujitcn/kratos-admin-core/table";
51
50
  import { t } from "@liujitcn/kratos-admin-core";
52
51
  import DynamicI18nEditor from "@liujitcn/kratos-admin-system/components/i18n/DynamicI18nEditor.vue";
@@ -139,7 +138,7 @@ function renderDictNameCell(scope: RenderScope<BaseDict>) {
139
138
  const row = scope.row;
140
139
  return h(DynamicI18nCell, {
141
140
  source: row.name,
142
- targetType: I18nTargetType.I18N_TARGET_TYPE_BASE_DICT_NAME,
141
+ targetKey: "base_dict.name",
143
142
  targetId: row.id,
144
143
  i18ns: row.i18ns
145
144
  });
@@ -294,7 +293,7 @@ function handleSubmit() {
294
293
  const submitData = JSON.parse(JSON.stringify(formData)) as BaseDictForm;
295
294
  submitData.i18ns = serializeDynamicI18ns(
296
295
  i18nValues.value,
297
- I18nTargetType.I18N_TARGET_TYPE_BASE_DICT_NAME,
296
+ "base_dict.name",
298
297
  submitData.id,
299
298
  submitData.name
300
299
  );
@@ -65,7 +65,6 @@ import type {
65
65
  PageBaseDictItemRequest
66
66
  } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_dict_item";
67
67
  import { Status } from "@liujitcn/kratos-admin-system/rpc/common/v1/enum";
68
- import { I18nTargetType } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_i18n";
69
68
  import { buildPageRequest, normalizeSelectedIds } from "@liujitcn/kratos-admin-core/table";
70
69
  import { t } from "@liujitcn/kratos-admin-core";
71
70
  import DynamicI18nEditor from "@liujitcn/kratos-admin-system/components/i18n/DynamicI18nEditor.vue";
@@ -194,7 +193,7 @@ function renderDictItemLabelCell(scope: RenderScope<BaseDictItem>) {
194
193
  const row = scope.row;
195
194
  return h(DynamicI18nCell, {
196
195
  source: row.label,
197
- targetType: I18nTargetType.I18N_TARGET_TYPE_BASE_DICT_ITEM_LABEL,
196
+ targetKey: "base_dict_item.label",
198
197
  targetId: row.id,
199
198
  i18ns: row.i18ns
200
199
  });
@@ -361,7 +360,7 @@ function handleSubmit() {
361
360
  const submitData = JSON.parse(JSON.stringify(formData)) as BaseDictItemForm;
362
361
  submitData.i18ns = serializeDynamicI18ns(
363
362
  i18nValues.value,
364
- I18nTargetType.I18N_TARGET_TYPE_BASE_DICT_ITEM_LABEL,
363
+ "base_dict_item.label",
365
364
  submitData.id,
366
365
  submitData.label
367
366
  );