读取本地chatandrole完成并优化排列
This commit is contained in:
0
data/chat/test/12312.jsonl
Normal file
0
data/chat/test/12312.jsonl
Normal file
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import Toolbar from './components/Toolbar/Toolbar';
|
import Toolbar from './components/ToolBar/ToolBar';
|
||||||
import ChatBox from './components/ChatBox/ChatBox';
|
import ChatBox from './components/ChatBox/ChatBox';
|
||||||
import DicePanel from './components/DicePanel/DicePanel';
|
import DicePanel from './components/DicePanel/DicePanel';
|
||||||
import ImageDisplay from './components/ImageDisplay/ImageDisplay';
|
import ImageDisplay from './components/ImageDisplay/ImageDisplay';
|
||||||
@@ -7,7 +7,6 @@ import PresetPanel from './components/PresetPanel/PresetPanel';
|
|||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
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);
|
||||||
|
|
||||||
@@ -24,11 +23,8 @@ function App() {
|
|||||||
return (
|
return (
|
||||||
<div className="app">
|
<div className="app">
|
||||||
<Toolbar
|
<Toolbar
|
||||||
isExpanded={isToolbarExpanded}
|
|
||||||
onToggle={() => setIsToolbarExpanded(!isToolbarExpanded)}
|
|
||||||
onRoleChange={handleRoleChange}
|
onRoleChange={handleRoleChange}
|
||||||
onChatChange={handleChatChange}
|
onChatChange={handleChatChange}
|
||||||
selectedRole={selectedRole}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 主内容容器 */}
|
{/* 主内容容器 */}
|
||||||
|
|||||||
@@ -2,18 +2,19 @@
|
|||||||
|
|
||||||
/* React 组件根容器 */
|
/* React 组件根容器 */
|
||||||
.chat-box {
|
.chat-box {
|
||||||
height: 95%;
|
height: 100%; /* 修改为100%,填满父容器 */
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background-color: #fafafa;
|
background-color: #fafafa;
|
||||||
|
overflow: hidden; /* 防止内容溢出 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 消息列表容器 */
|
/* 消息列表容器 */
|
||||||
.chat-messages {
|
.chat-messages {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
max-height: 100%;
|
max-height: calc(95vh - 62px); /* 减去输入区域的高度 */
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
padding-top: 60px;
|
padding-top: 60px;
|
||||||
@@ -147,7 +148,7 @@
|
|||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 消息工具栏 */
|
/* 消息工具栏 - 优化布局使图标更紧凑 */
|
||||||
.message-toolbar {
|
.message-toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -172,7 +173,7 @@
|
|||||||
|
|
||||||
.toolbar-buttons {
|
.toolbar-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 5px;
|
gap: 3px; /* 减少图标之间的间距 */
|
||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,13 +356,17 @@
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-top: 1px solid #ddd;
|
border-top: 1px solid #ddd;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
flex: 0 0 auto;
|
height: 62px; /* 明确设置高度 */
|
||||||
|
flex-shrink: 0; /* 防止被压缩 */
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.chat-input-area {
|
.chat-input-area {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,80 +1,69 @@
|
|||||||
/* ==================== 顶部工具栏区域 ==================== */
|
/* ==================== 顶部工具栏区域 ==================== */
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
position: absolute;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-bottom: 1px solid #ddd;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
padding: 0;
|
z-index: 1000;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
padding: 0 20px;
|
||||||
z-index: 100;
|
justify-content: flex-start; /* 确保内容从左到右排列 */
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
transition: all 0.3s ease;
|
||||||
transition: height 0.3s ease;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar.expanded {
|
/* 工具栏图标容器 */
|
||||||
height: auto;
|
.toolbar-icons {
|
||||||
max-height: 300px;
|
display: flex;
|
||||||
overflow-y: auto;
|
align-items: center;
|
||||||
padding-bottom: 10px;
|
gap: 15px;
|
||||||
|
margin-right: auto; /* 添加这行,确保图标靠左 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-content {
|
/* 工具栏图标 */
|
||||||
display: none;
|
.toolbar-icon {
|
||||||
width: 100%;
|
width: 36px;
|
||||||
padding: 10px 20px;
|
height: 36px;
|
||||||
}
|
border-radius: 6px;
|
||||||
|
|
||||||
.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;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
z-index: 101;
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #555;
|
||||||
|
background-color: #f5f5f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-toggle-btn:hover {
|
.toolbar-icon:hover {
|
||||||
background-color: #f5f5f5;
|
background-color: #e6f7ff;
|
||||||
|
color: #1890ff;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==================== 下拉框样式 ==================== */
|
/* ==================== 下拉框样式 ==================== */
|
||||||
|
|
||||||
/* 工具栏下拉框容器 */
|
/* 工具栏下拉框容器 */
|
||||||
.toolbar-dropdown {
|
.toolbar-dropdown {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar-dropdown label {
|
.toolbar-dropdown label {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
color: #333;
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 下拉框容器 */
|
/* 下拉框容器 */
|
||||||
.dropdown-container {
|
.dropdown-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
|
||||||
max-width: 300px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 下拉框触发器 */
|
/* 下拉框触发器 */
|
||||||
@@ -83,16 +72,28 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
background-color: #f5f5f5;
|
background-color: #fff;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #e0e0e0;
|
||||||
border-radius: 4px;
|
border-radius: 6px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 14px;
|
font-size: 13px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-trigger:hover {
|
||||||
|
border-color: #1890ff;
|
||||||
|
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-arrow {
|
.dropdown-arrow {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
font-size: 12px;
|
font-size: 10px;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-trigger.open .dropdown-arrow {
|
||||||
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 下拉菜单 */
|
/* 下拉菜单 */
|
||||||
@@ -100,23 +101,36 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
min-width: 180px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #e0e0e0;
|
||||||
border-radius: 4px;
|
border-radius: 6px;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
z-index: 10;
|
z-index: 1001;
|
||||||
max-height: 200px;
|
margin-top: 8px;
|
||||||
overflow-y: auto;
|
padding: 5px 0;
|
||||||
margin-top: 5px;
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu.visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 下拉菜单项 */
|
/* 下拉菜单项 */
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
padding: 8px 12px;
|
padding: 10px 15px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item:hover {
|
.dropdown-item:hover {
|
||||||
@@ -126,6 +140,7 @@
|
|||||||
.dropdown-item.selected {
|
.dropdown-item.selected {
|
||||||
background-color: #e6f7ff;
|
background-color: #e6f7ff;
|
||||||
color: #1890ff;
|
color: #1890ff;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 嵌套下拉框 */
|
/* 嵌套下拉框 */
|
||||||
@@ -133,16 +148,60 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
left: 100%;
|
left: 100%;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
min-width: 180px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #e0e0e0;
|
||||||
border-radius: 4px;
|
border-radius: 6px;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
z-index: 11;
|
z-index: 1002;
|
||||||
max-height: 200px;
|
margin-left: 5px;
|
||||||
overflow-y: auto;
|
padding: 5px 0;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-10px);
|
||||||
|
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||||
|
pointer-events: none;
|
||||||
|
/* 移除滚动条相关设置,让下拉框根据内容自动扩展 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.nested-dropdown .dropdown-item {
|
.nested-dropdown.visible {
|
||||||
padding-left: 20px;
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 添加指示箭头 */
|
||||||
|
.dropdown-item.has-children::after {
|
||||||
|
content: '›';
|
||||||
|
font-size: 18px;
|
||||||
|
color: #999;
|
||||||
|
margin-left: 8px;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item.selected.has-children::after {
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 主内容区域 */
|
||||||
|
.main-container {
|
||||||
|
margin-top: 50px; /* 为固定工具栏留出空间 */
|
||||||
|
height: calc(100vh - 50px);
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 加载动画 */
|
||||||
|
.loading-spinner {
|
||||||
|
display: inline-block;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border: 2px solid rgba(24, 144, 255, 0.2);
|
||||||
|
border-radius: 50%;
|
||||||
|
border-top-color: #1890ff;
|
||||||
|
animation: spin 1s ease-in-out infinite;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import './Toolbar.css';
|
import './ToolBar.css';
|
||||||
|
|
||||||
const Toolbar = ({
|
const Toolbar = ({
|
||||||
isExpanded,
|
|
||||||
onToggle,
|
|
||||||
onRoleChange,
|
onRoleChange,
|
||||||
onChatChange
|
onChatChange
|
||||||
}) => {
|
}) => {
|
||||||
@@ -11,9 +9,13 @@ const Toolbar = ({
|
|||||||
const [selectedChat, setSelectedChat] = useState(null);
|
const [selectedChat, setSelectedChat] = useState(null);
|
||||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||||
const [roleData, setRoleData] = useState({});
|
const [roleData, setRoleData] = useState({});
|
||||||
|
const [hoveredRole, setHoveredRole] = useState(null);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const dropdownRef = useRef(null);
|
||||||
|
|
||||||
// 获取角色和聊天数据
|
// 获取角色和聊天数据
|
||||||
const fetchRoleData = async () => {
|
const fetchRoleData = async () => {
|
||||||
|
setIsLoading(true);
|
||||||
console.log('开始获取角色数据...');
|
console.log('开始获取角色数据...');
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/tool_bar/get_all_role_and_chat', {
|
const response = await fetch('/api/tool_bar/get_all_role_and_chat', {
|
||||||
@@ -30,66 +32,123 @@ const fetchRoleData = async () => {
|
|||||||
setRoleData(data);
|
setRoleData(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取角色数据失败:', error);
|
console.error('获取角色数据失败:', error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 点击外部关闭下拉菜单
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClickOutside = (event) => {
|
||||||
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
||||||
|
setIsDropdownOpen(false);
|
||||||
|
setHoveredRole(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', handleClickOutside);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
// 处理下拉框展开/收起
|
// 处理下拉框展开/收起
|
||||||
const handleDropdownToggle = async () => {
|
const handleDropdownToggle = () => {
|
||||||
await fetchRoleData();
|
// 每次点击都重新获取数据
|
||||||
|
fetchRoleData();
|
||||||
setIsDropdownOpen(!isDropdownOpen);
|
setIsDropdownOpen(!isDropdownOpen);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理角色选择
|
// 处理角色选择
|
||||||
const handleRoleSelect = (role) => {
|
const handleRoleSelect = (role) => {
|
||||||
setSelectedRole(role);
|
setSelectedRole(role);
|
||||||
setSelectedChat(null);
|
|
||||||
if (onRoleChange) {
|
if (onRoleChange) {
|
||||||
onRoleChange(role);
|
onRoleChange(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果该角色有聊天记录,默认选择第一个
|
||||||
|
if (roleData[role] && roleData[role].length > 0) {
|
||||||
|
const firstChat = roleData[role][0];
|
||||||
|
setSelectedChat(firstChat);
|
||||||
|
if (onChatChange) {
|
||||||
|
onChatChange(role, firstChat);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setSelectedChat(null);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理聊天选择
|
// 处理聊天选择
|
||||||
const handleChatSelect = (chat) => {
|
const handleChatSelect = (chat) => {
|
||||||
setSelectedChat(chat);
|
setSelectedChat(chat);
|
||||||
setIsDropdownOpen(false);
|
setIsDropdownOpen(false);
|
||||||
|
setHoveredRole(null);
|
||||||
if (onChatChange) {
|
if (onChatChange) {
|
||||||
onChatChange(selectedRole, chat);
|
onChatChange(selectedRole, chat);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
// 处理角色项悬停
|
||||||
<div className={`toolbar ${isExpanded ? 'expanded' : ''}`}>
|
const handleRoleHover = (role) => {
|
||||||
<button
|
setHoveredRole(role);
|
||||||
className="toolbar-toggle-btn"
|
};
|
||||||
onClick={onToggle}
|
|
||||||
>
|
|
||||||
{isExpanded ? '▼' : '▲'}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{isExpanded && (
|
// 处理角色项离开
|
||||||
<div className="toolbar-content">
|
const handleRoleLeave = () => {
|
||||||
<div className="toolbar-dropdown">
|
// 不立即清除hoveredRole,以便用户可以移动到二级菜单
|
||||||
<label>选择角色</label>
|
};
|
||||||
<div className="dropdown-container">
|
|
||||||
|
// 处理二级菜单进入
|
||||||
|
const handleNestedMenuEnter = () => {
|
||||||
|
// 防止鼠标移动时菜单消失
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理二级菜单离开
|
||||||
|
const handleNestedMenuLeave = () => {
|
||||||
|
setHoveredRole(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="toolbar">
|
||||||
|
<div className="toolbar-icons">
|
||||||
|
{/* Logo图标 */}
|
||||||
|
<div className="toolbar-icon" title="首页">
|
||||||
|
🤖
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 角色选择下拉框 */}
|
||||||
|
<div className="dropdown-container" ref={dropdownRef}>
|
||||||
<div
|
<div
|
||||||
className="dropdown-trigger"
|
className="toolbar-icon"
|
||||||
onClick={handleDropdownToggle}
|
onClick={handleDropdownToggle}
|
||||||
|
title="选择角色"
|
||||||
>
|
>
|
||||||
{selectedRole || '选择角色'}
|
👤
|
||||||
<span className="dropdown-arrow">▼</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isDropdownOpen && (
|
{isDropdownOpen && (
|
||||||
<div className="dropdown-menu">
|
<div className="dropdown-menu visible">
|
||||||
{Object.keys(roleData).map(role => (
|
{isLoading ? (
|
||||||
|
<div className="dropdown-item">
|
||||||
|
加载中...
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
Object.keys(roleData).map(role => (
|
||||||
<div
|
<div
|
||||||
key={role}
|
key={role}
|
||||||
className={`dropdown-item ${selectedRole === role ? 'selected' : ''}`}
|
className={`dropdown-item ${selectedRole === role ? 'selected' : ''} ${roleData[role] && roleData[role].length > 0 ? 'has-children' : ''}`}
|
||||||
onClick={() => handleRoleSelect(role)}
|
onClick={() => handleRoleSelect(role)}
|
||||||
|
onMouseEnter={() => handleRoleHover(role)}
|
||||||
|
onMouseLeave={handleRoleLeave}
|
||||||
>
|
>
|
||||||
{role}
|
{role}
|
||||||
{selectedRole === role && (
|
{/* 当悬停在角色上时显示二级菜单 */}
|
||||||
<div className="nested-dropdown">
|
{hoveredRole === role && roleData[role] && roleData[role].length > 0 && (
|
||||||
|
<div
|
||||||
|
className={`nested-dropdown visible`}
|
||||||
|
onMouseEnter={handleNestedMenuEnter}
|
||||||
|
onMouseLeave={handleNestedMenuLeave}
|
||||||
|
>
|
||||||
{roleData[role].map(chat => (
|
{roleData[role].map(chat => (
|
||||||
<div
|
<div
|
||||||
key={chat}
|
key={chat}
|
||||||
@@ -105,13 +164,20 @@ const fetchRoleData = async () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 其他工具栏图标 */}
|
||||||
|
<div className="toolbar-icon" title="设置">
|
||||||
|
⚙️
|
||||||
|
</div>
|
||||||
|
<div className="toolbar-icon" title="帮助">
|
||||||
|
❓
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,99 +1,61 @@
|
|||||||
/* ==================== 全局样式 ==================== */
|
/* 重置样式 */
|
||||||
|
|
||||||
/* 全局重置与基础设置 */
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body, #root {
|
html, body, .app {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden; /* 禁止全局滚动 */
|
overflow: hidden; /* 防止出现滚动条 */
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
color: #333;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 布局容器 */
|
.app {
|
||||||
#root {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 滚动条美化 */
|
/* 主内容区域 */
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
height: 6px;
|
|
||||||
}
|
|
||||||
::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
background: #ccc;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
|
||||||
background: #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==================== 主内容区域 ==================== */
|
|
||||||
|
|
||||||
.main-container {
|
.main-container {
|
||||||
position: absolute;
|
flex: 1; /* 这会让主容器占据剩余的所有空间 */
|
||||||
top: 50px;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: hidden;
|
overflow: hidden; /* 防止内容溢出 */
|
||||||
|
height: 100%; /* 确保高度填满 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 左侧栏 */
|
/* 左侧栏 */
|
||||||
.sidebar-left {
|
.sidebar-left {
|
||||||
width: 20%;
|
width: 250px; /* 或者你想要的宽度 */
|
||||||
background-color: #fff;
|
height: 继承父元素高度; /* 确保高度填满 */
|
||||||
border-right: 1px solid #ddd;
|
overflow-y: auto; /* 内容过多时显示滚动条 */
|
||||||
overflow-y: auto;
|
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 中间栏:聊天框 */
|
/* 中间聊天区域 */
|
||||||
.chat-area {
|
.chat-area {
|
||||||
flex: 3;
|
flex: 1; /* 占据剩余空间 */
|
||||||
background-color: #fafafa;
|
height: 100%; /* 确保高度填满 */
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden; /* 防止内容溢出 */
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 右侧栏 */
|
/* 右侧栏 */
|
||||||
.sidebar-right {
|
.sidebar-right {
|
||||||
width: 20%;
|
width: 300px; /* 或者你想要的宽度 */
|
||||||
background-color: #fff;
|
height: 100%; /* 确保高度填满 */
|
||||||
border-left: 1px solid #ddd;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 右侧上下分割 */
|
|
||||||
.right-top, .right-bottom {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 10px;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 右侧栏顶部 */
|
||||||
.right-top {
|
.right-top {
|
||||||
border-bottom: 1px solid #ddd;
|
flex: 1; /* 占据剩余空间 */
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 右侧栏底部 */
|
||||||
|
.right-bottom {
|
||||||
|
height: 200px; /* 或者你想要的高度 */
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user