完成大量美化

This commit is contained in:
2026-05-01 14:44:18 +08:00
parent 1d0f0ae0ef
commit 6b65b24b0f
49 changed files with 2487 additions and 9019 deletions

View File

@@ -1,6 +1,7 @@
// frontend-react/src/components/ChatBox/ChatBox.jsx
import React, { useState, useEffect, useRef } from 'react';
import useChatBoxStore from '../../../Store/Mid/ChatBoxSlice';
import MarkdownRenderer from '../../shared/MarkdownRenderer';
import './ChatBox.css';
const ChatBox = () => {
@@ -10,6 +11,11 @@ const ChatBox = () => {
const [inputValue, setInputValue] = useState('');
const [showOptions, setShowOptions] = useState(false);
const optionsRef = useRef(null);
// 聊天选择器相关状态
const [showChatSelector, setShowChatSelector] = useState(false);
const [characterChats, setCharacterChats] = useState([]);
const chatSelectorRef = useRef(null);
// 新增管理每条消息的当前显示的swipe版本
const [currentSwipeId, setCurrentSwipeId] = useState({});
@@ -48,6 +54,24 @@ const ChatBox = () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [showOptions]);
// 点击外部关闭聊天选择器
useEffect(() => {
const handleClickOutside = (event) => {
if (chatSelectorRef.current && !chatSelectorRef.current.contains(event.target) &&
!event.target.closest('.chat-selector-button')) {
setShowChatSelector(false);
}
};
if (showChatSelector) {
document.addEventListener('mousedown', handleClickOutside);
}
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [showChatSelector]);
const handleInputHeight = (e) => {
const textarea = e.target;
@@ -128,6 +152,53 @@ const ChatBox = () => {
const toggleOptionsPanel = () => {
setShowOptions(!showOptions);
};
// 打开聊天选择器
const handleOpenChatSelector = async () => {
const { currentRole } = useChatBoxStore.getState();
// 如果没有当前角色,提示用户
if (!currentRole) {
alert('请先在左侧选择一个角色');
return;
}
try {
// 获取当前角色的聊天列表
const response = await fetch(`/api/chat/${encodeURIComponent(currentRole)}`);
if (!response.ok) throw new Error('获取聊天列表失败');
const chats = await response.json();
setCharacterChats(chats);
// 显示聊天选择器弹窗,让用户手动选择
setShowChatSelector(true);
} catch (error) {
console.error('获取聊天列表失败:', error);
alert('获取聊天列表失败: ' + error.message);
}
};
// 选择聊天并加载
const handleSelectChat = async (chatName) => {
try {
const { setChatBoxRoleAndChat, fetchChatHistory, currentRole } = useChatBoxStore.getState();
// 设置当前角色和聊天
setChatBoxRoleAndChat(currentRole, chatName);
// 加载聊天历史
await fetchChatHistory(currentRole, chatName);
// 关闭选择器
setShowChatSelector(false);
console.log(`已切换到聊天: ${chatName}`);
} catch (error) {
console.error('切换聊天失败:', error);
alert('切换聊天失败: ' + error.message);
}
};
// 渲染单条消息
const renderMessage = (message) => {
@@ -211,8 +282,10 @@ const ChatBox = () => {
<div className="bubble">
{options.htmlRender && !isUser ? (
<div dangerouslySetInnerHTML={{ __html: currentMes }} />
) : options.markdownRender ? (
<MarkdownRenderer content={currentMes} />
) : (
currentMes
<div className="plain-text">{currentMes}</div>
)}
{hasSwipes && isLatestMessage && !isUser && (
<div className="swipe-controls">
@@ -269,6 +342,15 @@ const ChatBox = () => {
</button>
{showOptions && (
<div className="chat-options">
<label className="option-checkbox">
<input
type="checkbox"
checked={options.markdownRender}
onChange={() => toggleOption('markdownRender')}
/>
<span className="checkmark"></span>
<span className="option-label">Markdown渲染</span>
</label>
<label className="option-checkbox">
<input
type="checkbox"
@@ -307,6 +389,18 @@ const ChatBox = () => {
<span className="checkmark"></span>
<span className="option-label">🎨 生图工作流</span>
</label>
{/* 分隔线 - 区分多选框和按钮区域 */}
<div className="option-divider"></div>
{/* 切换聊天按钮 */}
<button
className="switch-chat-btn"
onClick={handleOpenChatSelector}
title="切换到当前角色的最后聊天记录"
>
💬 切换聊天
</button>
</div>
)}
</div>
@@ -336,6 +430,56 @@ const ChatBox = () => {
</button>
</div>
</div>
{/* 聊天选择器弹窗 */}
{showChatSelector && (
<div className="chat-selector-overlay" onClick={() => setShowChatSelector(false)}>
<div
className="chat-selector-modal"
ref={chatSelectorRef}
onClick={(e) => e.stopPropagation()}
>
<div className="chat-selector-header">
<h3>{characterName || '未选择角色'} - 选择聊天</h3>
<button
className="close-btn"
onClick={() => setShowChatSelector(false)}
>
×
</button>
</div>
<div className="chat-selector-body">
{characterChats.length > 0 ? (
<div className="chat-list">
{characterChats.map((chat, index) => (
<div
key={index}
className="chat-option"
>
<div
className="chat-option-content"
onClick={() => handleSelectChat(chat.chat_name)}
>
<div className="chat-option-name">{chat.chat_name}</div>
{chat.last_message && (
<div className="chat-option-preview">
{chat.last_message.substring(0, 100)}{chat.last_message.length > 100 ? '...' : ''}
</div>
)}
</div>
</div>
))}
</div>
) : (
<div className="empty-chats">
<p>暂无聊天记录</p>
</div>
)}
</div>
</div>
</div>
)}
</div>
);
};