// Three main views: DR Roster, Alert Triage, Dashboard Overview function FilterChip({ active, onClick, children, dotColor }) { return ( ); } function TopBar({ now, activeTab = "dashboard", onSelectTab }) { const time = now.toLocaleTimeString("zh-CN", { hour12: false }); return (
群聊监控
DR · 客户对话健康度
实时同步 {time} · {MOCK.GLOBAL.totalGroups.toLocaleString()} 群在册 · {MOCK.GLOBAL.activeToday} 今日活跃
); } function FilterBar({ view, setView, drFilter, setDrFilter, timeRange, setTimeRange, statusFilter, setStatusFilter }) { return (
时间 {[["today","今日"],["week","本周"],["month","本月"]].map(([k,v]) => ( setTimeRange(k)}>{v} ))}
状态 setStatusFilter("all")}>全部 setStatusFilter("critical")} dotColor="var(--critical)">紧急 setStatusFilter("warning")} dotColor="var(--warning)">告警 setStatusFilter("silent")} dotColor="var(--silent)">沉默
DR setDrFilter("all")}>全部 10 人 {MOCK.DRS.slice(0, 5).map(d => ( setDrFilter(d.id)}> {d.name} ))} {}} active={false}>+5
); } function StatStrip() { const G = MOCK.GLOBAL; return (
); } function RosterView({ density, drFilter, statusFilter, onOpenDr, onOpenGroup }) { let drs = MOCK.DRS; if (drFilter !== "all") drs = drs.filter(d => d.id === drFilter); // sort by severity (criticals first, then warnings, then by score asc) drs = [...drs].sort((a, b) => b.critical - a.critical || b.warning - a.warning || a.score - b.score ); return (
{drs.map(dr => ( ))}
); } function AlertsView({ statusFilter, drFilter, onOpenGroup }) { let alerts = MOCK.ALERTS; if (statusFilter !== "all") { alerts = alerts.filter(a => statusFilter === "critical" ? a.severity === "critical" : statusFilter === "warning" ? (a.severity === "warning" && a.type !== "silent") : statusFilter === "silent" ? a.type === "silent" : true ); } if (drFilter !== "all") alerts = alerts.filter(a => a.drId === drFilter); const grouped = { critical: alerts.filter(a => a.severity === "critical"), warning: alerts.filter(a => a.severity === "warning" && a.type !== "silent"), silent: alerts.filter(a => a.type === "silent"), }; const sectionDef = [ { key: "critical", title: "紧急 · 立即处理", desc: "客户消息命中风险关键词或长时间未回复", color: "var(--critical)" }, { key: "warning", title: "告警 · 待跟进", desc: "DR 响应超出 SLA", color: "var(--warning)" }, { key: "silent", title: "沉默 · 关注", desc: "DR 已超过 24 小时未在群内发言", color: "var(--silent)" }, ]; return (
{sectionDef.map(s => grouped[s.key].length > 0 && (
{s.title}
{grouped[s.key].length} 项
{s.desc}
{grouped[s.key].slice(0, 12).map(a => ( onOpenGroup(a.group)} /> ))} {grouped[s.key].length > 12 && (
还有 {grouped[s.key].length - 12} 项 · 展开全部 →
)}
))} {sectionDef.every(s => grouped[s.key].length === 0) && (
所有群组当前状态良好
无紧急告警、超时或沉默情况
)}
); } function OverviewView({ aiOn, onOpenDr, onOpenGroup }) { const G = MOCK.GLOBAL; const drsRanked = [...MOCK.DRS].sort((a, b) => a.score - b.score); const nowHr = new Date().getHours(); return (
{aiOn && (
今日态势:共发现 {G.criticalAlerts} 起紧急告警与 {G.warningAlerts} 起一般告警。 周梓涵 名下 云汐科技-项目交付群 客户连续两次提到「退款」,建议优先介入; 黄俊杰 名下有 4 个群已超 24 小时无 DR 发言,请确认是否在休假。 全员平均响应时间为 {G.avgResponseMin} 分钟,较上周下降 12%。
)}
{/* DR 排行 */}
DR 健康度排行
10 人 · 升序
分值越低越需关注
#DR 14天活跃 响应/告警 分值
{drsRanked.map((d, i) => (
onOpenDr(d)}>
{String(i + 1).padStart(2, "0")}
{d.name}
{d.activeGroups} 群 · {d.todayMessages} 消息
{d.avgResponseMin}m {(d.critical + d.warning) > 0 && ( 0 ? "var(--critical)" : "var(--warning)", marginLeft: 6 }}> ⚠{d.critical + d.warning} )}
{d.score}
))}
{/* Right column */}
24h 消息流量
总 {G.todayMessages} 条
{G.hourly.map((v, i) => { const max = Math.max(...G.hourly); return (
); })}
0006121823
状态分布
{MOCK.GROUPS.length} 活跃群
{[ { k: "critical", l: "紧急", n: MOCK.GROUPS.filter(g => g.status === "critical").length, c: "var(--critical)" }, { k: "warning", l: "告警", n: MOCK.GROUPS.filter(g => g.status === "warning").length, c: "var(--warning)" }, { k: "silent", l: "沉默", n: MOCK.GROUPS.filter(g => g.status === "silent").length, c: "var(--silent)" }, { k: "healthy", l: "正常", n: MOCK.GROUPS.filter(g => g.status === "healthy").length, c: "var(--healthy)" }, ].map(s => { const pct = (s.n / MOCK.GROUPS.length) * 100; return (
{s.l}
{s.n} · {pct.toFixed(0)}%
); })}
{/* Top alert preview */}
需立即处理
最紧急 6 项
{MOCK.ALERTS.slice(0, 6).map(a => ( onOpenGroup(a.group)} /> ))}
); } // Drawer contents function DRDetailContent({ dr, onOpenGroup, aiOn }) { const groups = MOCK.groupsForDr(dr.id); const [tab, setTab] = useState("groups"); return (
{/* DR header summary */}
{dr.name}
活跃 {dr.activeGroups} 群 · 总计 {dr.totalGroups} · 最近活跃 {fmtMin(dr.lastActiveMinutes)}
{dr.score}
健康度
{aiOn && (
{dr.name} 今日共参与 {dr.todayMessages} 条消息,回复 {dr.todayReplies} 条; {dr.critical > 0 && {dr.critical} 个群处于紧急状态,} {dr.silent > 0 && {dr.silent} 个群已沉默超过 24 小时,} 建议优先处理列表顶部的标红群组。
)}
{tab === "groups" && (
{groups.map(g => onOpenGroup(g)} />)}
)} {tab === "stats" && (
14 日消息趋势
紧急
{dr.critical}
告警
{dr.warning}
沉默
{dr.silent}
正常
{dr.healthy}
)}
); } function GroupConvContent({ group, aiOn }) { const dr = MOCK.DRS.find(d => d.id === group.drId); const conv = MOCK.conversationFor(group.id); return (
{group.name}
{group.members} 人 · 客户主联系人:{group.customerName}
{group.alerts.length > 0 && (
{group.alerts.map((a, i) => (
⚠ {a.label}
))}
)} {aiOn && (
对话摘要:客户主要咨询集成方案进度,并追问报表 bug。 {group.status === "critical" && 最新一条消息含负面情绪关键词,需立即跟进。} {group.status === "warning" && 客户已等待多次,建议尽快给出明确时间。}
)}
最近对话 · {conv.length} 条
{conv.map((m, i) => { const flagged = m.who === "customer" && i === conv.length - 1 && group.status === "critical"; return (
{m.who === "dr" ? dr.initial : group.customerName[0]}
{m.text}
{m.author} · {fmtMin(m.minutesAgo)}
); })}
); } Object.assign(window, { TopBar, FilterBar, StatStrip, RosterView, AlertsView, OverviewView, DRDetailContent, GroupConvContent });