完成聊天框功能开发
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from fastapi import APIRouter
|
||||
from ..core.items import ChatRequest
|
||||
from ..tools.get_all_role_and_chat import get_all_role_and_chat
|
||||
from ..core.models import chat_history
|
||||
from ..core.models.chat_history import ChatHistory # 修改导入语句
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -12,9 +12,9 @@ def get_all_role_and_chat_endpoint():
|
||||
return get_all_role_and_chat()
|
||||
|
||||
# 2. 根据rolename和chatname读取特定聊天记录
|
||||
@router.post("/chat_box/get_chat_history")
|
||||
@router.get("/chat_box/get_chat_history")
|
||||
async def get_chat_history_endpoint(role_name: str, chat_name: str):
|
||||
# 实例化工具类
|
||||
reader = chat_history.load_from_file(role_name, chat_name)
|
||||
reader = ChatHistory.load_from_file(role_name, chat_name)
|
||||
|
||||
return reader.to_chatbox_format()
|
||||
|
||||
@@ -49,7 +49,8 @@ class ChatMetadata(BaseModel):
|
||||
note_interval: int = Field(0, description="笔记插入间隔数")
|
||||
note_position: int = Field(0, description="笔记插入位置")
|
||||
note_depth: int = Field(0, description="笔记插入深度")
|
||||
note_role: str = Field("", description="笔记使用角色类型")
|
||||
# 0:System,1:User,2:Assistant
|
||||
note_role: int = Field("", description="笔记使用角色类型")
|
||||
|
||||
# 扩展信息
|
||||
extensions: Dict[str, Any] = Field(
|
||||
|
||||
3
data/chat/testRole1/111.jsonl
Normal file
3
data/chat/testRole1/111.jsonl
Normal file
@@ -0,0 +1,3 @@
|
||||
{"integrity": "test-uuid-1", "chat_id_hash": "hash1", "note_prompt": "", "note_interval": 0, "note_position": 0, "note_depth": 0, "note_role": 0, "extensions": {}, "timedWorldInfo": {}, "variables": {}, "tainted": false, "lastInContextMessageId": -1}
|
||||
{"name": "User", "is_user": true, "is_system": false, "floor": 0,"send_date": "1700000000000", "mes": "你好,我是新来的冒险者。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
{"name": "Guide", "is_user": false, "is_system": false, "floor": 1,"send_date": "1700000001000", "mes": "欢迎来到我们的世界!你需要什么帮助?", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
4
data/chat/testRole1/11111.jsonl
Normal file
4
data/chat/testRole1/11111.jsonl
Normal file
@@ -0,0 +1,4 @@
|
||||
{"integrity": "test-uuid-2", "chat_id_hash": "hash2", "note_prompt": "探索神秘森林", "note_interval": 5, "note_position": 0, "note_depth": 2, "note_role": 1, "extensions": {}, "timedWorldInfo": {}, "variables": {"location": "forest"}, "tainted": false, "lastInContextMessageId": -1}
|
||||
{"name": "System", "is_user": false, "is_system": true, "floor": 0,"send_date": "1700000002000", "mes": "你进入了一片神秘的森林,周围充满了未知的危险。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
{"name": "User", "is_user": true, "is_system": false, "floor": 1,"send_date": "1700000003000", "mes": "我该往哪个方向走?", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
{"name": "Ranger", "is_user": false, "is_system": false, "floor": 2,"send_date": "1700000004000", "mes": "东边有一条小路,但西边似乎有奇怪的声音。", "extra": {}, "swipes": ["东边有一条小路,但西边似乎有奇怪的声音。", "建议往东走,那边比较安全。", "小心西边,可能有野兽。"], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
@@ -1,4 +0,0 @@
|
||||
{"integrity": "test-uuid-2", "chat_id_hash": "hash2", "note_prompt": "探索神秘森林", "note_interval": 5, "note_position": 0, "note_depth": 2, "note_role": 1, "extensions": {}, "timedWorldInfo": {}, "variables": {"location": "forest"}, "tainted": false, "lastInContextMessageId": -1}
|
||||
{"name": "System", "is_user": false, "is_system": true, "send_date": "1700000002000", "mes": "你进入了一片神秘的森林,周围充满了未知的危险。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
{"name": "User", "is_user": true, "is_system": false, "send_date": "1700000003000", "mes": "我该往哪个方向走?", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
{"name": "Ranger", "is_user": false, "is_system": false, "send_date": "1700000004000", "mes": "东边有一条小路,但西边似乎有奇怪的声音。", "extra": {}, "swipes": ["东边有一条小路,但西边似乎有奇怪的声音。", "建议往东走,那边比较安全。", "小心西边,可能有野兽。"], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
@@ -1,3 +0,0 @@
|
||||
{"integrity": "test-uuid-1", "chat_id_hash": "hash1", "note_prompt": "", "note_interval": 0, "note_position": 0, "note_depth": 0, "note_role": 0, "extensions": {}, "timedWorldInfo": {}, "variables": {}, "tainted": false, "lastInContextMessageId": -1}
|
||||
{"name": "User", "is_user": true, "is_system": false, "send_date": "1700000000000", "mes": "你好,我是新来的冒险者。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
{"name": "Guide", "is_user": false, "is_system": false, "send_date": "1700000001000", "mes": "欢迎来到我们的世界!你需要什么帮助?", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
@@ -1,4 +1,4 @@
|
||||
{"integrity": "test-uuid-3", "chat_id_hash": "hash3", "note_prompt": "酒馆闲聊", "note_interval": 0, "note_position": 0, "note_depth": 0, "note_role": 0, "extensions": {}, "timedWorldInfo": {}, "variables": {}, "tainted": false, "lastInContextMessageId": -1}
|
||||
{"name": "User", "is_user": true, "is_system": false, "send_date": "1700000005000", "mes": "有人知道关于龙的消息吗?", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
{"name": "Bard", "is_user": false, "is_system": false, "send_date": "1700000006000", "mes": "听说北边的山脉里有一条红龙,它守护着巨大的宝藏。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
{"name": "Warrior", "is_user": false, "is_system": false, "send_date": "1700000007000", "mes": "别信那些谣言,我上周去过那里,什么都没发现。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejsprocessed": [], "gen_started": null, "gen_finished": null}
|
||||
{"name": "User", "is_user": true, "is_system": false, "floor": 0,"send_date": "1700000005000", "mes": "有人知道关于龙的消息吗?", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
{"name": "Bard", "is_user": false, "is_system": false, "floor": 1,"send_date": "1700000006000", "mes": "听说北边的山脉里有一条红龙,它守护着巨大的宝藏。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
{"name": "Warrior", "is_user": false, "is_system": false, "floor": 2,"send_date": "1700000007000", "mes": "别信那些谣言,我上周去过那里,什么都没发现。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejsprocessed": [], "gen_started": null, "gen_finished": null}
|
||||
|
||||
6
data/chat/testRole3/333.jsonl
Normal file
6
data/chat/testRole3/333.jsonl
Normal file
@@ -0,0 +1,6 @@
|
||||
{"integrity": "test-uuid-3", "chat_id_hash": "hash3", "note_prompt": "酒馆闲聊", "note_interval": 0, "note_position": 0, "note_depth": 0, "note_role": 0, "extensions": {}, "timedWorldInfo": {}, "variables": {}, "tainted": false, "lastInContextMessageId": -1}
|
||||
{"name": "User", "is_user": true, "is_system": false, "floor": 0,"send_date": "1700000005000", "mes": "有人知道关于龙的消息吗?", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
{"name": "Bard", "is_user": false, "is_system": false, "floor": 1,"send_date": "1700000006000", "mes": "听说北边的山脉里有一条红龙,它守护着巨大的宝藏。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
{"name": "User", "is_user": true, "is_system": false, "floor": 3,"send_date": "1700000005000", "mes": "别信那些谣言,我上周去过那里,什么都没发现。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
{"name": "Warrior", "is_user": false, "is_system": false, "floor": 4,"send_date": "1700000007000", "mes": "别信那些谣言,我上周去过那里,什么都没发现。", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejsprocessed": [], "gen_started": null, "gen_finished": null}
|
||||
{"name": "User", "is_user": true, "is_system": false, "floor": 5,"send_date": "1700000005000", "mes": "你要是发现了,那我还去干什么?", "extra": {}, "swipes": [], "swipe_id": 0, "swipe_info": [], "title": "", "force_avatar": null, "variables": [], "variables_initialized": [], "is_ejs_processed": [], "gen_started": null, "gen_finished": null}
|
||||
@@ -39,6 +39,13 @@ const useChatBoxStore = create(
|
||||
// 设置当前聊天
|
||||
setCurrentChat: (chat) => set({ currentChat: chat }),
|
||||
|
||||
// 同时设置角色和聊天
|
||||
setChatBoxRoleAndChat: (role, chat) => set({
|
||||
currentRole: role,
|
||||
currentChat: chat
|
||||
}),
|
||||
|
||||
|
||||
// 加载聊天历史
|
||||
fetchChatHistory: async (roleName, chatName) => {
|
||||
set({ isLoading: true, error: null });
|
||||
@@ -48,10 +55,11 @@ const useChatBoxStore = create(
|
||||
throw new Error('Failed to fetch chat history');
|
||||
}
|
||||
const data = await response.json();
|
||||
// 修改数据处理逻辑,适配API返回的数据结构
|
||||
set({
|
||||
messages: data.messages || [],
|
||||
userName: data.userName || 'User',
|
||||
characterName: data.characterName || 'Assistant',
|
||||
messages: data || [], // 直接使用返回的数组
|
||||
userName: 'User', // 固定用户名
|
||||
characterName: roleName || 'Assistant', // 使用角色名作为角色名称
|
||||
isLoading: false
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -62,6 +70,7 @@ const useChatBoxStore = create(
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// 清空聊天历史
|
||||
clearChatHistory: () => set({
|
||||
messages: [],
|
||||
@@ -82,14 +91,21 @@ const useChatBoxStore = create(
|
||||
// 监听角色和聊天变化,自动加载聊天历史
|
||||
useChatBoxStore.subscribe(
|
||||
(state) => ({ role: state.currentRole, chat: state.currentChat }),
|
||||
({ role, chat }) => {
|
||||
if (role && chat) {
|
||||
useChatBoxStore.getState().fetchChatHistory(role, chat);
|
||||
} else {
|
||||
useChatBoxStore.getState().clearChatHistory();
|
||||
({ role, chat }, prev) => {
|
||||
// 只有当角色或聊天发生变化时才处理
|
||||
if (role !== prev.role || chat !== prev.chat) {
|
||||
// 确保角色和聊天都存在且不为null
|
||||
if (role && chat) {
|
||||
useChatBoxStore.getState().fetchChatHistory(role, chat);
|
||||
} else {
|
||||
useChatBoxStore.getState().clearChatHistory();
|
||||
}
|
||||
}
|
||||
},
|
||||
{ equalityFn: (a, b) => a.role === b.role && a.chat === b.chat }
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
export default useChatBoxStore;
|
||||
|
||||
@@ -63,6 +63,9 @@ const useRoleSelectorStore = create((set, get) => ({
|
||||
|
||||
setDeleteType: (type) => set({ deleteType: type }),
|
||||
|
||||
// 同时更新角色和聊天
|
||||
setSelectedRoleAndChat: (role, chat) => set({ selectedRole: role, selectedChat: chat }),
|
||||
|
||||
handleRenameRole: (oldName, newName) => {
|
||||
const { roleData, selectedRole } = get();
|
||||
if (newName && newName !== oldName) {
|
||||
@@ -199,4 +202,4 @@ const useRoleSelectorStore = create((set, get) => ({
|
||||
})
|
||||
}));
|
||||
|
||||
export default useRoleSelectorStore;
|
||||
export default useRoleSelectorStore;
|
||||
@@ -14,7 +14,6 @@
|
||||
.chat-messages {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
max-height: calc(95vh - 62px); /* 减去输入区域的高度 */
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
padding-top: 60px;
|
||||
@@ -427,3 +426,55 @@
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
/* 确保消息名称和工具栏在移动设备上也能正常显示 */
|
||||
@media (max-width: 768px) {
|
||||
.message-container {
|
||||
max-width: 85%;
|
||||
}
|
||||
|
||||
.message-header {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.message-toolbar {
|
||||
opacity: 1; /* 在移动设备上始终显示工具栏 */
|
||||
}
|
||||
}
|
||||
|
||||
/* 添加滚动条样式,使其更美观 */
|
||||
.chat-messages::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.chat-messages::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
.chat-messages::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.chat-messages::-webkit-scrollbar-thumb:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
|
||||
/* 确保消息内容中的链接样式 */
|
||||
.bubble a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 代码块样式 */
|
||||
.bubble pre {
|
||||
background-color: #f6f8fa;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
overflow-x: auto;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.bubble code {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
@@ -1,156 +1,133 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import useChatBoxStore from '../../Store/Slices/ChatBoxSlice';
|
||||
import './ChatBox.css';
|
||||
|
||||
const ChatBox = () => {
|
||||
const [isHtmlRender, setIsHtmlRender] = useState(false);
|
||||
const [isImageGen, setIsImageGen] = useState(false);
|
||||
const [isDynamicTable, setIsDynamicTable] = useState(false);
|
||||
const [editingId, setEditingId] = useState(null);
|
||||
const [editContent, setEditContent] = useState('');
|
||||
const messagesEndRef = useRef(null);
|
||||
|
||||
const textareaRef = useRef(null);
|
||||
// 从 ChatBoxStore 获取状态和方法
|
||||
const {
|
||||
messages,
|
||||
isLoading,
|
||||
error,
|
||||
userName,
|
||||
characterName,
|
||||
updateMessage
|
||||
} = useChatBoxStore();
|
||||
|
||||
// 从 store 获取状态
|
||||
const { messages, userName, characterName, isLoading, error } = useChatBoxStore();
|
||||
|
||||
// 自动调整 Textarea 高度
|
||||
const adjustHeight = () => {
|
||||
const textarea = textareaRef.current;
|
||||
const wrapper = textarea?.closest('.chat-input-wrapper');
|
||||
|
||||
if (textarea && wrapper) {
|
||||
textarea.style.height = '42px';
|
||||
wrapper.style.height = 'auto';
|
||||
const newHeight = textarea.scrollHeight;
|
||||
|
||||
if (newHeight > 42) {
|
||||
textarea.style.height = newHeight + 'px';
|
||||
} else {
|
||||
textarea.style.height = '42px';
|
||||
}
|
||||
}
|
||||
// 自动滚动到底部
|
||||
const scrollToBottom = () => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
};
|
||||
|
||||
const handleInput = () => {
|
||||
adjustHeight();
|
||||
useEffect(() => {
|
||||
scrollToBottom();
|
||||
}, [messages]);
|
||||
|
||||
// 处理编辑消息
|
||||
const handleEdit = (message) => {
|
||||
setEditingId(message.floor);
|
||||
setEditContent(message.mes);
|
||||
};
|
||||
|
||||
// 开始编辑消息
|
||||
const startEdit = (id, content) => {
|
||||
setEditingId(id);
|
||||
setEditContent(content);
|
||||
};
|
||||
|
||||
// 保存编辑的消息
|
||||
const saveEdit = (id) => {
|
||||
// 这里可以添加向后端发送请求的代码
|
||||
console.log(`保存消息 ${id} 的编辑内容: ${editContent}`);
|
||||
|
||||
// 更新前端显示
|
||||
const messageIndex = messages.findIndex(msg => msg.id === id);
|
||||
if (messageIndex !== -1) {
|
||||
messages[messageIndex].content = editContent;
|
||||
}
|
||||
|
||||
// 退出编辑模式
|
||||
// 保存编辑
|
||||
const handleSaveEdit = (messageId) => {
|
||||
// 调用 store 中的 updateMessage 方法
|
||||
updateMessage(messageId, editContent);
|
||||
setEditingId(null);
|
||||
setEditContent('');
|
||||
};
|
||||
|
||||
// 取消编辑
|
||||
const cancelEdit = () => {
|
||||
const handleCancelEdit = () => {
|
||||
setEditingId(null);
|
||||
setEditContent('');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="chat-box">
|
||||
{/* 上方:消息列表区域 */}
|
||||
<div className="chat-messages">
|
||||
{/* 加载状态和错误信息 */}
|
||||
{isLoading && <div className="loading">加载中...</div>}
|
||||
{error && <div className="error">{error}</div>}
|
||||
// 渲染单条消息
|
||||
const renderMessage = (message) => {
|
||||
const isUser = message.is_user;
|
||||
const isEditing = editingId === message.floor;
|
||||
|
||||
{/* 消息列表 */}
|
||||
{messages.map((msg) => (
|
||||
<div key={msg.id} className={`message ${msg.role}`}>
|
||||
<div className="message-container">
|
||||
{/* 消息名称和工具栏在同一行 */}
|
||||
<div className="message-header">
|
||||
<div className="message-name">{msg.role === 'user' ? userName : characterName}</div>
|
||||
// 根据消息类型设置显示名称
|
||||
const displayName = isUser ? userName : characterName;
|
||||
|
||||
{/* 消息工具栏 */}
|
||||
<div className="message-toolbar">
|
||||
<span className="message-id">ID: {msg.id}</span>
|
||||
<div className="toolbar-buttons">
|
||||
<button
|
||||
className="toolbar-button edit-button"
|
||||
onClick={() => startEdit(msg.id, msg.content)}
|
||||
>
|
||||
编辑
|
||||
</button>
|
||||
<button className="toolbar-button expand-button">
|
||||
⋮
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 消息内容 */}
|
||||
<div className="message-content">
|
||||
<div className="bubble">
|
||||
{editingId === msg.id ? (
|
||||
<div className="edit-container">
|
||||
<textarea
|
||||
value={editContent}
|
||||
onChange={(e) => setEditContent(e.target.value)}
|
||||
className="edit-textarea"
|
||||
/>
|
||||
<div className="edit-buttons">
|
||||
<button
|
||||
className="save-button"
|
||||
onClick={() => saveEdit(msg.id)}
|
||||
>
|
||||
保存
|
||||
</button>
|
||||
<button
|
||||
className="cancel-button"
|
||||
onClick={cancelEdit}
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
isHtmlRender ? (
|
||||
<div dangerouslySetInnerHTML={{ __html: msg.content }} />
|
||||
) : (
|
||||
<div>{msg.content}</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
return (
|
||||
<div key={message.floor} className={`message ${isUser ? 'user' : 'ai'}`}>
|
||||
<div className="message-container">
|
||||
<div className="message-header">
|
||||
<span className="message-name">{displayName}</span>
|
||||
<span className="message-id">#{message.floor}</span>
|
||||
<div className="message-toolbar">
|
||||
<div className="toolbar-buttons">
|
||||
<button
|
||||
className="toolbar-button"
|
||||
onClick={() => handleEdit(message)}
|
||||
title="编辑"
|
||||
>
|
||||
✎
|
||||
</button>
|
||||
<button
|
||||
className="toolbar-button"
|
||||
title="更多"
|
||||
>
|
||||
•••
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 下方:输入框区域 */}
|
||||
<div className="chat-input-wrapper">
|
||||
<div className="chat-input-area">
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
placeholder="输入消息..."
|
||||
onInput={handleInput}
|
||||
rows="1"
|
||||
style={{ height: '42px' }}
|
||||
/>
|
||||
<div className="message-content">
|
||||
{isEditing ? (
|
||||
<div className="edit-container">
|
||||
<textarea
|
||||
className="edit-textarea"
|
||||
value={editContent}
|
||||
onChange={(e) => setEditContent(e.target.value)}
|
||||
/>
|
||||
<div className="edit-buttons">
|
||||
<button
|
||||
className="cancel-button"
|
||||
onClick={handleCancelEdit}
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
className="save-button"
|
||||
onClick={() => handleSaveEdit(message.floor)}
|
||||
>
|
||||
保存
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="bubble">
|
||||
{message.mes}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<button className="send-button">发送</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="chat-box">
|
||||
<div className="chat-messages">
|
||||
{isLoading ? (
|
||||
<div className="loading">加载中...</div>
|
||||
) : error ? (
|
||||
<div className="error">{error}</div>
|
||||
) : messages.length === 0 ? (
|
||||
<div className="loading">暂无消息</div>
|
||||
) : (
|
||||
messages.map(renderMessage)
|
||||
)}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatBox;
|
||||
export default ChatBox;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import useRoleSelectorStore from '../../store/Slices/RoleSelectorSlice';
|
||||
import useChatBoxStore from '../../Store/Slices/ChatBoxSlice';
|
||||
|
||||
import './RoleSelector.css';
|
||||
|
||||
const RoleSelector = () => {
|
||||
@@ -22,6 +23,7 @@ const RoleSelector = () => {
|
||||
fetchRoleData,
|
||||
setSelectedRole,
|
||||
setSelectedChat,
|
||||
setSelectedRoleAndChat,
|
||||
setHoveredRole,
|
||||
setClickedRole,
|
||||
setSearchTerm,
|
||||
@@ -39,7 +41,10 @@ const RoleSelector = () => {
|
||||
} = useRoleSelectorStore();
|
||||
|
||||
// 从 ChatBoxStore 获取状态更新方法
|
||||
const { setCurrentRole, setCurrentChat } = useChatBoxStore();
|
||||
const chatBoxStore = useChatBoxStore();
|
||||
const { setCurrentRole, setCurrentChat } = chatBoxStore;
|
||||
const setChatBoxRoleAndChat = chatBoxStore.setChatBoxRoleAndChat;
|
||||
|
||||
|
||||
// 组件挂载时获取数据
|
||||
useEffect(() => {
|
||||
@@ -62,57 +67,47 @@ const RoleSelector = () => {
|
||||
|
||||
// 处理角色选择
|
||||
const handleRoleSelect = (role) => {
|
||||
setSelectedRole(role);
|
||||
// 更新 ChatBoxStore 中的当前角色
|
||||
setCurrentRole(role);
|
||||
|
||||
// 如果该角色有聊天记录,默认选择第一个
|
||||
if (roleData[role] && roleData[role].length > 0) {
|
||||
const firstChat = roleData[role][0];
|
||||
setSelectedChat(firstChat);
|
||||
// 更新 ChatBoxStore 中的当前聊天
|
||||
setCurrentChat(firstChat);
|
||||
// 使用新的原子操作方法同时更新角色和聊天
|
||||
setSelectedRoleAndChat(role, firstChat);
|
||||
// 同步更新 ChatBoxStore 中的状态
|
||||
setChatBoxRoleAndChat(role, firstChat);
|
||||
} else {
|
||||
setSelectedChat(null);
|
||||
// 清除 ChatBoxStore 中的当前聊天
|
||||
setCurrentChat(null);
|
||||
// 清空角色和聊天
|
||||
setSelectedRoleAndChat(null, null);
|
||||
// 同步更新 ChatBoxStore 中的状态
|
||||
setChatBoxRoleAndChat(null, null);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理聊天选择
|
||||
const handleChatSelect = (chat) => {
|
||||
// 获取当前展开的角色(聊天所属的角色)
|
||||
const currentRole = hoveredRole || clickedRole;
|
||||
// 获取当前展开的角色(聊天所属的角色)
|
||||
const currentRole = hoveredRole || clickedRole;
|
||||
|
||||
setSelectedChat(chat);
|
||||
setSelectedRole(currentRole); // 使用当前展开的角色
|
||||
setHoveredRole(null);
|
||||
setClickedRole(null);
|
||||
// 更新 ChatBoxStore 中的当前聊天和角色
|
||||
setCurrentChat(chat);
|
||||
setCurrentRole(currentRole);
|
||||
// 使用新的原子操作方法同时更新角色和聊天
|
||||
setSelectedRoleAndChat(currentRole, chat);
|
||||
// 同步更新 ChatBoxStore 中的状态
|
||||
setChatBoxRoleAndChat(currentRole, chat);
|
||||
setHoveredRole(null);
|
||||
setClickedRole(null);
|
||||
};
|
||||
|
||||
// 处理角色卡片点击
|
||||
const handleRoleCardClick = (role) => {
|
||||
if (clickedRole === role) {
|
||||
setClickedRole(null);
|
||||
// 取消选择角色时,更新 selectedRole 和 selectedChat
|
||||
setSelectedRole(null);
|
||||
setSelectedChat(null);
|
||||
// 同步更新 ChatBoxStore 中的状态
|
||||
setCurrentRole(null);
|
||||
setCurrentChat(null);
|
||||
} else {
|
||||
setClickedRole(role);
|
||||
handleRoleSelect(role);
|
||||
setSelectedRole(role);
|
||||
setSelectedChat(chat);
|
||||
// 同步更新 ChatBoxStore 中的状态
|
||||
setSele
|
||||
}
|
||||
};
|
||||
|
||||
const handleRoleCardClick = (role) => {
|
||||
if (clickedRole === role) {
|
||||
setClickedRole(null);
|
||||
// 取消选择角色时,更新 selectedRole 和 selectedChat
|
||||
setSelectedRoleAndChat(null, null);
|
||||
// 同步更新 ChatBoxStore 中的状态
|
||||
setChatBoxRoleAndChat(null, null);
|
||||
} else {
|
||||
setClickedRole(role);
|
||||
handleRoleSelect(role);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理搜索
|
||||
const handleSearchChange = (e) => {
|
||||
@@ -165,11 +160,10 @@ const RoleSelector = () => {
|
||||
confirmDelete();
|
||||
if (deleteType === 'role' && selectedRole === showDeleteConfirm) {
|
||||
// 清除 ChatBoxStore 中的当前角色和聊天
|
||||
setCurrentRole(null);
|
||||
setCurrentChat(null);
|
||||
setChatBoxRoleAndChat(null, null);
|
||||
} else if (deleteType === 'chat' && selectedChat === showDeleteConfirm) {
|
||||
// 清除 ChatBoxStore 中的当前聊天
|
||||
setCurrentChat(null);
|
||||
setChatBoxRoleAndChat(selectedRole, null);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100vh; /* 使用视口高度作为基准 */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.app {
|
||||
height: 100%; /* 继承body的100vh */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#root {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 重置样式 */
|
||||
* {
|
||||
margin: 0;
|
||||
@@ -21,7 +41,6 @@ html, body, .app {
|
||||
flex: 1; /* 这会让主容器占据剩余的所有空间 */
|
||||
display: flex;
|
||||
overflow: hidden; /* 防止内容溢出 */
|
||||
height: 100%; /* 确保高度填满 */
|
||||
}
|
||||
|
||||
/* 左侧栏 */
|
||||
@@ -59,3 +78,8 @@ html, body, .app {
|
||||
height: 200px; /* 或者你想要的高度 */
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
height: 60px; /* 或其他合适的固定高度 */
|
||||
flex-shrink: 0; /* 防止被压缩 */
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user