@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.
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,197 +1,201 @@
1
1
  <template>
2
- <div class="i18n-editor">
3
- <div class="i18n-editor__toolbar">
4
- <el-button
5
- type="primary"
6
- size="small"
7
- :icon="Promotion"
8
- :loading="translating"
9
- :disabled="!canTranslate"
10
- @click="handleBatchTranslate"
11
- >
12
- {{ t("system.base.i18n.action.batch_translate") }}
13
- </el-button>
14
- </div>
15
- <div v-for="item in localValues" :key="item.locale" class="i18n-editor__row">
16
- <div class="i18n-editor__heading">
17
- <span>{{ getLanguageLabel(item.locale) }}</span>
18
- </div>
19
- <div class="i18n-editor__control" :class="{ 'i18n-editor__control--multiline': multiline }">
20
- <el-input
21
- :model-value="item.text"
22
- :maxlength="maxlength"
23
- :type="multiline ? 'textarea' : 'text'"
24
- :rows="multiline ? 6 : undefined"
25
- :disabled="translating || translatingLocale === item.locale"
26
- show-word-limit
27
- :placeholder="t('system.base.i18n.placeholder.text', { language: getLanguageLabel(item.locale) })"
28
- @update:model-value="value => updateText(item.locale, value)"
29
- />
2
+ <template v-if="enabled">
3
+ <slot name="trigger" :open="openDialog">
4
+ <el-tooltip :content="t('system.base.i18n.field.i18ns')" placement="top" :show-after="250">
30
5
  <el-button
31
- class="i18n-editor__translate"
32
- link
33
- type="primary"
34
- size="small"
35
- :icon="Promotion"
36
- :loading="translatingLocale === item.locale"
37
- :disabled="!props.source || Boolean(item.text) || translating || Boolean(translatingLocale)"
38
- @click="handleTranslate(item.locale)"
39
- >
40
- {{ t("system.base.i18n.action.translate") }}
41
- </el-button>
6
+ :icon="Languages"
7
+ :disabled="disabled"
8
+ :aria-label="t('system.base.i18n.field.i18ns')"
9
+ aria-haspopup="dialog"
10
+ :aria-expanded="visible"
11
+ @click="openDialog"
12
+ />
13
+ </el-tooltip>
14
+ </slot>
15
+ <ProDialog
16
+ v-model="visible"
17
+ :title="t('system.base.i18n.field.i18ns')"
18
+ width="min(800px, calc(100vw - 32px))"
19
+ append-to-body
20
+ destroy-on-close
21
+ :confirm-loading="translating"
22
+ :show-footer="!readonly"
23
+ @confirm="confirmChanges"
24
+ @close="closeDialog"
25
+ >
26
+ <div class="i18n-editor">
27
+ <div v-if="!readonly" class="i18n-editor__toolbar">
28
+ <el-button
29
+ type="primary"
30
+ :loading="translating && !translatingLocale"
31
+ :disabled="!source || translating"
32
+ @click="translate()"
33
+ >
34
+ {{ t("system.base.i18n.action.batch_translate") }}
35
+ </el-button>
36
+ </div>
37
+ <el-form label-position="right" label-width="100px" @submit.prevent>
38
+ <el-form-item
39
+ v-for="item in draftValues"
40
+ :key="item.locale"
41
+ :label="getLanguageLabel(item.locale)"
42
+ :for="`${inputId}-${item.locale}`"
43
+ >
44
+ <div class="i18n-editor__control">
45
+ <el-input
46
+ :id="`${inputId}-${item.locale}`"
47
+ :model-value="item.text || source"
48
+ :maxlength="maxlength"
49
+ :type="multiline ? 'textarea' : 'text'"
50
+ :rows="multiline ? 5 : undefined"
51
+ :disabled="translating || readonly"
52
+ show-word-limit
53
+ @update:model-value="value => (item.text = value)"
54
+ />
55
+ <el-button
56
+ v-if="!readonly"
57
+ link
58
+ type="primary"
59
+ :loading="translatingLocale === item.locale"
60
+ :disabled="!source || translating"
61
+ @click="translate(item.locale)"
62
+ >
63
+ {{ t("system.base.i18n.action.translate") }}
64
+ </el-button>
65
+ </div>
66
+ </el-form-item>
67
+ </el-form>
42
68
  </div>
