Files
wecom_it_smart_desk/docs/01-产品文档/04-坐席工作台/原型-REQ-坐席-000-坐席工作台-v1.0.html
T

2448 lines
173 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IT智能服务台 · 坐席工作台 v5.4</title>
<style>
:root {
--bg-primary: #f5f7fa;
--bg-secondary: #ffffff;
--bg-tertiary: #f0f2f5;
--bg-hover: #e8ecf1;
--bg-active: #dce3ec;
--border: #e2e8f0;
--border-light: #cbd5e1;
--text-primary: #1e293b;
--text-secondary: #64748b;
--text-muted: #94a3b8;
--accent: #3b82f6;
--accent-soft: #dbeafe;
--success: #22c55e;
--success-soft: #dcfce7;
--warning: #f59e0b;
--warning-soft: #fef3c7;
--danger: #ef4444;
--danger-soft: #fee2e2;
--info: #60a5fa;
--purple: #8b5cf6;
--purple-soft: #ede9fe;
--orange: #f97316;
--orange-soft: #fff7ed;
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
--shadow: 0 2px 8px rgba(0,0,0,0.08);
--radius: 6px;
--radius-lg: 10px;
--transition: 0.25s cubic-bezier(0.4,0,0.2,1);
/* 快速回复 L1 主类颜色 */
--qr-c0: #3b82f6; /* 电脑 - 蓝 */
--qr-c1: #22c55e; /* 软件 - 绿 */
--qr-c2: #f97316; /* 外设 - 橙 */
--qr-c3: #8b5cf6; /* 网络 - 紫 */
--qr-c4: #ef4444; /* 安全 - 红 */
--qr-c5: #0ea5e9; /* 资产 - 青 */
--qr-c6: #6b7280; /* 其他 - 灰 */
}
[data-theme="dark"] {
--bg-primary: #0f1923;
--bg-secondary: #151f2b;
--bg-tertiary: #1a2736;
--bg-hover: #1e3044;
--bg-active: #243b52;
--border: #1e3044;
--border-light: #2a3f56;
--text-primary: #e8edf2;
--text-secondary: #8ba1b7;
--text-muted: #5c7185;
--accent: #4da6ff;
--accent-soft: #2b5f8a;
--success: #34d399;
--success-soft: #1a3a2a;
--warning: #fbbf24;
--warning-soft: #3a2f10;
--danger: #f87171;
--danger-soft: #3a1a1a;
--info: #60a5fa;
--purple: #a78bfa;
--purple-soft: #2d2060;
--orange: #fb923c;
--orange-soft: #3d1f08;
--shadow-sm: 0 1px 2px rgba(0,0,0,0.2);
--shadow: 0 2px 8px rgba(0,0,0,0.3);
/* 快速回复 L1 主类颜色(深色主题:同色系稍亮版本) */
--qr-c0: #60a5fa;
--qr-c1: #4ade80;
--qr-c2: #fb923c;
--qr-c3: #a78bfa;
--qr-c4: #f87171;
--qr-c5: #22d3ee;
--qr-c6: #9ca3af;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
height: 100vh;
overflow: hidden;
font-size: 13px;
line-height: 1.5;
transition: background 0.3s, color 0.3s;
}
/* ========== TOP BAR ========== */
.theme-toggle-bar {
position: fixed; top: 0; left: 0; right: 0;
height: 40px;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
display: flex; align-items: center;
justify-content: space-between;
padding: 0 16px;
z-index: 100;
box-shadow: var(--shadow-sm);
}
.bar-left { display: flex; align-items: center; gap: 10px; font-size: 13px; font-weight: 600; }
.bar-left .logo-sm {
width: 26px; height: 26px;
background: linear-gradient(135deg, var(--accent), var(--purple));
border-radius: 6px; display: flex; align-items: center; justify-content: center;
font-weight: 700; font-size: 10px; color: #fff;
}
.bar-left .sys-name {
font-size: 13px; font-weight: 700;
background: linear-gradient(135deg, var(--accent), var(--purple));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.bar-left .sys-tagline {
font-size: 10px; color: var(--text-muted);
font-weight: 400; margin-left: 4px;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
max-width: 280px;
}
.bar-right { display: flex; align-items: center; gap: 14px; }
.theme-switch { display: flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; }
.theme-switch .switch-icon { font-size: 15px; }
.theme-switch .switch-track {
width: 40px; height: 22px;
background: var(--border-light); border-radius: 11px;
position: relative; transition: background 0.3s;
}
[data-theme="dark"] .theme-switch .switch-track { background: var(--accent); }
.theme-switch .switch-thumb {
width: 18px; height: 18px; background: #fff;
border-radius: 50%; position: absolute;
top: 2px; left: 2px; transition: transform 0.3s;
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
}
[data-theme="dark"] .theme-switch .switch-thumb { transform: translateX(18px); }
.bar-user-info { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-secondary); }
.bar-user-avatar {
width: 28px; height: 28px; border-radius: 50%;
background: #f3f4f6; color: #374151;
display: flex; align-items: center; justify-content: center;
font-size: 11px; font-weight: 600;
}
/* ========== LAYOUT (3 columns) ========== */
.workspace {
display: flex; height: calc(100vh - 40px); margin-top: 40px;
width: 100%; transition: var(--transition);
}
/* ========== LEFT SIDEBAR ========== */
.sidebar-left {
width: 280px; min-width: 200px;
background: var(--bg-secondary);
border-right: none;
display: flex; flex-direction: column; overflow: hidden;
position: relative;
}
/* Search */
.conv-search { padding: 10px 12px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
/* ===== v5.4: 拖拽调整边框手柄 ===== */
.resize-handle {
width: 6px; cursor: col-resize;
background: var(--border);
position: relative; flex-shrink: 0;
transition: background 0.2s;
z-index: 10;
}
.resize-handle:hover,
.resize-handle.dragging {
background: var(--accent);
}
/* 手柄中间的拖拽指示纹 */
.resize-handle::after {
content: '⋮';
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
font-size: 12px;
color: var(--text-muted);
opacity: 0;
transition: opacity 0.2s;
}
.resize-handle:hover::after,
.resize-handle.dragging::after {
opacity: 1;
}
.conv-search-input-wrap { position: relative; }
.conv-search-input {
width: 100%; background: var(--bg-tertiary);
border: 1px solid var(--border); border-radius: var(--radius);
padding: 7px 10px 7px 30px;
color: var(--text-primary); font-size: 12px; font-family: inherit;
outline: none; transition: border-color var(--transition);
}
.conv-search-input:focus { border-color: var(--accent); }
.conv-search-input::placeholder { color: var(--text-muted); }
.conv-search-icon {
position: absolute; left: 10px; top: 50%;
transform: translateY(-50%); font-size: 12px;
color: var(--text-muted); pointer-events: none;
}
.conv-search-tabs { display: flex; gap: 2px; margin-top: 6px; }
.conv-search-tab {
font-size: 11px; padding: 3px 10px; border-radius: 10px;
cursor: pointer; transition: var(--transition);
color: var(--text-muted); background: transparent;
}
.conv-search-tab:hover { color: var(--text-secondary); background: var(--bg-hover); }
.conv-search-tab.active { color: var(--accent); background: var(--accent-soft); font-weight: 500; }
/* Sections */
.conv-sections { flex: 1; overflow-y: auto; padding: 0; }
.conv-sections::-webkit-scrollbar { width: 4px; }
.conv-sections::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
.conv-section-header {
padding: 7px 12px; font-size: 11px; font-weight: 600;
color: var(--text-muted); text-transform: uppercase;
letter-spacing: 0.5px; display: flex; align-items: center;
justify-content: space-between; user-select: none;
background: var(--bg-tertiary);
border-top: 1px solid var(--border);
border-bottom: 1px solid var(--border);
transition: var(--transition);
}
.conv-section-header .cs-count {
font-weight: 400; color: var(--text-muted);
background: var(--bg-secondary); padding: 1px 7px;
border-radius: 8px; font-size: 10px;
}
.conv-section-body { overflow: hidden; }
/* Priority icons on conv item (NO IT level badge) */
/* conv-item 样式已移至 v5.4 头像区块中 */
.conv-item .conv-top {
display: flex; align-items: center; justify-content: space-between;
margin-bottom: 2px;
}
.conv-item .conv-name-row { display: flex; align-items: center; gap: 5px; }
.conv-item .conv-name { font-size: 12px; font-weight: 500; color: var(--text-primary); }
.priority-icons {
display: flex; align-items: center; gap: 3px;
flex-shrink: 0;
}
.pi-icon {
width: 16px; height: 16px; border-radius: 3px;
display: flex; align-items: center; justify-content: center;
font-size: 8px; flex-shrink: 0;
}
.pi-blocked { background: var(--danger-soft); color: var(--danger); }
.pi-block { background: var(--danger-soft); color: var(--danger); }
.pi-impact { background: var(--warning-soft); color: var(--warning); }
.pi-impact.high { background: var(--danger-soft); color: var(--danger); }
.pi-role { background: var(--purple-soft); color: var(--purple); }
.pi-repeat { background: var(--orange-soft); color: var(--orange); }
.pi-confused { background: #e0e7ff; color: #6366f1; }
.pi-angry { background: #fee2e2; color: #ef4444; }
/* 会话标签 */
.conv-tag-warn, .conv-tag-new, .conv-tag-vip, .conv-tag-urgent {
font-size: 9px; padding: 1px 5px; border-radius: 4px; flex-shrink: 0; font-weight: 500;
}
.conv-tag-warn { background: #fef3c7; color: #d97706; }
.conv-tag-new { background: #dbeafe; color: #2563eb; }
.conv-tag-vip { background: #fce7f3; color: #db2777; }
.conv-tag-urgent{ background: #fee2e2; color: #ef4444; }
.conv-pin-badge { font-size: 9px; color: #ef4444; flex-shrink: 0; }
.conv-item .conv-time { font-size: 10px; color: var(--text-muted); flex-shrink: 0; }
.conv-item .conv-preview {
font-size: 11px; color: var(--text-secondary);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.conv-item .conv-waiting {
font-size: 10px;
background: var(--danger); color: #fff;
padding: 1px 6px; border-radius: 8px;
flex-shrink: 0;
}
.conv-item .conv-agent-tag {
font-size: 10px; color: var(--text-muted);
background: var(--bg-tertiary); padding: 1px 6px;
border-radius: 8px;
}
/* ===== v5.4: 会话头像 + 新消息圆点 + 处理对象缩略头像 ===== */
.conv-item {
display: flex; align-items: center; gap: 8px;
padding: 8px 10px; margin: 1px 6px;
border-radius: var(--radius); cursor: pointer;
transition: var(--transition);
border: 1px solid transparent; position: relative;
}
.conv-item:hover { background: var(--bg-hover); }
.conv-item.active { background: var(--accent-soft); border-color: var(--accent); }
/* 头像容器(含圆点) */
.conv-avatar-wrap {
position: relative; flex-shrink: 0;
}
/* 头像圆形 — 浅色主题:浅底+深色字 */
.conv-avatar {
width: 34px; height: 34px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 13px; font-weight: 600;
color: #1e293b;
flex-shrink: 0;
}
/* 浅色主题:各头像色系用浅底色 */
.conv-avatar.av-blue { background: #dbeafe; color: #1e40af; }
.conv-avatar.av-green { background: #dcfce7; color: #166534; }
.conv-avatar.av-orange { background: #fff7ed; color: #9a3412; }
.conv-avatar.av-purple { background: #ede9fe; color: #5b21b6; }
.conv-avatar.av-red { background: #fee2e2; color: #991b1b; }
.conv-avatar.av-teal { background: #f0fdfa; color: #115e59; }
.conv-avatar.av-pink { background: #fce7f3; color: #9d174d; }
/* 新消息圆点(在头像右上角) */
.new-msg-dot {
position: absolute; top: -1px; right: -1px;
width: 10px; height: 10px; border-radius: 50%;
border: 2px solid var(--bg-secondary);
z-index: 2;
}
.new-msg-dot.dot-urgent { background: var(--danger); } /* 紧急:红色 */
.new-msg-dot.dot-normal { background: var(--accent); } /* 普通:蓝色 */
.new-msg-dot.dot-muted { background: var(--text-muted); }/* 低优:灰色 */
/* 会话内容区(姓名+预览+标签,夹在头像和右侧缩略头像之间) */
.conv-content {
flex: 1; min-width: 0; /* 允许文字截断 */
}
/* 处理对象缩略头像(右侧小头像)— 浅色主题:浅底+深色字 */
.conv-target-avatar {
width: 22px; height: 22px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 9px; font-weight: 600;
flex-shrink: 0; margin-left: auto;
color: #1e293b;
}
.conv-target-avatar.ta-blue { background: #dbeafe; color: #1e40af; }
.conv-target-avatar.ta-green { background: #dcfce7; color: #166534; }
.conv-target-avatar.ta-orange { background: #fff7ed; color: #9a3412; }
.conv-target-avatar.ta-purple { background: #ede9fe; color: #5b21b6; }
.conv-target-avatar.ta-red { background: #fee2e2; color: #991b1b; }
.conv-target-avatar.ta-teal { background: #f0fdfa; color: #115e59; }
.conv-target-avatar.ta-pink { background: #fce7f3; color: #9d174d; }
/* 待办事项缩略头像 — 浅色主题:浅底+深色字 */
.kanban-item .ki-avatar {
width: 20px; height: 20px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
font-size: 9px; font-weight: 600;
flex-shrink: 0; margin-left: auto;
color: #1e293b;
}
.ki-avatar.ka-blue { background: #dbeafe; color: #1e40af; }
.ki-avatar.ka-green { background: #dcfce7; color: #166534; }
.ki-avatar.ka-orange { background: #fff7ed; color: #9a3412; }
.ki-avatar.ka-purple { background: #ede9fe; color: #5b21b6; }
.ki-avatar.ka-red { background: #fee2e2; color: #991b1b; }
/* 深色主题:头像恢复渐变亮色 */
[data-theme="dark"] .conv-avatar {
color: #fff;
}
[data-theme="dark"] .conv-avatar.av-blue { background: linear-gradient(135deg, #3b82f6, #6366f1); color: #fff; }
[data-theme="dark"] .conv-avatar.av-green { background: linear-gradient(135deg, #22c55e, #14b8a6); color: #fff; }
[data-theme="dark"] .conv-avatar.av-orange { background: linear-gradient(135deg, #f97316, #eab308); color: #fff; }
[data-theme="dark"] .conv-avatar.av-purple { background: linear-gradient(135deg, #8b5cf6, #ec4899); color: #fff; }
[data-theme="dark"] .conv-avatar.av-red { background: linear-gradient(135deg, #ef4444, #f97316); color: #fff; }
[data-theme="dark"] .conv-avatar.av-teal { background: linear-gradient(135deg, #0ea5e9, #6366f1); color: #fff; }
[data-theme="dark"] .conv-avatar.av-pink { background: linear-gradient(135deg, #ec4899, #f43f5e); color: #fff; }
[data-theme="dark"] .conv-target-avatar.ta-blue { background: linear-gradient(135deg, #3b82f6, #6366f1); color: #fff; }
[data-theme="dark"] .conv-target-avatar.ta-green { background: linear-gradient(135deg, #22c55e, #14b8a6); color: #fff; }
[data-theme="dark"] .conv-target-avatar.ta-orange { background: linear-gradient(135deg, #f97316, #eab308); color: #fff; }
[data-theme="dark"] .conv-target-avatar.ta-purple { background: linear-gradient(135deg, #8b5cf6, #ec4899); color: #fff; }
[data-theme="dark"] .conv-target-avatar.ta-red { background: linear-gradient(135deg, #ef4444, #f97316); color: #fff; }
[data-theme="dark"] .conv-target-avatar.ta-teal { background: linear-gradient(135deg, #0ea5e9, #6366f1); color: #fff; }
[data-theme="dark"] .conv-target-avatar.ta-pink { background: linear-gradient(135deg, #ec4899, #f43f5e); color: #fff; }
[data-theme="dark"] .bar-user-avatar {
background: linear-gradient(135deg, #6366f1, #8b5cf6);
color: #fff;
}
[data-theme="dark"] .user-avatar {
background: linear-gradient(135deg, #6366f1, #8b5cf6);
color: #fff;
}
[data-theme="dark"] .ki-avatar.ka-blue { background: linear-gradient(135deg, #3b82f6, #6366f1); color: #fff; }
[data-theme="dark"] .ki-avatar.ka-green { background: linear-gradient(135deg, #22c55e, #14b8a6); color: #fff; }
[data-theme="dark"] .ki-avatar.ka-orange { background: linear-gradient(135deg, #f97316, #eab308); color: #fff; }
[data-theme="dark"] .ki-avatar.ka-purple { background: linear-gradient(135deg, #8b5cf6, #ec4899); color: #fff; }
[data-theme="dark"] .ki-avatar.ka-red { background: linear-gradient(135deg, #ef4444, #f97316); color: #fff; }
/* ===== 待办事项 ===== */
.kanban { flex-shrink: 0; border-top: 1px solid var(--border); padding: 8px 12px; max-height: 220px; overflow-y: auto; }
.kanban::-webkit-scrollbar { width: 4px; }
.kanban::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
.kanban-title { font-size: 11px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px; display: flex; align-items: center; justify-content: space-between; }
.kanban-title .count { font-size: 10px; color: var(--danger); }
.kanban-item { display: flex; align-items: center; gap: 8px; padding: 7px 8px; border-radius: var(--radius); font-size: 11px; margin-bottom: 3px; cursor: pointer; transition: var(--transition); }
.kanban-item:hover { background: var(--bg-hover); }
.kanban-item.active { background: var(--accent-soft); border: 1px solid var(--accent); }
.kanban-item .ki-priority { width: 4px; height: 4px; border-radius: 50%; flex-shrink: 0; }
.ki-priority.urgent { background: var(--danger); }
.ki-priority.high { background: var(--warning); }
.ki-priority.normal { background: var(--accent); }
.ki-priority.done { background: var(--success); }
.kanban-item.resolved { opacity: 0.6; }
.kanban-item .ki-text { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text-secondary); }
.kanban-item .ki-type {
font-size: 9px; padding: 1px 5px; border-radius: 4px;
flex-shrink: 0; font-weight: 500;
}
.ki-type.ticket { background: var(--accent-soft); color: var(--accent); border: 1px solid var(--accent); }
.ki-type.approval { background: var(--purple-soft); color: var(--purple); border: 1px solid var(--purple); }
.ki-type.device { background: var(--orange-soft); color: var(--orange); border: 1px solid var(--orange); }
.kanban-item .ki-time { font-size: 10px; color: var(--text-muted); flex-shrink: 0; }
.kanban-stats { display: flex; gap: 8px; margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--border); font-size: 10px; color: var(--text-muted); }
.kanban-stats span { display: flex; align-items: center; gap: 4px; }
.kanban-stats .dot { width: 6px; height: 6px; border-radius: 50%; }
.dot-online { background: var(--success); }
.dot-busy { background: var(--warning); }
.dot-offline { background: var(--text-muted); }
/* ========== CENTER COLUMN ========== */
.center-column {
flex: 1; min-width: 340px;
display: flex; flex-direction: column;
background: var(--bg-primary); overflow: hidden;
transition: var(--transition);
position: relative;
}
/* --- Chat View (default) --- */
.chat-view { display: flex; flex-direction: column; height: 100%; overflow: hidden; }
.chat-view.hidden { display: none; }
/* IT Level Badge */
.it-level-badge {
display: inline-flex; align-items: center; gap: 3px;
padding: 2px 7px; border-radius: 10px;
font-size: 10px; font-weight: 600;
flex-shrink: 0;
position: relative; cursor: help;
}
.it-level-badge.bronze { background: linear-gradient(135deg, #78350f, #92400e); color: #fef3c7; border: 1px solid #b45309; }
.it-level-badge.silver { background: linear-gradient(135deg, #374151, #6b7280); color: #f3f4f6; border: 1px solid #9ca3af; }
.it-level-badge.gold { background: linear-gradient(135deg, #92400e, #d97706); color: #fffbeb; border: 1px solid #f59e0b; }
.it-level-badge.platinum { background: linear-gradient(135deg, #164e63, #0e7490); color: #cffafe; border: 1px solid #22d3ee; }
.it-level-badge.diamond { background: linear-gradient(135deg, #312e81, #4338ca); color: #e0e7ff; border: 1px solid #818cf8; }
.it-level-badge.star { background: linear-gradient(135deg, #831843, #be185d); color: #fce7f3; border: 1px solid #ec4899; }
.it-level-badge.king { background: linear-gradient(135deg, #7c2d12, #c2410c); color: #ffedd5; border: 1px solid #f97316;
animation: king-glow 2s ease-in-out infinite alternate;
}
@keyframes king-glow {
from { box-shadow: 0 0 4px rgba(249,115,22,0.3); }
to { box-shadow: 0 0 10px rgba(249,115,22,0.6); }
}
.it-level-badge .level-icon { font-size: 11px; }
.it-level-badge .level-num { font-size: 9px; opacity: 0.8; }
/* User info bar */
.user-info-bar {
padding: 10px 16px; background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
display: flex; align-items: center; gap: 10px;
flex-shrink: 0; flex-wrap: wrap; cursor: pointer;
transition: var(--transition); position: relative;
box-shadow: var(--shadow-sm);
}
.user-info-bar:hover { background: var(--bg-hover); }
.user-avatar {
width: 36px; height: 36px; border-radius: 50%;
background: #f3f4f6; color: #374151;
display: flex; align-items: center; justify-content: center;
font-size: 14px; font-weight: 600; flex-shrink: 0;
}
.user-name-section { display: flex; flex-direction: column; gap: 1px; }
.user-name { font-weight: 600; font-size: 14px; display: flex; align-items: center; gap: 6px; }
.user-dept { font-size: 11px; color: var(--text-secondary); }
.info-chips { display: flex; gap: 5px; flex-wrap: wrap; }
.info-chip { font-size: 11px; padding: 2px 8px; border-radius: 10px; display: flex; align-items: center; gap: 4px; }
.info-chip.emotion { background: var(--warning-soft); color: var(--warning); border: 1px solid var(--warning); }
.info-chip.default { background: var(--bg-tertiary); color: var(--text-secondary); border: 1px solid var(--border); }
.info-chip.repeat { background: var(--danger-soft); color: var(--danger); border: 1px solid var(--danger); }
.info-chip.remark { background: var(--purple-soft); color: var(--purple); border: 1px solid var(--purple); }
/* Device status icons row */
.device-status-row {
display: flex; align-items: center; gap: 8px;
margin-left: 6px; padding-left: 8px;
border-left: 1px solid var(--border);
}
.dev-icon {
display: flex; align-items: center; justify-content: center;
width: 26px; height: 26px; border-radius: 6px;
font-size: 14px; cursor: default; position: relative;
transition: all 0.2s;
background: var(--bg-tertiary); border: 1px solid var(--border);
}
.dev-icon:hover { transform: scale(1.15); }
.dev-icon .dev-tooltip {
display: none; position: absolute; bottom: calc(100% + 6px); left: 50%;
transform: translateX(-50%); white-space: nowrap;
background: var(--bg-primary); color: var(--text-primary);
border: 1px solid var(--border); border-radius: 6px;
padding: 4px 8px; font-size: 10px; z-index: 99;
box-shadow: var(--shadow-sm);
}
.dev-icon:hover .dev-tooltip { display: block; }
.dev-icon .dev-tooltip::after {
content: ''; position: absolute; top: 100%; left: 50%;
transform: translateX(-50%); border: 4px solid transparent;
border-top-color: var(--border);
}
/* Device icon status variants */
.dev-icon.online { color: #22c55e; border-color: rgba(34,197,94,0.3); background: rgba(34,197,94,0.08); }
.dev-icon.offline { color: var(--text-muted); opacity: 0.5; }
.dev-icon.safe { color: #22c55e; border-color: rgba(34,197,94,0.3); background: rgba(34,197,94,0.08); }
.dev-icon.risk { color: var(--danger); border-color: rgba(239,68,68,0.3); background: rgba(239,68,68,0.08); animation: pulse-risk 2s infinite; }
.dev-icon.warn { color: var(--warning); border-color: rgba(234,179,8,0.3); background: rgba(234,179,8,0.08); }
.dev-icon.certified { color: var(--accent); border-color: rgba(59,130,246,0.3); background: rgba(59,130,246,0.08); }
@keyframes pulse-risk {
0%, 100% { box-shadow: 0 0 0 0 rgba(239,68,68,0.3); }
50% { box-shadow: 0 0 0 3px rgba(239,68,68,0.1); }
}
/* Device badge (small superscript indicator) */
.dev-icon .dev-badge {
position: absolute; top: -3px; right: -3px;
width: 8px; height: 8px; border-radius: 50%;
border: 1.5px solid var(--bg-secondary);
}
.dev-icon .dev-badge.green { background: #22c55e; }
.dev-icon .dev-badge.red { background: #ef4444; }
.dev-icon .dev-badge.yellow { background: #eab308; }
.dev-icon .dev-badge.gray { background: var(--text-muted); }
[data-theme="dark"] .dev-icon.online { background: rgba(34,197,94,0.15); }
[data-theme="dark"] .dev-icon.safe { background: rgba(34,197,94,0.15); }
[data-theme="dark"] .dev-icon.risk { background: rgba(239,68,68,0.15); }
[data-theme="dark"] .dev-icon.warn { background: rgba(234,179,8,0.15); }
[data-theme="dark"] .dev-icon.certified { background: rgba(59,130,246,0.15); }
.user-info-bar .expand-icon { margin-left: auto; color: var(--text-muted); font-size: 11px; transition: transform 0.3s; }
.user-info-bar.expanded .expand-icon { transform: rotate(90deg); }
/* User detail panel */
.user-detail-panel {
max-height: 0; overflow: hidden;
transition: max-height 0.35s ease, padding 0.35s ease;
background: var(--bg-secondary);
border-bottom: 0px solid var(--border);
padding: 0 16px; display: grid;
grid-template-columns: 1fr 1fr 1fr; gap: 8px; font-size: 12px;
}
.user-info-bar.expanded + .user-detail-panel {
max-height: 320px; padding: 12px 16px; border-bottom-width: 1px;
}
.detail-card { background: var(--bg-tertiary); border-radius: var(--radius); padding: 8px 10px; border: 1px solid var(--border); }
.detail-card .dc-label { font-size: 10px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 3px; }
.detail-card .dc-value { font-size: 12px; font-weight: 500; color: var(--text-primary); }
.detail-card .dc-value.warn { color: var(--warning); }
/* Chat messages */
.chat-messages {
flex: 1; overflow-y: auto; padding: 16px 20px;
display: flex; flex-direction: column; gap: 10px;
min-height: 0; /* allow shrink */
}
.chat-messages::-webkit-scrollbar { width: 4px; }
.chat-messages::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
.msg-bubble {
max-width: 72%; padding: 10px 14px; border-radius: var(--radius-lg);
font-size: 13px; line-height: 1.6; word-break: break-word;
box-shadow: var(--shadow-sm);
}
.msg-user {
align-self: flex-start; background: var(--bg-secondary);
border: 1px solid var(--border); color: var(--text-primary);
border-bottom-left-radius: 2px;
}
.msg-agent {
align-self: flex-end; background: var(--accent); color: #fff;
border-bottom-right-radius: 2px;
}
[data-theme="dark"] .msg-agent { background: #1e5a9e; }
.msg-system {
align-self: center; font-size: 11px; color: var(--text-muted);
padding: 3px 12px; background: var(--bg-tertiary); border-radius: 10px;
}
/* AI recommend inline */
.ai-recommend-inline {
align-self: stretch; background: var(--accent-soft);
border: 1px dashed var(--accent); border-radius: var(--radius-lg);
padding: 10px 14px; margin: 4px 0;
}
[data-theme="dark"] .ai-recommend-inline { background: rgba(77,166,255,0.06); border-color: rgba(77,166,255,0.3); }
.ai-recommend-inline .ari-label {
font-size: 10px; color: var(--accent); text-transform: uppercase;
letter-spacing: 1px; margin-bottom: 6px; display: flex; align-items: center; gap: 6px;
}
.ari-options { display: flex; gap: 8px; }
.ari-opt {
flex: 1; padding: 8px 12px; background: var(--bg-secondary);
border: 1px solid var(--border-light); border-radius: var(--radius);
font-size: 12px; color: var(--text-primary); cursor: pointer;
transition: var(--transition); text-align: left; line-height: 1.5;
}
.ari-opt:hover { border-color: var(--accent); background: var(--bg-hover); }
.ari-opt .ari-shortcut { font-size: 10px; color: var(--text-muted); float: right; margin-left: 8px; background: var(--bg-tertiary); padding: 1px 5px; border-radius: 3px; }
/* Chat input — v5.4: 上边框可拖拽调整高度 */
.chat-input-area {
padding: 0 12px 8px 12px;
background: var(--bg-primary);
border-top: none;
flex-shrink: 0;
position: relative;
min-height: 50px;
display: flex; flex-direction: column;
}
/* 输入框上方的拖拽手柄 */
.chat-input-area .input-resize-handle {
position: absolute; top: 0; left: 0; right: 0;
height: 4px; cursor: row-resize;
background: var(--border);
transition: background 0.2s;
z-index: 5;
flex-shrink: 0;
}
.chat-input-area .input-resize-handle:hover,
.chat-input-area .input-resize-handle.dragging {
background: var(--accent);
}
.chat-input-area .input-resize-handle::after {
content: '···';
position: absolute; top: 50%; left: 50%;
transform: translate(-50%, -50%);
font-size: 10px; letter-spacing: 2px;
color: var(--text-muted);
opacity: 0; transition: opacity 0.2s;
}
.chat-input-area .input-resize-handle:hover::after,
.chat-input-area .input-resize-handle.dragging::after {
opacity: 1;
}
/* 快捷工具栏 — 整合圆角卡片风格 */
.chat-toolbar {
display: flex; align-items: center; gap: 1px;
padding: 8px 6px 4px 6px;
flex-shrink: 0;
}
.chat-toolbar .tb-btn {
display: flex; align-items: center; justify-content: center;
width: 32px; height: 28px;
border: none; background: transparent;
border-radius: var(--radius); cursor: pointer;
font-size: 14px; color: var(--text-muted);
transition: all var(--transition);
position: relative;
}
.chat-toolbar .tb-btn:hover {
background: var(--accent-soft);
color: var(--accent);
}
.chat-toolbar .tb-btn:active {
transform: scale(0.92);
}
.chat-toolbar .tb-btn .tb-tip {
display: none; position: absolute; bottom: 100%; left: 50%;
transform: translateX(-50%); margin-bottom: 6px;
background: var(--text-primary); color: var(--bg-secondary);
font-size: 10px; padding: 3px 8px; border-radius: 4px;
white-space: nowrap; pointer-events: none;
box-shadow: var(--shadow);
}
.chat-toolbar .tb-btn .tb-tip::after {
content: '';
position: absolute; top: 100%; left: 50%;
transform: translateX(-50%);
border: 4px solid transparent;
border-top-color: var(--text-primary);
}
.chat-toolbar .tb-btn:hover .tb-tip { display: block; }
.chat-toolbar .tb-sep {
width: 1px; height: 14px; background: var(--border);
margin: 0 6px; flex-shrink: 0;
}
/* 输入行 — 整合为圆角卡片 */
.chat-input-row {
display: flex; gap: 8px; align-items: stretch; flex: 1; min-height: 0;
}
/* 输入框容器(含textarea + 发送按钮,统一圆角卡片) */
.chat-input-card {
flex: 1; display: flex; align-items: flex-end;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
transition: border-color var(--transition), box-shadow var(--transition);
box-shadow: var(--shadow-sm);
}
.chat-input-card:focus-within {
border-color: var(--accent);
box-shadow: 0 0 0 2px var(--accent-soft);
}
.chat-input {
flex: 1; background: transparent; border: none;
padding: 10px 0 10px 14px;
color: var(--text-primary); font-size: 13px; font-family: inherit;
resize: none; min-height: 60px; max-height: 300px; outline: none;
line-height: 1.5;
overflow-y: auto;
}
.chat-input:focus { outline: none; }
.chat-input::placeholder { color: var(--text-muted); }
/* 发送按钮 — 内嵌于卡片右侧 */
.btn-send {
padding: 8px 18px; margin: 4px 6px 4px 0;
background: var(--accent);
color: #fff; border: none; border-radius: var(--radius);
font-size: 12px; font-weight: 600; cursor: pointer;
transition: all var(--transition); flex-shrink: 0;
letter-spacing: 0.5px;
}
.btn-send:hover {
opacity: 0.9;
box-shadow: 0 2px 8px rgba(59,130,246,0.3);
}
.btn-send:active {
transform: scale(0.95);
}
[data-theme="dark"] .btn-send {
background: linear-gradient(135deg, var(--accent), var(--purple));
}
/* ========== TROUBLESHOOT BAR (always visible, flowchart collapsible) ========== */
.troubleshoot-bar {
flex-shrink: 0;
border-top: 1px solid var(--border);
background: var(--bg-secondary);
display: flex; flex-direction: column;
transition: var(--transition);
max-height: 400px;
overflow: hidden;
}
/* Bar is ALWAYS expanded — no .collapsed class used for the bar itself */
.ts-header {
display: flex; align-items: center; gap: 0;
padding: 6px 12px; flex-shrink: 0;
min-height: 36px;
background: var(--bg-tertiary);
border-bottom: 1px solid var(--border);
cursor: default;
}
.ts-header:hover { background: var(--bg-hover); }
.ts-title {
font-size: 13px; font-weight: 600;
display: flex; align-items: center; gap: 6px;
flex-shrink: 0; margin-right: 10px;
}
/* Inline path steps inside header */
.ts-path-inline {
display: flex; align-items: center; gap: 0;
flex: 1; overflow: hidden;
}
.path-step {
padding: 2px 7px;
background: var(--bg-tertiary);
border: 1px solid var(--border-light);
border-radius: 3px;
font-size: 11px; color: var(--text-secondary);
white-space: nowrap; flex-shrink: 0;
cursor: pointer; transition: var(--transition);
}
.path-step:hover { border-color: var(--accent); color: var(--accent); }
.path-step.done { background: var(--success-soft); border-color: var(--success); color: var(--success); }
.path-step.current { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); font-weight: 600; }
.path-arrow-inline { display: flex; align-items: center; color: var(--text-muted); font-size: 10px; padding: 0 3px; flex-shrink: 0; }
/* Flowchart toggle — triangle icon only */
.ts-flowchart-toggle {
font-size: 12px; color: var(--accent);
cursor: pointer; padding: 4px 6px;
border-radius: 3px; transition: var(--transition);
user-select: none; flex-shrink: 0; line-height: 1;
}
.ts-flowchart-toggle:hover { background: var(--accent-soft); }
/* Full flowchart (collapsed by default — toggled via .show-flow on bar) */
.ts-flowchart {
padding: 10px 16px 14px;
overflow-x: auto;
overflow-y: auto;
max-height: 0;
transition: max-height 0.35s ease, padding 0.35s ease;
padding-top: 0;
padding-bottom: 0;
}
.troubleshoot-bar.show-flow .ts-flowchart {
max-height: 300px;
padding: 10px 16px 14px;
}
.flow-step {
display: flex; align-items: flex-start; gap: 10px;
margin-bottom: 4px; position: relative;
}
.flow-step:last-child { margin-bottom: 0; }
.flow-step-connector {
position: absolute; left: 10px; top: 26px; bottom: -12px;
width: 2px; background: var(--border);
}
.flow-step:last-child .flow-step-connector { display: none; }
.flow-node {
width: 22px; height: 22px; border-radius: 50%;
background: var(--accent-soft); color: var(--accent);
display: flex; align-items: center; justify-content: center;
font-size: 10px; font-weight: 600; flex-shrink: 0;
z-index: 1; border: 1px solid var(--accent);
}
.flow-node.done { background: var(--success-soft); color: var(--success); border-color: var(--success); }
.flow-node.judge {
width: auto; height: auto; border-radius: var(--radius);
padding: 3px 8px; font-size: 10px; font-weight: 500;
background: var(--warning-soft); color: var(--warning); border-color: var(--warning);
}
.flow-content { flex: 1; }
.flow-title { font-size: 12px; font-weight: 500; color: var(--text-primary); margin-bottom: 2px; }
.flow-desc { font-size: 11px; color: var(--text-secondary); line-height: 1.4; }
.flow-branch {
margin-left: 20px; margin-top: 4px;
padding-left: 10px;
border-left: 2px dashed var(--border-light);
}
.flow-branch-item {
font-size: 11px; color: var(--text-muted);
padding: 2px 0;
cursor: pointer;
transition: var(--transition);
}
.flow-branch-item:hover { color: var(--accent); }
.flow-branch-item.active { color: var(--success); font-weight: 500; }
/* ========== TASK DETAIL VIEW ========== */
.task-detail-view {
display: none;
flex-direction: column;
height: 100%;
overflow: hidden;
}
.task-detail-view.active { display: flex; }
.task-detail-header {
padding: 10px 16px; background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
display: flex; align-items: center; gap: 10px;
flex-shrink: 0;
box-shadow: var(--shadow-sm);
}
.btn-back {
padding: 4px 10px; background: var(--bg-tertiary);
border: 1px solid var(--border); border-radius: var(--radius);
cursor: pointer; font-size: 12px; color: var(--text-secondary);
transition: var(--transition);
}
.btn-back:hover { background: var(--bg-hover); color: var(--text-primary); }
.task-detail-header .task-title { font-size: 14px; font-weight: 600; flex: 1; }
.task-detail-header .task-type-badge {
font-size: 10px; padding: 2px 8px; border-radius: 10px;
font-weight: 500;
}
.task-detail-body {
flex: 1; overflow-y: auto; padding: 16px;
display: flex; flex-direction: column; gap: 12px;
}
.task-detail-body::-webkit-scrollbar { width: 4px; }
.task-detail-body::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
.task-info-card {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 14px 16px;
}
.task-info-card .tic-title { font-size: 13px; font-weight: 600; margin-bottom: 8px; display: flex; align-items: center; gap: 6px; }
.task-info-card .tic-row { display: flex; gap: 12px; margin-bottom: 4px; font-size: 12px; }
.task-info-card .tic-label { color: var(--text-muted); min-width: 70px; }
.task-info-card .tic-value { color: var(--text-primary); font-weight: 500; }
.task-info-card .tic-desc {
margin-top: 8px; padding-top: 8px;
border-top: 1px solid var(--border);
font-size: 12px; color: var(--text-secondary); line-height: 1.6;
}
.task-actions {
display: flex; gap: 8px; flex-shrink: 0;
padding: 10px 16px; background: var(--bg-secondary);
border-top: 1px solid var(--border);
}
.btn-action {
padding: 8px 18px; border-radius: var(--radius);
font-size: 12px; font-weight: 500; cursor: pointer;
transition: var(--transition); border: 1px solid transparent;
}
.btn-action.primary { background: var(--accent); color: #fff; border-color: var(--accent); }
.btn-action.primary:hover { opacity: 0.9; }
.btn-action.success { background: var(--success); color: #fff; border-color: var(--success); }
.btn-action.success:hover { opacity: 0.9; }
.btn-action.warning { background: var(--warning); color: #fff; border-color: var(--warning); }
.btn-action.warning:hover { opacity: 0.9; }
.btn-action.danger { background: var(--danger); color: #fff; border-color: var(--danger); }
.btn-action.danger:hover { opacity: 0.9; }
.btn-action.default { background: var(--bg-tertiary); color: var(--text-secondary); border-color: var(--border); }
.btn-action.default:hover { background: var(--bg-hover); color: var(--text-primary); }
/* Approval comment */
.approval-section {
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: 14px 16px;
}
.approval-section .as-title { font-size: 12px; font-weight: 600; margin-bottom: 8px; }
.approval-textarea {
width: 100%; min-height: 80px; resize: vertical;
background: var(--bg-tertiary); border: 1px solid var(--border);
border-radius: var(--radius); padding: 8px 10px;
color: var(--text-primary); font-size: 12px; font-family: inherit;
outline: none; transition: border-color var(--transition);
}
.approval-textarea:focus { border-color: var(--accent); }
.approval-textarea::placeholder { color: var(--text-muted); }
.device-status-grid {
display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
}
.device-status-item {
background: var(--bg-tertiary); border: 1px solid var(--border);
border-radius: var(--radius); padding: 8px 10px;
}
.device-status-item .dsi-label { font-size: 10px; color: var(--text-muted); margin-bottom: 2px; }
.device-status-item .dsi-value { font-size: 12px; font-weight: 500; }
.dsi-value.normal { color: var(--success); }
.dsi-value.warning { color: var(--warning); }
.dsi-value.danger { color: var(--danger); }
/* ========== RIGHT SIDEBAR ========== */
.sidebar-right {
width: 320px; min-width: 220px;
background: var(--bg-secondary);
border-left: none;
display: flex; flex-direction: column;
overflow: hidden; transition: var(--transition);
position: relative;
}
/* AI Recommend */
.ai-recommend-section { flex-shrink: 0; padding: 10px 12px; border-bottom: 1px solid var(--border); }
.ai-recommend-section .ars-title {
font-size: 12px; font-weight: 600; color: var(--accent);
margin-bottom: 8px; display: flex; align-items: center; gap: 6px;
}
.ai-recommend-section .ars-title::before {
content: ''; width: 3px; height: 12px;
background: var(--accent); border-radius: 2px; display: inline-block;
}
.ars-card {
background: var(--bg-tertiary); border: 1px solid var(--border);
border-radius: var(--radius); padding: 8px 10px;
margin-bottom: 5px; cursor: pointer; transition: var(--transition);
}
.ars-card:hover { border-color: var(--accent); background: var(--bg-hover); }
.ars-card .ars-card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 3px; }
.ars-card .ars-confidence { font-size: 10px; padding: 1px 6px; border-radius: 8px; background: var(--accent-soft); color: var(--accent); }
.ars-card .ars-card-text { font-size: 11px; color: var(--text-secondary); line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.ars-card .ars-shortcut-hint { font-size: 10px; color: var(--text-muted); margin-top: 3px; display: flex; align-items: center; gap: 4px; }
.key-badge {
display: inline-flex; align-items: center; justify-content: center;
font-size: 10px; padding: 1px 5px; background: var(--bg-hover);
border: 1px solid var(--border); border-radius: 3px;
color: var(--text-muted); min-width: 18px; font-family: monospace;
}
/* Quick Reply — 三层渐进式导航 */
.quick-reply-section { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-height: 0; }
/* 搜索栏 */
.qr-search { padding: 8px 12px; flex-shrink: 0; position: relative; }
.qr-search-input {
width: 100%; background: var(--bg-tertiary); border: 1px solid var(--border);
border-radius: var(--radius); padding: 7px 10px 7px 28px;
color: var(--text-primary); font-size: 12px; font-family: inherit;
outline: none; transition: border-color var(--transition);
}
.qr-search-input:focus { border-color: var(--accent); }
.qr-search-input::placeholder { color: var(--text-muted); font-size: 11px; }
.qr-search-icon { position: absolute; left: 22px; top: 50%; transform: translateY(-50%); color: var(--text-muted); font-size: 12px; }
/* 面包屑导航:显示当前路径 L1 > L2 */
.qr-breadcrumb {
padding: 6px 12px; flex-shrink: 0;
font-size: 11px; color: var(--text-secondary);
display: flex; align-items: center; gap: 4px; flex-wrap: wrap;
border-bottom: 1px solid var(--border);
background: var(--bg-tertiary);
min-height: 28px;
}
.qr-breadcrumb .bc-back {
cursor: pointer; color: var(--accent); font-weight: 500;
padding: 1px 6px; border-radius: 3px;
transition: var(--transition);
}
.qr-breadcrumb .bc-back:hover { background: var(--accent-soft); }
.qr-breadcrumb .bc-sep { color: var(--text-muted); }
.qr-breadcrumb .bc-current { font-weight: 600; color: var(--text-primary); }
.qr-breadcrumb .bc-hint {
margin-left: auto; font-size: 10px; color: var(--text-muted);
display: flex; align-items: center; gap: 3px;
}
/* ===== L1 一级目录:7列网格,强制一行,按钮内上下排列 ===== */
.qr-l1-grid {
padding: 4px 6px 6px; flex-shrink: 0;
border-bottom: 1px solid var(--border);
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 4px;
}
.qr-l1-grid.hidden { display: none; }
.qr-l1-btn {
display: flex; flex-direction: column; align-items: center; justify-content: center;
gap: 2px;
padding: 5px 2px;
border-radius: var(--radius);
cursor: pointer; transition: var(--transition);
font-size: 10px; color: var(--text-secondary);
background: transparent;
border: 1px solid var(--border);
min-width: 0; overflow: hidden;
}
.qr-l1-btn:hover { background: var(--bg-hover); color: var(--text-primary); border-color: var(--border-light); }
.qr-l1-btn.active { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); font-weight: 600; }
/* L1 按钮颜色 — 7个主类(nth-child */
.qr-l1-btn:nth-child(1).active { background: #dbeafe; color: #1e40af; border-color: #3b82f6; }
.qr-l1-btn:nth-child(1).active .l1-num { background: #3b82f6; color: #fff; }
.qr-l1-btn:nth-child(2).active { background: #dcfce7; color: #166534; border-color: #22c55e; }
.qr-l1-btn:nth-child(2).active .l1-num { background: #22c55e; color: #fff; }
.qr-l1-btn:nth-child(3).active { background: #fff7ed; color: #9a3412; border-color: #f97316; }
.qr-l1-btn:nth-child(3).active .l1-num { background: #f97316; color: #fff; }
.qr-l1-btn:nth-child(4).active { background: #ede9fe; color: #5b21b6; border-color: #8b5cf6; }
.qr-l1-btn:nth-child(4).active .l1-num { background: #8b5cf6; color: #fff; }
.qr-l1-btn:nth-child(5).active { background: #fee2e2; color: #991b1b; border-color: #ef4444; }
.qr-l1-btn:nth-child(5).active .l1-num { background: #ef4444; color: #fff; }
.qr-l1-btn:nth-child(6).active { background: #f0fdfa; color: #115e59; border-color: #0ea5e9; }
.qr-l1-btn:nth-child(6).active .l1-num { background: #0ea5e9; color: #fff; }
.qr-l1-btn:nth-child(7).active { background: #f3f4f6; color: #374151; border-color: #6b7280; }
.qr-l1-btn:nth-child(7).active .l1-num { background: #6b7280; color: #fff; }
.qr-l1-btn .l1-num {
display: flex; align-items: center; justify-content: center;
width: 18px; height: 18px; border-radius: 4px;
background: var(--bg-tertiary); color: var(--text-muted);
font-size: 10px; font-weight: 700; flex-shrink: 0;
border: 1px solid var(--border);
}
.qr-l1-btn .l1-name {
font-size: 10px; line-height: 1.2;
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
max-width: 100%;
}
.qr-l1-btn.active .l1-num { background: var(--accent); color: #fff; border-color: var(--accent); }
/* ===== L2 二级目录:横向流式,1-2行 ===== */
.qr-l2-row {
padding: 6px 10px; flex-shrink: 0;
border-bottom: 1px solid var(--border);
display: flex; flex-wrap: wrap; gap: 4px;
}
.qr-l2-row.hidden { display: none; }
.qr-l2-chip {
display: flex; align-items: center; gap: 4px;
padding: 4px 10px; border-radius: 12px;
cursor: pointer; transition: var(--transition);
font-size: 11px; color: var(--text-secondary);
background: var(--bg-tertiary);
border: 1px solid var(--border);
white-space: nowrap;
}
.qr-l2-chip:hover { background: var(--bg-hover); color: var(--text-primary); border-color: var(--border-light); }
.qr-l2-chip.selected { background: var(--qr-active-light, var(--accent-soft)); color: var(--qr-active-color, var(--accent)); border-color: var(--qr-active-color, var(--accent)); font-weight: 600; }
.qr-l2-chip .l2-num {
display: flex; align-items: center; justify-content: center;
width: 16px; height: 16px; border-radius: 50%;
background: var(--bg-hover); color: var(--text-muted);
font-size: 9px; font-weight: 700; flex-shrink: 0;
}
.qr-l2-chip.selected .l2-num { background: var(--qr-active-color, var(--accent)); color: #fff; }
/* ===== L3 回复列表 ===== */
.qr-l3-list {
flex: 1; overflow-y: auto; padding: 4px;
}
.qr-l3-list::-webkit-scrollbar { width: 4px; }
.qr-l3-list::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
.qr-l3-item {
display: flex; align-items: flex-start; gap: 8px;
padding: 8px 10px; margin: 2px 4px;
border-radius: var(--radius);
cursor: pointer; transition: var(--transition);
font-size: 12px; color: var(--text-secondary);
border: 1px solid transparent;
}
.qr-l3-item:hover { background: var(--bg-hover); color: var(--text-primary); }
.qr-l3-item.selected { background: var(--qr-active-light, var(--accent-soft)); border-color: var(--qr-active-color, var(--accent)); color: var(--text-primary); }
.qr-l3-item .l3-num {
display: flex; align-items: center; justify-content: center;
width: 20px; height: 20px; border-radius: 4px;
background: var(--bg-tertiary); color: var(--text-muted);
font-size: 10px; font-weight: 700; flex-shrink: 0;
border: 1px solid var(--border); margin-top: 1px;
}
.qr-l3-item.selected .l3-num { background: var(--qr-active-color, var(--accent)); color: #fff; border-color: var(--qr-active-color, var(--accent)); }
.qr-l3-item .l3-content { flex: 1; line-height: 1.5; }
.qr-l3-item .l3-title { font-weight: 500; color: var(--text-primary); margin-bottom: 2px; }
.qr-l3-item .l3-preview { font-size: 11px; color: var(--text-muted); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.qr-l3-empty {
display: flex; flex-direction: column; align-items: center; justify-content: center;
height: 100%; color: var(--text-muted); font-size: 12px;
gap: 6px; padding: 20px;
}
.qr-l3-empty .empty-icon { font-size: 28px; opacity: 0.4; }
.qr-l3-empty .empty-hint { font-size: 11px; text-align: center; line-height: 1.6; }
/* 已选中的回复预览条 */
.qr-selected-bar {
padding: 6px 12px; flex-shrink: 0;
border-top: 1px solid var(--border);
background: var(--success-soft);
font-size: 11px; color: var(--success);
display: flex; align-items: center; gap: 6px;
}
.qr-selected-bar.hidden { display: none; }
.qr-selected-bar .sel-text { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qr-selected-bar .sel-hint { font-size: 10px; color: var(--text-muted); flex-shrink: 0; }
/* 键盘指南 */
.keyboard-guide { padding: 6px 12px; border-top: 1px solid var(--border); font-size: 10px; color: var(--text-muted); display: flex; gap: 8px; flex-wrap: wrap; flex-shrink: 0; }
.keyboard-guide span { display: flex; align-items: center; gap: 3px; }
</style>
<script>
// Auto-generated from IT支持知识库2026-4-24.docx
// 7大类 28子类 180条快速回复模板
const qrData = [{"name": "电脑", "subs": [{"name": "硬件设备", "items": [{"title": "笔记本电脑电池续航异常", "content": "健康评估标准:剩余容量<70%或循环次数>500次。\n获取报告步骤::\nWindowscmd中输入 powercfg /batteryreport,查看报告中的“CYCLE COUNT”。\nMac:按住Option键点击苹果菜单→系统信息→电源→查看“循环计数”。\n将报告留言分享,等待人工坐席进一步评估。"}, {"title": "办公电脑常见问题处理(黑屏、警报)", "content": "排查步骤\n1. 观察电源指示灯:确认电脑的电源指示灯是否亮起或闪烁。\n2. 强制关机重启:长按电源键约15-20秒,直到电源指示灯完全熄灭,等待几秒钟后,再次按下电源键尝试开机。"}, {"title": "办公电脑常见问题处理(死机、卡顿)", "content": "排查步骤:\n1. 检查系统资源:按Ctrl+Shift+Esc打开任务管理器,结束占用高的非必要进程。\n2. 强制重启:长按电源键15-20秒至指示灯熄灭,等待后重新开机。"}]}, {"name": "Windows系统", "items": [{"title": "Windows本地账户密码修改", "content": "路径:设置→帐户→登录选项→密码→更改,按提示完成。"}, {"title": "办公电脑功能异常(无声音、屏幕显示、键盘热键)", "content": "排查步骤:\n1. 检查驱动:设备管理器查看是否有异常设备(黄色/红色标志)。\n2. 重装驱动:联想电脑使用官方工具;其他品牌从官网下载最新驱动。"}, {"title": "办公电脑麦克风无声音", "content": "排查步骤:\n1. 设置默认设备:右键任务栏扬声器图标→声音设置,确保麦克风设为默认输入设备。\n2. 授予权限:在Windows搜索“麦克风隐私设置”,开启麦克风访问权限及对应应用(如企业微信、小鱼)的权限。\n3. 调整属性:在设备属性中调整音量和麦克风增强,禁用独占模式。"}, {"title": "Windows电脑和Office许可证过期|激活|即将到期", "content": "适用场景:激活过期/失败/即将到期。\n操作步骤:\n1. 下载工具:https://drive.weixin.qq.com/s?k=AAoA1wcYAAcmKeQnWG\n2. 运行工具,按需取消选项(如不需激活Office)。\n3. 点击“开始”处理。"}, {"title": "电脑C盘空间不足", "content": "操作步骤:\n1. 打开企业微信,进入【设置】→【文档/文件管理】→【文件储存位置】。\n2. 点击【更改】,选择其他盘符的目录作为新存储路径。"}, {"title": "U盘、移动硬盘无法弹出报错“弹出USB大容量存储设备时出问题”", "content": "故障现象:\n弹出U盘提示“该设备正在使用中、请关闭可能使用该设备的所有程序或窗口,然后重试”\n解决方法:\n将电脑关机后再拔出硬盘"}, {"title": "办公电脑系统初始密码", "content": "总部新电脑:Windows系统无密码(直接回车)\n电脑开机密码是独立的,不与内部统一员工账密一致。"}, {"title": "电脑开机密码重置", "content": "重置电脑开机需使用专用工具由IT支持人员进行现场处理,总部员工请携带设备前往121室,区域同事请联系本地兼职网络协助处理"}]}, {"name": "鸿蒙系统", "items": [{"title": "公司办公IT环境不支持鸿蒙系统的软硬件清单", "content": "软件功能类:\n火绒安全、税友安全助手、企业微信-同事吧(发帖、回复)"}]}]}, {"name": "软件", "subs": [{"name": "常用工具", "items": [{"title": "常用办公软件下载地址", "content": "常用办公软件下载地址:https://drive.weixin.qq.com/s?k=AAoA1wcYAAcVScZYR4"}, {"title": "压缩工具", "content": "7-Zip是一款免费开源高压缩比的压缩软件,支持7z、ZIP、RAR、CAB、GZIP、BZIP2和TAR等格式。此软件压缩的压缩比要比普通ZIP文件高30-50%。\n7-Zip 客户端下载地址:https://sparanoid.com/lab/7z/download.html"}]}, {"name": "企业微信", "items": [{"title": "企业微信综合信息", "content
</script>
</head>
<body data-theme="light">
<!-- Top bar -->
<div class="theme-toggle-bar">
<div class="bar-left">
<div class="logo-sm">IT</div>
<span class="sys-name">IT智能服务台</span>
<span class="sys-tagline">· 坐席工作台 — AI驱动 · 多系统对接 · 一站式处理</span>
</div>
<div class="bar-right">
<div class="theme-switch" onclick="toggleTheme()">
<span class="switch-icon">☀️</span>
<div class="switch-track"><div class="switch-thumb"></div></div>
<span class="switch-icon">🌙</span>
</div>
<div class="bar-user-info">
<span>坐席: 陈思远</span>
<div class="bar-user-avatar"></div>
</div>
</div>
</div>
<div class="workspace">
<!-- ===== LEFT: Conversation List + 待办事项 ===== -->
<div class="sidebar-left">
<div class="conv-search">
<div class="conv-search-input-wrap">
<span class="conv-search-icon">🔍</span>
<input class="conv-search-input" placeholder="搜索用户、关键词..." />
</div>
<div class="conv-search-tabs">
<span class="conv-search-tab active">全部</span>
<span class="conv-search-tab">待处理</span>
<span class="conv-search-tab">进行中</span>
<span class="conv-search-tab">已完成</span>
</div>
</div>
<div class="conv-sections">
<!-- My conversations -->
<div class="conv-section-body" style="max-height:500px;">
<!-- 张伟 — 有新消息(紧急红色圆点)+ 处理对象缩略头像 -->
<div class="conv-item active">
<div class="conv-avatar-wrap">
<div class="conv-avatar av-blue"></div>
<span class="new-msg-dot dot-urgent" title="有新消息"></span>
</div>
<div class="conv-content">
<div class="conv-top">
<span class="conv-name-row">
<span class="conv-name">张伟</span>
<span class="priority-icons">
<span class="pi-icon pi-blocked" title="⛔ 问题阻断性:无法工作"></span>
<span class="pi-icon pi-impact high" title="📊 影响范围:12人">👥12</span>
<span class="pi-icon pi-role" title="👑 角色等级:核心研发">👑</span>
<span class="pi-icon pi-repeat" title="🔁 7天内重复3次">🔁</span>
</span>
</span>
<span class="conv-time">10:25</span>
</div>
<div class="conv-preview">VPN 连接失败,无法访问内网系统...</div>
</div>
<span class="conv-waiting">待回复</span>
<div class="conv-target-avatar ta-blue" title="张伟"></div>
</div>
<!-- 陈芳 — 有新消息(普通蓝色圆点)+ 处理对象缩略头像 -->
<div class="conv-item">
<div class="conv-avatar-wrap">
<div class="conv-avatar av-purple"></div>
<span class="new-msg-dot dot-normal" title="有新消息"></span>
</div>
<div class="conv-content">
<div class="conv-top">
<span class="conv-name-row">
<span class="conv-name">陈芳</span>
<span class="priority-icons">
<span class="pi-icon pi-impact" title="📊 影响范围:3人">👥3</span>
<span class="pi-icon pi-role" title="👑 角色等级:部门经理">👑👑</span>
</span>
</span>
<span class="conv-time">09:42</span>
</div>
<div class="conv-preview">系统卡顿严重影响工作...</div>
</div>
<span class="conv-waiting">待回复</span>
<div class="conv-target-avatar ta-purple" title="陈芳"></div>
</div>
<!-- 李娜 — 无新消息(无圆点)+ 处理对象缩略头像 -->
<div class="conv-item">
<div class="conv-avatar-wrap">
<div class="conv-avatar av-green"></div>
<!-- 无新消息,无圆点 -->
</div>
<div class="conv-content">
<div class="conv-top">
<span class="conv-name-row">
<span class="conv-name">李娜</span>
<span class="priority-icons">
<span class="pi-icon pi-role" title="👤 角色等级:普通员工">👤</span>
</span>
</span>
<span class="conv-time">10:18</span>
</div>
<div class="conv-preview">邮箱签名设置问题</div>
</div>
<div class="conv-target-avatar ta-green" title="李娜"></div>
</div>
</div>
<!-- === 新增:不同状态会话展示 === -->
<!-- 王磊 — 有新消息(紧急红色圆点)+ 举手标签 + 处理对象缩略头像 -->
<div class="conv-item" data-conv="conv-004" style="border-left:2px solid #f59e0b">
<div class="conv-avatar-wrap">
<div class="conv-avatar av-orange"></div>
<span class="new-msg-dot dot-urgent" title="有新消息"></span>
</div>
<div class="conv-content">
<div class="conv-top">
<span class="conv-name-row">
<span class="conv-name">王磊</span>
<span class="conv-tag-warn">需介入</span>
<span class="conv-tag-urgent">举手</span>
<span class="priority-icons">
<span class="pi-icon pi-block" title="🚫 阻断">🚫</span>
<span class="pi-impact">8人</span>
<span class="pi-icon pi-role" title="👤 部门主管">👤</span>
</span>
</span>
<span class="conv-time">10:25</span>
</div>
<div class="conv-preview">税控系统还是登录不上,下午3点前要报税</div>
</div>
<div class="conv-target-avatar ta-orange" title="王磊"></div>
</div>
<!-- 孙丽 — 无新消息(无圆点)+ 处理对象缩略头像 -->
<div class="conv-item" data-conv="conv-005" style="opacity:0.7; border-left:2px solid var(--border)">
<div class="conv-avatar-wrap">
<div class="conv-avatar av-teal"></div>
<!-- 无新消息,无圆点 -->
</div>
<div class="conv-content">
<div class="conv-top">
<span class="conv-name-row">
<span class="conv-name">孙丽</span>
<span class="conv-tag-new"></span>
<span class="priority-icons">
<span class="pi-icon pi-confused" title="😕 困惑">😕</span>
</span>
</span>
<span class="conv-time">10:10</span>
</div>
<div class="conv-preview">合同管理系统看不到审批列表,不知道怎么操作</div>
</div>
<div class="conv-target-avatar ta-teal" title="孙丽"></div>
</div>
<!-- 刘洋 — 有新消息(紧急红色圆点)+ 举手标签 + VIP + 置顶 + 处理对象缩略头像 -->
<div class="conv-item active" data-conv="conv-006" style="border-left:2px solid #ef4444; background:var(--accent-light)">
<div class="conv-avatar-wrap">
<div class="conv-avatar av-red"></div>
<span class="new-msg-dot dot-urgent" title="有新消息"></span>
</div>
<div class="conv-content">
<div class="conv-top">
<span class="conv-name-row">
<span class="conv-pin-badge">📌 置顶</span>
<span class="conv-name">刘洋</span>
<span class="conv-tag-vip">VIP</span>
<span class="conv-tag-urgent">举手</span>
<span class="priority-icons">
<span class="pi-icon pi-block" title="🚫 阻断">🚫</span>
<span class="pi-impact">20人</span>
<span class="pi-icon pi-angry" title="😡 愤怒">😡</span>
<span class="pi-icon pi-repeat" title="🔄 重复">🔄</span>
</span>
</span>
<span class="conv-time">10:27</span>
</div>
<div class="conv-preview">这个问题已经拖了三天了,请尽快解决!</div>
</div>
<div class="conv-target-avatar ta-red" title="刘洋"></div>
</div>
<!-- Colleague sessions (always expanded) -->
<div class="conv-section-body" style="max-height:300px;">
<!-- 赵敏 — 有新消息(普通蓝色圆点)+ 坐席刘明缩略头像 -->
<div class="conv-item">
<div class="conv-avatar-wrap">
<div class="conv-avatar av-pink"></div>
<span class="new-msg-dot dot-normal" title="有新消息"></span>
</div>
<div class="conv-content">
<div class="conv-top">
<span class="conv-name-row">
<span class="conv-name">赵敏</span>
</span>
</div>
<div class="conv-preview">OA 审批流程报错</div>
</div>
<div class="conv-target-avatar ta-blue" title="坐席:刘明"></div>
</div>
<!-- 周芳 — 无新消息(无圆点)+ 坐席缩略头像 -->
<div class="conv-item">
<div class="conv-avatar-wrap">
<div class="conv-avatar av-green"></div>
<!-- 无新消息 -->
</div>
<div class="conv-content">
<div class="conv-top">
<span class="conv-name-row">
<span class="conv-name">周芳</span>
</span>
</div>
<div class="conv-preview">打印机无法连接</div>
</div>
<div class="conv-target-avatar ta-green" title="坐席:王强"></div>
</div>
<!-- 吴明 — 有新消息(低优先灰色圆点)+ 坐席缩略头像 -->
<div class="conv-item">
<div class="conv-avatar-wrap">
<div class="conv-avatar av-orange"></div>
<span class="new-msg-dot dot-muted" title="低优先新消息"></span>
</div>
<div class="conv-content">
<div class="conv-top">
<span class="conv-name-row">
<span class="conv-name">吴明</span>
</span>
</div>
<div class="conv-preview">新电脑开机配置</div>
</div>
<div class="conv-target-avatar ta-purple" title="坐席:李静"></div>
</div>
</div>
<!-- History (always expanded) -->
<div class="conv-section-body" style="max-height:400px;">
<!-- 历史会话 — 无新消息(无圆点),无处理对象缩略头像 -->
<div class="conv-item">
<div class="conv-avatar-wrap">
<div class="conv-avatar av-teal"></div>
<!-- 历史会话,无新消息圆点 -->
</div>
<div class="conv-content">
<div class="conv-top">
<span class="conv-name-row">
<span class="conv-name">周杰</span>
</span>
<span class="conv-time">昨日</span>
</div>
<div class="conv-preview">VPN 证书过期 — 已解决</div>
</div>
</div>
</div>
</div>
<!-- ===== 待办事项 ===== -->
<div class="kanban">
<div class="kanban-title">待办事项 <span class="count">6</span></div>
<div class="kanban-item active" onclick="showTaskDetail('ticket')">
<span class="ki-priority urgent"></span>
<span class="ki-text">CEO办公室 - 投屏设备故障</span>
<span class="ki-type ticket">工单</span>
<span class="ki-time">5m</span>
<div class="ki-avatar ka-red" title="王莉"></div>
</div>
<div class="kanban-item" onclick="showTaskDetail('ticket')">
<span class="ki-priority urgent"></span>
<span class="ki-text">财务部 - 税控系统无法登录</span>
<span class="ki-type ticket">工单</span>
<span class="ki-time">12m</span>
<div class="ki-avatar ka-orange" title="王磊"></div>
</div>
<div class="kanban-item" onclick="showTaskDetail('approval')">
<span class="ki-priority high"></span>
<span class="ki-text">IT采购申请 - 研发部笔记本电脑</span>
<span class="ki-type approval">审批</span>
<span class="ki-time">28m</span>
<div class="ki-avatar ka-blue" title="李研发"></div>
</div>
<div class="kanban-item" onclick="showTaskDetail('device')">
<span class="ki-priority high"></span>
<span class="ki-text">会议室 A - 投影仪离线</span>
<span class="ki-type device">设备</span>
<span class="ki-time">45m</span>
<div class="ki-avatar ka-green" title="行政部"></div>
</div>
<div class="kanban-item" onclick="showTaskDetail('approval')">
<span class="ki-priority normal"></span>
<span class="ki-text">新员工入职 - 设备申领审批</span>
<span class="ki-type approval">审批</span>
<span class="ki-time">32m</span>
<div class="ki-avatar ka-purple" title="HR部">H</div>
</div>
<div class="kanban-item resolved" onclick="showTaskDetail('ticket')">
<span class="ki-priority done"></span>
<span class="ki-text">客服部 - 耳机无声(驱动重装)</span>
<span class="ki-type ticket">工单</span>
<span class="ki-time">1h</span>
<div class="ki-avatar ka-blue" title="客服部"></div>
</div>
<div class="kanban-stats"><span><span class="dot dot-online"></span> 在线 4</span><span><span class="dot dot-busy"></span> 忙碌 2</span><span><span class="dot dot-offline"></span> 离线 1</span></div>
</div>
</div>
<!-- 拖拽手柄:左栏↔中间栏 -->
<div class="resize-handle" id="resizeLeft" title="拖拽调整左栏宽度"></div>
<!-- ===== CENTER: Chat View (default) + Task Detail View ===== -->
<div class="center-column">
<!-- ===== Chat View (default) ===== -->
<div class="chat-view" id="chatView">
<!-- User info bar -->
<div class="user-info-bar" id="userInfoBar" onclick="toggleUserDetail()">
<div class="user-avatar"></div>
<div class="user-name-section">
<span class="user-name">
张伟 · 研发一部 高级工程师
<span class="it-level-badge gold" title="IT技能等级:黄金 — 熟练使用,高级操作需指导">
🥇 黄金
<span class="level-num">Lv.3</span>
</span>
</span>
</div>
<div class="info-chips">
<span class="info-chip emotion">😟 焦虑</span>
<span class="info-chip default">⏱ 8分32秒</span>
<span class="info-chip default">💬 6轮</span>
<span class="info-chip repeat">🔁 重复</span>
<span class="info-chip remark">📝 备注</span>
</div>
<!-- Device status icons -->
<div class="device-status-row">
<!-- OS Type: Windows (online) -->
<div class="dev-icon online" title="Windows 11 · 在线">
<span>🪟</span>
<span class="dev-badge green"></span>
<span class="dev-tooltip">Windows 11 · 在线联网</span>
</div>
<!-- Antivirus: Huorong installed & up-to-date -->
<div class="dev-icon safe" title="火绒 · 已安装 · 病毒库最新">
<span>🛡️</span>
<span class="dev-badge green"></span>
<span class="dev-tooltip">火绒杀毒 · 已安装 · 病毒库最新</span>
</div>
<!-- Adobe: Certified/Licensed -->
<div class="dev-icon certified" title="Adobe · 已授权">
<span style="font-size:11px;font-weight:700;letter-spacing:-0.5px;">Ai</span>
<span class="dev-badge green"></span>
<span class="dev-tooltip">Adobe Illustrator · ✅ 已授权</span>
</div>
<!-- Office: Certified -->
<div class="dev-icon certified" title="Office 365 · 已授权">
<span style="font-size:11px;font-weight:700;">O</span>
<span class="dev-badge green"></span>
<span class="dev-tooltip">Microsoft 365 · ✅ 已授权</span>
</div>
<!-- Patch: Warning - 2 pending -->
<div class="dev-icon warn" title="系统补丁 · 2个待更新">
<span>🔄</span>
<span class="dev-badge yellow"></span>
<span class="dev-tooltip">系统补丁 · ⚠️ 2个待更新</span>
</div>
</div>
<!-- 2026-07-24 优化:接单按钮(用户信息栏右侧操作区) -->
<div style="display:flex;align-items:center;gap:6px;margin-left:auto;">
<!-- 待接单状态 -->
<button class="btn-action primary" style="font-size:12px;padding:4px 10px;" onclick="this.outerHTML='<button class=\'btn-action primary\' disabled style=\'opacity:0.5;cursor:not-allowed;background:#9ca3af;border-color:#9ca3af;font-size:12px;padding:4px 10px;\'>📥 已接单</button>'">📥 接单</button>
<!-- 已接单状态(点击后变为):<button class="btn-action primary" disabled style="opacity:0.5;cursor:not-allowed;background:#9ca3af;border-color:#9ca3af;">📥 已接单</button> -->
<button class="btn-action warning" style="font-size:12px;padding:4px 10px;">📌 置顶</button>
<button class="btn-action default" style="font-size:12px;padding:4px 10px;">📋 代办</button>
<button class="btn-action default" style="font-size:12px;padding:4px 10px;">🔄 转接</button>
</div>
<span class="expand-icon"></span>
</div>
<!-- 2026-07-24 更新:根据线上实际情况,右侧详情栏6个卡片 -->
<div class="user-detail-panel" style="display:grid;grid-template-columns:repeat(3,1fr);gap:12px;padding:12px 16px;">
<!-- 卡片1:情绪状态 -->
<div class="detail-card" style="background:var(--bg-tertiary);border-radius:8px;padding:12px;">
<div class="dc-label" style="font-size:11px;color:var(--text-secondary);margin-bottom:6px;">情绪状态</div>
<div class="dc-value" style="font-size:13px;">😟 担忧</div>
<div style="font-size:11px;color:var(--text-muted);margin-top:4px;">语气急促,连续追问进度</div>
</div>
<!-- 卡片2:会话详情 -->
<div class="detail-card" style="background:var(--bg-tertiary);border-radius:8px;padding:12px;">
<div class="dc-label" style="font-size:11px;color:var(--text-secondary);margin-bottom:6px;">会话详情</div>
<div class="dc-value" style="font-size:13px;">⏱ 等待时长:<strong>8分钟</strong></div>
<div class="dc-value" style="font-size:13px;margin-top:4px;">💬 对话轮次:<strong>6轮</strong></div>
</div>
<!-- 卡片3:问题分析 -->
<div class="detail-card" style="background:var(--bg-tertiary);border-radius:8px;padding:12px;">
<div class="dc-label" style="font-size:11px;color:var(--text-secondary);margin-bottom:6px;">问题分析</div>
<div class="dc-value" style="font-size:13px;">🔁 重复问题</div>
<div style="font-size:11px;color:var(--text-muted);margin-top:4px;">7天内反馈:<strong>3次</strong></div>
</div>
<!-- 卡片4IT技能等级 -->
<div class="detail-card" style="background:var(--bg-tertiary);border-radius:8px;padding:12px;">
<div class="dc-label" style="font-size:11px;color:var(--text-secondary);margin-bottom:6px;">IT技能等级</div>
<div style="display:flex;align-items:center;gap:6px;">
<span class="it-level-badge gold">🥇 黄金 Lv.3</span>
<span style="font-size:12px;font-weight:600;">黄金</span>
</div>
<div style="font-size:11px;color:var(--text-muted);margin-top:4px;">熟悉常见操作,可独立解决一般问题</div>
</div>
<!-- 卡片5:历史工单 -->
<div class="detail-card" style="background:var(--bg-tertiary);border-radius:8px;padding:12px;">
<div class="dc-label" style="font-size:11px;color:var(--text-secondary);margin-bottom:6px;">历史工单</div>
<div style="font-size:13px;">30天内工单:<strong>3次</strong></div>
<div style="font-size:11px;color:var(--text-muted);margin-top:4px;">类型分布:VPN(2次) 邮箱(1次)</div>
</div>
<!-- 卡片6:其他备注 -->
<div class="detail-card" style="background:var(--bg-tertiary);border-radius:8px;padding:12px;">
<div class="dc-label" style="font-size:11px;color:var(--text-secondary);margin-bottom:6px;">其他备注</div>
<div style="font-size:12px;color:var(--text-secondary);">暂无备注</div>
</div>
</div>
<!-- ===== Troubleshoot bar (ALWAYS visible, flowchart collapsible) ===== -->
<div class="troubleshoot-bar" id="tsBar">
<div class="ts-header">
<span class="ts-title">🔧 排查步骤</span>
<div class="ts-path-inline" id="tsPathView">
<span class="path-step done">① 确认版本</span>
<span class="path-arrow-inline"></span>
<span class="path-step done">② 清除缓存</span>
<span class="path-arrow-inline"></span>
<span class="path-step current">③ 远程排查</span>
<span class="path-arrow-inline"></span>
<span class="path-step">④ 升级客户端</span>
<span class="path-arrow-inline"></span>
<span class="path-step">⑤ 回访确认</span>
</div>
<span class="ts-flowchart-toggle" id="tsFlowBtn" onclick="toggleFlowchart()" title="展开全流程图"></span>
</div>
<!-- Full flowchart with decision nodes (collapsed by default) -->
<div class="ts-flowchart" id="tsFlowchart">
<div style="font-size:11px;color:var(--text-muted);margin-bottom:8px;">
🔀 VPN 连接失败 — 完整排查决策树
</div>
<!-- Step 1 -->
<div class="flow-step">
<div class="flow-node done">1</div>
<div class="flow-step-connector"></div>
<div class="flow-content">
<div class="flow-title">确认 VPN 客户端版本</div>
<div class="flow-desc">询问用户使用 AnyConnect 或 SSL VPN,记录版本号</div>
<div class="flow-branch">
<div class="flow-branch-item active">→ AnyConnect 4.10 及以下:进入兼容性问题分支</div>
<div class="flow-branch-item">→ SSL VPN 网页版:进入浏览器兼容分支</div>
<div class="flow-branch-item">→ 其他版本:进入通用排查</div>
</div>
</div>
</div>
<!-- Judge node 1 -->
<div class="flow-step">
<div class="flow-node judge">❓ 判断</div>
<div class="flow-step-connector"></div>
<div class="flow-content">
<div class="flow-title">版本 &lt; 4.14</div>
<div class="flow-desc">AnyConnect 4.10 及更早版本存在已知兼容性问题</div>
<div class="flow-branch">
<div class="flow-branch-item active">是 → 执行步骤②清除缓存,若仍失败直接进入步骤④升级</div>
<div class="flow-branch-item">否 → 进入网络配置排查分支</div>
</div>
</div>
</div>
<!-- Step 2 -->
<div class="flow-step">
<div class="flow-node done">2</div>
<div class="flow-step-connector"></div>
<div class="flow-content">
<div class="flow-title">清除缓存配置文件</div>
<div class="flow-desc">指导用户删除 C:\ProgramData\Cisco\ 下 XML 文件,重启客户端</div>
</div>
</div>
<!-- Judge node 2 -->
<div class="flow-step">
<div class="flow-node judge">❓ 判断</div>
<div class="flow-step-connector"></div>
<div class="flow-content">
<div class="flow-title">清除缓存后能否连接?</div>
<div class="flow-branch">
<div class="flow-branch-item active">能 → 标记为"已解决",进入步骤⑤回访</div>
<div class="flow-branch-item">不能 → 进入步骤③远程排查</div>
</div>
</div>
</div>
<!-- Step 3 -->
<div class="flow-step">
<div class="flow-node current">3</div>
<div class="flow-step-connector"></div>
<div class="flow-content">
<div class="flow-title">远程桌面排查</div>
<div class="flow-desc">通过内网远程桌面连接用户电脑,检查网络适配器、防火墙、hosts 文件</div>
</div>
</div>
<!-- Step 4 -->
<div class="flow-step">
<div class="flow-node">4</div>
<div class="flow-step-connector"></div>
<div class="flow-content">
<div class="flow-title">升级客户端至 4.14</div>
<div class="flow-desc">发送下载链接,指导用户卸载旧版并安装新版,重新配置连接</div>
</div>
</div>
<!-- Step 5 -->
<div class="flow-step">
<div class="flow-node">5</div>
<div class="flow-content">
<div class="flow-title">24H 后回访确认</div>
<div class="flow-desc">创建提醒任务,24小时后通过企业微信确认 VPN 使用正常</div>
</div>
</div>
</div>
</div>
<!-- Chat -->
<div class="chat-messages">
<div class="msg-system">—— 2026年6月6日 10:25 ——</div>
<div class="msg-bubble msg-user">VPN 连接一直失败,提示"无法建立安全连接",重启了好几次都不行,急需访问内网代码仓库…</div>
<div class="msg-bubble msg-agent">张工您好,先帮您排查一下:请问您是使用的 AnyConnect 客户端还是 SSL VPN 网页版?</div>
<div class="msg-bubble msg-user">AnyConnect 客户端,版本是 4.10。刚才又试了一次,还是同样的错误。</div>
<div class="msg-bubble msg-agent">收到。建议您先清除缓存配置:1)退出客户端;2)删除 XML 文件;3)重新连接。如果还不行,可以远程协助。</div>
<div class="msg-bubble msg-user">按你说的试了,还是不行。能远程看下吗?</div>
</div>
<!-- Chat input -->
<div class="chat-input-area" id="chatInputArea">
<div class="input-resize-handle" id="inputResizeHandle" title="拖拽调整输入框高度"></div>
<!-- 快捷工具栏 -->
<div class="chat-toolbar">
<button class="tb-btn" title="表情"><span class="tb-tip">表情</span>😊</button>
<button class="tb-btn" title="图片"><span class="tb-tip">图片</span>🖼</button>
<button class="tb-btn" title="截图"><span class="tb-tip">截图</span></button>
<button class="tb-btn" title="文件"><span class="tb-tip">文件</span>📎</button>
<button class="tb-btn" title="语音"><span class="tb-tip">语音</span>🎤</button>
<div class="tb-sep"></div>
<button class="tb-btn" title="远程协助"><span class="tb-tip">远程协助</span>🖥</button>
<button class="tb-btn" title="快速回复"><span class="tb-tip">快速回复</span></button>
</div>
<div class="chat-input-row">
<div class="chat-input-card">
<textarea class="chat-input" id="chatInput" placeholder="输入回复内容... (Enter 发送,Shift+Enter 换行)" rows="3"></textarea>
<button class="btn-send">发 送</button>
</div>
</div>
</div>
</div>
<!-- ===== Task Detail View (hidden by default) ===== -->
<div class="task-detail-view" id="taskDetailView">
<div class="task-detail-header">
<button class="btn-back" onclick="showChatView()">← 返回会话</button>
<span class="task-title" id="taskDetailTitle">工单详情</span>
<span class="task-type-badge" id="taskTypeBadge">工单</span>
</div>
<div class="task-detail-body" id="taskDetailBody">
<!-- Dynamic content inserted by JS -->
</div>
<div class="task-actions" id="taskActions">
<!-- Dynamic buttons inserted by JS -->
</div>
</div>
</div>
<!-- 拖拽手柄:中间栏↔右栏 -->
<div class="resize-handle" id="resizeRight" title="拖拽调整右栏宽度"></div>
<!-- ===== RIGHT SIDEBAR ===== -->
<div class="sidebar-right">
<!-- AI Recommend -->
<div class="ai-recommend-section">
<div class="ars-title">🤖 AI 智能推荐</div>
<div class="ars-card" onclick="fillInput(this)">
<div class="ars-card-header"><span style="font-size:10px;color:var(--text-secondary);">远程协助方案</span><span class="ars-confidence">92%</span></div>
<div class="ars-card-text">可以远程,请先安装 TeamViewer 或允许内网远程桌面。您的电脑名是什么?</div>
<div class="ars-shortcut-hint"><span class="key-badge">Ctrl</span>+<span class="key-badge">1</span></div>
</div>
<div class="ars-card" onclick="fillInput(this)">
<div class="ars-card-header"><span style="font-size:10px;color:var(--text-secondary);">备用方案</span><span class="ars-confidence">85%</span></div>
<div class="ars-card-text">临时使用 SSL VPN 网页版:https://sslvpn.servyou-it.com 企业微信扫码登录</div>
<div class="ars-shortcut-hint"><span class="key-badge">Ctrl</span>+<span class="key-badge">2</span></div>
</div>
</div>
<!-- Quick Reply — 三层渐进式导航 -->
<div class="quick-reply-section" id="qrSection">
<!-- 搜索栏 -->
<div class="qr-search">
<span class="qr-search-icon">🔍</span>
<input class="qr-search-input" id="qrSearchInput" placeholder="搜索快速回复 / Alt+目录数字" />
</div>
<!-- 面包屑导航 -->
<div class="qr-breadcrumb" id="qrBreadcrumb">
<span class="bc-back hidden" id="qrBackBtn" onclick="qrGoBack()">← 返回上级</span>
<span class="bc-current" id="qrPathLabel">请选择分类</span>
<span class="bc-hint hidden" id="qrPathHint">
<span class="key-badge">Alt</span>+数字切换一级
</span>
</div>
<!-- L1 一级目录:网格 2 列 × 4 行 -->
<div class="qr-l1-grid" id="qrL1Grid"></div>
<!-- L2 二级目录:流式 chip -->
<div class="qr-l2-row hidden" id="qrL2Row"></div>
<!-- L3 回复列表 -->
<div class="qr-l3-list" id="qrL3List">
<div class="qr-l3-empty">
<span class="empty-icon">💬</span>
<span class="empty-hint"><span class="key-badge">Alt</span>+数字 选择一级分类<br>进入后按数字键逐层筛选<br>选中回复后按 <span class="key-badge">Enter</span> 填入输入框</span>
</div>
</div>
<!-- 已选中回复预览条 -->
<div class="qr-selected-bar hidden" id="qrSelectedBar">
<span></span>
<span class="sel-text" id="qrSelectedText"></span>
<span class="sel-hint">Enter 填入</span>
</div>
<!-- 键盘指南 -->
<div class="keyboard-guide" id="qrKeyboardGuide">
<span><span class="key-badge">Alt</span>+<span class="key-badge">1-7</span> 一级分类</span>
<span><span class="key-badge">数字</span> 选子项</span>
<span><span class="key-badge">Enter</span> 填入</span>
<span><span class="key-badge"></span> 返回</span>
</div>
</div>
</div>
</div>
<script>
/* ========== 主题切换 ========== */
function toggleTheme() {
const body = document.body;
body.setAttribute('data-theme', body.getAttribute('data-theme') === 'dark' ? 'light' : 'dark');
}
function toggleUserDetail() {
document.getElementById('userInfoBar').classList.toggle('expanded');
}
function toggleSection(header) {
header.classList.toggle('collapsed');
header.nextElementSibling.classList.toggle('collapsed');
}
/* ===== FLOWCHART TOGGLE ===== */
function toggleFlowchart() {
const bar = document.getElementById('tsBar');
const btn = document.getElementById('tsFlowBtn');
bar.classList.toggle('show-flow');
if (bar.classList.contains('show-flow')) {
btn.textContent = '▼';
btn.title = '收起全流程图';
} else {
btn.textContent = '▶';
btn.title = '展开全流程图';
}
}
function fillInput(el) {
const text = el.textContent.replace(/Ctrl\s*\+\s*\d/g, '').replace(/快速填入|执行操作/g, '').trim();
document.getElementById('chatInput').value = text;
autoResize(document.getElementById('chatInput'));
}
/* 输入框自动高度(最小3行约60px,最大300px */
function autoResize(textarea) {
textarea.style.height = 'auto';
textarea.style.height = Math.max(60, Math.min(textarea.scrollHeight, 300)) + 'px';
}
document.addEventListener('DOMContentLoaded', function() {
var chatInput = document.getElementById('chatInput');
if (chatInput) {
chatInput.addEventListener('input', function() { autoResize(this); });
}
});
/* ========== Task Detail View Logic ========== */
function showTaskDetail(type) {
document.getElementById('chatView').classList.add('hidden');
const detailView = document.getElementById('taskDetailView');
detailView.classList.add('active');
const titleEl = document.getElementById('taskDetailTitle');
const badgeEl = document.getElementById('taskTypeBadge');
const bodyEl = document.getElementById('taskDetailBody');
const actionsEl = document.getElementById('taskActions');
if (type === 'ticket') {
titleEl.textContent = '工单 #20240606001';
badgeEl.textContent = '运维工单';
badgeEl.style.background = 'var(--accent-soft)';
badgeEl.style.color = 'var(--accent)';
badgeEl.style.border = '1px solid var(--accent)';
bodyEl.innerHTML = `
<div class="task-info-card">
<div class="tic-title">📋 工单描述</div>
<div class="tic-row"><span class="tic-label">标题</span><span class="tic-value">CEO办公室 - 投屏设备故障</span></div>
<div class="tic-row"><span class="tic-label">类型</span><span class="tic-value">硬件故障</span></div>
<div class="tic-row"><span class="tic-label">优先级</span><span class="tic-value" style="color:var(--danger);">🔴 紧急</span></div>
<div class="tic-row"><span class="tic-label">上报人</span><span class="tic-value">张总秘书 · 王莉</span></div>
<div class="tic-row"><span class="tic-label">上报时间</span><span class="tic-value">2026-06-06 10:20</span></div>
<div class="tic-desc">CEO 办公室无线投屏设备(型号:Microsoft Wireless Display Adapter v2)无法连接,会议即将开始(10:40),急需处理。已尝试重启设备和更换 HDMI 端口,问题依旧。</div>
</div>
<div class="task-info-card">
<div class="tic-title">📍 处理进度</div>
<div class="tic-row"><span class="tic-label">状态</span><span class="tic-value" style="color:var(--warning);">⏳ 待接单</span></div>
<div class="tic-row"><span class="tic-label">接单人</span><span class="tic-value" style="color:var(--text-muted);">未分配</span></div>
<div class="tic-row"><span class="tic-label">SLA</span><span class="tic-value" style="color:var(--danger);">⏰ 剩余 12 分钟</span></div>
</div>
`;
// 2026-07-24 优化:按钮持续显示,已接单时显示"已接单"(禁用状态)
// 待接单:绿色可点击
// 已接单:灰色禁用
actionsEl.innerHTML = `
<button class="btn-action primary" onclick="this.outerHTML='<button class=\\'btn-action primary\\' disabled style=\\'opacity:0.5;cursor:not-allowed;background:#9ca3af;border-color:#9ca3af;\\'>📥 已接单</button>'">📥 接单</button>
<button class="btn-action warning" onclick="alert('开始处理')">🔧 开始处理</button>
<button class="btn-action success" onclick="alert('工单已结单')">✅ 结单</button>
<button class="btn-action default" onclick="alert('已转派')">🔄 转派</button>
`;
} else if (type === 'approval') {
titleEl.textContent = '审批 #APP-20240606003';
badgeEl.textContent = '审批单';
badgeEl.style.background = 'var(--purple-soft)';
badgeEl.style.color = 'var(--purple)';
badgeEl.style.border = '1px solid var(--purple)';
bodyEl.innerHTML = `
<div class="task-info-card">
<div class="tic-title">📝 审批内容</div>
<div class="tic-row"><span class="tic-label">申请人</span><span class="tic-value">李研发经理 · 研发一部</span></div>
<div class="tic-row"><span class="tic-label">审批类型</span><span class="tic-value">IT设备采购</span></div>
<div class="tic-row"><span class="tic-label">申请时间</span><span class="tic-value">2026-06-05 14:30</span></div>
<div class="tic-row"><span class="tic-label">预算</span><span class="tic-value">¥86,40010台 × ¥8,640</span></div>
<div class="tic-desc">
<strong>采购明细:</strong><br>
• MacBook Pro 14" M3 Pro × 10台<br>
• 预计交付日期:2026-06-20<br>
• 用途:新员工入职配发<br>
• 附件:<a href="#" style="color:var(--accent);">采购清单.xlsx</a>
</div>
</div>
<div class="approval-section">
<div class="as-title">✏️ 审批意见(可选)</div>
<textarea class="approval-textarea" placeholder="输入审批意见..."></textarea>
</div>
`;
actionsEl.innerHTML = `
<button class="btn-action success" onclick="alert('已审批通过')">✅ 审批通过</button>
<button class="btn-action danger" onclick="alert('已拒绝审批')">❌ 拒绝审批</button>
<button class="btn-action default" onclick="alert('已转交')">🔄 转交审批</button>
`;
} else if (type === 'device') {
titleEl.textContent = '设备异常 #DEV-20240606007';
badgeEl.textContent = '设备异常';
badgeEl.style.background = 'var(--orange-soft)';
badgeEl.style.color = 'var(--orange)';
badgeEl.style.border = '1px solid var(--orange)';
bodyEl.innerHTML = `
<div class="task-info-card">
<div class="tic-title">🖥 设备状态</div>
<div class="device-status-grid">
<div class="device-status-item">
<div class="dsi-label">设备名称</div>
<div class="dsi-value">会议室A-投影仪</div>
</div>
<div class="device-status-item">
<div class="dsi-label">设备型号</div>
<!-- === 新增 AI 推荐 === -->
<div class="recommend-item" data-ctrl="Ctrl+3">
<div class="rec-header">
<span class="rec-badge" style="background:#22c55e20;color:#22c55e;">知识库匹配</span>
<span class="rec-confidence">88%</span>
</div>
<div class="rec-content" style="font-size:11px;">邮箱登录异常通常原因:密码过期(90天强制更换)、账号临时锁定、客户端配置错误。请先确认用户是否收到密码过期提醒。</div>
</div>
<div class="recommend-item" data-ctrl="Ctrl+4">
<div class="rec-header">
<span class="rec-badge" style="background:#a855f720;color:#a855f7;">相似案例</span>
<span class="rec-confidence">78%</span>
</div>
<div class="rec-content" style="font-size:11px;">打印驱动重装:设置→蓝牙和其他设备→打印机→删除→访问 print-server 下载最新驱动→重新添加。</div>
</div>
<div class="dsi-value">Epson CB-W42</div>
</div>
<div class="device-status-item">
<div class="dsi-label">在线状态</div>
<div class="dsi-value danger">🔴 离线</div>
</div>
<div class="device-status-item">
<div class="dsi-label">最后在线</div>
<div class="dsi-value">2026-06-06 09:15</div>
</div>
<div class="device-status-item">
<div class="dsi-label">IP 地址</div>
<div class="dsi-value">192.168.10.88</div>
</div>
<div class="device-status-item">
<div class="dsi-label">告警次数</div>
<div class="dsi-value warning">3 次/今日</div>
</div>
</div>
<div class="tic-desc" style="margin-top:8px;">
<strong>异常描述:</strong>设备连续 3 次离线,每次恢复后约 10 分钟再次掉线。可能原因:电源接触不良 / 网络端口故障 / 设备固件异常。
</div>
</div>
<div class="task-info-card">
<div class="tic-title">🔧 处理记录</div>
<div class="tic-row"><span class="tic-label">10:15</span><span class="tic-value">尝试 Ping 设备 — 超时</span></div>
<div class="tic-row"><span class="tic-label">10:20</span><span class="tic-value">检查交换机端口 — 指示灯不亮</span></div>
<div class="tic-row"><span class="tic-label">10:25</span><span class="tic-value" style="color:var(--warning);">⏳ 待现场排查物理连接</span></div>
</div>
`;
actionsEl.innerHTML = `
<button class="btn-action primary" onclick="alert('一键开单')">📝 一键开单</button>
<button class="btn-action warning" onclick="alert('已派工给硬件组')">🚚 派工</button>
<button class="btn-action success" onclick="alert('设备已恢复')">✅ 标记恢复</button>
<button class="btn-action default" onclick="alert('已加入巡检计划')">📅 加入巡检</button>
`;
}
}
function showChatView() {
document.getElementById('chatView').classList.remove('hidden');
document.getElementById('taskDetailView').classList.remove('active');
}
/* ================================================================
快速回复 — 三层渐进式导航系统
数据来源:IT支持知识库 180 条模板(2026-04-24
L1(Alt+数字) → L2(数字) → L3(数字) → Enter 填入
================================================================ */
// 知识库数据从 qr_data.js 加载(IT支持知识库2026-4-24.docx 导出)
// 导航状态
let qrState = {
l1Index: -1, // 当前选中的一级分类索引(-1 = 未选)
l2Index: -1, // 当前选中的二级分类索引(-1 = 未选)
l3Index: -1, // 当前选中的三级回复索引(-1 = 未选)
selectedText: '', // 当前选中的回复文本
};
// 快速回复 L1 主类颜色(与 CSS 变量 --qr-c0 ~ --qr-c6 对应)
const qrColors = ['#3b82f6', '#22c55e', '#f97316', '#8b5cf6', '#ef4444', '#0ea5e9', '#6b7280'];
const qrLightColors = ['#dbeafe', '#dcfce7', '#fff7ed', '#ede9fe', '#fee2e2', '#f0fdfa', '#f3f4f6'];
// 判断当前焦点是否在搜索框
function isSearchFocused() {
return document.activeElement === document.getElementById('qrSearchInput');
}
// 渲染 L1 网格
function renderL1() {
const grid = document.getElementById('qrL1Grid');
grid.innerHTML = qrData.map((cat, i) => {
const isActive = qrState.l1Index === i;
return `
<div class="qr-l1-btn${isActive ? ' active' : ''}" data-l1="${i}" onclick="selectL1(${i})">
<span class="l1-num">${i + 1}</span>
<span class="l1-name">${cat.name}</span>
</div>`;
}).join('');
grid.classList.remove('hidden');
}
// 渲染 L2 chip 行
function renderL2() {
const row = document.getElementById('qrL2Row');
const cat = qrData[qrState.l1Index];
if (!cat) { row.classList.add('hidden'); return; }
row.innerHTML = cat.subs.map((sub, i) => {
const isSelected = qrState.l2Index === i;
return `
<div class="qr-l2-chip${isSelected ? ' selected' : ''}" data-l2="${i}" onclick="selectL2(${i})">
<span class="l2-num">${i + 1}</span>
<span>${sub.name}</span>
</div>`;
}).join('');
row.classList.remove('hidden');
}
// 渲染 L3 回复列表
function renderL3() {
const list = document.getElementById('qrL3List');
const cat = qrData[qrState.l1Index];
if (!cat || qrState.l2Index === -1) {
// 还没选到 L2,显示空状态
list.innerHTML = `
<div class="qr-l3-empty">
<span class="empty-icon">👆</span>
<span class="empty-hint">请先在上方选择子分类<br>或按数字键 <span class="key-badge">1</span>~<span class="key-badge">${cat ? cat.subs.length : 'N'}</span> 快速选择</span>
</div>`;
return;
}
const sub = cat.subs[qrState.l2Index];
if (!sub) return;
list.innerHTML = sub.items.map((item, i) => {
const isSelected = qrState.l3Index === i;
return `
<div class="qr-l3-item${isSelected ? ' selected' : ''}" data-l3="${i}" onclick="selectL3(${i})">
<span class="l3-num">${i + 1}</span>
<div class="l3-content">
<div class="l3-title">${item.title}</div>
<div class="l3-preview">${item.content}</div>
</div>
</div>`;
}).join('');
}
// 更新面包屑
function updateBreadcrumb() {
const backBtn = document.getElementById('qrBackBtn');
const pathLabel = document.getElementById('qrPathLabel');
const pathHint = document.getElementById('qrPathHint');
if (qrState.l1Index === -1) {
backBtn.classList.add('hidden');
pathLabel.textContent = '请选择分类';
pathHint.classList.remove('hidden');
pathHint.innerHTML = '<span class="key-badge">Alt</span>+数字 选择分类';
} else {
const cat = qrData[qrState.l1Index];
if (qrState.l2Index === -1) {
backBtn.classList.remove('hidden');
backBtn.textContent = '← 返回分类';
pathLabel.textContent = `${cat.name}`;
pathHint.classList.remove('hidden');
pathHint.innerHTML = `数字 <span class="key-badge">1</span>~<span class="key-badge">${cat.subs.length}</span> 选子类`;
} else {
const sub = cat.subs[qrState.l2Index];
backBtn.classList.remove('hidden');
backBtn.textContent = '← 返回子类';
pathLabel.innerHTML = `${cat.name} <span class="bc-sep"></span> ${sub.name}`;
pathHint.classList.remove('hidden');
pathHint.innerHTML = `数字 <span class="key-badge">1</span>~<span class="key-badge">${sub.items.length}</span> 选回复 · <span class="key-badge">Enter</span> 填入`;
}
}
}
// 更新选中回复预览条
function updateSelectedBar() {
const bar = document.getElementById('qrSelectedBar');
const textEl = document.getElementById('qrSelectedText');
if (qrState.selectedText && qrState.l3Index !== -1) {
bar.classList.remove('hidden');
const cat = qrData[qrState.l1Index];
const sub = cat.subs[qrState.l2Index];
const item = sub.items[qrState.l3Index];
textEl.textContent = item.title + '' + item.content.substring(0, 40) + '...';
} else {
bar.classList.add('hidden');
}
}
// ===== 选择动作 =====
function selectL1(index) {
qrState.l1Index = index;
qrState.l2Index = -1;
qrState.l3Index = -1;
qrState.selectedText = '';
renderL1();
renderL2();
renderL3();
updateBreadcrumb();
updateSelectedBar();
// 设置 L2/L3 继承的主类颜色
const qrSection = document.getElementById('qrSection');
qrSection.style.setProperty('--qr-active-color', qrColors[index]);
qrSection.style.setProperty('--qr-active-light', qrLightColors[index]);
}
function selectL2(index) {
qrState.l2Index = index;
qrState.l3Index = -1;
qrState.selectedText = '';
renderL2();
renderL3();
updateBreadcrumb();
updateSelectedBar();
}
function selectL3(index) {
qrState.l3Index = index;
const cat = qrData[qrState.l1Index];
const sub = cat.subs[qrState.l2Index];
const item = sub.items[index];
qrState.selectedText = item.content;
renderL3();
updateBreadcrumb();
updateSelectedBar();
}
// 将选中回复填入聊天输入框
function qrInsertToChat() {
if (qrState.selectedText) {
document.getElementById('chatInput').value = qrState.selectedText;
document.getElementById('chatInput').focus();
// 闪烁反馈
const bar = document.getElementById('qrSelectedBar');
bar.style.background = 'var(--accent-soft)';
bar.style.color = 'var(--accent)';
setTimeout(() => {
bar.style.background = 'var(--success-soft)';
bar.style.color = 'var(--success)';
}, 300);
}
}
// 返回上一级
function qrGoBack() {
if (qrState.l2Index !== -1) {
// 从 L3 回到 L2
qrState.l2Index = -1;
qrState.l3Index = -1;
qrState.selectedText = '';
} else if (qrState.l1Index !== -1) {
// 从 L2 回到 L1
qrState.l1Index = -1;
qrState.l2Index = -1;
qrState.l3Index = -1;
qrState.selectedText = '';
document.getElementById('qrL2Row').classList.add('hidden');
// 清除 L2/L3 继承的主类颜色
const qrSection = document.getElementById('qrSection');
qrSection.style.removeProperty('--qr-active-color');
qrSection.style.removeProperty('--qr-active-light');
}
renderL1();
renderL2();
renderL3();
updateBreadcrumb();
updateSelectedBar();
}
// ===== 键盘事件 =====
document.addEventListener('keydown', function(e) {
// 状态判断
const chatFocused = document.activeElement === document.getElementById('chatInput');
// "/" 聚焦搜索框(非输入状态)
if (e.key === '/' && !chatFocused && !isSearchFocused()) {
e.preventDefault();
document.getElementById('qrSearchInput').focus();
return;
}
// Alt+数字:选择 L1 一级分类(全局有效,除聊天输入框外)
if (e.altKey && e.key >= '1' && e.key <= '7') {
if (chatFocused) return; // 聊天输入框中不拦截 Alt+数字
e.preventDefault();
const idx = parseInt(e.key) - 1;
if (idx < qrData.length) {
selectL1(idx);
}
return;
}
// 在搜索框内时不处理数字导航
if (isSearchFocused()) return;
// 聊天输入框内不处理快速回复导航
if (chatFocused) return;
// 数字键:逐层选择 L2 或 L3
if (e.key >= '0' && e.key <= '9' && !e.altKey && !e.ctrlKey && !e.metaKey) {
const num = parseInt(e.key);
if (num === 0) return; // 0 不参与导航
if (qrState.l1Index !== -1 && qrState.l2Index === -1) {
// 已选中 L1,数字选择 L2
const cat = qrData[qrState.l1Index];
if (num <= cat.subs.length) {
e.preventDefault();
selectL2(num - 1);
}
} else if (qrState.l1Index !== -1 && qrState.l2Index !== -1) {
// 已选中 L2,数字选择 L3
const cat = qrData[qrState.l1Index];
const sub = cat.subs[qrState.l2Index];
if (num <= sub.items.length) {
e.preventDefault();
selectL3(num - 1);
}
}
return;
}
// Enter:将选中的回复填入聊天输入框
if (e.key === 'Enter' && qrState.selectedText) {
if (chatFocused) return; // 聊天输入框中 Enter 用于发送
e.preventDefault();
qrInsertToChat();
return;
}
// Backspace/Escape:返回上一级
if ((e.key === 'Backspace' || e.key === 'Escape') && (qrState.l1Index !== -1)) {
if (chatFocused) return;
e.preventDefault();
qrGoBack();
return;
}
});
// 初始渲染 — 默认展示第一分类第二层目录效果(方便原型评审)
selectL1(0);
/* ===== v5.4: 拖拽调整中间栏左右边框 ===== */
(function initResizeHandles() {
const leftSidebar = document.querySelector('.sidebar-left');
const rightSidebar = document.querySelector('.sidebar-right');
const handleLeft = document.getElementById('resizeLeft');
const handleRight = document.getElementById('resizeRight');
if (!handleLeft || !handleRight) return;
let dragging = null; // 'left' 或 'right'
let startX = 0;
let startWidth = 0;
function onMouseDown(e, side) {
e.preventDefault();
dragging = side;
startX = e.clientX;
startWidth = side === 'left'
? leftSidebar.offsetWidth
: rightSidebar.offsetWidth;
document.body.style.cursor = 'col-resize';
document.body.style.userSelect = 'none';
// 高亮当前手柄
(side === 'left' ? handleLeft : handleRight).classList.add('dragging');
}
function onMouseMove(e) {
if (!dragging) return;
if (dragging === 'left') {
const dx = e.clientX - startX;
const newW = Math.max(200, Math.min(500, startWidth + dx));
leftSidebar.style.width = newW + 'px';
leftSidebar.style.minWidth = newW + 'px';
} else {
const dx = startX - e.clientX;
const newW = Math.max(220, Math.min(500, startWidth + dx));
rightSidebar.style.width = newW + 'px';
rightSidebar.style.minWidth = newW + 'px';
}
}
function onMouseUp() {
if (!dragging) return;
document.body.style.cursor = '';
document.body.style.userSelect = '';
handleLeft.classList.remove('dragging');
handleRight.classList.remove('dragging');
dragging = null;
}
handleLeft.addEventListener('mousedown', function(e) { onMouseDown(e, 'left'); });
handleRight.addEventListener('mousedown', function(e) { onMouseDown(e, 'right'); });
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
})();
/* ===== v5.4: 输入框上方拖拽手柄(调整输入框高度,textarea 同步伸缩) ===== */
(function initInputResize() {
const inputArea = document.getElementById('chatInputArea');
const handle = document.getElementById('inputResizeHandle');
const chatInput = document.getElementById('chatInput');
if (!inputArea || !handle) return;
let dragging = false;
let startY = 0;
let startHeight = 0;
let startInputHeight = 0;
handle.addEventListener('mousedown', function(e) {
e.preventDefault();
dragging = true;
startY = e.clientY;
startHeight = inputArea.offsetHeight;
startInputHeight = chatInput ? chatInput.offsetHeight : 40;
document.body.style.cursor = 'row-resize';
document.body.style.userSelect = 'none';
handle.classList.add('dragging');
});
document.addEventListener('mousemove', function(e) {
if (!dragging) return;
// 向上拖 → 整个区域变高,向下拖 → 变矮
const dy = startY - e.clientY;
const newH = Math.max(80, Math.min(400, startHeight + dy));
inputArea.style.height = newH + 'px';
// textarea 同步伸缩
if (chatInput) {
const delta = newH - startHeight;
const newInputH = Math.max(60, Math.min(350, startInputHeight + delta));
chatInput.style.height = newInputH + 'px';
}
});
document.addEventListener('mouseup', function() {
if (!dragging) return;
dragging = false;
document.body.style.cursor = '';
document.body.style.userSelect = '';
handle.classList.remove('dragging');
});
})();
</script>
<script>
/* ===== 排查步骤交互:用户端同步逻辑 ===== */
(function() {
var pathSteps = document.querySelectorAll('.path-step-inline');
pathSteps.forEach(function(step, idx) {
step.addEventListener('click', function() {
pathSteps.forEach(function(s, i) {
s.classList.remove('path-step-inline--done', 'path-step-inline--current', 'path-step-inline--pending');
if (i < idx) s.classList.add('path-step-inline--done');
else if (i === idx) s.classList.add('path-step-inline--current');
else s.classList.add('path-step-inline--pending');
});
});
});
})();
</script>
</body>
</html>