@liujitcn/kratos-admin-system 0.0.19 → 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.
Files changed (86) hide show
  1. package/README.md +51 -50
  2. package/dist/package/declarations/src/api/system/base_language.d.ts +51 -0
  3. package/dist/package/declarations/src/api/system/base_translation.d.ts +7 -0
  4. package/dist/package/declarations/src/api/system/code_gen_i18n.d.ts +8 -0
  5. package/dist/package/declarations/src/components/DynamicTranslationCell.vue.d.ts +39 -0
  6. package/dist/package/declarations/src/components/DynamicTranslationEditor.vue.d.ts +23 -0
  7. package/dist/package/declarations/src/components/dynamicTranslation.d.ts +44 -0
  8. package/dist/package/declarations/src/rpc/system/admin/v1/base_config.d.ts +5 -0
  9. package/dist/package/declarations/src/rpc/system/admin/v1/base_dict.d.ts +79 -0
  10. package/dist/package/declarations/src/rpc/system/admin/v1/base_language.d.ts +114 -0
  11. package/dist/package/declarations/src/rpc/system/admin/v1/base_menu.d.ts +40 -0
  12. package/dist/package/declarations/src/rpc/system/admin/v1/base_translation.d.ts +102 -0
  13. package/dist/package/declarations/src/rpc/system/admin/v1/code_gen.d.ts +2 -0
  14. package/dist/package/declarations/src/rpc/system/admin/v1/code_gen_column.d.ts +7 -0
  15. package/dist/package/declarations/src/rpc/system/admin/v1/code_gen_table.d.ts +13 -0
  16. package/dist/package/declarations/src/views/tool/code-gen/components/CodeGenLocaleEditor.vue.d.ts +23 -0
  17. package/dist/package/declarations/src/views/tool/code-gen/config.d.ts +7 -7
  18. package/dist/package/declarations/src/views/tool/language/index.vue.d.ts +3 -0
  19. package/dist/package/src/api/base/ai_message.ts +7 -12
  20. package/dist/package/src/api/base/sse.ts +3 -1
  21. package/dist/package/src/api/system/base_language.ts +137 -0
  22. package/dist/package/src/api/system/base_translation.ts +22 -0
  23. package/dist/package/src/api/system/code_gen_column.ts +17 -3
  24. package/dist/package/src/api/system/code_gen_i18n.ts +18 -0
  25. package/dist/package/src/api/system/code_gen_table.ts +12 -4
  26. package/dist/package/src/components/DynamicTranslationCell.vue +176 -0
  27. package/dist/package/src/components/DynamicTranslationEditor.vue +166 -0
  28. package/dist/package/src/components/dynamicTranslation.ts +86 -0
  29. package/dist/package/src/locales/README.md +28 -0
  30. package/dist/package/src/locales/en-US.json +929 -0
  31. package/dist/package/src/locales/es-ES.json +928 -0
  32. package/dist/package/src/locales/fr-FR.json +928 -0
  33. package/dist/package/src/locales/ja-JP.json +929 -0
  34. package/dist/package/src/locales/ko-KR.json +928 -0
  35. package/dist/package/src/locales/zh-CN.json +929 -0
  36. package/dist/package/src/locales/zh-TW.json +928 -0
  37. package/dist/package/src/module.ts +19 -1
  38. package/dist/package/src/rpc/system/admin/v1/base_config.ts +5 -0
  39. package/dist/package/src/rpc/system/admin/v1/base_dict.ts +81 -0
  40. package/dist/package/src/rpc/system/admin/v1/base_language.ts +135 -0
  41. package/dist/package/src/rpc/system/admin/v1/base_menu.ts +41 -0
  42. package/dist/package/src/rpc/system/admin/v1/base_translation.ts +117 -0
  43. package/dist/package/src/rpc/system/admin/v1/code_gen.ts +5 -1
  44. package/dist/package/src/rpc/system/admin/v1/code_gen_column.ts +8 -0
  45. package/dist/package/src/rpc/system/admin/v1/code_gen_table.ts +15 -0
  46. package/dist/package/src/views/ai/chat/components/AiMarkdown.vue +1 -6
  47. package/dist/package/src/views/ai/chat/components/ChatPanel.vue +70 -53
  48. package/dist/package/src/views/ai/chat/components/SessionPanel.vue +20 -9
  49. package/dist/package/src/views/ai/chat/components/XSender.vue +36 -23
  50. package/dist/package/src/views/ai/chat/index.vue +55 -39
  51. package/dist/package/src/views/ai/chat/message.ts +18 -17
  52. package/dist/package/src/views/base/api/index.vue +121 -83
  53. package/dist/package/src/views/base/area/index.vue +81 -31
  54. package/dist/package/src/views/base/config/index.vue +272 -78
  55. package/dist/package/src/views/base/dept/index.vue +110 -61
  56. package/dist/package/src/views/base/dict/index.vue +137 -58
  57. package/dist/package/src/views/base/dict/item.vue +147 -61
  58. package/dist/package/src/views/base/job/index.vue +134 -81
  59. package/dist/package/src/views/base/job/log.vue +21 -22
  60. package/dist/package/src/views/base/log/index.vue +68 -46
  61. package/dist/package/src/views/base/menu/index.vue +257 -156
  62. package/dist/package/src/views/base/migration/index.vue +49 -29
  63. package/dist/package/src/views/base/post/index.vue +128 -59
  64. package/dist/package/src/views/base/role/index.vue +145 -78
  65. package/dist/package/src/views/base/tenant/index.vue +132 -63
  66. package/dist/package/src/views/base/user/components/dept-tree.vue +2 -1
  67. package/dist/package/src/views/base/user/index.vue +192 -112
  68. package/dist/package/src/views/profile/components/base.vue +44 -27
  69. package/dist/package/src/views/profile/components/password.vue +43 -27
  70. package/dist/package/src/views/profile/components/security.vue +89 -53
  71. package/dist/package/src/views/profile/index.vue +10 -9
  72. package/dist/package/src/views/tool/api-doc/index.vue +6 -2
  73. package/dist/package/src/views/tool/code-gen/code-preview/index.vue +30 -14
  74. package/dist/package/src/views/tool/code-gen/columns/index.vue +382 -273
  75. package/dist/package/src/views/tool/code-gen/columns/option-copy.ts +1 -6
  76. package/dist/package/src/views/tool/code-gen/components/CodeGenLocaleEditor.vue +118 -0
  77. package/dist/package/src/views/tool/code-gen/components/CodeGenProgressDialog.vue +65 -36
  78. package/dist/package/src/views/tool/code-gen/components/CodePreviewPane.vue +8 -5
  79. package/dist/package/src/views/tool/code-gen/config.ts +114 -77
  80. package/dist/package/src/views/tool/code-gen/preview/data.ts +19 -17
  81. package/dist/package/src/views/tool/code-gen/preview/index.vue +96 -58
  82. package/dist/package/src/views/tool/code-gen/proto/index.vue +110 -62
  83. package/dist/package/src/views/tool/code-gen/table/index.vue +197 -117
  84. package/dist/package/src/views/tool/language/index.vue +256 -0
  85. package/dist/package/src/views/tool/project-doc/index.vue +17 -15
  86. 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">可直接提问,也可以上传附件一起分析。</div>
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">快捷操作</div>
11
- <div class="agent-shortcuts__title">从常用工作流开始</div>
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>换一批</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">正在加载快捷入口...</div>
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">服务器异常,建议稍后重试</div>
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="错误详情" :name="String(item.id)">
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="错误详情" :name="String(item.id)">
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">运行明细</div>
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>输入</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>输出</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>缓存读取</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>总计</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">耗时</div>
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>首 Token</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>总耗时</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">取消</el-button>
266
- <el-button size="small" type="primary" :loading="sending" @click="submitMessageEdit(item)">发送</el-button>
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">快捷操作</div>
279
- <div class="agent-shortcuts__title">继续调用常用流程</div>
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>换一批</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">正在加载快捷入口...</div>
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 "上午好,我是通用 AI 助手";
512
- if (hour < 18) return "下午好,我是通用 AI 助手";
513
- return "晚上好,我是通用 AI 助手";
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 `${total} 项`;
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 ? `${group} · ${toolCount} 个工具` : group;
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 = { key: "copy" as const, label: "复制", icon: CopyDocument };
671
- const deleteAction: MessageActionOption = { key: "delete" as const, label: "删除", icon: Delete };
672
- const editAction: MessageActionOption = { key: "edit" as const, label: "编辑", icon: EditPen };
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: "重新发送", icon: Refresh }, copyAction, deleteAction]
678
- : [{ key: "retry" as const, label: "重新发送", icon: Refresh }, ...actions];
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: "重新生成", icon: Refresh }];
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: "从此处创建分支会话", icon: BranchActionIcon });
686
- actions.push({ key: "speak" as const, label: item.speaking ? "停止朗读" : "朗读", icon: SpeakActionIcon });
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("zh-CN", {
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("zh-CN").format(Number(value ?? 0));
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">最近对话</div>
12
+ <div class="agent-panel-title">{{ t("system.ai.chat.title.recentConversations") }}</div>
13
13
  <div class="agent-panel-actions">
14
- <el-tooltip content="收起会话栏" placement="top">
15
- <button class="agent-panel-icon" type="button" aria-label="收起会话栏" @click="$emit('toggleCollapse')">
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 class="agent-panel-create" type="button" aria-label="新建会话" @click="handleCreateSession">
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>新建</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">重命名</el-dropdown-item>
57
- <el-dropdown-item command="delete" :icon="Delete" divided>删除</el-dropdown-item>
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 class="agent-icon-button" type="button" :disabled="sending || uploading" aria-label="上传附件">
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">上传附件</div>
39
- <div class="agent-popover-desc">可补充图片或文本类附件,发送后用于当前问题分析。</div>
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 :content="recording ? '停止语音输入' : '语音输入'" placement="top">
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="发送" placement="top">
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) return `已选 ${selectedAttachments.value.length} 个附件`;
161
- return "可上传附件";
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)) return "麦克风授权失败,请检查浏览器权限";
270
- return "语音识别失败,请稍后重试";
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(`已上传 ${response.files.length} 个附件`);
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(`最多上传 ${maxAttachmentCount} 个附件`);
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(`附件「${file.name}」暂不支持,已跳过`);
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(`附件「${file.name}」超过 ${maxAttachmentSizeMB}MB,已跳过`);
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(`最多还能选择 ${remainingCount} 个附件,已自动截取`);
359
+ ElMessage.warning(t("system.ai.chat.message.attachmentRemaining", { count: remainingCount }));
347
360
  }
348
361
  return validFiles.slice(0, remainingCount);
349
362
  }