@liujitcn/kratos-admin-system 0.0.39 → 0.0.41
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.
- package/README.md +22 -0
- package/dist/package/declarations/src/api/system/admin/v1/base_config.d.ts +1 -5
- package/dist/package/declarations/src/components/DynamicI18nCell.vue.d.ts +2 -8
- package/dist/package/declarations/src/components/DynamicI18nEditor.vue.d.ts +20 -3
- package/dist/package/declarations/src/components/dynamicI18n.d.ts +2 -2
- package/dist/package/declarations/src/locales/generated.d.ts +5797 -5813
- package/dist/package/declarations/src/rpc/system/admin/v1/base_config.d.ts +4 -40
- package/dist/package/declarations/src/utils/code-gen-progress.d.ts +3 -0
- package/dist/package/declarations/src/views/tool/code-gen/components/CodeGenGenerateConfirm.vue.d.ts +11 -0
- package/dist/package/declarations/src/views/tool/code-gen/components/CodeGenLocaleEditor.vue.d.ts +5 -9
- package/dist/package/declarations/src/views/tool/code-gen/config.d.ts +3 -0
- package/dist/package/src/api/system/admin/v1/base_config.ts +0 -20
- package/dist/package/src/components/DynamicI18nCell.vue +55 -220
- package/dist/package/src/components/DynamicI18nEditor.vue +150 -146
- package/dist/package/src/components/dynamicI18n.ts +10 -6
- package/dist/package/src/config/index.ts +1 -3
- package/dist/package/src/locales/en-US.json +1430 -1434
- package/dist/package/src/locales/ja-JP.json +1430 -1434
- package/dist/package/src/locales/zh-CN.json +1429 -1433
- package/dist/package/src/locales/zh-TW.json +1429 -1433
- package/dist/package/src/module.ts +0 -1
- package/dist/package/src/rpc/system/admin/v1/base_config.ts +3 -43
- package/dist/package/src/utils/code-gen-progress.ts +15 -0
- package/dist/package/src/views/base/config/index.vue +183 -48
- package/dist/package/src/views/base/dict/index.vue +6 -9
- package/dist/package/src/views/base/dict/item.vue +6 -15
- package/dist/package/src/views/base/job/index.vue +16 -17
- package/dist/package/src/views/base/menu/index.vue +78 -15
- package/dist/package/src/views/tool/code-gen/columns/index.vue +17 -25
- package/dist/package/src/views/tool/code-gen/components/CodeGenGenerateConfirm.vue +115 -0
- package/dist/package/src/views/tool/code-gen/components/CodeGenLocaleEditor.vue +27 -186
- package/dist/package/src/views/tool/code-gen/components/CodeGenProgressDialog.vue +2 -0
- package/dist/package/src/views/tool/code-gen/config.ts +15 -1
- package/dist/package/src/views/tool/code-gen/table/index.vue +69 -40
- package/package.json +3 -2
- package/dist/package/declarations/src/config/database-backup.d.ts +0 -12
- package/dist/package/declarations/src/views/base/runtime-config/index.vue.d.ts +0 -3
- package/dist/package/src/config/database-backup.ts +0 -57
- package/dist/package/src/views/base/runtime-config/index.vue +0 -479
|
@@ -15,11 +15,7 @@
|
|
|
15
15
|
@close="handleCloseDialog"
|
|
16
16
|
>
|
|
17
17
|
<template #i18ns>
|
|
18
|
-
<DynamicI18nEditor
|
|
19
|
-
v-model="i18nValues"
|
|
20
|
-
:source="formData.name"
|
|
21
|
-
:maxlength="50"
|
|
22
|
-
/>
|
|
18
|
+
<DynamicI18nEditor v-model="i18nValues" :source="formData.name" :maxlength="50" />
|
|
23
19
|
</template>
|
|
24
20
|
</FormDialog>
|
|
25
21
|
</div>
|
|
@@ -215,7 +211,11 @@ function renderOperationCell(scope: RenderScope<BaseJob>) {
|
|
|
215
211
|
);
|
|
216
212
|
}
|
|
217
213
|
|
|
218
|
-
if (
|
|
214
|
+
if (
|
|
215
|
+
row.status === Status.STATUS_ENABLE &&
|
|
216
|
+
(row.entry_id === undefined || row.entry_id === 0) &&
|
|
217
|
+
BUTTONS.value["base:job:start"]
|
|
218
|
+
) {
|
|
219
219
|
actionNodes.push(
|
|
220
220
|
h(
|
|
221
221
|
ElButton,
|
|
@@ -249,7 +249,11 @@ function renderOperationCell(scope: RenderScope<BaseJob>) {
|
|
|
249
249
|
);
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
if (
|
|
252
|
+
if (
|
|
253
|
+
row.status === Status.STATUS_ENABLE &&
|
|
254
|
+
(row.entry_id === undefined || row.entry_id === 0) &&
|
|
255
|
+
BUTTONS.value["base:job:exec"]
|
|
256
|
+
) {
|
|
253
257
|
actionNodes.push(
|
|
254
258
|
h(
|
|
255
259
|
ElButton,
|
|
@@ -298,17 +302,11 @@ function renderOperationCell(scope: RenderScope<BaseJob>) {
|
|
|
298
302
|
const formFields = computed<ProFormField[]>(() => [
|
|
299
303
|
{
|
|
300
304
|
prop: "name",
|
|
305
|
+
suffixSlotName: "i18ns",
|
|
301
306
|
label: t("system.base.job.field.name"),
|
|
302
307
|
component: "input",
|
|
303
308
|
props: { placeholder: t("system.base.job.placeholder.name") }
|
|
304
309
|
},
|
|
305
|
-
{
|
|
306
|
-
prop: "i18ns",
|
|
307
|
-
label: t("system.base.i18n.field.i18ns"),
|
|
308
|
-
component: "slot",
|
|
309
|
-
slotName: "i18ns",
|
|
310
|
-
colSpan: 24
|
|
311
|
-
},
|
|
312
310
|
{
|
|
313
311
|
prop: "invoke_target",
|
|
314
312
|
label: t("system.base.job.field.invoke_target"),
|
|
@@ -427,7 +425,7 @@ async function handleOpenDialog(jobId?: number) {
|
|
|
427
425
|
|
|
428
426
|
const data = await defBaseJobService.GetBaseJob({ id: jobId });
|
|
429
427
|
Object.assign(formData, data);
|
|
430
|
-
|
|
428
|
+
i18nValues.value = normalizeDynamicI18ns(data.i18ns as DynamicI18nRecord[]);
|
|
431
429
|
}
|
|
432
430
|
|
|
433
431
|
/**
|
|
@@ -451,7 +449,7 @@ function resetForm() {
|
|
|
451
449
|
formData.cron_expression = "";
|
|
452
450
|
formData.status = Status.STATUS_ENABLE;
|
|
453
451
|
formData.i18ns = [];
|
|
454
|
-
|
|
452
|
+
i18nValues.value = normalizeDynamicI18ns(undefined);
|
|
455
453
|
}
|
|
456
454
|
|
|
457
455
|
/**
|
|
@@ -465,7 +463,8 @@ function handleSubmit() {
|
|
|
465
463
|
submitData.i18ns = serializeDynamicI18ns(
|
|
466
464
|
i18nValues.value,
|
|
467
465
|
I18nTargetType.I18N_TARGET_TYPE_BASE_JOB_NAME,
|
|
468
|
-
submitData.id
|
|
466
|
+
submitData.id,
|
|
467
|
+
submitData.name
|
|
469
468
|
);
|
|
470
469
|
const request = submitData.id
|
|
471
470
|
? defBaseJobService.UpdateBaseJob({ base_job: submitData })
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
ref="formDialogRef"
|
|
19
19
|
class="menu-form-dialog"
|
|
20
20
|
:title="t(dialog.editing ? 'system.base.menu.action.edit' : 'system.base.menu.action.create')"
|
|
21
|
-
width="
|
|
21
|
+
width="min(1440px, calc(100vw - 32px))"
|
|
22
|
+
top="4vh"
|
|
22
23
|
label-width="180px"
|
|
23
24
|
:model="formData"
|
|
24
25
|
:fields="formFields"
|
|
@@ -32,11 +33,7 @@
|
|
|
32
33
|
</template>
|
|
33
34
|
|
|
34
35
|
<template #i18ns>
|
|
35
|
-
<DynamicI18nEditor
|
|
36
|
-
v-model="i18nValues"
|
|
37
|
-
:source="formData.meta.title"
|
|
38
|
-
:maxlength="100"
|
|
39
|
-
/>
|
|
36
|
+
<DynamicI18nEditor v-model="i18nValues" :source="formData.meta.title" :maxlength="100" />
|
|
40
37
|
</template>
|
|
41
38
|
|
|
42
39
|
<template #apiTransferItem="slotScope">
|
|
@@ -452,6 +449,7 @@ const formFields = computed<ProFormField[]>(() => [
|
|
|
452
449
|
},
|
|
453
450
|
{
|
|
454
451
|
prop: "meta.title",
|
|
452
|
+
suffixSlotName: "i18ns",
|
|
455
453
|
label: t(
|
|
456
454
|
formData.type === BaseMenuType.BASE_MENU_TYPE_BUTTON ? "system.base.menu.field.button_name" : "system.base.menu.field.title"
|
|
457
455
|
),
|
|
@@ -467,13 +465,6 @@ const formFields = computed<ProFormField[]>(() => [
|
|
|
467
465
|
)
|
|
468
466
|
})
|
|
469
467
|
},
|
|
470
|
-
{
|
|
471
|
-
prop: "i18ns",
|
|
472
|
-
label: t("system.base.i18n.field.i18ns"),
|
|
473
|
-
component: "slot",
|
|
474
|
-
slotName: "i18ns",
|
|
475
|
-
colSpan: 24
|
|
476
|
-
},
|
|
477
468
|
{
|
|
478
469
|
prop: "path",
|
|
479
470
|
label: getPathFieldLabel(),
|
|
@@ -666,6 +657,7 @@ const formFields = computed<ProFormField[]>(() => [
|
|
|
666
657
|
slotName: "apiTransferItem",
|
|
667
658
|
options: transferData.value,
|
|
668
659
|
props: {
|
|
660
|
+
class: "menu-api-transfer",
|
|
669
661
|
filterable: true,
|
|
670
662
|
titles: [t("system.base.menu.value.available_api"), t("system.base.menu.value.selected_api")]
|
|
671
663
|
},
|
|
@@ -1002,7 +994,8 @@ function buildSubmitPayload(): BaseMenuForm {
|
|
|
1002
994
|
payload.i18ns = serializeDynamicI18ns(
|
|
1003
995
|
i18nValues.value,
|
|
1004
996
|
I18nTargetType.I18N_TARGET_TYPE_BASE_MENU_META_TITLE,
|
|
1005
|
-
payload.id
|
|
997
|
+
payload.id,
|
|
998
|
+
payload.meta?.title ?? ""
|
|
1006
999
|
);
|
|
1007
1000
|
// 一级菜单在表单中保持空白,提交时仍按接口约定传回根节点标识。
|
|
1008
1001
|
if (payload.id > 0 && payload.parent_id === undefined) payload.parent_id = 0;
|
|
@@ -1145,7 +1138,7 @@ function resetForm(data?: Partial<BaseMenuForm>) {
|
|
|
1145
1138
|
formDialogRef.value?.resetFields();
|
|
1146
1139
|
formDialogRef.value?.clearValidate();
|
|
1147
1140
|
Object.assign(formData, normalizeMenuForm(data));
|
|
1148
|
-
|
|
1141
|
+
i18nValues.value = normalizeDynamicI18ns(data?.i18ns as DynamicI18nRecord[] | undefined);
|
|
1149
1142
|
}
|
|
1150
1143
|
|
|
1151
1144
|
/** 提交菜单表单,并在成功后关闭弹窗、刷新表格。 */
|
|
@@ -1249,3 +1242,73 @@ function handleDeleteMenu(selected?: number | string | Array<number | string> |
|
|
|
1249
1242
|
align-items: flex-start;
|
|
1250
1243
|
}
|
|
1251
1244
|
</style>
|
|
1245
|
+
|
|
1246
|
+
<style lang="scss">
|
|
1247
|
+
.menu-form-dialog {
|
|
1248
|
+
.menu-api-transfer {
|
|
1249
|
+
display: grid;
|
|
1250
|
+
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
|
1251
|
+
align-items: center;
|
|
1252
|
+
gap: 16px;
|
|
1253
|
+
width: 100%;
|
|
1254
|
+
.el-transfer-panel {
|
|
1255
|
+
width: 100%;
|
|
1256
|
+
min-width: 0;
|
|
1257
|
+
}
|
|
1258
|
+
.el-transfer__buttons {
|
|
1259
|
+
display: flex;
|
|
1260
|
+
flex-direction: column;
|
|
1261
|
+
gap: 12px;
|
|
1262
|
+
padding: 0;
|
|
1263
|
+
.el-button {
|
|
1264
|
+
margin: 0;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
.el-transfer-panel__body {
|
|
1268
|
+
height: 360px;
|
|
1269
|
+
}
|
|
1270
|
+
.el-transfer-panel__list.is-filterable {
|
|
1271
|
+
height: 300px;
|
|
1272
|
+
}
|
|
1273
|
+
.el-transfer-panel__item.el-checkbox {
|
|
1274
|
+
align-items: flex-start;
|
|
1275
|
+
height: auto;
|
|
1276
|
+
min-height: 36px;
|
|
1277
|
+
padding-top: 8px;
|
|
1278
|
+
padding-bottom: 8px;
|
|
1279
|
+
.el-checkbox__input {
|
|
1280
|
+
top: 3px;
|
|
1281
|
+
}
|
|
1282
|
+
.el-checkbox__label {
|
|
1283
|
+
display: block;
|
|
1284
|
+
white-space: normal;
|
|
1285
|
+
overflow-wrap: anywhere;
|
|
1286
|
+
line-height: 20px;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
@media (max-width: 767px) {
|
|
1293
|
+
.menu-form-dialog {
|
|
1294
|
+
.el-col {
|
|
1295
|
+
flex: 0 0 100%;
|
|
1296
|
+
max-width: 100%;
|
|
1297
|
+
}
|
|
1298
|
+
.el-form-item {
|
|
1299
|
+
flex-direction: column;
|
|
1300
|
+
}
|
|
1301
|
+
.el-form-item__label {
|
|
1302
|
+
justify-content: flex-start;
|
|
1303
|
+
width: auto !important;
|
|
1304
|
+
}
|
|
1305
|
+
.menu-api-transfer {
|
|
1306
|
+
grid-template-columns: minmax(0, 1fr);
|
|
1307
|
+
.el-transfer__buttons {
|
|
1308
|
+
flex-direction: row;
|
|
1309
|
+
justify-content: center;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
</style>
|
|
@@ -103,21 +103,12 @@
|
|
|
103
103
|
maxlength="255"
|
|
104
104
|
:placeholder="t('system.code.gen.column.placeholder.comment')"
|
|
105
105
|
/>
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
:aria-label="t('system.code.gen.i18n.action.edit_column')"
|
|
113
|
-
/>
|
|
114
|
-
</template>
|
|
115
|
-
<CodeGenLocaleEditor
|
|
116
|
-
:model-value="row.i18n_config"
|
|
117
|
-
:source-comment="row.comment"
|
|
118
|
-
@update:model-value="value => (row.i18n_config = value)"
|
|
119
|
-
/>
|
|
120
|
-
</el-popover>
|
|
106
|
+
<CodeGenLocaleEditor
|
|
107
|
+
:model-value="row.i18n_config"
|
|
108
|
+
:source-comment="row.comment"
|
|
109
|
+
:disabled="!canEdit"
|
|
110
|
+
@update:model-value="value => (row.i18n_config = value)"
|
|
111
|
+
/>
|
|
121
112
|
</div>
|
|
122
113
|
</template>
|
|
123
114
|
</el-table-column>
|
|
@@ -536,7 +527,7 @@
|
|
|
536
527
|
<script setup lang="ts">
|
|
537
528
|
import Sortable from "sortablejs";
|
|
538
529
|
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from "vue";
|
|
539
|
-
import {
|
|
530
|
+
import { Delete, Document, List, Plus, Setting } from "@element-plus/icons-vue";
|
|
540
531
|
import { useRoute, useRouter } from "vue-router";
|
|
541
532
|
import { setAdminDocumentTitle, t } from "@liujitcn/kratos-admin-core";
|
|
542
533
|
import ProDialog from "@liujitcn/kratos-admin-core/components/Dialog/ProDialog.vue";
|
|
@@ -557,7 +548,6 @@ import type {
|
|
|
557
548
|
} from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/code_gen_column";
|
|
558
549
|
import type { CodeGenDatabaseTable, CodeGenTableForm } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/code_gen_table";
|
|
559
550
|
import CodeGenLocaleEditor from "../components/CodeGenLocaleEditor.vue";
|
|
560
|
-
import { getEditableLanguageOptions } from "@liujitcn/kratos-admin-system/components/dynamicI18n";
|
|
561
551
|
import {
|
|
562
552
|
copyCodeGenOptionToEmptyMatches,
|
|
563
553
|
copyFirstMatchingCodeGenOption,
|
|
@@ -576,7 +566,8 @@ import {
|
|
|
576
566
|
createDefaultCodeGenListConfig,
|
|
577
567
|
createDefaultCodeGenOptionConfig,
|
|
578
568
|
createDefaultCodeGenQueryConfig,
|
|
579
|
-
createDefaultCodeGenTableForm
|
|
569
|
+
createDefaultCodeGenTableForm,
|
|
570
|
+
withCodeGenLocaleDefaults
|
|
580
571
|
} from "../config";
|
|
581
572
|
|
|
582
573
|
defineOptions({
|
|
@@ -784,6 +775,7 @@ async function handleSaveColumns(showMessage = true) {
|
|
|
784
775
|
code_gen_columns: columns.value.map((item, index) => ({
|
|
785
776
|
...item,
|
|
786
777
|
table_id: formData.id,
|
|
778
|
+
i18n_config: withCodeGenLocaleDefaults(item.i18n_config, item.comment),
|
|
787
779
|
sort: index + 1
|
|
788
780
|
}))
|
|
789
781
|
});
|
|
@@ -999,7 +991,10 @@ async function loadDatabaseColumns(tableName: string) {
|
|
|
999
991
|
if (!tableName || databaseColumns[tableName] || loadingDatabaseColumns.has(tableName)) return;
|
|
1000
992
|
loadingDatabaseColumns.add(tableName);
|
|
1001
993
|
try {
|
|
1002
|
-
const data = await defCodeGenColumnService.ListCodeGenDatabaseColumn({
|
|
994
|
+
const data = await defCodeGenColumnService.ListCodeGenDatabaseColumn({
|
|
995
|
+
source_name: formData.source_name,
|
|
996
|
+
table_name: tableName
|
|
997
|
+
});
|
|
1003
998
|
databaseColumns[tableName] = data.columns ?? [];
|
|
1004
999
|
} finally {
|
|
1005
1000
|
loadingDatabaseColumns.delete(tableName);
|
|
@@ -1197,7 +1192,9 @@ function hasOptionConfig(option: CodeGenColumnOptionConfig) {
|
|
|
1197
1192
|
/** 返回选项入口的当前状态提示。 */
|
|
1198
1193
|
function optionEntryTip(option: CodeGenColumnOptionConfig) {
|
|
1199
1194
|
return t(
|
|
1200
|
-
hasOptionConfig(option)
|
|
1195
|
+
hasOptionConfig(option)
|
|
1196
|
+
? "system.code.gen.column.tooltip.option_configured"
|
|
1197
|
+
: "system.code.gen.column.tooltip.configure_option"
|
|
1201
1198
|
);
|
|
1202
1199
|
}
|
|
1203
1200
|
|
|
@@ -1206,11 +1203,6 @@ function codeGenScopeLabel(scope: CodeGenOptionScope) {
|
|
|
1206
1203
|
return t(`system.code.gen.column.scope.${scope}`);
|
|
1207
1204
|
}
|
|
1208
1205
|
|
|
1209
|
-
/** 判断字段是否缺少任一非主语言描述。 */
|
|
1210
|
-
function hasMissingColumnLocales(column: CodeGenColumnView) {
|
|
1211
|
-
return getEditableLanguageOptions().some(item => !column.i18n_config?.get(item.value)?.comment);
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
1206
|
/** 判断组件是否依赖选择数据源。 */
|
|
1215
1207
|
function hasOptionComponent(component: string, scope: CodeGenOptionScope) {
|
|
1216
1208
|
return (
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="generate-confirm">
|
|
3
|
+
<p class="generate-confirm__message">{{ message }}</p>
|
|
4
|
+
<template v-if="count">
|
|
5
|
+
<el-alert
|
|
6
|
+
:title="t('system.code.gen.table.dialog.missing_count', { count })"
|
|
7
|
+
:description="t('system.code.gen.table.dialog.missing_hint')"
|
|
8
|
+
type="warning"
|
|
9
|
+
show-icon
|
|
10
|
+
:closable="false"
|
|
11
|
+
/>
|
|
12
|
+
<div class="generate-confirm__groups" tabindex="0" :aria-label="t('system.code.gen.table.dialog.missing_count', { count })">
|
|
13
|
+
<details v-for="group in groups" :key="group.name" :open="groups.length === 1">
|
|
14
|
+
<summary>
|
|
15
|
+
<span class="generate-confirm__name">{{ group.name }}</span>
|
|
16
|
+
<span class="generate-confirm__count">{{ group.items.length }}</span>
|
|
17
|
+
</summary>
|
|
18
|
+
<ul>
|
|
19
|
+
<li v-for="(item, index) in group.items" :key="index">{{ item }}</li>
|
|
20
|
+
</ul>
|
|
21
|
+
</details>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
</div>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<script setup lang="ts">
|
|
28
|
+
import { computed } from "vue";
|
|
29
|
+
import { t } from "@liujitcn/kratos-admin-core";
|
|
30
|
+
|
|
31
|
+
/** 生成确认内容,按业务表保留缺失翻译明细。 */
|
|
32
|
+
const props = defineProps<{
|
|
33
|
+
message: string;
|
|
34
|
+
groups: Array<{ name: string; items: string[] }>;
|
|
35
|
+
}>();
|
|
36
|
+
|
|
37
|
+
const count = computed(() => props.groups.reduce((total, group) => total + group.items.length, 0));
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<style scoped lang="scss">
|
|
41
|
+
:global(.code-gen-generate-confirm) {
|
|
42
|
+
width: 600px;
|
|
43
|
+
max-width: calc(100vw - 32px);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
:global(.code-gen-generate-confirm .el-message-box__message) {
|
|
47
|
+
width: 100%;
|
|
48
|
+
min-width: 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.generate-confirm {
|
|
52
|
+
display: grid;
|
|
53
|
+
gap: 16px;
|
|
54
|
+
min-width: 0;
|
|
55
|
+
&__message {
|
|
56
|
+
margin: 0;
|
|
57
|
+
color: var(--el-text-color-primary);
|
|
58
|
+
font-size: 15px;
|
|
59
|
+
line-height: 1.6;
|
|
60
|
+
overflow-wrap: anywhere;
|
|
61
|
+
}
|
|
62
|
+
&__groups {
|
|
63
|
+
max-height: min(320px, 35vh);
|
|
64
|
+
overflow: auto;
|
|
65
|
+
border: 1px solid var(--el-border-color-lighter);
|
|
66
|
+
border-radius: 8px;
|
|
67
|
+
scrollbar-gutter: stable;
|
|
68
|
+
}
|
|
69
|
+
details + details {
|
|
70
|
+
border-top: 1px solid var(--el-border-color-lighter);
|
|
71
|
+
}
|
|
72
|
+
summary {
|
|
73
|
+
padding: 12px 16px;
|
|
74
|
+
color: var(--el-text-color-primary);
|
|
75
|
+
cursor: pointer;
|
|
76
|
+
background: var(--el-fill-color-light);
|
|
77
|
+
overflow-wrap: anywhere;
|
|
78
|
+
&:hover {
|
|
79
|
+
background: var(--el-fill-color);
|
|
80
|
+
}
|
|
81
|
+
&:focus-visible {
|
|
82
|
+
outline: 2px solid var(--el-color-primary);
|
|
83
|
+
outline-offset: -2px;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
&__name {
|
|
87
|
+
font-weight: 600;
|
|
88
|
+
}
|
|
89
|
+
&__count {
|
|
90
|
+
display: inline-block;
|
|
91
|
+
padding: 0 8px;
|
|
92
|
+
margin-left: 8px;
|
|
93
|
+
font-size: 12px;
|
|
94
|
+
line-height: 20px;
|
|
95
|
+
color: var(--el-color-warning);
|
|
96
|
+
background: var(--el-color-warning-light-9);
|
|
97
|
+
border-radius: 10px;
|
|
98
|
+
}
|
|
99
|
+
ul {
|
|
100
|
+
padding: 4px 16px;
|
|
101
|
+
margin: 0;
|
|
102
|
+
list-style: none;
|
|
103
|
+
}
|
|
104
|
+
li {
|
|
105
|
+
padding: 8px 0;
|
|
106
|
+
font-size: 13px;
|
|
107
|
+
line-height: 1.6;
|
|
108
|
+
color: var(--el-text-color-regular);
|
|
109
|
+
overflow-wrap: anywhere;
|
|
110
|
+
& + li {
|
|
111
|
+
border-top: 1px solid var(--el-border-color-extra-light);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
</style>
|
|
@@ -1,200 +1,41 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/>
|
|
10
|
-
<div v-for="locale in editableLocales" :key="locale.value" class="code-gen-locale-editor__group">
|
|
11
|
-
<div class="code-gen-locale-editor__locale">
|
|
12
|
-
<span>{{ locale.label }}</span>
|
|
13
|
-
<el-tag v-if="hasComment(locale.value)" size="small" type="success" effect="plain">
|
|
14
|
-
{{ t("system.code.gen.i18n.status.complete") }}
|
|
15
|
-
</el-tag>
|
|
16
|
-
<el-tag v-else size="small" type="warning" effect="plain">
|
|
17
|
-
{{ t("system.code.gen.i18n.status.missing") }}
|
|
18
|
-
</el-tag>
|
|
19
|
-
</div>
|
|
20
|
-
<div class="code-gen-locale-editor__control">
|
|
21
|
-
<el-input
|
|
22
|
-
:model-value="localeValue(locale.value).comment"
|
|
23
|
-
:placeholder="t('system.code.gen.i18n.placeholder.comment', { source: sourceComment || '-' })"
|
|
24
|
-
:disabled="isTranslating(locale.value, 'comment')"
|
|
25
|
-
maxlength="255"
|
|
26
|
-
@update:model-value="value => updateLocale(locale.value, 'comment', value)"
|
|
27
|
-
/>
|
|
28
|
-
<el-button
|
|
29
|
-
class="code-gen-locale-editor__translate"
|
|
30
|
-
link
|
|
31
|
-
type="primary"
|
|
32
|
-
size="small"
|
|
33
|
-
:icon="Promotion"
|
|
34
|
-
:loading="isTranslating(locale.value, 'comment')"
|
|
35
|
-
:disabled="!canTranslate(locale.value, 'comment', sourceComment)"
|
|
36
|
-
@click="handleTranslate(locale.value, 'comment', sourceComment, locale.label)"
|
|
37
|
-
>
|
|
38
|
-
{{ t("system.base.i18n.action.translate") }}
|
|
39
|
-
</el-button>
|
|
40
|
-
</div>
|
|
41
|
-
<div v-if="showLeftTreeComment" class="code-gen-locale-editor__control">
|
|
42
|
-
<el-input
|
|
43
|
-
:model-value="localeValue(locale.value).left_tree_comment"
|
|
44
|
-
:placeholder="t('system.code.gen.i18n.placeholder.left_tree_comment', { source: sourceLeftTreeComment || '-' })"
|
|
45
|
-
:disabled="isTranslating(locale.value, 'left_tree_comment')"
|
|
46
|
-
maxlength="255"
|
|
47
|
-
@update:model-value="value => updateLocale(locale.value, 'left_tree_comment', value)"
|
|
48
|
-
/>
|
|
49
|
-
<el-button
|
|
50
|
-
class="code-gen-locale-editor__translate"
|
|
51
|
-
link
|
|
52
|
-
type="primary"
|
|
53
|
-
size="small"
|
|
54
|
-
:icon="Promotion"
|
|
55
|
-
:loading="isTranslating(locale.value, 'left_tree_comment')"
|
|
56
|
-
:disabled="!canTranslate(locale.value, 'left_tree_comment', sourceLeftTreeComment)"
|
|
57
|
-
@click="handleTranslate(locale.value, 'left_tree_comment', sourceLeftTreeComment, locale.label)"
|
|
58
|
-
>
|
|
59
|
-
{{ t("system.base.i18n.action.translate") }}
|
|
60
|
-
</el-button>
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
2
|
+
<DynamicI18nEditor
|
|
3
|
+
:model-value="values"
|
|
4
|
+
:source="sourceComment"
|
|
5
|
+
:maxlength="255"
|
|
6
|
+
:disabled="disabled"
|
|
7
|
+
@update:model-value="updateValues"
|
|
8
|
+
/>
|
|
64
9
|
</template>
|
|
65
10
|
|
|
66
11
|
<script setup lang="ts">
|
|
67
|
-
import { computed
|
|
68
|
-
import
|
|
69
|
-
import {
|
|
70
|
-
import { defBaseI18nService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_i18n";
|
|
71
|
-
import { loadEnabledBaseLanguages, useEnabledBaseLanguages } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_language";
|
|
12
|
+
import { computed } from "vue";
|
|
13
|
+
import DynamicI18nEditor from "@liujitcn/kratos-admin-system/components/DynamicI18nEditor.vue";
|
|
14
|
+
import { getEditableLanguageOptions, type DynamicI18nValue } from "@liujitcn/kratos-admin-system/components/dynamicI18n";
|
|
72
15
|
import type { CodeGenLocaleConfig } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/base_i18n";
|
|
73
16
|
|
|
74
|
-
/**
|
|
17
|
+
/** 代码生成描述使用统一国际化入口,按字段保存回语言配置。 */
|
|
75
18
|
interface CodeGenLocaleEditorProps {
|
|
76
|
-
/** 按语言区域索引的生成配置。 */
|
|
77
19
|
modelValue: Map<string, CodeGenLocaleConfig>;
|
|
78
|
-
/** 中文业务或字段描述。 */
|
|
79
20
|
sourceComment?: string;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
const emit = defineEmits<{
|
|
93
|
-
"update:modelValue": [value: Map<string, CodeGenLocaleConfig>];
|
|
94
|
-
}>();
|
|
95
|
-
|
|
96
|
-
const { languages } = useEnabledBaseLanguages();
|
|
97
|
-
const translatingField = ref("");
|
|
98
|
-
void loadEnabledBaseLanguages();
|
|
99
|
-
|
|
100
|
-
const editableLocales = computed(() =>
|
|
101
|
-
languages.value
|
|
102
|
-
.filter(item => !item.is_primary)
|
|
103
|
-
.map(item => ({ value: item.language_code, label: item.native_name || item.language_name || item.language_code }))
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
const missingLocales = computed(() =>
|
|
107
|
-
editableLocales.value.filter(locale => !hasComment(locale.value)).map(locale => locale.label)
|
|
21
|
+
field?: keyof CodeGenLocaleConfig;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
}
|
|
24
|
+
const props = withDefaults(defineProps<CodeGenLocaleEditorProps>(), { sourceComment: "", field: "comment", disabled: false });
|
|
25
|
+
const emit = defineEmits<{ "update:modelValue": [value: Map<string, CodeGenLocaleConfig>] }>();
|
|
26
|
+
const values = computed<DynamicI18nValue[]>(() =>
|
|
27
|
+
getEditableLanguageOptions().map(({ value }) => ({
|
|
28
|
+
locale: value,
|
|
29
|
+
id: 0,
|
|
30
|
+
text: props.modelValue.get(value)?.[props.field] ?? ""
|
|
31
|
+
}))
|
|
108
32
|
);
|
|
109
33
|
|
|
110
|
-
/**
|
|
111
|
-
function
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
/** 判断指定语言是否已填写业务或字段描述。 */
|
|
116
|
-
function hasComment(locale: string) {
|
|
117
|
-
const value = localeValue(locale);
|
|
118
|
-
return Boolean(value.comment && (!props.showLeftTreeComment || value.left_tree_comment));
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/** 更新单个语言字段并保持 Map 引用语义。 */
|
|
122
|
-
function updateLocale(locale: string, field: keyof CodeGenLocaleConfig, value: string) {
|
|
123
|
-
const next = new Map(props.modelValue ?? []);
|
|
124
|
-
next.set(locale, { ...localeValue(locale), [field]: value });
|
|
34
|
+
/** 更新当前描述译文,保留其他字段和语言配置。 */
|
|
35
|
+
function updateValues(values: DynamicI18nValue[]) {
|
|
36
|
+
const next = new Map(props.modelValue);
|
|
37
|
+
for (const item of values)
|
|
38
|
+
next.set(item.locale, { comment: "", left_tree_comment: "", ...next.get(item.locale), [props.field]: item.text });
|
|
125
39
|
emit("update:modelValue", next);
|
|
126
40
|
}
|
|
127
|
-
|
|
128
|
-
/** 判断指定语言字段是否正在翻译。 */
|
|
129
|
-
function isTranslating(locale: string, field: keyof CodeGenLocaleConfig) {
|
|
130
|
-
return translatingField.value === `${locale}:${field}`;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/** 判断指定语言字段当前是否允许自动翻译。 */
|
|
134
|
-
function canTranslate(locale: string, field: keyof CodeGenLocaleConfig, source: string) {
|
|
135
|
-
return Boolean(source) && !localeValue(locale)[field] && !translatingField.value;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** 翻译指定语言的单个代码生成描述。 */
|
|
139
|
-
async function handleTranslate(locale: string, field: keyof CodeGenLocaleConfig, source: string, language: string) {
|
|
140
|
-
if (!canTranslate(locale, field, source)) return;
|
|
141
|
-
translatingField.value = `${locale}:${field}`;
|
|
142
|
-
try {
|
|
143
|
-
const response = await defBaseI18nService.DraftBaseI18n({ source, locale });
|
|
144
|
-
const i18n = response.i18ns.find(item => item.locale === locale)?.i18n;
|
|
145
|
-
if (!i18n) throw new Error("i18n not found");
|
|
146
|
-
updateLocale(locale, field, i18n);
|
|
147
|
-
ElMessage.success(t("system.base.i18n.message.translate_success", { language }));
|
|
148
|
-
} catch {
|
|
149
|
-
ElMessage.error(t("system.base.i18n.message.translate_failed", { language }));
|
|
150
|
-
} finally {
|
|
151
|
-
translatingField.value = "";
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
41
|
</script>
|
|
155
|
-
|
|
156
|
-
<style scoped lang="scss">
|
|
157
|
-
.code-gen-locale-editor {
|
|
158
|
-
display: flex;
|
|
159
|
-
flex-direction: column;
|
|
160
|
-
gap: 12px;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
.code-gen-locale-editor__group {
|
|
164
|
-
display: grid;
|
|
165
|
-
gap: 8px;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
.code-gen-locale-editor__locale {
|
|
169
|
-
display: flex;
|
|
170
|
-
align-items: center;
|
|
171
|
-
justify-content: space-between;
|
|
172
|
-
min-height: 24px;
|
|
173
|
-
font-size: 13px;
|
|
174
|
-
font-weight: 600;
|
|
175
|
-
color: var(--el-text-color-primary);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
.code-gen-locale-editor__control {
|
|
179
|
-
position: relative;
|
|
180
|
-
display: flex;
|
|
181
|
-
align-items: center;
|
|
182
|
-
width: 100%;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
.code-gen-locale-editor__control :deep(.el-input) {
|
|
186
|
-
flex: 1;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
.code-gen-locale-editor__control :deep(.el-input__suffix) {
|
|
190
|
-
padding-right: 78px;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
.code-gen-locale-editor__translate {
|
|
194
|
-
position: absolute;
|
|
195
|
-
z-index: 1;
|
|
196
|
-
top: 50%;
|
|
197
|
-
right: 8px;
|
|
198
|
-
transform: translateY(-50%);
|
|
199
|
-
}
|
|
200
|
-
</style>
|
|
@@ -111,6 +111,7 @@ import { getCurrentLocale, t } from "@liujitcn/kratos-admin-core";
|
|
|
111
111
|
import ProDialog from "@liujitcn/kratos-admin-core/components/Dialog/ProDialog.vue";
|
|
112
112
|
import { defCodeGenService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/code_gen";
|
|
113
113
|
import { subscribeCodeGenProgress, type SseStop } from "../../../../utils/code_gen_sse";
|
|
114
|
+
import { normalizeCodeGenTask } from "../../../../utils/code-gen-progress";
|
|
114
115
|
import type { CodeGenTask } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/code_gen";
|
|
115
116
|
import { CodeGenTaskStatus, CodeGenTaskStepStatus } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/code_gen";
|
|
116
117
|
|
|
@@ -174,6 +175,7 @@ async function loadTask(silent = false, taskId = props.taskId) {
|
|
|
174
175
|
|
|
175
176
|
/** 应用实时或轮询返回的任务快照。 */
|
|
176
177
|
function applyTask(latest: CodeGenTask) {
|
|
178
|
+
latest = normalizeCodeGenTask(latest);
|
|
177
179
|
task.value = latest;
|
|
178
180
|
const runningTable = latest.tables.find(table => table.status === CodeGenTaskStatus.CODE_GEN_TASK_STATUS_RUNNING);
|
|
179
181
|
if (runningTable && !expandedTables.value.includes(runningTable.table_id)) {
|