@liujitcn/kratos-admin-system 0.0.39 → 0.0.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +22 -0
  2. package/dist/package/declarations/src/api/system/admin/v1/base_config.d.ts +1 -5
  3. package/dist/package/declarations/src/components/DynamicI18nCell.vue.d.ts +2 -8
  4. package/dist/package/declarations/src/components/DynamicI18nEditor.vue.d.ts +20 -3
  5. package/dist/package/declarations/src/components/dynamicI18n.d.ts +2 -2
  6. package/dist/package/declarations/src/locales/generated.d.ts +5797 -5813
  7. package/dist/package/declarations/src/rpc/system/admin/v1/base_config.d.ts +4 -40
  8. package/dist/package/declarations/src/utils/code-gen-progress.d.ts +3 -0
  9. package/dist/package/declarations/src/views/tool/code-gen/components/CodeGenGenerateConfirm.vue.d.ts +11 -0
  10. package/dist/package/declarations/src/views/tool/code-gen/components/CodeGenLocaleEditor.vue.d.ts +5 -9
  11. package/dist/package/declarations/src/views/tool/code-gen/config.d.ts +3 -0
  12. package/dist/package/src/api/system/admin/v1/base_config.ts +0 -20
  13. package/dist/package/src/components/DynamicI18nCell.vue +55 -220
  14. package/dist/package/src/components/DynamicI18nEditor.vue +150 -146
  15. package/dist/package/src/components/dynamicI18n.ts +10 -6
  16. package/dist/package/src/config/index.ts +1 -3
  17. package/dist/package/src/locales/en-US.json +1430 -1434
  18. package/dist/package/src/locales/ja-JP.json +1430 -1434
  19. package/dist/package/src/locales/zh-CN.json +1429 -1433
  20. package/dist/package/src/locales/zh-TW.json +1429 -1433
  21. package/dist/package/src/module.ts +0 -1
  22. package/dist/package/src/rpc/system/admin/v1/base_config.ts +3 -43
  23. package/dist/package/src/utils/code-gen-progress.ts +15 -0
  24. package/dist/package/src/views/base/config/index.vue +183 -48
  25. package/dist/package/src/views/base/dict/index.vue +6 -9
  26. package/dist/package/src/views/base/dict/item.vue +6 -15
  27. package/dist/package/src/views/base/job/index.vue +16 -17
  28. package/dist/package/src/views/base/menu/index.vue +78 -15
  29. package/dist/package/src/views/tool/code-gen/columns/index.vue +17 -25
  30. package/dist/package/src/views/tool/code-gen/components/CodeGenGenerateConfirm.vue +115 -0
  31. package/dist/package/src/views/tool/code-gen/components/CodeGenLocaleEditor.vue +27 -186
  32. package/dist/package/src/views/tool/code-gen/components/CodeGenProgressDialog.vue +2 -0
  33. package/dist/package/src/views/tool/code-gen/config.ts +15 -1
  34. package/dist/package/src/views/tool/code-gen/table/index.vue +69 -40
  35. package/package.json +3 -2
  36. package/dist/package/declarations/src/config/database-backup.d.ts +0 -12
  37. package/dist/package/declarations/src/views/base/runtime-config/index.vue.d.ts +0 -3
  38. package/dist/package/src/config/database-backup.ts +0 -57
  39. package/dist/package/src/views/base/runtime-config/index.vue +0 -479