43
- </div>
44
- </div>
69
+ </ProDialog>
70
+ </template>
45
71
  </template>
46
72
 
47
73
  <script setup lang="ts">
48
- import { computed, ref, watch } from "vue";
49
- import { ElMessage } from "element-plus";
50
- import { Promotion } from "@element-plus/icons-vue";
51
- import { t, useLocaleStore } from "@liujitcn/kratos-admin-core";
74
+ import { computed, ref, useId, watch } from "vue";
75
+ import { Languages } from "@lucide/vue";
76
+ import { t } from "@liujitcn/kratos-admin-core";
77
+ import ProDialog from "@liujitcn/kratos-admin-core/components/Dialog/ProDialog.vue";
52
78
  import { defBaseI18nService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/base_i18n";
53
- import { getLanguageLabel, type DynamicI18nValue } from "./dynamicI18n";
79
+ import { getEditableLanguageOptions, getLanguageLabel, type DynamicI18nValue } from "./dynamicI18n";
54
80
 
55
- /** DynamicI18nEditorProps 动态翻译编辑器属性。 */
81
+ /** 字段旁国际化入口及本地草稿编辑弹窗。 */
56
82
  interface DynamicI18nEditorProps {
57
83
  modelValue: DynamicI18nValue[];
58
- /** 当前语言源文本。 */
59
84
  source?: string;
60
85
  maxlength?: number;
61
86
  multiline?: boolean;
87
+ disabled?: boolean;
88
+ readonly?: boolean;
62
89
  }
63
-
64
90
  const props = withDefaults(defineProps<DynamicI18nEditorProps>(), {
65
91
  source: "",
66
92
  maxlength: 100,
67
- multiline: false
93
+ multiline: false,
94
+ disabled: false,
95
+ readonly: false
68
96
  });
69
-
70
- const { locale: currentLocale } = useLocaleStore();
71
97
  const emit = defineEmits<{ "update:modelValue": [value: DynamicI18nValue[]] }>();
72
- const localValues = ref<DynamicI18nValue[]>([]);
98
+ const enabled = computed(() => getEditableLanguageOptions().length > 0);
99
+ const inputId = useId();
100
+ const visible = ref(false);
101
+ const draftValues = ref<DynamicI18nValue[]>([]);
73
102
  const translating = ref(false);
74
103
  const translatingLocale = ref("");
75
- const canTranslate = computed(() => Boolean(props.source) && localValues.value.some(item => !item.text && item.locale !== currentLocale.value));
104
+ let requestVersion = 0;
76
105
 
77
- watch(
78
- () => props.modelValue,
79
- value => {
80
- localValues.value = value.map(item => ({ ...item }));
81
- },
82
- { immediate: true, deep: true }
83
- );
106
+ /** 打开编辑窗口,未填写译文使用原值展示,不提前写入主表单。 */
107
+ function openDialog() {
108
+ draftValues.value = getEditableLanguageOptions().map(({ value }) => ({
109
+ locale: value,
110
+ id: props.modelValue.find(item => item.locale === value)?.id ?? 0,
111
+ text: props.modelValue.find(item => item.locale === value)?.text ?? ""
112
+ }));
113
+ visible.value = true;
114
+ }
84
115
 
85
- /** updateText 更新指定语言的人工译文。 */
86
- function updateText(locale: DynamicI18nValue["locale"], text: string) {
87
- const values = localValues.value.map(item => (item.locale === locale ? { ...item, text } : item));
88
- localValues.value = values;
89
- emit("update:modelValue", values);
116
+ /** 将用户确认的译文交给主表单,实际保存由主表单负责。 */
117
+ function confirmChanges() {
118
+ if (translating.value) return;
119
+ const editable = new Set(draftValues.value.map(item => item.locale));
120
+ emit("update:modelValue", [
121
+ ...props.modelValue.filter(item => !editable.has(item.locale)),
122
+ ...draftValues.value.map(item => ({ ...item }))
123
+ ]);
124
+ visible.value = false;
90
125
  }
91
126
 
92
- /** handleBatchTranslate 批量翻译当前仍为空的语言输入框。 */
93
- async function handleBatchTranslate() {
94
- if (!canTranslate.value || translating.value) return;
127
+ /** 关闭时丢弃草稿并使尚未返回的翻译结果失效。 */
128
+ function closeDialog() {
129
+ requestVersion++;
130
+ translating.value = false;
131
+ translatingLocale.value = "";
132
+ draftValues.value = [];
133
+ }
134
+
135
+ /** 用当前原文翻译指定语言或全部目标语言,成功结果直接替换草稿。 */
136
+ async function translate(locale?: string) {
137
+ if (!props.source || translating.value) return;
138
+ const version = ++requestVersion;
139
+ const source = props.source;
95
140
  translating.value = true;
141
+ translatingLocale.value = locale ?? "";
96
142
  try {
97
- const pending = localValues.value.filter(item => !item.text && item.locale !== currentLocale.value);
98
- const response = await defBaseI18nService.DraftBaseI18n({ source: props.source });
99
- const i18ns = new Map(response.i18ns.map(item => [item.locale, item.i18n]));
100
- const failed = pending.filter(item => !i18ns.has(item.locale)).length;
101
- if (i18ns.size) {
102
- const values = localValues.value.map(item => {
103
- const i18n = i18ns.get(item.locale);
104
- return i18n === undefined ? item : { ...item, text: i18n };
105
- });
106
- localValues.value = values;
107
- emit("update:modelValue", values);
108
- }
109
- if (failed) {
110
- ElMessage.warning(
111
- t("system.base.i18n.message.batch_translate_partial", {
112
- success: i18ns.size,
113
- failed
114
- })
115
- );
116
- } else {
117
- ElMessage.success(t("system.base.i18n.message.batch_translate_success", { count: i18ns.size }));
143
+ const response = await defBaseI18nService.DraftBaseI18n({ source, locale });
144
+ if (version !== requestVersion || source !== props.source || !visible.value) return;
145
+ const targets = draftValues.value.filter(item => !locale || item.locale === locale);
146
+ const results = new Map(response.i18ns.filter(item => item.i18n).map(item => [item.locale, item.i18n]));
147
+ let success = 0;
148
+ for (const item of targets) {
149
+ const text = results.get(item.locale);
150
+ if (text === undefined) continue;
151
+ item.text = text;
152
+ success++;
118
153
  }
154
+ const failed = targets.length - success;
155
+ if (failed) ElMessage.warning(t("system.base.i18n.message.batch_translate_partial", { success, failed }));
156
+ else ElMessage.success(t("system.base.i18n.message.batch_translate_success", { count: success }));
119
157
  } catch {
120
- ElMessage.error(t("system.base.i18n.message.batch_translate_failed"));
158
+ if (version === requestVersion) ElMessage.error(t("system.base.i18n.message.batch_translate_failed"));
121
159
  } finally {
122
- translating.value = false;
160
+ if (version === requestVersion) {
161
+ translating.value = false;
162
+ translatingLocale.value = "";
163
+ }
123
164
  }
124
165
  }
125
166
 
126
- /** handleTranslate 翻译指定语言的单个输入框。 */
127
- async function handleTranslate(locale: string) {
128
- const item = localValues.value.find(value => value.locale === locale);
129
- if (!item || item.text || item.locale === currentLocale.value || !props.source || translating.value || translatingLocale.value) return;
130
- translatingLocale.value = locale;
131
- try {
132
- const response = await defBaseI18nService.DraftBaseI18n({ source: props.source, locale });
133
- const i18n = response.i18ns.find(item => item.locale === locale)?.i18n;
134
- if (!i18n) throw new Error("i18n not found");
135
- const values = localValues.value.map(value => (value.locale === locale ? { ...value, text: i18n } : value));
136
- localValues.value = values;
137
- emit("update:modelValue", values);
138
- ElMessage.success(t("system.base.i18n.message.translate_success", { language: getLanguageLabel(locale) }));
139
- } catch {
140
- ElMessage.error(t("system.base.i18n.message.translate_failed", { language: getLanguageLabel(locale) }));
141
- } finally {
167
+ watch(enabled, value => {
168
+ if (!value) visible.value = false;
169
+ });
170
+ watch(
171
+ () => props.source,
172
+ () => {
173
+ requestVersion++;
174
+ translating.value = false;
142
175
  translatingLocale.value = "";
143
176
  }
144
- }
145
-
177
+ );
146
178
  </script>
147
179
 
148
180
  <style scoped>
149
181
  .i18n-editor {
150
- display: flex;
151
- flex-direction: column;
152
- gap: 14px;
153
- width: 100%;
182
+ display: grid;
183
+ gap: 16px;
154
184
  }
155
185
  .i18n-editor__toolbar {
156
186
  display: flex;
157
187
  justify-content: flex-end;
158
188
  }
159
- .i18n-editor__row {
160
- display: flex;
161
- flex-direction: column;
162
- gap: 6px;
163
- }
164
- .i18n-editor__heading,
165
189
  .i18n-editor__control {
166
190
  display: flex;
167
- gap: 8px;
168
- align-items: center;
169
- }
170
- .i18n-editor__heading {
171
- color: var(--el-text-color-regular);
172
- }
173
- .i18n-editor__control {
174
- position: relative;
175
191
  width: 100%;
192
+ min-width: 0;
193
+ align-items: flex-start;
194
+ gap: 12px;
176
195
  }
177
- .i18n-editor__control :deep(.el-input) {
196
+ .i18n-editor__control :deep(.el-input),
197
+ .i18n-editor__control :deep(.el-textarea) {
178
198
  flex: 1;
179
- }
180
- .i18n-editor__control--multiline :deep(.el-textarea__inner) {
181
- padding-right: 78px;
182
- }
183
- .i18n-editor__control:not(.i18n-editor__control--multiline) :deep(.el-input__suffix) {
184
- padding-right: 78px;
185
- }
186
- .i18n-editor__translate {
187
- position: absolute;
188
- z-index: 1;
189
- top: 50%;
190
- right: 8px;
191
- transform: translateY(-50%);
192
- }
193
- .i18n-editor__control--multiline .i18n-editor__translate {
194
- top: 8px;
195
- transform: none;
199
+ min-width: 0;
196
200
  }
197
201
  </style>
@@ -11,9 +11,9 @@ export interface DynamicI18nValue {
11
11
 
12
12
  /** DynamicI18nRecord 描述三类动态资源翻译共有字段。 */
13
13
  export interface DynamicI18nRecord extends Omit<DynamicI18nValue, "text"> {
14
- target_type: I18nTargetType;
15
- target_id: number;
16
- name: string;
14
+ target_type: I18nTargetType;
15
+ target_id: number;
16
+ name: string;
17
17
  }
18
18
 
19
19
  /** DynamicLanguageOption 动态翻译编辑器显示的语言选项。 */
@@ -52,11 +52,15 @@ export function normalizeDynamicI18ns(
52
52
  });
53
53
  }
54
54
 
55
- /** serializeDynamicI18ns 将编辑状态转换回后端资源翻译结构。 */
55
+ /** serializeDynamicI18ns 按启用语言序列化译文,未填写时保存原值。 */
56
56
  export function serializeDynamicI18ns(
57
57
  values: DynamicI18nValue[],
58
58
  targetType: I18nTargetType,
59
- targetId: number
59
+ targetId: number,
60
+ source: string
60
61
  ): Array<{ id: number; target_type: I18nTargetType; target_id: number; locale: string; name: string }> {
61
- return values.map(({ id, locale, text }) => ({ id, target_type: targetType, target_id: targetId, locale, name: text }));
62
+ return getEditableLanguageOptions().map(({ value: locale }) => {
63
+ const item = values.find(value => value.locale === locale);
64
+ return { id: item?.id ?? 0, target_type: targetType, target_id: targetId, locale, name: item?.text || source };
65
+ });
62
66
  }
@@ -13,9 +13,7 @@ export type {
13
13
  } from "./types";
14
14
 
15
15
  /** Admin 内置运行配置定义集合。 */
16
- export const runtimeConfigDefinitions = shallowReactive<RuntimeConfigDefinition[]>([
17
- baseLogFallbackConfig
18
- ]);
16
+ export const runtimeConfigDefinitions = shallowReactive<RuntimeConfigDefinition[]>([baseLogFallbackConfig]);
19
17
 
20
18
  /** 注册外部业务模块提供的运行配置定义。 */
21
19
  export function registerRuntimeConfig(definition: RuntimeConfigDefinition) {