diff --git a/server/routers/chat_router.py b/server/routers/chat_router.py index acc3575d..0f29ef43 100644 --- a/server/routers/chat_router.py +++ b/server/routers/chat_router.py @@ -706,10 +706,16 @@ async def create_thread( @chat.get("/threads", response_model=list[ThreadResponse]) async def list_threads( - agent_id: str, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_required_user) + agent_id: str, + limit: int = Query(100, ge=1, le=500), + offset: int = Query(0, ge=0), + db: AsyncSession = Depends(get_db), + current_user: User = Depends(get_required_user), ): """获取用户的所有对话线程 (使用新存储系统)""" - return await list_threads_view(agent_id=agent_id, db=db, current_user_id=str(current_user.id)) + return await list_threads_view( + agent_id=agent_id, db=db, current_user_id=str(current_user.id), limit=limit, offset=offset + ) @chat.delete("/thread/{thread_id}") diff --git a/src/repositories/conversation_repository.py b/src/repositories/conversation_repository.py index 018fb180..7e0fabcd 100644 --- a/src/repositories/conversation_repository.py +++ b/src/repositories/conversation_repository.py @@ -196,7 +196,12 @@ class ConversationRepository: return await self.get_messages(conversation.id, limit, offset) async def list_conversations( - self, user_id: str | None = None, agent_id: str | None = None, status: str = "active" + self, + user_id: str | None = None, + agent_id: str | None = None, + status: str = "active", + limit: int | None = None, + offset: int = 0, ) -> list[Conversation]: query = select(Conversation).where(Conversation.status == status) @@ -206,6 +211,10 @@ class ConversationRepository: query = query.where(Conversation.agent_id == agent_id) query = query.order_by(Conversation.updated_at.desc()) + + if limit: + query = query.limit(limit).offset(offset) + result = await self.db.execute(query) return list(result.scalars().all()) diff --git a/src/services/conversation_service.py b/src/services/conversation_service.py index d03d478d..7c9743b1 100644 --- a/src/services/conversation_service.py +++ b/src/services/conversation_service.py @@ -156,6 +156,8 @@ async def list_threads_view( agent_id: str, db: AsyncSession, current_user_id: str, + limit: int | None = None, + offset: int = 0, ) -> list[dict]: if not agent_id: raise HTTPException(status_code=422, detail="agent_id 不能为空") @@ -165,6 +167,8 @@ async def list_threads_view( user_id=str(current_user_id), agent_id=agent_id, status="active", + limit=limit, + offset=offset, ) return [ diff --git a/web/src/apis/agent_api.js b/web/src/apis/agent_api.js index cd2ec56a..4dcce5d9 100644 --- a/web/src/apis/agent_api.js +++ b/web/src/apis/agent_api.js @@ -286,10 +286,12 @@ export const threadApi = { /** * 获取对话线程列表 * @param {string} agentId - 智能体ID + * @param {number} limit - 返回数量限制,默认100 + * @param {number} offset - 偏移量,默认0 * @returns {Promise} - 对话线程列表 */ - getThreads: (agentId) => { - const url = `/api/chat/threads?agent_id=${agentId}` + getThreads: (agentId, limit = 100, offset = 0) => { + const url = `/api/chat/threads?agent_id=${agentId}&limit=${limit}&offset=${offset}` return apiGet(url) }, diff --git a/web/src/components/AgentChatComponent.vue b/web/src/components/AgentChatComponent.vue index 08a48235..cf2c836b 100644 --- a/web/src/components/AgentChatComponent.vue +++ b/web/src/components/AgentChatComponent.vue @@ -9,12 +9,15 @@ :agents="agents" :selected-agent-id="currentAgentId" :is-creating-new-chat="chatUIStore.creatingNewChat" + :has-more-chats="hasMoreChats" + :is-loading-more="isLoadingMoreChats" @create-chat="createNewChat" @select-chat="selectChat" @delete-chat="deleteChat" @rename-chat="renameChat" @toggle-sidebar="toggleSidebar" @open-agent-modal="openAgentModal" + @load-more-chats="loadMoreChats" :class="{ 'sidebar-open': chatUIStore.isSidebarOpen, 'no-transition': localUIState.isInitialRender @@ -286,6 +289,8 @@ const chatState = reactive({ // 组件级别的线程和消息状态 const threads = ref([]) const threadMessages = ref({}) +const hasMoreChats = ref(true) // 是否还有更多对话可加载 +const isLoadingMoreChats = ref(false) // 加载更多对话中 // 本地 UI 状态(仅在本组件使用) const localUIState = reactive({ @@ -588,8 +593,10 @@ const fetchThreads = async (agentId = null) => { chatUIStore.isLoadingThreads = true try { - const fetchedThreads = await threadApi.getThreads(targetAgentId) + const fetchedThreads = await threadApi.getThreads(targetAgentId, 100, 0) threads.value = fetchedThreads || [] + // 如果返回的数量小于limit,说明没有更多了 + hasMoreChats.value = fetchedThreads && fetchedThreads.length >= 100 } catch (error) { console.error('Failed to fetch threads:', error) handleChatError(error, 'fetch') @@ -599,6 +606,31 @@ const fetchThreads = async (agentId = null) => { } } +// 加载更多对话 +const loadMoreChats = async () => { + if (isLoadingMoreChats.value || !hasMoreChats.value) return + + const targetAgentId = currentAgentId.value + if (!targetAgentId) return + + isLoadingMoreChats.value = true + try { + const offset = threads.value.length + const fetchedThreads = await threadApi.getThreads(targetAgentId, 100, offset) + if (fetchedThreads && fetchedThreads.length > 0) { + threads.value = [...threads.value, ...fetchedThreads] + hasMoreChats.value = fetchedThreads.length >= 100 + } else { + hasMoreChats.value = false + } + } catch (error) { + console.error('Failed to load more chats:', error) + handleChatError(error, 'fetch') + } finally { + isLoadingMoreChats.value = false + } +} + // 创建新线程 const createThread = async (agentId, title = '新的对话') => { if (!agentId) return null @@ -1234,10 +1266,6 @@ const createNewChat = async () => { // 如果第一个对话为空,直接切换到第一个对话而不是创建新对话 if (await switchToFirstChatIfEmpty()) return - // 只有当当前对话是第一个对话且为空时,才阻止创建新对话 - const currentThreadIndex = threads.value.findIndex((thread) => thread.id === currentChatId.value) - if (currentChatId.value && conversations.value.length === 0 && currentThreadIndex === 0) return - chatUIStore.creatingNewChat = true try { const newThread = await createThread(currentAgentId.value, '新的对话') diff --git a/web/src/components/ChatSidebarComponent.vue b/web/src/components/ChatSidebarComponent.vue index a0a86c40..01bd4e6e 100644 --- a/web/src/components/ChatSidebarComponent.vue +++ b/web/src/components/ChatSidebarComponent.vue @@ -29,54 +29,61 @@