mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-05-24 15:40:19 +08:00
feat: AI聊天交互优化
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
Vendored
+3
@@ -16,6 +16,9 @@ declare module 'vue' {
|
||||
UApp: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.3__1572391ae10a8169a5c9784ec5cec455/node_modules/@nuxt/ui/dist/runtime/components/App.vue')['default']
|
||||
UBadge: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.3__1572391ae10a8169a5c9784ec5cec455/node_modules/@nuxt/ui/dist/runtime/components/Badge.vue')['default']
|
||||
UButton: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.3__1572391ae10a8169a5c9784ec5cec455/node_modules/@nuxt/ui/dist/runtime/components/Button.vue')['default']
|
||||
UCard: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.3__1572391ae10a8169a5c9784ec5cec455/node_modules/@nuxt/ui/dist/runtime/components/Card.vue')['default']
|
||||
UChatPrompt: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.3__1572391ae10a8169a5c9784ec5cec455/node_modules/@nuxt/ui/dist/runtime/components/ChatPrompt.vue')['default']
|
||||
UChatPromptSubmit: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.3__1572391ae10a8169a5c9784ec5cec455/node_modules/@nuxt/ui/dist/runtime/components/ChatPromptSubmit.vue')['default']
|
||||
UContextMenu: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.3__1572391ae10a8169a5c9784ec5cec455/node_modules/@nuxt/ui/dist/runtime/components/ContextMenu.vue')['default']
|
||||
UIcon: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.3__1572391ae10a8169a5c9784ec5cec455/node_modules/@nuxt/ui/dist/runtime/vue/components/Icon.vue')['default']
|
||||
UInput: typeof import('./../node_modules/.pnpm/@nuxt+ui@4.2.1_@babel+parser@7.28.5_axios@1.13.2_embla-carousel@8.6.0_typescript@5.9.3__1572391ae10a8169a5c9784ec5cec455/node_modules/@nuxt/ui/dist/runtime/components/Input.vue')['default']
|
||||
|
||||
@@ -152,111 +152,131 @@ watch(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-full gap-4 overflow-hidden">
|
||||
<div class="relative flex h-full overflow-hidden px-4">
|
||||
<!-- 左侧:对话记录列表 -->
|
||||
<ConversationList
|
||||
ref="conversationListRef"
|
||||
:session-id="sessionId"
|
||||
:active-id="currentConversationId"
|
||||
@select="handleSelectConversation"
|
||||
@create="handleCreateConversation"
|
||||
@delete="handleDeleteConversation"
|
||||
/>
|
||||
<div class="absolute left-0 top-0 h-full w-64 p-4">
|
||||
<ConversationList
|
||||
ref="conversationListRef"
|
||||
:session-id="sessionId"
|
||||
:active-id="currentConversationId"
|
||||
@select="handleSelectConversation"
|
||||
@create="handleCreateConversation"
|
||||
@delete="handleDeleteConversation"
|
||||
class="h-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 中间:对话区域 -->
|
||||
<div class="flex min-w-0 flex-1 flex-col overflow-hidden rounded-xl bg-white shadow-sm dark:bg-gray-900">
|
||||
<!-- 对话区域头部 -->
|
||||
<div class="flex h-full flex-1 justify-center pl-64 pr-80">
|
||||
<div
|
||||
class="flex items-center justify-between border-b border-gray-200 px-4 py-3 dark:border-gray-800"
|
||||
class="flex min-w-0 flex-1 max-w-3xl flex-col overflow-hidden rounded-xl bg-white shadow-sm dark:bg-gray-900"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-heroicons-sparkles" class="h-5 w-5 text-violet-500" />
|
||||
<span class="font-medium text-gray-900 dark:text-white">AI 对话探索</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<!-- 配置状态指示 -->
|
||||
<div
|
||||
v-if="!isCheckingConfig"
|
||||
class="flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs"
|
||||
:class="[
|
||||
hasLLMConfig
|
||||
? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400'
|
||||
: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400',
|
||||
]"
|
||||
>
|
||||
<span
|
||||
class="h-2 w-2 rounded-full"
|
||||
:class="[hasLLMConfig ? 'bg-green-500' : 'bg-amber-500']"
|
||||
/>
|
||||
{{ hasLLMConfig ? '已配置' : '未配置' }}
|
||||
<!-- 对话区域头部 -->
|
||||
<div class="shrink-0 flex items-center justify-between border-b border-gray-200 px-4 py-3 dark:border-gray-800">
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-heroicons-sparkles" class="h-5 w-5 text-violet-500" />
|
||||
<span class="font-medium text-gray-900 dark:text-white">AI 对话探索</span>
|
||||
</div>
|
||||
<UButton
|
||||
icon="i-heroicons-cog-6-tooth"
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
@click="showConfigModal = true"
|
||||
>
|
||||
配置
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 消息列表 -->
|
||||
<div ref="messagesContainer" class="flex-1 overflow-y-auto p-4">
|
||||
<div class="mx-auto max-w-2xl space-y-4">
|
||||
<ChatMessage
|
||||
v-for="msg in messages"
|
||||
:key="msg.id"
|
||||
:role="msg.role"
|
||||
:content="msg.content"
|
||||
:timestamp="msg.timestamp"
|
||||
:is-streaming="msg.isStreaming"
|
||||
/>
|
||||
|
||||
<!-- AI 思考中指示器 -->
|
||||
<div v-if="isAIThinking && !messages[messages.length - 1]?.isStreaming" class="flex items-start gap-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<!-- 配置状态指示 -->
|
||||
<div
|
||||
class="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-violet-500 to-purple-600"
|
||||
v-if="!isCheckingConfig"
|
||||
class="flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs"
|
||||
:class="[
|
||||
hasLLMConfig
|
||||
? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400'
|
||||
: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400',
|
||||
]"
|
||||
>
|
||||
<UIcon name="i-heroicons-sparkles" class="h-4 w-4 text-white" />
|
||||
<span class="h-2 w-2 rounded-full" :class="[hasLLMConfig ? 'bg-green-500' : 'bg-amber-500']" />
|
||||
{{ hasLLMConfig ? '已配置' : '未配置' }}
|
||||
</div>
|
||||
<div
|
||||
class="rounded-2xl rounded-tl-sm bg-gray-100 px-4 py-3 dark:bg-gray-800"
|
||||
<UButton
|
||||
icon="i-heroicons-cog-6-tooth"
|
||||
color="gray"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
@click="showConfigModal = true"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ isLoadingSource ? '正在搜索相关记录...' : '正在生成回复...' }}
|
||||
</span>
|
||||
<span class="flex gap-1">
|
||||
<span class="h-2 w-2 animate-bounce rounded-full bg-violet-500 [animation-delay:0ms]" />
|
||||
<span class="h-2 w-2 animate-bounce rounded-full bg-violet-500 [animation-delay:150ms]" />
|
||||
<span class="h-2 w-2 animate-bounce rounded-full bg-violet-500 [animation-delay:300ms]" />
|
||||
</span>
|
||||
配置
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 消息列表 -->
|
||||
<div ref="messagesContainer" class="min-h-0 flex-1 overflow-y-auto p-4">
|
||||
<div class="mx-auto space-y-4">
|
||||
<ChatMessage
|
||||
v-for="msg in messages"
|
||||
:key="msg.id"
|
||||
:role="msg.role"
|
||||
:content="msg.content"
|
||||
:timestamp="msg.timestamp"
|
||||
:is-streaming="msg.isStreaming"
|
||||
/>
|
||||
|
||||
<!-- AI 思考中指示器 -->
|
||||
<div v-if="isAIThinking && !messages[messages.length - 1]?.isStreaming" class="flex items-start gap-3">
|
||||
<div
|
||||
class="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-violet-500 to-purple-600"
|
||||
>
|
||||
<UIcon name="i-heroicons-sparkles" class="h-4 w-4 text-white" />
|
||||
</div>
|
||||
<div class="rounded-2xl rounded-tl-sm bg-gray-100 px-4 py-3 dark:bg-gray-800">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ isLoadingSource ? '正在搜索相关记录...' : '正在生成回复...' }}
|
||||
</span>
|
||||
<span class="flex gap-1">
|
||||
<span class="h-2 w-2 animate-bounce rounded-full bg-violet-500 [animation-delay:0ms]" />
|
||||
<span class="h-2 w-2 animate-bounce rounded-full bg-violet-500 [animation-delay:150ms]" />
|
||||
<span class="h-2 w-2 animate-bounce rounded-full bg-violet-500 [animation-delay:300ms]" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 输入框 -->
|
||||
<ChatInput :disabled="isAIThinking" @send="handleSend" />
|
||||
<!-- 输入框 -->
|
||||
<ChatInput
|
||||
:disabled="isAIThinking"
|
||||
:status="isAIThinking ? (isLoadingSource ? 'submitted' : 'streaming') : 'ready'"
|
||||
@send="handleSend"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:数据源面板(限制高度) -->
|
||||
<div class="flex w-80 flex-col gap-4">
|
||||
<DataSourcePanel
|
||||
:messages="sourceMessages"
|
||||
:keywords="currentKeywords"
|
||||
:is-loading="isLoadingSource"
|
||||
:is-collapsed="isSourcePanelCollapsed"
|
||||
class="max-h-[60vh]"
|
||||
@toggle="toggleSourcePanel"
|
||||
@load-more="handleLoadMore"
|
||||
/>
|
||||
</div>
|
||||
<Transition name="slide-fade">
|
||||
<div v-if="sourceMessages.length > 0 && !isSourcePanelCollapsed" class="absolute right-0 top-0 h-full w-80 p-4">
|
||||
<DataSourcePanel
|
||||
:messages="sourceMessages"
|
||||
:keywords="currentKeywords"
|
||||
:is-loading="isLoadingSource"
|
||||
:is-collapsed="isSourcePanelCollapsed"
|
||||
class="h-full"
|
||||
@toggle="toggleSourcePanel"
|
||||
@load-more="handleLoadMore"
|
||||
/>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- AI 配置弹窗 -->
|
||||
<AIConfigModal v-model:open="showConfigModal" @saved="handleConfigSaved" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Transition styles for slide-fade */
|
||||
.slide-fade-enter-active,
|
||||
.slide-fade-leave-active {
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
|
||||
.slide-fade-enter-from,
|
||||
.slide-fade-leave-to {
|
||||
transform: translateX(20px);
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,96 +5,57 @@ import { ref, computed } from 'vue'
|
||||
const props = defineProps<{
|
||||
disabled?: boolean
|
||||
placeholder?: string
|
||||
status?: 'ready' | 'submitted' | 'streaming' | 'error'
|
||||
}>()
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits<{
|
||||
send: [content: string]
|
||||
stop: []
|
||||
}>()
|
||||
|
||||
// 输入内容
|
||||
const inputValue = ref('')
|
||||
const textareaRef = ref<HTMLTextAreaElement | null>(null)
|
||||
|
||||
// 是否可以发送
|
||||
const canSend = computed(() => inputValue.value.trim() && !props.disabled)
|
||||
// 计算 status
|
||||
const chatStatus = computed(() => {
|
||||
if (props.disabled) {
|
||||
return props.status || 'submitted'
|
||||
}
|
||||
return 'ready'
|
||||
})
|
||||
|
||||
// 发送消息
|
||||
function handleSend() {
|
||||
if (!canSend.value) return
|
||||
function handleSubmit() {
|
||||
if (!inputValue.value.trim() || props.disabled) return
|
||||
|
||||
emit('send', inputValue.value.trim())
|
||||
inputValue.value = ''
|
||||
|
||||
// 重置 textarea 高度
|
||||
if (textareaRef.value) {
|
||||
textareaRef.value.style.height = 'auto'
|
||||
}
|
||||
}
|
||||
|
||||
// 处理键盘事件
|
||||
function handleKeydown(e: KeyboardEvent) {
|
||||
// Enter 发送(Shift+Enter 换行)
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault()
|
||||
handleSend()
|
||||
}
|
||||
}
|
||||
|
||||
// 自动调整 textarea 高度
|
||||
function handleInput(e: Event) {
|
||||
const target = e.target as HTMLTextAreaElement
|
||||
target.style.height = 'auto'
|
||||
target.style.height = Math.min(target.scrollHeight, 200) + 'px'
|
||||
// 停止生成
|
||||
function handleStop() {
|
||||
emit('stop')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="border-t border-gray-200 p-4 dark:border-gray-800">
|
||||
<div class="shrink-0 border-t border-gray-200 p-4 dark:border-gray-800">
|
||||
<div class="mx-auto max-w-2xl">
|
||||
<div
|
||||
class="flex items-end gap-3 rounded-2xl bg-gray-100 px-4 py-3 dark:bg-gray-800"
|
||||
:class="[disabled ? 'opacity-60' : '']"
|
||||
<UChatPrompt
|
||||
v-model="inputValue"
|
||||
:placeholder="placeholder || '输入你的问题...'"
|
||||
:disabled="disabled"
|
||||
variant="subtle"
|
||||
@submit="handleSubmit"
|
||||
>
|
||||
<!-- 输入框 -->
|
||||
<textarea
|
||||
ref="textareaRef"
|
||||
v-model="inputValue"
|
||||
:placeholder="placeholder || '输入你的问题...'"
|
||||
:disabled="disabled"
|
||||
rows="1"
|
||||
class="max-h-[200px] min-h-[24px] flex-1 resize-none bg-transparent text-sm text-gray-900 placeholder-gray-500 outline-none dark:text-white dark:placeholder-gray-400"
|
||||
@keydown="handleKeydown"
|
||||
@input="handleInput"
|
||||
<UChatPromptSubmit
|
||||
:status="chatStatus"
|
||||
class="rounded-full"
|
||||
color="primary"
|
||||
@stop="handleStop"
|
||||
/>
|
||||
|
||||
<!-- 发送按钮 -->
|
||||
<button
|
||||
:disabled="!canSend"
|
||||
class="flex h-8 w-8 shrink-0 items-center justify-center rounded-full transition-colors"
|
||||
:class="[
|
||||
canSend
|
||||
? 'bg-violet-500 text-white hover:bg-violet-600'
|
||||
: 'cursor-not-allowed bg-gray-300 text-gray-500 dark:bg-gray-700',
|
||||
]"
|
||||
@click="handleSend"
|
||||
>
|
||||
<UIcon name="i-heroicons-paper-airplane" class="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 提示文字 -->
|
||||
<div class="mt-2 flex items-center justify-center gap-2">
|
||||
<span class="text-xs text-gray-400">
|
||||
<kbd class="rounded bg-gray-200 px-1 py-0.5 text-[10px] dark:bg-gray-700">Enter</kbd>
|
||||
发送
|
||||
</span>
|
||||
<span class="text-xs text-gray-400">
|
||||
<kbd class="rounded bg-gray-200 px-1 py-0.5 text-[10px] dark:bg-gray-700">Shift + Enter</kbd>
|
||||
换行
|
||||
</span>
|
||||
</div>
|
||||
</UChatPrompt>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { computed } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import userAvatar from '@/assets/images/momo.png'
|
||||
|
||||
// Props
|
||||
const props = defineProps<{
|
||||
@@ -37,18 +38,14 @@ const renderedContent = computed(() => {
|
||||
<template>
|
||||
<div class="flex items-start gap-3" :class="[isUser ? 'flex-row-reverse' : '']">
|
||||
<!-- 头像 -->
|
||||
<div v-if="isUser" class="h-8 w-8 shrink-0 overflow-hidden rounded-full">
|
||||
<img :src="userAvatar" alt="用户头像" class="h-full w-full object-cover" />
|
||||
</div>
|
||||
<div
|
||||
class="flex h-8 w-8 shrink-0 items-center justify-center rounded-full"
|
||||
:class="[
|
||||
isUser
|
||||
? 'bg-gradient-to-br from-blue-500 to-cyan-500'
|
||||
: 'bg-gradient-to-br from-violet-500 to-purple-600',
|
||||
]"
|
||||
v-else
|
||||
class="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-violet-500 to-purple-600"
|
||||
>
|
||||
<UIcon
|
||||
:name="isUser ? 'i-heroicons-user' : 'i-heroicons-sparkles'"
|
||||
class="h-4 w-4 text-white"
|
||||
/>
|
||||
<UIcon name="i-heroicons-sparkles" class="h-4 w-4 text-white" />
|
||||
</div>
|
||||
|
||||
<!-- 消息内容 -->
|
||||
|
||||
@@ -113,7 +113,7 @@ defineExpose({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-full w-48 flex-col rounded-xl bg-white shadow-sm dark:bg-gray-900">
|
||||
<div class="flex h-full w-64 flex-col rounded-xl bg-white shadow-sm dark:bg-gray-900">
|
||||
<!-- 头部 -->
|
||||
<div class="flex items-center justify-between border-b border-gray-200 px-4 py-3 dark:border-gray-800">
|
||||
<div class="flex items-center gap-2">
|
||||
|
||||
@@ -333,7 +333,10 @@ export function useAIChat(sessionId: string, timeFilter?: { startTs: number; end
|
||||
|
||||
// 5. 保存对话到数据库
|
||||
console.log('[AI] === Step 5: 保存对话 ===')
|
||||
await saveConversation(userMessage, aiMessage)
|
||||
// 使用数组中的最新消息(流式更新后的内容)
|
||||
const finalAiMessage = messages.value[aiMessageIndex]
|
||||
console.log('[AI] 保存 AI 消息内容长度:', finalAiMessage.content.length)
|
||||
await saveConversation(userMessage, finalAiMessage)
|
||||
console.log('[AI] 对话已保存')
|
||||
console.log('[AI] ====== 处理完成 ======')
|
||||
} catch (error) {
|
||||
@@ -362,26 +365,40 @@ export function useAIChat(sessionId: string, timeFilter?: { startTs: number; end
|
||||
* 保存对话到数据库
|
||||
*/
|
||||
async function saveConversation(userMsg: ChatMessage, aiMsg: ChatMessage): Promise<void> {
|
||||
console.log('[AI] saveConversation 调用')
|
||||
console.log('[AI] 用户消息内容长度:', userMsg.content?.length || 0)
|
||||
console.log('[AI] AI消息内容长度:', aiMsg.content?.length || 0)
|
||||
console.log('[AI] AI消息内容预览:', aiMsg.content?.slice(0, 100))
|
||||
|
||||
try {
|
||||
// 如果没有当前对话,创建新对话
|
||||
// 如果没有当前对话,创建新对话(使用用户第一次提问作为标题)
|
||||
if (!currentConversationId.value) {
|
||||
const conversation = await window.aiApi.createConversation(sessionId)
|
||||
// 截取前 50 个字符作为标题
|
||||
const title = userMsg.content.slice(0, 50) + (userMsg.content.length > 50 ? '...' : '')
|
||||
const conversation = await window.aiApi.createConversation(sessionId, title)
|
||||
currentConversationId.value = conversation.id
|
||||
console.log('[AI] 创建了新对话:', conversation.id)
|
||||
}
|
||||
|
||||
// 保存用户消息
|
||||
console.log('[AI] 保存用户消息...')
|
||||
await window.aiApi.addMessage(currentConversationId.value, 'user', userMsg.content)
|
||||
|
||||
// 保存 AI 消息
|
||||
// 保存 AI 消息(需要将 Proxy 对象转为普通对象以便 IPC 序列化)
|
||||
console.log('[AI] 保存 AI 消息...')
|
||||
const keywords = aiMsg.dataSource?.keywords ? [...aiMsg.dataSource.keywords] : undefined
|
||||
const messageCount = aiMsg.dataSource?.messageCount
|
||||
|
||||
await window.aiApi.addMessage(
|
||||
currentConversationId.value,
|
||||
'assistant',
|
||||
aiMsg.content,
|
||||
aiMsg.dataSource?.keywords,
|
||||
aiMsg.dataSource?.messageCount
|
||||
keywords,
|
||||
messageCount
|
||||
)
|
||||
console.log('[AI] 消息保存完成')
|
||||
} catch (error) {
|
||||
console.error('保存对话失败:', error)
|
||||
console.error('[AI] 保存对话失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,8 +406,17 @@ export function useAIChat(sessionId: string, timeFilter?: { startTs: number; end
|
||||
* 加载对话历史
|
||||
*/
|
||||
async function loadConversation(conversationId: string): Promise<void> {
|
||||
console.log('[AI] 加载对话历史,conversationId:', conversationId)
|
||||
try {
|
||||
const history = await window.aiApi.getMessages(conversationId)
|
||||
console.log('[AI] 获取到的历史消息数量:', history.length)
|
||||
console.log('[AI] 历史消息详情:', history.map(m => ({
|
||||
id: m.id,
|
||||
role: m.role,
|
||||
contentLength: m.content?.length || 0,
|
||||
content: m.content?.slice(0, 50) + '...'
|
||||
})))
|
||||
|
||||
currentConversationId.value = conversationId
|
||||
|
||||
messages.value = history.map((msg) => ({
|
||||
@@ -405,8 +431,9 @@ export function useAIChat(sessionId: string, timeFilter?: { startTs: number; end
|
||||
}
|
||||
: undefined,
|
||||
}))
|
||||
console.log('[AI] 加载完成,messages.value 数量:', messages.value.length)
|
||||
} catch (error) {
|
||||
console.error('加载对话历史失败:', error)
|
||||
console.error('[AI] 加载对话历史失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user