Files
wecom_it_smart_desk/docs/01-产品文档/01-02产品设计/h5-user-v1_8.html
T
Simon facc04aa65 chore: docs 结构整改 + compose 双目录对齐(合并重建提交)
本提交为 .git 对象库损坏后的重建提交,内容等价于原先三个本地提交
(5e2fd4c2 / 57a53c98 / 5d7e1873)的累积结果,未做任何额外改动。

一、docs 结构整改(整改 #14)
根因:重构时新结构为 untracked 文件,执行 git stash(未带 -u)未纳入,
随后 git reset 拉回 HEAD 旧 tracked 树,导致旧树复活、新旧两棵目录
树并存于 docs/,共 791 文件、双分类体系冲突。

修复动作:
- b2 同名异主题文件改名迁移保全 9 个
- C 类 39 个孤立文件按主题正确归类
- A/B1 类 222 个重复文件删除(新结构已有内容副本)
- 9 个旧独有空目录删除
- 270 处内部引用按 verified 映射改写
- 整改记录 #14 登记于 04-运维文档/部署运维

结果:docs 791 → 569 文件,顶层仅规范 8 类 + 治理文件,单树恢复。
残留:约 20 处指向从未存在文件的陈旧死链,归入独立文档卫生任务。

二、compose 双目录对齐(消除踩坑 A)
- docker-compose.yml:nginx 前端挂载全部由根目录 frontend-*/dist
  改为 src/frontend-*/dist(h5 / agent / admin / terminal)
- docker-compose.dev.yml:dev 服务 build context 与卷同步改 src/
- 效果:本地 docker compose up 不再把根目录 stale dist 挂回,
  与线上一致,分叉隐患消除(已 docker compose config 校验通过)

防复发铁律:
- 重构须提交;仓库修复须 git stash -u 或先 commit
- 新结构须 git add 并提交,避免再次 untracked 复活
- H5 改动只动 src/frontend-h5/,禁改根目录遗留 frontend-*/
2026-08-07 22:31:32 +08:00

1363 lines
58 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>IT智能服务台 · H5用户端原型 v1.8 — 修复桌面端截断</title>
<style>
/* ============================================================================
CSS 变量 — 浅色/深色双主题
============================================================================ */
:root {
--accent: #3b82f6;
--accent-soft: rgba(59, 130, 246, 0.12);
--bg-primary: #f5f6fa;
--bg-secondary: #ffffff;
--bg-tertiary: #eef0f5;
--text-primary: #1a1a2e;
--text-secondary: #555770;
--text-tertiary: #9ca3af;
--text-placeholder: #b0b4c0;
--border-color: #e5e7eb;
--shadow: 0 1px 4px rgba(0,0,0,0.06);
--radius: 12px;
--transition: 0.2s ease;
--color-employee-bg: #3b82f6;
--color-agent-bg: #ffffff;
--color-agent-border: #e5e7eb;
--color-ai-bg: #e8f5e9;
--color-ai-text: #1b5e20;
--color-ai-tag-text: #2e7d32;
--color-ai-tag-bg: #c8e6c9;
--color-system-text: #9ca3af;
--color-system-bg: #eef0f5;
--color-warning: #ff9800;
--color-danger: #ef4444;
--color-success: #22c55e;
--color-guide-active: #ff9800;
--ts-done: #22c55e;
--ts-current: #3b82f6;
--ts-pending-bg: #eef0f5;
--ts-yes-bg: rgba(34,197,94,0.06);
--ts-yes-border: rgba(34,197,94,0.3);
--ts-no-bg: rgba(239,68,68,0.06);
--ts-no-border: rgba(239,68,68,0.3);
--color-ts-card-bg: #ffffff;
--color-ts-card-border: #e5e7eb;
--color-ts-card-header: rgba(59,130,246,0.08);
--resize-handle-color: #cbd5e1;
--tool-btn-bg: #eef0f5;
--tool-btn-text: #555770;
}
[data-theme="dark"] {
--accent: #3b82f6;
--accent-soft: rgba(59, 130, 246, 0.18);
--bg-primary: #0f172a;
--bg-secondary: #1e293b;
--bg-tertiary: #162032;
--text-primary: #e2e8f0;
--text-secondary: #94a3b8;
--text-tertiary: #64748b;
--text-placeholder: #475569;
--border-color: #334155;
--shadow: 0 1px 4px rgba(0,0,0,0.3);
--color-employee-bg: #2563eb;
--color-agent-bg: #1e293b;
--color-agent-border: #334155;
--color-ai-bg: #0d3320;
--color-ai-text: #86efac;
--color-ai-tag-text: #86efac;
--color-ai-tag-bg: #14532d;
--color-system-text: #64748b;
--color-system-bg: #162032;
--color-warning: #f97316;
--color-danger: #ef4444;
--color-success: #22c55e;
--color-guide-active: #f97316;
--ts-done: #22c55e;
--ts-current: #3b82f6;
--ts-pending-bg: #162032;
--ts-yes-bg: rgba(34,197,94,0.08);
--ts-yes-border: rgba(34,197,94,0.3);
--ts-no-bg: rgba(239,68,68,0.08);
--ts-no-border: rgba(239,68,68,0.3);
--color-ts-card-bg: #1e293b;
--color-ts-card-border: #334155;
--color-ts-card-header: rgba(59,130,246,0.12);
--resize-handle-color: #475569;
--tool-btn-bg: #162032;
--tool-btn-text: #94a3b8;
}
/* ============================================================================
基础重置
============================================================================ */
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
height: 100%;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', 'Microsoft YaHei', sans-serif;
background: #0a0f1a;
color: var(--text-primary);
-webkit-font-smoothing: antialiased;
overflow-x: hidden;
}
/* ============================================================================
页面级布局 — 双视图并排
============================================================================ */
.page-wrapper {
display: flex;
gap: 32px;
padding: 24px;
min-height: 100vh;
align-items: flex-start;
justify-content: center;
flex-wrap: wrap;
}
/* 视图标签 */
.view-label {
text-align: center;
padding: 8px 0 12px;
font-size: 13px;
font-weight: 600;
color: #94a3b8;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
}
.view-label .badge {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 10px;
font-weight: 700;
}
.badge--primary { background: rgba(59,130,246,0.2); color: #60a5fa; }
.badge--secondary { background: rgba(249,115,22,0.2); color: #fb923c; }
.badge--percent { background: rgba(34,197,94,0.2); color: #4ade80; }
.badge--new { background: rgba(239,68,68,0.2); color: #f87171; }
.badge--fix { background: rgba(168,85,247,0.2); color: #c084fc; }
/* ============================================================================
视图容器
============================================================================ */
.view-container { flex-shrink: 0; }
/* ===== 桌面端企微壳 — v1.8 拉长至940px修复截断 ===== */
.desktop-shell {
width: 720px;
height: 940px;
border-radius: 16px;
border: 2px solid #334155;
overflow: hidden;
background: var(--bg-primary);
box-shadow: 0 8px 40px rgba(0,0,0,0.4);
position: relative;
}
/* 企微顶栏模拟 */
.desktop-shell__wecom-bar {
height: 36px;
background: #2a2a3e;
display: flex;
align-items: center;
padding: 0 12px;
gap: 8px;
border-bottom: 1px solid #3a3a5c;
flex-shrink: 0;
}
.wecom-tab { font-size: 12px; color: #888; padding: 4px 10px; border-radius: 4px; cursor: default; }
.wecom-tab--active { color: #e2e8f0; background: rgba(59,130,246,0.15); }
.wecom-title { font-size: 13px; color: #ccc; margin-left: auto; }
/* ===== 手机壳 ===== */
.phone-shell {
width: 375px;
height: 820px;
border-radius: 36px;
border: 4px solid #334155;
overflow: hidden;
background: var(--bg-primary);
box-shadow: 0 8px 40px rgba(0,0,0,0.4);
position: relative;
}
.phone-shell__statusbar {
height: 44px;
background: var(--bg-secondary);
display: flex;
align-items: flex-end;
justify-content: center;
padding-bottom: 8px;
border-bottom: 1px solid var(--border-color);
flex-shrink: 0;
}
.phone-shell__statusbar-title {
font-size: 16px;
font-weight: 700;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 6px;
}
/* ============================================================================
主布局
============================================================================ */
.chat-panel {
display: flex;
flex-direction: column;
height: 100%;
background: var(--bg-primary);
}
/* ===== 桌面端双栏主体 ===== */
.desktop-body {
display: flex;
flex: 1;
overflow: hidden;
position: relative;
}
/* 左侧:消息区 */
.desktop-left {
flex: 1;
display: flex;
flex-direction: column;
min-width: 280px;
}
/* 拖拽手柄 — 左右栏之间 */
.resize-handle-v {
width: 6px;
cursor: col-resize;
background: transparent;
position: relative;
flex-shrink: 0;
z-index: 10;
transition: background 0.2s;
}
.resize-handle-v:hover,
.resize-handle-v--active {
background: var(--accent);
}
.resize-handle-v::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 2px;
height: 24px;
border-radius: 1px;
background: var(--resize-handle-color);
opacity: 0.5;
}
.resize-handle-v:hover::after,
.resize-handle-v--active::after {
opacity: 1;
background: #fff;
}
/* 右侧:三段式面板 */
.desktop-right {
width: 280px;
flex-shrink: 0;
display: flex;
flex-direction: column;
overflow: hidden;
background: var(--bg-secondary);
min-width: 200px;
max-width: 400px;
}
/* ============================================================================
顶部标题栏
============================================================================ */
.chat-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 16px;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border-color);
flex-shrink: 0;
}
.chat-header__title {
font-size: 15px;
font-weight: 700;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 6px;
}
.chat-header__title .logo-icon { font-size: 18px; }
.chat-header__actions {
display: flex;
align-items: center;
gap: 10px;
}
.chat-header__online {
display: flex;
align-items: center;
gap: 4px;
font-size: 11px;
color: var(--color-success);
}
.chat-header__online-dot {
width: 6px; height: 6px; border-radius: 50%;
background: var(--color-success);
animation: online-pulse 2s ease-in-out infinite;
}
.theme-switch {
display: flex; align-items: center; gap: 4px;
cursor: pointer; user-select: none;
-webkit-tap-highlight-color: transparent;
}
.switch-track {
width: 32px; height: 18px; border-radius: 9px;
background: var(--bg-tertiary); border: 1px solid var(--border-color);
position: relative; transition: background var(--transition);
}
.switch-thumb {
width: 14px; height: 14px; border-radius: 50%;
background: var(--accent); position: absolute; top: 1px; left: 1px;
transition: transform var(--transition);
}
[data-theme="dark"] .switch-thumb { transform: translateX(14px); }
/* ============================================================================
排查步骤 — 固定在消息区顶部
============================================================================ */
.ts-top-bar {
flex-shrink: 0;
background: var(--color-ts-card-bg);
border-bottom: 1px solid var(--color-ts-card-border);
overflow: hidden;
transition: max-height 0.3s ease;
z-index: 5;
}
.ts-top-bar__header {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
background: var(--color-ts-card-header);
border-bottom: 1px solid var(--border-color);
font-size: 11px;
font-weight: 700;
color: var(--accent);
cursor: pointer;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
.ts-top-bar__header-icon { font-size: 12px; }
.ts-top-bar__header-title { flex: 1; }
.ts-top-bar__header-tag {
font-size: 9px;
padding: 1px 6px;
border-radius: 3px;
background: var(--accent-soft);
color: var(--accent);
font-weight: 600;
}
.ts-top-bar__toggle {
font-size: 10px;
color: var(--text-tertiary);
transition: transform 0.2s;
}
.ts-top-bar__toggle--collapsed { transform: rotate(180deg); }
.ts-top-bar__path {
padding: 6px 12px 4px;
border-bottom: 1px solid var(--border-color);
}
.ts-path-bar {
display: flex; align-items: center; gap: 2px;
overflow-x: auto; padding-bottom: 4px;
-webkit-overflow-scrolling: touch;
}
.ts-path-bar::-webkit-scrollbar { height: 0; }
.ts-step { display: flex; align-items: center; gap: 4px; white-space: nowrap; flex-shrink: 0; }
.ts-dot {
width: 16px; height: 16px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 8px; font-weight: 700; flex-shrink: 0;
}
.ts-dot--done { background: var(--ts-done); color: #fff; }
.ts-dot--current { background: var(--ts-current); color: #fff; box-shadow: 0 0 0 2px var(--accent-soft); animation: ts-dot-pulse 2s ease-in-out infinite; }
.ts-dot--pending { background: var(--ts-pending-bg); color: var(--text-tertiary); border: 1px solid var(--border-color); }
.ts-label { font-size: 10px; color: var(--text-secondary); }
.ts-step--done .ts-label { color: var(--text-tertiary); text-decoration: line-through; }
.ts-step--current .ts-label { color: var(--accent); font-weight: 600; }
.ts-step--pending .ts-label { color: var(--text-tertiary); }
.ts-arrow { color: var(--text-tertiary); font-size: 8px; flex-shrink: 0; padding: 0 1px; }
.ts-arrow--active { color: var(--ts-done); }
.ts-top-bar__body {
padding: 8px 12px 10px;
}
/* 决策节点 */
.ts-question-header { display: flex; align-items: flex-start; gap: 6px; margin-bottom: 8px; }
.ts-question-icon { font-size: 14px; flex-shrink: 0; margin-top: 1px; }
.ts-question-text { font-size: 13px; font-weight: 600; color: var(--text-primary); line-height: 1.5; }
.ts-options { display: flex; gap: 8px; margin-top: 2px; }
.ts-option {
flex: 1; display: flex; align-items: center; justify-content: center; gap: 4px;
padding: 8px 10px; border-radius: 8px;
border: 2px solid var(--border-color); background: var(--bg-primary);
color: var(--text-primary); font-size: 12px; font-weight: 600;
cursor: pointer; transition: all 0.2s; font-family: inherit;
-webkit-tap-highlight-color: transparent;
}
.ts-option:active { transform: scale(0.97); }
.ts-option--yes { border-color: var(--ts-yes-border); background: var(--ts-yes-bg); }
.ts-option--no { border-color: var(--ts-no-border); background: var(--ts-no-bg); }
.ts-option-icon { font-size: 12px; }
.ts-option--selected-yes { border-color: #22c55e !important; background: rgba(34,197,94,0.15) !important; box-shadow: 0 0 0 2px rgba(34,197,94,0.2); }
.ts-option--selected-no { border-color: #ef4444 !important; background: rgba(239,68,68,0.15) !important; box-shadow: 0 0 0 2px rgba(239,68,68,0.2); }
/* 步骤节点 */
.ts-instruction-header { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
.ts-instruction-icon { font-size: 12px; flex-shrink: 0; }
.ts-instruction-title { font-size: 12px; font-weight: 600; color: var(--accent); }
.ts-sub-steps { display: flex; flex-direction: column; gap: 4px; margin: 6px 0; padding-left: 2px; }
.ts-sub-step { display: flex; align-items: center; gap: 5px; font-size: 11px; }
.ts-sub-dot {
width: 16px; height: 16px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 8px; font-weight: 700; flex-shrink: 0;
background: var(--ts-pending-bg); color: var(--text-tertiary); border: 1px solid var(--border-color);
}
.ts-sub-step--done .ts-sub-dot { background: #22c55e; color: #fff; border-color: #22c55e; }
.ts-sub-step--done .ts-sub-label { color: var(--text-tertiary); text-decoration: line-through; }
.ts-sub-step--current .ts-sub-dot { background: var(--accent); color: #fff; border-color: var(--accent); }
.ts-sub-step--current .ts-sub-label { color: var(--accent); font-weight: 600; }
.ts-sub-label { color: var(--text-secondary); }
.ts-hint {
margin-top: 6px; padding: 4px 8px;
background: var(--accent-soft); border-radius: 6px;
font-size: 10px; color: var(--accent);
}
/* ============================================================================
消息列表
============================================================================ */
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 8px 0;
-webkit-overflow-scrolling: touch;
}
.chat-messages::-webkit-scrollbar { width: 3px; }
.chat-messages::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 3px; }
/* ============================================================================
消息气泡
============================================================================ */
.msg {
display: flex; flex-direction: column;
padding: 4px 14px; max-width: 100%;
animation: bubble-in 0.3s ease-out;
}
.msg--employee { align-items: flex-end; }
.msg--agent, .msg--ai { align-items: flex-start; }
.msg--system { align-items: center; padding: 6px 14px; }
.msg--system .msg__system-text {
font-size: 11px; color: var(--color-system-text);
background: var(--color-system-bg);
padding: 3px 10px; border-radius: 10px;
text-align: center; line-height: 1.5;
}
.msg__sender { display: flex; align-items: center; gap: 4px; margin-bottom: 2px; padding-left: 4px; }
.msg__ai-tag {
font-size: 10px; color: var(--color-ai-tag-text);
background: var(--color-ai-tag-bg);
padding: 1px 5px; border-radius: 3px; font-weight: 600;
}
.msg__sender-name { font-size: 11px; color: var(--text-tertiary); }
.msg__bubble {
max-width: 75%; padding: 9px 13px; border-radius: 14px;
word-break: break-word; line-height: 1.6; font-size: 13px;
}
.msg--employee .msg__bubble { background: var(--color-employee-bg); color: #fff; border-top-right-radius: 4px; }
.msg--agent .msg__bubble { background: var(--color-agent-bg); color: var(--text-primary); border-top-left-radius: 4px; border: 1px solid var(--color-agent-border); }
.msg--ai .msg__bubble { background: var(--color-ai-bg); color: var(--color-ai-text); border-top-left-radius: 4px; }
.msg__time { font-size: 10px; color: var(--text-placeholder); margin-top: 2px; }
.msg--employee .msg__time { text-align: right; padding-right: 4px; }
.msg--agent .msg__time, .msg--ai .msg__time { text-align: left; padding-left: 4px; }
.msg__avatar {
width: 28px; height: 28px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 12px; flex-shrink: 0; margin-bottom: 2px;
}
.msg__avatar--agent { background: linear-gradient(135deg, #3b82f6, #6366f1); }
.msg__avatar--ai { background: linear-gradient(135deg, #22c55e, #14b8a6); }
.msg__avatar-row { display: flex; align-items: flex-end; gap: 6px; max-width: 88%; }
/* ============================================================================
输入栏 — 通用样式(桌面端+手机端共用)
============================================================================ */
.input-bar {
background: var(--bg-secondary);
border-top: 1px solid var(--border-color);
flex-shrink: 0;
}
/* 工具栏 — 表情/图片/文件/拍照 */
.input-toolbar {
display: flex;
align-items: center;
gap: 2px;
padding: 4px 12px 2px;
border-bottom: 1px solid var(--border-color);
}
.tool-btn {
width: 32px; height: 32px; border-radius: 8px;
border: none; background: var(--tool-btn-bg);
color: var(--tool-btn-text); font-size: 15px;
cursor: pointer; display: flex; align-items: center; justify-content: center;
transition: all 0.15s; flex-shrink: 0;
-webkit-tap-highlight-color: transparent; outline: none;
}
.tool-btn:active { transform: scale(0.92); background: var(--accent-soft); }
.tool-btn:hover { background: var(--accent-soft); color: var(--accent); }
.tool-sep {
width: 1px; height: 18px; background: var(--border-color);
margin: 0 4px; flex-shrink: 0;
}
/* 输入行 */
.input-row {
display: flex;
align-items: flex-end;
gap: 6px;
padding: 6px 12px;
padding-bottom: calc(6px + env(safe-area-inset-bottom, 6px));
}
/* 输入框 — 通用 */
.input-field {
flex: 1; background: var(--bg-tertiary); border-radius: 8px;
padding: 8px 10px; border: 1px solid var(--border-color);
font-size: 13px; font-family: inherit; color: var(--text-primary);
resize: none; outline: none; min-height: 54px; max-height: 120px;
line-height: 1.5; transition: border-color 0.2s;
}
.input-field:focus { border-color: var(--accent); }
.input-field::placeholder { color: var(--text-placeholder); }
/* 摇铃按钮 — 圆形(手机端输入栏 + 桌面端输入栏) */
.bell-btn {
position: relative; width: 34px; height: 34px; border-radius: 50%; border: none;
cursor: pointer; background: linear-gradient(135deg, var(--color-warning), #f97316);
display: flex; align-items: center; justify-content: center;
box-shadow: 0 2px 6px rgba(255, 152, 0, 0.25);
transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
-webkit-tap-highlight-color: transparent; outline: none; flex-shrink: 0;
}
.bell-btn:active { transform: scale(0.92); }
.bell-btn__icon {
font-size: 16px; line-height: 1; display: inline-block;
animation: bell-ring 2s ease-in-out infinite;
}
.bell-btn__dot {
position: absolute; top: 1px; right: 1px;
width: 6px; height: 6px; border-radius: 50%;
background: var(--color-danger); border: 1px solid var(--bg-secondary);
}
/* 发送按钮 */
.send-btn {
flex-shrink: 0; height: 34px; width: 34px; border-radius: 50%;
border: none; background: var(--accent); color: #fff;
font-size: 14px; cursor: pointer;
transition: all 0.2s; font-family: inherit;
display: flex; align-items: center; justify-content: center;
box-shadow: 0 2px 6px rgba(59, 130, 246, 0.25);
}
.send-btn:active { transform: scale(0.92); }
.send-btn:disabled { opacity: 0.4; cursor: not-allowed; box-shadow: none; }
/* ============================================================================
摇铃按钮 — 标题栏胶囊样式(桌面端+手机端共用)
============================================================================ */
.bell-btn--header {
height: 28px; border-radius: 14px; border: none;
padding: 0 10px; gap: 4px;
font-size: 11px; font-weight: 600; color: #fff;
background: linear-gradient(135deg, var(--color-warning), #f97316);
cursor: pointer; display: flex; align-items: center; justify-content: center;
box-shadow: 0 2px 6px rgba(255, 152, 0, 0.25);
transition: transform 0.2s;
-webkit-tap-highlight-color: transparent; outline: none;
}
.bell-btn--header:active { transform: scale(0.95); }
.bell-btn--header__icon {
font-size: 13px; animation: none;
display: inline-block;
}
.bell-btn--header__dot {
width: 5px; height: 5px; border-radius: 50%;
background: var(--color-danger); border: none;
display: inline-block; margin-left: 2px;
}
/* ============================================================================
桌面端右侧面板 — 三段式布局
============================================================================ */
.right-panel {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
.ai-push {
padding: 10px 12px;
border-bottom: 1px solid var(--border-color);
flex-shrink: 0;
overflow-y: auto;
}
.ai-push::-webkit-scrollbar { width: 3px; }
.ai-push::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 3px; }
.ai-push__header { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
.ai-push__icon { font-size: 14px; }
.ai-push__title { font-size: 12px; font-weight: 700; color: var(--text-primary); }
.ai-push__badge { margin-left: auto; font-size: 9px; padding: 2px 6px; border-radius: 4px; background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.ai-push__item {
padding: 7px 9px; border-radius: 8px; background: var(--bg-tertiary);
margin-bottom: 5px; cursor: pointer; transition: all 0.2s;
}
.ai-push__item:hover { background: var(--accent-soft); }
.ai-push__item-title { font-size: 11px; font-weight: 600; color: var(--text-primary); margin-bottom: 2px; display: flex; align-items: center; gap: 4px; }
.ai-push__item-desc { font-size: 10px; color: var(--text-secondary); line-height: 1.4; }
.ai-push__item-tag { display: inline-block; font-size: 9px; padding: 1px 4px; border-radius: 3px; background: var(--accent-soft); color: var(--accent); margin-right: 3px; }
.resources {
flex: 1; display: flex; flex-direction: column;
border-bottom: 1px solid var(--border-color); overflow: hidden;
}
.resources__tabs { display: flex; border-bottom: 1px solid var(--border-color); flex-shrink: 0; }
.resources__tab { flex: 1; text-align: center; padding: 7px 0; font-size: 11px; color: var(--text-secondary); cursor: pointer; border-bottom: 2px solid transparent; transition: all 0.2s; }
.resources__tab--active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
.resources__content { flex: 1; overflow-y: auto; padding: 8px 12px; }
.resources__content::-webkit-scrollbar { width: 3px; }
.resources__content::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 3px; }
.resource-item { display: flex; align-items: center; gap: 8px; padding: 7px 9px; border-radius: 8px; margin-bottom: 4px; cursor: pointer; transition: all 0.2s; }
.resource-item:hover { background: var(--accent-soft); }
.resource-item__icon { width: 28px; height: 28px; border-radius: 6px; background: var(--bg-tertiary); display: flex; align-items: center; justify-content: center; font-size: 14px; flex-shrink: 0; }
.resource-item__info { flex: 1; min-width: 0; }
.resource-item__name { font-size: 11px; font-weight: 600; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.resource-item__desc { font-size: 9px; color: var(--text-tertiary); margin-top: 1px; }
.fun-quiz { padding: 10px 12px; flex-shrink: 0; }
.fun-quiz__header { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
.fun-quiz__icon { font-size: 14px; }
.fun-quiz__title { font-size: 12px; font-weight: 700; color: var(--text-primary); }
.fun-quiz__score { margin-left: auto; font-size: 10px; color: var(--color-warning); font-weight: 600; }
.fun-quiz__question { font-size: 11px; color: var(--text-primary); margin-bottom: 6px; line-height: 1.5; }
.fun-quiz__options { display: flex; flex-direction: column; gap: 3px; }
.fun-quiz__option { padding: 5px 9px; border-radius: 6px; border: 1px solid var(--border-color); background: var(--bg-primary); font-size: 10px; color: var(--text-secondary); cursor: pointer; transition: all 0.2s; }
.fun-quiz__option:hover { border-color: var(--accent); color: var(--accent); }
.fun-quiz__option--correct { border-color: var(--color-success); background: rgba(34,197,94,0.1); color: var(--color-success); }
.fun-quiz__option--wrong { border-color: var(--color-danger); background: rgba(239,68,68,0.1); color: var(--color-danger); }
/* ============================================================================
呼叫坐席弹窗 — 摇铃样式
============================================================================ */
.modal-overlay {
position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 9999;
display: flex; justify-content: center; align-items: center;
opacity: 0; pointer-events: none; transition: opacity 0.25s ease;
}
.modal-overlay--visible { opacity: 1; pointer-events: auto; }
.modal-content {
background: var(--bg-secondary); border-radius: 16px;
width: 320px; max-height: 80vh; overflow-y: auto;
box-shadow: 0 8px 32px rgba(0,0,0,0.2);
transform: scale(0.8); transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.modal-overlay--visible .modal-content { transform: scale(1); }
.modal-header { text-align: center; padding: 16px 14px 6px; }
.modal-header__icon { font-size: 28px; display: inline-block; animation: bell-ring 0.6s ease-in-out infinite; }
.modal-header h3 { margin: 6px 0 4px; font-size: 15px; color: var(--text-primary); }
.modal-body { padding: 10px 14px; display: flex; flex-direction: column; align-items: center; }
.modal-footer { padding: 10px 14px 16px; display: flex; justify-content: center; }
.modal-btn { border: none; border-radius: 8px; padding: 8px 20px; font-size: 13px; cursor: pointer; transition: all 0.2s; font-family: inherit; }
.modal-btn:active { transform: scale(0.96); }
.modal-btn--cancel { background: var(--bg-tertiary); color: var(--text-secondary); }
.modal-sending { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-secondary); }
.modal-dot-flashing { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); animation: dot-flash 1s infinite; }
.modal-success { font-size: 13px; color: var(--color-success); font-weight: 600; padding: 8px 0; }
/* ============================================================================
标注区
============================================================================ */
.annotations {
position: fixed; top: 20px; right: 20px; width: 300px;
background: var(--bg-secondary); border: 1px solid var(--border-color);
border-radius: 12px; padding: 14px; font-size: 11px; line-height: 1.5;
color: var(--text-secondary); box-shadow: 0 4px 16px rgba(0,0,0,0.2);
z-index: 100; max-height: 90vh; overflow-y: auto;
}
.annotations h3 { font-size: 13px; color: var(--text-primary); margin-bottom: 6px; }
.annotations ul { list-style: none; padding: 0; }
.annotations li { padding: 3px 0; border-bottom: 1px solid var(--border-color); }
.annotations li:last-child { border-bottom: none; }
.annotations .tag { display: inline-block; padding: 1px 5px; border-radius: 3px; font-size: 9px; font-weight: 600; margin-right: 3px; }
.tag--layout { background: rgba(168,85,247,0.15); color: #a855f7; }
.tag--decision { background: rgba(59,130,246,0.15); color: #3b82f6; }
.tag--sync { background: rgba(249,115,22,0.15); color: #f97316; }
.tag--diff { background: rgba(239,68,68,0.15); color: #ef4444; }
.tag--new { background: rgba(34,197,94,0.15); color: #22c55e; }
.tag--update { background: rgba(59,130,246,0.15); color: #3b82f6; }
.tag--fix { background: rgba(168,85,247,0.15); color: #a855f7; }
/* ============================================================================
动画
============================================================================ */
@keyframes online-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
@keyframes ts-dot-pulse { 0%, 100% { box-shadow: 0 0 0 2px var(--accent-soft); } 50% { box-shadow: 0 0 0 5px var(--accent-soft); } }
@keyframes bubble-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
@keyframes dot-flash { 0%, 100% { opacity: 0.3; } 50% { opacity: 1; } }
@keyframes bell-ring {
0%, 80%, 100% { transform: rotate(0); }
85% { transform: rotate(12deg); }
90% { transform: rotate(-12deg); }
95% { transform: rotate(6deg); }
}
</style>
</head>
<body>
<div class="page-wrapper">
<!-- ================================================================
左:桌面端视图 — v1.7 拉长至820px确保输入框完整可见
================================================================ -->
<div class="view-container">
<div class="view-label">
💻 企微桌面端 · 自建应用
<span class="badge badge--primary">主入口</span>
<span class="badge badge--percent">~70%</span>
<span class="badge badge--fix">v1.8修复截断</span>
</div>
<div class="desktop-shell">
<div class="desktop-shell__wecom-bar">
<span class="wecom-tab">消息</span>
<span class="wecom-tab wecom-tab--active">工作台</span>
<span class="wecom-tab">通讯录</span>
<span class="wecom-title">IT智能服务台</span>
</div>
<div class="desktop-body" id="desktopBody">
<!-- 左侧:消息区 -->
<div class="desktop-left" id="desktopLeft">
<!-- 标题栏 -->
<div class="chat-header">
<div class="chat-header__title">
<span class="logo-icon">🛎️</span>
<span>IT智能服务台</span>
</div>
<div class="chat-header__actions">
<div class="chat-header__online">
<span class="chat-header__online-dot"></span>
<span>坐席在线</span>
</div>
<button class="bell-btn--header" onclick="showCallModal()" title="呼叫人工坐席">
<span class="bell-btn--header__icon">🔔</span>
<span>呼叫</span>
<span class="bell-btn--header__dot"></span>
</button>
<div class="theme-switch" onclick="toggleTheme()" title="切换主题">
<div class="switch-track"><div class="switch-thumb"></div></div>
</div>
</div>
</div>
<!-- 排查步骤固定在消息区顶部 -->
<div class="ts-top-bar" id="desktopTsBar">
<div class="ts-top-bar__header" onclick="toggleTsBar('desktop')">
<span class="ts-top-bar__header-icon">🔧</span>
<span class="ts-top-bar__header-title">排查步骤</span>
<span class="ts-top-bar__header-tag">进行中</span>
<span class="ts-top-bar__toggle" id="desktopTsToggle">▼ 收起</span>
</div>
<div id="desktopTsContent">
<div class="ts-top-bar__path">
<div class="ts-path-bar">
<div class="ts-step ts-step--done">
<span class="ts-dot ts-dot--done"></span>
<span class="ts-label">确认问题</span>
</div>
<span class="ts-arrow ts-arrow--active"></span>
<div class="ts-step ts-step--current">
<span class="ts-dot ts-dot--current">2</span>
<span class="ts-label">网络适配器</span>
</div>
<span class="ts-arrow"></span>
<div class="ts-step ts-step--pending">
<span class="ts-dot ts-dot--pending">3</span>
<span class="ts-label">驱动检查</span>
</div>
</div>
</div>
<div class="ts-top-bar__body" id="desktopTsBody">
<div id="desktopTsDecision">
<div class="ts-question-header">
<span class="ts-question-icon"></span>
<span class="ts-question-text">WiFi适配器是否显示"已禁用"?</span>
</div>
<div class="ts-options">
<button class="ts-option ts-option--yes" onclick="selectDecision('desktop','yes')">
<span class="ts-option-icon"></span><span>是,已禁用</span>
</button>
<button class="ts-option ts-option--no" onclick="selectDecision('desktop','no')">
<span class="ts-option-icon"></span><span>否,正常</span>
</button>
</div>
</div>
<div id="desktopTsInstruction" style="display:none;">
<div class="ts-instruction-header">
<span class="ts-instruction-icon">📋</span>
<span class="ts-instruction-title">正在执行:启用WiFi适配器</span>
</div>
<div class="ts-sub-steps">
<div class="ts-sub-step ts-sub-step--done">
<span class="ts-sub-dot"></span>
<span class="ts-sub-label">打开「设置」→「网络和Internet」</span>
</div>
<div class="ts-sub-step ts-sub-step--current">
<span class="ts-sub-dot">2</span>
<span class="ts-sub-label">点击「更改适配器选项」</span>
</div>
<div class="ts-sub-step">
<span class="ts-sub-dot">3</span>
<span class="ts-sub-label">右键WiFi适配器 → 启用</span>
</div>
</div>
<div class="ts-hint">💡 请按步骤操作,完成后坐席推进下一步</div>
</div>
</div>
</div>
</div>
<!-- 消息列表 -->
<div class="chat-messages" id="desktopMessages">
<div class="msg msg--system">
<span class="msg__system-text">坐席 小宋 已接入会话</span>
</div>
<div class="msg msg--ai">
<div class="msg__sender"><span class="msg__ai-tag">AI回复</span></div>
<div class="msg__avatar-row">
<div class="msg__avatar msg__avatar--ai">🤖</div>
<div>
<div class="msg__bubble">您好!我是AI助手,请问有什么可以帮您的?</div>
<div class="msg__time">10:02</div>
</div>
</div>
</div>
<div class="msg msg--employee">
<div class="msg__bubble">我的电脑连不上公司WiFi了,网络图标显示黄色感叹号</div>
<div class="msg__time">10:03</div>
</div>
<div class="msg msg--agent">
<div class="msg__avatar-row">
<div class="msg__avatar msg__avatar--agent">👨‍💻</div>
<div>
<div class="msg__bubble">你好,我来帮你排查WiFi问题。请在上方排查步骤中选择 👆</div>
<div class="msg__time">10:05</div>
</div>
</div>
</div>
<div class="msg msg--system">
<span class="msg__system-text">排查步骤已同步至坐席端</span>
</div>
<div class="msg msg--agent">
<div class="msg__avatar-row">
<div class="msg__avatar msg__avatar--agent">👨‍💻</div>
<div>
<div class="msg__bubble">好的,我看到你选择了"是,已禁用"。请按照上方步骤操作,启用WiFi适配器后告诉我结果。</div>
<div class="msg__time">10:06</div>
</div>
</div>
</div>
<div class="msg msg--employee">
<div class="msg__bubble">已经启用了,但还是连不上</div>
<div class="msg__time">10:08</div>
</div>
<div class="msg msg--agent">
<div class="msg__avatar-row">
<div class="msg__avatar msg__avatar--agent">👨‍💻</div>
<div>
<div class="msg__bubble">明白,我继续帮你排查驱动问题。请稍等…</div>
<div class="msg__time">10:08</div>
</div>
</div>
</div>
</div>
<!-- ★ 桌面端输入栏(含工具栏+发送)— v1.7确保完整可见 -->
<div class="input-bar" id="desktopInputBar">
<div class="input-toolbar">
<button class="tool-btn" title="表情" onclick="showToolTip('😊 表情面板')">😊</button>
<button class="tool-btn" title="图片" onclick="showToolTip('🖼️ 选择图片')">🖼️</button>
<div class="tool-sep"></div>
<button class="tool-btn" title="文件" onclick="showToolTip('📎 选择文件')">📎</button>
<button class="tool-btn" title="拍照" onclick="showToolTip('📸 打开相机')">📸</button>
</div>
<div class="input-row">
<textarea class="input-field" rows="3" placeholder="请输入消息..." oninput="onInput('desktop', this)"></textarea>
<button class="bell-btn" onclick="showCallModal()" title="呼叫人工坐席">
<span class="bell-btn__icon">🔔</span>
<span class="bell-btn__dot"></span>
</button>
<button class="send-btn" id="desktopSendBtn" disabled title="发送" onclick="sendMessage('desktop')"></button>
</div>
</div>
</div>
<!-- 左右栏拖拽手柄 -->
<div class="resize-handle-v" id="resizeHandleV" title="左右拖动调整面板宽度"></div>
<!-- 右侧:三段式面板 -->
<div class="desktop-right" id="desktopRight">
<div class="right-panel">
<div class="ai-push">
<div class="ai-push__header">
<span class="ai-push__icon">🤖</span>
<span class="ai-push__title">AI智能推荐</span>
<span class="ai-push__badge">根据会话推送</span>
</div>
<div class="ai-push__item">
<div class="ai-push__item-title"><span>📖</span> WiFi连接问题处理指南</div>
<div class="ai-push__item-desc">相似问题处理方案,已解决 28 次</div>
<div><span class="ai-push__item-tag">相似问题</span><span class="ai-push__item-tag">WiFi</span></div>
</div>
<div class="ai-push__item">
<div class="ai-push__item-title"><span>📋</span> 网络连接申请流程</div>
<div class="ai-push__item-desc">新员工网络连接申请审批流程</div>
<div><span class="ai-push__item-tag">流程入口</span></div>
</div>
<div class="ai-push__item">
<div class="ai-push__item-title"><span>📥</span> 无线网卡驱动下载</div>
<div class="ai-push__item-desc">Intel/Realtek 常用无线网卡驱动</div>
<div><span class="ai-push__item-tag">软件下载</span></div>
</div>
</div>
<div class="resources">
<div class="resources__tabs">
<div class="resources__tab resources__tab--active" onclick="switchResourceTab('process', this)">申请流程</div>
<div class="resources__tab" onclick="switchResourceTab('software', this)">必装软件</div>
</div>
<div class="resources__content" id="resourceContent">
<div class="resource-item"><div class="resource-item__icon">📋</div><div class="resource-item__info"><div class="resource-item__name">IT设备申请</div><div class="resource-item__desc">电脑/显示器/外设</div></div></div>
<div class="resource-item"><div class="resource-item__icon">🔑</div><div class="resource-item__info"><div class="resource-item__name">权限申请</div><div class="resource-item__desc">文件夹/系统访问权限</div></div></div>
<div class="resource-item"><div class="resource-item__icon">📞</div><div class="resource-item__info"><div class="resource-item__name">VPN申请</div><div class="resource-item__desc">远程办公网络连接</div></div></div>
</div>
</div>
<div class="fun-quiz">
<div class="fun-quiz__header">
<span class="fun-quiz__icon">🎮</span>
<span class="fun-quiz__title">趣味问答</span>
<span class="fun-quiz__score">积分: 120</span>
</div>
<div class="fun-quiz__question">IT服务台电话分机号是?</div>
<div class="fun-quiz__options">
<div class="fun-quiz__option" onclick="selectQuizOption(this, true)">A. 8001</div>
<div class="fun-quiz__option" onclick="selectQuizOption(this, false)">B. 8002</div>
<div class="fun-quiz__option" onclick="selectQuizOption(this, false)">C. 8003</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- ================================================================
右:手机端视图 — v1.7 摇铃上移至标题栏
================================================================ -->
<div class="view-container">
<div class="view-label">
📱 企微手机端 · 自建应用
<span class="badge badge--secondary">辅助入口</span>
<span class="badge badge--percent">~30%</span>
<span class="badge badge--fix">摇铃上移</span>
</div>
<div class="phone-shell">
<div class="chat-panel">
<div class="phone-shell__statusbar">
<div class="phone-shell__statusbar-title">
<span>🛎️</span> IT智能服务台
</div>
</div>
<!-- ★ v1.7:手机端标题栏 — 摇铃从输入栏移至此处,与桌面端一致 -->
<div class="chat-header">
<div class="chat-header__actions" style="width:100%;justify-content:space-between;">
<div class="chat-header__online">
<span class="chat-header__online-dot"></span>
<span>坐席在线</span>
</div>
<div style="display:flex;align-items:center;gap:8px;">
<button class="bell-btn--header" onclick="showCallModal()" title="呼叫人工坐席">
<span class="bell-btn--header__icon">🔔</span>
<span>呼叫</span>
<span class="bell-btn--header__dot"></span>
</button>
<div class="theme-switch" onclick="toggleTheme()" title="切换主题">
<div class="switch-track"><div class="switch-thumb"></div></div>
</div>
</div>
</div>
</div>
<!-- 排查步骤固定在消息区顶部 -->
<div class="ts-top-bar" id="mobileTsBar">
<div class="ts-top-bar__header" onclick="toggleTsBar('mobile')">
<span class="ts-top-bar__header-icon">🔧</span>
<span class="ts-top-bar__header-title">排查步骤</span>
<span class="ts-top-bar__header-tag">进行中</span>
<span class="ts-top-bar__toggle" id="mobileTsToggle">▼ 收起</span>
</div>
<div id="mobileTsContent">
<div class="ts-top-bar__path">
<div class="ts-path-bar">
<div class="ts-step ts-step--done">
<span class="ts-dot ts-dot--done"></span>
<span class="ts-label">确认问题</span>
</div>
<span class="ts-arrow ts-arrow--active"></span>
<div class="ts-step ts-step--current">
<span class="ts-dot ts-dot--current">2</span>
<span class="ts-label">网络适配器</span>
</div>
<span class="ts-arrow"></span>
<div class="ts-step ts-step--pending">
<span class="ts-dot ts-dot--pending">3</span>
<span class="ts-label">驱动检查</span>
</div>
</div>
</div>
<div class="ts-top-bar__body" id="mobileTsBody">
<div id="mobileTsDecision">
<div class="ts-question-header">
<span class="ts-question-icon"></span>
<span class="ts-question-text">WiFi适配器是否显示"已禁用"?</span>
</div>
<div class="ts-options">
<button class="ts-option ts-option--yes" onclick="selectDecision('mobile','yes')">
<span class="ts-option-icon"></span><span>是,已禁用</span>
</button>
<button class="ts-option ts-option--no" onclick="selectDecision('mobile','no')">
<span class="ts-option-icon"></span><span>否,正常</span>
</button>
</div>
</div>
<div id="mobileTsInstruction" style="display:none;">
<div class="ts-instruction-header">
<span class="ts-instruction-icon">📋</span>
<span class="ts-instruction-title">正在执行:启用WiFi适配器</span>
</div>
<div class="ts-sub-steps">
<div class="ts-sub-step ts-sub-step--done">
<span class="ts-sub-dot"></span>
<span class="ts-sub-label">打开「设置」→「网络和Internet」</span>
</div>
<div class="ts-sub-step ts-sub-step--current">
<span class="ts-sub-dot">2</span>
<span class="ts-sub-label">点击「更改适配器选项」</span>
</div>
<div class="ts-sub-step">
<span class="ts-sub-dot">3</span>
<span class="ts-sub-label">右键WiFi适配器 → 启用</span>
</div>
</div>
<div class="ts-hint">💡 请按步骤操作,完成后坐席推进下一步</div>
</div>
</div>
</div>
</div>
<!-- 手机端消息列表 -->
<div class="chat-messages" id="mobileMessages">
<div class="msg msg--system">
<span class="msg__system-text">坐席 小宋 已接入会话</span>
</div>
<div class="msg msg--ai">
<div class="msg__sender"><span class="msg__ai-tag">AI回复</span></div>
<div class="msg__avatar-row">
<div class="msg__avatar msg__avatar--ai">🤖</div>
<div>
<div class="msg__bubble">您好!我是AI助手,请问有什么可以帮您的?</div>
<div class="msg__time">10:02</div>
</div>
</div>
</div>
<div class="msg msg--employee">
<div class="msg__bubble">我的电脑连不上公司WiFi了</div>
<div class="msg__time">10:03</div>
</div>
<div class="msg msg--agent">
<div class="msg__avatar-row">
<div class="msg__avatar msg__avatar--agent">👨‍💻</div>
<div>
<div class="msg__bubble">你好,我来帮你排查。请在上方排查步骤中选择 👆</div>
<div class="msg__time">10:05</div>
</div>
</div>
</div>
<div class="msg msg--system">
<span class="msg__system-text">排查步骤已同步至坐席端</span>
</div>
<div class="msg msg--agent">
<div class="msg__avatar-row">
<div class="msg__avatar msg__avatar--agent">👨‍💻</div>
<div>
<div class="msg__bubble">好的,我看到你选择了"是,已禁用"。请按照上方步骤操作,完成后告诉我。</div>
<div class="msg__time">10:06</div>
</div>
</div>
</div>
<div class="msg msg--employee">
<div class="msg__bubble">已经启用了,但还是连不上</div>
<div class="msg__time">10:08</div>
</div>
</div>
<!-- ★ v1.7:手机端输入栏 — 摇铃已移至标题栏,输入栏仅保留工具栏+输入框+发送 -->
<div class="input-bar">
<div class="input-toolbar">
<button class="tool-btn" title="表情" onclick="showToolTip('😊 表情面板')">😊</button>
<button class="tool-btn" title="图片" onclick="showToolTip('🖼️ 选择图片')">🖼️</button>
<div class="tool-sep"></div>
<button class="tool-btn" title="文件" onclick="showToolTip('📎 选择文件')">📎</button>
<button class="tool-btn" title="拍照" onclick="showToolTip('📸 打开相机')">📸</button>
</div>
<div class="input-row">
<textarea class="input-field" rows="3" placeholder="请输入消息..." oninput="onInput('mobile', this)"></textarea>
<button class="send-btn" id="mobileSendBtn" disabled title="发送" onclick="sendMessage('mobile')"></button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 呼叫坐席弹窗 — 摇铃风格 -->
<div class="modal-overlay" id="callModal" onclick="hideCallModal(event)">
<div class="modal-content">
<div class="modal-header">
<span class="modal-header__icon">🔔</span>
<h3>正在呼叫 IT 坐席...</h3>
</div>
<div class="modal-body">
<div class="modal-sending" id="modalSending">
<span class="modal-dot-flashing"></span>
<span>摇铃呼叫中,请稍候...</span>
</div>
<div class="modal-success" id="modalSuccess" style="display:none;">✅ 呼叫成功!坐席马上就来~</div>
</div>
<div class="modal-footer">
<button class="modal-btn modal-btn--cancel" onclick="hideCallModal()">取消</button>
</div>
</div>
</div>
<!-- 标注区 -->
<div class="annotations">
<h3>📝 H5用户端原型 v1.8</h3>
<ul>
<li>
<span class="tag tag--fix">v1.8修复</span>
<b>修复桌面端截断</b>:从820px→940px,解决输入框+趣味问答被 overflow:hidden 裁掉的问题(原内容总高~853px,820px不够)
</li>
<li>
<span class="tag tag--fix">v1.7修复</span>
<b>手机端摇铃上移</b>:从输入栏移至标题栏坐席状态右侧,与桌面端一致(🔔呼叫 胶囊按钮)
</li>
<li>
<span class="tag tag--layout">桌面端布局</span>
企微顶栏 → 标题栏(🛎️+坐席在线+🔔呼叫+主题) → 排查步骤(固定顶部) → 消息流 → 输入栏(工具栏+输入+🔔+➤) | 拖拽 | AI推送/资源/问答
</li>
<li>
<span class="tag tag--layout">手机端布局</span>
标题栏(坐席在线+🔔呼叫+主题) → 排查步骤(固定顶部) → 消息流 → 输入栏(工具栏+输入+➤)
</li>
<li>
<span class="tag tag--decision">双端差异</span>
摇铃位置统一:桌面端+手机端均在标题栏;输入栏:桌面端保留🔔+➤,手机端仅➤
</li>
<li>
<span class="tag tag--decision">规则</span>
影响显示效果的代码更新前,必须先通过原型图确认
</li>
</ul>
</div>
<script>
/** 主题切换 */
function toggleTheme() {
const html = document.documentElement;
html.setAttribute('data-theme', html.getAttribute('data-theme') === 'dark' ? 'light' : 'dark');
}
/** 排查步骤栏 收起/展开 */
function toggleTsBar(view) {
const prefix = view === 'desktop' ? 'desktop' : 'mobile';
const content = document.getElementById(prefix + 'TsContent');
const toggle = document.getElementById(prefix + 'TsToggle');
if (content.style.display === 'none') {
content.style.display = '';
toggle.textContent = '▼ 收起';
toggle.classList.remove('ts-top-bar__toggle--collapsed');
} else {
content.style.display = 'none';
toggle.textContent = '▲ 展开';
toggle.classList.add('ts-top-bar__toggle--collapsed');
}
}
/** 决策节点选择 */
function selectDecision(view, choice) {
const prefix = view === 'desktop' ? 'desktop' : 'mobile';
const decision = document.getElementById(prefix + 'TsDecision');
const instruction = document.getElementById(prefix + 'TsInstruction');
const buttons = decision.querySelectorAll('.ts-option');
buttons.forEach(btn => { btn.disabled = true; btn.style.pointerEvents = 'none'; });
if (choice === 'yes') buttons[0].classList.add('ts-option--selected-yes');
else buttons[1].classList.add('ts-option--selected-no');
setTimeout(() => { decision.style.display = 'none'; instruction.style.display = 'block'; }, 400);
}
/** 切换资源标签页 */
function switchResourceTab(tab, tabEl) {
tabEl.parentElement.querySelectorAll('.resources__tab').forEach(t => t.classList.remove('resources__tab--active'));
tabEl.classList.add('resources__tab--active');
const content = document.getElementById('resourceContent');
if (tab === 'process') {
content.innerHTML = '<div class="resource-item"><div class="resource-item__icon">📋</div><div class="resource-item__info"><div class="resource-item__name">IT设备申请</div><div class="resource-item__desc">电脑/显示器/外设</div></div></div><div class="resource-item"><div class="resource-item__icon">🔑</div><div class="resource-item__info"><div class="resource-item__name">权限申请</div><div class="resource-item__desc">文件夹/系统访问权限</div></div></div><div class="resource-item"><div class="resource-item__icon">📞</div><div class="resource-item__info"><div class="resource-item__name">VPN申请</div><div class="resource-item__desc">远程办公网络连接</div></div></div>';
} else {
content.innerHTML = '<div class="resource-item"><div class="resource-item__icon">📄</div><div class="resource-item__info"><div class="resource-item__name">Office 365</div><div class="resource-item__desc">办公套件</div></div></div><div class="resource-item"><div class="resource-item__icon">📊</div><div class="resource-item__info"><div class="resource-item__name">Adobe Acrobat</div><div class="resource-item__desc">PDF编辑工具</div></div></div><div class="resource-item"><div class="resource-item__icon">📱</div><div class="resource-item__info"><div class="resource-item__name">企业微信</div><div class="resource-item__desc">即时通讯工具</div></div></div>';
}
}
/** 趣味问答选择 */
function selectQuizOption(option, isCorrect) {
document.querySelectorAll('.fun-quiz__option').forEach(opt => { opt.style.pointerEvents = 'none'; });
if (isCorrect) {
option.classList.add('fun-quiz__option--correct');
setTimeout(() => alert('✅ 答对啦!+10积分'), 300);
} else {
option.classList.add('fun-quiz__option--wrong');
document.querySelectorAll('.fun-quiz__option')[0].classList.add('fun-quiz__option--correct');
setTimeout(() => alert('❌ 答错了!正确答案是 A. 8001'), 300);
}
}
/** 呼叫坐席弹窗 */
function showCallModal() {
document.getElementById('callModal').classList.add('modal-overlay--visible');
setTimeout(() => {
document.getElementById('modalSending').style.display = 'none';
document.getElementById('modalSuccess').style.display = 'block';
}, 2500);
}
function hideCallModal(e) {
if (e && e.target !== e.currentTarget) return;
document.getElementById('callModal').classList.remove('modal-overlay--visible');
setTimeout(() => {
document.getElementById('modalSending').style.display = 'flex';
document.getElementById('modalSuccess').style.display = 'none';
}, 300);
}
/** 输入框监听 — 统一桌面端+手机端 */
function onInput(view, field) {
const btnId = view === 'desktop' ? 'desktopSendBtn' : 'mobileSendBtn';
const btn = document.getElementById(btnId);
btn.disabled = !field.value.trim();
}
/** 发送消息 — 统一桌面端+手机端 */
function sendMessage(view) {
const messagesId = view === 'desktop' ? 'desktopMessages' : 'mobileMessages';
const messages = document.getElementById(messagesId);
const container = view === 'desktop' ? document.getElementById('desktopLeft') : messages.closest('.chat-panel');
const textarea = container.querySelector('.input-row textarea');
if (!textarea || !textarea.value.trim()) return;
const msgHtml = '<div class="msg msg--employee" style="animation: bubble-in 0.3s ease-out"><div class="msg__bubble">' + escapeHtml(textarea.value) + '</div><div class="msg__time">刚刚</div></div>';
const sysMsg = messages.querySelector('.msg--system:last-child');
if (sysMsg) {
sysMsg.insertAdjacentHTML('beforebegin', msgHtml);
} else {
messages.insertAdjacentHTML('beforeend', msgHtml);
}
textarea.value = '';
onInput(view, textarea);
messages.scrollTop = messages.scrollHeight;
}
/** HTML转义 */
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
/** 工具按钮提示 */
function showToolTip(text) {
alert(text);
}
/* ============================================================================
拖拽调整 — 左右栏宽度
============================================================================ */
(function() {
const handle = document.getElementById('resizeHandleV');
const left = document.getElementById('desktopLeft');
const right = document.getElementById('desktopRight');
const body = document.getElementById('desktopBody');
if (!handle || !body) return;
let isDragging = false;
handle.addEventListener('mousedown', startDrag);
function startDrag(e) {
isDragging = true;
handle.classList.add('resize-handle-v--active');
document.body.style.cursor = 'col-resize';
document.body.style.userSelect = 'none';
document.addEventListener('mousemove', onDrag);
document.addEventListener('mouseup', stopDrag);
e.preventDefault();
}
function onDrag(e) {
if (!isDragging) return;
const rect = body.getBoundingClientRect();
const offsetX = e.clientX - rect.left;
const totalWidth = rect.width;
const leftWidth = Math.max(280, Math.min(totalWidth - 200, offsetX));
const rightWidth = totalWidth - leftWidth - 6;
left.style.flex = 'none';
left.style.width = leftWidth + 'px';
right.style.width = rightWidth + 'px';
}
function stopDrag() {
isDragging = false;
handle.classList.remove('resize-handle-v--active');
document.body.style.cursor = '';
document.body.style.userSelect = '';
document.removeEventListener('mousemove', onDrag);
document.removeEventListener('mouseup', stopDrag);
}
})();
</script>
</body>
</html>