@liujitcn/kratos-admin-system 0.0.20 → 0.0.21
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 +51 -50
- package/dist/package/declarations/src/api/system/base_language.d.ts +51 -0
- package/dist/package/declarations/src/api/system/base_translation.d.ts +7 -0
- package/dist/package/declarations/src/api/system/code_gen_i18n.d.ts +8 -0
- package/dist/package/declarations/src/components/DynamicTranslationCell.vue.d.ts +39 -0
- package/dist/package/declarations/src/components/DynamicTranslationEditor.vue.d.ts +23 -0
- package/dist/package/declarations/src/components/dynamicTranslation.d.ts +44 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/base_config.d.ts +5 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/base_dict.d.ts +79 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/base_language.d.ts +114 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/base_menu.d.ts +40 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/base_translation.d.ts +102 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/code_gen.d.ts +2 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/code_gen_column.d.ts +7 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/code_gen_table.d.ts +13 -0
- package/dist/package/declarations/src/views/tool/code-gen/components/CodeGenLocaleEditor.vue.d.ts +23 -0
- package/dist/package/declarations/src/views/tool/code-gen/config.d.ts +7 -7
- package/dist/package/declarations/src/views/tool/language/index.vue.d.ts +3 -0
- package/dist/package/src/api/base/ai_message.ts +7 -12
- package/dist/package/src/api/base/sse.ts +3 -1
- package/dist/package/src/api/system/base_language.ts +137 -0
- package/dist/package/src/api/system/base_translation.ts +22 -0
- package/dist/package/src/api/system/code_gen_column.ts +17 -3
- package/dist/package/src/api/system/code_gen_i18n.ts +18 -0
- package/dist/package/src/api/system/code_gen_table.ts +12 -4
- package/dist/package/src/components/DynamicTranslationCell.vue +176 -0
- package/dist/package/src/components/DynamicTranslationEditor.vue +166 -0
- package/dist/package/src/components/dynamicTranslation.ts +86 -0
- package/dist/package/src/locales/README.md +28 -0
- package/dist/package/src/locales/en-US.json +929 -0
- package/dist/package/src/locales/es-ES.json +928 -0
- package/dist/package/src/locales/fr-FR.json +928 -0
- package/dist/package/src/locales/ja-JP.json +929 -0
- package/dist/package/src/locales/ko-KR.json +928 -0
- package/dist/package/src/locales/zh-CN.json +929 -0
- package/dist/package/src/locales/zh-TW.json +928 -0
- package/dist/package/src/module.ts +19 -1
- package/dist/package/src/rpc/system/admin/v1/base_config.ts +5 -0
- package/dist/package/src/rpc/system/admin/v1/base_dict.ts +81 -0
- package/dist/package/src/rpc/system/admin/v1/base_language.ts +135 -0
- package/dist/package/src/rpc/system/admin/v1/base_menu.ts +41 -0
- package/dist/package/src/rpc/system/admin/v1/base_translation.ts +117 -0
- package/dist/package/src/rpc/system/admin/v1/code_gen.ts +5 -1
- package/dist/package/src/rpc/system/admin/v1/code_gen_column.ts +8 -0
- package/dist/package/src/rpc/system/admin/v1/code_gen_table.ts +15 -0
- package/dist/package/src/views/ai/chat/components/AiMarkdown.vue +1 -6
- package/dist/package/src/views/ai/chat/components/ChatPanel.vue +70 -53
- package/dist/package/src/views/ai/chat/components/SessionPanel.vue +20 -9
- package/dist/package/src/views/ai/chat/components/XSender.vue +36 -23
- package/dist/package/src/views/ai/chat/index.vue +55 -39
- package/dist/package/src/views/ai/chat/message.ts +18 -17
- package/dist/package/src/views/base/api/index.vue +121 -83
- package/dist/package/src/views/base/area/index.vue +81 -31
- package/dist/package/src/views/base/config/index.vue +272 -78
- package/dist/package/src/views/base/dept/index.vue +110 -61
- package/dist/package/src/views/base/dict/index.vue +137 -58
- package/dist/package/src/views/base/dict/item.vue +147 -61
- package/dist/package/src/views/base/job/index.vue +134 -81
- package/dist/package/src/views/base/job/log.vue +21 -22
- package/dist/package/src/views/base/log/index.vue +68 -46
- package/dist/package/src/views/base/menu/index.vue +257 -156
- package/dist/package/src/views/base/migration/index.vue +49 -29
- package/dist/package/src/views/base/post/index.vue +128 -59
- package/dist/package/src/views/base/role/index.vue +145 -78
- package/dist/package/src/views/base/tenant/index.vue +132 -63
- package/dist/package/src/views/base/user/components/dept-tree.vue +2 -1
- package/dist/package/src/views/base/user/index.vue +192 -112
- package/dist/package/src/views/profile/components/base.vue +44 -27
- package/dist/package/src/views/profile/components/password.vue +43 -27
- package/dist/package/src/views/profile/components/security.vue +89 -53
- package/dist/package/src/views/profile/index.vue +10 -9
- package/dist/package/src/views/tool/api-doc/index.vue +6 -2
- package/dist/package/src/views/tool/code-gen/code-preview/index.vue +30 -14
- package/dist/package/src/views/tool/code-gen/columns/index.vue +382 -273
- package/dist/package/src/views/tool/code-gen/columns/option-copy.ts +1 -6
- package/dist/package/src/views/tool/code-gen/components/CodeGenLocaleEditor.vue +118 -0
- package/dist/package/src/views/tool/code-gen/components/CodeGenProgressDialog.vue +65 -36
- package/dist/package/src/views/tool/code-gen/components/CodePreviewPane.vue +8 -5
- package/dist/package/src/views/tool/code-gen/config.ts +114 -77
- package/dist/package/src/views/tool/code-gen/preview/data.ts +19 -17
- package/dist/package/src/views/tool/code-gen/preview/index.vue +96 -58
- package/dist/package/src/views/tool/code-gen/proto/index.vue +110 -62
- package/dist/package/src/views/tool/code-gen/table/index.vue +197 -117
- package/dist/package/src/views/tool/language/index.vue +256 -0
- package/dist/package/src/views/tool/project-doc/index.vue +17 -15
- package/package.json +6 -2
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
<template v-if="isEmptyState">
|
|
4
4
|
<div class="agent-chat-empty">
|
|
5
5
|
<div class="agent-chat-empty__title">{{ welcomeTitle }}</div>
|
|
6
|
-
<div class="agent-chat-empty__desc"
|
|
6
|
+
<div class="agent-chat-empty__desc">{{ t("system.ai.chat.welcomeDescription") }}</div>
|
|
7
7
|
<section v-if="hasShortcutPanel" class="agent-shortcuts">
|
|
8
8
|
<div class="agent-shortcuts__head">
|
|
9
9
|
<div>
|
|
10
|
-
<div class="agent-shortcuts__eyebrow"
|
|
11
|
-
<div class="agent-shortcuts__title"
|
|
10
|
+
<div class="agent-shortcuts__eyebrow">{{ t("system.ai.chat.shortcut.eyebrow") }}</div>
|
|
11
|
+
<div class="agent-shortcuts__title">{{ t("system.ai.chat.shortcut.startTitle") }}</div>
|
|
12
12
|
</div>
|
|
13
13
|
<div class="agent-shortcuts__actions">
|
|
14
14
|
<span class="agent-shortcuts__count">{{ shortcutBatchLabel }}</span>
|
|
@@ -19,12 +19,14 @@
|
|
|
19
19
|
:disabled="sending"
|
|
20
20
|
@click="refreshShortcutBatch"
|
|
21
21
|
>
|
|
22
|
-
<span
|
|
22
|
+
<span>{{ t("system.ai.chat.action.refreshShortcuts") }}</span>
|
|
23
23
|
<el-icon><Refresh /></el-icon>
|
|
24
24
|
</button>
|
|
25
25
|
</div>
|
|
26
26
|
</div>
|
|
27
|
-
<div v-if="loadingShortcuts" class="agent-shortcuts__loading"
|
|
27
|
+
<div v-if="loadingShortcuts" class="agent-shortcuts__loading">
|
|
28
|
+
{{ t("system.ai.chat.message.loadingShortcuts") }}
|
|
29
|
+
</div>
|
|
28
30
|
<div v-else class="agent-shortcuts__grid">
|
|
29
31
|
<button
|
|
30
32
|
v-for="shortcut in shortcutVisibleList"
|
|
@@ -104,7 +106,7 @@
|
|
|
104
106
|
<button
|
|
105
107
|
class="agent-message-tool-copy"
|
|
106
108
|
type="button"
|
|
107
|
-
aria-label="
|
|
109
|
+
:aria-label="t('system.ai.chat.action.copyRequest')"
|
|
108
110
|
@click.stop="handleCopyToolRequest(tool)"
|
|
109
111
|
>
|
|
110
112
|
<el-icon><CopyDocument /></el-icon>
|
|
@@ -132,10 +134,10 @@
|
|
|
132
134
|
}"
|
|
133
135
|
>
|
|
134
136
|
<div v-if="isAIFailedMessage(item)" class="agent-message-error">
|
|
135
|
-
<div class="agent-message-error__title"
|
|
137
|
+
<div class="agent-message-error__title">{{ t("system.ai.chat.message.serverError") }}</div>
|
|
136
138
|
<div class="agent-message-error__content">{{ resolveAIErrorMessage(item) }}</div>
|
|
137
139
|
<el-collapse v-if="item.fallback_reason" class="agent-message-error__detail" accordion>
|
|
138
|
-
<el-collapse-item title="
|
|
140
|
+
<el-collapse-item :title="t('system.ai.chat.title.errorDetail')" :name="String(item.id)">
|
|
139
141
|
<pre>{{ item.fallback_reason }}</pre>
|
|
140
142
|
</el-collapse-item>
|
|
141
143
|
</el-collapse>
|
|
@@ -144,7 +146,7 @@
|
|
|
144
146
|
<AiMarkdown :content="item.content" :streaming="item.progressState === 'streaming'" />
|
|
145
147
|
<slot name="flow-blocks" :message="item" :active-flow-message-id="activeFlowMessageID" />
|
|
146
148
|
<el-collapse v-if="item.fallback_reason" class="agent-message-error__detail" accordion>
|
|
147
|
-
<el-collapse-item title="
|
|
149
|
+
<el-collapse-item :title="t('system.ai.chat.title.errorDetail')" :name="String(item.id)">
|
|
148
150
|
<pre>{{ item.fallback_reason }}</pre>
|
|
149
151
|
</el-collapse-item>
|
|
150
152
|
</el-collapse>
|
|
@@ -168,8 +170,8 @@
|
|
|
168
170
|
<el-popconfirm
|
|
169
171
|
v-if="shouldConfirmMessageAction(action.key)"
|
|
170
172
|
:title="resolveActionConfirmTitle(action.key, item)"
|
|
171
|
-
confirm-button-text="
|
|
172
|
-
cancel-button-text="
|
|
173
|
+
:confirm-button-text="t('common.action.confirm')"
|
|
174
|
+
:cancel-button-text="t('common.action.cancel')"
|
|
173
175
|
width="220"
|
|
174
176
|
@confirm="handleMessageAction(action.key, item)"
|
|
175
177
|
>
|
|
@@ -207,7 +209,7 @@
|
|
|
207
209
|
<button
|
|
208
210
|
class="agent-message-action agent-message-runtime-trigger"
|
|
209
211
|
type="button"
|
|
210
|
-
aria-label="
|
|
212
|
+
:aria-label="t('system.ai.chat.action.viewRuntime')"
|
|
211
213
|
:disabled="item.progressState === 'streaming'"
|
|
212
214
|
>
|
|
213
215
|
<el-icon><DataAnalysis /></el-icon>
|
|
@@ -216,34 +218,34 @@
|
|
|
216
218
|
</button>
|
|
217
219
|
</template>
|
|
218
220
|
<div class="agent-runtime-detail">
|
|
219
|
-
<div class="agent-runtime-detail__title"
|
|
221
|
+
<div class="agent-runtime-detail__title">{{ t("system.ai.chat.title.runtime") }}</div>
|
|
220
222
|
<div v-if="resolveTokenTotal(item) > 0" class="agent-runtime-detail__section">
|
|
221
223
|
<div class="agent-runtime-detail__section-title">Token</div>
|
|
222
224
|
<div class="agent-runtime-detail__row">
|
|
223
|
-
<span
|
|
225
|
+
<span>{{ t("system.ai.chat.field.inputTokens") }}</span>
|
|
224
226
|
<strong>{{ formatNumber(item.token?.input) }}</strong>
|
|
225
227
|
</div>
|
|
226
228
|
<div class="agent-runtime-detail__row">
|
|
227
|
-
<span
|
|
229
|
+
<span>{{ t("system.ai.chat.field.outputTokens") }}</span>
|
|
228
230
|
<strong>{{ formatNumber(item.token?.output) }}</strong>
|
|
229
231
|
</div>
|
|
230
232
|
<div v-if="(item.token?.cache ?? 0) > 0" class="agent-runtime-detail__row">
|
|
231
|
-
<span
|
|
233
|
+
<span>{{ t("system.ai.chat.field.cacheTokens") }}</span>
|
|
232
234
|
<strong>{{ formatNumber(item.token?.cache) }}</strong>
|
|
233
235
|
</div>
|
|
234
236
|
<div class="agent-runtime-detail__row is-total">
|
|
235
|
-
<span
|
|
237
|
+
<span>{{ t("system.ai.chat.field.totalTokens") }}</span>
|
|
236
238
|
<strong>{{ formatNumber(item.token?.total) }}</strong>
|
|
237
239
|
</div>
|
|
238
240
|
</div>
|
|
239
241
|
<div v-if="item.first_token_ms > 0 || item.duration_ms > 0" class="agent-runtime-detail__section">
|
|
240
|
-
<div class="agent-runtime-detail__section-title"
|
|
242
|
+
<div class="agent-runtime-detail__section-title">{{ t("system.ai.chat.field.duration") }}</div>
|
|
241
243
|
<div v-if="item.first_token_ms > 0" class="agent-runtime-detail__row">
|
|
242
|
-
<span
|
|
244
|
+
<span>{{ t("system.ai.chat.field.firstToken") }}</span>
|
|
243
245
|
<strong>{{ formatDurationMs(item.first_token_ms) }}</strong>
|
|
244
246
|
</div>
|
|
245
247
|
<div v-if="item.duration_ms > 0" class="agent-runtime-detail__row is-total">
|
|
246
|
-
<span
|
|
248
|
+
<span>{{ t("system.ai.chat.field.totalDuration") }}</span>
|
|
247
249
|
<strong>{{ formatDurationMs(item.duration_ms) }}</strong>
|
|
248
250
|
</div>
|
|
249
251
|
</div>
|
|
@@ -262,8 +264,12 @@
|
|
|
262
264
|
@keydown.meta.enter.prevent="submitMessageEdit(item)"
|
|
263
265
|
/>
|
|
264
266
|
<div class="agent-message-edit__footer">
|
|
265
|
-
<el-button size="small" :disabled="sending" @click="cancelMessageEdit"
|
|
266
|
-
|
|
267
|
+
<el-button size="small" :disabled="sending" @click="cancelMessageEdit">
|
|
268
|
+
{{ t("common.action.cancel") }}
|
|
269
|
+
</el-button>
|
|
270
|
+
<el-button size="small" type="primary" :loading="sending" @click="submitMessageEdit(item)">
|
|
271
|
+
{{ t("system.ai.chat.action.send") }}
|
|
272
|
+
</el-button>
|
|
267
273
|
</div>
|
|
268
274
|
</div>
|
|
269
275
|
</div>
|
|
@@ -275,8 +281,8 @@
|
|
|
275
281
|
<section v-if="hasShortcutPanel" class="agent-shortcuts agent-shortcuts--compact">
|
|
276
282
|
<div class="agent-shortcuts__head">
|
|
277
283
|
<div>
|
|
278
|
-
<div class="agent-shortcuts__eyebrow"
|
|
279
|
-
<div class="agent-shortcuts__title"
|
|
284
|
+
<div class="agent-shortcuts__eyebrow">{{ t("system.ai.chat.shortcut.eyebrow") }}</div>
|
|
285
|
+
<div class="agent-shortcuts__title">{{ t("system.ai.chat.shortcut.continueTitle") }}</div>
|
|
280
286
|
</div>
|
|
281
287
|
<div class="agent-shortcuts__actions">
|
|
282
288
|
<span class="agent-shortcuts__count">{{ shortcutBatchLabel }}</span>
|
|
@@ -287,12 +293,14 @@
|
|
|
287
293
|
:disabled="sending"
|
|
288
294
|
@click="refreshShortcutBatch"
|
|
289
295
|
>
|
|
290
|
-
<span
|
|
296
|
+
<span>{{ t("system.ai.chat.action.refreshShortcuts") }}</span>
|
|
291
297
|
<el-icon><Refresh /></el-icon>
|
|
292
298
|
</button>
|
|
293
299
|
</div>
|
|
294
300
|
</div>
|
|
295
|
-
<div v-if="loadingShortcuts" class="agent-shortcuts__loading"
|
|
301
|
+
<div v-if="loadingShortcuts" class="agent-shortcuts__loading">
|
|
302
|
+
{{ t("system.ai.chat.message.loadingShortcuts") }}
|
|
303
|
+
</div>
|
|
296
304
|
<div v-else class="agent-shortcuts__grid">
|
|
297
305
|
<button
|
|
298
306
|
v-for="shortcut in shortcutVisibleList"
|
|
@@ -325,6 +333,7 @@ import { Attachments, BubbleList } from "vue-element-plus-x";
|
|
|
325
333
|
import type { FilesCardProps } from "vue-element-plus-x/types/FilesCard";
|
|
326
334
|
import { Check, CopyDocument, DataAnalysis, Delete, EditPen, Link, Memo, PieChart, Refresh, User } from "@element-plus/icons-vue";
|
|
327
335
|
import { ElMessage } from "element-plus";
|
|
336
|
+
import { getCurrentLocale, t } from "@liujitcn/kratos-admin-core";
|
|
328
337
|
import type { AiAction } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_message";
|
|
329
338
|
import { type AiAttachment, type AiSession } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_session";
|
|
330
339
|
import type { AiShortcut, AiToolCall } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_tool";
|
|
@@ -508,9 +517,9 @@ const activeFlowMessageID = computed(() => {
|
|
|
508
517
|
|
|
509
518
|
const welcomeTitle = computed(() => {
|
|
510
519
|
const hour = new Date().getHours();
|
|
511
|
-
if (hour < 12) return "
|
|
512
|
-
if (hour < 18) return "
|
|
513
|
-
return "
|
|
520
|
+
if (hour < 12) return t("system.ai.chat.welcome.morning");
|
|
521
|
+
if (hour < 18) return t("system.ai.chat.welcome.afternoon");
|
|
522
|
+
return t("system.ai.chat.welcome.evening");
|
|
514
523
|
});
|
|
515
524
|
|
|
516
525
|
const shortcutAllList = computed(() =>
|
|
@@ -533,8 +542,8 @@ const canRefreshShortcutBatch = computed(() => shortcutAllList.value.length > SH
|
|
|
533
542
|
|
|
534
543
|
const shortcutBatchLabel = computed(() => {
|
|
535
544
|
const total = shortcutAllList.value.length;
|
|
536
|
-
if (props.loadingShortcuts && total === 0) return "
|
|
537
|
-
if (total <= SHORTCUT_BATCH_SIZE) return
|
|
545
|
+
if (props.loadingShortcuts && total === 0) return t("system.ai.chat.status.loading");
|
|
546
|
+
if (total <= SHORTCUT_BATCH_SIZE) return t("system.ai.chat.value.itemCount", { count: total });
|
|
538
547
|
const pageIndex = shortcutBatchIndex.value % shortcutBatchCount.value;
|
|
539
548
|
const start = pageIndex * SHORTCUT_BATCH_SIZE + 1;
|
|
540
549
|
const end = Math.min(start + SHORTCUT_BATCH_SIZE - 1, total);
|
|
@@ -629,9 +638,9 @@ function resolveShortcutIcon(shortcut: AiShortcut) {
|
|
|
629
638
|
|
|
630
639
|
/** 展示快捷入口的分组和工具依赖摘要,帮助运营判断动作上下文。 */
|
|
631
640
|
function resolveShortcutMeta(shortcut: AiShortcut) {
|
|
632
|
-
const group = shortcut.group || "
|
|
641
|
+
const group = shortcut.group || t("system.ai.chat.value.generalAssistant");
|
|
633
642
|
const toolCount = shortcut.required_tools?.length ?? 0;
|
|
634
|
-
return toolCount > 0 ?
|
|
643
|
+
return toolCount > 0 ? t("system.ai.chat.value.shortcutTools", { group, count: toolCount }) : group;
|
|
635
644
|
}
|
|
636
645
|
|
|
637
646
|
/** 生成快捷入口对应的流程动作。 */
|
|
@@ -667,23 +676,31 @@ function parseActionPayload(payload?: string) {
|
|
|
667
676
|
function resolveMessageActions(item: ChatMessageItem) {
|
|
668
677
|
if (item.progressState === "streaming" || item.status === AiMessageStatus.GENERATING_AMS) return [];
|
|
669
678
|
|
|
670
|
-
const copyAction: MessageActionOption = {
|
|
671
|
-
|
|
672
|
-
|
|
679
|
+
const copyAction: MessageActionOption = {
|
|
680
|
+
key: "copy" as const,
|
|
681
|
+
label: t("system.ai.chat.action.copy"),
|
|
682
|
+
icon: CopyDocument
|
|
683
|
+
};
|
|
684
|
+
const deleteAction: MessageActionOption = { key: "delete" as const, label: t("common.action.delete"), icon: Delete };
|
|
685
|
+
const editAction: MessageActionOption = { key: "edit" as const, label: t("common.action.edit"), icon: EditPen };
|
|
673
686
|
if (item.role === "user") {
|
|
674
687
|
const actions = isLastEditableUserMessage(item) ? [editAction, copyAction, deleteAction] : [copyAction, deleteAction];
|
|
675
688
|
if (item.status === AiMessageStatus.FAILED_AMS) {
|
|
676
689
|
return item.localOnly
|
|
677
|
-
? [{ key: "retry" as const, label: "
|
|
678
|
-
: [{ key: "retry" as const, label: "
|
|
690
|
+
? [{ key: "retry" as const, label: t("system.ai.chat.action.resend"), icon: Refresh }, copyAction, deleteAction]
|
|
691
|
+
: [{ key: "retry" as const, label: t("system.ai.chat.action.resend"), icon: Refresh }, ...actions];
|
|
679
692
|
}
|
|
680
693
|
return item.localOnly ? [copyAction, deleteAction] : actions;
|
|
681
694
|
}
|
|
682
695
|
|
|
683
|
-
const actions: MessageActionOption[] = [{ key: "retry" as const, label: "
|
|
696
|
+
const actions: MessageActionOption[] = [{ key: "retry" as const, label: t("system.ai.chat.action.regenerate"), icon: Refresh }];
|
|
684
697
|
if (item.status === AiMessageStatus.SUCCESS_AMS) {
|
|
685
|
-
actions.push({ key: "branch" as const, label: "
|
|
686
|
-
actions.push({
|
|
698
|
+
actions.push({ key: "branch" as const, label: t("system.ai.chat.action.createBranch"), icon: BranchActionIcon });
|
|
699
|
+
actions.push({
|
|
700
|
+
key: "speak" as const,
|
|
701
|
+
label: item.speaking ? t("system.ai.chat.action.stopSpeaking") : t("system.ai.chat.action.speak"),
|
|
702
|
+
icon: SpeakActionIcon
|
|
703
|
+
});
|
|
687
704
|
}
|
|
688
705
|
return [...actions, copyAction, deleteAction];
|
|
689
706
|
}
|
|
@@ -696,7 +713,7 @@ function isAIFailedMessage(item: ChatMessageItem) {
|
|
|
696
713
|
/** 返回助手错误摘要,优先展示服务端可读错误。 */
|
|
697
714
|
function resolveAIErrorMessage(item: ChatMessageItem) {
|
|
698
715
|
const content = String(item.content ?? "").trim();
|
|
699
|
-
return content || "
|
|
716
|
+
return content || t("system.ai.chat.message.replyFailed");
|
|
700
717
|
}
|
|
701
718
|
|
|
702
719
|
/** 判断助手消息是否存在最终用量信息。 */
|
|
@@ -720,7 +737,7 @@ function formatDurationMs(value?: number) {
|
|
|
720
737
|
function resolveMessageReplyTime(item: ChatMessageItem) {
|
|
721
738
|
const timestamp = resolveMessageCreatedAt(item);
|
|
722
739
|
if (!timestamp) return "";
|
|
723
|
-
return new Intl.DateTimeFormat(
|
|
740
|
+
return new Intl.DateTimeFormat(getCurrentLocale(), {
|
|
724
741
|
year: "numeric",
|
|
725
742
|
month: "2-digit",
|
|
726
743
|
day: "2-digit",
|
|
@@ -749,7 +766,7 @@ function formatCompactNumber(value?: number) {
|
|
|
749
766
|
|
|
750
767
|
/** 格式化明细数字,保持 Token 明细易读。 */
|
|
751
768
|
function formatNumber(value?: number) {
|
|
752
|
-
return new Intl.NumberFormat(
|
|
769
|
+
return new Intl.NumberFormat(getCurrentLocale()).format(Number(value ?? 0));
|
|
753
770
|
}
|
|
754
771
|
|
|
755
772
|
/** 生成工具标签稳定键。 */
|
|
@@ -782,7 +799,7 @@ function resolveVisibleTools(item: ChatMessageItem) {
|
|
|
782
799
|
|
|
783
800
|
/** 生成工具展示名称。 */
|
|
784
801
|
function resolveToolTitle(tool: AiToolCall) {
|
|
785
|
-
return tool.title || tool.name || "
|
|
802
|
+
return tool.title || tool.name || t("system.ai.chat.value.tool");
|
|
786
803
|
}
|
|
787
804
|
|
|
788
805
|
/** 返回工具真实名称,用于排障时识别具体 Agent Tool。 */
|
|
@@ -797,7 +814,7 @@ function resolveToolStatusClass(status?: string) {
|
|
|
797
814
|
|
|
798
815
|
/** 生成工具状态展示文案。 */
|
|
799
816
|
function resolveToolStatusText(status?: string) {
|
|
800
|
-
return isToolFailed(status) ? "
|
|
817
|
+
return isToolFailed(status) ? t("system.ai.chat.status.failed") : t("system.ai.chat.status.completed");
|
|
801
818
|
}
|
|
802
819
|
|
|
803
820
|
/** 判断工具调用是否失败,兼容后端历史状态值。 */
|
|
@@ -876,9 +893,9 @@ function formatToolArgumentValue(value: unknown) {
|
|
|
876
893
|
async function handleCopyToolRequest(tool: AiToolCall) {
|
|
877
894
|
try {
|
|
878
895
|
await navigator.clipboard.writeText(formatToolRequest(tool));
|
|
879
|
-
ElMessage.success("
|
|
896
|
+
ElMessage.success(t("system.ai.chat.message.requestCopied"));
|
|
880
897
|
} catch {
|
|
881
|
-
ElMessage.error("
|
|
898
|
+
ElMessage.error(t("system.ai.chat.message.copyUnsupported"));
|
|
882
899
|
}
|
|
883
900
|
}
|
|
884
901
|
|
|
@@ -924,11 +941,11 @@ function submitMessageEdit(item: ChatMessageItem) {
|
|
|
924
941
|
if (props.sending) return;
|
|
925
942
|
const content = editingContent.value;
|
|
926
943
|
if (content === "") {
|
|
927
|
-
ElMessage.warning("
|
|
944
|
+
ElMessage.warning(t("system.ai.chat.message.contentRequired"));
|
|
928
945
|
return;
|
|
929
946
|
}
|
|
930
947
|
if (content === String(item.content ?? "")) {
|
|
931
|
-
ElMessage.info("
|
|
948
|
+
ElMessage.info(t("system.ai.chat.message.contentUnchanged"));
|
|
932
949
|
return;
|
|
933
950
|
}
|
|
934
951
|
emit("messageEdit", { item, content });
|
|
@@ -942,9 +959,9 @@ function shouldConfirmMessageAction(action: ChatMessageAction) {
|
|
|
942
959
|
|
|
943
960
|
/** 返回消息操作确认文案,避免误删或误覆盖当前气泡。 */
|
|
944
961
|
function resolveActionConfirmTitle(action: ChatMessageAction, item: ChatMessageItem) {
|
|
945
|
-
if (action === "delete") return "
|
|
946
|
-
if (item.role === "user") return "
|
|
947
|
-
return "
|
|
962
|
+
if (action === "delete") return t("system.ai.chat.dialog.confirmDeleteMessage");
|
|
963
|
+
if (item.role === "user") return t("system.ai.chat.dialog.confirmResendMessage");
|
|
964
|
+
return t("system.ai.chat.dialog.confirmRegenerateMessage");
|
|
948
965
|
}
|
|
949
966
|
|
|
950
967
|
/** 统一回复来源标签配色。 */
|
|
@@ -2,23 +2,33 @@
|
|
|
2
2
|
<aside class="agent-session-panel">
|
|
3
3
|
<el-input
|
|
4
4
|
:model-value="keyword"
|
|
5
|
-
placeholder="
|
|
5
|
+
:placeholder="t('system.ai.chat.placeholder.searchConversation')"
|
|
6
6
|
clearable
|
|
7
7
|
:prefix-icon="Search"
|
|
8
8
|
@update:model-value="handleKeywordUpdate"
|
|
9
9
|
/>
|
|
10
10
|
<div class="agent-divider"></div>
|
|
11
11
|
<div class="agent-panel-header">
|
|
12
|
-
<div class="agent-panel-title"
|
|
12
|
+
<div class="agent-panel-title">{{ t("system.ai.chat.title.recentConversations") }}</div>
|
|
13
13
|
<div class="agent-panel-actions">
|
|
14
|
-
<el-tooltip content="
|
|
15
|
-
<button
|
|
14
|
+
<el-tooltip :content="t('system.ai.chat.action.collapseSessions')" placement="top">
|
|
15
|
+
<button
|
|
16
|
+
class="agent-panel-icon"
|
|
17
|
+
type="button"
|
|
18
|
+
:aria-label="t('system.ai.chat.action.collapseSessions')"
|
|
19
|
+
@click="$emit('toggleCollapse')"
|
|
20
|
+
>
|
|
16
21
|
<el-icon><DArrowLeft /></el-icon>
|
|
17
22
|
</button>
|
|
18
23
|
</el-tooltip>
|
|
19
|
-
<button
|
|
24
|
+
<button
|
|
25
|
+
class="agent-panel-create"
|
|
26
|
+
type="button"
|
|
27
|
+
:aria-label="t('system.ai.chat.action.newConversation')"
|
|
28
|
+
@click="handleCreateSession"
|
|
29
|
+
>
|
|
20
30
|
<el-icon><Plus /></el-icon>
|
|
21
|
-
<span
|
|
31
|
+
<span>{{ t("common.action.create") }}</span>
|
|
22
32
|
</button>
|
|
23
33
|
</div>
|
|
24
34
|
</div>
|
|
@@ -48,13 +58,13 @@
|
|
|
48
58
|
@click.stop
|
|
49
59
|
@command="command => handleMenuCommand(command, item)"
|
|
50
60
|
>
|
|
51
|
-
<button class="agent-session-more" type="button" aria-label="
|
|
61
|
+
<button class="agent-session-more" type="button" :aria-label="t('system.ai.chat.action.conversationMenu')">
|
|
52
62
|
<el-icon><MoreFilled /></el-icon>
|
|
53
63
|
</button>
|
|
54
64
|
<template #dropdown>
|
|
55
65
|
<el-dropdown-menu>
|
|
56
|
-
<el-dropdown-item command="rename" :icon="EditPen"
|
|
57
|
-
<el-dropdown-item command="delete" :icon="Delete" divided
|
|
66
|
+
<el-dropdown-item command="rename" :icon="EditPen">{{ t("system.ai.chat.action.rename") }}</el-dropdown-item>
|
|
67
|
+
<el-dropdown-item command="delete" :icon="Delete" divided>{{ t("common.action.delete") }}</el-dropdown-item>
|
|
58
68
|
</el-dropdown-menu>
|
|
59
69
|
</template>
|
|
60
70
|
</el-dropdown>
|
|
@@ -65,6 +75,7 @@
|
|
|
65
75
|
|
|
66
76
|
<script setup lang="ts" name="SessionPanel">
|
|
67
77
|
import { DArrowLeft, Delete, EditPen, MoreFilled, Plus, Search } from "@element-plus/icons-vue";
|
|
78
|
+
import { t } from "@liujitcn/kratos-admin-core";
|
|
68
79
|
import type { AiSession } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_session";
|
|
69
80
|
|
|
70
81
|
/** 会话列表支持的菜单操作。 */
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
class="agent-sender"
|
|
6
6
|
variant="updown"
|
|
7
7
|
submit-type="enter"
|
|
8
|
-
placeholder="
|
|
8
|
+
:placeholder="t('system.ai.chat.placeholder.input')"
|
|
9
9
|
:loading="sending"
|
|
10
10
|
:clearable="true"
|
|
11
11
|
:tip-config="false"
|
|
@@ -27,7 +27,12 @@
|
|
|
27
27
|
<div class="agent-prefix-actions">
|
|
28
28
|
<el-popover placement="top-start" :width="236" trigger="click" popper-class="agent-sender-popover">
|
|
29
29
|
<template #reference>
|
|
30
|
-
<button
|
|
30
|
+
<button
|
|
31
|
+
class="agent-icon-button"
|
|
32
|
+
type="button"
|
|
33
|
+
:disabled="sending || uploading"
|
|
34
|
+
:aria-label="t('system.ai.chat.action.uploadAttachment')"
|
|
35
|
+
>
|
|
31
36
|
<el-icon :class="{ 'is-loading': uploading }">
|
|
32
37
|
<Loading v-if="uploading" />
|
|
33
38
|
<Paperclip v-else />
|
|
@@ -35,10 +40,10 @@
|
|
|
35
40
|
</button>
|
|
36
41
|
</template>
|
|
37
42
|
<div class="agent-popover-card">
|
|
38
|
-
<div class="agent-popover-title"
|
|
39
|
-
<div class="agent-popover-desc"
|
|
43
|
+
<div class="agent-popover-title">{{ t("system.ai.chat.action.uploadAttachment") }}</div>
|
|
44
|
+
<div class="agent-popover-desc">{{ t("system.ai.chat.message.attachmentHint") }}</div>
|
|
40
45
|
<button class="agent-popover-action" type="button" :disabled="sending || uploading" @click="handleSelectAttachment">
|
|
41
|
-
{{ uploading ? "
|
|
46
|
+
{{ uploading ? t("system.ai.chat.status.uploading") : t("system.ai.chat.action.selectLocalFile") }}
|
|
42
47
|
</button>
|
|
43
48
|
</div>
|
|
44
49
|
</el-popover>
|
|
@@ -49,14 +54,17 @@
|
|
|
49
54
|
|
|
50
55
|
<template #action-list>
|
|
51
56
|
<div class="agent-sender-actions">
|
|
52
|
-
<el-tooltip
|
|
57
|
+
<el-tooltip
|
|
58
|
+
:content="recording ? t('system.ai.chat.action.stopVoiceInput') : t('system.ai.chat.action.voiceInput')"
|
|
59
|
+
placement="top"
|
|
60
|
+
>
|
|
53
61
|
<button
|
|
54
62
|
class="agent-icon-button"
|
|
55
63
|
:class="{ 'is-active': recording }"
|
|
56
64
|
type="button"
|
|
57
65
|
:disabled="sending"
|
|
58
66
|
:aria-pressed="recording"
|
|
59
|
-
:aria-label="recording ? '
|
|
67
|
+
:aria-label="recording ? t('system.ai.chat.action.stopVoiceInput') : t('system.ai.chat.action.voiceInput')"
|
|
60
68
|
@click="handleToggleRecord"
|
|
61
69
|
>
|
|
62
70
|
<el-icon :class="{ 'is-loading': recording }">
|
|
@@ -65,12 +73,12 @@
|
|
|
65
73
|
</el-icon>
|
|
66
74
|
</button>
|
|
67
75
|
</el-tooltip>
|
|
68
|
-
<el-tooltip content="
|
|
76
|
+
<el-tooltip :content="t('system.ai.chat.action.send')" placement="top">
|
|
69
77
|
<button
|
|
70
78
|
class="agent-send-button"
|
|
71
79
|
type="button"
|
|
72
80
|
:disabled="sending || uploading || recording || isSubmitDisabled"
|
|
73
|
-
aria-label="
|
|
81
|
+
:aria-label="t('system.ai.chat.action.send')"
|
|
74
82
|
@click="handleSubmit()"
|
|
75
83
|
>
|
|
76
84
|
<el-icon v-if="!sending"><Promotion /></el-icon>
|
|
@@ -98,6 +106,7 @@ import { Attachments, useRecord, XSender as BaseXSender } from "vue-element-plus
|
|
|
98
106
|
import type { FilesCardProps } from "vue-element-plus-x/types/FilesCard";
|
|
99
107
|
import { Loading, Microphone, Paperclip, Promotion } from "@element-plus/icons-vue";
|
|
100
108
|
import { ElMessage } from "element-plus";
|
|
109
|
+
import { t } from "@liujitcn/kratos-admin-core";
|
|
101
110
|
import { defFileService } from "@liujitcn/kratos-admin-core/api/base/file";
|
|
102
111
|
import type { AiAttachment } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_session";
|
|
103
112
|
import type { SubmitPayload } from "../types";
|
|
@@ -155,10 +164,12 @@ const {
|
|
|
155
164
|
});
|
|
156
165
|
|
|
157
166
|
const actionHintText = computed(() => {
|
|
158
|
-
if (recording.value) return "
|
|
159
|
-
if (uploading.value) return "
|
|
160
|
-
if (selectedAttachments.value.length)
|
|
161
|
-
|
|
167
|
+
if (recording.value) return t("system.ai.chat.status.recognizingVoice");
|
|
168
|
+
if (uploading.value) return t("system.ai.chat.status.uploadingAttachments");
|
|
169
|
+
if (selectedAttachments.value.length) {
|
|
170
|
+
return t("system.ai.chat.value.selectedAttachments", { count: selectedAttachments.value.length });
|
|
171
|
+
}
|
|
172
|
+
return t("system.ai.chat.value.attachmentsAvailable");
|
|
162
173
|
});
|
|
163
174
|
|
|
164
175
|
const attachmentItems = computed<FilesCardProps[]>(() =>
|
|
@@ -185,7 +196,7 @@ function handleSubmit() {
|
|
|
185
196
|
if (!trimmedText && selectedAttachments.value.length === 0) return;
|
|
186
197
|
|
|
187
198
|
emit("submit", {
|
|
188
|
-
text: trimmedText || "
|
|
199
|
+
text: trimmedText || t("system.ai.chat.value.analyzeAttachments"),
|
|
189
200
|
attachments: [...selectedAttachments.value]
|
|
190
201
|
});
|
|
191
202
|
inputText.value = "";
|
|
@@ -264,10 +275,12 @@ function matchCumulativeRecordCandidate(text: string, candidate: string) {
|
|
|
264
275
|
|
|
265
276
|
/** 根据浏览器语音识别错误类型生成用户可理解的提示。 */
|
|
266
277
|
function resolveRecordErrorMessage(error: RecordError) {
|
|
267
|
-
if (error.code === -1) return "
|
|
278
|
+
if (error.code === -1) return t("system.ai.chat.message.voiceUnsupported");
|
|
268
279
|
const errorName = error.error ?? "";
|
|
269
|
-
if (["not-allowed", "service-not-allowed", "permission-denied"].includes(errorName))
|
|
270
|
-
|
|
280
|
+
if (["not-allowed", "service-not-allowed", "permission-denied"].includes(errorName)) {
|
|
281
|
+
return t("system.ai.chat.message.microphoneDenied");
|
|
282
|
+
}
|
|
283
|
+
return t("system.ai.chat.message.voiceFailed");
|
|
271
284
|
}
|
|
272
285
|
|
|
273
286
|
/** 同步输入器内部文本,保证发送按钮禁用态能实时响应。 */
|
|
@@ -314,10 +327,10 @@ async function uploadAttachments(files: File[]) {
|
|
|
314
327
|
});
|
|
315
328
|
selectedAttachments.value = Array.from(attachmentMap.values());
|
|
316
329
|
if (response?.files?.length) {
|
|
317
|
-
ElMessage.success(
|
|
330
|
+
ElMessage.success(t("system.ai.chat.message.attachmentsUploaded", { count: response.files.length }));
|
|
318
331
|
}
|
|
319
332
|
} catch {
|
|
320
|
-
ElMessage.error("
|
|
333
|
+
ElMessage.error(t("system.ai.chat.message.attachmentUploadFailed"));
|
|
321
334
|
} finally {
|
|
322
335
|
uploading.value = false;
|
|
323
336
|
resetFileInput();
|
|
@@ -328,22 +341,22 @@ async function uploadAttachments(files: File[]) {
|
|
|
328
341
|
function filterUploadFiles(files: File[]) {
|
|
329
342
|
const remainingCount = maxAttachmentCount - selectedAttachments.value.length;
|
|
330
343
|
if (remainingCount <= 0) {
|
|
331
|
-
ElMessage.warning(
|
|
344
|
+
ElMessage.warning(t("system.ai.chat.message.attachmentLimit", { count: maxAttachmentCount }));
|
|
332
345
|
return [];
|
|
333
346
|
}
|
|
334
347
|
const validFiles = files.filter(file => {
|
|
335
348
|
if (!isAcceptedAttachmentFile(file)) {
|
|
336
|
-
ElMessage.warning(
|
|
349
|
+
ElMessage.warning(t("system.ai.chat.message.attachmentUnsupported", { name: file.name }));
|
|
337
350
|
return false;
|
|
338
351
|
}
|
|
339
352
|
if (file.size > maxAttachmentSize) {
|
|
340
|
-
ElMessage.warning(
|
|
353
|
+
ElMessage.warning(t("system.ai.chat.message.attachmentTooLarge", { name: file.name, size: maxAttachmentSizeMB }));
|
|
341
354
|
return false;
|
|
342
355
|
}
|
|
343
356
|
return true;
|
|
344
357
|
});
|
|
345
358
|
if (validFiles.length > remainingCount) {
|
|
346
|
-
ElMessage.warning(
|
|
359
|
+
ElMessage.warning(t("system.ai.chat.message.attachmentRemaining", { count: remainingCount }));
|
|
347
360
|
}
|
|
348
361
|
return validFiles.slice(0, remainingCount);
|
|
349
362
|
}
|