feat: 重构AI消息气泡结构,集成thinking、tool和content为统一block
- AI消息使用统一的可折叠容器(message-block) - thinking和tool使用相同样式,支持展开/收起 - 工具调用显示input/output内容,区分入参和出参 - 添加状态徽章样式(running/completed/error + 脉冲动画) - 消息气泡添加毛玻璃效果和精细阴影 - 删除file场景(分析上传的报表) - 同步更新main specs
This commit is contained in:
@@ -4,7 +4,6 @@ export const conversations = [
|
||||
{ id: 'welcome', title: '新对话', time: '欢迎页', scene: 'welcome', status: 'running' },
|
||||
{ id: 'text', title: '代码重构方案讨论', time: '普通对话', scene: 'text', status: 'running' },
|
||||
{ id: 'skill', title: '查询客户数据', time: '调用 Skill', scene: 'skill', status: 'running' },
|
||||
{ id: 'file', title: '分析上传的报表', time: '上传文件', scene: 'file', status: 'running' },
|
||||
{ id: 'code', title: '生成 Python 函数', time: '代码展示', scene: 'code', status: 'running' },
|
||||
{ id: 'table', title: '查询销售报表', time: '表格数据', scene: 'table', status: 'running' },
|
||||
{ id: 'multiTurn', title: '产品方案讨论', time: '多轮对话', scene: 'multiTurn', status: 'running' },
|
||||
@@ -74,12 +73,13 @@ export function getChatScenes() {
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar assistant">🤖</div>
|
||||
<div class="message-content">
|
||||
<div class="message-thinking">
|
||||
<div class="message-thinking-header">
|
||||
<span class="message-thinking-icon">▶</span>
|
||||
<span>已深度思考</span>
|
||||
<div class="message-block expanded">
|
||||
<div class="message-block-header">
|
||||
<span class="message-block-header-icon">▶</span>
|
||||
<span class="message-block-header-title">已深度思考</span>
|
||||
<span class="message-block-header-status">2.1s</span>
|
||||
</div>
|
||||
<div class="message-thinking-content">
|
||||
<div class="message-block-content">
|
||||
<p>让我分析一下代码重构的思路:</p>
|
||||
<ul>
|
||||
<li>首先识别代码中的重复模式和冗余逻辑</li>
|
||||
@@ -90,6 +90,7 @@ export function getChatScenes() {
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-bubble">
|
||||
<p>好的,我来帮你重构这段代码。首先让我分析一下现有代码的问题,然后提供优化方案。</p>
|
||||
<p style="margin-top: 12px;"><strong>优化建议:</strong></p>
|
||||
@@ -114,23 +115,13 @@ export function getChatScenes() {
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar assistant">🤖</div>
|
||||
<div class="message-content">
|
||||
<div class="message-bubble">
|
||||
<p style="display: flex; align-items: center; gap: 8px; color: #3B82F6;">
|
||||
🧩 已加载CRM客户查询技能
|
||||
</p>
|
||||
</div>
|
||||
<div class="message-time">10:15</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar assistant">🤖</div>
|
||||
<div class="message-content">
|
||||
<div class="message-thinking">
|
||||
<div class="message-thinking-header">
|
||||
<span class="message-thinking-icon">▶</span>
|
||||
<span>已深度思考</span>
|
||||
<div class="message-block expanded">
|
||||
<div class="message-block-header">
|
||||
<span class="message-block-header-icon">▶</span>
|
||||
<span class="message-block-header-title">已深度思考</span>
|
||||
<span class="message-block-header-status">1.8s</span>
|
||||
</div>
|
||||
<div class="message-thinking-content">
|
||||
<div class="message-block-content">
|
||||
<p>正在调用CRM客户查询技能...</p>
|
||||
<ul>
|
||||
<li>识别用户意图:查询客户"张三"的订单信息</li>
|
||||
@@ -141,6 +132,56 @@ export function getChatScenes() {
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-block message-tool">
|
||||
<div class="message-block-header">
|
||||
<span class="message-block-header-icon">▶</span>
|
||||
<span class="message-block-header-title">🧩 使用工具: CRM客户查询</span>
|
||||
<span class="message-block-header-status message-tool-completed">✓ 0.4s</span>
|
||||
</div>
|
||||
<div class="message-block-content">
|
||||
<div class="message-tool-io">
|
||||
<div class="message-tool-io-label">INPUT</div>
|
||||
<div class="message-tool-io-content"><pre>{ "name": "张三", "timeRange": "1year" }</pre></div>
|
||||
</div>
|
||||
<div class="message-tool-io">
|
||||
<div class="message-tool-io-label">OUTPUT</div>
|
||||
<div class="message-tool-io-content"><pre>{ "customerId": "C001", "orders": 3, "totalAmount": 28560 }</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message-block message-tool">
|
||||
<div class="message-block-header">
|
||||
<span class="message-block-header-icon">▶</span>
|
||||
<span class="message-block-header-title">🧩 使用工具: 订单数据分析</span>
|
||||
<span class="message-block-header-status message-tool-completed">✓ 0.3s</span>
|
||||
</div>
|
||||
<div class="message-block-content">
|
||||
<div class="message-tool-io">
|
||||
<div class="message-tool-io-label">INPUT</div>
|
||||
<div class="message-tool-io-content"><pre>{ "orders": [...], "timeRange": "1year" }</pre></div>
|
||||
</div>
|
||||
<div class="message-tool-io">
|
||||
<div class="message-tool-io-label">OUTPUT</div>
|
||||
<div class="message-tool-io-content"><pre>{ "totalAmount": 28560, "orderCount": 3, "lastOrderDate": "2026-03-10" }</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message-block expanded">
|
||||
<div class="message-block-header">
|
||||
<span class="message-block-header-icon">▶</span>
|
||||
<span class="message-block-header-title">已深度思考</span>
|
||||
<span class="message-block-header-status">0.6s</span>
|
||||
</div>
|
||||
<div class="message-block-content">
|
||||
<p>根据分析结果,用户可能还想知道:</p>
|
||||
<ul>
|
||||
<li>订单详情列表</li>
|
||||
<li>客户等级权益</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-bubble">
|
||||
<p><strong>客户信息查询结果:</strong></p>
|
||||
<div style="margin-top: 12px; padding: 12px; background: #F8FAFC; border-radius: 8px;">
|
||||
@@ -154,70 +195,6 @@ export function getChatScenes() {
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
file: `
|
||||
<div class="message user">
|
||||
<div class="message-avatar user">张</div>
|
||||
<div class="message-content">
|
||||
<div class="message-bubble">
|
||||
<p>帮我分析一下这个 Excel 文件里的销售数据</p>
|
||||
<div style="margin-top: 12px; padding: 10px 14px; background: #F8FAFC; border-radius: 8px; display: inline-flex; align-items: center; gap: 10px; color: #475569;">
|
||||
<span style="font-size: 20px;">📊</span>
|
||||
<div>
|
||||
<div style="font-weight: 600;">Q1销售数据.xlsx</div>
|
||||
<div style="font-size: 12px; color: #94A3B8;">2.4 MB</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message-time">16:20</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar assistant">🤖</div>
|
||||
<div class="message-content">
|
||||
<div class="message-bubble">
|
||||
<p>文件已接收!正在分析数据...</p>
|
||||
<div style="margin-top: 14px;">
|
||||
<div style="height: 6px; background: #E2E8F0; border-radius: 3px; overflow: hidden;">
|
||||
<div style="width: 65%; height: 100%; background: linear-gradient(90deg, #3B82F6, #8B5CF6); border-radius: 3px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message-time">16:20</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar assistant">🤖</div>
|
||||
<div class="message-content">
|
||||
<div class="message-thinking">
|
||||
<div class="message-thinking-header">
|
||||
<span class="message-thinking-icon">▶</span>
|
||||
<span>已深度思考</span>
|
||||
</div>
|
||||
<div class="message-thinking-content">
|
||||
<p>正在分析 Excel 文件中的销售数据...</p>
|
||||
<ul>
|
||||
<li>文件格式识别:Excel (.xlsx),大小 2.4MB</li>
|
||||
<li>数据结构解析:包含日期、产品、销售额等字段</li>
|
||||
<li>时间范围:2026年 Q1(1-3月)</li>
|
||||
<li>计算指标:总销售额、同比增长、月度趋势、产品占比</li>
|
||||
<li>异常检测:无异常值或缺失数据</li>
|
||||
<li>关键洞察提取:3月表现突出,产品C增长势头强劲</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message-bubble">
|
||||
<p><strong>数据分析完成!以下是关键发现:</strong></p>
|
||||
<ul style="margin-top: 12px; padding-left: 20px;">
|
||||
<li>Q1 总销售额:<strong>¥128.5 万</strong>,同比增长 18%</li>
|
||||
<li>3 月份表现最佳,单月突破 50 万</li>
|
||||
<li>产品 A 占比最高(42%),产品 C 增长最快</li>
|
||||
</ul>
|
||||
<p style="margin-top: 12px;">需要生成可视化图表吗?</p>
|
||||
</div>
|
||||
<div class="message-time">16:22</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
code: `
|
||||
<div class="message user">
|
||||
<div class="message-avatar user">张</div>
|
||||
@@ -229,12 +206,13 @@ export function getChatScenes() {
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar assistant">🤖</div>
|
||||
<div class="message-content">
|
||||
<div class="message-thinking">
|
||||
<div class="message-thinking-header">
|
||||
<span class="message-thinking-icon">▶</span>
|
||||
<span>已深度思考</span>
|
||||
<div class="message-block expanded">
|
||||
<div class="message-block-header">
|
||||
<span class="message-block-header-icon">▶</span>
|
||||
<span class="message-block-header-title">已深度思考</span>
|
||||
<span class="message-block-header-status">0.8s</span>
|
||||
</div>
|
||||
<div class="message-thinking-content">
|
||||
<div class="message-block-content">
|
||||
<p>分析需求:列表去重并保持顺序</p>
|
||||
<ul>
|
||||
<li>使用字典键的有序性(Python 3.7+)</li>
|
||||
@@ -243,6 +221,7 @@ export function getChatScenes() {
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-bubble">
|
||||
<p>好的,这是一个常用的工具函数。以下提供两种实现方式:</p>
|
||||
<pre style="margin-top: 12px; padding: 16px; background: #1E293B; border-radius: 8px; overflow-x: auto;"><code style="color: #E2E8F0; font-family: 'Fira Code', monospace; font-size: 13px; line-height: 1.6;"><span style="color: #60A5FA;">def</span> <span style="color: #34D399;">dedupe_ordered</span>(lst):
|
||||
@@ -274,12 +253,13 @@ data = [<span style="color: #A78BFA;">1</span>, <span style="color: #A78BFA;">2<
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar assistant">🤖</div>
|
||||
<div class="message-content">
|
||||
<div class="message-thinking">
|
||||
<div class="message-thinking-header">
|
||||
<span class="message-thinking-icon">▶</span>
|
||||
<span>已深度思考</span>
|
||||
<div class="message-block expanded">
|
||||
<div class="message-block-header">
|
||||
<span class="message-block-header-icon">▶</span>
|
||||
<span class="message-block-header-title">已深度思考</span>
|
||||
<span class="message-block-header-status">1.2s</span>
|
||||
</div>
|
||||
<div class="message-thinking-content">
|
||||
<div class="message-block-content">
|
||||
<p>正在查询销售数据...</p>
|
||||
<ul>
|
||||
<li>时间范围:2026年3月1日 - 3月19日</li>
|
||||
@@ -288,6 +268,7 @@ data = [<span style="color: #A78BFA;">1</span>, <span style="color: #A78BFA;">2<
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-bubble">
|
||||
<p><strong>本月各部门销售业绩汇总:</strong></p>
|
||||
<table class="msg-table">
|
||||
@@ -423,12 +404,13 @@ data = [<span style="color: #A78BFA;">1</span>, <span style="color: #A78BFA;">2<
|
||||
<div class="message assistant">
|
||||
<div class="message-avatar assistant">🤖</div>
|
||||
<div class="message-content">
|
||||
<div class="message-thinking">
|
||||
<div class="message-thinking-header">
|
||||
<span class="message-thinking-icon">▶</span>
|
||||
<span>已深度思考</span>
|
||||
<div class="message-block expanded">
|
||||
<div class="message-block-header">
|
||||
<span class="message-block-header-icon">▶</span>
|
||||
<span class="message-block-header-title">已深度思考</span>
|
||||
<span class="message-block-header-status">30.2s</span>
|
||||
</div>
|
||||
<div class="message-thinking-content">
|
||||
<div class="message-block-content">
|
||||
<p>正在连接日志数据库...</p>
|
||||
<ul>
|
||||
<li>目标数据库:log-db-prod-01</li>
|
||||
@@ -437,6 +419,7 @@ data = [<span style="color: #A78BFA;">1</span>, <span style="color: #A78BFA;">2<
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message-bubble" style="border-left: 3px solid #EF4444; background: #FEF2F2;">
|
||||
<p style="display: flex; align-items: center; gap: 8px; color: #EF4444; font-weight: 600;">
|
||||
❌ 请求失败
|
||||
|
||||
@@ -155,20 +155,20 @@ function ChatPage() {
|
||||
useEffect(() => {
|
||||
if (!chatMessagesRef.current) return;
|
||||
|
||||
const thinkingElements = chatMessagesRef.current.querySelectorAll('.message-thinking');
|
||||
const blockElements = chatMessagesRef.current.querySelectorAll('.message-block');
|
||||
|
||||
const handleClick = (event) => {
|
||||
const thinkingElement = event.currentTarget;
|
||||
thinkingElement.classList.toggle('expanded');
|
||||
const blockElement = event.currentTarget;
|
||||
blockElement.classList.toggle('expanded');
|
||||
};
|
||||
|
||||
thinkingElements.forEach(el => {
|
||||
blockElements.forEach(el => {
|
||||
el.addEventListener('click', handleClick);
|
||||
el.style.cursor = 'pointer';
|
||||
});
|
||||
|
||||
return () => {
|
||||
thinkingElements.forEach(el => {
|
||||
blockElements.forEach(el => {
|
||||
el.removeEventListener('click', handleClick);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -105,7 +105,13 @@
|
||||
}
|
||||
|
||||
.message.assistant .message-bubble {
|
||||
background: var(--color-bg-2);
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.6);
|
||||
box-shadow:
|
||||
0 1px 2px rgba(0, 0, 0, 0.04),
|
||||
0 4px 12px rgba(0, 0, 0, 0.06);
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
@@ -122,17 +128,27 @@
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
// AI 思考过程
|
||||
.message-thinking {
|
||||
// AI 消息Block容器(thinking和tool通用)
|
||||
.message-block {
|
||||
margin-bottom: 12px;
|
||||
border: 1px solid var(--color-border-3);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
background: #FFFBEB;
|
||||
cursor: pointer;
|
||||
|
||||
&.expanded {
|
||||
.message-block-header-icon {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.message-block-content {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.message-thinking-header {
|
||||
.message-block-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
@@ -150,26 +166,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
.message-thinking-icon {
|
||||
.message-block-header-icon {
|
||||
transition: transform 0.2s ease;
|
||||
font-size: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.message-thinking.expanded .message-thinking-icon {
|
||||
transform: rotate(90deg);
|
||||
.message-block-header-title {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.message-thinking-content {
|
||||
.message-block-header-status {
|
||||
font-size: 12px;
|
||||
color: var(--color-text-3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.message-block-content {
|
||||
padding: 12px 14px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: #78350F;
|
||||
display: none;
|
||||
|
||||
.message-thinking.expanded & {
|
||||
display: block;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 8px 0 0 0;
|
||||
padding-left: 20px;
|
||||
@@ -180,6 +201,95 @@
|
||||
}
|
||||
}
|
||||
|
||||
.message-block-time {
|
||||
font-size: 12px;
|
||||
color: var(--color-text-4);
|
||||
padding: 8px 14px 12px;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
// 工具调用Block
|
||||
.message-tool {
|
||||
background: #F0F9FF;
|
||||
border-color: #BFDBFE;
|
||||
|
||||
.message-block-header {
|
||||
color: #1E40AF;
|
||||
background: rgba(219, 234, 254, 0.5);
|
||||
}
|
||||
|
||||
.message-block-header-status {
|
||||
color: #1D4ED8;
|
||||
}
|
||||
}
|
||||
|
||||
.message-tool-running {
|
||||
color: #3B82F6;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
animation: pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.message-tool-completed {
|
||||
color: #10B981;
|
||||
}
|
||||
|
||||
.message-tool-error {
|
||||
color: #EF4444;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
|
||||
// 工具调用的input/output分隔
|
||||
.message-tool-io {
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
padding-top: 12px;
|
||||
margin-top: 12px;
|
||||
|
||||
&:first-child {
|
||||
border-top: none;
|
||||
padding-top: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.message-tool-io-label {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #64748B;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.message-tool-io-content {
|
||||
background: #1E293B;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
overflow-x: auto;
|
||||
|
||||
pre, code {
|
||||
font-family: 'Fira Code', monospace;
|
||||
font-size: 13px;
|
||||
color: #E2E8F0;
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
// 输入区
|
||||
.chat-input-wrapper {
|
||||
padding: 16px 24px 24px;
|
||||
|
||||
Reference in New Issue
Block a user