${i
${esc(x[0])} ${esc(x[1])} `).join("");
const message = (task.messages && task.messages[0]) || {time:"", text:"队列为空时,选择任务后会在这里显示来源消息。"};
return `
任务详情 客户 ${esc(task.name || "暂无选中任务")}
会话 ID ${esc(task.sessionDisplay || task.sessionId || "--")} ${icon("copy")}
已等待 ${esc(task.wait || "--")}
重试 ${esc(task.retries || "0 次")}
来源消息
${esc(message.time || "")} ${esc(message.text || "")}
${icon("sparkles")} ${esc(task.action || "等待新任务")}
${process}
× 取消任务 ${icon("refresh")}立即重试 ${icon("users")}转人工
`;
}
function schedulePanel(){
const s = (STATE.task && STATE.task.schedule) || {priority:"—", eta:"—", protect:"—", retry:"—"};
return `
调度信息 ${icon("flag")}
优先级 ${esc(s.priority)}
${icon("clock")}
预计完成 ${esc(s.eta)}
${icon("shield")}
人工保护 ${esc(s.protect)}
${icon("refresh")}
下次重试 ${esc(s.retry)}
`;
}
autoView = function(){
if (STATE.demo) return _autoView();
const count = (STATE.people || []).length;
const waiting = (STATE.metrics && STATE.metrics.waiting) || "0";
return `
${head("桌面自动化控制台","监听企业微信未读消息,并由 AI 自动生成与发送回复",connected(`
${icon("send")}自动发送 `,"danger"))}${liveRibbonAuto()}
任务队列 ${esc(waiting)} ${icon("filter")} ${icon("sort")}
${queueList()}${currentTaskCard()}
自动化执行状态 ${esc(STATE.journeyTotal || "")} ${journey()}${windowStatusCard()}
`;
};
queueView = function(){
if (STATE.demo) return _queueView();
const count = (STATE.people || []).length;
const filter = STATE.queueFilter || "全部";
const pills = ["全部","处理中","等待中","待重试"].map(name=>`
${name} `).join("");
return `
${head("任务队列","查看排队、处理中与待重试的自动回复任务",connected(`
${icon("send")}自动发送 `,"danger"))}${liveRibbonQueue()}
全部任务 ${pills}
${queueList()}${queueDetailPanel()}${schedulePanel()}
`;
};
archiveView = function(){
if (STATE.demo) return _archiveView();
const rows = STATE.archives || [];
const chat = STATE.chat || [];
const selected = rows.find(x=>x.key===STATE.selectedArchive) || rows[0] || {};
const chatHtml = chat.length ? chat.map(x=>`
${icon(x.role==="assistant"?"bot":"user")} ${esc(x.time || "")} ${esc(x.text || "")}
`).join("") : `
选择会话后显示聊天记录
`;
const tags = (STATE.memory && STATE.memory.tags || []).map((t,i)=>`
${esc(t)} `).join("") || `
暂无标签 `;
const settle = (STATE.settle || []).map(x=>`
${x.iconHtml || icon("clipboard")}
${esc(x.title)} ${esc(x.hint)}
${esc(x.value)} `).join("");
const filter = STATE.archiveFilter || "全部会话";
const dateValue = STATE.archiveDate || "";
return `
${head("会话归档","保存客户上下文、自动回复与回访线索",`
${esc(STATE.wecomLabel || "企业微信")} ${esc(STATE.aiLabel || "AI 服务")}${icon("download")}导出记录 `)}
会话记录 ${icon("user")}${esc(selected.name || "未选择")} ${esc(selected.count || "0")} 条消息 ${esc(selected.badge || "")} ${icon("clock")}${esc(selected.time || "")}
${chatHtml}
客户记忆 ${tags}
${esc((STATE.memory && STATE.memory.text) || "选择会话后显示长期上下文。")}
${icon("clock")} 上下文 ${esc((STATE.memory && STATE.memory.rounds) || 0)} 轮
最近更新 ${activities("复制全部记录")}`;
};
aiView = function(){
if (STATE.demo) return _aiView();
const ai = STATE.ai || {};
const toolsOn = ai.tools || [true, true, true];
const names = ["挂号登记","客户查询","回访记录"];
const toolIcons = ["clipboard","search","clipboard"];
const tab = STATE.aiTab || "基础设置";
const tabs = ["基础设置","知识与工具","安全策略"].map(n=>`
${n} `).join("");
const lengthIndex = Number(ai.replyLengthIndex ?? 1);
const lengthLabel = ai.replyLength || ["短","中等","长"][lengthIndex] || "中等";
const tempVal = Number(ai.temperature || 0.35);
return `
${head("AI 设置","配置模型、客服人格、知识库与工具能力",`
配置已同步${icon("play")} 测试 AI ${icon("send")}保存并发布 `)}
${tabs}
客服人格 AI
${icon("sparkles")} 实时预览 您好!我是甄养堂${esc(ai.name || "贴心管家")}, 很高兴为您服务 😊
请问有什么可以帮您的吗?
${icon("brain")} 模型引擎 当前模型 ${icon("sparkles")} ${esc(ai.model || "云端模型")}
${icon("tray")} 企业知识库 ${esc(ai.knowledge || "0")}个 MCP 服务
● ${esc(ai.syncAt || "尚未同步")} ✓ ${Number(ai.knowledge||0)?"已连接":"未配置"} ${icon("refresh")}立即同步 ${icon("folder")}管理知识
${icon("link")} MCP 配置 服务数量 ${esc(ai.knowledge || "0")}
工具开关 ${toolsOn.filter(Boolean).length ? "已启用" : "未启用"}
${icon("settings")}编辑服务器 JSON
${icon("shield")} 安全边界 ${icon("shield")} 不做医疗诊断
${icon("pill")} 不修改用药方案
${icon("users")} 高风险转人工
转人工规则 ${icon("shield")} 诊断 ${icon("package")} 用药调整 ${icon("coins")} 投诉退款
${icon("settings")}规则管理
当前模式 ${icon("shield")}严格模式
配置状态 ${statusCells([["cloud", STATE.aiOk ? "云端模型正常" : "AI 服务异常", `最后同步:${esc(ai.syncAt || "尚未同步")}`],["folder","MCP 工具配置",`${esc(ai.knowledge || "0")} 个服务`],["folder",`${esc(ai.toolCount || 0)} 个工具可用`, toolsOn.filter(Boolean).length ? "已启用" : "未启用"],["shield","安全策略已生效","严格模式"]])}`;
};
// Keep the live WebChannel pages on the exact HTML reference structure. Data
// and actions are dynamic, but no extra cards or alternate tab layouts are
// introduced here.
archiveView = function(){
if (STATE.demo) return wireDemoArchive(_archiveView());
const rows = STATE.archives || [];
const chat = STATE.chat || [];
const selected = rows.find(x=>x.key===STATE.selectedArchive) || rows[0] || {};
const chatHtml = chat.length
? chat.map(x=>`
${icon(x.role==="assistant"?"bot":"user")} ${esc(x.time || "")} ${esc(x.text || "").replace(/\n/g," ")}
`).join("")
: `
选择会话后显示聊天记录
`;
const tags = (STATE.memory && STATE.memory.tags || []).map((tag,index)=>`
${esc(tag)} `).join("") || `
暂无标签 `;
const settleTokens = ["▣","☎",icon("user")];
const settleStyles = ["","background:linear-gradient(145deg,#9d7cff,#6e5be5)","background:linear-gradient(145deg,#ffc65a,#ff941f)"];
const settle = (STATE.settle || []).map((item,index)=>`
${settleTokens[index] || icon("clipboard")}
${esc(item.title)} ${esc(item.hint)}
${esc(item.value)} `).join("");
const filter = STATE.archiveFilter || "全部会话";
const now = new Date();
const currentDate = `${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,"0")}-${String(now.getDate()).padStart(2,"0")}`;
const dateLabel = STATE.archiveDate || currentDate;
const filterButtons = ["全部会话","今日","AI 已回复","已转人工"].map(name=>`
${name}${name==="全部会话"?"⌄":""} `).join("");
return `
${head("会话归档","保存客户上下文、自动回复与回访线索",`
${esc(STATE.wecomLabel || "企业微信")} ${esc(STATE.aiLabel || "AI 服务")}${icon("download")}导出记录 `)}
会话记录 ${icon("user")}${esc(selected.name || "未选择")} ${esc(selected.count || "0")} 条消息 ${esc(selected.badge || "")} ${icon("clock")}${esc(selected.time || "")} 更新
${chatHtml}
客户记忆 ${tags}
${esc((STATE.memory && STATE.memory.text) || "选择会话后显示长期上下文。").replace(/\n/g," ")}
◔ 上下文 ${esc((STATE.memory && STATE.memory.rounds) || 0)} 轮
最近更新 ${activities("复制全部记录")}`;
};
aiView = function(){
if (STATE.demo) return wireDemoAi(_aiView());
const ai = STATE.ai || {};
const toolsOn = ai.tools || [true,true,true];
const names = ["挂号登记","客户查询","回访记录"];
const toolTokens = ["▣","⌕","▤"];
const tab = STATE.aiTab || "基础设置";
const tabs = ["基础设置","知识与工具","安全策略"].map(name=>`
${name} `).join("");
const contextValue = Math.max(1,Number(ai.context || 8));
const contextValues = [...new Set([4,8,12,16,20,contextValue])].sort((a,b)=>a-b);
const contextOptions = contextValues.map(value=>`
${value} 轮 `).join("");
const tokenValue = Math.max(50,Number(ai.maxTokens || 500));
const tokenValues = [...new Set([300,500,1000,tokenValue])].sort((a,b)=>a-b);
const tokenOptions = tokenValues.map(value=>`
${value} tokens `).join("");
const lengthIndex = Math.max(0,Math.min(2,Number(ai.replyLengthIndex ?? 1)));
const lengthLabel = ["短","中等","长"][lengthIndex];
const lengthValue = [10,55,90][lengthIndex];
const temperature = Math.max(0,Math.min(1,Number(ai.temperature || 0.35)));
const knowledgeCount = ai.knowledgeCount || ai.knowledge || "0";
const connectedKnowledge = ai.knowledgeConnected !== undefined ? Boolean(ai.knowledgeConnected) : Number(ai.knowledge || 0) > 0;
const syncLabel = ai.syncLabel || ai.syncAt || "尚未同步";
const toolCount = Number(ai.toolCount ?? toolsOn.filter(Boolean).length);
const configLabel = ai.synced === false ? "配置未同步" : "配置已同步";
const knowledgeStatus = connectedKnowledge ? "知识库已同步" : "知识库未连接";
const toolStatus = toolCount === 3 ? "全部运行正常" : `${toolCount} 个已启用`;
return `
${head("AI 设置","配置模型、客服人格、知识库与工具能力",`
${configLabel}▷ 测试 AI ${icon("send")}保存并发布 `)}
${tabs}
客服人格 AI
✧ 实时预览 您好!我是甄养堂${esc(ai.name || "贴心管家")}, 很高兴为您服务 😊
请问有什么可以帮您的吗?
✣ 模型引擎 当前模型 ✦ ${esc(ai.model || "云端模型")}
▰ 企业知识库 ${esc(knowledgeCount)}个知识片段
● ${esc(syncLabel)} ✓ ${connectedKnowledge?"已连接":"未连接"} ${icon("refresh")}立即同步 ${icon("folder")}管理知识
◇ MCP 工具 单次最多调用 ${esc(ai.mcpRounds || 5)} 个工具 ⓘ
${icon("shield")} 安全边界 🛡 不做医疗诊断
💊 不修改用药方案
♙ 高风险转人工
当前模式: ${icon("shield")}严格模式
配置状态 ${statusCells([["cloud",STATE.aiOk===false?"AI 服务异常":"云端模型正常","最后检查:刚刚"],["folder",knowledgeStatus,`${esc(knowledgeCount)} 个知识片段`],["folder",`${toolCount} 个工具可用`,toolStatus],["shield","安全策略已生效","严格模式"]])}`;
};
automationView = function(){
if (STATE.demo) return _automationView();
const a = STATE.automation || {};
const autoOn = (STATE.sendMode || "auto") === "auto";
return `
${head("自动化设置","配置企业微信监听、消息合并与自动发送规则",`
${esc(a.wecom || "企业微信未识别")} 规则已生效${icon("save")}保存设置 `)}
${icon("send")} 自动发送 ${icon("users")} 审核后发送
监听与发送策略 ${icon("activity")} ● 规则运行中 ${[["activity","扫描未读"],["log","提取消息"],["folder","合并消息"],["brain","请求 AI"],["send","回填发送"]].map(x=>`
`).join("")}
企业微信窗口 ${icon("clipboard")} 主窗口: ● ${esc(a.wecom || "未识别")}
${icon("user")} 当前账号: ${esc(a.account || "—")}
${icon("globe")} DPI: ${esc(STATE.dpi || "—")}
${icon("monitor")} 最小化恢复: ● 已开启
${icon("refresh")}重新检测
人机共存 鼠标静止 秒 后恢复
${icon("bot")}AI 运行中 ${icon("cursor")}
${icon("pause")}检测到人工操作 AI 暂停
消息处理 ✓ 同一客户连续消息自动合并
✓ 保留最近 轮上下文 ${esc(a.context || 8)} 轮
✓ 一次只处理 1 个会话 1 个
异常与兜底 ${icon("clock")} AI 超时:${esc(a.timeout || 120)} 秒后重试 2 次
${icon("alert")} 发送失败:自动重试 已开启
${icon("message")} 固定回复:${esc(a.reply || "在的,请稍等")} 编辑
高风险转人工 ● 已开启 ${icon("shield")} 诊断 ${icon("package")} 用药调整 ${icon("coins")} 投诉退款
${icon("settings")}规则管理
启动前检查 ${statusCells([["check","企业微信已登录", STATE.wecomConnected ? "检查完成" : "等待连接"],["cloud","AI 服务正常","检查完成"],["activity","窗口坐标已校准", STATE.wecomConnected ? "检查完成" : "待校准"],["shield","规则配置有效","检查完成"]])}`;
};
logsView = function(){
if (STATE.demo) return _logsView();
const events = STATE.logs || [];
const list = events.length ? events.map(x=>`
✓
${esc(x.time)} ● ${icon(x.icon || "activity")}
${esc(x.title)} ${esc(x.detail)}
${esc(x.state || "")} `).join("") : `
当前没有运行记录
`;
const ex = STATE.exceptions || {};
const detail = STATE.logDetail || {title:"等待事件", text:"开始监听后,这里会显示最近一条运行事件。", tags:[]};
const chain = (STATE.chain && STATE.chain.length ? STATE.chain : [{icon:"activity",title:"扫描未读",time:""},{icon:"message",title:"读取消息",time:""},{icon:"brain",title:"请求 AI",time:""},{icon:"send",title:"回填发送",time:""}]);
return `
${head("运行日志","实时追踪连接、消息识别、AI 调用与自动发送事件",`
${icon("refresh")}实时刷新 ${icon("download")}导出日志 ${icon("tray")} 清空日志 ${esc(STATE.statusLabel || "监控中")}`)}${liveRibbonLogs()}
实时事件流 ${["全部","连接","识别","AI","发送","警告"].map(n=>`${n} `).join("")}${icon("search")}
${list}
系统健康 ${[["企业微信连接", STATE.wecomConnected ? "正常" : "待连接"],["AI 服务", STATE.aiOk ? "正常" : "异常"],["窗口识别", STATE.wecomConnected ? "正常" : "待监听"]].map(x=>`
${icon("check")}
${x[0]} ${x[1]} `).join("")}
${icon("clock")}运行时长 ${esc(STATE.runtime || "00:00:00")}
当前回复链路 ${chain.map((x,i)=>`
${icon(x.icon || "activity")}
${esc(x.title)} ${esc(x.time || "")} `).join("")}
事件详情 ${icon("user")}
${esc(detail.title)} ${esc(detail.text)}
${(detail.tags||[]).map(t=>`${esc(t)} `).join("")}
异常追踪 ${icon("alert")}
发送失败${esc(ex.sendFail || 0)}
${icon("clock")}
模型超时${esc(ex.timeout || 0)}
${icon("monitor")}
窗口丢失${esc(ex.windowLost || 0)}
查看异常 ›
最近活动 ${activities("查看完整记录")}`;
};
systemView = function(){
if (STATE.demo) return _systemView();
const s = STATE.system || {};
const on = k => s[k] ? "on" : "";
return `
${head("系统设置","管理软件启动、数据存储、通知与系统维护",`
当前版本 v${esc(s.version || "2.0")} ${icon("info")} ${icon("refresh")}检查更新 ${icon("save")}保存设置 `)}
基础设置 ${icon("activity")}
启动后自动监听
${icon("log")}
关闭窗口时保持后台运行
${icon("globe")}界面语言 简体中文
${icon("monitor")}缩放比例 ${["自动","100%","125%","150%","200%"].map(n=>`${n} `).join("")}
数据与存储 ${esc(s.storage || "—")}已使用
● 会话归档 ${esc(s.archiveSize || "—")}
● 运行日志 ${esc(s.logSize || "—")}
● 缓存数据 ${esc(s.cacheSize || "—")}
数据保留 天
${icon("folder")}打开目录 ${icon("cloud")}立即备份 ${icon("wifi")} 清理缓存
通知提醒 ${icon("hourglass")}
模型超时提醒
${icon("bell")}
发送失败提醒 消息发送失败,已加入重试队列
${esc(s.noticeTime || "—")}●
网络与服务 ${icon("cloud")}云端 AI: ${esc(s.cloudAi || "未检测")}
${icon("folder")}企业知识库: ${esc(s.knowledgeNet || "未检测")}
${icon("wifi")}网络延迟: ${esc(s.latency || "—")}
${icon("wifi")} 连接测试
维护与诊断 ${icon("refresh")}
检查更新 ${esc(s.updateLabel || "已是最新版本")}
${icon("clipboard")}
导出诊断 导出系统诊断报告
${icon("refresh")}
恢复默认设置 重置所有设置项
${icon("shield")}
查看许可信息 查看软件许可详情
系统状态 ${statusCells([["shield","配置已保存","所有设置已生效"],["cloud","最近备份", esc(s.lastBackup || "尚未备份")],["shield","安全策略正常","所有安全策略已启用"],["monitor","后台服务", STATE.listening ? "运行中" : "已停止"]])}`;
};
views = {auto:autoView,queue:queueView,archive:archiveView,ai:aiView,automation:automationView,logs:logsView,system:systemView};
function syncOrb(){
const orb = document.getElementById("orb");
const label = document.getElementById("orb-label");
if (orb) orb.classList.toggle("paused", !STATE.listening);
if (label) label.textContent = STATE.statusLabel || (STATE.listening ? "监听中" : "已停止");
}
function coreFingerprint(state){
const sys = state.system || {};
const ai = state.ai || {};
const auto = state.automation || {};
const metrics = state.metrics || {};
const view = state.view || "auto";
const common = {
view: state.view,
demo: state.demo,
listening: state.listening,
wecomConnected: state.wecomConnected,
aiOk: state.aiOk,
wecomLabel: state.wecomLabel,
aiLabel: state.aiLabel,
};
const people = (state.people || []).map(p => [
p.key, p.name, p.status, p.bubbles, p.avatar, p.tone, p.dot
]);
const task = state.task ? {
key: state.task.key,
name: state.task.name,
sessionId: state.task.sessionId,
sessionDisplay: state.task.sessionDisplay,
action: state.task.action,
stepIndex: state.task.stepIndex,
answer: state.task.answer,
messageCount: state.task.messageCount,
confidence: state.task.confidence,
messages: (state.task.messages || []).map(message => [message.time, message.text]),
steps: (state.task.steps || []).map(step => [step[0], step[1]]),
} : null;
const metricModes = [Number(metrics.failed) > 0, Number(metrics.warnings) > 0];
let page;
if (view === "auto") {
page = {
selectedKey: state.selectedKey,
queueFilter: state.queueFilter,
queueSort: state.queueSort,
people,
task,
journey: (state.journey || []).map(j => [j.title, j.status, j.mode, j.done, j.active]),
windowStats: state.windowStats,
metricModes,
};
} else if (view === "queue") {
page = {
selectedKey: state.selectedKey,
queueFilter: state.queueFilter,
queueSort: state.queueSort,
people,
task,
metricModes,
};
} else if (view === "archive") {
page = {
archiveFilter: state.archiveFilter,
archiveQuery: state.archiveQuery,
archiveDate: state.archiveDate,
selectedArchive: state.selectedArchive,
archives: (state.archives || []).map(a => [
a.key, a.badge, a.count, a.name, a.time, a.preview, a.avatar, a.tone
]),
chat: (state.chat || []).map(message => [message.role, message.time, message.text]),
memory: state.memory,
settle: state.settle,
};
} else if (view === "ai") {
page = {aiTab: state.aiTab, ai};
} else if (view === "automation") {
page = {sendMode: state.sendMode, dpi: state.dpi, auto, tray: sys.tray};
} else if (view === "logs") {
page = {
statusLabel: state.statusLabel,
eventFilter: state.eventFilter,
eventQuery: state.eventQuery,
exceptions: state.exceptions,
logDetail: state.logDetail,
chain: (state.chain || []).map(item => [item.icon, item.title]),
metricModes,
};
} else {
const {
storage: _storage,
archiveSize: _archiveSize,
logSize: _logSize,
cacheSize: _cacheSize,
noticeTime: _noticeTime,
...systemStructure
} = sys;
page = {system: systemStructure};
}
return JSON.stringify({common, page});
}
function logsFingerprint(state){
return JSON.stringify((state.logs || []).map(x => [x.time, x.title, x.detail, x.state]));
}
function logRowsHtml(events){
events = (events || []).slice(-5);
if (!events.length){
return `
当前没有运行记录
`;
}
return events.map(x=>`
✓
${esc(x.time)} ● ${icon(x.icon || "activity")}
${esc(x.title)} ${esc(x.detail)}
${esc(x.state || "")} `).join("");
}
function wireDemoArchive(html){
let rowIndex = 0;
return html
.replace('data-action="toast">' + icon("download") + '导出记录', 'data-action="export-archive">' + icon("download") + '导出记录')
.replace('
', '
')
.replace('
全部会话⌄ ', '
全部会话⌄ ')
.replace('
今日 ', '
今日 ')
.replace('
AI 已回复 ', '
AI 已回复 ')
.replace('
已转人工 ', '
已转人工 ')
.replace('
2026-08-18 □ ', '
2026-08-18 □ ')
.replace(/
/g, (_match, classes) => `
`)
.replace('
' + icon("refresh") + ' ', '
' + icon("refresh") + ' ')
.replace('data-action="logs">复制全部记录', 'data-action="copy-archive">复制全部记录');
}
function wireDemoAi(html){
const activeTab = STATE.aiTab || "基础设置";
let toolIndex = 0;
return html
.replace('
配置已同步', '
配置已同步')
.replace('
▷ 测试 AI ', '
▷ 测试 AI ')
.replace('
' + icon("send") + '保存并发布 ', '
' + icon("send") + '保存并发布 ')
.replace('
基础设置 知识与工具 安全策略
', `
${["基础设置","知识与工具","安全策略"].map(name=>`${name} `).join("")}
`)
.replace('
', '
')
.replace('