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-*/
654 lines
19 KiB
HTML
654 lines
19 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>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
body {
|
||
font-family: -apple-system, 'PingFang SC', 'Helvetica Neue', sans-serif;
|
||
background: #f0f2f5;
|
||
min-height: 100vh;
|
||
display: flex;
|
||
}
|
||
|
||
/* === 左侧会话列表(模拟) === */
|
||
.sidebar {
|
||
width: 280px;
|
||
background: #fff;
|
||
border-right: 1px solid #e8e8e8;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.sidebar-header {
|
||
height: 56px;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0 20px;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
.conv-item {
|
||
padding: 12px 20px;
|
||
border-bottom: 1px solid #f5f5f5;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
.conv-item:hover { background: #f5f5f5; }
|
||
.conv-item.active { background: #e6f7ff; border-left: 3px solid #07C160; }
|
||
.conv-avatar {
|
||
width: 40px; height: 40px; border-radius: 4px;
|
||
background: #ccc; flex-shrink: 0;
|
||
display: flex; align-items: center; justify-content: center;
|
||
color: #fff; font-size: 16px;
|
||
}
|
||
.conv-info { flex: 1; min-width: 0; }
|
||
.conv-name { font-size: 14px; color: #333; font-weight: 500; }
|
||
.conv-preview { font-size: 12px; color: #999; margin-top: 4px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||
|
||
/* === 右侧聊天区域 === */
|
||
.chat-area {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
background: #f5f5f5;
|
||
min-width: 0;
|
||
}
|
||
.chat-header {
|
||
height: 56px;
|
||
background: #fff;
|
||
border-bottom: 1px solid #e8e8e8;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0 20px;
|
||
gap: 12px;
|
||
}
|
||
.chat-title { font-size: 16px; font-weight: 600; color: #333; }
|
||
.chat-subtitle { font-size: 12px; color: #999; }
|
||
|
||
/* === 参与者缩略条 === */
|
||
.participant-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 6px 20px;
|
||
background: #fafafa;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
cursor: pointer;
|
||
transition: background 0.2s;
|
||
}
|
||
.participant-bar:hover { background: #f0f0f0; }
|
||
|
||
.bar-avatars {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
/* 头像基础样式 */
|
||
.avatar {
|
||
width: 20px;
|
||
height: 20px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 9px;
|
||
font-weight: 600;
|
||
color: #fff;
|
||
flex-shrink: 0;
|
||
border: 1.5px solid transparent;
|
||
background-size: cover;
|
||
background-position: center;
|
||
}
|
||
.avatar + .avatar { margin-left: -4px; }
|
||
|
||
/* 角色边框色 */
|
||
.avatar.role-primary { border-color: #3b82f6; }
|
||
.avatar.role-collaborator { border-color: #07C160; }
|
||
.avatar.role-owner { border-color: #FF9800; }
|
||
.avatar.role-invitee { border-color: transparent; }
|
||
|
||
/* 自己高亮环 */
|
||
.avatar.is-self {
|
||
box-shadow: 0 0 0 2px #07C160;
|
||
transform: scale(1.1);
|
||
z-index: 10;
|
||
}
|
||
|
||
/* 超员 +N */
|
||
.avatar-more {
|
||
width: 20px;
|
||
height: 20px;
|
||
border-radius: 50%;
|
||
background: #e0e0e0;
|
||
color: #666;
|
||
font-size: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-left: -4px;
|
||
border: 1.5px solid #fff;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* 待加入红点 */
|
||
.avatar-wrap { position: relative; display: inline-flex; }
|
||
.pending-dot {
|
||
position: absolute;
|
||
top: -1px;
|
||
right: -1px;
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
background: #ee0a24;
|
||
border: 1px solid #fff;
|
||
}
|
||
|
||
.bar-label {
|
||
font-size: 12px;
|
||
color: #666;
|
||
margin-left: 4px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
.bar-expand-icon {
|
||
font-size: 10px;
|
||
transition: transform 0.2s;
|
||
}
|
||
.bar-expand-icon.expanded { transform: rotate(180deg); }
|
||
|
||
/* === 就地展开面板 === */
|
||
.expanded-panel {
|
||
max-height: 0;
|
||
overflow: hidden;
|
||
transition: max-height 0.2s ease, opacity 0.2s ease;
|
||
opacity: 0;
|
||
background: #fff;
|
||
border-bottom: 1px solid #e8e8e8;
|
||
}
|
||
.expanded-panel.show {
|
||
max-height: 150px;
|
||
overflow-y: auto;
|
||
opacity: 1;
|
||
}
|
||
|
||
.expanded-panel-inner {
|
||
padding: 8px 20px;
|
||
}
|
||
|
||
/* 参与者列表项 */
|
||
.participant-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 6px 0;
|
||
}
|
||
.participant-item:hover { background: #f9f9f9; }
|
||
|
||
.participant-item .avatar-md {
|
||
width: 24px;
|
||
height: 24px;
|
||
font-size: 11px;
|
||
border-width: 1.5px;
|
||
}
|
||
|
||
.participant-info {
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
.participant-name {
|
||
font-size: 13px;
|
||
color: #333;
|
||
font-weight: 500;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
max-width: 120px;
|
||
}
|
||
|
||
/* 角色标签 */
|
||
.role-tag {
|
||
font-size: 10px;
|
||
padding: 1px 5px;
|
||
border-radius: 3px;
|
||
font-weight: 500;
|
||
}
|
||
.role-tag.primary { background: #3b82f6; color: #fff; }
|
||
.role-tag.collaborator { background: #07C160; color: #fff; }
|
||
.role-tag.owner { background: #FF9800; color: #fff; }
|
||
.role-tag.invitee { background: #f0f0f0; color: #666; }
|
||
|
||
.participant-dept {
|
||
font-size: 11px;
|
||
color: #999;
|
||
}
|
||
|
||
/* 移除图标 */
|
||
.remove-icon {
|
||
width: 20px;
|
||
height: 20px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #ee0a24;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
border-radius: 4px;
|
||
opacity: 0;
|
||
transition: opacity 0.15s;
|
||
}
|
||
.participant-item:hover .remove-icon { opacity: 1; }
|
||
.remove-icon:hover { background: #fee; }
|
||
|
||
/* 消息列表区域 */
|
||
.message-list {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 16px 20px;
|
||
min-height: 200px;
|
||
}
|
||
.message-item {
|
||
display: flex;
|
||
margin-bottom: 16px;
|
||
gap: 10px;
|
||
}
|
||
.msg-avatar {
|
||
width: 32px; height: 32px; border-radius: 4px;
|
||
flex-shrink: 0; display: flex; align-items: center; justify-content: center;
|
||
font-size: 13px; color: #fff;
|
||
}
|
||
.msg-content { max-width: 60%; }
|
||
.msg-name { font-size: 11px; color: #999; margin-bottom: 2px; }
|
||
.msg-bubble {
|
||
background: #fff; padding: 8px 12px; border-radius: 0 8px 8px 8px;
|
||
font-size: 13px; color: #333; display: inline-block;
|
||
}
|
||
|
||
/* 底部输入区 */
|
||
.input-area {
|
||
height: 120px;
|
||
background: #fff;
|
||
border-top: 1px solid #e8e8e8;
|
||
padding: 12px 20px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
.input-toolbar {
|
||
display: flex;
|
||
gap: 8px;
|
||
}
|
||
.input-toolbar button {
|
||
font-size: 12px;
|
||
color: #666;
|
||
background: #f5f5f5;
|
||
border: none;
|
||
border-radius: 4px;
|
||
padding: 4px 10px;
|
||
cursor: pointer;
|
||
}
|
||
.input-textbox {
|
||
flex: 1;
|
||
height: 60px;
|
||
border: 1px solid #e0e0e0;
|
||
border-radius: 4px;
|
||
padding: 8px;
|
||
font-size: 13px;
|
||
resize: none;
|
||
outline: none;
|
||
}
|
||
.input-textbox:focus { border-color: #07C160; }
|
||
|
||
/* 控制面板 */
|
||
.control-panel {
|
||
position: fixed;
|
||
top: 10px;
|
||
right: 10px;
|
||
z-index: 200;
|
||
background: rgba(0,0,0,0.8);
|
||
border-radius: 8px;
|
||
padding: 8px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
}
|
||
.control-panel button {
|
||
font-size: 12px;
|
||
color: #fff;
|
||
background: #07C160;
|
||
border: none;
|
||
border-radius: 4px;
|
||
padding: 4px 10px;
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
}
|
||
.control-panel button:hover { opacity: 0.85; }
|
||
.control-panel .info {
|
||
font-size: 11px;
|
||
color: #aaa;
|
||
padding: 2px 4px;
|
||
}
|
||
|
||
/* P1: expandedStateMap 按会话ID记忆 */
|
||
.panel-section-title {
|
||
font-size: 11px;
|
||
color: #999;
|
||
padding: 4px 0 2px;
|
||
font-weight: 500;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="control-panel">
|
||
<button onclick="toggleExpand()">点击缩略条展开/收起</button>
|
||
<button onclick="toggleScene()">切换场景(3人/7人/10人)</button>
|
||
<div class="info" id="sceneInfo">场景: 3人</div>
|
||
</div>
|
||
|
||
<!-- 左侧会话列表 -->
|
||
<div class="sidebar">
|
||
<div class="sidebar-header">会话列表</div>
|
||
<div class="conv-item active">
|
||
<div class="conv-avatar" style="background:#FF9800">李</div>
|
||
<div class="conv-info">
|
||
<div class="conv-name">李四 · 网络故障</div>
|
||
<div class="conv-preview">王五: 我看了一下交换机日志...</div>
|
||
</div>
|
||
</div>
|
||
<div class="conv-item">
|
||
<div class="conv-avatar" style="background:#3b82f6">陈</div>
|
||
<div class="conv-info">
|
||
<div class="conv-name">陈六 · 电脑蓝屏</div>
|
||
<div class="conv-preview">已重启,问题解决</div>
|
||
</div>
|
||
</div>
|
||
<div class="conv-item">
|
||
<div class="conv-avatar" style="background:#07C160">周</div>
|
||
<div class="conv-info">
|
||
<div class="conv-name">周九 · 打印机问题</div>
|
||
<div class="conv-preview">好的,我来看看</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右侧聊天区域 -->
|
||
<div class="chat-area">
|
||
<div class="chat-header">
|
||
<span class="chat-title">李四 · 网络故障</span>
|
||
<span class="chat-subtitle" id="convStatus">3人参与</span>
|
||
</div>
|
||
|
||
<!-- 参与者缩略条 -->
|
||
<div class="participant-bar" onclick="toggleExpand()" id="bar">
|
||
<div class="bar-avatars" id="barAvatars"></div>
|
||
<span class="bar-label" id="barLabel">
|
||
<span id="barText">3位参与者</span>
|
||
<span class="bar-expand-icon" id="expandIcon">▼</span>
|
||
</span>
|
||
</div>
|
||
|
||
<!-- 就地展开面板 -->
|
||
<div class="expanded-panel" id="panel">
|
||
<div class="expanded-panel-inner" id="panelBody"></div>
|
||
</div>
|
||
|
||
<!-- 消息列表 -->
|
||
<div class="message-list">
|
||
<div class="message-item">
|
||
<div class="msg-avatar" style="background:#3b82f6">张</div>
|
||
<div class="msg-content">
|
||
<div class="msg-name">张明(主责坐席)</div>
|
||
<div class="msg-bubble">您好,网络问题我来协助处理。已邀请网络组同事加入。</div>
|
||
</div>
|
||
</div>
|
||
<div class="message-item">
|
||
<div class="msg-avatar" style="background:#FF9800">李</div>
|
||
<div class="msg-content">
|
||
<div class="msg-name">李四(发起人)</div>
|
||
<div class="msg-bubble">好的谢谢,我们网段的电脑都上不了网</div>
|
||
</div>
|
||
</div>
|
||
<div class="message-item">
|
||
<div class="msg-avatar" style="background:#07C160">王</div>
|
||
<div class="msg-content">
|
||
<div class="msg-name">王五(被邀请人)</div>
|
||
<div class="msg-bubble">交换机端口有异常告警,正在排查中</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 底部输入区 -->
|
||
<div class="input-area">
|
||
<div class="input-toolbar">
|
||
<button>快速回复</button>
|
||
<button>转接</button>
|
||
<button>邀请</button>
|
||
<button>摇人</button>
|
||
</div>
|
||
<textarea class="input-textbox" placeholder="输入回复内容..."></textarea>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// 角色配置
|
||
const ROLE_COLORS = {
|
||
primary_agent: '#3b82f6',
|
||
collaborator: '#07C160',
|
||
owner: '#FF9800',
|
||
invitee: 'transparent'
|
||
};
|
||
const ROLE_LABELS = {
|
||
primary_agent: '主责',
|
||
collaborator: '协作',
|
||
owner: '发起人',
|
||
invitee: '被邀请'
|
||
};
|
||
const ROLE_BG = {
|
||
primary_agent: '#3b82f6',
|
||
collaborator: '#07C160',
|
||
owner: '#FF9800',
|
||
invitee: '#bbb'
|
||
};
|
||
|
||
const MAX_AVATARS = 6; // 坐席端超员阈值
|
||
const CURRENT_AGENT_ID = '1'; // 当前坐席 = 张明(主责)
|
||
|
||
// 场景数据
|
||
const scenes = [
|
||
// 3人场景
|
||
[
|
||
{ id: '1', name: '张明', avatar: '', dept: 'IT支持组', role: 'primary_agent', joined: true, isSelf: true, joinedAt: '2026-07-10T10:00:00' },
|
||
{ id: '2', name: '李四', avatar: '', dept: '财务部', role: 'owner', joined: true, isSelf: false, joinedAt: '2026-07-10T10:01:00' },
|
||
{ id: '3', name: '王五', avatar: '', dept: '网络运维组', role: 'invitee', joined: true, isSelf: false, joinedAt: '2026-07-10T10:05:00' }
|
||
],
|
||
// 7人场景
|
||
[
|
||
{ id: '1', name: '张明', avatar: '', dept: 'IT支持组', role: 'primary_agent', joined: true, isSelf: true, joinedAt: '2026-07-10T10:00:00' },
|
||
{ id: '2', name: '李四', avatar: '', dept: '财务部', role: 'owner', joined: true, isSelf: false, joinedAt: '2026-07-10T10:01:00' },
|
||
{ id: '10', name: '陈十', avatar: '', dept: 'IT支持组', role: 'collaborator', joined: true, isSelf: false, joinedAt: '2026-07-10T10:02:00' },
|
||
{ id: '3', name: '王五', avatar: '', dept: '网络运维组', role: 'invitee', joined: true, isSelf: false, joinedAt: '2026-07-10T10:05:00' },
|
||
{ id: '4', name: '赵六', avatar: '', dept: '系统组', role: 'invitee', joined: true, isSelf: false, joinedAt: '2026-07-10T10:06:00' },
|
||
{ id: '5', name: '钱七', avatar: '', dept: '安全组', role: 'invitee', joined: false, isSelf: false, joinedAt: null },
|
||
{ id: '6', name: '孙八', avatar: '', dept: '桌面组', role: 'invitee', joined: true, isSelf: false, joinedAt: '2026-07-10T10:08:00' }
|
||
],
|
||
// 10人场景(超员)
|
||
[
|
||
{ id: '1', name: '张明', avatar: '', dept: 'IT支持组', role: 'primary_agent', joined: true, isSelf: true, joinedAt: '2026-07-10T10:00:00' },
|
||
{ id: '2', name: '李四', avatar: '', dept: '财务部', role: 'owner', joined: true, isSelf: false, joinedAt: '2026-07-10T10:01:00' },
|
||
{ id: '10', name: '陈十', avatar: '', dept: 'IT支持组', role: 'collaborator', joined: true, isSelf: false, joinedAt: '2026-07-10T10:02:00' },
|
||
{ id: '11', name: '郑十一', avatar: '', dept: '运维组', role: 'collaborator', joined: true, isSelf: false, joinedAt: '2026-07-10T10:03:00' },
|
||
{ id: '3', name: '王五', avatar: '', dept: '网络运维组', role: 'invitee', joined: true, isSelf: false, joinedAt: '2026-07-10T10:05:00' },
|
||
{ id: '4', name: '赵六', avatar: '', dept: '系统组', role: 'invitee', joined: true, isSelf: false, joinedAt: '2026-07-10T10:06:00' },
|
||
{ id: '5', name: '钱七', avatar: '', dept: '安全组', role: 'invitee', joined: false, isSelf: false, joinedAt: null },
|
||
{ id: '6', name: '孙八', avatar: '', dept: '桌面组', role: 'invitee', joined: true, isSelf: false, joinedAt: '2026-07-10T10:08:00' },
|
||
{ id: '7', name: '周九', avatar: '', dept: '数据库组', role: 'invitee', joined: true, isSelf: false, joinedAt: '2026-07-10T10:09:00' },
|
||
{ id: '8', name: '吴十', avatar: '', dept: '中间件组', role: 'invitee', joined: false, isSelf: false, joinedAt: null }
|
||
]
|
||
];
|
||
|
||
let currentScene = 0;
|
||
let participants = [];
|
||
let isExpanded = false;
|
||
// P1: expandedStateMap 按会话ID记忆展开状态
|
||
const expandedStateMap = {};
|
||
|
||
function getLastChar(name) {
|
||
return name ? name.charAt(name.length - 1) : '?';
|
||
}
|
||
|
||
function sortParticipants(list) {
|
||
// 排列:主责 → 发起人 → 协作坐席 → 被邀请人(按joinedAt, 已加入在前)
|
||
return [...list].sort((a, b) => {
|
||
const order = { primary_agent: 0, owner: 1, collaborator: 2, invitee: 3 };
|
||
if (order[a.role] !== order[b.role]) return order[a.role] - order[b.role];
|
||
// 已加入在前
|
||
if (a.joined !== b.joined) return a.joined ? -1 : 1;
|
||
if (a.joinedAt && b.joinedAt) return new Date(a.joinedAt) - new Date(b.joinedAt);
|
||
return 0;
|
||
});
|
||
}
|
||
|
||
function renderBar() {
|
||
const container = document.getElementById('barAvatars');
|
||
const text = document.getElementById('barText');
|
||
const status = document.getElementById('convStatus');
|
||
container.innerHTML = '';
|
||
|
||
const sorted = sortParticipants(participants);
|
||
const showCount = Math.min(sorted.length, MAX_AVATARS);
|
||
const hasPending = sorted.some(p => !p.joined);
|
||
|
||
for (let i = 0; i < showCount; i++) {
|
||
const p = sorted[i];
|
||
const wrap = document.createElement('div');
|
||
wrap.className = 'avatar-wrap';
|
||
|
||
const av = document.createElement('div');
|
||
av.className = `avatar role-${p.role}`;
|
||
if (p.isSelf) av.classList.add('is-self');
|
||
av.style.background = ROLE_BG[p.role];
|
||
av.textContent = getLastChar(p.name);
|
||
|
||
if (!p.joined) {
|
||
const dot = document.createElement('div');
|
||
dot.className = 'pending-dot';
|
||
wrap.appendChild(dot);
|
||
}
|
||
|
||
wrap.appendChild(av);
|
||
container.appendChild(wrap);
|
||
}
|
||
|
||
if (sorted.length > MAX_AVATARS) {
|
||
const more = document.createElement('div');
|
||
more.className = 'avatar-more';
|
||
more.textContent = `+${sorted.length - MAX_AVATARS}`;
|
||
container.appendChild(more);
|
||
}
|
||
|
||
text.textContent = `${participants.length}位参与者${hasPending ? ' · 有待加入' : ''}`;
|
||
status.textContent = `${participants.length}人参与`;
|
||
}
|
||
|
||
function renderPanel() {
|
||
const body = document.getElementById('panelBody');
|
||
body.innerHTML = '';
|
||
|
||
const sorted = sortParticipants(participants);
|
||
const isPrimaryAgent = participants.some(p => p.id === CURRENT_AGENT_ID && p.role === 'primary_agent');
|
||
|
||
sorted.forEach(p => {
|
||
const item = document.createElement('div');
|
||
item.className = 'participant-item';
|
||
|
||
const av = document.createElement('div');
|
||
av.className = `avatar avatar-md role-${p.role}`;
|
||
if (p.isSelf) av.classList.add('is-self');
|
||
av.style.background = ROLE_BG[p.role];
|
||
av.textContent = getLastChar(p.name);
|
||
|
||
const info = document.createElement('div');
|
||
info.className = 'participant-info';
|
||
|
||
const name = document.createElement('span');
|
||
name.className = 'participant-name';
|
||
name.textContent = p.name + (p.isSelf ? '(我)' : '');
|
||
name.title = p.name; // P1: tooltip仅截断时显示
|
||
info.appendChild(name);
|
||
|
||
const tag = document.createElement('span');
|
||
tag.className = `role-tag ${p.role}`;
|
||
tag.textContent = ROLE_LABELS[p.role];
|
||
info.appendChild(tag);
|
||
|
||
const dept = document.createElement('span');
|
||
dept.className = 'participant-dept';
|
||
dept.textContent = p.dept + (p.joined ? '' : ' · 待加入');
|
||
info.appendChild(dept);
|
||
|
||
item.appendChild(av);
|
||
item.appendChild(info);
|
||
|
||
// P1: 移除图标仅主责坐席 + invitee角色显示
|
||
if (isPrimaryAgent && p.role === 'invitee') {
|
||
const remove = document.createElement('div');
|
||
remove.className = 'remove-icon';
|
||
remove.textContent = '×';
|
||
remove.title = '移除参与者';
|
||
remove.onclick = (e) => {
|
||
e.stopPropagation();
|
||
if (confirm(`确认移除 ${p.name}?`)) {
|
||
alert('已移除(模拟)');
|
||
}
|
||
};
|
||
item.appendChild(remove);
|
||
}
|
||
|
||
body.appendChild(item);
|
||
});
|
||
}
|
||
|
||
function toggleExpand() {
|
||
isExpanded = !isExpanded;
|
||
const panel = document.getElementById('panel');
|
||
const icon = document.getElementById('expandIcon');
|
||
|
||
if (isExpanded) {
|
||
panel.classList.add('show');
|
||
icon.classList.add('expanded');
|
||
renderPanel();
|
||
// P1: 记忆展开状态
|
||
expandedStateMap['conv-1'] = true;
|
||
} else {
|
||
panel.classList.remove('show');
|
||
icon.classList.remove('expanded');
|
||
expandedStateMap['conv-1'] = false;
|
||
}
|
||
}
|
||
|
||
function toggleScene() {
|
||
currentScene = (currentScene + 1) % scenes.length;
|
||
participants = JSON.parse(JSON.stringify(scenes[currentScene]));
|
||
document.getElementById('sceneInfo').textContent = `场景: ${participants.length}人`;
|
||
renderBar();
|
||
if (isExpanded) renderPanel();
|
||
}
|
||
|
||
// 初始化
|
||
participants = JSON.parse(JSON.stringify(scenes[0]));
|
||
renderBar();
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|