拆分成功
This commit is contained in:
39
frontend-react/package-lock.json
generated
39
frontend-react/package-lock.json
generated
@@ -943,9 +943,6 @@
|
|||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -960,9 +957,6 @@
|
|||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -977,9 +971,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -994,9 +985,6 @@
|
|||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1011,9 +999,6 @@
|
|||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1028,9 +1013,6 @@
|
|||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1045,9 +1027,6 @@
|
|||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1062,9 +1041,6 @@
|
|||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1079,9 +1055,6 @@
|
|||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1096,9 +1069,6 @@
|
|||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1113,9 +1083,6 @@
|
|||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1130,9 +1097,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -1147,9 +1111,6 @@
|
|||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"libc": [
|
|
||||||
"musl"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
|
|||||||
@@ -1,86 +1,61 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
import Toolbar from './components/Toolbar/Toolbar';
|
||||||
// 引入各区域组件
|
import ChatBox from './components/ChatBox/ChatBox';
|
||||||
import PresetPanel from './components/PresetPanel';
|
import DicePanel from './components/DicePanel/DicePanel';
|
||||||
import ChatBox from './components/ChatBox';
|
import ImageDisplay from './components/ImageDisplay/ImageDisplay';
|
||||||
import ImageDisplay from './components/ImageDisplay';
|
import PresetPanel from './components/PresetPanel/PresetPanel';
|
||||||
import DicePanel from './components/DicePanel';
|
import './index.css';
|
||||||
import RoleSelector from './components/RoleSelector';
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [isToolbarExpanded, setIsToolbarExpanded] = useState(false);
|
const [isToolbarExpanded, setIsToolbarExpanded] = useState(false);
|
||||||
const [selectedRole, setSelectedRole] = useState(null);
|
const [selectedRole, setSelectedRole] = useState(null);
|
||||||
const [selectedChat, setSelectedChat] = useState(null);
|
const [selectedChat, setSelectedChat] = useState(null);
|
||||||
|
|
||||||
const toggleToolbar = () => {
|
|
||||||
setIsToolbarExpanded(!isToolbarExpanded);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理角色选择变化
|
|
||||||
const handleRoleChange = (role) => {
|
const handleRoleChange = (role) => {
|
||||||
setSelectedRole(role);
|
setSelectedRole(role);
|
||||||
console.log('选择的角色:', role);
|
console.log('角色已更改:', role);
|
||||||
// 这里可以添加其他逻辑,比如加载角色的聊天记录等
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理聊天选择变化
|
|
||||||
const handleChatChange = (role, chat) => {
|
const handleChatChange = (role, chat) => {
|
||||||
setSelectedChat(chat);
|
setSelectedChat(chat);
|
||||||
console.log('选择的聊天:', role, chat);
|
console.log('聊天已更改:', role, chat);
|
||||||
// 这里可以添加其他逻辑,比如加载聊天内容等
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app-container">
|
<div className="app">
|
||||||
{/* 顶部工具栏 */}
|
<Toolbar
|
||||||
<header className={`toolbar ${isToolbarExpanded ? 'expanded' : ''}`}>
|
isExpanded={isToolbarExpanded}
|
||||||
{/* 工具栏内容区域 */}
|
onToggle={() => setIsToolbarExpanded(!isToolbarExpanded)}
|
||||||
{isToolbarExpanded && (
|
onRoleChange={handleRoleChange}
|
||||||
<div className="toolbar-content">
|
onChatChange={handleChatChange}
|
||||||
{/* 角色选择器 */}
|
selectedRole={selectedRole}
|
||||||
<RoleSelector
|
/>
|
||||||
onRoleChange={handleRoleChange}
|
|
||||||
onChatChange={handleChatChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 工具栏切换按钮 */}
|
{/* 主内容容器 */}
|
||||||
<button
|
<div className="main-container">
|
||||||
className={`toolbar-toggle-btn ${isToolbarExpanded ? 'expanded' : ''}`}
|
{/* 左侧栏 - 预设面板 */}
|
||||||
onClick={toggleToolbar}
|
<div className="sidebar-left">
|
||||||
aria-label="Toggle Toolbar"
|
|
||||||
>
|
|
||||||
{isToolbarExpanded ? '▼' : '▲'}
|
|
||||||
</button>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
{/* 主体内容区域 */}
|
|
||||||
<main className="main-container">
|
|
||||||
|
|
||||||
{/* 左侧:预设栏 (仿AI酒馆) */}
|
|
||||||
<section className="sidebar-left">
|
|
||||||
<PresetPanel />
|
<PresetPanel />
|
||||||
</section>
|
</div>
|
||||||
|
|
||||||
{/* 中间:历史对话框 + 输入框 */}
|
{/* 中间栏:聊天框 */}
|
||||||
<section className="chat-area">
|
<div className="chat-area">
|
||||||
<ChatBox />
|
<ChatBox
|
||||||
</section>
|
selectedRole={selectedRole}
|
||||||
|
selectedChat={selectedChat}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 右侧:上下布局 */}
|
{/* 右侧栏 */}
|
||||||
<section className="sidebar-right">
|
<div className="sidebar-right">
|
||||||
{/* 上方:图片展示区 */}
|
|
||||||
<div className="right-top">
|
<div className="right-top">
|
||||||
<ImageDisplay />
|
<ImageDisplay /> {/* 图片展示放在顶部 */}
|
||||||
</div>
|
</div>
|
||||||
{/* 下方:骰子区 */}
|
|
||||||
<div className="right-bottom">
|
<div className="right-bottom">
|
||||||
<DicePanel />
|
<DicePanel /> {/* 骰子面板放在底部 */}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
|
</div>
|
||||||
</main>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
400
frontend-react/src/components/ChatBox/ChatBox.css
Normal file
400
frontend-react/src/components/ChatBox/ChatBox.css
Normal file
@@ -0,0 +1,400 @@
|
|||||||
|
/* ==================== 聊天框区域 ==================== */
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import React, { useState, useRef } from 'react';
|
import React, { useState, useRef } from 'react';
|
||||||
|
import './ChatBox.css';
|
||||||
|
|
||||||
const ChatBox = () => {
|
const ChatBox = () => {
|
||||||
const [isHtmlRender, setIsHtmlRender] = useState(false);
|
const [isHtmlRender, setIsHtmlRender] = useState(false);
|
||||||
const [isImageGen, setIsImageGen] = useState(false);
|
const [isImageGen, setIsImageGen] = useState(false);
|
||||||
const [isDynamicTable, setIsDynamicTable] = useState(false);
|
const [isDynamicTable, setIsDynamicTable] = useState(false);
|
||||||
const [isSettingsExpanded, setIsSettingsExpanded] = useState(false);
|
|
||||||
const [editingId, setEditingId] = useState(null);
|
const [editingId, setEditingId] = useState(null);
|
||||||
const [editContent, setEditContent] = useState('');
|
const [editContent, setEditContent] = useState('');
|
||||||
|
|
||||||
@@ -83,46 +83,6 @@ const ChatBox = () => {
|
|||||||
<div className="chat-box">
|
<div className="chat-box">
|
||||||
{/* 上方:消息列表区域 */}
|
{/* 上方:消息列表区域 */}
|
||||||
<div className="chat-messages">
|
<div className="chat-messages">
|
||||||
|
|
||||||
{/* 右上角:可折叠设置面板 */}
|
|
||||||
<div className={`settings-panel ${isSettingsExpanded ? 'expanded' : 'collapsed'}`}>
|
|
||||||
<div
|
|
||||||
className="settings-header"
|
|
||||||
onClick={() => setIsSettingsExpanded(!isSettingsExpanded)}
|
|
||||||
>
|
|
||||||
{isSettingsExpanded ? '▼' : '⚙'}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isSettingsExpanded && (
|
|
||||||
<div className="settings-options">
|
|
||||||
<div className="setting-item">
|
|
||||||
<label>HTML 渲染</label>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={isHtmlRender}
|
|
||||||
onChange={() => setIsHtmlRender(!isHtmlRender)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="setting-item">
|
|
||||||
<label>开启生图</label>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={isImageGen}
|
|
||||||
onChange={() => setIsImageGen(!isImageGen)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="setting-item">
|
|
||||||
<label>动态表格</label>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={isDynamicTable}
|
|
||||||
onChange={() => setIsDynamicTable(!isDynamicTable)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 消息列表 */}
|
{/* 消息列表 */}
|
||||||
{messages.map((msg) => (
|
{messages.map((msg) => (
|
||||||
<div key={msg.id} className={`message ${msg.role}`}>
|
<div key={msg.id} className={`message ${msg.role}`}>
|
||||||
@@ -204,4 +164,4 @@ const ChatBox = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ChatBox;
|
export default ChatBox;
|
||||||
57
frontend-react/src/components/PresetPanel/PresetPanel.css
Normal file
57
frontend-react/src/components/PresetPanel/PresetPanel.css
Normal file
@@ -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;
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import './PresetPanel.css';
|
||||||
|
|
||||||
const PresetPanel = () => {
|
const PresetPanel = () => {
|
||||||
return (
|
return (
|
||||||
95
frontend-react/src/components/RoleSelector/RoleSelector.css
Normal file
95
frontend-react/src/components/RoleSelector/RoleSelector.css
Normal file
@@ -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;
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import './RoleSelector.css';
|
||||||
|
|
||||||
const RoleSelector = ({ onRoleChange, onChatChange }) => {
|
const RoleSelector = ({ onRoleChange, onChatChange }) => {
|
||||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||||
148
frontend-react/src/components/ToolBar/ToolBar.css
Normal file
148
frontend-react/src/components/ToolBar/ToolBar.css
Normal file
@@ -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;
|
||||||
|
}
|
||||||
107
frontend-react/src/components/ToolBar/ToolBar.jsx
Normal file
107
frontend-react/src/components/ToolBar/ToolBar.jsx
Normal file
@@ -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 (
|
||||||
|
<div className={`toolbar ${isExpanded ? 'expanded' : ''}`}>
|
||||||
|
<button
|
||||||
|
className="toolbar-toggle-btn"
|
||||||
|
onClick={onToggle}
|
||||||
|
>
|
||||||
|
{isExpanded ? '▼' : '▲'}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{isExpanded && (
|
||||||
|
<div className="toolbar-content">
|
||||||
|
<div className="toolbar-dropdown">
|
||||||
|
<label>选择角色</label>
|
||||||
|
<div className="dropdown-container">
|
||||||
|
<div
|
||||||
|
className="dropdown-trigger"
|
||||||
|
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
|
||||||
|
>
|
||||||
|
{selectedRole || '选择角色'}
|
||||||
|
<span className="dropdown-arrow">▼</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isDropdownOpen && (
|
||||||
|
<div className="dropdown-menu">
|
||||||
|
{Object.keys(roleData).map(role => (
|
||||||
|
<div
|
||||||
|
key={role}
|
||||||
|
className={`dropdown-item ${selectedRole === role ? 'selected' : ''}`}
|
||||||
|
onClick={() => handleRoleSelect(role)}
|
||||||
|
>
|
||||||
|
{role}
|
||||||
|
{selectedRole === role && (
|
||||||
|
<div className="nested-dropdown">
|
||||||
|
{roleData[role].map(chat => (
|
||||||
|
<div
|
||||||
|
key={chat}
|
||||||
|
className={`dropdown-item ${selectedChat === chat ? 'selected' : ''}`}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleChatSelect(chat);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{chat}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Toolbar;
|
||||||
@@ -41,239 +41,6 @@ html, body, #root {
|
|||||||
background: #aaa;
|
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 {
|
.main-container {
|
||||||
@@ -330,668 +97,3 @@ html, body, #root {
|
|||||||
.right-top {
|
.right-top {
|
||||||
border-bottom: 1px solid #ddd;
|
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 (
|
|
||||||
<div className="role-selector">
|
|
||||||
<label>选择角色</label>
|
|
||||||
<div className="dropdown-container">
|
|
||||||
<div
|
|
||||||
className="dropdown-trigger"
|
|
||||||
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
|
|
||||||
>
|
|
||||||
{selectedRole || '选择角色'}
|
|
||||||
<span className="dropdown-arrow">▼</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isDropdownOpen && (
|
|
||||||
<div className="dropdown-menu">
|
|
||||||
{Object.keys(roleData).map(role => (
|
|
||||||
<div
|
|
||||||
key={role}
|
|
||||||
className={`dropdown-item ${selectedRole === role ? 'selected' : ''}`}
|
|
||||||
onClick={() => handleRoleSelect(role)}
|
|
||||||
>
|
|
||||||
{role}
|
|
||||||
{selectedRole === role && (
|
|
||||||
<div className="nested-dropdown">
|
|
||||||
{roleData[role].map(chat => (
|
|
||||||
<div
|
|
||||||
key={chat}
|
|
||||||
className={`dropdown-item ${selectedChat === chat ? 'selected' : ''}`}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleChatSelect(chat);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{chat}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<div className="role-selector">
|
|
||||||
<label>选择角色</label>
|
|
||||||
<div className="dropdown-container">
|
|
||||||
<div
|
|
||||||
className="dropdown-trigger"
|
|
||||||
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
|
|
||||||
>
|
|
||||||
{selectedRole || '选择角色'}
|
|
||||||
<span className="dropdown-arrow">▼</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isDropdownOpen && (
|
|
||||||
<div className="dropdown-menu">
|
|
||||||
{Object.keys(roleData).map(role => (
|
|
||||||
<div
|
|
||||||
key={role}
|
|
||||||
className={`dropdown-item ${selectedRole === role ? 'selected' : ''}`}
|
|
||||||
onClick={() => handleRoleSelect(role)}
|
|
||||||
>
|
|
||||||
{role}
|
|
||||||
{selectedRole === role && (
|
|
||||||
<div className="nested-dropdown">
|
|
||||||
{roleData[role].map(chat => (
|
|
||||||
<div
|
|
||||||
key={chat}
|
|
||||||
className={`dropdown-item ${selectedChat === chat ? 'selected' : ''}`}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleChatSelect(chat);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{chat}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<div className="role-selector">
|
|
||||||
<label>选择角色</label>
|
|
||||||
<div className="dropdown-container">
|
|
||||||
<div
|
|
||||||
className="dropdown-trigger"
|
|
||||||
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
|
|
||||||
>
|
|
||||||
{selectedRole || '选择角色'}
|
|
||||||
<span className="dropdown-arrow">▼</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isDropdownOpen && (
|
|
||||||
<div className="dropdown-menu">
|
|
||||||
{Object.keys(roleData).map(role => (
|
|
||||||
<div
|
|
||||||
key={role}
|
|
||||||
className={`dropdown-item ${selectedRole === role ? 'selected' : ''}`}
|
|
||||||
onClick={() => handleRoleSelect(role)}
|
|
||||||
>
|
|
||||||
{role}
|
|
||||||
{selectedRole === role && (
|
|
||||||
<div className="nested-dropdown">
|
|
||||||
{roleData[role].map(chat => (
|
|
||||||
<div
|
|
||||||
key={chat}
|
|
||||||
className={`dropdown-item ${selectedChat === chat ? 'selected' : ''}`}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleChatSelect(chat);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{chat}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RoleSelector;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user