diff --git a/chat/api/v1/ai_chat.py b/chat/api/v1/ai_chat.py index 45a6c0c..c2f3087 100644 --- a/chat/api/v1/ai_chat.py +++ b/chat/api/v1/ai_chat.py @@ -1,7 +1,7 @@ import os import asyncio -from fastapi import APIRouter, Depends, Request +from fastapi import APIRouter, Depends, Request, Query from fastapi.responses import StreamingResponse from sqlalchemy.orm import Session @@ -12,6 +12,7 @@ from langchain_community.chat_models import ChatOpenAI from deps.auth import get_current_user from services.chat_service import ChatDBService from db.session import get_db +from models.ai import ChatConversation, ChatMessage router = APIRouter() @@ -29,7 +30,7 @@ def get_deepseek_llm(api_key: str, model: str, openai_api_base: str): ) @router.post('/stream') -async def chat_stream(request: Request, db: Session = Depends(get_db), user=Depends(get_current_user)): +async def chat_stream(request: Request, user=Depends(get_current_user), db: Session = Depends(get_db)): body = await request.json() content = body.get('content') conversation_id = body.get('conversation_id') @@ -67,4 +68,45 @@ async def chat_stream(request: Request, db: Session = Depends(get_db), user=Depe yield f"data: {chunk}\n\n" await asyncio.sleep(0.01) - return StreamingResponse(event_generator(), media_type='text/event-stream') \ No newline at end of file + return StreamingResponse(event_generator(), media_type='text/event-stream') + +@router.get('/conversations') +def get_conversations( + user_id: int = Query(None), + db: Session = Depends(get_db) +): + """获取指定用户的聊天对话列表""" + conversations = db.query(ChatConversation).filter(ChatConversation.user_id == user_id).order_by(ChatConversation.update_time.desc()).all() + # 可根据需要序列化 + return [ + { + 'id': c.id, + 'title': c.title, + 'update_time': c.update_time, + 'last_message': c.messages[-1].content if c.messages else '', + } + for c in conversations + ] + +@router.get('/messages') +def get_messages( + conversation_id: int = Query(None), + user_id: int = Query(None), + db: Session = Depends(get_db) +): + """获取指定会话的消息列表,可选user_id过滤""" + query = db.query(ChatMessage).filter(ChatMessage.conversation_id == conversation_id) + if user_id is not None: + query = query.filter(ChatMessage.user_id == user_id) + messages = query.order_by(ChatMessage.id).all() + return [ + { + 'id': m.id, + 'role': m.role_id, # 如需role名可再查 + 'content': m.content, + 'user_id': m.user_id, + 'conversation_id': m.conversation_id, + 'create_time': m.create_time, + } + for m in messages + ] \ No newline at end of file diff --git a/web/apps/web-antd/src/views/ai/chat/index.vue b/web/apps/web-antd/src/views/ai/chat/index.vue index 4c59b39..6777777 100644 --- a/web/apps/web-antd/src/views/ai/chat/index.vue +++ b/web/apps/web-antd/src/views/ai/chat/index.vue @@ -1,5 +1,5 @@ @@ -142,28 +163,34 @@ function scrollToBottom() { style="margin: 12px 0 8px 0" /> - - - - - - {{ - item.title - }} - - - {{ item.lastMessage }} - - - - - + + + + + + + {{ item.title.slice(0, 1) }} + + + + {{ + item.title + }} + + + + {{ item.lastMessage }} + + + + + @@ -376,4 +403,95 @@ function scrollToBottom() { opacity: 0; } } +.chat-list-item { + display: flex; + align-items: center; + border-radius: 8px; + margin-bottom: 6px; + padding: 8px 12px; + cursor: pointer; + transition: + background 0.2s, + box-shadow 0.2s; +} +.chat-list-item.selected { + background: #e6f7ff; + box-shadow: 0 2px 8px #1677ff22; + border: 1.5px solid #1677ff; +} +.chat-list-item:hover { + background: #f0f5ff; + box-shadow: 0 2px 8px #1677ff11; +} +.chat-item-avatar { + width: 36px; + height: 36px; + background: #1677ff22; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-right: 12px; + font-size: 18px; + color: #1677ff; + font-weight: bold; +} +.avatar-text { + user-select: none; +} +.chat-item-content { + flex: 1; + min-width: 0; +} +.chat-item-title-row { + display: flex; + align-items: center; + justify-content: space-between; +} +.chat-title { + font-weight: 500; + font-size: 15px; + max-width: 140px; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.unread-dot { + display: inline-block; + width: 8px; + height: 8px; + background: #ff4d4f; + border-radius: 50%; + margin-left: 8px; +} +.chat-desc { + color: #888; + font-size: 12px; + max-width: 140px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-top: 2px; +} +.chat-sider { + background: #fafbfc; + display: flex; + flex-direction: column; + border-right: 1px solid #eee; + padding: 16px 8px 8px 8px; + height: 100%; /* 关键:让侧边栏高度100% */ + min-width: 220px; +} + +.sider-header { + margin-bottom: 8px; +} + +.chat-list { + flex: 1; + overflow-y: auto; /* 只在对话列表区滚动 */ + min-height: 0; /* 关键:flex子项内滚动时必须加 */ + max-height: calc(100vh - 120px); /* 可根据实际header/footer高度调整 */ +}