refactor(ai): remove unused data source panel

This commit is contained in:
digua committed 2026-09-28 14:53:15 +08:00
1 parent 245a99b8ff
commit 23f77f9a89
9 files changed
+1 -252

No files matched your search

-39
View File
@@ -4,7 +4,6 @@ import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
import { useToast } from '@/composables/useToast'
import ConversationList from './chat/ConversationList.vue'
import DataSourcePanel from './chat/DataSourcePanel.vue'
import ChatMessage from './chat/ChatMessage.vue'
import AIChatInput from './input/AIChatInput.vue'
import AIThinkingIndicator from './chat/AIThinkingIndicator.vue'
@@ -53,9 +52,6 @@ const initialAIChatId = typeof route.query.aiChatId === 'string' ? route.query.a
const {
initialization,
messages,
sourceMessages,
currentKeywords,
isLoadingSource,
isAIThinking,
currentAIChatId,
currentToolStatus,
@@ -190,7 +186,6 @@ const currentPresetQuestions = computed(() => {
const currentChatType = computed(() => props.chatType ?? 'group')
// UI 状态
const isSourcePanelCollapsed = ref(false)
const hasLLMConfig = ref(false)
const isCheckingConfig = ref(true)
const configModalScrollToSection = ref<string | undefined>(undefined)
@@ -419,11 +414,6 @@ async function handleForkAIChat(messageId: string) {
}
}
// 切换数据源面板
function toggleSourcePanel() {
isSourcePanelCollapsed.value = !isSourcePanelCollapsed.value
}
// 选择对话
async function handleSelectAIChat(convId: string) {
await loadAIChat(convId)
@@ -686,23 +676,6 @@ watch(
</div>
</div>
</div>
<!-- 右侧:数据源面板 -->
<Transition name="slide-fade">
<div
v-if="sourceMessages.length > 0 && !isSourcePanelCollapsed"
class="w-80 shrink-0 border-l border-gray-200 bg-gray-50/50 p-4 dark:border-gray-800 dark:bg-page-dark/50"
>
<DataSourcePanel
:messages="sourceMessages"
:keywords="currentKeywords"
:is-loading="isLoadingSource"
:is-collapsed="isSourcePanelCollapsed"
class="h-full"
@toggle="toggleSourcePanel"
/>
</div>
</Transition>
</div>
<!-- 助手配置弹窗 -->
@@ -754,18 +727,6 @@ watch(
</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;
}
/* Transition styles for slide-up (status bar) */
.slide-up-enter-active,
.slide-up-leave-active {
@@ -1,147 +0,0 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import dayjs from 'dayjs'
const { t } = useI18n()
// Props
const props = defineProps<{
messages: Array<{
id: number
senderName: string
content: string
timestamp: number
}>
keywords: string[]
isLoading: boolean
isCollapsed: boolean
}>()
// Emits
const emit = defineEmits<{
toggle: []
}>()
// 格式化时间
function formatTime(timestamp: number): string {
return dayjs(timestamp).format('MM-DD HH:mm')
}
// 高亮关键词
function highlightKeywords(text: string): string {
if (!props.keywords.length) return text
const pattern = props.keywords.join('|')
const regex = new RegExp(`(${pattern})`, 'gi')
return text.replace(regex, '<mark class="bg-yellow-200 dark:bg-yellow-800/50 px-0.5 rounded">$1</mark>')
}
</script>
<template>
<div
class="relative flex flex-col rounded-xl bg-white transition-all duration-300 dark:bg-page-dark"
:class="[isCollapsed ? 'w-12' : 'w-full']"
>
<!-- 折叠状态 -->
<template v-if="isCollapsed">
<button
class="flex h-full flex-col items-center justify-center gap-2 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
@click="emit('toggle')"
>
<UIcon name="i-heroicons-chevron-left" class="h-5 w-5" />
<span class="writing-vertical text-xs">{{ t('ai.chat.dataSource.title') }}</span>
</button>
</template>
<!-- 展开状态 -->
<template v-else>
<!-- 头部 -->
<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">
<UIcon name="i-heroicons-document-magnifying-glass" class="h-5 w-5 text-gray-500" />
<span class="font-medium text-gray-900 dark:text-white">{{ t('ai.chat.dataSource.title') }}</span>
</div>
<UButton icon="i-heroicons-chevron-right" color="gray" variant="ghost" size="xs" @click="emit('toggle')" />
</div>
<!-- 当前关键词 -->
<div v-if="keywords.length > 0" class="border-b border-gray-200 px-4 py-2 dark:border-gray-800">
<div class="flex flex-wrap items-center gap-2">
<span class="text-xs text-gray-500">{{ t('ai.chat.dataSource.keywords') }}</span>
<span
v-for="kw in keywords"
:key="kw"
class="rounded-full bg-violet-100 px-2 py-0.5 text-xs text-violet-700 dark:bg-violet-900/30 dark:text-violet-400"
>
{{ kw }}
</span>
</div>
</div>
<!-- 消息列表 -->
<div class="flex-1 overflow-y-auto">
<!-- 加载中 -->
<div v-if="isLoading" class="flex items-center justify-center py-8">
<UIcon name="i-heroicons-arrow-path" class="h-6 w-6 animate-spin text-gray-400" />
</div>
<!-- 空状态 -->
<div v-else-if="messages.length === 0" class="flex flex-col items-center justify-center py-8 text-center">
<UIcon name="i-heroicons-inbox" class="h-10 w-10 text-gray-300 dark:text-gray-600" />
<p class="mt-2 text-sm text-gray-500">{{ t('ai.chat.dataSource.empty') }}</p>
<p class="text-xs text-gray-400">{{ t('ai.chat.dataSource.emptyHint') }}</p>
</div>
<!-- 消息列表 -->
<div v-else class="divide-y divide-gray-100 dark:divide-gray-800">
<div
v-for="msg in messages"
:key="msg.id"
class="px-4 py-3 transition-colors hover:bg-gray-50 dark:hover:bg-gray-800/50"
>
<div class="mb-1 flex items-center justify-between">
<span class="text-sm font-medium text-gray-900 dark:text-white">
{{ msg.senderName }}
</span>
<span class="text-xs text-gray-400">{{ formatTime(msg.timestamp) }}</span>
</div>
<p class="line-clamp-3 text-sm text-gray-600 dark:text-gray-400" v-html="highlightKeywords(msg.content)" />
</div>
</div>
</div>
<!-- 底部统计 -->
<div v-if="messages.length > 0" class="border-t border-gray-200 px-4 py-2 dark:border-gray-800">
<span class="text-xs text-gray-500">
{{ t('ai.chat.dataSource.totalRecords', { count: messages.length }) }}
</span>
</div>
</template>
<!-- 半透明数据流背景效果 -->
<div v-if="!isCollapsed" class="pointer-events-none absolute inset-0 overflow-hidden rounded-xl opacity-[0.03]">
<div class="data-flow-bg absolute inset-0" />
</div>
</div>
</template>
<style scoped>
.writing-vertical {
writing-mode: vertical-rl;
text-orientation: mixed;
}
/* 数据流背景动画 */
.data-flow-bg {
background: repeating-linear-gradient(0deg, transparent, transparent 20px, currentColor 20px, currentColor 21px);
animation: dataFlow 20s linear infinite;
}
@keyframes dataFlow {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-100px);
}
}
</style>
+1 -1
View File
@@ -2,7 +2,7 @@
* Composables 统一导出
*/
export { usePageAnchors, type AnchorItem } from './usePageAnchors'
export { useAIChat, type ChatMessage, type SourceMessage } from './useAIChat'
export { useAIChat, type ChatMessage } from './useAIChat'
export { useToast } from './useToast'
export { useScreenCapture, type ScreenCaptureOptions } from './useScreenCapture'
export { useTimeSelect } from './useTimeSelect'
-5
View File
@@ -7,7 +7,6 @@ import { toRef } from 'vue'
import { useAIChatStore } from '@/stores/aiChat'
import type {
ChatMessage,
SourceMessage,
ToolStatus,
ToolCallRecord,
ToolBlockContent,
@@ -22,7 +21,6 @@ import type { TokenUsage, AgentRuntimeStatus } from '@electron/shared/types'
export type { TokenUsage, AgentRuntimeStatus }
export type {
ChatMessage,
SourceMessage,
ToolStatus,
ToolCallRecord,
ToolBlockContent,
@@ -58,9 +56,6 @@ export function useAIChat(
return {
initialization,
messages: toRef(state, 'messages'),
sourceMessages: toRef(state, 'sourceMessages'),
currentKeywords: toRef(state, 'currentKeywords'),
isLoadingSource: toRef(state, 'isLoadingSource'),
isAIThinking: toRef(state, 'isAIThinking'),
currentAIChatId: toRef(state, 'currentAIChatId'),
currentToolStatus: toRef(state, 'currentToolStatus'),
-7
View File
@@ -223,13 +223,6 @@
"copied": "Copied to clipboard",
"unknown": "Unknown error"
},
"dataSource": {
"title": "Data Source",
"keywords": "Keywords:",
"empty": "No data",
"emptyHint": "Related records will appear here after asking questions",
"totalRecords": "{count} records"
},
"evidence": {
"title": "Evidence",
"query": "Question",
-7
View File
@@ -223,13 +223,6 @@
"copied": "クリップボードにコピーしました",
"unknown": "不明なエラー"
},
"dataSource": {
"title": "データソース",
"keywords": "キーワード:",
"empty": "データがありません",
"emptyHint": "質問を送信すると、関連する記録がここに表示されます",
"totalRecords": "全 {count} 件の記録"
},
"evidence": {
"title": "根拠",
"query": "質問",
-7
View File
@@ -223,13 +223,6 @@
"copied": "已复制到剪贴板",
"unknown": "未知错误"
},
"dataSource": {
"title": "数据源",
"keywords": "关键词:",
"empty": "暂无数据",
"emptyHint": "发送问题后,相关记录会显示在这里",
"totalRecords": "共 {count} 条记录"
},
"evidence": {
"title": "依据证据",
"query": "问题",
-7
View File
@@ -223,13 +223,6 @@
"copied": "已複製到剪貼簿",
"unknown": "未知錯誤"
},
"dataSource": {
"title": "資料來源",
"keywords": "關鍵字:",
"empty": "暫無資料",
"emptyHint": "傳送問題後,相關紀錄會顯示在這裡",
"totalRecords": "共 {count} 筆紀錄"
},
"evidence": {
"title": "依據證據",
"query": "問題",
-32
View File
@@ -132,16 +132,6 @@ export interface ChatMessage {
entityRefs?: AIEntityRef[]
}
// 搜索结果消息类型(保留用于数据源面板)
export interface SourceMessage {
id: number
senderName: string
senderPlatformId: string
content: string
timestamp: number
type: number
}
interface OwnerInfo {
platformId: string
displayName: string
@@ -149,8 +139,6 @@ interface OwnerInfo {
interface AIChatBuffer {
messages: ChatMessage[]
sourceMessages: SourceMessage[]
currentKeywords: string[]
assistantId: string | null
loaded: boolean
sessionTokenUsage?: TokenUsage
@@ -165,9 +153,6 @@ export interface AIChatSessionState {
timeFilter?: { startTs: number; endTs: number }
selectedAssistantId: string | null
messages: ChatMessage[]
sourceMessages: SourceMessage[]
currentKeywords: string[]
isLoadingSource: boolean
isAIThinking: boolean
currentAIChatId: string | null
currentToolStatus: ToolStatus | null
@@ -287,8 +272,6 @@ function toRuntimeMessage(msg: PersistedAIMessage): ChatMessage {
function createAIChatBuffer(assistantId: string | null = null): AIChatBuffer {
return {
messages: [],
sourceMessages: [],
currentKeywords: [],
assistantId,
loaded: false,
}
@@ -305,9 +288,6 @@ function createSessionState(params: EnsureAIChatSessionParams): AIChatSessionSta
timeFilter: params.timeFilter,
selectedAssistantId: null,
messages: draftBuffer.messages,
sourceMessages: draftBuffer.sourceMessages,
currentKeywords: draftBuffer.currentKeywords,
isLoadingSource: false,
isAIThinking: false,
currentAIChatId: null,
currentToolStatus: null,
@@ -424,8 +404,6 @@ export const useAIChatStore = defineStore('aiChatRuntime', () => {
const buffer = getOrCreateBuffer(state, bufferKey)
state.currentAIChatId = bufferKey === DRAFT_AI_CHAT_KEY ? null : bufferKey
state.messages = buffer.messages
state.sourceMessages = buffer.sourceMessages
state.currentKeywords = buffer.currentKeywords
state.selectedAssistantId = buffer.assistantId
state.sessionTokenUsage = buffer.sessionTokenUsage ? { ...buffer.sessionTokenUsage } : createEmptyTokenUsage()
state.agentStatus = null
@@ -592,8 +570,6 @@ export const useAIChatStore = defineStore('aiChatRuntime', () => {
if (!isLatestAIChatSelection(chatKey, selectionGeneration)) return false
buffer.messages.splice(0, buffer.messages.length, ...history.map((msg) => toRuntimeMessage(msg)))
buffer.sourceMessages.splice(0, buffer.sourceMessages.length)
buffer.currentKeywords.splice(0, buffer.currentKeywords.length)
buffer.sessionTokenUsage = toTokenUsage(tokenUsage)
buffer.loaded = true
}
@@ -693,7 +669,6 @@ export const useAIChatStore = defineStore('aiChatRuntime', () => {
bindDisplayedBuffer(state, DRAFT_AI_CHAT_KEY)
state.currentToolStatus = null
state.toolsUsedInCurrentRound = []
state.isLoadingSource = false
state.sessionTokenUsage = createEmptyTokenUsage()
state.agentStatus = null
state.isAborted = false
@@ -1058,7 +1033,6 @@ export const useAIChatStore = defineStore('aiChatRuntime', () => {
const currentEntityRefs = (options?.entityRefs ?? []).map((ref) => ({ ...ref }))
state.isAIThinking = true
state.isLoadingSource = true
state.currentToolStatus = null
state.toolsUsedInCurrentRound = []
state.agentStatus = null
@@ -1332,7 +1306,6 @@ export const useAIChatStore = defineStore('aiChatRuntime', () => {
status: toolFailed ? 'error' : 'done',
})
}
state.isLoadingSource = false
break
case 'plan_delta':
@@ -1556,7 +1529,6 @@ export const useAIChatStore = defineStore('aiChatRuntime', () => {
return { success: false, reason: 'error' }
} finally {
state.isAIThinking = false
state.isLoadingSource = false
state.currentToolStatus = null
state.isAborted = false
state.currentRequestId = ''
@@ -1752,7 +1724,6 @@ export const useAIChatStore = defineStore('aiChatRuntime', () => {
void ensureOwnerInfo(chatKey)
state.isAIThinking = true
state.isLoadingSource = true
state.currentToolStatus = null
state.toolsUsedInCurrentRound = []
state.agentStatus = null
@@ -1951,7 +1922,6 @@ export const useAIChatStore = defineStore('aiChatRuntime', () => {
status: toolFailed ? 'error' : 'done',
})
}
state.isLoadingSource = false
break
case 'plan_delta':
if (chunk.planDelta) appendPlanDraftToBlocks(chunk.planDelta)
@@ -2089,7 +2059,6 @@ export const useAIChatStore = defineStore('aiChatRuntime', () => {
return { success: false, reason: 'error' }
} finally {
state.isAIThinking = false
state.isLoadingSource = false
state.currentToolStatus = null
state.isAborted = false
state.currentRequestId = ''
@@ -2104,7 +2073,6 @@ export const useAIChatStore = defineStore('aiChatRuntime', () => {
state.isAborted = true
state.isAIThinking = false
state.isLoadingSource = false
state.currentToolStatus = null
setAgentPhase(state, 'aborted')