@@ -204,4 +164,4 @@ const ChatBox = () => {
);
};
-export default ChatBox;
+export default ChatBox;
\ No newline at end of file
diff --git a/frontend-react/src/components/DicePanel.jsx b/frontend-react/src/components/DicePanel/DicePanel.jsx
similarity index 100%
rename from frontend-react/src/components/DicePanel.jsx
rename to frontend-react/src/components/DicePanel/DicePanel.jsx
diff --git a/frontend-react/src/components/ImageDisplay.jsx b/frontend-react/src/components/ImageDisplay/ImageDisplay.jsx
similarity index 100%
rename from frontend-react/src/components/ImageDisplay.jsx
rename to frontend-react/src/components/ImageDisplay/ImageDisplay.jsx
diff --git a/frontend-react/src/components/PresetPanel/PresetPanel.css b/frontend-react/src/components/PresetPanel/PresetPanel.css
new file mode 100644
index 0000000..58f50e5
--- /dev/null
+++ b/frontend-react/src/components/PresetPanel/PresetPanel.css
@@ -0,0 +1,57 @@
+/* ==================== 顶部工具栏区域 ==================== */
+
+.toolbar {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 50px;
+ background-color: #fff;
+ border-bottom: 1px solid #ddd;
+ padding: 0;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ z-index: 100;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.05);
+ transition: height 0.3s ease;
+}
+
+.toolbar.expanded {
+ height: auto;
+ max-height: 300px;
+ overflow-y: auto;
+ padding-bottom: 10px;
+}
+
+.toolbar-content {
+ display: none;
+ width: 100%;
+ padding: 10px 20px;
+}
+
+.toolbar.expanded .toolbar-content {
+ display: block;
+}
+
+.toolbar-toggle-btn {
+ position: absolute;
+ right: 0;
+ top: 0;
+ height: 50px;
+ width: 50px;
+ background: none;
+ border: none;
+ border-left: 1px solid #ddd;
+ cursor: pointer;
+ font-size: 0.8rem;
+ color: #666;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 101;
+}
+
+.toolbar-toggle-btn:hover {
+ background-color: #f5f5f5;
+}
diff --git a/frontend-react/src/components/PresetPanel.jsx b/frontend-react/src/components/PresetPanel/PresetPanel.jsx
similarity index 96%
rename from frontend-react/src/components/PresetPanel.jsx
rename to frontend-react/src/components/PresetPanel/PresetPanel.jsx
index 31a0303..10c068b 100644
--- a/frontend-react/src/components/PresetPanel.jsx
+++ b/frontend-react/src/components/PresetPanel/PresetPanel.jsx
@@ -1,4 +1,5 @@
import React from 'react';
+import './PresetPanel.css';
const PresetPanel = () => {
return (
diff --git a/frontend-react/src/components/RoleSelector/RoleSelector.css b/frontend-react/src/components/RoleSelector/RoleSelector.css
new file mode 100644
index 0000000..e39d7bc
--- /dev/null
+++ b/frontend-react/src/components/RoleSelector/RoleSelector.css
@@ -0,0 +1,95 @@
+/* ==================== 下拉框样式 ==================== */
+
+/* 角色选择器容器 */
+.role-selector {
+ width: 100%;
+}
+
+/* 下拉框容器 */
+.dropdown-container {
+ position: relative;
+ width: 100%;
+ max-width: 300px;
+}
+
+/* 下拉框触发器 */
+.dropdown-trigger {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px 12px;
+ background-color: #f5f5f5;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 14px;
+}
+
+.dropdown-arrow {
+ margin-left: 8px;
+ font-size: 12px;
+}
+
+/* 下拉菜单 */
+.dropdown-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ width: 100%;
+ background-color: white;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+ z-index: 10;
+ max-height: 200px;
+ overflow-y: auto;
+ margin-top: 5px;
+}
+
+/* 下拉菜单项 */
+.dropdown-item {
+ padding: 8px 12px;
+ cursor: pointer;
+ position: relative;
+ font-size: 14px;
+}
+
+.dropdown-item:hover {
+ background-color: #f5f5f5;
+}
+
+.dropdown-item.selected {
+ background-color: #e6f7ff;
+ color: #1890ff;
+}
+
+/* 嵌套下拉框 */
+.nested-dropdown {
+ position: absolute;
+ left: 100%;
+ top: 0;
+ width: 100%;
+ background-color: white;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+ z-index: 11;
+ max-height: 200px;
+ overflow-y: auto;
+}
+
+.nested-dropdown .dropdown-item {
+ padding-left: 20px;
+}
+
+/* 工具栏下拉框容器 */
+.toolbar-dropdown {
+ margin-bottom: 15px;
+}
+
+.toolbar-dropdown label {
+ display: block;
+ margin-bottom: 5px;
+ font-size: 14px;
+ color: #333;
+}
diff --git a/frontend-react/src/components/RoleSelector.jsx b/frontend-react/src/components/RoleSelector/RoleSelector.jsx
similarity index 98%
rename from frontend-react/src/components/RoleSelector.jsx
rename to frontend-react/src/components/RoleSelector/RoleSelector.jsx
index 580dfbe..a520d6b 100644
--- a/frontend-react/src/components/RoleSelector.jsx
+++ b/frontend-react/src/components/RoleSelector/RoleSelector.jsx
@@ -1,4 +1,5 @@
import React, { useState, useEffect } from 'react';
+import './RoleSelector.css';
const RoleSelector = ({ onRoleChange, onChatChange }) => {
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
diff --git a/frontend-react/src/components/ToolBar/ToolBar.css b/frontend-react/src/components/ToolBar/ToolBar.css
new file mode 100644
index 0000000..0f72aef
--- /dev/null
+++ b/frontend-react/src/components/ToolBar/ToolBar.css
@@ -0,0 +1,148 @@
+/* ==================== 顶部工具栏区域 ==================== */
+
+.toolbar {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 50px;
+ background-color: #fff;
+ border-bottom: 1px solid #ddd;
+ padding: 0;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ z-index: 100;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.05);
+ transition: height 0.3s ease;
+}
+
+.toolbar.expanded {
+ height: auto;
+ max-height: 300px;
+ overflow-y: auto;
+ padding-bottom: 10px;
+}
+
+.toolbar-content {
+ display: none;
+ width: 100%;
+ padding: 10px 20px;
+}
+
+.toolbar.expanded .toolbar-content {
+ display: block;
+}
+
+.toolbar-toggle-btn {
+ position: absolute;
+ right: 0;
+ top: 0;
+ height: 50px;
+ width: 50px;
+ background: none;
+ border: none;
+ border-left: 1px solid #ddd;
+ cursor: pointer;
+ font-size: 0.8rem;
+ color: #666;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 101;
+}
+
+.toolbar-toggle-btn:hover {
+ background-color: #f5f5f5;
+}
+
+/* ==================== 下拉框样式 ==================== */
+
+/* 工具栏下拉框容器 */
+.toolbar-dropdown {
+ margin-bottom: 15px;
+}
+
+.toolbar-dropdown label {
+ display: block;
+ margin-bottom: 5px;
+ font-size: 14px;
+ color: #333;
+}
+
+/* 下拉框容器 */
+.dropdown-container {
+ position: relative;
+ width: 100%;
+ max-width: 300px;
+}
+
+/* 下拉框触发器 */
+.dropdown-trigger {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px 12px;
+ background-color: #f5f5f5;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 14px;
+}
+
+.dropdown-arrow {
+ margin-left: 8px;
+ font-size: 12px;
+}
+
+/* 下拉菜单 */
+.dropdown-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ width: 100%;
+ background-color: white;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+ z-index: 10;
+ max-height: 200px;
+ overflow-y: auto;
+ margin-top: 5px;
+}
+
+/* 下拉菜单项 */
+.dropdown-item {
+ padding: 8px 12px;
+ cursor: pointer;
+ position: relative;
+ font-size: 14px;
+}
+
+.dropdown-item:hover {
+ background-color: #f5f5f5;
+}
+
+.dropdown-item.selected {
+ background-color: #e6f7ff;
+ color: #1890ff;
+}
+
+/* 嵌套下拉框 */
+.nested-dropdown {
+ position: absolute;
+ left: 100%;
+ top: 0;
+ width: 100%;
+ background-color: white;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+ z-index: 11;
+ max-height: 200px;
+ overflow-y: auto;
+}
+
+.nested-dropdown .dropdown-item {
+ padding-left: 20px;
+}
diff --git a/frontend-react/src/components/ToolBar/ToolBar.jsx b/frontend-react/src/components/ToolBar/ToolBar.jsx
new file mode 100644
index 0000000..558a6b0
--- /dev/null
+++ b/frontend-react/src/components/ToolBar/ToolBar.jsx
@@ -0,0 +1,107 @@
+import React, { useState } from 'react';
+import './Toolbar.css';
+
+const Toolbar = ({
+ isExpanded,
+ onToggle,
+ onRoleChange,
+ onChatChange
+}) => {
+ const [selectedRole, setSelectedRole] = useState(null);
+ const [selectedChat, setSelectedChat] = useState(null);
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false);
+ const [roleData, setRoleData] = useState({});
+
+ // 获取角色和聊天数据
+ React.useEffect(() => {
+ const fetchRoleData = async () => {
+ try {
+ const response = await fetch('/api/get_all_role_and_chat');
+ const data = await response.json();
+ setRoleData(data);
+ } catch (error) {
+ console.error('获取角色数据失败:', error);
+ }
+ };
+
+ fetchRoleData();
+ }, []);
+
+ // 处理角色选择
+ const handleRoleSelect = (role) => {
+ setSelectedRole(role);
+ setSelectedChat(null);
+ if (onRoleChange) {
+ onRoleChange(role);
+ }
+ };
+
+ // 处理聊天选择
+ const handleChatSelect = (chat) => {
+ setSelectedChat(chat);
+ setIsDropdownOpen(false);
+ if (onChatChange) {
+ onChatChange(selectedRole, chat);
+ }
+ };
+
+ return (
+
+
+
+ {isExpanded && (
+
+
+
+
+
setIsDropdownOpen(!isDropdownOpen)}
+ >
+ {selectedRole || '选择角色'}
+ ▼
+
+
+ {isDropdownOpen && (
+
+ {Object.keys(roleData).map(role => (
+
handleRoleSelect(role)}
+ >
+ {role}
+ {selectedRole === role && (
+
+ {roleData[role].map(chat => (
+
{
+ e.stopPropagation();
+ handleChatSelect(chat);
+ }}
+ >
+ {chat}
+
+ ))}
+
+ )}
+
+ ))}
+
+ )}
+
+
+
+ )}
+
+ );
+};
+
+export default Toolbar;
\ No newline at end of file
diff --git a/frontend-react/src/index.css b/frontend-react/src/index.css
index 5008c71..6a889cb 100644
--- a/frontend-react/src/index.css
+++ b/frontend-react/src/index.css
@@ -41,239 +41,6 @@ html, body, #root {
background: #aaa;
}
-/* ==================== 顶部工具栏区域 ==================== */
-
-.toolbar {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- height: 50px;
- background-color: #fff;
- border-bottom: 1px solid #ddd;
- padding: 0;
- display: flex;
- align-items: center;
- justify-content: flex-end;
- z-index: 100;
- box-shadow: 0 2px 4px rgba(0,0,0,0.05);
- transition: height 0.3s ease;
-}
-
-.toolbar.expanded {
- height: auto;
- max-height: 300px;
- overflow-y: auto;
- padding-bottom: 10px;
-}
-
-.toolbar-content {
- display: none;
- width: 100%;
- padding: 10px 20px;
-}
-
-.toolbar.expanded .toolbar-content {
- display: block;
-}
-
-.toolbar-toggle-btn {
- position: absolute;
- right: 0;
- top: 0;
- height: 50px;
- width: 50px;
- background: none;
- border: none;
- border-left: 1px solid #ddd;
- cursor: pointer;
- font-size: 0.8rem;
- color: #666;
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 101;
-}
-
-.toolbar-toggle-btn:hover {
- background-color: #f5f5f5;
-}
-
-/* ==================== 下拉框样式 ==================== */
-
-/* 角色选择器容器 */
-.role-selector {
- width: 100%;
-}
-
-/* 下拉框容器 */
-.dropdown-container {
- position: relative;
- width: 100%;
- max-width: 300px;
-}
-
-/* 下拉框触发器 */
-.dropdown-trigger {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 8px 12px;
- background-color: #f5f5f5;
- border: 1px solid #ddd;
- border-radius: 4px;
- cursor: pointer;
- font-size: 14px;
-}
-
-.dropdown-arrow {
- margin-left: 8px;
- font-size: 12px;
-}
-
-/* 下拉菜单 */
-.dropdown-menu {
- position: absolute;
- top: 100%;
- left: 0;
- width: 100%;
- background-color: white;
- border: 1px solid #ddd;
- border-radius: 4px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
- z-index: 10;
- max-height: 200px;
- overflow-y: auto;
- margin-top: 5px;
-}
-
-/* 下拉菜单项 */
-.dropdown-item {
- padding: 8px 12px;
- cursor: pointer;
- position: relative;
- font-size: 14px;
-}
-
-.dropdown-item:hover {
- background-color: #f5f5f5;
-}
-
-.dropdown-item.selected {
- background-color: #e6f7ff;
- color: #1890ff;
-}
-
-/* 嵌套下拉框 */
-.nested-dropdown {
- position: absolute;
- left: 100%;
- top: 0;
- width: 100%;
- background-color: white;
- border: 1px solid #ddd;
- border-radius: 4px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
- z-index: 11;
- max-height: 200px;
- overflow-y: auto;
-}
-
-.nested-dropdown .dropdown-item {
- padding-left: 20px;
-}
-
-/* ==================== 下拉框样式 ==================== */
-
-/* 工具栏下拉框容器 */
-.toolbar-dropdown {
- margin-bottom: 15px;
-}
-
-.toolbar-dropdown label {
- display: block;
- margin-bottom: 5px;
- font-size: 14px;
- color: #333;
-}
-
-/* 下拉框容器 */
-.dropdown-container {
- position: relative;
- width: 100%;
- max-width: 300px;
-}
-
-/* 下拉框触发器 */
-.dropdown-trigger {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 8px 12px;
- background-color: #f5f5f5;
- border: 1px solid #ddd;
- border-radius: 4px;
- cursor: pointer;
- font-size: 14px;
-}
-
-.dropdown-arrow {
- margin-left: 8px;
- font-size: 12px;
-}
-
-/* 下拉菜单 */
-.dropdown-menu {
- position: absolute;
- top: 100%;
- left: 0;
- width: 100%;
- background-color: white;
- border: 1px solid #ddd;
- border-radius: 4px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
- z-index: 10;
- max-height: 200px;
- overflow-y: auto;
- margin-top: 5px;
-}
-
-/* 下拉菜单项 */
-.dropdown-item {
- padding: 8px 12px;
- cursor: pointer;
- position: relative;
- font-size: 14px;
-}
-
-.dropdown-item:hover {
- background-color: #f5f5f5;
-}
-
-.dropdown-item.selected {
- background-color: #e6f7ff;
- color: #1890ff;
-}
-
-/* 嵌套下拉框 */
-.nested-dropdown {
- position: absolute;
- left: 100%;
- top: 0;
- width: 100%;
- background-color: white;
- border: 1px solid #ddd;
- border-radius: 4px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
- z-index: 11;
- max-height: 200px;
- overflow-y: auto;
-}
-
-.nested-dropdown .dropdown-item {
- padding-left: 20px;
-}
-
/* ==================== 主内容区域 ==================== */
.main-container {
@@ -330,668 +97,3 @@ html, body, #root {
.right-top {
border-bottom: 1px solid #ddd;
}
-
-/* ==================== 聊天框区域 ==================== */
-
-/* React 组件根容器 */
-.chat-box {
- height: 95%;
- width: 100%;
- display: flex;
- flex-direction: column;
- background-color: #fafafa;
-}
-
-/* 消息列表容器 */
-.chat-messages {
- flex: 1;
- min-height: 0;
- max-height: 100%;
- overflow-y: auto;
- padding: 20px;
- padding-top: 60px;
- display: flex;
- flex-direction: column;
- gap: 15px;
-}
-
-/* 设置面板 */
-.settings-panel {
- position: absolute;
- top: 0;
- right: 0;
- z-index: 20;
- background-color: #fff;
- border-bottom-left-radius: 8px;
- box-shadow: 0 2px 8px rgba(0,0,0,0.1);
- overflow: hidden;
- transition: all 0.3s ease;
- max-width: 200px;
-}
-
-.settings-panel.collapsed {
- width: 40px;
- height: 40px;
- cursor: pointer;
-}
-
-.settings-panel.expanded {
- width: auto;
- min-width: 150px;
- padding: 10px;
- border: 1px solid #eee;
- border-top: none;
- border-right: none;
-}
-
-.settings-header {
- height: 40px;
- width: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- background-color: #f0f0f0;
- cursor: pointer;
- font-size: 18px;
- color: #666;
-}
-
-.settings-panel.collapsed .settings-header:hover {
- background-color: #e0e0e0;
-}
-
-.settings-options {
- display: none;
- flex-direction: column;
- gap: 10px;
- padding-top: 10px;
-}
-
-.settings-panel.expanded .settings-options {
- display: flex;
-}
-
-.setting-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- font-size: 14px;
- color: #333;
-}
-
-.setting-item input[type="checkbox"] {
- margin-right: 8px;
- cursor: pointer;
-}
-
-/* ==================== 聊天气泡样式 ==================== */
-
-/* 消息行容器 */
-.message {
- display: flex;
- width: 100%;
- margin-bottom: 10px;
-}
-
-/* 区分左右布局 */
-.message.user {
- justify-content: flex-end; /* 用户消息靠右 */
-}
-
-.message.ai {
- justify-content: flex-start; /* AI 消息靠左 */
-}
-
-/* 消息容器 - 调整为垂直排列 */
-.message-container {
- display: flex;
- flex-direction: column;
- max-width: 70%;
-}
-
-/* 消息头部 - 包含名称和工具栏 */
-.message-header {
- display: flex;
- align-items: center;
- margin-bottom: 5px;
- order: 1; /* 确保头部显示在第一个位置 */
-}
-
-/* 消息名称 */
-.message-name {
- font-size: 14px;
- font-weight: 500;
- color: #333;
- padding: 2px 8px;
- border-radius: 12px;
- background-color: rgba(0, 0, 0, 0.05);
- display: inline-block;
-}
-
-/* 用户消息名称 */
-.message.user .message-name {
- background-color: rgba(24, 144, 255, 0.1);
- color: #1890ff;
-}
-
-/* AI消息名称 */
-.message.ai .message-name {
- background-color: rgba(0, 0, 0, 0.05);
- color: #333;
-}
-
-/* 消息工具栏 */
-.message-toolbar {
- display: flex;
- align-items: center;
- padding: 0 5px;
- opacity: 0;
- transition: opacity 0.2s ease;
-}
-
-.message:hover .message-toolbar {
- opacity: 1;
-}
-
-.message-id {
- font-size: 12px;
- color: #888;
- padding: 2px 6px;
- border-radius: 4px;
- background-color: rgba(0, 0, 0, 0.05);
- display: flex;
- align-items: center;
-}
-
-.toolbar-buttons {
- display: flex;
- gap: 5px;
- margin: 0 5px;
-}
-
-.toolbar-button {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 24px;
- height: 24px;
- border: none;
- background-color: rgba(0, 0, 0, 0.05);
- border-radius: 4px;
- cursor: pointer;
- color: #555;
- transition: all 0.2s ease;
-}
-
-.toolbar-button:hover {
- background-color: rgba(0, 0, 0, 0.1);
- color: #333;
-}
-
-/* AI消息头部布局:AI助手 - ID - 编辑 - 更多 */
-.message.ai .message-header {
- flex-direction: row;
- justify-content: flex-start;
-}
-
-.message.ai .message-name {
- order: 1;
-}
-
-.message.ai .message-id {
- order: 2;
- margin-left: 5px;
-}
-
-.message.ai .toolbar-buttons {
- order: 3;
- margin-left: 5px;
-}
-
-/* 用户消息头部布局:更多 - 编辑 - ID - 我 */
-.message.user .message-header {
- flex-direction: row;
- justify-content: flex-end;
-}
-
-.message.user .message-name {
- order: 4;
-}
-
-.message.user .message-id {
- order: 3;
- margin-right: 5px;
-}
-
-.message.user .toolbar-buttons {
- order: 1;
- margin-right: 5px;
-}
-
-/* 消息内容 - 确保显示在名称下方 */
-.message-content {
- display: flex;
- flex-direction: column;
- max-width: 100%;
- order: 2; /* 确保内容显示在第二个位置 */
-}
-
-/* 气泡本体 */
-.message .bubble {
- max-width: 100%;
- padding: 10px 15px;
- border-radius: 12px;
- position: relative;
- font-size: 14px;
- line-height: 1.6;
- word-wrap: break-word;
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
-}
-
-/* AI 气泡样式 */
-.message.ai .bubble {
- background-color: #fff;
- color: #333;
- border-top-left-radius: 2px;
- border-bottom-left-radius: 2px;
- border: 1px solid #eee;
-}
-
-/* 用户气泡样式 */
-.message.user .bubble {
- background-color: #1890ff;
- color: #fff;
- border-top-right-radius: 2px;
- border-bottom-right-radius: 2px;
-}
-
-/* 针对 AI 消息中的 HTML 内容进行简单的样式重置 */
-.message.ai .bubble p {
- margin: 0 0 8px 0;
-}
-.message.ai .bubble p:last-child {
- margin-bottom: 0;
-}
-.message.ai .bubble ul, .message.ai .bubble ol {
- margin: 0 0 8px 0;
- padding-left: 20px;
-}
-.message.ai .bubble b {
- font-weight: 600;
- color: #000;
-}
-
-/* 编辑模式 */
-.edit-container {
- display: flex;
- flex-direction: column;
- gap: 10px;
-}
-
-.edit-textarea {
- width: 100%;
- min-height: 80px;
- padding: 10px;
- border: 1px solid #ddd;
- border-radius: 8px;
- resize: vertical;
- font-family: inherit;
- font-size: 14px;
- line-height: 1.5;
-}
-
-.edit-textarea:focus {
- outline: none;
- border-color: #1890ff;
- box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
-}
-
-.edit-buttons {
- display: flex;
- justify-content: flex-end;
- gap: 8px;
-}
-
-.save-button, .cancel-button {
- display: flex;
- align-items: center;
- gap: 4px;
- padding: 6px 12px;
- border: none;
- border-radius: 4px;
- font-size: 14px;
- cursor: pointer;
- transition: all 0.2s ease;
-}
-
-.save-button {
- background-color: #1890ff;
- color: white;
-}
-
-.save-button:hover {
- background-color: #40a9ff;
-}
-
-.cancel-button {
- background-color: #f0f0f0;
- color: #333;
-}
-
-.cancel-button:hover {
- background-color: #e6e6e6;
-}
-
-/* ==================== 输入区域 ==================== */
-
-.chat-input-wrapper {
- background-color: #fff;
- border-top: 1px solid #ddd;
- padding: 10px 20px;
- flex: 0 0 auto;
- display: flex;
- align-items: flex-end;
- gap: 10px;
- width: 100%;
-}
-
-.chat-input-area {
- flex: 1;
- display: flex;
-}
-
-.chat-input-area textarea {
- width: 100%;
- height: 42px;
- min-height: 42px;
- max-height: 300px;
- padding: 10px;
- border: 1px solid #ddd;
- border-radius: 4px;
- resize: none;
- outline: none;
- font-family: inherit;
- line-height: 1.5;
- overflow-y: auto;
- display: block;
-}
-
-.send-button {
- height: 42px;
- padding: 0 20px;
- background-color: #1890ff;
- color: white;
- border: none;
- border-radius: 4px;
- cursor: pointer;
- white-space: nowrap;
- flex-shrink: 0;
-}
-
-.send-button:hover {
- background-color: #40a9ff;import React, { useState, useEffect } from 'react';
-
-const RoleSelector = ({ onRoleChange, onChatChange }) => {
- const [isDropdownOpen, setIsDropdownOpen] = useState(false);
- const [roleData, setRoleData] = useState({});
- const [selectedRole, setSelectedRole] = useState(null);
- const [selectedChat, setSelectedChat] = useState(null);
-
- // 获取角色和聊天数据
- useEffect(() => {
- const fetchRoleData = async () => {
- try {
- const response = await fetch('/api/get_all_role_and_chat');
- const data = await response.json();
- setRoleData(data);
- } catch (error) {
- console.error('获取角色数据失败:', error);
- }
- };
-
- fetchRoleData();
- }, []);
-
- // 处理角色选择
- const handleRoleSelect = (role) => {
- setSelectedRole(role);
- setSelectedChat(null);
- if (onRoleChange) {
- onRoleChange(role);
- }
- };
-
- // 处理聊天选择
- const handleChatSelect = (chat) => {
- setSelectedChat(chat);
- setIsDropdownOpen(false);
- if (onChatChange) {
- onChatChange(selectedRole, chat);
- }
- };
-
- return (
-
-
-
-
setIsDropdownOpen(!isDropdownOpen)}
- >
- {selectedRole || '选择角色'}
- ▼
-
-
- {isDropdownOpen && (
-
- {Object.keys(roleData).map(role => (
-
handleRoleSelect(role)}
- >
- {role}
- {selectedRole === role && (
-
- {roleData[role].map(chat => (
-
{
- e.stopPropagation();
- handleChatSelect(chat);
- }}
- >
- {chat}
-
- ))}
-
- )}
-
- ))}
-
- )}
-
-
- );
-};
-
-export default RoleSelector;
-import React, { useState, useEffect } from 'react';
-
-const RoleSelector = ({ onRoleChange, onChatChange }) => {
- const [isDropdownOpen, setIsDropdownOpen] = useState(false);
- const [roleData, setRoleData] = useState({});
- const [selectedRole, setSelectedRole] = useState(null);
- const [selectedChat, setSelectedChat] = useState(null);
-
- // 获取角色和聊天数据
- useEffect(() => {
- const fetchRoleData = async () => {
- try {
- const response = await fetch('/api/get_all_role_and_chat');
- const data = await response.json();
- setRoleData(data);
- } catch (error) {
- console.error('获取角色数据失败:', error);
- }
- };
-
- fetchRoleData();
- }, []);
-
- // 处理角色选择
- const handleRoleSelect = (role) => {
- setSelectedRole(role);
- setSelectedChat(null);
- if (onRoleChange) {
- onRoleChange(role);
- }
- };
-
- // 处理聊天选择
- const handleChatSelect = (chat) => {
- setSelectedChat(chat);
- setIsDropdownOpen(false);
- if (onChatChange) {
- onChatChange(selectedRole, chat);
- }
- };
-
- return (
-
-
-
-
setIsDropdownOpen(!isDropdownOpen)}
- >
- {selectedRole || '选择角色'}
- ▼
-
-
- {isDropdownOpen && (
-
- {Object.keys(roleData).map(role => (
-
handleRoleSelect(role)}
- >
- {role}
- {selectedRole === role && (
-
- {roleData[role].map(chat => (
-
{
- e.stopPropagation();
- handleChatSelect(chat);
- }}
- >
- {chat}
-
- ))}
-
- )}
-
- ))}
-
- )}
-
-
- );
-};
-
-export default RoleSelector;
-import React, { useState, useEffect } from 'react';
-
-const RoleSelector = ({ onRoleChange, onChatChange }) => {
- const [isDropdownOpen, setIsDropdownOpen] = useState(false);
- const [roleData, setRoleData] = useState({});
- const [selectedRole, setSelectedRole] = useState(null);
- const [selectedChat, setSelectedChat] = useState(null);
-
- // 获取角色和聊天数据
- useEffect(() => {
- const fetchRoleData = async () => {
- try {
- const response = await fetch('/api/get_all_role_and_chat');
- const data = await response.json();
- setRoleData(data);
- } catch (error) {
- console.error('获取角色数据失败:', error);
- }
- };
-
- fetchRoleData();
- }, []);
-
- // 处理角色选择
- const handleRoleSelect = (role) => {
- setSelectedRole(role);
- setSelectedChat(null);
- if (onRoleChange) {
- onRoleChange(role);
- }
- };
-
- // 处理聊天选择
- const handleChatSelect = (chat) => {
- setSelectedChat(chat);
- setIsDropdownOpen(false);
- if (onChatChange) {
- onChatChange(selectedRole, chat);
- }
- };
-
- return (
-
-
-
-
setIsDropdownOpen(!isDropdownOpen)}
- >
- {selectedRole || '选择角色'}
- ▼
-
-
- {isDropdownOpen && (
-
- {Object.keys(roleData).map(role => (
-
handleRoleSelect(role)}
- >
- {role}
- {selectedRole === role && (
-
- {roleData[role].map(chat => (
-
{
- e.stopPropagation();
- handleChatSelect(chat);
- }}
- >
- {chat}
-
- ))}
-
- )}
-
- ))}
-
- )}
-
-
- );
-};
-
-export default RoleSelector;
-
-}