@@ -1,479 +0,0 @@
1
- <template>
2
- <div v-loading="pageLoading" class="table-box runtime-config-page">
3
- <main class="runtime-config-shell">
4
- <aside class="runtime-config-navigation" :aria-label="t('system.base.runtime_config.navigation')">
5
- <header class="runtime-config-navigation__header">
6
- <el-input
7
- v-model="keyword"
8
- clearable
9
- :placeholder="t('system.base.runtime_config.placeholder.search')"
10
- :prefix-icon="Search"
11
- />
12
- <span class="runtime-config-count">{{ filteredDefinitions.length }} / {{ runtimeConfigDefinitions.length }}</span>
13
- </header>
14
-
15
- <nav
16
- v-if="filteredDefinitions.length"
17
- class="runtime-config-list"
18
- :aria-label="t('system.base.runtime_config.navigation')"
19
- >
20
- <button
21
- v-for="definition in filteredDefinitions"
22
- :key="definition.key"
23
- type="button"
24
- class="runtime-config-item"
25
- :class="{ 'is-active': definition.key === selectedKey }"
26
- @click="selectConfig(definition.key)"
27
- >
28
- <el-icon class="runtime-config-item__icon"><component :is="definition.icon" /></el-icon>
29
- <span class="runtime-config-item__text">
30
- <strong>{{ t(definition.titleKey) }}</strong>
31
- <small>{{ definition.key }}</small>
32
- </span>
33
- </button>
34
- </nav>
35
- <el-empty v-else :description="t('system.base.runtime_config.message.no_match')" :image-size="72" />
36
- </aside>
37
-
38
- <section class="runtime-config-content">
39
- <div v-if="!selectedDefinition" class="runtime-config-empty">
40
- <el-empty :description="t('system.base.runtime_config.message.select')" />
41
- </div>
42
- <template v-else>
43
- <header class="runtime-config-header">
44
- <div class="runtime-config-header__identity">
45
- <span class="runtime-config-header__icon">
46
- <el-icon><component :is="selectedDefinition.icon" /></el-icon>
47
- </span>
48
- <div>
49
- <h1>{{ t(selectedDefinition.titleKey) }}</h1>
50
- <p>{{ t(selectedDefinition.descriptionKey) }}</p>
51
- </div>
52
- </div>
53
- <time v-if="updatedAt" :datetime="updatedAt" :title="updatedAt">
54
- {{ t("system.base.runtime_config.value.updated_at", { time: formatUpdatedAt(updatedAt) }) }}
55
- </time>
56
- </header>
57
-
58
- <div v-loading="configLoading" class="runtime-config-form-scroll">
59
- <el-skeleton v-if="configLoading && !loadedKeys.has(selectedDefinition.key)" :rows="8" animated />
60
- <ProForm
61
- v-else
62
- :key="selectedDefinition.key"
63
- ref="formRef"
64
- :model="selectedModel"
65
- :fields="localizedFields"
66
- label-width="220px"
67
- />
68
- </div>
69
-
70
- <footer v-if="canUpdateRuntimeConfig" class="runtime-config-footer">
71
- <el-button type="primary" :icon="Check" :loading="configSaving" @click="saveCurrent">
72
- {{ t("common.action.save") }}
73
- </el-button>
74
- </footer>
75
- </template>
76
- </section>
77
- </main>
78
- </div>
79
- </template>
80
-
81
- <script setup lang="ts">
82
- import { computed, onMounted, reactive, ref, watch } from "vue";
83
- import { Check, Search } from "@element-plus/icons-vue";
84
- import { ElMessage } from "element-plus";
85
- import { getCurrentLocale, t, useLocaleStore } from "@liujitcn/kratos-admin-core";
86
- import ProForm from "@liujitcn/kratos-admin-core/components/ProForm/index.vue";
87
- import type { ProFormField, ProFormInstance } from "@liujitcn/kratos-admin-core/components/ProForm/interface";
88
- import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
89
- import { defBaseConfigService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_config";
90
- import {
91
- runtimeConfigDefinitions,
92
- type RuntimeConfigDefinition,
93
- type RuntimeConfigField,
94
- type RuntimeConfigMessageArg,
95
- type RuntimeConfigModel
96
- } from "@liujitcn/kratos-admin-system/config";
97
-
98
- defineOptions({ name: "BaseRuntimeConfig", inheritAttrs: false });
99
-
100
- const { locale } = useLocaleStore();
101
- const { BUTTONS } = useAuthButtons();
102
- /** 判断当前用户是否拥有运行配置更新权限。 */
103
- const canUpdateRuntimeConfig = computed(() => !!BUTTONS.value["base:runtime-config:update"]);
104
- const pageLoading = ref(false);
105
- const configLoading = ref(false);
106
- const configSaving = ref(false);
107
- const keyword = ref("");
108
- const selectedKey = ref("");
109
- const updatedAt = ref("");
110
- const loadedKeys = reactive(new Set<string>());
111
- const models = reactive<Record<string, RuntimeConfigModel>>({});
112
- const formRef = ref<ProFormInstance>();
113
- let requestToken = 0;
114
-
115
- const sortedDefinitions = computed(() => {
116
- const currentLocale = locale.value;
117
- return [...runtimeConfigDefinitions].sort((left, right) => {
118
- const titleOrder = t(left.titleKey).localeCompare(t(right.titleKey), currentLocale || getCurrentLocale(), {
119
- sensitivity: "base"
120
- });
121
- return titleOrder || left.key.localeCompare(right.key);
122
- });
123
- });
124
-
125
- const filteredDefinitions = computed(() => {
126
- const search = keyword.value.trim().toLocaleLowerCase();
127
- if (!search) return sortedDefinitions.value;
128
- return sortedDefinitions.value.filter(definition => {
129
- return [t(definition.titleKey), t(definition.descriptionKey), definition.key].some(value =>
130
- value.toLocaleLowerCase().includes(search)
131
- );
132
- });
133
- });
134
-
135
- const selectedDefinition = computed<RuntimeConfigDefinition | undefined>(() => {
136
- return runtimeConfigDefinitions.find(definition => definition.key === selectedKey.value);
137
- });
138
-
139
- const selectedModel = computed(() => {
140
- return selectedKey.value ? (models[selectedKey.value] ?? {}) : {};
141
- });
142
-
143
- const localizedFields = computed<ProFormField[]>(() => {
144
- if (!selectedDefinition.value) return [];
145
- return selectedDefinition.value.fields.map(localizeField);
146
- });
147
-
148
- /** 加载指定配置并恢复默认模型。 */
149
- async function loadConfig(key: string) {
150
- const definition = runtimeConfigDefinitions.find(item => item.key === key);
151
- if (!definition) return;
152
- const currentToken = requestToken + 1;
153
- requestToken = currentToken;
154
- configLoading.value = true;
155
- if (!models[key]) models[key] = definition.createModel();
156
- try {
157
- const result = await defBaseConfigService.GetBaseConfigByKey({ key });
158
- if (requestToken !== currentToken || selectedKey.value !== key) return;
159
- Object.assign(models[key], JSON.parse(result.value_json) as RuntimeConfigModel);
160
- updatedAt.value = result.updated_at;
161
- loadedKeys.add(key);
162
- } catch {
163
- if (requestToken === currentToken) ElMessage.error(t("system.base.runtime_config.message.load_failed"));
164
- } finally {
165
- if (requestToken === currentToken) configLoading.value = false;
166
- }
167
- }
168
-
169
- /** 切换当前配置表单。 */
170
- function selectConfig(key: string) {
171
- if (selectedKey.value === key) return;
172
- selectedKey.value = key;
173
- updatedAt.value = "";
174
- formRef.value?.clearValidate();
175
- void loadConfig(key);
176
- }
177
-
178
- /** 保存当前配置并刷新服务端缓存。 */
179
- async function saveCurrent() {
180
- const definition = selectedDefinition.value;
181
- if (!canUpdateRuntimeConfig.value || !definition || !selectedKey.value) return;
182
- const valid = await formRef.value?.validate();
183
- if (valid !== true) return;
184
- configSaving.value = true;
185
- try {
186
- await defBaseConfigService.UpdateBaseConfigByKey({
187
- key: definition.key,
188
- value_json: JSON.stringify(selectedModel.value)
189
- });
190
- loadedKeys.add(definition.key);
191
- ElMessage.success(t("system.base.runtime_config.message.save_success"));
192
- } catch {
193
- ElMessage.error(t("system.base.runtime_config.message.save_failed"));
194
- } finally {
195
- configSaving.value = false;
196
- }
197
- }
198
-
199
- /** 将配置字段定义本地化为 ProForm 字段。 */
200
- function localizeField(field: RuntimeConfigField): ProFormField {
201
- const fieldProps = field.props;
202
- return {
203
- prop: field.prop,
204
- label: t(field.labelKey),
205
- component: field.component,
206
- props:
207
- typeof fieldProps === "function"
208
- ? model => {
209
- const props = fieldProps(model);
210
- return { ...props, disabled: !canUpdateRuntimeConfig.value || Boolean(props.disabled) };
211
- }
212
- : { ...(fieldProps ?? {}), disabled: !canUpdateRuntimeConfig.value || Boolean(fieldProps?.disabled) },
213
- itemProps: field.itemProps,
214
- options: field.options,
215
- visible: field.visible,
216
- labelTooltip: field.labelTooltipKey ? t(field.labelTooltipKey) : undefined,
217
- rules: field.rules?.map(rule => ({
218
- ...rule,
219
- message: t(rule.messageKey, localizeArgs(rule.messageArgs))
220
- }))
221
- };
222
- }
223
-
224
- /** 将配置校验规则中的消息键参数转换为当前语言文本。 */
225
- function localizeArgs(args?: Record<string, RuntimeConfigMessageArg>) {
226
- if (!args) return undefined;
227
- const result: Record<string, string | number> = {};
228
- for (const [key, value] of Object.entries(args)) {
229
- result[key] = typeof value === "object" ? t(value.key) : typeof value === "boolean" ? String(value) : value;
230
- }
231
- return result;
232
- }
233
-
234
- /** 格式化配置更新时间。 */
235
- function formatUpdatedAt(value: string) {
236
- const date = new Date(value);
237
- if (Number.isNaN(date.getTime())) return value;
238
- return new Intl.DateTimeFormat(getCurrentLocale(), {
239
- month: "2-digit",
240
- day: "2-digit",
241
- hour: "2-digit",
242
- minute: "2-digit",
243
- hourCycle: "h23"
244
- })
245
- .format(date)
246
- .replaceAll("/", "-");
247
- }
248
-
249
- watch(locale, () => {
250
- if (!selectedKey.value) return;
251
- void loadConfig(selectedKey.value);
252
- });
253
-
254
- onMounted(async () => {
255
- pageLoading.value = true;
256
- try {
257
- const firstDefinition = sortedDefinitions.value[0];
258
- if (firstDefinition) {
259
- selectedKey.value = firstDefinition.key;
260
- await loadConfig(firstDefinition.key);
261
- }
262
- } finally {
263
- pageLoading.value = false;
264
- }
265
- });
266
- </script>
267
-
268
- <style scoped lang="scss">
269
- .runtime-config-page {
270
- box-sizing: border-box;
271
- color: var(--admin-page-text-primary);
272
- background: var(--el-bg-color-page);
273
- }
274
-
275
- .runtime-config-shell {
276
- display: grid;
277
- grid-template-columns: 300px minmax(0, 1fr);
278
- min-width: 0;
279
- min-height: 560px;
280
- overflow: hidden;
281
- background: var(--admin-page-card-bg);
282
- border: 1px solid var(--admin-page-card-border);
283
- border-radius: var(--admin-page-radius);
284
- box-shadow: var(--admin-page-shadow);
285
- }
286
-
287
- .runtime-config-navigation,
288
- .runtime-config-content {
289
- display: flex;
290
- flex-direction: column;
291
- min-width: 0;
292
- min-height: 0;
293
- overflow: hidden;
294
- }
295
-
296
- .runtime-config-navigation {
297
- border-right: 1px solid var(--admin-page-divider);
298
- }
299
-
300
- .runtime-config-navigation__header {
301
- display: flex;
302
- gap: 12px;
303
- align-items: center;
304
- padding: 16px;
305
- border-bottom: 1px solid var(--admin-page-divider);
306
- }
307
-
308
- .runtime-config-count {
309
- flex: 0 0 auto;
310
- font-size: 12px;
311
- color: var(--admin-page-text-secondary);
312
- white-space: nowrap;
313
- }
314
-
315
- .runtime-config-list {
316
- flex: 1;
317
- min-height: 0;
318
- padding: 8px;
319
- overflow-y: auto;
320
- }
321
-
322
- .runtime-config-item {
323
- display: flex;
324
- gap: 12px;
325
- align-items: center;
326
- width: 100%;
327
- min-height: 64px;
328
- padding: 10px 12px;
329
- color: var(--admin-page-text-primary);
330
- text-align: left;
331
- cursor: pointer;
332
- background: transparent;
333
- border: 0;
334
- border-radius: 6px;
335
- transition:
336
- background-color 0.2s ease,
337
- color 0.2s ease;
338
- }
339
-
340
- .runtime-config-item:hover {
341
- background: var(--el-fill-color-light);
342
- }
343
-
344
- .runtime-config-item.is-active {
345
- color: var(--el-color-primary);
346
- background: var(--el-color-primary-light-9);
347
- }
348
-
349
- .runtime-config-item__icon {
350
- flex: 0 0 auto;
351
- font-size: 18px;
352
- }
353
-
354
- .runtime-config-item__text {
355
- display: flex;
356
- flex-direction: column;
357
- min-width: 0;
358
- gap: 4px;
359
- }
360
-
361
- .runtime-config-item__text strong,
362
- .runtime-config-item__text small {
363
- overflow: hidden;
364
- text-overflow: ellipsis;
365
- white-space: nowrap;
366
- }
367
-
368
- .runtime-config-item__text strong {
369
- font-size: 14px;
370
- font-weight: 600;
371
- }
372
-
373
- .runtime-config-item__text small {
374
- color: var(--admin-page-text-secondary);
375
- font-size: 12px;
376
- }
377
-
378
- .runtime-config-content {
379
- background: var(--el-bg-color-page);
380
- }
381
-
382
- .runtime-config-header {
383
- display: flex;
384
- gap: 24px;
385
- align-items: flex-start;
386
- justify-content: space-between;
387
- padding: 24px 28px;
388
- background: var(--admin-page-card-bg);
389
- border-bottom: 1px solid var(--admin-page-divider);
390
- }
391
-
392
- .runtime-config-header__identity {
393
- display: flex;
394
- gap: 14px;
395
- align-items: flex-start;
396
- min-width: 0;
397
- }
398
-
399
- .runtime-config-header__icon {
400
- display: inline-flex;
401
- flex: 0 0 auto;
402
- align-items: center;
403
- justify-content: center;
404
- width: 36px;
405
- height: 36px;
406
- color: var(--el-color-primary);
407
- background: var(--el-color-primary-light-9);
408
- border-radius: 8px;
409
- }
410
-
411
- .runtime-config-header h1 {
412
- margin: 0;
413
- color: var(--admin-page-text-primary);
414
- font-size: 20px;
415
- font-weight: 650;
416
- }
417
-
418
- .runtime-config-header p {
419
- max-width: 720px;
420
- margin: 8px 0 0;
421
- color: var(--admin-page-text-secondary);
422
- line-height: 1.6;
423
- }
424
-
425
- .runtime-config-header time {
426
- flex: 0 0 auto;
427
- color: var(--admin-page-text-secondary);
428
- font-size: 12px;
429
- white-space: nowrap;
430
- }
431
-
432
- .runtime-config-form-scroll {
433
- flex: 1;
434
- min-height: 0;
435
- padding: 28px;
436
- overflow-y: auto;
437
- background: var(--admin-page-card-bg);
438
- }
439
-
440
- .runtime-config-footer {
441
- display: flex;
442
- justify-content: flex-end;
443
- padding: 16px 28px;
444
- background: var(--admin-page-card-bg);
445
- border-top: 1px solid var(--admin-page-divider);
446
- }
447
-
448
- .runtime-config-empty {
449
- display: grid;
450
- flex: 1;
451
- place-items: center;
452
- background: var(--admin-page-card-bg);
453
- }
454
-
455
- @media (max-width: 800px) {
456
- .runtime-config-shell {
457
- grid-template-columns: 1fr;
458
- }
459
-
460
- .runtime-config-navigation {
461
- max-height: 280px;
462
- border-right: 0;
463
- border-bottom: 1px solid var(--admin-page-divider);
464
- }
465
-
466
- .runtime-config-header {
467
- flex-direction: column;
468
- padding: 20px;
469
- }
470
-
471
- .runtime-config-form-scroll {
472
- padding: 20px;
473
- }
474
-
475
- .runtime-config-footer {
476
- padding: 14px 20px;
477
- }
478
- }
479
- </style>