53 lines
21 KiB
Diff
53 lines
21 KiB
Diff
--- 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: "保存中…",
|