Files
wecom_it_smart_desk/docs/01-产品文档/02-会话管理/原型-REQ-会话-001-工具栏统一设计v0.8-图片头像.archive.html
T
2026-08-11 14:15:36 +08:00

454 lines
18 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>工具栏统一设计 v0.8 - 居中布局 + 图片头像</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
background: linear-gradient(135deg, #e0e7ff 0%, #f3e8ff 50%, #fce7f3 100%);
color: #1a1a1a;
padding: 32px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
min-height: 100vh;
}
h1 { font-size: 24px; font-weight: 600; letter-spacing: -0.02em; margin-bottom: 8px; color: #1a1a1a; }
h2 { font-size: 17px; font-weight: 600; letter-spacing: -0.01em; margin: 40px 0 12px; color: #1a1a1a; }
h3 { font-size: 14px; font-weight: 600; margin: 16px 0 8px; color: #333; }
.meta { color: #6b7280; font-size: 12.5px; margin-bottom: 24px; }
/* === 模拟 InputBar 容器(居中布局) === */
.inputbar-mock {
width: 100%;
max-width: 800px;
margin: 0 auto;
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.8);
border-radius: 20px;
padding: 16px;
box-shadow:
0 1px 0 rgba(255, 255, 255, 1) inset,
0 -1px 0 rgba(99, 102, 241, 0.08) inset,
0 4px 8px rgba(99, 102, 241, 0.05),
0 16px 32px rgba(99, 102, 241, 0.1);
}
.inputbar-mock-field {
width: 100%;
min-height: 100px;
background: rgba(255, 255, 255, 0.4);
border: 1px solid rgba(99, 102, 241, 0.12);
border-radius: 12px;
padding: 12px 16px;
font-size: 13px;
color: #9ca3af;
line-height: 1.6;
}
/* === 居中的整体工具栏(v0.5 玻璃拟态) === */
.glass-toolbar {
display: flex;
align-items: center;
justify-content: center; /* 居中 */
gap: 6px;
padding: 8px 16px;
margin: 0 auto 16px;
width: fit-content;
background: rgba(255, 255, 255, 0.55);
backdrop-filter: blur(28px) saturate(180%);
-webkit-backdrop-filter: blur(28px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.7);
border-radius: 18px;
box-shadow:
0 1px 0 rgba(255, 255, 255, 0.9) inset,
0 -1px 0 rgba(99, 102, 241, 0.08) inset,
0 2px 4px rgba(99, 102, 241, 0.05),
0 8px 16px rgba(99, 102, 241, 0.08),
0 24px 48px rgba(99, 102, 241, 0.05);
position: relative;
}
.glass-toolbar::before {
content: "";
position: absolute;
inset: 0;
border-radius: 18px;
background: linear-gradient(180deg, rgba(255, 255, 255, 0.6) 0%, rgba(255, 255, 255, 0.1) 40%, transparent 60%);
pointer-events: none;
}
.glass-toolbar::after {
content: "";
position: absolute;
inset: 0;
border-radius: 18px;
background: radial-gradient(circle at 20% 20%, rgba(99, 102, 241, 0.15) 0%, transparent 50%),
radial-gradient(circle at 80% 80%, rgba(236, 72, 153, 0.12) 0%, transparent 50%);
pointer-events: none;
z-index: 0;
}
/* === 玻璃按钮(38px 圆) === */
.glass-btn {
width: 38px;
height: 38px;
border: 1px solid rgba(255, 255, 255, 0.6);
background: rgba(255, 255, 255, 0.5);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
-webkit-tap-highlight-color: transparent;
position: relative;
z-index: 2;
box-shadow:
0 1px 0 rgba(255, 255, 255, 0.9) inset,
0 -1px 0 rgba(0, 0, 0, 0.04) inset,
0 1px 2px rgba(0, 0, 0, 0.04),
0 2px 4px rgba(0, 0, 0, 0.04);
overflow: hidden;
}
.glass-btn:hover {
background: rgba(255, 255, 255, 0.7);
transform: translateY(-1px) scale(1.04);
}
.glass-btn:active { transform: translateY(0) scale(0.95); }
.glass-btn svg { width: 20px; height: 20px; }
/* === 坐席头像(图片 + 42px 圆) === */
.agent-avatar-img {
width: 42px;
height: 42px;
border-radius: 50%;
object-fit: cover;
object-position: center top;
display: block;
}
.agent-btn {
width: 42px;
height: 42px;
border-radius: 50%;
border: 2px solid transparent;
cursor: pointer;
position: relative;
z-index: 2;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
-webkit-tap-highlight-color: transparent;
overflow: hidden;
background: transparent;
}
.agent-btn img {
width: 100%;
height: 100%;
border-radius: 50%;
object-fit: cover;
object-position: center 15%;
}
/* 6 态 */
.agent-btn.is-active {
border: 2.5px solid transparent;
background: linear-gradient(135deg, #6366f1 0%, #a855f7 50%, #ec4899 100%) padding-box,
linear-gradient(135deg, #6366f1 0%, #a855f7 50%, #ec4899 100%) border-box;
box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.4) inset;
}
.agent-btn.is-active::before {
content: "";
position: absolute;
inset: -2px;
border-radius: 50%;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.3) 0%, transparent 50%);
pointer-events: none;
}
.agent-btn.is-disabled { opacity: 0.35; cursor: not-allowed; filter: grayscale(0.7); }
.agent-btn.is-disabled:hover { transform: none; }
.agent-btn.is-urgent {
border: 2.5px solid #ef4444;
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.5), 0 4px 12px rgba(239, 68, 68, 0.3);
animation: agentGlow 1.6s ease-in-out infinite;
}
@keyframes agentGlow {
0%, 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0), 0 4px 12px rgba(239, 68, 68, 0.3); }
50% { box-shadow: 0 0 0 6px rgba(239, 68, 68, 0.18), 0 4px 12px rgba(239, 68, 68, 0.3); }
}
.agent-btn.is-waiting {
border: 2.5px solid #f59e0b;
box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.5), 0 4px 12px rgba(245, 158, 11, 0.3);
}
.agent-btn.is-end {
border: 2.5px solid #10b981;
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.5), 0 4px 12px rgba(16, 185, 129, 0.3);
}
.agent-btn.is-reopen {
border: 2.5px solid #3b82f6;
box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.5), 0 4px 12px rgba(59, 130, 246, 0.3);
}
/* === 演示容器 === */
.demo-block {
background: rgba(255, 255, 255, 0.6);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.7);
border-radius: 18px;
padding: 32px;
margin-bottom: 20px;
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.9) inset, 0 8px 16px rgba(99, 102, 241, 0.06);
}
.demo-label { font-size: 11.5px; color: #6b7280; text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 14px; font-weight: 500; }
/* === 状态切换 tab === */
.state-tabs { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 20px; padding-bottom: 16px; border-bottom: 1px solid rgba(99, 102, 241, 0.12); }
.state-tab {
padding: 6px 12px;
border: 1px solid rgba(99, 102, 241, 0.2);
background: rgba(255, 255, 255, 0.6);
border-radius: 8px;
cursor: pointer;
font-size: 12.5px;
color: #4b5563;
transition: all 0.15s;
font-family: inherit;
}
.state-tab:hover { background: rgba(255, 255, 255, 0.8); }
.state-tab.active { background: linear-gradient(135deg, #6366f1, #8b5cf6); color: #fff; border-color: transparent; }
.state-desc { color: #4b5563; font-size: 12.5px; line-height: 1.7; background: rgba(255, 255, 255, 0.5); padding: 12px 14px; border-radius: 10px; border-left: 2px solid #6366f1; margin-top: 12px; backdrop-filter: blur(10px); }
.state-desc strong { color: #1a1a1a; }
.note { background: rgba(254, 243, 199, 0.6); border-left: 3px solid #d97706; padding: 12px 16px; border-radius: 4px; font-size: 12.5px; color: #78350f; margin: 16px 0; backdrop-filter: blur(10px); }
.avatar-display { display: flex; align-items: center; justify-content: center; padding: 24px; }
</style>
</head>
<body>
<h1>工具栏统一设计 v0.8 — 居中布局 + 真实图片头像</h1>
<p class="meta">v0.5 玻璃拟态工具栏 + 整体居中 + 用户提供的 3D 拟人吉祥物图片 · 2026-08-04 凌晨</p>
<div class="note">
📌 本版核心改动(对比 v0.7):<br>
<strong>头像用真实图片</strong>(你提供的 3D 拟人吉祥物,108KB JPG),不再是 SVG — 品牌化 + 高级感<br>
<strong>整体工具栏居中</strong>(不再是左上角 absolute 定位) — 居中对齐工具栏的 4 按钮<br>
• v0.5 玻璃拟态✅ 沿用<br>
• 4 工具图标✅ 沿用 v0.5/v0.6 升级版(高质感几何)
</div>
<h2>🎨 整体 InputBar 工具栏(居中布局)</h2>
<div class="demo-block">
<div class="demo-label">InputBar 工具栏 · 整体居中(不再左上角)+ 4 按钮 + 真实图片坐席头像</div>
<div class="inputbar-mock">
<!-- 居中的整体工具栏(v0.5 玻璃拟态) -->
<div class="glass-toolbar">
<!-- 表情 -->
<button class="glass-btn" title="表情">
<svg viewBox="0 0 22 22" fill="none">
<circle cx="11" cy="11" r="8" fill="url(#g-cool)"/>
<ellipse cx="11" cy="6" rx="5" ry="2" fill="url(#g-hl)"/>
<ellipse cx="8" cy="10" rx="1.2" ry="1.4" fill="#1e1b4b"/>
<ellipse cx="14" cy="10" rx="1.2" ry="1.4" fill="#1e1b4b"/>
<circle cx="8.4" cy="9.5" r="0.4" fill="white"/>
<circle cx="14.4" cy="9.5" r="0.4" fill="white"/>
<path d="M7.5 13.5 Q11 16 14.5 13.5" fill="none" stroke="#1e1b4b" stroke-width="1.4" stroke-linecap="round"/>
</svg>
</button>
<!-- 文件 -->
<button class="glass-btn" title="文件">
<svg viewBox="0 0 22 22" fill="none">
<path d="M4 2 L13 2 L19 8 L19 19 L4 19 Z" fill="url(#g-doc)" stroke="#818cf8" stroke-width="0.8" stroke-linejoin="round"/>
<path d="M13 2 L13 8 L19 8" fill="url(#g-doc-fold)" stroke="#818cf8" stroke-width="0.8" stroke-linejoin="round"/>
<ellipse cx="12" cy="4" rx="5" ry="0.8" fill="url(#g-hl)"/>
<line x1="6" y1="11" x2="16" y2="11" stroke="#6366f1" stroke-width="0.8" opacity="0.5"/>
<line x1="6" y1="14" x2="14" y2="14" stroke="#6366f1" stroke-width="0.8" opacity="0.5"/>
</svg>
</button>
<!-- 语音 -->
<button class="glass-btn" title="语音">
<svg viewBox="0 0 22 22" fill="none">
<rect x="7" y="2" width="8" height="12" rx="4" fill="url(#g-mic)"/>
<ellipse cx="10" cy="3.5" rx="1.2" ry="3" fill="white" opacity="0.55"/>
<path d="M3 11 Q3 17 11 17 Q19 17 19 11" fill="none" stroke="url(#g-mic)" stroke-width="2" stroke-linecap="round"/>
<line x1="11" y1="17" x2="11" y2="20" stroke="url(#g-mic)" stroke-width="2" stroke-linecap="round"/>
</svg>
</button>
<!-- 坐席(真实图片) -->
<div id="agent-stage"></div>
</div>
<!-- 输入框(在工具栏下方) -->
<div class="inputbar-mock-field">
请输入消息...(输入框在工具栏下方)
</div>
</div>
</div>
<h2>🎬 坐席按钮 6 态切换(真实图片)</h2>
<div class="demo-block">
<div class="state-tabs">
<button class="state-tab active" data-state="active">active</button>
<button class="state-tab" data-state="disabled">disabled</button>
<button class="state-tab" data-state="urgent">urgent</button>
<button class="state-tab" data-state="waiting">waiting</button>
<button class="state-tab" data-state="end">end</button>
<button class="state-tab" data-state="reopen">reopen</button>
</div>
<div class="avatar-display">
<div id="state-stage"></div>
</div>
<div class="state-desc" id="state-desc"></div>
</div>
<svg width="0" height="0" style="position: absolute;">
<defs>
<linearGradient id="g-cool" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#c7d2fe"/><stop offset="100%" stop-color="#818cf8"/>
</linearGradient>
<linearGradient id="g-hl" x1="50%" y1="0%" x2="50%" y2="100%">
<stop offset="0%" stop-color="white" stop-opacity="0.95"/><stop offset="100%" stop-color="white" stop-opacity="0"/>
</linearGradient>
<linearGradient id="g-doc" x1="50%" y1="0%" x2="50%" y2="100%">
<stop offset="0%" stop-color="#ffffff"/><stop offset="100%" stop-color="#dbeafe"/>
</linearGradient>
<linearGradient id="g-doc-fold" x1="50%" y1="0%" x2="50%" y2="100%">
<stop offset="0%" stop-color="#c7d2fe"/><stop offset="100%" stop-color="#818cf8"/>
</linearGradient>
<linearGradient id="g-mic" x1="50%" y1="0%" x2="50%" y2="100%">
<stop offset="0%" stop-color="#a78bfa"/><stop offset="50%" stop-color="#8b5cf6"/><stop offset="100%" stop-color="#6d28d9"/>
</linearGradient>
</defs>
</svg>
<h2>📊 v0.8 vs v0.7 对比</h2>
<table style="width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 12.5px;">
<thead>
<tr style="background: rgba(255, 255, 255, 0.6);">
<th style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px; text-align: left;">维度</th>
<th style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px; text-align: left;">v0.7 终极版 SVG</th>
<th style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px; text-align: left;">v0.8 图片头像</th>
</tr>
</thead>
<tbody>
<tr>
<td style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px;">头像形式</td>
<td style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px;">SVG(西装+眼镜+领带)</td>
<td style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px;"><strong>真实图片(3D 渲染的吉祥物)</strong></td>
</tr>
<tr>
<td style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px;">高级感</td>
<td style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px;">★★★(SVG 上限)</td>
<td style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px;"><strong>★★★★★+(真实 3D 渲染)</strong></td>
</tr>
<tr>
<td style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px;">整体工具栏布局</td>
<td style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px;">左上角 absolute</td>
<td style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px;"><strong>整体居中(justify-content: center)</strong></td>
</tr>
<tr>
<td style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px;">实施成本</td>
<td style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px;">极低(0.5 天)</td>
<td style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px;"><strong>低(0.5-1 天,需放置图片到项目 public 目录)</strong></td>
</tr>
<tr>
<td style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px;">资源依赖</td>
<td style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px;"></td>
<td style="border: 1px solid rgba(99, 102, 241, 0.15); padding: 8px;">需要 1 张 108KB JPG(已有)</td>
</tr>
</tbody>
</table>
<h2>📦 实施说明(图片放置 + 引用)</h2>
<div class="note">
<strong>实施步骤:</strong><br>
1. 复制图片到项目:`src/frontend-h5/public/avatars/agent.png` 或 `agent.jpg`<br>
2. InputBar.vue 引用:`&lt;img src="/avatars/agent.png" class="agent-avatar-img"&gt;`<br>
3. CSS 控制:`border-radius: 50%`、`object-fit: cover`、`object-position: center top`(调整头像位置)<br>
4. 6 态用边框颜色 + 滤镜 + 阴影变化(图片本身不变,只换外圈装饰)
</div>
<h2>❓ 待你拍板</h2>
<ol style="margin-left: 20px; line-height: 1.8;">
<li><strong>整体居中布局</strong>符合期望吗?(不再是左上角)</li>
<li><strong>真实图片头像</strong>符合期望吗?(你提供的 3D 拟人吉祥物)</li>
<li><strong>头像大小</strong>:42px(当前)还是 38px(与其他工具按钮一致)?</li>
<li><strong>6 态边框设计</strong>OK 吗?(紫粉渐变 / 红 / 橙 / 绿 / 蓝 + 阴影)</li>
</ol>
<p style="margin-top: 32px; padding-top: 16px; border-top: 1px solid rgba(99, 102, 241, 0.15); color: #6b7280; font-size: 11.5px;">
v0.8 图片头像 · 2026-08-04 · Duckula 主理人 · 等待用户拍板
</p>
<script>
const stateMap = {
active: {
cls: 'is-active',
desc: '<strong>active</strong>: 紫粉渐变边框 + 顶部高光 + 立体感阴影 — 默认"客服在线"。3D 拟人吉祥物清晰可见,品牌感最强'
},
disabled: {
cls: 'is-disabled',
desc: '<strong>disabled</strong>: 灰度滤镜 + opacity 0.35 + cursor not-allowed — 表达"不可用",头像变灰保留轮廓'
},
urgent: {
cls: 'is-urgent',
desc: '<strong>urgent</strong>: 红色边框 + 1.6s 发光呼吸(光晕扩散 6px) — 表达"紧急,需关注"'
},
waiting: {
cls: 'is-waiting',
desc: '<strong>waiting</strong>: 橙色边框 + 橙色阴影 — 表达"排队中"'
},
end: {
cls: 'is-end',
desc: '<strong>end</strong>: 绿色边框 + 绿色阴影 — 表达"已接入坐席,通话中"'
},
reopen: {
cls: 'is-reopen',
desc: '<strong>reopen</strong>: 蓝色边框 + 蓝色阴影 — 表达"会话已关闭,可重新打开"'
}
};
const imgHTML = '<img src="./agent-avatar-v0.8.jpg" class="agent-avatar-img" alt="客服" />';
const stateStage = document.getElementById('state-stage');
const toolbarStage = document.getElementById('agent-stage');
const stateDesc = document.getElementById('state-desc');
const stateTabs = document.querySelectorAll('.state-tab');
function renderAgent(stage) {
const s = stateMap[currentState];
stage.innerHTML = `<button class="agent-btn ${s.cls}">${imgHTML}</button>`;
}
function renderAll() {
const s = stateMap[currentState];
// 顶部工具栏的坐席
if (toolbarStage) renderAgent(toolbarStage);
// 6 态演示区
if (stateStage) stateStage.innerHTML = `<button class="agent-btn ${s.cls}" style="width: 80px; height: 80px;">${imgHTML}</button>`;
if (stateDesc) stateDesc.innerHTML = s.desc;
}
let currentState = 'active';
stateTabs.forEach(tab => {
tab.addEventListener('click', () => {
stateTabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
currentState = tab.dataset.state;
renderAll();
});
});
renderAll();
</script>
</body>
</html>