完成初步布局框架

This commit is contained in:
2026-03-18 20:41:22 +08:00
parent 04ea889d75
commit 1aa90f5acf
2 changed files with 346 additions and 21 deletions

View File

@@ -1,15 +1,116 @@
import React from 'react';
import React, { useState, useRef } from 'react';
const ChatBox = () => {
const [isHtmlRender, setIsHtmlRender] = useState(false);
const [isImageGen, setIsImageGen] = useState(false);
const [isDynamicTable, setIsDynamicTable] = useState(false);
const [isSettingsExpanded, setIsSettingsExpanded] = useState(false);
const textareaRef = useRef(null);
// 自动调整 Textarea 高度
const adjustHeight = () => {
const textarea = textareaRef.current;
if (textarea) {
textarea.style.height = 'auto'; // 重置高度以获取正确的 scrollHeight
// 重新设置高度,限制最大高度由 CSS max-height 控制
textarea.style.height = textarea.scrollHeight + 'px';
}
};
const handleInput = () => {
adjustHeight();
};
// 生成示例数据
const generateMessages = () => {
const messages = [];
for (let i = 1; i <= 150; i++) {
const isUser = i % 2 !== 0;
messages.push({
id: i,
role: isUser ? 'user' : 'ai',
content: isUser
? `这是第 ${i} 条用户消息。这是一段比较长的文本,用来测试气泡的换行效果以及滚动条的表现。`
: `这是第 ${i} 条 AI 回复。<b>包含 HTML 标签</b>的内容。如果渲染开关开启,这里应该显示粗体字。如果不开启,应该显示原始标签。`
});
}
return messages;
};
const messages = generateMessages();
return (
<div className="chat-box">
{/* 上方:消息列表区域 */}
<div className="chat-messages">
{/* 消息列表占位 */}
<div>历史消息区域</div>
{/* 右上角:可折叠设置面板 */}
<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) => (
<div key={msg.id} className={`message ${msg.role}`}>
<div className="bubble">
{isHtmlRender ? (
<div dangerouslySetInnerHTML={{ __html: msg.content }} />
) : (
<div>{msg.content}</div>
)}
</div>
</div>
))}
</div>
<div className="chat-input-area">
{/* 输入框占位 */}
<input type="text" placeholder="输入消息..." disabled />
{/* 下方:输入框区域 */}
<div className="chat-input-wrapper">
<div className="chat-input-area">
<textarea
ref={textareaRef}
placeholder="输入消息..."
onInput={handleInput}
rows="1"
// 关键修改显式设置初始样式高度确保可见
style={{ height: '42px' }}
/>
</div>
<button className="send-button">发送</button>
</div>
</div>
);

View File

@@ -18,12 +18,14 @@ html, body, #root {
#root {
display: flex;
flex-direction: column;
position: relative; /* 关键:让绝对定位的工具栏相对于此容器定位 */
position: relative; /* 让绝对定位的工具栏相对于此容器定位 */
height: 100%; /* 确保高度占满 */
width: 100%;
}
/* --- 顶部工具栏区域 --- */
.toolbar {
position: absolute; /* 关键:脱离文档流,实现覆盖效果 */
position: absolute; /* 脱离文档流,实现覆盖效果 */
top: 0;
left: 0;
right: 0;
@@ -58,16 +60,31 @@ html, body, #root {
display: block; /* 展开时显示 */
}
/* 工具栏按钮样式微调 */
.toolbar-toggle-btn {
background: none;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
padding: 4px 8px;
font-size: 0.8rem;
color: #666;
}
/* --- 主内容区域 --- */
.main-container {
/* 关键布局:占据除工具栏外的所有空间 */
/* 由于工具栏是 absolute 的,我们需要手动设置 top */
/* 占据除工具栏外的所有空间 */
position: absolute;
top: 50px; /* 对应 .toolbar 的默认高度 */
bottom: 0;
left: 0;
right: 0;
/* 【关键修复 1】显式设置高度为 100%,而不是 auto */
/* 这确保了容器高度由视口决定,而不是由内容撑开 */
height: 100%;
width: 100%;
display: flex;
overflow: hidden; /* 防止主容器本身滚动 */
}
@@ -77,17 +94,21 @@ html, body, #root {
width: 20%;
background-color: #fff;
border-right: 1px solid #ddd;
overflow-y: auto; /* 独立滚动 */
overflow-y: auto; /* 独立滚动 1】左侧栏滚动 */
height: 100%; /* 填满父容器高度 */
}
/* --- 中间栏:聊天框 --- */
.chat-area {
flex: 3;
background-color: #fafafa;
height: 100%; /* 填满父容器高度 */
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden; /* 内部元素滚动,外层不滚动 */
flex-direction: column; /* 垂直排列:消息列表 + 输入框 */
overflow: hidden; /* 禁止外层滚动,强制内部滚动 */
position: relative; /* 为绝对定位元素提供参考 */
}
.sidebar-right {
@@ -103,7 +124,7 @@ html, body, #root {
/* 右侧上下分割 */
.right-top, .right-bottom {
flex: 1;
overflow-y: auto; /* 独立滚动 */
overflow-y: auto; /* 【独立滚动 2 & 3】右侧上下部分独立滚动 */
padding: 10px;
min-height: 0; /* 关键:允许 flex 子项收缩 */
}
@@ -111,15 +132,155 @@ html, body, #root {
border-bottom: 1px solid #ddd;
}
/* 工具栏按钮样式微调 */
.toolbar-toggle-btn {
background: none;
/* --- 消息列表容器 --- */
.chat-messages {
/* 【关键修改 1】不再自动占据剩余空间高度由内容决定 */
flex: 0 0 auto;
/* 【关键修改 2】设置最大高度例如视口高度的 80% */
max-height: calc(100vh - 110px);
/* 强制约束高度,防止内容撑开 */
min-height: 0;
/* 内部独立滚动 */
overflow-y: auto; /* 内容溢出时显示滚动条 */
padding: 20px;
/* 增加顶部 padding防止内容被绝对定位的设置面板遮挡 */
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;
}
/* --- 输入区域容器 --- */
.chat-input-wrapper {
background-color: #fff;
border-top: 1px solid #ddd;
padding: 10px 20px;
/* 使用 flex 属性,不伸缩,高度由内容决定 */
flex: 0 0 auto;
/* 保持 flex 布局以便内部对齐 */
display: flex;
align-items: flex-end; /* 底部对齐,适应多行输入框 */
gap: 10px;
width: 100%;
}
.chat-input-area {
flex: 1; /* 占据除按钮外的所有宽度 */
display: flex;
/* 移除 height: 100%,让高度由内容决定 */
}
.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;
/* 【独立滚动 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;
padding: 4px 8px;
font-size: 0.8rem;
color: #666;
white-space: nowrap;
flex-shrink: 0;
margin-bottom: 0; /* 确保与输入框底部对齐 */
}
.send-button:hover {
background-color: #40a9ff;
}
/* 通用滚动条美化 */
@@ -137,3 +298,66 @@ html, body, #root {
::-webkit-scrollbar-thumb:hover {
background: #aaa;
}
/* --- 聊天气泡样式美化 --- */
/* 消息行容器 */
.message {
display: flex;
width: 100%;
margin-bottom: 10px;
}
/* 区分左右布局 */
.message.user {
justify-content: flex-end; /* 用户消息靠右 */
}
.message.ai {
justify-content: flex-start; /* AI 消息靠左 */
}
/* 气泡本体 */
.message .bubble {
max-width: 70%; /* 气泡最大宽度,防止太长 */
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;
}