Files
kefu/deploy/review-assistant-20260917/source-js.diff
T
2026-09-21 10:34:06 +08:00

53 lines
21 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
--- C:\kefu\wechat_rpa\backups\review-assistant-20260917\assets\ui\console-app.js
+++ C:\kefu\wechat_rpa\assets\ui\console-app.js
@@ -469,11 +469,32 @@
return `<div class="screen">${head("AI 设置","配置模型、客服人格、知识库与工具能力",`<span class="btn" data-config-status="1"><i class="led"></i>${configLabel}</span><button class="btn" data-action="test-ai">▷ 测试 AI</button><button class="btn primary" data-action="save-ai">${icon("send")}保存并发布</button>`)}<div class="tabs">${tabs}</div><div class="ai-top"><section class="panel panel-pad"><div class="panel-title"><h2>客服人格</h2></div><div class="ai-profile"><div class="ai-ball">AI</div><div class="stack"><div class="field"><label>客服名称:</label><input class="input" id="ai-name" value="${esc(ai.name || "贴心管家")}" maxlength="40"></div><div class="toolbar"><b style="font-size:13px">服务风格:</b><span class="badge green">温和</span><span class="badge">专业</span><span class="badge" style="color:#7657e7;background:rgba(125,91,236,.08)">简洁</span></div><div class="field"><label>人格提示词:</label><textarea class="textarea" id="ai-prompt" readonly aria-readonly="true">${esc(ai.prompt || "")}</textarea></div></div><div class="preview-box"><b>✧ 实时预览</b><p>您好!我是甄养堂${esc(ai.name || "贴心管家")},<br>很高兴为您服务 😊</p><p>请问有什么可以帮您的吗?</p></div></div></section><section class="panel panel-pad"><div class="panel-title"><h2>✣ 模型编排</h2><small>由后台决定,本机只执行</small></div><div class="stack" style="gap:7px"><div class="system-line"><span>答题模型:</span><b>${esc(plan.answer || "后台未配置")}</b></div><div class="system-line"><span>裁判模型:</span><b>${esc(plan.judge || "未启用")}</b></div><div class="system-line"><span>裁判模式:</span><b>${esc(plan.mode || "—")}</b></div><div class="system-line"><span>编排版本:</span><b>${plan.version ? "v" + esc(plan.version) : "—"}</b></div><div class="system-line"><span>上下文轮数:</span><b>${esc(ai.context || 8)} 轮</b></div></div><div class="tiny" style="margin-top:10px;word-break:break-all">网关:${esc(plan.gateway || "未下发——模型调用会失败,请去「系统设置 → 后台连接」")}</div><div class="tiny" style="margin-top:6px">最后同步:${esc(plan.syncedAt || "尚未同步")}</div><div class="tiny" style="margin-top:10px;line-height:1.7">模型接口、密钥、温度、最大 tokens 现在统一在后台的<b>「AI 模型 → 模型清单 / 角色编排」</b>里配。<br>上下文轮数和 MCP 在<b>「桌面端 → 客户端配置」</b>里配,随配置下发——在这台机器上改会被下一次同步覆盖。</div></section></div><div class="ai-bottom"><section class="panel panel-pad"><div class="panel-title"><h2>▰ 企业知识库</h2></div><div class="knowledge"><div class="donut"><strong>${esc(knowledgeCount)}<small>个知识片段</small></strong></div><div class="stack"><span class="badge green">● ${esc(syncLabel)}</span><span class="badge green">✓ ${connectedKnowledge?"已连接":"未连接"}</span><button class="btn small primary" data-action="sync-knowledge">${icon("refresh")}立即同步</button><button class="btn small" data-action="edit-knowledge">${icon("folder")}管理知识</button></div></div></section><section class="panel panel-pad"><div class="panel-title"><h2>◇ MCP 工具</h2><small>单次最多调用 ${esc(ai.mcpRounds || 5)} 轮 · 由后台下发</small></div><div class="tool-grid">${names.map((name,index)=>`<div class="tool-card"><div class="tool-icon">${toolTokens[index]}</div><b>${name}</b><div class="row"><small>${toolsOn[index]?"已启用":"未启用"}</small><div class="switch ${toolsOn[index]?"on":""} switch-locked" title="由后台「桌面端 → 客户端配置」决定"></div></div></div>`).join("")}</div><div class="tiny" style="margin-top:10px;line-height:1.7">工具清单和调用轮数在后台的<b>「桌面端 → 客户端配置 → MCP 服务器」</b>里配,随配置下发。<br>在这台机器上改会被下一次同步覆盖,所以这里只展示不修改。</div></section><section class="panel panel-pad"><div class="panel-title"><h2>${icon("shield")} 选择性审核规则</h2><small>由后台决定,本机只执行</small></div><div class="stack">${reviewRulesHtml}</div><div class="tiny" style="margin-top:8px;line-height:1.7">命中任意一条,这一条回复才会停下来等人工确认;其余照常自动发送。模型裁判判定为高风险时同样会转人工,那一条不在这份清单里、关不掉。</div><div class="tiny" style="margin-top:4px">在后台的<b>「桌面端 → 客户端配置 → 审核规则」</b>里配置,随配置下发。</div></section><section class="panel panel-pad"><div class="panel-title"><h2>发送模式</h2></div><div class="row" style="height:100%;align-items:center"><span class="badge ${blanketReview?"orange":"green"}">${icon("shield")}${blanketReview?"全部审核后发送":"自动发送(命中规则单独审核)"}</span></div><div class="tiny" style="margin-top:8px">在「自动化设置」页顶部切换;这个总开关和上面的选择性规则是 OR 的关系,任一个成立都会拦下。</div></section></div><section class="panel panel-pad config-strip"><div class="panel-title"><h2>配置状态</h2></div>${statusCells([["cloud",STATE.aiOk===false?"AI 服务异常":"云端模型正常","最后检查:刚刚"],["folder",knowledgeStatus,`${esc(knowledgeCount)} 个知识片段`],["folder",`${toolCount} 个工具可用`,toolStatus],["shield","安全策略已生效","严格模式"]])}</section></div>`;
};
+function reviewAssistantPanel(a){
+ const r = a.reviewAssistant || {};
+ const recipient = r.recipient || {};
+ const busy = Boolean(r.busy);
+ const canChoose = r.ready && !busy;
+ const canToggle = canChoose && (recipient.id || r.enabled);
+ return `<section class="panel panel-pad" style="margin:16px 0"><div class="panel-title"><h2>医疗助理审核提醒</h2><span class="badge ${r.enabled ? "green" : ""}">${r.enabled ? "当前账号已开启" : "当前账号未开启"}</span></div>
+ <p>客户会话进入待审核时,由当前托管企业微信账号向指定助理的私人微信发送客户咨询、待发送回复和审核原因。</p>
+ <div class="setting-line"><span>托管企微账号(最近核验)</span><strong>${esc(r.accountName || "尚未核验")}</strong>${r.accountId ? `<small>${esc(r.accountId)}</small>` : ""}</div>
+ <div class="setting-line"><span>助理私人微信</span><div style="flex:1;text-align:right"><strong>${esc(recipient.name || "尚未选择")}</strong>${recipient.label ? `<div class="tiny">${esc(recipient.label)}</div>` : ""}</div></div>
+ <div class="toolbar" style="margin:12px 0;flex-wrap:wrap"><button class="btn small" data-action="review-assistant-refresh" ${!r.supported || busy ? "disabled" : ""}>${icon("refresh")}${busy ? "正在核验…" : "读取当前账号"}</button>
+ <button class="btn small blue" data-action="review-assistant-choose" ${canChoose ? "" : "disabled"}>${recipient.id ? "更换助理联系人" : "选择助理联系人"}</button>
+ <span style="margin-left:auto">开启审核提醒</span><button class="switch ${r.enabled ? "on" : ""}" role="switch" aria-label="开启医疗助理审核提醒" aria-checked="${Boolean(r.enabled)}" data-action="review-assistant-toggle" ${canToggle ? "" : "disabled"}></button></div>
+ ${r.reason ? `<p class="tiny" style="color:var(--orange)">${esc(r.reason)}</p>` : ""}
+ ${r.message ? `<p class="tiny" role="status">${esc(r.message)}</p>` : ""}
+ ${r.notification && r.notification.message ? `<p class="tiny">最近提醒(读取账号时刷新):${esc(r.notification.message)}</p>` : ""}
+ <p class="tiny">设置按企微账号分别保存。选择或更换联系人后默认关闭,确认收件人后再开启;同名联系人按列表中的联系编号区分。</p>
+ <p class="tiny">人工用同一企业微信账号直接回复客户后结束本次审核;后续自动回复仍逐条风控。此识别以账号发出的消息为准,不区分手机或电脑。在助理私人微信回复提醒,不会解除客户审核。</p>
+ <details style="margin-top:12px"><summary style="cursor:pointer">提醒内容示例(只读,不会发送)</summary><pre style="white-space:pre-wrap;word-break:break-word;margin:12px 0 0;font:inherit;font-size:12px">${esc(r.preview || "请先读取当前账号")}</pre></details></section>`;
+}
+
automationView = function(){
if (STATE.demo) return _automationView();
const a = STATE.automation || {};
const autoOn = (STATE.sendMode || "auto") === "auto";
- return `<div class="screen">${head("自动化设置","配置企业微信监听、消息合并与自动发送规则",`<span class="btn"><i class="led"></i>${esc(a.wecom || "企业微信未识别")}</span><span class="btn"><i class="led"></i>规则已生效</span><button class="btn primary" data-action="save-automation">${icon("save")}保存设置</button>`)}<div class="tabs"><button class="tab ${autoOn?"active":""}" data-action="send-mode" data-key="auto">${icon("send")} 自动发送</button><button class="tab ${autoOn?"":"active"}" data-action="send-mode" data-key="review">${icon("users")} 审核后发送</button></div><div class="automation-top"><section class="panel panel-pad"><div class="panel-title"><h2>监听与发送策略</h2></div><span class="badge green">${icon("activity")} ● 规则运行中</span><div class="auto-flow">${[["activity","扫描未读"],["log","提取消息"],["folder","合并消息"],["brain","请求 AI"],["send","回填发送"]].map(x=>`<div class="flow-step"><div class="flow-circle">${icon(x[0])}</div><strong>${x[1]}</strong></div>`).join("")}</div><div class="form-grid"><div class="flow-pill">${icon("clock")}扫描间隔 <input class="num" id="auto-poll" type="number" min="0.2" max="300" step="0.1" value="${esc(a.poll || "2")}"> 秒</div><div class="flow-pill">${icon("hourglass")} 连续消息等待 <input class="num" id="auto-batch" type="number" min="1" max="120" step="1" value="${esc(a.batch || "20")}"> 秒</div><div class="flow-pill">${icon("clock")}发送前等待 <input class="num" id="auto-delay" type="number" min="0" max="30" step="0.5" value="${esc(a.delay || "1")}"> 秒</div></div></section><section class="panel panel-pad"><div class="panel-title"><h2>企业微信窗口</h2></div><div class="wechat-graphic"><div class="fake-window"><div class="fake-check">✓</div></div><div class="stack"><div class="row"><span>${icon("clipboard")} 主窗口:</span><b style="color:var(--green)">● ${esc(a.wecom || "未识别")}</b></div><div class="row"><span>${icon("user")} 当前账号:</span><b>${esc(a.account || "—")}</b></div><div class="row"><span>${icon("globe")} DPI:</span><b>${esc(STATE.dpi || "—")}</b></div><div class="row"><span>${icon("monitor")} 最小化恢复:</span><b style="color:var(--green)">● 已开启</b></div></div></div><div class="task-actions"><button class="btn small blue" data-action="redetect">${icon("refresh")}重新检测</button></div></section></div><div class="automation-mid"><section class="panel panel-pad"><div class="panel-title"><h2>人机共存</h2></div><div class="setting-line"><span>启用人工操作保护</span><div class="switch ${a.mouseProtect?"on":""}" data-action="toggle-idle"></div></div><div style="text-align:center;margin-top:8px">鼠标静止 <input class="idle-num" id="auto-idle" type="number" min="0" max="3600" step="0.5" value="${esc(a.idle || "5")}"> 秒 后恢复</div><div class="setting-line" style="margin-top:10px"><span>草稿超时直发</span><div class="switch ${a.draftAutosend > 0 ? "on" : ""}" data-action="toggle-draft-autosend"></div></div><div style="text-align:center;margin-top:6px">输入框里有人工草稿 <input class="idle-num" id="auto-draft-autosend" type="number" min="1" max="1440" step="5" value="${esc(a.draftAutosend > 0 ? a.draftAutosend : 15)}" ${a.draftAutosend > 0 ? "" : "disabled"}> 分钟后直接发出</div><div class="tiny" style="text-align:center;margin-top:4px;color:var(--muted);font-size:11px">关闭时只跳过该会话、绝不动别人写的字;命中审核规则的仍会等人确认</div><div class="mouse-orb"><span style="color:#315fec">${icon("bot")}<br><small>AI 运行中</small></span><div class="mouse-pointer">${icon("cursor")}</div><span style="color:var(--orange)">${icon("pause")}<br><small>检测到人工操作<br>AI 暂停</small></span></div></section><section class="panel panel-pad"><div class="panel-title"><h2>消息处理</h2></div><div class="stack"><div class="setting-line"><span>✓ 同一客户连续消息自动合并</span></div><div class="setting-line"><span>✓ 保留最近 <input class="ai-num" id="auto-context" type="number" min="1" max="50" value="${esc(a.context || 8)}"> 轮上下文</span><span class="badge">${esc(a.context || 8)} 轮</span></div><div class="setting-line"><span>✓ 一次只处理 1 个会话</span><span class="badge">1 个</span></div></div></section><section class="panel panel-pad"><div class="panel-title"><h2>异常与兜底</h2></div><div class="stack"><div class="setting-line"><span style="color:var(--orange)">${icon("clock")}</span><span style="flex:1">AI 超时:${esc(a.timeout || 120)} 秒后重试</span><span class="badge">2 次</span></div><div class="setting-line"><span style="color:var(--orange)">${icon("alert")}</span><span style="flex:1">发送失败:自动重试</span><span class="badge green">已开启</span></div><div class="setting-line"><span style="color:#3c69f4">${icon("message")}</span><span style="flex:1">固定回复:${esc(a.reply || "在的,请稍等")}</span><button class="badge" data-action="edit-reply">编辑</button></div></div></section><section class="panel panel-pad"><div class="panel-title"><h2>高风险转人工</h2><span class="badge green">● 已开启</span></div><div class="risk-tags"><span class="risk-tag">${icon("shield")} 诊断</span><span class="risk-tag">${icon("package")} 用药调整</span><span class="risk-tag">${icon("coins")} 投诉退款</span></div><button class="btn small blue" style="margin:11px auto 0;display:flex" data-action="security-rules">${icon("settings")}规则管理</button></section></div><section class="panel panel-pad check-strip"><div class="panel-title"><h2>启动前检查</h2></div>${statusCells([["check","企业微信已登录", STATE.wecomConnected ? "检查完成" : "等待连接"],["cloud","AI 服务正常","检查完成"],["activity","窗口坐标已校准", STATE.wecomConnected ? "检查完成" : "待校准"],["shield","规则配置有效","检查完成"]])}</section></div>`;
+ return `<div class="screen">${head("自动化设置","配置企业微信监听、消息合并与自动发送规则",`<span class="btn"><i class="led"></i>${esc(a.wecom || "企业微信未识别")}</span><span class="btn"><i class="led"></i>规则已生效</span><button class="btn primary" data-action="save-automation">${icon("save")}保存设置</button>`)}<div class="tabs"><button class="tab ${autoOn?"active":""}" data-action="send-mode" data-key="auto">${icon("send")} 自动发送</button><button class="tab ${autoOn?"":"active"}" data-action="send-mode" data-key="review">${icon("users")} 审核后发送</button></div>${reviewAssistantPanel(a)}<div class="automation-top"><section class="panel panel-pad"><div class="panel-title"><h2>监听与发送策略</h2></div><span class="badge green">${icon("activity")} ● 规则运行中</span><div class="auto-flow">${[["activity","扫描未读"],["log","提取消息"],["folder","合并消息"],["brain","请求 AI"],["send","回填发送"]].map(x=>`<div class="flow-step"><div class="flow-circle">${icon(x[0])}</div><strong>${x[1]}</strong></div>`).join("")}</div><div class="form-grid"><div class="flow-pill">${icon("clock")}扫描间隔 <input class="num" id="auto-poll" type="number" min="0.2" max="300" step="0.1" value="${esc(a.poll || "2")}"> 秒</div><div class="flow-pill">${icon("hourglass")} 连续消息等待 <input class="num" id="auto-batch" type="number" min="1" max="120" step="1" value="${esc(a.batch || "20")}"> 秒</div><div class="flow-pill">${icon("clock")}发送前等待 <input class="num" id="auto-delay" type="number" min="0" max="30" step="0.5" value="${esc(a.delay || "1")}"> 秒</div></div></section><section class="panel panel-pad"><div class="panel-title"><h2>企业微信窗口</h2></div><div class="wechat-graphic"><div class="fake-window"><div class="fake-check">✓</div></div><div class="stack"><div class="row"><span>${icon("clipboard")} 主窗口:</span><b style="color:var(--green)">● ${esc(a.wecom || "未识别")}</b></div><div class="row"><span>${icon("user")} 当前账号:</span><b>${esc(a.account || "—")}</b></div><div class="row"><span>${icon("globe")} DPI:</span><b>${esc(STATE.dpi || "—")}</b></div><div class="row"><span>${icon("monitor")} 最小化恢复:</span><b style="color:var(--green)">● 已开启</b></div></div></div><div class="task-actions"><button class="btn small blue" data-action="redetect">${icon("refresh")}重新检测</button></div></section></div><div class="automation-mid"><section class="panel panel-pad"><div class="panel-title"><h2>人机共存</h2></div><div class="setting-line"><span>启用人工操作保护</span><div class="switch ${a.mouseProtect?"on":""}" data-action="toggle-idle"></div></div><div style="text-align:center;margin-top:8px">鼠标静止 <input class="idle-num" id="auto-idle" type="number" min="0" max="3600" step="0.5" value="${esc(a.idle || "5")}"> 秒 后恢复</div><div class="setting-line" style="margin-top:10px"><span>草稿超时直发</span><div class="switch ${a.draftAutosend > 0 ? "on" : ""}" data-action="toggle-draft-autosend"></div></div><div style="text-align:center;margin-top:6px">输入框里有人工草稿 <input class="idle-num" id="auto-draft-autosend" type="number" min="1" max="1440" step="5" value="${esc(a.draftAutosend > 0 ? a.draftAutosend : 15)}" ${a.draftAutosend > 0 ? "" : "disabled"}> 分钟后直接发出</div><div class="tiny" style="text-align:center;margin-top:4px;color:var(--muted);font-size:11px">关闭时只跳过该会话、绝不动别人写的字;命中审核规则的仍会等人确认</div><div class="mouse-orb"><span style="color:#315fec">${icon("bot")}<br><small>AI 运行中</small></span><div class="mouse-pointer">${icon("cursor")}</div><span style="color:var(--orange)">${icon("pause")}<br><small>检测到人工操作<br>AI 暂停</small></span></div></section><section class="panel panel-pad"><div class="panel-title"><h2>消息处理</h2></div><div class="stack"><div class="setting-line"><span>✓ 同一客户连续消息自动合并</span></div><div class="setting-line"><span>✓ 保留最近 <input class="ai-num" id="auto-context" type="number" min="1" max="50" value="${esc(a.context || 8)}"> 轮上下文</span><span class="badge">${esc(a.context || 8)} 轮</span></div><div class="setting-line"><span>✓ 一次只处理 1 个会话</span><span class="badge">1 个</span></div></div></section><section class="panel panel-pad"><div class="panel-title"><h2>异常与兜底</h2></div><div class="stack"><div class="setting-line"><span style="color:var(--orange)">${icon("clock")}</span><span style="flex:1">AI 超时:${esc(a.timeout || 120)} 秒后重试</span><span class="badge">2 次</span></div><div class="setting-line"><span style="color:var(--orange)">${icon("alert")}</span><span style="flex:1">发送失败:自动重试</span><span class="badge green">已开启</span></div><div class="setting-line"><span style="color:#3c69f4">${icon("message")}</span><span style="flex:1">固定回复:${esc(a.reply || "在的,请稍等")}</span><button class="badge" data-action="edit-reply">编辑</button></div></div></section><section class="panel panel-pad"><div class="panel-title"><h2>高风险转人工</h2><span class="badge green">● 已开启</span></div><div class="risk-tags"><span class="risk-tag">${icon("shield")} 诊断</span><span class="risk-tag">${icon("package")} 用药调整</span><span class="risk-tag">${icon("coins")} 投诉退款</span></div><button class="btn small blue" style="margin:11px auto 0;display:flex" data-action="security-rules">${icon("settings")}规则管理</button></section></div><section class="panel panel-pad check-strip"><div class="panel-title"><h2>启动前检查</h2></div>${statusCells([["check","企业微信已登录", STATE.wecomConnected ? "检查完成" : "等待连接"],["cloud","AI 服务正常","检查完成"],["activity","窗口坐标已校准", STATE.wecomConnected ? "检查完成" : "待校准"],["shield","规则配置有效","检查完成"]])}</section></div>`;
};
logsView = function(){
@@ -1181,6 +1202,15 @@
"edit-knowledge": () => callBridge("editKnowledge"),
"sync-knowledge": () => callBridge("syncKnowledge"),
"open-data": () => callBridge("openDataDir"),
+ "review-assistant-refresh": () => callBridge("refreshReviewAssistant"),
+ "review-assistant-choose": () => callBridge("chooseReviewAssistant"),
+ "review-assistant-toggle": () => {
+ const r = (STATE.automation || {}).reviewAssistant || {};
+ if (!r.ready || r.busy) return;
+ callBridgeWithFeedback("setReviewAssistantEnabled", [JSON.stringify({expected_account: r.accountId, enabled: !r.enabled})], {
+ pending: "正在核对当前账号与助理联系人…",
+ });
+ },
"save-automation": () => callBridgeWithFeedback(
"saveAutomationSettings", [collectAutomationPayload()], {
button: action, busyLabel: "保存中…",