facc04aa65
本提交为 .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-*/
747 lines
46 KiB
HTML
747 lines
46 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>方案三:邀请功能原型 — 交互流程</title>
|
||
<style>
|
||
:root {
|
||
--bg: #0f172a;
|
||
--bg2: #1e293b;
|
||
--bg3: #334155;
|
||
--border: #475569;
|
||
--text: #f1f5f9;
|
||
--dim: #94a3b8;
|
||
--accent: #3b82f6;
|
||
--accent-l: #60a5fa;
|
||
--green: #22c55e;
|
||
--amber: #f59e0b;
|
||
--red: #ef4444;
|
||
--purple: #a855f7;
|
||
--cyan: #06b6d4;
|
||
}
|
||
*{margin:0;padding:0;box-sizing:border-box}
|
||
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;background:var(--bg);color:var(--text);line-height:1.6}
|
||
|
||
.app{max-width:1200px;margin:0 auto;padding:20px}
|
||
|
||
h1{font-size:1.8em;font-weight:700;background:linear-gradient(135deg,var(--accent-l),var(--cyan));-webkit-background-clip:text;-webkit-text-fill-color:transparent;text-align:center;margin-bottom:8px}
|
||
.subtitle{text-align:center;color:var(--dim);margin-bottom:32px;font-size:0.95em}
|
||
|
||
h2{font-size:1.2em;font-weight:600;color:var(--accent-l);margin:24px 0 12px;padding-left:12px;border-left:3px solid var(--accent)}
|
||
|
||
.flow-steps{display:flex;gap:8px;margin-bottom:32px;overflow-x:auto;padding:8px 0}
|
||
.step{flex-shrink:0;padding:10px 16px;border-radius:10px;background:var(--bg2);border:1px solid var(--border);cursor:pointer;transition:all .2s;font-size:0.88em;white-space:nowrap}
|
||
.step:hover{border-color:var(--accent);background:#1e3a5f}
|
||
.step.active{background:var(--accent);border-color:var(--accent);color:#fff;font-weight:600}
|
||
.step .step-num{display:inline-block;width:20px;height:20px;border-radius:50%;background:rgba(255,255,255,0.15);text-align:center;line-height:20px;font-size:0.75em;margin-right:6px}
|
||
.step.active .step-num{background:rgba(255,255,255,0.3)}
|
||
|
||
.scene{display:none}
|
||
.scene.show{display:block}
|
||
|
||
.card{background:var(--bg2);border:1px solid var(--border);border-radius:12px;padding:20px;margin-bottom:16px}
|
||
.card h3{color:var(--accent-l);margin-bottom:10px;font-size:1em}
|
||
|
||
.row2{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px}
|
||
@media(max-width:768px){.row2{grid-template-columns:1fr}}
|
||
|
||
.mockup{background:#111827;border-radius:12px;border:1px solid var(--border);overflow:hidden;position:relative}
|
||
.mockup-bar{height:32px;background:var(--bg3);display:flex;align-items:center;padding:0 12px;gap:6px;border-bottom:1px solid var(--border)}
|
||
.mockup-dot{width:10px;height:10px;border-radius:50%}
|
||
.mockup-body{padding:16px;min-height:400px;position:relative}
|
||
|
||
.tag{display:inline-block;padding:2px 8px;border-radius:4px;font-size:0.78em;font-weight:600}
|
||
.tag-blue{background:rgba(59,130,246,0.2);color:var(--accent-l)}
|
||
.tag-green{background:rgba(34,197,94,0.2);color:var(--green)}
|
||
.tag-amber{background:rgba(245,158,11,0.2);color:var(--amber)}
|
||
|
||
table{width:100%;border-collapse:collapse;font-size:0.88em}
|
||
th,td{padding:8px 12px;text-align:left;border-bottom:1px solid var(--border)}
|
||
th{background:var(--bg3);color:var(--accent-l);font-weight:600;font-size:0.85em}
|
||
|
||
.avatar{display:inline-flex;width:32px;height:32px;border-radius:50%;align-items:center;justify-content:center;font-size:0.8em;font-weight:600;flex-shrink:0}
|
||
.av-user{background:#3b82f6;color:#fff}
|
||
.av-agent{background:#22c55e;color:#fff}
|
||
.av-invited{background:#f59e0b;color:#fff}
|
||
|
||
.msg{display:flex;gap:8px;margin-bottom:10px;align-items:flex-start}
|
||
.msg-left{flex-direction:row}
|
||
.msg-right{flex-direction:row-reverse}
|
||
.msg-bubble{max-width:70%;padding:10px 14px;border-radius:12px;font-size:0.88em;line-height:1.5}
|
||
.bubble-user{background:#1e3a5f;color:var(--text)}
|
||
.bubble-agent{background:#1a3d2e;color:var(--text)}
|
||
.bubble-sys{background:var(--bg3);color:var(--dim);font-size:0.82em;max-width:100%;text-align:center;padding:6px 12px;border-radius:8px}
|
||
.bubble-invited{background:#3d2e1a;color:var(--text)}
|
||
|
||
.btn{padding:8px 16px;border-radius:8px;border:1px solid var(--border);background:var(--bg3);color:var(--text);cursor:pointer;font-size:0.85em;transition:all .15s;display:inline-flex;align-items:center;gap:6px}
|
||
.btn:hover{border-color:var(--accent);background:#1e3a5f}
|
||
.btn-primary{background:var(--accent);border-color:var(--accent);color:#fff}
|
||
.btn-primary:hover{background:#2563eb}
|
||
|
||
.tree{padding:4px 0}
|
||
.tree-item{display:flex;align-items:center;gap:8px;padding:6px 12px;border-radius:6px;cursor:pointer;font-size:0.85em;transition:background .15s}
|
||
.tree-item:hover{background:var(--bg3)}
|
||
.tree-item.selected{background:rgba(59,130,246,0.15);outline:1px solid var(--accent)}
|
||
.tree-expand{width:16px;text-align:center;color:var(--dim);font-size:0.8em;cursor:pointer}
|
||
.tree-ck{width:16px;height:16px;border:1px solid var(--border);border-radius:3px;display:inline-flex;align-items:center;justify-content:center;font-size:0.7em;cursor:pointer;flex-shrink:0}
|
||
.tree-ck.checked{background:var(--accent);border-color:var(--accent);color:#fff}
|
||
|
||
.desc{color:var(--dim);font-size:0.9em;margin:8px 0;line-height:1.7}
|
||
.desc strong{color:var(--text)}
|
||
|
||
.callout{background:rgba(59,130,246,0.1);border-left:3px solid var(--accent);border-radius:0 8px 8px 0;padding:12px 16px;margin:12px 0;font-size:0.9em}
|
||
.callout.warn{background:rgba(245,158,11,0.1);border-left-color:var(--amber)}
|
||
.callout.ok{background:rgba(34,197,94,0.1);border-left-color:var(--green)}
|
||
|
||
.arrow{color:var(--accent);font-size:1.2em;font-weight:700;margin:0 6px}
|
||
|
||
.seq{display:flex;flex-direction:column;gap:0;margin:16px 0}
|
||
.seq-row{display:flex;align-items:center;gap:12px;padding:8px 0}
|
||
.seq-line{width:2px;height:20px;background:var(--accent);margin-left:15px}
|
||
.seq-dot{width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:0.8em;font-weight:700;flex-shrink:0}
|
||
.seq-dot-blue{background:var(--accent);color:#fff}
|
||
.seq-dot-green{background:var(--green);color:#fff}
|
||
.seq-dot-amber{background:var(--amber);color:#fff}
|
||
.seq-dot-purple{background:var(--purple);color:#fff}
|
||
.seq-text{flex:1;font-size:0.88em;color:var(--dim)}
|
||
.seq-text strong{color:var(--text)}
|
||
.seq-actor{font-size:0.75em;color:var(--accent-l);font-weight:600}
|
||
|
||
.wecom-notif{background:var(--bg3);border-radius:10px;padding:14px;border:1px solid var(--border);max-width:340px;margin:12px auto}
|
||
.wecom-notif-header{display:flex;align-items:center;gap:8px;margin-bottom:8px}
|
||
.wecom-notif-app{font-size:0.8em;color:var(--accent-l);font-weight:600}
|
||
.wecom-notif-time{font-size:0.7em;color:var(--dim)}
|
||
.wecom-notif-title{font-weight:600;font-size:0.92em;margin-bottom:4px}
|
||
.wecom-notif-body{font-size:0.85em;color:var(--dim);line-height:1.5}
|
||
.wecom-notif-btn{display:inline-block;margin-top:8px;padding:6px 14px;border-radius:6px;background:var(--accent);color:#fff;font-size:0.82em;cursor:pointer}
|
||
|
||
.compare-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
|
||
@media(max-width:768px){.compare-grid{grid-template-columns:1fr}}
|
||
.compare-card{background:var(--bg2);border:1px solid var(--border);border-radius:12px;padding:16px}
|
||
.compare-card.recommend{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}
|
||
.compare-card h3{text-align:center;margin-bottom:12px;font-size:1em}
|
||
.compare-card ul{padding-left:16px;font-size:0.88em}
|
||
.compare-card li{margin-bottom:4px;color:var(--dim)}
|
||
|
||
.badge{display:inline-block;padding:2px 10px;border-radius:12px;font-size:0.75em;font-weight:600}
|
||
.badge-blue{background:rgba(59,130,246,0.2);color:var(--accent-l)}
|
||
.badge-green{background:rgba(34,197,94,0.2);color:var(--green)}
|
||
.badge-red{background:rgba(239,68,68,0.2);color:var(--red)}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="app">
|
||
|
||
<h1>邀请功能 — 方案三交互原型</h1>
|
||
<div class="subtitle">WebSocket + 应用消息双通道架构 | 在现有一对一对话上扩展多人参与</div>
|
||
|
||
<div class="flow-steps" id="flowSteps">
|
||
<div class="step active" data-scene="s0">0. 架构概览</div>
|
||
<div class="step" data-scene="s1">1. 一对一会话中</div>
|
||
<div class="step" data-scene="s2">2. 点击邀请</div>
|
||
<div class="step" data-scene="s3">3. 选人界面</div>
|
||
<div class="step" data-scene="s4">4. 历史共享</div>
|
||
<div class="step" data-scene="s5">5. 确认邀请</div>
|
||
<div class="step" data-scene="s6">6. 被邀请方通知</div>
|
||
<div class="step" data-scene="s7">7. 加入会话</div>
|
||
<div class="step" data-scene="s8">8. 多人会话</div>
|
||
</div>
|
||
|
||
<!-- ===== Scene 0: 架构概览 ===== -->
|
||
<div class="scene show" id="s0">
|
||
<div class="card">
|
||
<h3>方案三核心架构</h3>
|
||
<p class="desc">在现有 <strong>WebSocket 双通道</strong> 架构上扩展,不引入群聊概念。后端维护 <strong>conversation.participants</strong> 数组,所有参与者共享同一个 WebSocket 会话通道。</p>
|
||
</div>
|
||
<div class="mockup">
|
||
<div class="mockup-bar"><div class="mockup-dot" style="background:#ef4444"></div><div class="mockup-dot" style="background:#f59e0b"></div><div class="mockup-dot" style="background:#22c55e"></div><span style="color:var(--dim);font-size:0.8em;margin-left:8px">架构流程图</span></div>
|
||
<div class="mockup-body" style="display:flex;align-items:center;justify-content:center;min-height:300px">
|
||
<svg viewBox="0 0 700 260" style="max-width:100%">
|
||
<defs>
|
||
<marker id="arrowBlue" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#3b82f6"/></marker>
|
||
<marker id="arrowGreen" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#22c55e"/></marker>
|
||
<marker id="arrowAmber" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f59e0b"/></marker>
|
||
</defs>
|
||
<rect x="20" y="80" width="120" height="100" rx="12" fill="#1e3a5f" stroke="#3b82f6" stroke-width="1"/>
|
||
<text x="80" y="118" text-anchor="middle" fill="#60a5fa" font-size="13" font-weight="600">H5 员工端</text>
|
||
<text x="80" y="138" text-anchor="middle" fill="#94a3b8" font-size="10">发起者</text>
|
||
<text x="80" y="155" text-anchor="middle" fill="#94a3b8" font-size="10">WebSocket</text>
|
||
|
||
<rect x="290" y="40" width="120" height="80" rx="12" fill="#1a3d2e" stroke="#22c55e" stroke-width="1"/>
|
||
<text x="350" y="72" text-anchor="middle" fill="#4ade80" font-size="13" font-weight="600">后端</text>
|
||
<text x="350" y="92" text-anchor="middle" fill="#94a3b8" font-size="10">FastAPI + Redis</text>
|
||
<text x="350" y="107" text-anchor="middle" fill="#94a3b8" font-size="10">participants[]</text>
|
||
|
||
<rect x="290" y="160" width="120" height="80" rx="12" fill="#3d2e1a" stroke="#f59e0b" stroke-width="1"/>
|
||
<text x="350" y="192" text-anchor="middle" fill="#fbbf24" font-size="13" font-weight="600">企微 API</text>
|
||
<text x="350" y="210" text-anchor="middle" fill="#94a3b8" font-size="10">应用消息推送</text>
|
||
<text x="350" y="225" text-anchor="middle" fill="#94a3b8" font-size="10">通讯录</text>
|
||
|
||
<rect x="560" y="30" width="120" height="80" rx="12" fill="#1a3d2e" stroke="#22c55e" stroke-width="1"/>
|
||
<text x="620" y="62" text-anchor="middle" fill="#4ade80" font-size="13" font-weight="600">坐席工作台</text>
|
||
<text x="620" y="82" text-anchor="middle" fill="#94a3b8" font-size="10">WebSocket</text>
|
||
<text x="620" y="97" text-anchor="middle" fill="#94a3b8" font-size="10">接单+邀请</text>
|
||
|
||
<rect x="560" y="150" width="120" height="80" rx="12" fill="#1e3a5f" stroke="#3b82f6" stroke-width="1"/>
|
||
<text x="620" y="182" text-anchor="middle" fill="#60a5fa" font-size="13" font-weight="600">H5 被邀请人</text>
|
||
<text x="620" y="202" text-anchor="middle" fill="#94a3b8" font-size="10">企微消息→H5</text>
|
||
<text x="620" y="217" text-anchor="middle" fill="#94a3b8" font-size="10">WebSocket</text>
|
||
|
||
<line x1="140" y1="110" x2="290" y2="80" stroke="#3b82f6" stroke-width="1.5" marker-end="url(#arrowBlue)"/>
|
||
<line x1="410" y1="80" x2="560" y2="70" stroke="#22c55e" stroke-width="1.5" marker-end="url(#arrowGreen)"/>
|
||
<line x1="140" y1="140" x2="290" y2="80" stroke="#3b82f6" stroke-width="1.5" stroke-dasharray="4 3"/>
|
||
<line x1="350" y1="120" x2="350" y2="160" stroke="#f59e0b" stroke-width="1.5" marker-end="url(#arrowAmber)"/>
|
||
<line x1="410" y1="200" x2="560" y2="190" stroke="#3b82f6" stroke-width="1.5" marker-end="url(#arrowBlue)"/>
|
||
|
||
<text x="200" y="80" fill="#94a3b8" font-size="9">WS 双向</text>
|
||
<text x="470" y="65" fill="#94a3b8" font-size="9">WS 双向</text>
|
||
<text x="370" y="148" fill="#94a3b8" font-size="9">邀请通知</text>
|
||
<text x="470" y="185" fill="#94a3b8" font-size="9">应用消息→H5→WS</text>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
<div class="callout ok"><strong>关键区别</strong>:不创建企微群聊,所有参与者通过 H5+WebSocket 接入同一个后端 conversation。企微应用消息仅用于「通知被邀请人」和「断连降级推送」。</div>
|
||
</div>
|
||
|
||
<!-- ===== Scene 1: 一对一会话中 ===== -->
|
||
<div class="scene" id="s1">
|
||
<div class="card">
|
||
<h3>场景:坐席正在与用户一对一对话</h3>
|
||
<p class="desc">用户「张三」遇到 VPN 问题,坐席「小李」正在处理。对话过程中发现需要网络安全组同事「王工」协助排查。</p>
|
||
</div>
|
||
<div class="row2">
|
||
<div class="mockup">
|
||
<div class="mockup-bar"><div class="mockup-dot" style="background:#ef4444"></div><div class="mockup-dot" style="background:#f59e0b"></div><div class="mockup-dot" style="background:#22c55e"></div><span style="color:var(--dim);font-size:0.78em;margin-left:8px">坐席工作台 — 会话区</span></div>
|
||
<div class="mockup-body">
|
||
<div style="display:flex;align-items:center;gap:8px;padding-bottom:10px;border-bottom:1px solid var(--border);margin-bottom:12px">
|
||
<div class="avatar av-user">张</div>
|
||
<div style="flex:1">
|
||
<div style="font-weight:600;font-size:0.92em">张三</div>
|
||
<div style="font-size:0.78em;color:var(--dim)">IT研发部 · 高级开发工程师</div>
|
||
</div>
|
||
<span class="tag tag-green">会话中</span>
|
||
<button class="btn" style="font-size:0.8em" onclick="goScene('s2')">+ 邀请</button>
|
||
</div>
|
||
<div class="msg msg-left">
|
||
<div class="avatar av-user" style="width:28px;height:28px;font-size:0.7em">张</div>
|
||
<div class="msg-bubble bubble-user">VPN 连接不上,提示证书错误</div>
|
||
</div>
|
||
<div class="msg msg-right">
|
||
<div class="avatar av-agent" style="width:28px;height:28px;font-size:0.7em">李</div>
|
||
<div class="msg-bubble bubble-agent">您好,请问您使用的是 ATrust 还是零信任客户端?</div>
|
||
</div>
|
||
<div class="msg msg-left">
|
||
<div class="avatar av-user" style="width:28px;height:28px;font-size:0.7em">张</div>
|
||
<div class="msg-bubble bubble-user">ATrust,之前一直正常的,今天突然不行了</div>
|
||
</div>
|
||
<div class="msg msg-right">
|
||
<div class="avatar av-agent" style="width:28px;height:28px;font-size:0.7em">李</div>
|
||
<div class="msg-bubble bubble-agent">这个问题可能涉及网络策略变更,我邀请网络安全组的同事一起排查</div>
|
||
</div>
|
||
<div style="text-align:center;margin:16px 0">
|
||
<button class="btn-primary btn" onclick="goScene('s2')">点击「+ 邀请」按钮</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div class="card">
|
||
<h3>交互说明</h3>
|
||
<div class="seq">
|
||
<div class="seq-row"><div class="seq-dot seq-dot-green">1</div><div class="seq-text"><strong>坐席点击「+ 邀请」</strong><br><span class="seq-actor">坐席工作台</span></div></div>
|
||
<div class="seq-line"></div>
|
||
<div class="seq-row"><div class="seq-dot seq-dot-blue">2</div><div class="seq-text"><strong>弹出选人弹窗</strong>(组织架构树/搜索)<br><span class="seq-actor">坐席工作台</span></div></div>
|
||
<div class="seq-line"></div>
|
||
<div class="seq-row"><div class="seq-dot seq-dot-amber">3</div><div class="seq-text"><strong>选择历史消息共享范围</strong><br><span class="seq-actor">选人弹窗</span></div></div>
|
||
<div class="seq-line"></div>
|
||
<div class="seq-row"><div class="seq-dot seq-dot-blue">4</div><div class="seq-text"><strong>确认邀请 → 后端处理</strong><br><span class="seq-actor">后端 API</span></div></div>
|
||
<div class="seq-line"></div>
|
||
<div class="seq-row"><div class="seq-dot seq-dot-amber">5</div><div class="seq-text"><strong>企微应用消息通知被邀请人</strong><br><span class="seq-actor">企微 API</span></div></div>
|
||
<div class="seq-line"></div>
|
||
<div class="seq-row"><div class="seq-dot seq-dot-green">6</div><div class="seq-text"><strong>被邀请人打开 H5 加入会话</strong><br><span class="seq-actor">H5 员工端</span></div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== Scene 2: 点击邀请 ===== -->
|
||
<div class="scene" id="s2">
|
||
<div class="card">
|
||
<h3>步骤 2:点击「+ 邀请」→ 弹出选人弹窗</h3>
|
||
<p class="desc">弹窗包含三个区域:<strong>组织架构树</strong>(左侧)、<strong>已选人员</strong>(右侧上方)、<strong>历史消息共享设置</strong>(右侧下方)。</p>
|
||
</div>
|
||
<div class="mockup">
|
||
<div class="mockup-bar"><div class="mockup-dot" style="background:#ef4444"></div><div class="mockup-dot" style="background:#f59e0b"></div><div class="mockup-dot" style="background:#22c55e"></div><span style="color:var(--dim);font-size:0.78em;margin-left:8px">坐席工作台 — 邀请弹窗</span></div>
|
||
<div class="mockup-body" style="min-height:440px">
|
||
<div style="display:flex;gap:16px;height:100%">
|
||
<!-- 左侧:组织架构 -->
|
||
<div style="flex:1;border-right:1px solid var(--border);padding-right:12px">
|
||
<div style="font-weight:600;font-size:0.9em;margin-bottom:8px;color:var(--accent-l)">选择邀请对象</div>
|
||
<input type="text" placeholder="搜索姓名/部门..." style="width:100%;padding:8px 12px;border-radius:8px;border:1px solid var(--border);background:var(--bg3);color:var(--text);font-size:0.85em;margin-bottom:10px;outline:none">
|
||
<div class="tree">
|
||
<div class="tree-item" onclick="toggleTree(this)">
|
||
<div class="tree-expand">▼</div>
|
||
<div class="tree-ck" onclick="event.stopPropagation();toggleCk(this)">✓</div>
|
||
<span>信息技术部</span>
|
||
</div>
|
||
<div style="padding-left:20px">
|
||
<div class="tree-item" onclick="toggleTree(this)">
|
||
<div class="tree-expand">▶</div>
|
||
<div class="tree-ck" onclick="event.stopPropagation();toggleCk(this)"></div>
|
||
<span>IT支持组</span>
|
||
</div>
|
||
<div class="tree-item selected">
|
||
<div class="tree-expand" style="visibility:hidden"></div>
|
||
<div class="tree-ck checked" onclick="event.stopPropagation();toggleCk(this)">✓</div>
|
||
<span>王工</span>
|
||
<span style="font-size:0.75em;color:var(--dim);margin-left:4px">网络安全</span>
|
||
</div>
|
||
<div class="tree-item">
|
||
<div class="tree-expand" style="visibility:hidden"></div>
|
||
<div class="tree-ck" onclick="event.stopPropagation();toggleCk(this)"></div>
|
||
<span>赵磊</span>
|
||
<span style="font-size:0.75em;color:var(--dim);margin-left:4px">系统运维</span>
|
||
</div>
|
||
</div>
|
||
<div style="padding-left:20px">
|
||
<div class="tree-item">
|
||
<div class="tree-expand" style="visibility:hidden"></div>
|
||
<div class="tree-ck" onclick="event.stopPropagation();toggleCk(this)"></div>
|
||
<span>软件开发组</span>
|
||
<span style="font-size:0.75em;color:var(--dim);margin-left:4px">8人</span>
|
||
</div>
|
||
</div>
|
||
<div class="tree-item" onclick="toggleTree(this)">
|
||
<div class="tree-expand">▶</div>
|
||
<div class="tree-ck" onclick="event.stopPropagation();toggleCk(this)"></div>
|
||
<span>信息安全部</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 右侧:已选+设置 -->
|
||
<div style="flex:1;padding-left:12px">
|
||
<div style="font-weight:600;font-size:0.9em;margin-bottom:8px;color:var(--accent-l)">已选择 (1)</div>
|
||
<div style="background:var(--bg3);border-radius:8px;padding:10px;margin-bottom:16px">
|
||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
|
||
<div class="avatar av-invited" style="width:28px;height:28px;font-size:0.7em">王</div>
|
||
<div style="flex:1;font-size:0.88em">王工</div>
|
||
<span style="font-size:0.75em;color:var(--dim)">信息技术部·网络安全</span>
|
||
<span style="cursor:pointer;color:var(--red);font-size:0.85em">✕</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="font-weight:600;font-size:0.9em;margin-bottom:8px;color:var(--accent-l)">历史消息共享</div>
|
||
<div style="background:var(--bg3);border-radius:8px;padding:12px;margin-bottom:16px">
|
||
<div style="font-size:0.85em;color:var(--dim);margin-bottom:8px">被邀请人加入后能看到哪些历史消息?</div>
|
||
<label style="display:flex;align-items:center;gap:8px;font-size:0.88em;margin-bottom:6px;cursor:pointer">
|
||
<input type="radio" name="share" checked style="accent-color:var(--accent)"> 共享全部历史消息
|
||
</label>
|
||
<label style="display:flex;align-items:center;gap:8px;font-size:0.88em;margin-bottom:6px;cursor:pointer">
|
||
<input type="radio" name="share" style="accent-color:var(--accent)"> 仅共享最近10条消息
|
||
</label>
|
||
<label style="display:flex;align-items:center;gap:8px;font-size:0.88em;cursor:pointer">
|
||
<input type="radio" name="share" style="accent-color:var(--accent)"> 不共享历史消息
|
||
</label>
|
||
</div>
|
||
|
||
<div style="font-weight:600;font-size:0.9em;margin-bottom:8px;color:var(--accent-l)">邀请说明(可选)</div>
|
||
<textarea placeholder="例如:需要你协助排查VPN证书问题..." style="width:100%;height:56px;padding:8px 12px;border-radius:8px;border:1px solid var(--border);background:var(--bg3);color:var(--text);font-size:0.85em;resize:none;outline:none"></textarea>
|
||
|
||
<div style="display:flex;gap:8px;justify-content:flex-end;margin-top:16px">
|
||
<button class="btn" onclick="goScene('s1')">取消</button>
|
||
<button class="btn btn-primary" onclick="goScene('s5')">确认邀请</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== Scene 3: 选人界面(同Scene2,此处展示另一种交互:选部门) ===== -->
|
||
<div class="scene" id="s3">
|
||
<div class="card">
|
||
<h3>步骤 3:选择人员 — 支持按部门批量邀请</h3>
|
||
<p class="desc">勾选部门节点 = 邀请该部门<strong>所有人</strong>。每个被邀请人都会收到独立的应用消息通知。</p>
|
||
</div>
|
||
<div class="row2">
|
||
<div class="mockup">
|
||
<div class="mockup-bar"><div class="mockup-dot" style="background:#ef4444"></div><div class="mockup-dot" style="background:#f59e0b"></div><div class="mockup-dot" style="background:#22c55e"></div><span style="color:var(--dim);font-size:0.78em;margin-left:8px">选人 — 勾选整个部门</span></div>
|
||
<div class="mockup-body" style="min-height:280px">
|
||
<div class="tree">
|
||
<div class="tree-item" style="background:rgba(59,130,246,0.1)">
|
||
<div class="tree-expand">▼</div>
|
||
<div class="tree-ck checked">✓</div>
|
||
<span style="font-weight:600">信息安全部</span>
|
||
<span class="tag tag-blue" style="margin-left:8px">全部邀请</span>
|
||
</div>
|
||
<div style="padding-left:20px">
|
||
<div class="tree-item">
|
||
<div class="tree-expand" style="visibility:hidden"></div>
|
||
<div class="tree-ck checked">✓</div>
|
||
<span>陈安全</span>
|
||
<span style="font-size:0.75em;color:var(--dim);margin-left:4px">安全主管</span>
|
||
</div>
|
||
<div class="tree-item">
|
||
<div class="tree-expand" style="visibility:hidden"></div>
|
||
<div class="tree-ck checked">✓</div>
|
||
<span>刘防护</span>
|
||
<span style="font-size:0.75em;color:var(--dim);margin-left:4px">安全工程师</span>
|
||
</div>
|
||
</div>
|
||
<div class="tree-item">
|
||
<div class="tree-expand">▶</div>
|
||
<div class="tree-ck"></div>
|
||
<span>IT支持组</span>
|
||
<span style="font-size:0.75em;color:var(--dim);margin-left:4px">3人</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div class="card">
|
||
<h3>选人交互规则</h3>
|
||
<table>
|
||
<tr><th>操作</th><th>效果</th></tr>
|
||
<tr><td>勾选人员</td><td>该人员加入已选列表</td></tr>
|
||
<tr><td>勾选部门</td><td>该部门下所有人员加入已选列表</td></tr>
|
||
<tr><td>搜索姓名</td><td>模糊匹配,点击结果直接加入已选</td></tr>
|
||
<tr><td>取消勾选</td><td>从已选列表移除</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="callout warn"><strong>注意</strong>:邀请人数建议上限 10 人。超过 10 人时应提示「当前邀请人数较多,建议优先邀请关键人员」。这不是硬限制,而是用户体验优化。</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== Scene 4: 历史消息共享 ===== -->
|
||
<div class="scene" id="s4">
|
||
<div class="card">
|
||
<h3>步骤 4:历史消息共享设置</h3>
|
||
<p class="desc">邀请时坐席可以选择被邀请人能看到哪些历史消息。这是<strong>隐私保护</strong>的重要设计——避免敏感信息(如员工个人账号问题)被无关人员看到。</p>
|
||
</div>
|
||
<div class="row2">
|
||
<div class="card">
|
||
<h3>三种共享模式对比</h3>
|
||
<table>
|
||
<tr><th>模式</th><th>适用场景</th><th>隐私风险</th></tr>
|
||
<tr><td><span class="badge badge-green">共享全部</span></td><td>通用IT问题(VPN/网络/打印),无敏感信息</td><td style="color:var(--green)">低</td></tr>
|
||
<tr><td><span class="badge badge-blue">最近10条</span></td><td>对话较长,仅需上下文即可理解当前问题</td><td style="color:var(--amber)">中</td></tr>
|
||
<tr><td><span class="badge badge-red">不共享</span></td><td>涉及员工个人账号/权限等敏感信息</td><td style="color:var(--green)">低</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="card">
|
||
<h3>被邀请人视角 — 共享 vs 不共享</h3>
|
||
<div style="display:flex;gap:12px">
|
||
<div style="flex:1;background:var(--bg3);border-radius:8px;padding:10px">
|
||
<div style="font-size:0.82em;font-weight:600;color:var(--green);margin-bottom:6px">共享全部</div>
|
||
<div style="font-size:0.82em;color:var(--dim)">
|
||
[张三] VPN连不上...<br>
|
||
[小李] 请问用的是...<br>
|
||
[张三] ATrust...<br>
|
||
<span style="color:var(--accent-l)">↑ 可看到完整上下文</span>
|
||
</div>
|
||
</div>
|
||
<div style="flex:1;background:var(--bg3);border-radius:8px;padding:10px">
|
||
<div style="font-size:0.82em;font-weight:600;color:var(--red);margin-bottom:6px">不共享</div>
|
||
<div style="font-size:0.82em;color:var(--dim)">
|
||
<span style="opacity:0.4">[历史消息不可见]</span><br>
|
||
── 加入会话 ──<br>
|
||
[王工] 我来看看VPN...<br>
|
||
<span style="color:var(--amber)">↑ 仅能看到加入后的消息</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="callout"><strong>默认值</strong>:建议默认选中「最近10条」,在可见性和隐私之间取得平衡。坐席可根据实际情况调整。</div>
|
||
</div>
|
||
|
||
<!-- ===== Scene 5: 确认邀请 ===== -->
|
||
<div class="scene" id="s5">
|
||
<div class="card">
|
||
<h3>步骤 5:确认邀请 → 后端处理流程</h3>
|
||
</div>
|
||
<div class="mockup">
|
||
<div class="mockup-bar"><div class="mockup-dot" style="background:#ef4444"></div><div class="mockup-dot" style="background:#f59e0b"></div><div class="mockup-dot" style="background:#22c55e"></div><span style="color:var(--dim);font-size:0.78em;margin-left:8px">确认邀请 — 后端处理时序</span></div>
|
||
<div class="mockup-body" style="min-height:340px">
|
||
<div class="seq">
|
||
<div class="seq-row">
|
||
<div class="seq-dot seq-dot-green">1</div>
|
||
<div class="seq-text"><strong>坐席点击「确认邀请」</strong><div class="seq-actor">坐席工作台 → POST /api/conversations/:id/invite</div></div>
|
||
</div>
|
||
<div class="seq-line"></div>
|
||
<div class="seq-row">
|
||
<div class="seq-dot seq-dot-blue">2</div>
|
||
<div class="seq-text"><strong>后端更新 participants 数组</strong>,将王工 userid 加入 conversation.participants<div class="seq-actor">后端 → PostgreSQL</div></div>
|
||
</div>
|
||
<div class="seq-line"></div>
|
||
<div class="seq-row">
|
||
<div class="seq-dot seq-dot-blue">3</div>
|
||
<div class="seq-text"><strong>后端生成邀请卡片消息</strong>(含会话ID + H5入口链接 + 邀请说明 + 共享的历史消息摘要)<div class="seq-actor">后端 → 企微应用消息 API</div></div>
|
||
</div>
|
||
<div class="seq-line"></div>
|
||
<div class="seq-row">
|
||
<div class="seq-dot seq-dot-amber">4</div>
|
||
<div class="seq-text"><strong>企微推送应用消息给王工</strong>(单聊卡片消息,非群聊消息)<div class="seq-actor">企微 → 王工企微客户端</div></div>
|
||
</div>
|
||
<div class="seq-line"></div>
|
||
<div class="seq-row">
|
||
<div class="seq-dot seq-dot-green">5</div>
|
||
<div class="seq-text"><strong>坐席工作台显示「已邀请王工」系统消息</strong>,会话参与者列表更新<div class="seq-actor">WebSocket → 坐席工作台</div></div>
|
||
</div>
|
||
<div class="seq-line"></div>
|
||
<div class="seq-row">
|
||
<div class="seq-dot seq-dot-blue">6</div>
|
||
<div class="seq-text"><strong>坐席工作台显示「已邀请王工」系统消息</strong>,会话参与者列表更新<div class="seq-actor">WebSocket → H5 发起者</div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="card" style="margin-top:16px">
|
||
<h3>API 接口设计</h3>
|
||
<table>
|
||
<tr><th>接口</th><th>方法</th><th>说明</th></tr>
|
||
<tr><td style="font-family:monospace;color:var(--cyan)">/api/conversations/:id/invite</td><td>POST</td><td>邀请人员加入会话</td></tr>
|
||
<tr><td style="font-family:monospace;color:var(--cyan)">/api/conversations/:id/participants</td><td>GET</td><td>获取会话参与者列表</td></tr>
|
||
<tr><td style="font-family:monospace;color:var(--cyan)">/api/conversations/:id/participants/:uid</td><td>DELETE</td><td>移除参与者(坐席/被邀请人退出)</td></tr>
|
||
<tr><td style="font-family:monospace;color:var(--cyan)">/api/contacts/departments</td><td>GET</td><td>获取组织架构树(缓存自eHR/企微通讯录)</td></tr>
|
||
<tr><td style="font-family:monospace;color:var(--cyan)">/api/contacts/search</td><td>GET</td><td>搜索人员(姓名/工号模糊匹配)</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== Scene 6: 被邀请方通知 ===== -->
|
||
<div class="scene" id="s6">
|
||
<div class="card">
|
||
<h3>步骤 6:被邀请人收到企微应用消息</h3>
|
||
<p class="desc">王工在企微中收到一条<strong>应用消息卡片</strong>,点击卡片中的按钮即可跳转到 H5 会话页面。</p>
|
||
</div>
|
||
<div class="row2">
|
||
<div class="mockup">
|
||
<div class="mockup-bar"><div class="mockup-dot" style="background:#ef4444"></div><div class="mockup-dot" style="background:#f59e0b"></div><div class="mockup-dot" style="background:#22c55e"></div><span style="color:var(--dim);font-size:0.78em;margin-left:8px">企微消息卡片 — 通知样式</span></div>
|
||
<div class="mockup-body" style="display:flex;align-items:center;justify-content:center;min-height:340px">
|
||
<div class="wecom-notif">
|
||
<div class="wecom-notif-header">
|
||
<div class="avatar av-invited" style="width:24px;height:24px;font-size:0.65em">IT</div>
|
||
<div class="wecom-notif-app">IT智能服务台</div>
|
||
<div class="wecom-notif-time">刚刚</div>
|
||
</div>
|
||
<div class="wecom-notif-title">邀请你加入 IT 支持会话</div>
|
||
<div class="wecom-notif-body">
|
||
<div style="margin-bottom:6px"><strong>邀请人:</strong>小李(IT支持组)</div>
|
||
<div style="margin-bottom:6px"><strong>发起人:</strong>张三(IT研发部)</div>
|
||
<div style="margin-bottom:6px"><strong>问题摘要:</strong>VPN证书错误,ATrust客户端无法连接</div>
|
||
<div style="margin-bottom:6px"><strong>邀请说明:</strong>需要你协助排查VPN证书问题</div>
|
||
</div>
|
||
<div style="display:flex;gap:8px;margin-top:10px">
|
||
<div class="wecom-notif-btn" onclick="goScene('s7')">加入会话</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div class="card">
|
||
<h3>消息卡片设计要点</h3>
|
||
<ul style="color:var(--dim);font-size:0.88em">
|
||
<li><strong style="color:var(--text)">使用交互式卡片消息</strong>(textcard 类型),而非纯文本</li>
|
||
<li><strong style="color:var(--text)">包含问题摘要</strong>,让被邀请人判断是否需要立即加入</li>
|
||
<li><strong style="color:var(--text)">「加入会话」按钮</strong>直接跳转 H5 页面(企微内置浏览器)</li>
|
||
<li><strong style="color:var(--text)">如果共享了历史消息</strong>,摘要中自动包含最近 2-3 条关键消息</li>
|
||
</ul>
|
||
</div>
|
||
<div class="card">
|
||
<h3>降级方案</h3>
|
||
<p class="desc">如果被邀请人<strong>未安装企微</strong>或<strong>处于离线</strong>:后端记录邀请状态为「待加入」。被邀请人下次登录企微时会收到消息。同时,坐席工作台显示邀请状态:<span class="tag tag-amber">待加入</span> / <span class="tag tag-green">已加入</span></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== Scene 7: 加入会话 ===== -->
|
||
<div class="scene" id="s7">
|
||
<div class="card">
|
||
<h3>步骤 7:被邀请人点击「加入会话」→ H5 页面</h3>
|
||
<p class="desc">王工点击消息卡片按钮 → 企微内置浏览器打开 H5 页面 → 自动加入 WebSocket 会话 → 看到历史消息(如果被共享)→ 可直接发消息</p>
|
||
</div>
|
||
<div class="row2">
|
||
<div class="mockup">
|
||
<div class="mockup-bar"><div class="mockup-dot" style="background:#ef4444"></div><div class="mockup-dot" style="background:#f59e0b"></div><div class="mockup-dot" style="background:#22c55e"></div><span style="color:var(--dim);font-size:0.78em;margin-left:8px">H5 被邀请人视角 — 加入后</span></div>
|
||
<div class="mockup-body">
|
||
<div style="display:flex;align-items:center;gap:8px;padding-bottom:10px;border-bottom:1px solid var(--border);margin-bottom:12px">
|
||
<div style="font-weight:600;font-size:0.92em;flex:1">IT支持会话</div>
|
||
<span style="font-size:0.78em;color:var(--dim)">3人参与</span>
|
||
</div>
|
||
<div class="msg" style="justify-content:center">
|
||
<div class="msg-bubble bubble-sys">── 小李 邀请了 王工 加入会话 ──</div>
|
||
</div>
|
||
<div class="msg msg-left">
|
||
<div class="avatar av-user" style="width:28px;height:28px;font-size:0.7em">张</div>
|
||
<div class="msg-bubble bubble-user">VPN 连接不上,提示证书错误</div>
|
||
</div>
|
||
<div class="msg msg-right">
|
||
<div class="avatar av-agent" style="width:28px;height:28px;font-size:0.7em">李</div>
|
||
<div class="msg-bubble bubble-agent">请问您使用的是 ATrust 还是零信任客户端?</div>
|
||
</div>
|
||
<div class="msg msg-left">
|
||
<div class="avatar av-user" style="width:28px;height:28px;font-size:0.7em">张</div>
|
||
<div class="msg-bubble bubble-user">ATrust,之前一直正常的,今天突然不行了</div>
|
||
</div>
|
||
<div class="msg" style="justify-content:center">
|
||
<div class="msg-bubble bubble-sys">── 王工 已加入会话 ──</div>
|
||
</div>
|
||
<div class="msg msg-right">
|
||
<div class="avatar av-invited" style="width:28px;height:28px;font-size:0.7em">王</div>
|
||
<div class="msg-bubble bubble-invited">我来看看,先确认下证书服务状态</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div class="card">
|
||
<h3>加入流程细节</h3>
|
||
<div class="seq">
|
||
<div class="seq-row"><div class="seq-dot seq-dot-amber">1</div><div class="seq-text"><strong>点击「加入会话」</strong><br>企微内置浏览器打开 H5 URL(含 conversation_id + token)</div></div>
|
||
<div class="seq-line"></div>
|
||
<div class="seq-row"><div class="seq-dot seq-dot-blue">2</div><div class="seq-text"><strong>H5 自动认证</strong><br>企微环境 → OAuth2 自动登录;非企微 → Mock 登录</div></div>
|
||
<div class="seq-line"></div>
|
||
<div class="seq-row"><div class="seq-dot seq-dot-blue">3</div><div class="seq-text"><strong>建立 WebSocket 连接</strong><br>加入 conversation 的消息通道</div></div>
|
||
<div class="seq-line"></div>
|
||
<div class="seq-row"><div class="seq-dot seq-dot-green">4</div><div class="seq-text"><strong>拉取历史消息</strong><br>根据共享设置,显示可查看的历史消息</div></div>
|
||
<div class="seq-line"></div>
|
||
<div class="seq-row"><div class="seq-dot seq-dot-green">5</div><div class="seq-text"><strong>广播「XX 已加入」</strong><br>所有参与者(坐席+用户+其他被邀请人)都能看到</div></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== Scene 8: 多人会话 ===== -->
|
||
<div class="scene" id="s8">
|
||
<div class="card">
|
||
<h3>步骤 8:多人会话持续进行</h3>
|
||
<p class="desc">所有人通过 <strong>WebSocket</strong> 实时通信。坐席工作台和所有 H5 参与者共享同一个消息通道。坐席拥有<strong>管理权限</strong>(可移除参与者、结束会话)。</p>
|
||
</div>
|
||
<div class="row2">
|
||
<!-- 坐席工作台视角 -->
|
||
<div class="mockup">
|
||
<div class="mockup-bar"><div class="mockup-dot" style="background:#ef4444"></div><div class="mockup-dot" style="background:#f59e0b"></div><div class="mockup-dot" style="background:#22c55e"></div><span style="color:var(--dim);font-size:0.78em;margin-left:8px">坐席工作台 — 多人会话</span></div>
|
||
<div class="mockup-body" style="min-height:400px">
|
||
<!-- 参与者栏 -->
|
||
<div style="display:flex;align-items:center;gap:8px;padding-bottom:10px;border-bottom:1px solid var(--border);margin-bottom:12px;flex-wrap:wrap">
|
||
<div class="avatar av-user" style="width:32px;height:32px;font-size:0.7em">张</div>
|
||
<div style="font-size:0.8em">张三<span style="color:var(--dim);margin-left:4px">发起</span></div>
|
||
<div class="avatar av-agent" style="width:32px;height:32px;font-size:0.7em">李</div>
|
||
<div style="font-size:0.8em">小李<span style="color:var(--dim);margin-left:4px">坐席</span></div>
|
||
<div class="avatar av-invited" style="width:32px;height:32px;font-size:0.7em">王</div>
|
||
<div style="font-size:0.8em">王工<span style="color:var(--dim);margin-left:4px">邀请</span></div>
|
||
<button class="btn" style="font-size:0.75em;margin-left:auto" onclick="goScene('s2')">+ 邀请</button>
|
||
</div>
|
||
<!-- 消息 -->
|
||
<div class="msg" style="justify-content:center"><div class="msg-bubble bubble-sys">── 小李 邀请了 王工 加入会话 ──</div></div>
|
||
<div class="msg msg-left">
|
||
<div class="avatar av-user" style="width:28px;height:28px;font-size:0.7em">张</div>
|
||
<div style="font-size:0.72em;color:var(--dim);margin-bottom:2px">张三</div>
|
||
<div class="msg-bubble bubble-user">VPN 连接不上,提示证书错误</div>
|
||
</div>
|
||
<div class="msg msg-right">
|
||
<div class="avatar av-agent" style="width:28px;height:28px;font-size:0.7em">李</div>
|
||
<div style="font-size:0.72em;color:var(--dim);margin-bottom:2px">小李(坐席)</div>
|
||
<div class="msg-bubble bubble-agent">请问您使用的是 ATrust 还是零信任客户端?</div>
|
||
</div>
|
||
<div class="msg msg-left">
|
||
<div class="avatar av-user" style="width:28px;height:28px;font-size:0.7em">张</div>
|
||
<div style="font-size:0.72em;color:var(--dim);margin-bottom:2px">张三</div>
|
||
<div class="msg-bubble bubble-user">ATrust,今天突然不行了</div>
|
||
</div>
|
||
<div class="msg" style="justify-content:center"><div class="msg-bubble bubble-sys">── 王工 已加入会话 ──</div></div>
|
||
<div class="msg msg-right">
|
||
<div class="avatar av-invited" style="width:28px;height:28px;font-size:0.7em">王</div>
|
||
<div style="font-size:0.72em;color:var(--dim);margin-bottom:2px">王工(受邀)</div>
|
||
<div class="msg-bubble bubble-invited">证书服务器刚做了更新,我来检查下白名单配置</div>
|
||
</div>
|
||
<div class="msg msg-left">
|
||
<div class="avatar av-user" style="width:28px;height:28px;font-size:0.7em">张</div>
|
||
<div style="font-size:0.72em;color:var(--dim);margin-bottom:2px">张三</div>
|
||
<div class="msg-bubble bubble-user">好的,麻烦了</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- H5 被邀请人视角 -->
|
||
<div class="mockup">
|
||
<div class="mockup-bar"><div class="mockup-dot" style="background:#ef4444"></div><div class="mockup-dot" style="background:#f59e0b"></div><div class="mockup-dot" style="background:#22c55e"></div><span style="color:var(--dim);font-size:0.78em;margin-left:8px">H5 被邀请人视角 — 王工</span></div>
|
||
<div class="mockup-body" style="min-height:400px">
|
||
<div style="display:flex;align-items:center;gap:8px;padding-bottom:10px;border-bottom:1px solid var(--border);margin-bottom:12px">
|
||
<div style="font-weight:600;font-size:0.92em;flex:1">IT支持会话</div>
|
||
<span style="font-size:0.78em;color:var(--dim)">3人</span>
|
||
<span class="tag tag-green">已加入</span>
|
||
</div>
|
||
<!-- 王工只能看到共享后的消息 -->
|
||
<div style="font-size:0.78em;color:var(--dim);text-align:center;margin-bottom:8px;padding:6px;background:var(--bg3);border-radius:6px">你可以查看最近10条历史消息</div>
|
||
<div class="msg msg-left">
|
||
<div class="avatar av-user" style="width:28px;height:28px;font-size:0.7em">张</div>
|
||
<div style="font-size:0.72em;color:var(--dim);margin-bottom:2px">张三</div>
|
||
<div class="msg-bubble bubble-user">ATrust,今天突然不行了</div>
|
||
</div>
|
||
<div class="msg" style="justify-content:center"><div class="msg-bubble bubble-sys">── 王工 已加入会话 ──</div></div>
|
||
<div class="msg msg-right">
|
||
<div class="avatar av-invited" style="width:28px;height:28px;font-size:0.7em">王</div>
|
||
<div class="msg-bubble bubble-invited">证书服务器刚做了更新,我来检查下白名单配置</div>
|
||
</div>
|
||
<div class="msg msg-left">
|
||
<div class="avatar av-user" style="width:28px;height:28px;font-size:0.7em">张</div>
|
||
<div style="font-size:0.72em;color:var(--dim);margin-bottom:2px">张三</div>
|
||
<div class="msg-bubble bubble-user">好的,麻烦了</div>
|
||
</div>
|
||
<!-- 输入区 -->
|
||
<div style="margin-top:auto;padding-top:12px;border-top:1px solid var(--border)">
|
||
<div style="display:flex;gap:8px">
|
||
<input type="text" placeholder="输入消息..." style="flex:1;padding:8px 12px;border-radius:8px;border:1px solid var(--border);background:var(--bg3);color:var(--text);font-size:0.88em;outline:none">
|
||
<button class="btn btn-primary" style="font-size:0.85em">发送</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card" style="margin-top:16px">
|
||
<h3>坐席管理能力</h3>
|
||
<div class="row2" style="margin-bottom:0">
|
||
<div>
|
||
<table>
|
||
<tr><th>操作</th><th>权限</th><th>说明</th></tr>
|
||
<tr><td>邀请人员</td><td>坐席</td><td>选择人员加入会话</td></tr>
|
||
<tr><td>移除参与者</td><td>坐席</td><td>将某人移出会话(不通知)</td></tr>
|
||
<tr><td>主动退出</td><td>被邀请人</td><td>被邀请人可自行退出</td></tr>
|
||
<tr><td>结束会话</td><td>坐席</td><td>关闭会话,所有人断开</td></tr>
|
||
<tr><td>转让坐席</td><td>坐席</td><td>将坐席角色转给其他参与者</td></tr>
|
||
</table>
|
||
</div>
|
||
<div>
|
||
<div class="callout warn"><strong>与企微群聊的核心区别</strong>:<br>1. 会话数据完全在后端控制,不依赖企微群聊 API<br>2. 被邀请人通过 H5 WebSocket 通信,无需加入企微群<br>3. 坐席拥有管理权限,可控制参与者和历史可见性<br>4. 外部联系人(供应商等)也可通过 H5 链接加入</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
const steps = document.querySelectorAll('.step');
|
||
const scenes = document.querySelectorAll('.scene');
|
||
|
||
function goScene(id) {
|
||
steps.forEach(s => s.classList.remove('active'));
|
||
scenes.forEach(s => s.classList.remove('show'));
|
||
const idx = parseInt(id.replace('s',''));
|
||
steps[idx].classList.add('active');
|
||
document.getElementById(id).classList.add('show');
|
||
window.scrollTo({top: 0, behavior: 'smooth'});
|
||
}
|
||
|
||
steps.forEach(s => {
|
||
s.addEventListener('click', () => goScene(s.dataset.scene));
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|