@liujitcn/kratos-admin-system 0.0.65 → 0.0.67
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 +5 -3
- package/dist/package/declarations/src/api/base/v1/ai_knowledge.d.ts +7 -0
- package/dist/package/declarations/src/api/system/admin/v1/ai_provider.d.ts +3 -1
- package/dist/package/declarations/src/api/system/admin/v1/ai_query.d.ts +10 -0
- package/dist/package/declarations/src/api/system/admin/v1/ai_session.d.ts +9 -0
- package/dist/package/declarations/src/locales/generated.d.ts +1805 -1553
- package/dist/package/declarations/src/rpc/base/v1/ai_knowledge.d.ts +22 -0
- package/dist/package/declarations/src/rpc/base/v1/ai_message.d.ts +2 -0
- package/dist/package/declarations/src/rpc/base/v1/login.d.ts +2 -39
- package/dist/package/declarations/src/rpc/base/v1/oauth.d.ts +2 -2
- package/dist/package/declarations/src/rpc/common/v1/types.d.ts +4 -4
- package/dist/package/declarations/src/rpc/system/admin/v1/ai_provider.d.ts +66 -7
- package/dist/package/declarations/src/rpc/system/admin/v1/ai_query.d.ts +73 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/ai_session.d.ts +114 -0
- package/dist/package/declarations/src/rpc/system/admin/v1/auth.d.ts +3 -3
- package/dist/package/declarations/src/rpc/system/admin/v1/base_config.d.ts +4 -3
- package/dist/package/declarations/src/rpc/system/admin/v1/base_login_policy.d.ts +2 -2
- package/dist/package/declarations/src/rpc/system/admin/v1/base_message_provider.d.ts +3 -2
- package/dist/package/declarations/src/rpc/system/admin/v1/base_oauth_provider.d.ts +3 -2
- package/dist/package/declarations/src/rpc/system/admin/v1/base_user.d.ts +3 -3
- package/dist/package/declarations/src/views/ai/chat/components/ChatPanel.vue.d.ts +9 -2
- package/dist/package/declarations/src/views/ai/chat/components/XSender.vue.d.ts +7 -0
- package/dist/package/declarations/src/views/ai/query/index.vue.d.ts +3 -0
- package/dist/package/declarations/src/views/ai/session/index.vue.d.ts +3 -0
- package/dist/package/src/api/base/v1/ai_knowledge.ts +22 -0
- package/dist/package/src/api/system/admin/v1/ai_provider.ts +7 -0
- package/dist/package/src/api/system/admin/v1/ai_query.ts +25 -0
- package/dist/package/src/api/system/admin/v1/ai_session.ts +25 -0
- package/dist/package/src/components/account/ChangePasswordForm.vue +7 -5
- package/dist/package/src/components/tenant-project/TenantProjectSelect.vue +21 -4
- package/dist/package/src/locales/README.md +1 -1
- package/dist/package/src/locales/en-US.json +355 -292
- package/dist/package/src/locales/ja-JP.json +352 -289
- package/dist/package/src/locales/zh-CN.json +352 -289
- package/dist/package/src/locales/zh-TW.json +352 -289
- package/dist/package/src/module.ts +8 -0
- package/dist/package/src/rpc/base/v1/ai_knowledge.ts +33 -0
- package/dist/package/src/rpc/base/v1/ai_message.ts +2 -0
- package/dist/package/src/rpc/base/v1/login.ts +2 -42
- package/dist/package/src/rpc/base/v1/oauth.ts +2 -2
- package/dist/package/src/rpc/common/v1/types.ts +4 -4
- package/dist/package/src/rpc/system/admin/v1/ai_provider.ts +73 -7
- package/dist/package/src/rpc/system/admin/v1/ai_query.ts +89 -0
- package/dist/package/src/rpc/system/admin/v1/ai_session.ts +131 -0
- package/dist/package/src/rpc/system/admin/v1/auth.ts +3 -3
- package/dist/package/src/rpc/system/admin/v1/base_config.ts +6 -3
- package/dist/package/src/rpc/system/admin/v1/base_login_policy.ts +2 -2
- package/dist/package/src/rpc/system/admin/v1/base_message_provider.ts +5 -2
- package/dist/package/src/rpc/system/admin/v1/base_oauth_provider.ts +5 -2
- package/dist/package/src/rpc/system/admin/v1/base_user.ts +3 -3
- package/dist/package/src/views/ai/chat/components/ChatPanel.vue +18 -0
- package/dist/package/src/views/ai/chat/components/XSender.vue +102 -0
- package/dist/package/src/views/ai/chat/index.vue +22 -2
- package/dist/package/src/views/ai/query/index.vue +222 -0
- package/dist/package/src/views/ai/session/index.vue +422 -0
- package/dist/package/src/views/base/ai-provider/index.vue +216 -169
- package/dist/package/src/views/base/config/index.vue +9 -9
- package/dist/package/src/views/base/login-policy/index.vue +2 -2
- package/dist/package/src/views/base/message-provider/index.vue +7 -5
- package/dist/package/src/views/base/oauth-provider/index.vue +10 -7
- package/dist/package/src/views/base/tenant-project-grant/index.vue +9 -4
- package/dist/package/src/views/base/user/index.vue +3 -3
- package/dist/package/src/views/profile/components/security.vue +4 -5
- package/dist/package/src/views/tool/code-gen/config.ts +2 -0
- package/package.json +2 -2
|
@@ -53,8 +53,11 @@
|
|
|
53
53
|
:model-providers="modelProviders"
|
|
54
54
|
:provider-id="providerId"
|
|
55
55
|
:model-name="modelName"
|
|
56
|
+
:knowledge-options="knowledgeOptions"
|
|
57
|
+
:knowledge-ids="knowledgeIds"
|
|
56
58
|
@submit="handleSubmit"
|
|
57
59
|
@model-change="handleModelChange"
|
|
60
|
+
@knowledge-change="handleKnowledgeChange"
|
|
58
61
|
/>
|
|
59
62
|
</div>
|
|
60
63
|
</div>
|
|
@@ -336,8 +339,11 @@
|
|
|
336
339
|
:model-providers="modelProviders"
|
|
337
340
|
:provider-id="providerId"
|
|
338
341
|
:model-name="modelName"
|
|
342
|
+
:knowledge-options="knowledgeOptions"
|
|
343
|
+
:knowledge-ids="knowledgeIds"
|
|
339
344
|
@submit="handleSubmit"
|
|
340
345
|
@model-change="handleModelChange"
|
|
346
|
+
@knowledge-change="handleKnowledgeChange"
|
|
341
347
|
/>
|
|
342
348
|
</div>
|
|
343
349
|
</template>
|
|
@@ -356,6 +362,7 @@ import { formatDateTime } from "@liujitcn/kratos-admin-core/format";
|
|
|
356
362
|
import type { AiAction } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_message";
|
|
357
363
|
import { type AiAttachment, type AiSession } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_session";
|
|
358
364
|
import type { AiProviderModelOption } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_provider";
|
|
365
|
+
import type { AiKnowledgeOption } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_knowledge";
|
|
359
366
|
import type { AiShortcut, AiToolCall } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_tool";
|
|
360
367
|
import { AiMessageStatus } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_session";
|
|
361
368
|
import { resolvePersistedMessage } from "../../../../utils/persisted-message";
|
|
@@ -501,6 +508,10 @@ const props = defineProps<{
|
|
|
501
508
|
providerId: number;
|
|
502
509
|
/** 当前选择的模型名称。 */
|
|
503
510
|
modelName: string;
|
|
511
|
+
/** 当前可供选择的知识库。 */
|
|
512
|
+
knowledgeOptions: AiKnowledgeOption[];
|
|
513
|
+
/** 当前选择的知识库编号列表。 */
|
|
514
|
+
knowledgeIds: number[];
|
|
504
515
|
}>();
|
|
505
516
|
|
|
506
517
|
const emit = defineEmits<{
|
|
@@ -508,6 +519,8 @@ const emit = defineEmits<{
|
|
|
508
519
|
submit: [payload: SubmitPayload];
|
|
509
520
|
/** 更新当前选择的供应商和模型。 */
|
|
510
521
|
modelChange: [providerId: number, modelName: string];
|
|
522
|
+
/** 更新当前选择的知识库编号列表。 */
|
|
523
|
+
knowledgeChange: [knowledgeIds: number[]];
|
|
511
524
|
/** 触发消息级操作。 */
|
|
512
525
|
messageAction: [payload: { action: ChatMessageAction; item: ChatMessageItem }];
|
|
513
526
|
/** 提交当前用户消息的文本编辑。 */
|
|
@@ -648,6 +661,11 @@ function handleModelChange(providerId: number, modelName: string) {
|
|
|
648
661
|
emit("modelChange", providerId, modelName);
|
|
649
662
|
}
|
|
650
663
|
|
|
664
|
+
/** 更新当前选择的知识库编号列表。 */
|
|
665
|
+
function handleKnowledgeChange(knowledgeIds: number[]) {
|
|
666
|
+
emit("knowledgeChange", knowledgeIds);
|
|
667
|
+
}
|
|
668
|
+
|
|
651
669
|
/** 点击快捷入口时提交对应入口动作。 */
|
|
652
670
|
function handleShortcutClick(shortcut: AiShortcut) {
|
|
653
671
|
emit("submit", {
|
|
@@ -126,6 +126,73 @@
|
|
|
126
126
|
{{ t("system.ai.model.empty") }}
|
|
127
127
|
</div>
|
|
128
128
|
</el-popover>
|
|
129
|
+
<el-popover
|
|
130
|
+
ref="knowledgePopoverRef"
|
|
131
|
+
placement="top-start"
|
|
132
|
+
:width="324"
|
|
133
|
+
trigger="click"
|
|
134
|
+
:show-arrow="false"
|
|
135
|
+
:persistent="false"
|
|
136
|
+
popper-class="agent-model-popper"
|
|
137
|
+
>
|
|
138
|
+
<template #reference>
|
|
139
|
+
<button
|
|
140
|
+
class="agent-model-selector"
|
|
141
|
+
:class="{ 'is-disabled': sending || !knowledgeOptions.length, 'is-selected': selectedKnowledgeCount > 0 }"
|
|
142
|
+
type="button"
|
|
143
|
+
:disabled="sending || !knowledgeOptions.length"
|
|
144
|
+
:aria-label="t('system.ai.knowledge.title')"
|
|
145
|
+
>
|
|
146
|
+
<el-icon class="agent-model-selector__spark"><Collection /></el-icon>
|
|
147
|
+
<span class="agent-model-selector__label">{{
|
|
148
|
+
selectedKnowledgeCount > 0
|
|
149
|
+
? t("system.ai.knowledge.selected_count", { count: selectedKnowledgeCount })
|
|
150
|
+
: t("system.ai.knowledge.title")
|
|
151
|
+
}}</span>
|
|
152
|
+
<el-icon class="agent-model-selector__arrow"><ArrowDown /></el-icon>
|
|
153
|
+
</button>
|
|
154
|
+
</template>
|
|
155
|
+
<div v-if="knowledgeOptions.length" class="agent-model-panel">
|
|
156
|
+
<div class="agent-model-panel__head">
|
|
157
|
+
<span class="agent-model-panel__title">{{ t("system.ai.knowledge.title") }}</span>
|
|
158
|
+
<span class="agent-model-panel__meta">{{
|
|
159
|
+
t("system.ai.knowledge.select_hint")
|
|
160
|
+
}}</span>
|
|
161
|
+
</div>
|
|
162
|
+
<div class="agent-model-panel__list">
|
|
163
|
+
<button
|
|
164
|
+
v-for="option in knowledgeOptions"
|
|
165
|
+
:key="option.id"
|
|
166
|
+
class="agent-model-row"
|
|
167
|
+
:class="{ 'is-selected': isKnowledgeSelected(option.id) }"
|
|
168
|
+
type="button"
|
|
169
|
+
@click="handleKnowledgeToggle(option.id)"
|
|
170
|
+
>
|
|
171
|
+
<span class="agent-model-row__name">{{ option.name }}</span>
|
|
172
|
+
<!-- proto3 JSON 会省略零值字段,计数为 0 时前端补默认值。 -->
|
|
173
|
+
<span class="agent-model-row__raw">{{
|
|
174
|
+
t("system.ai.knowledge.doc_count", { count: option.doc_count ?? 0 })
|
|
175
|
+
}}</span>
|
|
176
|
+
<el-icon v-if="isKnowledgeSelected(option.id)" class="agent-model-row__check">
|
|
177
|
+
<Check />
|
|
178
|
+
</el-icon>
|
|
179
|
+
</button>
|
|
180
|
+
</div>
|
|
181
|
+
<button
|
|
182
|
+
v-if="aiKnowledgeRoute"
|
|
183
|
+
class="agent-model-panel__manage"
|
|
184
|
+
type="button"
|
|
185
|
+
@click="openKnowledgeManage"
|
|
186
|
+
>
|
|
187
|
+
<el-icon><Setting /></el-icon>
|
|
188
|
+
<span>{{ t("system.ai.knowledge.manage") }}</span>
|
|
189
|
+
<el-icon class="agent-model-panel__manage-arrow"><ArrowRight /></el-icon>
|
|
190
|
+
</button>
|
|
191
|
+
</div>
|
|
192
|
+
<div v-else class="agent-model-panel agent-model-panel--empty">
|
|
193
|
+
{{ t("system.ai.knowledge.empty") }}
|
|
194
|
+
</div>
|
|
195
|
+
</el-popover>
|
|
129
196
|
<el-tooltip
|
|
130
197
|
:content="recording ? t('system.ai.chat.action.stop_voice_input') : t('system.ai.chat.action.voice_input')"
|
|
131
198
|
placement="top"
|
|
@@ -181,6 +248,7 @@ import {
|
|
|
181
248
|
ArrowDown,
|
|
182
249
|
ArrowRight,
|
|
183
250
|
Check,
|
|
251
|
+
Collection,
|
|
184
252
|
Loading,
|
|
185
253
|
MagicStick,
|
|
186
254
|
Microphone,
|
|
@@ -195,6 +263,7 @@ import { useAuthStore } from "@liujitcn/kratos-admin-core/stores/runtime";
|
|
|
195
263
|
import { defFileService } from "@liujitcn/kratos-admin-core/api/base/v1/file";
|
|
196
264
|
import type { AiAttachment } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_session";
|
|
197
265
|
import type { AiProviderModelOption } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_provider";
|
|
266
|
+
import type { AiKnowledgeOption } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_knowledge";
|
|
198
267
|
import type { SubmitPayload } from "../types";
|
|
199
268
|
import { buildAIAttachmentFileCard } from "../attachment";
|
|
200
269
|
import { fallbackModelDisplayName } from "../modelDisplay";
|
|
@@ -210,6 +279,10 @@ const props = defineProps<{
|
|
|
210
279
|
providerId: number;
|
|
211
280
|
/** 当前选择的模型名称。 */
|
|
212
281
|
modelName: string;
|
|
282
|
+
/** 当前可供选择的知识库。 */
|
|
283
|
+
knowledgeOptions: AiKnowledgeOption[];
|
|
284
|
+
/** 当前选择的知识库编号列表。 */
|
|
285
|
+
knowledgeIds: number[];
|
|
213
286
|
}>();
|
|
214
287
|
|
|
215
288
|
const emit = defineEmits<{
|
|
@@ -217,6 +290,8 @@ const emit = defineEmits<{
|
|
|
217
290
|
submit: [payload: SubmitPayload];
|
|
218
291
|
/** 更新当前选择的供应商和模型。 */
|
|
219
292
|
"model-change": [providerId: number, modelName: string];
|
|
293
|
+
/** 更新当前选择的知识库编号列表。 */
|
|
294
|
+
"knowledge-change": [knowledgeIds: number[]];
|
|
220
295
|
}>();
|
|
221
296
|
|
|
222
297
|
const senderRef = ref<InstanceType<typeof BaseXSender>>();
|
|
@@ -249,6 +324,9 @@ const selectedModelDisplayName = computed(() => {
|
|
|
249
324
|
const aiProviderRoute = computed(() =>
|
|
250
325
|
authStore.flatMenuListGet.find(item => item.name === "AiProvider" && item.path)
|
|
251
326
|
);
|
|
327
|
+
const aiKnowledgeRoute = computed(() =>
|
|
328
|
+
authStore.flatMenuListGet.find(item => item.name === "AiKnowledge" && item.path)
|
|
329
|
+
);
|
|
252
330
|
const uploading = ref(false);
|
|
253
331
|
const maxAttachmentCount = 6;
|
|
254
332
|
const maxAttachmentSizeMB = 20;
|
|
@@ -323,6 +401,30 @@ async function openModelManage() {
|
|
|
323
401
|
await navigateTo(router, aiProviderRoute.value.path);
|
|
324
402
|
}
|
|
325
403
|
|
|
404
|
+
const knowledgePopoverRef = ref<InstanceType<typeof ElPopover>>();
|
|
405
|
+
|
|
406
|
+
const selectedKnowledgeCount = computed(() => props.knowledgeIds.length);
|
|
407
|
+
|
|
408
|
+
/** 判断知识库是否在当前选择中。 */
|
|
409
|
+
function isKnowledgeSelected(knowledgeId: number) {
|
|
410
|
+
return props.knowledgeIds.includes(knowledgeId);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** 切换知识库选择状态并同步父组件。 */
|
|
414
|
+
function handleKnowledgeToggle(knowledgeId: number) {
|
|
415
|
+
const current = new Set(props.knowledgeIds);
|
|
416
|
+
if (current.has(knowledgeId)) current.delete(knowledgeId);
|
|
417
|
+
else current.add(knowledgeId);
|
|
418
|
+
emit("knowledge-change", [...current]);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/** 跳转到 AI 知识库管理页。 */
|
|
422
|
+
async function openKnowledgeManage() {
|
|
423
|
+
if (!aiKnowledgeRoute.value?.path) return;
|
|
424
|
+
knowledgePopoverRef.value?.hide();
|
|
425
|
+
await navigateTo(router, aiKnowledgeRoute.value.path);
|
|
426
|
+
}
|
|
427
|
+
|
|
326
428
|
/** 读取输入内容并发送给父组件。 */
|
|
327
429
|
function handleSubmit() {
|
|
328
430
|
if (uploading.value) return;
|
|
@@ -30,8 +30,11 @@
|
|
|
30
30
|
:model-providers="modelProviders"
|
|
31
31
|
:provider-id="selectedProviderId"
|
|
32
32
|
:model-name="selectedModelName"
|
|
33
|
+
:knowledge-options="knowledgeOptions"
|
|
34
|
+
:knowledge-ids="selectedKnowledgeIds"
|
|
33
35
|
@submit="handleSubmit"
|
|
34
36
|
@model-change="handleModelChange"
|
|
37
|
+
@knowledge-change="handleKnowledgeChange"
|
|
35
38
|
@message-action="handleMessageAction"
|
|
36
39
|
@message-edit="handleEditMessage"
|
|
37
40
|
>
|
|
@@ -59,9 +62,11 @@ import { defAiMessageService } from "@liujitcn/kratos-admin-system/api/base/v1/a
|
|
|
59
62
|
import { defAiSessionService } from "@liujitcn/kratos-admin-system/api/base/v1/ai_session";
|
|
60
63
|
import { defAiToolService } from "@liujitcn/kratos-admin-system/api/base/v1/ai_tool";
|
|
61
64
|
import { defAiModelService } from "@liujitcn/kratos-admin-system/api/base/v1/ai_provider";
|
|
65
|
+
import { defAiKnowledgeService } from "@liujitcn/kratos-admin-system/api/base/v1/ai_knowledge";
|
|
62
66
|
import type { AiAction } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_message";
|
|
63
67
|
import type { AiSession } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_session";
|
|
64
68
|
import type { AiProviderModelOption } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_provider";
|
|
69
|
+
import type { AiKnowledgeOption } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_knowledge";
|
|
65
70
|
import type { AiShortcut } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_tool";
|
|
66
71
|
import { AiMessageStatus } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_session";
|
|
67
72
|
import { Terminal } from "@liujitcn/kratos-admin-system/rpc/base/v1/ai_tool";
|
|
@@ -112,6 +117,8 @@ const starterShortcuts = ref<AiShortcut[]>([]);
|
|
|
112
117
|
const modelProviders = ref<AiProviderModelOption[]>([]);
|
|
113
118
|
const selectedProviderId = ref(0);
|
|
114
119
|
const selectedModelName = ref("");
|
|
120
|
+
const knowledgeOptions = ref<AiKnowledgeOption[]>([]);
|
|
121
|
+
const selectedKnowledgeIds = ref<number[]>([]);
|
|
115
122
|
const messages = ref<Record<string, ChatMessageItem[]>>({});
|
|
116
123
|
const pendingDeltaMap = new Map<string, AiStreamPayload>();
|
|
117
124
|
const runningStreamTaskMap = new Map<string, AiStreamTask>();
|
|
@@ -229,6 +236,7 @@ async function runAiStreamTask(sessionID: string, payload: SubmitPayload) {
|
|
|
229
236
|
content: payload.text,
|
|
230
237
|
provider_id: selectedProviderId.value,
|
|
231
238
|
model_name: selectedModelName.value,
|
|
239
|
+
knowledge_base_ids: selectedKnowledgeIds.value,
|
|
232
240
|
attachments: payload.attachments.map(item => ({
|
|
233
241
|
id: item.id,
|
|
234
242
|
name: item.name,
|
|
@@ -657,16 +665,23 @@ async function loadAiShortcuts() {
|
|
|
657
665
|
if (loadingShortcuts.value) return;
|
|
658
666
|
loadingShortcuts.value = true;
|
|
659
667
|
try {
|
|
660
|
-
const [response, providerResponse] = await Promise.all([
|
|
668
|
+
const [response, providerResponse, knowledgeResponse] = await Promise.all([
|
|
661
669
|
defAiToolService.ListAiShortcut({ terminal: Terminal.TERMINAL_ADMIN }),
|
|
662
|
-
defAiModelService.ListAiProviderModelOptions({})
|
|
670
|
+
defAiModelService.ListAiProviderModelOptions({}),
|
|
671
|
+
defAiKnowledgeService.ListAiKnowledgeOptions({})
|
|
663
672
|
]);
|
|
664
673
|
starterShortcuts.value = normalizeStarterShortcuts(response.shortcuts);
|
|
665
674
|
modelProviders.value = providerResponse.providers ?? [];
|
|
675
|
+
knowledgeOptions.value = knowledgeResponse.options ?? [];
|
|
676
|
+
// 知识库可能已被删除,剔除无效选择。
|
|
677
|
+
selectedKnowledgeIds.value = selectedKnowledgeIds.value.filter(id =>
|
|
678
|
+
knowledgeOptions.value.some(option => option.id === id)
|
|
679
|
+
);
|
|
666
680
|
applyModelSelection();
|
|
667
681
|
} catch {
|
|
668
682
|
starterShortcuts.value = [];
|
|
669
683
|
modelProviders.value = [];
|
|
684
|
+
knowledgeOptions.value = [];
|
|
670
685
|
selectedProviderId.value = 0;
|
|
671
686
|
selectedModelName.value = "";
|
|
672
687
|
} finally {
|
|
@@ -701,6 +716,11 @@ function handleModelChange(providerId: number, modelName: string) {
|
|
|
701
716
|
saveModelSelection({ provider_id: providerId, model_name: modelName });
|
|
702
717
|
}
|
|
703
718
|
|
|
719
|
+
/** 更新当前聊天检索注入的知识库选择。 */
|
|
720
|
+
function handleKnowledgeChange(knowledgeIds: number[]) {
|
|
721
|
+
selectedKnowledgeIds.value = knowledgeIds;
|
|
722
|
+
}
|
|
723
|
+
|
|
704
724
|
/** 使指定会话仍在途的消息列表请求失效。 */
|
|
705
725
|
function invalidateLoadMessages(sessionID: string) {
|
|
706
726
|
loadMessagesSeq.set(sessionID, (loadMessagesSeq.get(sessionID) ?? 0) + 1);
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="table-box">
|
|
3
|
+
<ProTable ref="table" row-key="id" :columns="columns" :request-api="requestTable" />
|
|
4
|
+
|
|
5
|
+
<el-drawer
|
|
6
|
+
v-model="detail.visible"
|
|
7
|
+
:title="t('system.ai.query.detail.title')"
|
|
8
|
+
size="min(720px, calc(100vw - 32px))"
|
|
9
|
+
destroy-on-close
|
|
10
|
+
>
|
|
11
|
+
<div v-if="detail.row" v-loading="detail.loading" class="ai-query-detail">
|
|
12
|
+
<div class="ai-query-detail__meta">
|
|
13
|
+
<span>{{ t("system.ai.query.field.user") }}:{{ displayUser(detail.row) }}</span>
|
|
14
|
+
<el-tag :type="statusTagType(detail.row.status)" size="small" effect="plain">
|
|
15
|
+
{{ statusLabel(detail.row.status) }}
|
|
16
|
+
</el-tag>
|
|
17
|
+
<span>{{ t("system.ai.query.field.row_count") }}:{{ detail.row.row_count }}</span>
|
|
18
|
+
<span>{{ t("system.ai.query.field.elapsed_ms") }}:{{ detail.row.elapsed_ms }}</span>
|
|
19
|
+
<span>{{ t("common.field.created_at") }}:{{ detail.row.created_at }}</span>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="ai-query-detail__question">{{ detail.row.question }}</div>
|
|
22
|
+
<div v-if="detail.row.error_msg" class="ai-query-detail__error">
|
|
23
|
+
{{ t("system.ai.query.field.error_msg") }}:{{ detail.row.error_msg }}
|
|
24
|
+
</div>
|
|
25
|
+
<div class="ai-query-detail__sql-title">{{ t("system.ai.query.field.sql") }}</div>
|
|
26
|
+
<CodeBlock class="ai-query-detail__sql" :code="detail.row.sql || '-'" language="sql" />
|
|
27
|
+
<template v-if="resultRows.length">
|
|
28
|
+
<div class="ai-query-detail__sql-title">{{ t("system.ai.query.detail.result") }}</div>
|
|
29
|
+
<el-table :data="resultRows" size="small" max-height="320" border class="ai-query-detail__result">
|
|
30
|
+
<el-table-column
|
|
31
|
+
v-for="(column, index) in detail.row.result_columns"
|
|
32
|
+
:key="`${column}-${index}`"
|
|
33
|
+
:prop="String(index)"
|
|
34
|
+
:label="column"
|
|
35
|
+
show-overflow-tooltip
|
|
36
|
+
min-width="120"
|
|
37
|
+
/>
|
|
38
|
+
</el-table>
|
|
39
|
+
</template>
|
|
40
|
+
</div>
|
|
41
|
+
</el-drawer>
|
|
42
|
+
</div>
|
|
43
|
+
</template>
|
|
44
|
+
|
|
45
|
+
<script setup lang="ts">
|
|
46
|
+
import { computed, reactive, ref } from "vue";
|
|
47
|
+
import { View } from "@element-plus/icons-vue";
|
|
48
|
+
import ProTable from "@liujitcn/kratos-admin-core/components/ProTable";
|
|
49
|
+
import CodeBlock from "@liujitcn/kratos-admin-core/components/CodeBlock/index.vue";
|
|
50
|
+
import type { ColumnProps, EnumProps, ProTableInstance } from "@liujitcn/kratos-admin-core/components/ProTable/interface";
|
|
51
|
+
import { useAuthButtons } from "@liujitcn/kratos-admin-core/auth";
|
|
52
|
+
import { buildPageRequest } from "@liujitcn/kratos-admin-core/table";
|
|
53
|
+
import { t } from "@liujitcn/kratos-admin-core";
|
|
54
|
+
import { defAiQueryService } from "@liujitcn/kratos-admin-system/api/system/admin/v1/ai_query";
|
|
55
|
+
import type { AiQueryRecord, PageAiQueryRequest } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/ai_query";
|
|
56
|
+
import { AiQueryStatus } from "@liujitcn/kratos-admin-system/rpc/system/admin/v1/ai_query";
|
|
57
|
+
|
|
58
|
+
defineOptions({ name: "AiQuery", inheritAttrs: false });
|
|
59
|
+
|
|
60
|
+
const { BUTTONS } = useAuthButtons();
|
|
61
|
+
const table = ref<ProTableInstance>();
|
|
62
|
+
const detail = reactive({
|
|
63
|
+
visible: false,
|
|
64
|
+
row: null as AiQueryRecord | null,
|
|
65
|
+
loading: false
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
/** 问数状态枚举选项。 */
|
|
69
|
+
const statusEnum = computed<EnumProps[]>(() => [
|
|
70
|
+
{ label: t("system.ai.query.status.success"), value: AiQueryStatus.AI_QUERY_STATUS_SUCCESS, tagType: "success" },
|
|
71
|
+
{ label: t("system.ai.query.status.failed"), value: AiQueryStatus.AI_QUERY_STATUS_FAILED, tagType: "danger" }
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
const columns = computed<ColumnProps[]>(() => [
|
|
75
|
+
{ prop: "id", label: t("system.ai.query.field.id"), width: 90, align: "center" },
|
|
76
|
+
{
|
|
77
|
+
prop: "question",
|
|
78
|
+
label: t("system.ai.query.field.question"),
|
|
79
|
+
minWidth: 220,
|
|
80
|
+
showOverflowTooltip: true,
|
|
81
|
+
search: { el: "input" }
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
prop: "user",
|
|
85
|
+
label: t("system.ai.query.field.user"),
|
|
86
|
+
minWidth: 120,
|
|
87
|
+
showOverflowTooltip: true,
|
|
88
|
+
render: scope => displayUser(scope.row as AiQueryRecord)
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
prop: "status",
|
|
92
|
+
label: t("system.ai.query.field.status"),
|
|
93
|
+
width: 110,
|
|
94
|
+
align: "center",
|
|
95
|
+
enum: statusEnum,
|
|
96
|
+
search: { el: "select", props: { clearable: true } }
|
|
97
|
+
},
|
|
98
|
+
{ prop: "row_count", label: t("system.ai.query.field.row_count"), width: 100, align: "right" },
|
|
99
|
+
{ prop: "elapsed_ms", label: t("system.ai.query.field.elapsed_ms"), width: 110, align: "right" },
|
|
100
|
+
{ prop: "created_at", label: t("common.field.created_at"), minWidth: 170, align: "center" },
|
|
101
|
+
{
|
|
102
|
+
prop: "operation",
|
|
103
|
+
label: t("common.field.operation"),
|
|
104
|
+
cellType: "actions",
|
|
105
|
+
fixed: "right",
|
|
106
|
+
width: 120,
|
|
107
|
+
actions: [
|
|
108
|
+
{
|
|
109
|
+
label: t("system.ai.query.action.detail"),
|
|
110
|
+
link: true,
|
|
111
|
+
icon: View,
|
|
112
|
+
hidden: () => !BUTTONS.value["base:ai-query:detail"],
|
|
113
|
+
onClick: scope => openDetail(scope.row as AiQueryRecord)
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
]);
|
|
118
|
+
|
|
119
|
+
/** 请求智能问数记录表格数据。 */
|
|
120
|
+
async function requestTable(params: Record<string, unknown>) {
|
|
121
|
+
const request = buildPageRequest<PageAiQueryRequest>(params as unknown as PageAiQueryRequest);
|
|
122
|
+
if (!request.status) delete request.status;
|
|
123
|
+
const data = await defAiQueryService.PageAiQuery(request);
|
|
124
|
+
return { data: { list: data.queries ?? [], total: data.total } };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** 展示记录所属用户,昵称缺失时回退账号。 */
|
|
128
|
+
function displayUser(row: AiQueryRecord): string {
|
|
129
|
+
return row.user_name || String(row.user_id ?? "");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** 转换问数状态为展示文案。 */
|
|
133
|
+
function statusLabel(status?: AiQueryStatus): string {
|
|
134
|
+
return status === AiQueryStatus.AI_QUERY_STATUS_SUCCESS
|
|
135
|
+
? t("system.ai.query.status.success")
|
|
136
|
+
: status === AiQueryStatus.AI_QUERY_STATUS_FAILED
|
|
137
|
+
? t("system.ai.query.status.failed")
|
|
138
|
+
: "";
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** 转换问数状态为标签类型。 */
|
|
142
|
+
function statusTagType(status?: AiQueryStatus): "success" | "danger" | "info" {
|
|
143
|
+
switch (status) {
|
|
144
|
+
case AiQueryStatus.AI_QUERY_STATUS_SUCCESS:
|
|
145
|
+
return "success";
|
|
146
|
+
case AiQueryStatus.AI_QUERY_STATUS_FAILED:
|
|
147
|
+
return "danger";
|
|
148
|
+
default:
|
|
149
|
+
return "info";
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** 打开问数详情抽屉。 */
|
|
154
|
+
async function openDetail(row: AiQueryRecord) {
|
|
155
|
+
detail.row = row;
|
|
156
|
+
detail.visible = true;
|
|
157
|
+
detail.loading = true;
|
|
158
|
+
try {
|
|
159
|
+
detail.row = await defAiQueryService.GetAiQuery({ id: row.id });
|
|
160
|
+
} finally {
|
|
161
|
+
detail.loading = false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** 将结果快照行转换为表格行数据,按列下标取值。 */
|
|
166
|
+
const resultRows = computed<Record<string, string>[]>(() => {
|
|
167
|
+
const columns = detail.row?.result_columns ?? [];
|
|
168
|
+
return (detail.row?.result_rows ?? []).map(row => {
|
|
169
|
+
const item: Record<string, string> = {};
|
|
170
|
+
columns.forEach((_, index) => {
|
|
171
|
+
item[String(index)] = row.values[index] ?? "";
|
|
172
|
+
});
|
|
173
|
+
return item;
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
</script>
|
|
177
|
+
|
|
178
|
+
<style scoped>
|
|
179
|
+
.ai-query-detail {
|
|
180
|
+
display: flex;
|
|
181
|
+
flex-direction: column;
|
|
182
|
+
gap: 16px;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.ai-query-detail__meta {
|
|
186
|
+
display: flex;
|
|
187
|
+
align-items: center;
|
|
188
|
+
flex-wrap: wrap;
|
|
189
|
+
gap: 12px;
|
|
190
|
+
color: var(--el-text-color-secondary);
|
|
191
|
+
font-size: 12px;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.ai-query-detail__question {
|
|
195
|
+
font-size: 14px;
|
|
196
|
+
font-weight: 600;
|
|
197
|
+
line-height: 1.6;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.ai-query-detail__error {
|
|
201
|
+
padding: 8px 12px;
|
|
202
|
+
color: var(--el-color-danger);
|
|
203
|
+
font-size: 13px;
|
|
204
|
+
border: 1px solid var(--el-color-danger-light-8);
|
|
205
|
+
border-radius: 6px;
|
|
206
|
+
background-color: var(--el-color-danger-light-9);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.ai-query-detail__sql-title {
|
|
210
|
+
color: var(--el-text-color-secondary);
|
|
211
|
+
font-size: 12px;
|
|
212
|
+
font-weight: 600;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.ai-query-detail__result {
|
|
216
|
+
width: 100%;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
:deep(.ai-query-detail__result .el-table__cell) {
|
|
220
|
+
font-size: 12px;
|
|
221
|
+
}
|
|
222
|
+
</style>
|