mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-05-06 05:01:19 +08:00
feat: 逻辑优化
This commit is contained in:
@@ -147,7 +147,7 @@ function getLockedPromptSection(chatType: 'group' | 'private'): string {
|
|||||||
`
|
`
|
||||||
: `成员查询策略:
|
: `成员查询策略:
|
||||||
- 当用户提到特定群成员(如"张三说过什么"、"小明的发言"等)时,应先调用 get_group_members 获取成员列表
|
- 当用户提到特定群成员(如"张三说过什么"、"小明的发言"等)时,应先调用 get_group_members 获取成员列表
|
||||||
- 群成员有三种名称:accountName(QQ原始昵称)、groupNickname(群昵称)、aliases(用户自定义别名)
|
- 群成员有三种名称:accountName(原始昵称)、groupNickname(群昵称)、aliases(用户自定义别名)
|
||||||
- 通过 get_group_members 的 search 参数可以模糊搜索这三种名称
|
- 通过 get_group_members 的 search 参数可以模糊搜索这三种名称
|
||||||
- 找到成员后,使用其 id 字段作为 search_messages 的 sender_id 参数来获取该成员的发言
|
- 找到成员后,使用其 id 字段作为 search_messages 的 sender_id 参数来获取该成员的发言
|
||||||
`
|
`
|
||||||
@@ -553,6 +553,16 @@ export class Agent {
|
|||||||
let toolParams: Record<string, unknown> | undefined
|
let toolParams: Record<string, unknown> | undefined
|
||||||
try {
|
try {
|
||||||
toolParams = JSON.parse(tc.function.arguments || '{}')
|
toolParams = JSON.parse(tc.function.arguments || '{}')
|
||||||
|
|
||||||
|
// 对于消息获取类工具,用用户配置的 limit 覆盖 LLM 传递的值(保持显示一致)
|
||||||
|
const toolsWithLimit = ['search_messages', 'get_recent_messages', 'get_conversation_between']
|
||||||
|
if (this.context.maxMessagesLimit && toolsWithLimit.includes(tc.function.name)) {
|
||||||
|
toolParams = {
|
||||||
|
...toolParams,
|
||||||
|
limit: this.context.maxMessagesLimit, // 用户配置优先
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 对于搜索类工具,添加时间范围信息
|
// 对于搜索类工具,添加时间范围信息
|
||||||
if (
|
if (
|
||||||
this.context.timeFilter &&
|
this.context.timeFilter &&
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ async function searchMessagesExecutor(
|
|||||||
context: ToolContext
|
context: ToolContext
|
||||||
): Promise<unknown> {
|
): Promise<unknown> {
|
||||||
const { sessionId, timeFilter: contextTimeFilter, maxMessagesLimit } = context
|
const { sessionId, timeFilter: contextTimeFilter, maxMessagesLimit } = context
|
||||||
// 优先使用 LLM 指定的 limit,其次使用用户配置,最后使用默认值 200,上限 5000
|
// 用户配置优先:如果用户设置了 maxMessagesLimit,使用它;否则使用 LLM 指定的值或默认值 200,上限 5000
|
||||||
const limit = Math.min(params.limit || maxMessagesLimit || 200, 5000)
|
const limit = Math.min(maxMessagesLimit || params.limit || 200, 5000)
|
||||||
|
|
||||||
// 构建时间过滤器:优先使用 LLM 指定的年/月,否则使用 context 中的
|
// 构建时间过滤器:优先使用 LLM 指定的年/月,否则使用 context 中的
|
||||||
let effectiveTimeFilter = contextTimeFilter
|
let effectiveTimeFilter = contextTimeFilter
|
||||||
@@ -144,8 +144,15 @@ async function getRecentMessagesExecutor(
|
|||||||
context: ToolContext
|
context: ToolContext
|
||||||
): Promise<unknown> {
|
): Promise<unknown> {
|
||||||
const { sessionId, timeFilter: contextTimeFilter, maxMessagesLimit } = context
|
const { sessionId, timeFilter: contextTimeFilter, maxMessagesLimit } = context
|
||||||
// 优先使用 LLM 指定的 limit,其次使用用户配置,最后使用默认值 100
|
// 用户配置优先:如果用户设置了 maxMessagesLimit,使用它;否则使用 LLM 指定的值或默认值 100
|
||||||
const limit = params.limit || maxMessagesLimit || 100
|
const limit = maxMessagesLimit || params.limit || 100
|
||||||
|
|
||||||
|
console.log('[getRecentMessages] 参数调试:', {
|
||||||
|
'params.limit': params.limit,
|
||||||
|
'context.maxMessagesLimit': maxMessagesLimit,
|
||||||
|
'计算后的 limit': limit,
|
||||||
|
'context 完整内容': JSON.stringify(context),
|
||||||
|
})
|
||||||
|
|
||||||
// 构建时间过滤器:优先使用 LLM 指定的年/月
|
// 构建时间过滤器:优先使用 LLM 指定的年/月
|
||||||
let effectiveTimeFilter = contextTimeFilter
|
let effectiveTimeFilter = contextTimeFilter
|
||||||
@@ -495,8 +502,8 @@ async function getConversationBetweenExecutor(
|
|||||||
context: ToolContext
|
context: ToolContext
|
||||||
): Promise<unknown> {
|
): Promise<unknown> {
|
||||||
const { sessionId, timeFilter: contextTimeFilter, maxMessagesLimit } = context
|
const { sessionId, timeFilter: contextTimeFilter, maxMessagesLimit } = context
|
||||||
// 优先使用 LLM 指定的 limit,其次使用用户配置,最后使用默认值 100
|
// 用户配置优先:如果用户设置了 maxMessagesLimit,使用它;否则使用 LLM 指定的值或默认值 100
|
||||||
const limit = params.limit || maxMessagesLimit || 100
|
const limit = maxMessagesLimit || params.limit || 100
|
||||||
|
|
||||||
// 构建时间过滤器
|
// 构建时间过滤器
|
||||||
let effectiveTimeFilter = contextTimeFilter
|
let effectiveTimeFilter = contextTimeFilter
|
||||||
|
|||||||
@@ -476,11 +476,13 @@ export function registerAIHandlers({ win }: IpcContext): void {
|
|||||||
if (abortController.signal.aborted) {
|
if (abortController.signal.aborted) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
aiLogger.debug('IPC', `Agent chunk: ${requestId}`, {
|
// 减少日志噪音:只在工具调用时记录
|
||||||
type: chunk.type,
|
if (chunk.type === 'tool_start' || chunk.type === 'tool_result') {
|
||||||
contentLength: chunk.content?.length,
|
aiLogger.debug('IPC', `Agent chunk: ${requestId}`, {
|
||||||
toolName: chunk.toolName,
|
type: chunk.type,
|
||||||
})
|
toolName: chunk.toolName,
|
||||||
|
})
|
||||||
|
}
|
||||||
win.webContents.send('agent:streamChunk', { requestId, chunk })
|
win.webContents.send('agent:streamChunk', { requestId, chunk })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -472,7 +472,15 @@ const aiApi = {
|
|||||||
dataMessageCount?: number,
|
dataMessageCount?: number,
|
||||||
contentBlocks?: ContentBlock[]
|
contentBlocks?: ContentBlock[]
|
||||||
): Promise<AIMessage> => {
|
): Promise<AIMessage> => {
|
||||||
return ipcRenderer.invoke('ai:addMessage', conversationId, role, content, dataKeywords, dataMessageCount, contentBlocks)
|
return ipcRenderer.invoke(
|
||||||
|
'ai:addMessage',
|
||||||
|
conversationId,
|
||||||
|
role,
|
||||||
|
content,
|
||||||
|
dataKeywords,
|
||||||
|
dataMessageCount,
|
||||||
|
contentBlocks
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ watch(
|
|||||||
() => props.open,
|
() => props.open,
|
||||||
(newVal) => {
|
(newVal) => {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
activeTab.value = 'ai-config'
|
activeTab.value = 'settings' // 默认打开基础设置 Tab
|
||||||
// 刷新 AI 配置
|
// 刷新缓存管理
|
||||||
aiConfigRef.value?.refresh()
|
cacheManageRef.value?.refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,10 +4,6 @@ import { storeToRefs } from 'pinia'
|
|||||||
import { ref, onMounted, nextTick } from 'vue'
|
import { ref, onMounted, nextTick } from 'vue'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import type { AnalysisSession } from '@/types/chat'
|
import type { AnalysisSession } from '@/types/chat'
|
||||||
import pkg from '../../../package.json'
|
|
||||||
|
|
||||||
const { version } = pkg
|
|
||||||
|
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
import 'dayjs/locale/zh-cn'
|
import 'dayjs/locale/zh-cn'
|
||||||
@@ -331,11 +327,6 @@ function isPrivateChat(session: AnalysisSession): boolean {
|
|||||||
<span v-if="!isCollapsed" class="truncate">设置</span>
|
<span v-if="!isCollapsed" class="truncate">设置</span>
|
||||||
</UButton>
|
</UButton>
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
|
|
||||||
<!-- 版本号 & 社交链接 -->
|
|
||||||
<div v-if="!isCollapsed" class="flex items-center justify-center gap-2 py-1 text-xs text-gray-400">
|
|
||||||
<span>v{{ version }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -105,9 +105,9 @@ function isActivePreset(presetId: string, chatType: 'group' | 'private'): boolea
|
|||||||
<div class="mb-3 flex items-center justify-between">
|
<div class="mb-3 flex items-center justify-between">
|
||||||
<h4 class="flex items-center gap-2 text-sm font-semibold text-gray-900 dark:text-white">
|
<h4 class="flex items-center gap-2 text-sm font-semibold text-gray-900 dark:text-white">
|
||||||
<UIcon name="i-heroicons-chat-bubble-left-right" class="h-4 w-4 text-violet-500" />
|
<UIcon name="i-heroicons-chat-bubble-left-right" class="h-4 w-4 text-violet-500" />
|
||||||
群聊预设
|
群聊系统提示词
|
||||||
</h4>
|
</h4>
|
||||||
<UButton size="xs" variant="ghost" color="gray" @click="openAddModal('group')">
|
<UButton variant="ghost" color="gray" @click="openAddModal('group')">
|
||||||
<UIcon name="i-heroicons-plus" class="mr-1 h-3.5 w-3.5" />
|
<UIcon name="i-heroicons-plus" class="mr-1 h-3.5 w-3.5" />
|
||||||
添加
|
添加
|
||||||
</UButton>
|
</UButton>
|
||||||
@@ -141,17 +141,17 @@ function isActivePreset(presetId: string, chatType: 'group' | 'private'): boolea
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-sm font-medium text-gray-900 dark:text-white">{{ preset.name }}</span>
|
<span class="text-sm font-medium text-gray-900 dark:text-white">{{ preset.name }}</span>
|
||||||
<UBadge v-if="preset.isBuiltIn" color="gray" variant="soft" size="xs">内置</UBadge>
|
<UBadge v-if="preset.isBuiltIn" color="gray" variant="soft">内置</UBadge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 操作按钮 -->
|
||||||
<div class="flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100" @click.stop>
|
<div class="flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100" @click.stop>
|
||||||
<UButton size="xs" color="gray" variant="ghost" @click="openEditModal(preset)">
|
<UButton color="gray" variant="ghost" @click="openEditModal(preset)">
|
||||||
{{ preset.isBuiltIn ? '查看' : '编辑' }}
|
{{ preset.isBuiltIn ? '查看' : '编辑' }}
|
||||||
</UButton>
|
</UButton>
|
||||||
<UButton size="xs" color="gray" variant="ghost" @click="duplicatePreset(preset.id)">复制</UButton>
|
<UButton color="gray" variant="ghost" @click="duplicatePreset(preset.id)">复制</UButton>
|
||||||
<UButton v-if="!preset.isBuiltIn" size="xs" color="error" variant="ghost" @click="deletePreset(preset.id)">
|
<UButton v-if="!preset.isBuiltIn" color="error" variant="ghost" @click="deletePreset(preset.id)">
|
||||||
删除
|
删除
|
||||||
</UButton>
|
</UButton>
|
||||||
</div>
|
</div>
|
||||||
@@ -162,14 +162,14 @@ function isActivePreset(presetId: string, chatType: 'group' | 'private'): boolea
|
|||||||
<!-- 分隔线 -->
|
<!-- 分隔线 -->
|
||||||
<div class="border-t border-gray-200 dark:border-gray-700"></div>
|
<div class="border-t border-gray-200 dark:border-gray-700"></div>
|
||||||
|
|
||||||
<!-- 私聊预设组 -->
|
<!-- 私聊系统提示词 -->
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-3 flex items-center justify-between">
|
<div class="mb-3 flex items-center justify-between">
|
||||||
<h4 class="flex items-center gap-2 text-sm font-semibold text-gray-900 dark:text-white">
|
<h4 class="flex items-center gap-2 text-sm font-semibold text-gray-900 dark:text-white">
|
||||||
<UIcon name="i-heroicons-user" class="h-4 w-4 text-blue-500" />
|
<UIcon name="i-heroicons-user" class="h-4 w-4 text-blue-500" />
|
||||||
私聊预设
|
私聊系统提示词
|
||||||
</h4>
|
</h4>
|
||||||
<UButton size="xs" variant="ghost" color="gray" @click="openAddModal('private')">
|
<UButton variant="ghost" color="gray" @click="openAddModal('private')">
|
||||||
<UIcon name="i-heroicons-plus" class="mr-1 h-3.5 w-3.5" />
|
<UIcon name="i-heroicons-plus" class="mr-1 h-3.5 w-3.5" />
|
||||||
添加
|
添加
|
||||||
</UButton>
|
</UButton>
|
||||||
@@ -203,33 +203,23 @@ function isActivePreset(presetId: string, chatType: 'group' | 'private'): boolea
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-sm font-medium text-gray-900 dark:text-white">{{ preset.name }}</span>
|
<span class="text-sm font-medium text-gray-900 dark:text-white">{{ preset.name }}</span>
|
||||||
<UBadge v-if="preset.isBuiltIn" color="gray" variant="soft" size="xs">内置</UBadge>
|
<UBadge v-if="preset.isBuiltIn" color="gray" variant="soft">内置</UBadge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 操作按钮 -->
|
||||||
<div class="flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100" @click.stop>
|
<div class="flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100" @click.stop>
|
||||||
<UButton size="xs" color="gray" variant="ghost" @click="openEditModal(preset)">
|
<UButton color="gray" variant="ghost" @click="openEditModal(preset)">
|
||||||
{{ preset.isBuiltIn ? '查看' : '编辑' }}
|
{{ preset.isBuiltIn ? '查看' : '编辑' }}
|
||||||
</UButton>
|
</UButton>
|
||||||
<UButton size="xs" color="gray" variant="ghost" @click="duplicatePreset(preset.id)">复制</UButton>
|
<UButton color="gray" variant="ghost" @click="duplicatePreset(preset.id)">复制</UButton>
|
||||||
<UButton v-if="!preset.isBuiltIn" size="xs" color="error" variant="ghost" @click="deletePreset(preset.id)">
|
<UButton v-if="!preset.isBuiltIn" color="error" variant="ghost" @click="deletePreset(preset.id)">
|
||||||
删除
|
删除
|
||||||
</UButton>
|
</UButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 提示信息 -->
|
|
||||||
<div class="rounded-lg border border-gray-200 bg-gray-50 p-3 dark:border-gray-700 dark:bg-gray-800/50">
|
|
||||||
<div class="flex items-start gap-2">
|
|
||||||
<UIcon name="i-heroicons-light-bulb" class="mt-0.5 h-4 w-4 shrink-0 text-amber-500" />
|
|
||||||
<p class="text-xs text-gray-600 dark:text-gray-400">
|
|
||||||
点击预设即可激活。时间信息和工具说明由系统自动注入,你只需编辑角色定义和回答要求。
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑/添加弹窗 -->
|
<!-- 编辑/添加弹窗 -->
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ const formData = ref({
|
|||||||
const isBuiltIn = computed(() => props.preset?.isBuiltIn ?? false)
|
const isBuiltIn = computed(() => props.preset?.isBuiltIn ?? false)
|
||||||
const isEditMode = computed(() => props.mode === 'edit')
|
const isEditMode = computed(() => props.mode === 'edit')
|
||||||
const modalTitle = computed(() => {
|
const modalTitle = computed(() => {
|
||||||
if (isBuiltIn.value) return '查看预设'
|
if (isBuiltIn.value) return '查看系统提示词'
|
||||||
return isEditMode.value ? '编辑预设' : '添加预设'
|
return isEditMode.value ? '编辑系统提示词' : '添加系统提示词'
|
||||||
})
|
})
|
||||||
|
|
||||||
const canSave = computed(() => {
|
const canSave = computed(() => {
|
||||||
@@ -149,7 +149,7 @@ function getLockedPromptSection(chatType: string): string {
|
|||||||
- 当用户提到"对方"、"他/她"时,通过 get_group_members 获取另一方信息`
|
- 当用户提到"对方"、"他/她"时,通过 get_group_members 获取另一方信息`
|
||||||
: `成员查询策略:
|
: `成员查询策略:
|
||||||
- 当用户提到特定群成员(如"张三说过什么"、"小明的发言"等)时,应先调用 get_group_members 获取成员列表
|
- 当用户提到特定群成员(如"张三说过什么"、"小明的发言"等)时,应先调用 get_group_members 获取成员列表
|
||||||
- 群成员有三种名称:accountName(QQ原始昵称)、groupNickname(群昵称)、aliases(用户自定义别名)
|
- 群成员有三种名称:accountName(原始昵称)、groupNickname(群昵称)、aliases(用户自定义别名)
|
||||||
- 通过 get_group_members 的 search 参数可以模糊搜索这三种名称
|
- 通过 get_group_members 的 search 参数可以模糊搜索这三种名称
|
||||||
- 找到成员后,使用其 id 字段作为 search_messages 的 sender_id 参数来获取该成员的发言`
|
- 找到成员后,使用其 id 字段作为 search_messages 的 sender_id 参数来获取该成员的发言`
|
||||||
|
|
||||||
@@ -213,12 +213,15 @@ ${formData.value.responseRules}`
|
|||||||
<!-- 预设名称 -->
|
<!-- 预设名称 -->
|
||||||
<div>
|
<div>
|
||||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-300">预设名称</label>
|
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-300">预设名称</label>
|
||||||
<UInput v-model="formData.name" placeholder="为预设起个名字" :disabled="isBuiltIn" />
|
<UInput v-model="formData.name" placeholder="为预设起个名字" :disabled="isBuiltIn" class="w-60" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 适用类型(只读显示) -->
|
<!-- 适用类型(只读显示) -->
|
||||||
<div class="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400">
|
<div class="flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400">
|
||||||
<UIcon :name="formData.chatType === 'group' ? 'i-heroicons-chat-bubble-left-right' : 'i-heroicons-user'" class="h-4 w-4" />
|
<UIcon
|
||||||
|
:name="formData.chatType === 'group' ? 'i-heroicons-chat-bubble-left-right' : 'i-heroicons-user'"
|
||||||
|
class="h-4 w-4"
|
||||||
|
/>
|
||||||
<span>适用于{{ formData.chatType === 'group' ? '群聊' : '私聊' }}</span>
|
<span>适用于{{ formData.chatType === 'group' ? '群聊' : '私聊' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -227,10 +230,10 @@ ${formData.value.responseRules}`
|
|||||||
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-300">角色定义</label>
|
<label class="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-300">角色定义</label>
|
||||||
<UTextarea
|
<UTextarea
|
||||||
v-model="formData.roleDefinition"
|
v-model="formData.roleDefinition"
|
||||||
:rows="5"
|
:rows="8"
|
||||||
placeholder="定义 AI 助手的角色和任务..."
|
placeholder="定义 AI 助手的角色和任务..."
|
||||||
:disabled="isBuiltIn"
|
:disabled="isBuiltIn"
|
||||||
class="font-mono text-sm"
|
class="font-mono text-sm w-120"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -245,7 +248,7 @@ ${formData.value.responseRules}`
|
|||||||
:rows="5"
|
:rows="5"
|
||||||
placeholder="定义 AI 回答的格式和要求..."
|
placeholder="定义 AI 回答的格式和要求..."
|
||||||
:disabled="isBuiltIn"
|
:disabled="isBuiltIn"
|
||||||
class="font-mono text-sm"
|
class="font-mono text-sm w-120"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -256,7 +259,7 @@ ${formData.value.responseRules}`
|
|||||||
完整提示词预览
|
完整提示词预览
|
||||||
</label>
|
</label>
|
||||||
<div class="rounded-lg border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-800/50">
|
<div class="rounded-lg border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-800/50">
|
||||||
<pre class="max-h-48 overflow-y-auto whitespace-pre-wrap text-xs text-gray-700 dark:text-gray-300">{{ previewContent }}</pre>
|
<pre class="whitespace-pre-wrap text-sm text-gray-700 dark:text-gray-300">{{ previewContent }}</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -272,4 +275,3 @@ ${formData.value.responseRules}`
|
|||||||
</template>
|
</template>
|
||||||
</UModal>
|
</UModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -243,6 +243,12 @@ export function useAIChat(
|
|||||||
maxMessagesLimit: aiGlobalSettings.value.maxMessagesPerRequest,
|
maxMessagesLimit: aiGlobalSettings.value.maxMessagesPerRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('[AI] 构建 context:', {
|
||||||
|
sessionId,
|
||||||
|
maxMessagesLimit: context.maxMessagesLimit,
|
||||||
|
aiGlobalSettings: aiGlobalSettings.value,
|
||||||
|
})
|
||||||
|
|
||||||
// 收集历史消息(排除当前用户消息和 AI 占位消息)
|
// 收集历史消息(排除当前用户消息和 AI 占位消息)
|
||||||
const historyMessages = messages.value
|
const historyMessages = messages.value
|
||||||
.slice(0, -2) // 排除刚添加的用户消息和 AI 占位消息
|
.slice(0, -2) // 排除刚添加的用户消息和 AI 占位消息
|
||||||
@@ -270,7 +276,10 @@ export function useAIChat(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[AI] Agent chunk:', chunk.type, chunk.toolName || chunk.content?.slice(0, 50))
|
// 只在工具调用时记录,减少日志噪音
|
||||||
|
if (chunk.type === 'tool_start' || chunk.type === 'tool_result') {
|
||||||
|
console.log('[AI] Agent chunk:', chunk.type, chunk.toolName)
|
||||||
|
}
|
||||||
|
|
||||||
switch (chunk.type) {
|
switch (chunk.type) {
|
||||||
case 'content':
|
case 'content':
|
||||||
|
|||||||
@@ -0,0 +1,280 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
// Props
|
||||||
|
defineProps<{
|
||||||
|
open: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// Emits
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:open': [value: boolean]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// 通用格式弹窗状态
|
||||||
|
const showFormatModal = ref(false)
|
||||||
|
|
||||||
|
// 复制格式示例
|
||||||
|
const formatExample = `{
|
||||||
|
"chatlab": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"exportedAt": 1732924800,
|
||||||
|
"generator": "Your Tool Name"
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"name": "群聊名称",
|
||||||
|
"platform": "qq",
|
||||||
|
"type": "group"
|
||||||
|
},
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"platformId": "123456789",
|
||||||
|
"accountName": "用户昵称",
|
||||||
|
"groupNickname": "群昵称(可选)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"sender": "123456789",
|
||||||
|
"accountName": "发送时昵称",
|
||||||
|
"timestamp": 1732924800,
|
||||||
|
"type": 0,
|
||||||
|
"content": "消息内容"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
|
||||||
|
function copyFormatExample() {
|
||||||
|
navigator.clipboard.writeText(formatExample)
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
emit('update:open', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function openExternalLink(url: string) {
|
||||||
|
window.open(url, '_blank')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- 导入教程弹窗 -->
|
||||||
|
<UModal :open="open" @update:open="emit('update:open', $event)" :ui="{ content: 'md:w-full max-w-2xl' }">
|
||||||
|
<template #content>
|
||||||
|
<div class="p-6">
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="mb-6 flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div
|
||||||
|
class="flex h-10 w-10 items-center justify-center rounded-xl bg-linear-to-br from-pink-100 to-rose-100 dark:from-pink-900/30 dark:to-rose-900/30"
|
||||||
|
>
|
||||||
|
<UIcon name="i-heroicons-book-open" class="h-5 w-5 text-pink-600 dark:text-pink-400" />
|
||||||
|
</div>
|
||||||
|
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">聊天记录导入教程</h2>
|
||||||
|
</div>
|
||||||
|
<UButton icon="i-heroicons-x-mark" variant="ghost" size="sm" @click="closeModal" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 教程内容 -->
|
||||||
|
<div class="space-y-6">
|
||||||
|
<!-- QQ 教程 -->
|
||||||
|
<div class="rounded-xl border border-gray-200 p-4 dark:border-gray-700">
|
||||||
|
<div class="mb-3 flex items-center gap-2">
|
||||||
|
<UIcon name="i-heroicons-chat-bubble-left-right" class="h-5 w-5 text-pink-500" />
|
||||||
|
<h3 class="font-semibold text-gray-900 dark:text-white">QQ</h3>
|
||||||
|
</div>
|
||||||
|
<ol class="space-y-2">
|
||||||
|
<li class="flex items-start gap-3 text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
<span
|
||||||
|
class="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-pink-100 text-xs font-medium text-pink-600 dark:bg-pink-900/30 dark:text-pink-400"
|
||||||
|
>
|
||||||
|
1
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
使用
|
||||||
|
<a
|
||||||
|
class="cursor-pointer text-pink-600 underline underline-offset-2 hover:text-pink-700 dark:text-pink-400 dark:hover:text-pink-300"
|
||||||
|
@click="openExternalLink('https://github.com/shuakami/qq-chat-exporter')"
|
||||||
|
>
|
||||||
|
qq-chat-exporter
|
||||||
|
<UIcon name="i-heroicons-arrow-top-right-on-square" class="inline h-3 w-3" />
|
||||||
|
</a>
|
||||||
|
导出聊天记录(目前仅支持Windows/Linux)
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start gap-3 text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
<span
|
||||||
|
class="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-pink-100 text-xs font-medium text-pink-600 dark:bg-pink-900/30 dark:text-pink-400"
|
||||||
|
>
|
||||||
|
2
|
||||||
|
</span>
|
||||||
|
<span>导出完成后会得到 .json 文件</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start gap-3 text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
<span
|
||||||
|
class="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-pink-100 text-xs font-medium text-pink-600 dark:bg-pink-900/30 dark:text-pink-400"
|
||||||
|
>
|
||||||
|
3
|
||||||
|
</span>
|
||||||
|
<span>将 .json 文件拖拽到上方导入区域</span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 微信教程 -->
|
||||||
|
<div class="rounded-xl border border-gray-200 p-4 dark:border-gray-700">
|
||||||
|
<div class="mb-3 flex items-center gap-2">
|
||||||
|
<UIcon name="i-heroicons-device-phone-mobile" class="h-5 w-5 text-blue-500" />
|
||||||
|
<h3 class="font-semibold text-gray-900 dark:text-white">微信</h3>
|
||||||
|
</div>
|
||||||
|
<ol class="mb-4 space-y-2">
|
||||||
|
<li class="flex items-start gap-3 text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
<span
|
||||||
|
class="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-blue-100 text-xs font-medium text-blue-600 dark:bg-blue-900/30 dark:text-blue-400"
|
||||||
|
>
|
||||||
|
1
|
||||||
|
</span>
|
||||||
|
<span>网络上工具较多,请自行找工具导出聊天记录</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start gap-3 text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
<span
|
||||||
|
class="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-blue-100 text-xs font-medium text-blue-600 dark:bg-blue-900/30 dark:text-blue-400"
|
||||||
|
>
|
||||||
|
2
|
||||||
|
</span>
|
||||||
|
<span>使用脚本,将导出文件转换为 ChatLab 通用格式</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-start gap-3 text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
<span
|
||||||
|
class="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-blue-100 text-xs font-medium text-blue-600 dark:bg-blue-900/30 dark:text-blue-400"
|
||||||
|
>
|
||||||
|
3
|
||||||
|
</span>
|
||||||
|
<span>将转换后的 .json 文件拖拽到上方导入区域</span>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<UButton
|
||||||
|
variant="soft"
|
||||||
|
size="sm"
|
||||||
|
:trailing-icon="'i-heroicons-document-text'"
|
||||||
|
@click="showFormatModal = true"
|
||||||
|
>
|
||||||
|
查看通用格式说明
|
||||||
|
</UButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部提示 -->
|
||||||
|
<div class="mt-6 rounded-lg bg-gray-50 p-4 dark:bg-gray-800/50">
|
||||||
|
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
💡 提示:ChatLab 支持多种聊天记录格式,包括 QQ、微信、Discord
|
||||||
|
等平台。将导出的文件直接拖拽到导入区域即可开始分析。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UModal>
|
||||||
|
|
||||||
|
<!-- 通用格式说明弹窗(层级高于教程弹窗) -->
|
||||||
|
<UModal v-model:open="showFormatModal" :ui="{ content: 'md:w-full max-w-3xl z-[60]', overlay: 'z-[60]' }">
|
||||||
|
<template #content>
|
||||||
|
<div class="p-6">
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="mb-6 flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div
|
||||||
|
class="flex h-10 w-10 items-center justify-center rounded-xl bg-linear-to-br from-blue-100 to-indigo-100 dark:from-blue-900/30 dark:to-indigo-900/30"
|
||||||
|
>
|
||||||
|
<UIcon name="i-heroicons-document-text" class="h-5 w-5 text-blue-600 dark:text-blue-400" />
|
||||||
|
</div>
|
||||||
|
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">ChatLab 通用格式说明</h2>
|
||||||
|
</div>
|
||||||
|
<UButton icon="i-heroicons-x-mark" variant="ghost" size="sm" @click="showFormatModal = false" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 格式说明 -->
|
||||||
|
<div class="space-y-4">
|
||||||
|
<p class="text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
ChatLab 定义了一套聊天记录分析用标准 JSON 格式。只需在 JSON 文件中包含
|
||||||
|
<code class="rounded bg-gray-100 px-1.5 py-0.5 text-pink-600 dark:bg-gray-800 dark:text-pink-400">
|
||||||
|
chatlab
|
||||||
|
</code>
|
||||||
|
对象即可被识别。
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- JSON 示例 -->
|
||||||
|
<div class="rounded-xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-800/50">
|
||||||
|
<div class="mb-2 flex items-center justify-between">
|
||||||
|
<span class="text-xs font-medium text-gray-500 dark:text-gray-400">示例格式</span>
|
||||||
|
<UButton variant="ghost" size="xs" icon="i-heroicons-clipboard-document" @click="copyFormatExample">
|
||||||
|
复制
|
||||||
|
</UButton>
|
||||||
|
</div>
|
||||||
|
<pre class="overflow-x-auto text-xs leading-relaxed text-gray-700 dark:text-gray-300"><code>{
|
||||||
|
"chatlab": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"exportedAt": 1732924800,
|
||||||
|
"generator": "Your Tool Name"
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"name": "群聊名称",
|
||||||
|
"platform": "qq", // qq | wechat | telegram | discord 等
|
||||||
|
"type": "group" // group | private (群聊|私聊)
|
||||||
|
},
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"platformId": "123456789",
|
||||||
|
"accountName": "用户昵称",
|
||||||
|
"groupNickname": "群昵称(可选)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"sender": "123456789",
|
||||||
|
"accountName": "发送时昵称",
|
||||||
|
"timestamp": 1732924800, // 秒级时间戳
|
||||||
|
"type": 0, // 0=文本 1=图片 2=语音 3=视频
|
||||||
|
"content": "消息内容"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}</code></pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 字段说明 -->
|
||||||
|
<div class="rounded-xl border border-gray-200 bg-white p-4 dark:border-gray-700 dark:bg-gray-800">
|
||||||
|
<h3 class="mb-3 text-sm font-semibold text-gray-900 dark:text-white">消息类型说明</h3>
|
||||||
|
<div class="grid grid-cols-2 gap-2 text-xs sm:grid-cols-4">
|
||||||
|
<div class="rounded-lg bg-gray-50 p-2 dark:bg-gray-700">
|
||||||
|
<span class="font-mono text-pink-600 dark:text-pink-400">0</span>
|
||||||
|
<span class="ml-2 text-gray-600 dark:text-gray-300">文本</span>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-lg bg-gray-50 p-2 dark:bg-gray-700">
|
||||||
|
<span class="font-mono text-pink-600 dark:text-pink-400">1</span>
|
||||||
|
<span class="ml-2 text-gray-600 dark:text-gray-300">图片</span>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-lg bg-gray-50 p-2 dark:bg-gray-700">
|
||||||
|
<span class="font-mono text-pink-600 dark:text-pink-400">2</span>
|
||||||
|
<span class="ml-2 text-gray-600 dark:text-gray-300">语音</span>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-lg bg-gray-50 p-2 dark:bg-gray-700">
|
||||||
|
<span class="font-mono text-pink-600 dark:text-pink-400">3</span>
|
||||||
|
<span class="ml-2 text-gray-600 dark:text-gray-300">视频</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部提示 -->
|
||||||
|
<div class="mt-6 rounded-lg bg-blue-50 p-4 dark:bg-blue-900/20">
|
||||||
|
<p class="text-sm text-blue-600 dark:text-blue-400">
|
||||||
|
💡 文件名只需以
|
||||||
|
<code class="rounded bg-blue-100 px-1 dark:bg-blue-800">.json</code>
|
||||||
|
结尾,JSON 中包含
|
||||||
|
<code class="rounded bg-blue-100 px-1 dark:bg-blue-800">chatlab</code>
|
||||||
|
对象即可被识别。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UModal>
|
||||||
|
</template>
|
||||||
@@ -4,13 +4,13 @@ import { FileDropZone } from '@/components/UI'
|
|||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import ImportTutorialModal from './components/ImportTutorialModal.vue'
|
||||||
|
|
||||||
const chatStore = useChatStore()
|
const chatStore = useChatStore()
|
||||||
const { isImporting, importProgress } = storeToRefs(chatStore)
|
const { isImporting, importProgress } = storeToRefs(chatStore)
|
||||||
|
|
||||||
const importError = ref<string | null>(null)
|
const importError = ref<string | null>(null)
|
||||||
const showTutorialModal = ref(false)
|
const showTutorialModal = ref(false)
|
||||||
const showFormatModal = ref(false)
|
|
||||||
|
|
||||||
const features = [
|
const features = [
|
||||||
{
|
{
|
||||||
@@ -85,71 +85,10 @@ async function handleFileDrop({ paths }: { files: File[]; paths: string[] }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 教程 Accordion 数据
|
|
||||||
const tutorialItems = [
|
|
||||||
{
|
|
||||||
value: 'qq',
|
|
||||||
label: 'QQ',
|
|
||||||
icon: 'i-heroicons-chat-bubble-left-right',
|
|
||||||
steps: [
|
|
||||||
'使用 qq-chat-exporter 导出聊天记录(推荐最新版)',
|
|
||||||
'导出完成后会得到 .json 文件',
|
|
||||||
'将 .json 文件拖拽到上方导入区域',
|
|
||||||
],
|
|
||||||
link: 'https://github.com/shuakami/qq-chat-exporter',
|
|
||||||
hasExternalLink: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'other',
|
|
||||||
label: '其他平台',
|
|
||||||
icon: 'i-heroicons-device-phone-mobile',
|
|
||||||
steps: ['使用任意工具导出聊天记录', '将导出文件转换为 ChatLab 通用格式', '将转换后的 .json 文件拖拽到上方导入区域'],
|
|
||||||
hasExternalLink: false,
|
|
||||||
showFormatButton: true,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
// 默认展开所有项
|
|
||||||
const tutorialDefaultValue = tutorialItems.map((item) => item.value)
|
|
||||||
|
|
||||||
function openTutorial() {
|
function openTutorial() {
|
||||||
showTutorialModal.value = true
|
showTutorialModal.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 复制格式示例
|
|
||||||
const formatExample = `{
|
|
||||||
"chatlab": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"exportedAt": 1732924800,
|
|
||||||
"generator": "Your Tool Name"
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"name": "群聊名称",
|
|
||||||
"platform": "qq",
|
|
||||||
"type": "group"
|
|
||||||
},
|
|
||||||
"members": [
|
|
||||||
{
|
|
||||||
"platformId": "123456789",
|
|
||||||
"accountName": "用户昵称",
|
|
||||||
"groupNickname": "群昵称(可选)"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"messages": [
|
|
||||||
{
|
|
||||||
"sender": "123456789",
|
|
||||||
"accountName": "发送时昵称",
|
|
||||||
"timestamp": 1732924800,
|
|
||||||
"type": 0,
|
|
||||||
"content": "消息内容"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`
|
|
||||||
|
|
||||||
function copyFormatExample() {
|
|
||||||
window.electron.copyToClipboard(formatExample)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getProgressText(): string {
|
function getProgressText(): string {
|
||||||
if (!importProgress.value) return ''
|
if (!importProgress.value) return ''
|
||||||
switch (importProgress.value.stage) {
|
switch (importProgress.value.stage) {
|
||||||
@@ -324,176 +263,7 @@ function getProgressDetail(): string {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 通用格式说明弹窗(层级高于教程弹窗) -->
|
|
||||||
<UModal v-model:open="showFormatModal" :ui="{ content: 'md:w-full max-w-3xl z-[60]', overlay: 'z-[60]' }">
|
|
||||||
<template #content>
|
|
||||||
<div class="p-6">
|
|
||||||
<!-- Header -->
|
|
||||||
<div class="mb-6 flex items-center justify-between">
|
|
||||||
<div class="flex items-center gap-3">
|
|
||||||
<div
|
|
||||||
class="flex h-10 w-10 items-center justify-center rounded-xl bg-linear-to-br from-blue-100 to-indigo-100 dark:from-blue-900/30 dark:to-indigo-900/30"
|
|
||||||
>
|
|
||||||
<UIcon name="i-heroicons-document-text" class="h-5 w-5 text-blue-600 dark:text-blue-400" />
|
|
||||||
</div>
|
|
||||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">ChatLab 通用格式说明</h2>
|
|
||||||
</div>
|
|
||||||
<UButton icon="i-heroicons-x-mark" variant="ghost" size="sm" @click="showFormatModal = false" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 格式说明 -->
|
|
||||||
<div class="space-y-4">
|
|
||||||
<p class="text-sm text-gray-600 dark:text-gray-300">
|
|
||||||
ChatLab 支持通用的 JSON 格式。只需在 JSON 文件中包含
|
|
||||||
<code class="rounded bg-gray-100 px-1.5 py-0.5 text-pink-600 dark:bg-gray-800 dark:text-pink-400">
|
|
||||||
chatlab
|
|
||||||
</code>
|
|
||||||
对象即可被识别。
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<!-- JSON 示例 -->
|
|
||||||
<div class="rounded-xl border border-gray-200 bg-gray-50 p-4 dark:border-gray-700 dark:bg-gray-800/50">
|
|
||||||
<div class="mb-2 flex items-center justify-between">
|
|
||||||
<span class="text-xs font-medium text-gray-500 dark:text-gray-400">示例格式</span>
|
|
||||||
<UButton variant="ghost" size="xs" icon="i-heroicons-clipboard-document" @click="copyFormatExample">
|
|
||||||
复制
|
|
||||||
</UButton>
|
|
||||||
</div>
|
|
||||||
<pre class="overflow-x-auto text-xs leading-relaxed text-gray-700 dark:text-gray-300"><code>{
|
|
||||||
"chatlab": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"exportedAt": 1732924800,
|
|
||||||
"generator": "Your Tool Name"
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"name": "群聊名称",
|
|
||||||
"platform": "qq", // qq | wechat | telegram | discord 等
|
|
||||||
"type": "group" // group | private (群聊|私聊)
|
|
||||||
},
|
|
||||||
"members": [
|
|
||||||
{
|
|
||||||
"platformId": "123456789",
|
|
||||||
"accountName": "用户昵称",
|
|
||||||
"groupNickname": "群昵称(可选)"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"messages": [
|
|
||||||
{
|
|
||||||
"sender": "123456789",
|
|
||||||
"accountName": "发送时昵称",
|
|
||||||
"timestamp": 1732924800, // 秒级时间戳
|
|
||||||
"type": 0, // 0=文本 1=图片 2=语音 3=视频
|
|
||||||
"content": "消息内容"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}</code></pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 字段说明 -->
|
|
||||||
<div class="rounded-xl border border-gray-200 bg-white p-4 dark:border-gray-700 dark:bg-gray-800">
|
|
||||||
<h3 class="mb-3 text-sm font-semibold text-gray-900 dark:text-white">消息类型说明</h3>
|
|
||||||
<div class="grid grid-cols-2 gap-2 text-xs sm:grid-cols-4">
|
|
||||||
<div class="rounded-lg bg-gray-50 p-2 dark:bg-gray-700">
|
|
||||||
<span class="font-mono text-pink-600 dark:text-pink-400">0</span>
|
|
||||||
<span class="ml-2 text-gray-600 dark:text-gray-300">文本</span>
|
|
||||||
</div>
|
|
||||||
<div class="rounded-lg bg-gray-50 p-2 dark:bg-gray-700">
|
|
||||||
<span class="font-mono text-pink-600 dark:text-pink-400">1</span>
|
|
||||||
<span class="ml-2 text-gray-600 dark:text-gray-300">图片</span>
|
|
||||||
</div>
|
|
||||||
<div class="rounded-lg bg-gray-50 p-2 dark:bg-gray-700">
|
|
||||||
<span class="font-mono text-pink-600 dark:text-pink-400">2</span>
|
|
||||||
<span class="ml-2 text-gray-600 dark:text-gray-300">语音</span>
|
|
||||||
</div>
|
|
||||||
<div class="rounded-lg bg-gray-50 p-2 dark:bg-gray-700">
|
|
||||||
<span class="font-mono text-pink-600 dark:text-pink-400">3</span>
|
|
||||||
<span class="ml-2 text-gray-600 dark:text-gray-300">视频</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 底部提示 -->
|
|
||||||
<div class="mt-6 rounded-lg bg-blue-50 p-4 dark:bg-blue-900/20">
|
|
||||||
<p class="text-sm text-blue-600 dark:text-blue-400">
|
|
||||||
💡 文件名只需以
|
|
||||||
<code class="rounded bg-blue-100 px-1 dark:bg-blue-800">.json</code>
|
|
||||||
结尾,JSON 中包含
|
|
||||||
<code class="rounded bg-blue-100 px-1 dark:bg-blue-800">chatlab</code>
|
|
||||||
对象即可被识别。
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</UModal>
|
|
||||||
|
|
||||||
<!-- 导入教程弹窗 -->
|
<!-- 导入教程弹窗 -->
|
||||||
<UModal v-model:open="showTutorialModal" :ui="{ content: 'md:w-full max-w-2xl' }">
|
<ImportTutorialModal v-model:open="showTutorialModal" />
|
||||||
<template #content>
|
|
||||||
<div class="p-6">
|
|
||||||
<!-- Header -->
|
|
||||||
<div class="mb-6 flex items-center justify-between">
|
|
||||||
<div class="flex items-center gap-3">
|
|
||||||
<div
|
|
||||||
class="flex h-10 w-10 items-center justify-center rounded-xl bg-linear-to-br from-pink-100 to-rose-100 dark:from-pink-900/30 dark:to-rose-900/30"
|
|
||||||
>
|
|
||||||
<UIcon name="i-heroicons-book-open" class="h-5 w-5 text-pink-600 dark:text-pink-400" />
|
|
||||||
</div>
|
|
||||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">聊天记录导入教程</h2>
|
|
||||||
</div>
|
|
||||||
<UButton icon="i-heroicons-x-mark" variant="ghost" size="sm" @click="showTutorialModal = false" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 教程内容 - 使用 Accordion -->
|
|
||||||
<UAccordion type="multiple" :default-value="tutorialDefaultValue" :items="tutorialItems">
|
|
||||||
<template #body="{ item }">
|
|
||||||
<!-- 步骤列表 -->
|
|
||||||
<ol class="mb-4 space-y-2">
|
|
||||||
<li
|
|
||||||
v-for="(step, index) in item.steps"
|
|
||||||
:key="index"
|
|
||||||
class="flex items-start gap-3 text-sm text-gray-600 dark:text-gray-300"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-pink-100 text-xs font-medium text-pink-600 dark:bg-pink-900/30 dark:text-pink-400"
|
|
||||||
>
|
|
||||||
{{ index + 1 }}
|
|
||||||
</span>
|
|
||||||
<span>{{ step }}</span>
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<!-- 工具链接 / 格式说明按钮 -->
|
|
||||||
<UButton
|
|
||||||
v-if="item.hasExternalLink"
|
|
||||||
variant="soft"
|
|
||||||
size="sm"
|
|
||||||
:trailing-icon="'i-heroicons-arrow-top-right-on-square'"
|
|
||||||
@click="window.electron.openExternal(item.link)"
|
|
||||||
>
|
|
||||||
查看导出工具
|
|
||||||
</UButton>
|
|
||||||
<UButton
|
|
||||||
v-if="item.showFormatButton"
|
|
||||||
variant="soft"
|
|
||||||
size="sm"
|
|
||||||
:trailing-icon="'i-heroicons-document-text'"
|
|
||||||
@click="showFormatModal = true"
|
|
||||||
>
|
|
||||||
查看通用格式说明
|
|
||||||
</UButton>
|
|
||||||
</template>
|
|
||||||
</UAccordion>
|
|
||||||
|
|
||||||
<!-- 底部提示 -->
|
|
||||||
<div class="mt-6 rounded-lg bg-gray-50 p-4 dark:bg-gray-800/50">
|
|
||||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
|
||||||
💡 提示:ChatLab 支持多种聊天记录格式,包括 QQ、微信、Discord
|
|
||||||
等平台。将导出的文件直接拖拽到导入区域即可开始分析。
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</UModal>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
+1
-1
@@ -5,7 +5,7 @@ export const router = createRouter({
|
|||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'home',
|
name: 'home',
|
||||||
component: () => import('@/pages/index.vue'),
|
component: () => import('@/pages/home/index.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/group-chat/:id',
|
path: '/group-chat/:id',
|
||||||
|
|||||||
Reference in New Issue
Block a user