1 line
14 KiB
JavaScript
1 line
14 KiB
JavaScript
import{D as e,E as t,On as n,_ as r,b as i,bt as a,ct as o,g as s,gn as ee,k as te,mt as c,o as l,qt as u,tr as d,v as ne,vn as f,y as p}from"./runtime-core.esm-bundler-DkysjgTd.js";import{t as m}from"./objectSpread2-BmonrgTA.js";import{n as h}from"./objectWithoutProperties-BjoYlK0I.js";import{t as re}from"./use-access-ObRBpYsT.js";import{h as ie}from"./useApi-CROJJdhE-oP73ypFI.js";import{n as g,t as ae}from"./form-CZK1ilUa.js";import{t as _}from"./select-DyNDAEL2.js";import{u as oe,w as se}from"./console-2d_MKq1R.js";import{a as v}from"./config-provider-D1kcLCP5.js";import{t as y}from"./alert-CkUknOsQ.js";import{t as b}from"./button-B2y2NpJm.js";import{t as x}from"./card-D3WO92mr.js";import{n as S,t as C}from"./descriptions-BP4B3brJ.js";import{r as ce,t as w}from"./input-DgEff0DC.js";import{t as T}from"./input-number-B39v8Wj_.js";import{t as E}from"./switch-CtSw_Hgg.js";var le={class:`p-5`},ue={class:`mb-2`},de={key:0,class:`mt-2`},fe={class:`mt-1 text-xs text-gray-500`},pe={class:`grid grid-cols-1 gap-x-8 gap-y-1 md:grid-cols-2`},me={class:`min-w-0`},he={class:`text-sm font-medium`},ge={class:`mt-0.5 text-xs text-gray-500`},_e={class:`grid grid-cols-1 gap-x-8 md:grid-cols-2`},D={class:`grid grid-cols-1 gap-x-6 md:grid-cols-3`},O={class:`mt-1 text-xs text-gray-500`},k={class:`w-40 shrink-0`},A={class:`min-w-0 flex-1`},j={key:1,class:`mt-2 text-xs text-gray-500`},M={class:`grid grid-cols-1 gap-x-6 md:grid-cols-4`},N={key:0,class:`mt-1 text-xs text-red-500`},P={key:1,class:`mt-1 text-xs text-gray-500`},F={key:0,class:`sticky bottom-0 -mx-5 border-t bg-white/90 px-5 py-3 backdrop-blur dark:bg-black/70`},I=te({__name:`desktop-config`,setup(te){let{hasAccessByCodes:I}=re(),L=s(()=>I([`config:write`])),ve=s(()=>I([`model:read`])),R=ie(),z=f(!1),B=f(!1),V=f(0),H=f(``),U=f(``),W=f(``),G=f(``),K=f(``),q=f(`[]`),J=f(``),Y=ee({ARCHIVE_AUTO_BACKUP_ENABLED:!0,ARCHIVE_AUTO_BACKUP_INTERVAL_MINUTES:60,AI_AGENT_NAME:``,AI_GATEWAY_URL:``,ZYT_LOGIN_API_URL:``,AI_CONTEXT_ENABLED:!0,AI_CONTEXT_MAX_ROUNDS:8,AI_COUNTER_INSULT_ENABLED:!1,AI_DEVELOPMENT_MODE:!1,AI_ENABLED:!0,AI_HOSPITAL_NAME:``,AI_MCP_ENABLED:!1,AI_MCP_MAX_ROUNDS:5,AI_MCP_SERVERS:[],AI_REVIEW_RULES:[],AI_UI_GUARD_ENABLED:!0,AI_USE_VISION:!0}),ye=[{key:`AI_ENABLED`,label:`启用 AI 自动回复`,hint:`总开关。关掉之后客户端只监听不回复。`},{key:`AI_USE_VISION`,label:`启用视觉识别`,hint:`图片和表情包走视觉模型识别,OCR 认不出时兜底。用哪个模型看「角色编排」里的媒体消息专用模型。`},{key:`AI_UI_GUARD_ENABLED`,label:`人工操作保护`,hint:`检测到有人在操作鼠标键盘时暂停自动化,避免抢焦点。`},{key:`AI_CONTEXT_ENABLED`,label:`携带上下文`,hint:`把最近几轮对话一起发给模型。关掉会省 token,但答复会失忆。`},{key:`AI_COUNTER_INSULT_ENABLED`,label:`应对辱骂`,hint:`客户情绪激动时用专门的话术回应,而不是照常答业务问题。`},{key:`AI_MCP_ENABLED`,label:`启用 MCP 工具`,hint:`允许模型调用挂号登记、客户查询等外部工具。工具在客户端本机执行。`},{key:`AI_DEVELOPMENT_MODE`,label:`开发模式`,hint:`打印详细日志、跳过部分节流。生产环境请关闭。`}];function X(){return Z.apply(this,arguments)}function Z(){return Z=h(function*(){z.value=!0;try{var e;let t=yield oe();Object.assign(Y,t.config),q.value=JSON.stringify((e=t.config.AI_MCP_SERVERS)==null?[]:e,null,2),V.value=t.version,H.value=t.updated_at,U.value=t.updated_by,W.value=t.model_settings_moved_to,G.value=t.gateway_url_effective,K.value=t.login_api_url_effective}finally{z.value=!1}}),Z.apply(this,arguments)}function be(){Y.AI_REVIEW_RULES.push({label:``,keywords:[],enabled:!0})}function xe(e){Y.AI_REVIEW_RULES.splice(e,1)}function Se(){for(let[e,t]of Y.AI_REVIEW_RULES.entries()){if(!t.label.trim())return`第 ${e+1} 条规则没有填名称`;if(t.keywords.length===0)return`规则「${t.label}」至少要有一个关键词,否则永远不会命中`}return``}function Q(){try{let e=JSON.parse(q.value||`[]`);return Array.isArray(e)?(J.value=``,e):(J.value=`JSON 根节点必须是数组`,null)}catch(e){return J.value=`不是有效的 JSON:${e.message}`,null}}function Ce(){return $.apply(this,arguments)}function $(){return $=h(function*(){let e=Q();if(e===null){v.warning(`MCP 服务器配置不是有效 JSON,请先修正`);return}let t=Se();if(t){v.warning(t);return}B.value=!0;try{let t=yield se(m(m({},Y),{},{AI_MCP_SERVERS:e}));v.success(`已发布为 v${t.version},桌面端下次同步时生效`),yield X()}finally{B.value=!1}}),$.apply(this,arguments)}return o(X),(o,s)=>(c(),i(`div`,le,[e(n(y),{class:`mb-4`,type:`warning`,"show-icon":``,message:`这份配置会下发到每一台桌面客户端`,description:`保存即产生新版本号,客户端下次同步就会拿到。改错一个字段是全线生效的,不是只影响你自己这台。`}),e(n(y),{class:`mb-4`,type:`info`,"show-icon":``},{message:u(()=>[...s[11]||(s[11]=[t(`模型配置不在这里`,-1)])]),description:u(()=>[r(`p`,ue,[s[12]||(s[12]=t(` 服务类型、API 地址、API Key、模型名称、温度、最大 tokens、请求超时——这些以前在本页的设置, 现在统一在 `,-1)),r(`b`,null,d(W.value||`AI 模型 → 模型清单 / 角色编排`),1),s[13]||(s[13]=t(`。 `,-1))]),s[16]||(s[16]=r(`p`,{class:`mb-2 text-xs`},` 搬走有三个原因:同一件事两个地方能配迟早会不一致;密钥留在这份配置里就必须明文下发到每一台客户端, 而模型清单里是加密存储、只返遮罩值;一个客户只能配一个模型的话,双模型并发和裁判根本无从谈起。 `,-1)),ve.value?(c(),i(`div`,de,[e(n(b),{size:`small`,onClick:s[0]||(s[0]=e=>n(R).push(`/model/catalog`))},{default:u(()=>[...s[14]||(s[14]=[t(`去模型清单`,-1)])]),_:1}),e(n(b),{size:`small`,class:`ml-2`,onClick:s[1]||(s[1]=e=>n(R).push(`/model/plan`))},{default:u(()=>[...s[15]||(s[15]=[t(`去角色编排`,-1)])]),_:1})])):p(``,!0)]),_:1}),e(n(x),{title:`当前生效的配置`,class:`mb-4`,loading:z.value},{default:u(()=>[e(n(C),{column:3,size:`small`,bordered:``},{default:u(()=>[e(n(S),{label:`版本`},{default:u(()=>[t(`v`+d(V.value),1)]),_:1}),e(n(S),{label:`最后修改`},{default:u(()=>[t(d(U.value||`—`),1)]),_:1}),e(n(S),{label:`时间`},{default:u(()=>[t(d(H.value||`—`),1)]),_:1})]),_:1})]),_:1},8,[`loading`]),e(n(ae),{model:Y,layout:`vertical`},{default:u(()=>[e(n(x),{title:`桌面账号登录`,class:`mb-4`,loading:z.value},{default:u(()=>[s[19]||(s[19]=r(`p`,{class:`mb-3 text-xs text-gray-500`},` 软件打开登录窗口后,会先请求本管理端的公开客户端配置,再按这里返回的地址请求 ZYT 登录。 换登录域名只需修改这一处,无需重新修改或打包桌面软件。 `,-1)),e(n(g),{label:`ZYT 登录 API 域名(留空 = 使用管理 API 启动配置)`},{default:u(()=>[e(n(w),{value:Y.ZYT_LOGIN_API_URL,"onUpdate:value":s[2]||(s[2]=e=>Y.ZYT_LOGIN_API_URL=e),disabled:!L.value,placeholder:`https://api.example.com`},null,8,[`value`,`disabled`]),r(`p`,fe,[s[17]||(s[17]=t(` 当前客户端实际获取:`,-1)),r(`code`,null,d(K.value||`—`),1)]),s[18]||(s[18]=r(`p`,{class:`mt-1 text-xs text-gray-500`},[t(` 填写完整的 `),r(`code`,null,`http://`),t(` 或 `),r(`code`,null,`https://`),t(` 基础地址,不要填写 `),r(`code`,null,`/api/login/account`),t(`;客户端会自动拼接登录接口路径。 `)],-1))]),_:1})]),_:1},8,[`loading`]),e(n(x),{title:`能力开关`,class:`mb-4`,loading:z.value},{default:u(()=>[r(`div`,pe,[(c(),i(l,null,a(ye,t=>r(`div`,{key:t.key,class:`flex items-start justify-between gap-4 border-b border-dashed py-3 last:border-0`},[r(`div`,me,[r(`div`,he,d(t.label),1),r(`div`,ge,d(t.hint),1)]),e(n(E),{checked:Y[t.key],"onUpdate:checked":e=>Y[t.key]=e,disabled:!L.value,class:`mt-1 shrink-0`},null,8,[`checked`,`onUpdate:checked`,`disabled`])])),64))])]),_:1},8,[`loading`]),e(n(x),{title:`企业微信数据库自动备份`,class:`mb-4`,loading:z.value},{default:u(()=>[s[21]||(s[21]=r(`p`,{class:`mb-3 text-xs text-gray-500`},` 软件启动后等待设定时间执行第一次增量备份,之后按相同间隔持续运行。只上传服务端检查点之后的新消息和新媒体,修改配置后客户端会自动同步,无需重启。 `,-1)),r(`div`,_e,[e(n(g),{label:`自动增量备份`},{default:u(()=>[e(n(E),{checked:Y.ARCHIVE_AUTO_BACKUP_ENABLED,"onUpdate:checked":s[3]||(s[3]=e=>Y.ARCHIVE_AUTO_BACKUP_ENABLED=e),disabled:!L.value},null,8,[`checked`,`disabled`])]),_:1}),e(n(g),{label:`启动等待及备份间隔(分钟)`},{default:u(()=>[e(n(T),{value:Y.ARCHIVE_AUTO_BACKUP_INTERVAL_MINUTES,"onUpdate:value":s[4]||(s[4]=e=>Y.ARCHIVE_AUTO_BACKUP_INTERVAL_MINUTES=e),min:1,max:10080,disabled:!L.value||!Y.ARCHIVE_AUTO_BACKUP_ENABLED,class:`w-full`},null,8,[`value`,`disabled`]),s[20]||(s[20]=r(`p`,{class:`mt-1 text-xs text-gray-500`},`范围 1 分钟至 7 天。`,-1))]),_:1})])]),_:1},8,[`loading`]),e(n(x),{title:`客服人格`,class:`mb-4`,loading:z.value},{default:u(()=>[s[23]||(s[23]=r(`p`,{class:`mb-3 text-xs text-gray-500`},` 这几项会拼进系统提示词,决定客户看到的是"谁"在说话。它们不是模型参数,所以留在这里。 `,-1)),r(`div`,D,[e(n(g),{label:`客服名称`},{default:u(()=>[e(n(w),{value:Y.AI_AGENT_NAME,"onUpdate:value":s[5]||(s[5]=e=>Y.AI_AGENT_NAME=e),disabled:!L.value,placeholder:`贴心管家`},null,8,[`value`,`disabled`])]),_:1}),e(n(g),{label:`机构名称`},{default:u(()=>[e(n(w),{value:Y.AI_HOSPITAL_NAME,"onUpdate:value":s[6]||(s[6]=e=>Y.AI_HOSPITAL_NAME=e),disabled:!L.value,placeholder:`甄养堂`},null,8,[`value`,`disabled`])]),_:1}),e(n(g),{label:`上下文轮数(1–50)`},{default:u(()=>[e(n(T),{value:Y.AI_CONTEXT_MAX_ROUNDS,"onUpdate:value":s[7]||(s[7]=e=>Y.AI_CONTEXT_MAX_ROUNDS=e),min:1,max:50,disabled:!L.value,class:`w-full`},null,8,[`value`,`disabled`]),s[22]||(s[22]=r(`p`,{class:`mt-1 text-xs text-gray-500`},` 记几轮对话。这是客户端行为,和"用哪个模型"无关。 `,-1))]),_:1})])]),_:1},8,[`loading`]),e(n(x),{title:`模型网关地址`,class:`mb-4`,loading:z.value},{default:u(()=>[s[26]||(s[26]=r(`p`,{class:`mb-3 text-xs text-gray-500`},` 桌面客户端只配一个后台地址,模型请求发到哪儿由这里决定、随配置一起下发。 换网关位置只需要改这一处,不用挨个动客户端。 `,-1)),e(n(g),{label:`地址(留空 = 按后台自己的地址自动推算)`},{default:u(()=>[e(n(w),{value:Y.AI_GATEWAY_URL,"onUpdate:value":s[8]||(s[8]=e=>Y.AI_GATEWAY_URL=e),disabled:!L.value,placeholder:`留空即可,绝大多数情况不用填`},null,8,[`value`,`disabled`]),r(`p`,O,[s[24]||(s[24]=t(` 当前实际下发:`,-1)),r(`code`,null,d(G.value||`—`),1)]),s[25]||(s[25]=r(`p`,{class:`mt-1 text-xs text-gray-500`},[t(` 只有网关在别的域名或别的机器上时才需要填。写完整地址(`),r(`code`,null,`https://…`),t(`) 或以 `),r(`code`,null,`/`),t(` 开头的路径(挂在本后台的域名下)。 `)],-1))]),_:1})]),_:1},8,[`loading`]),e(n(x),{title:`选择性审核规则`,class:`mb-4`,loading:z.value},{default:u(()=>[s[29]||(s[29]=r(`p`,{class:`mb-3 text-xs text-gray-500`},` 命中任意一条,那一条回复才会停下来贴进企业微信输入框等人工确认,不会自动按回车; 没命中的照常自动发送。关键词不区分大小写,客户这句话和模型准备发的回复, 沾上任意一边都算命中。 `,-1)),e(n(y),{class:`mb-3`,type:`info`,"show-icon":``,message:`还有一条内置规则不在这份清单里、关不掉`,description:`模型裁判把某条回复判成「高风险」时,无论有没有命中下面的关键词,同样会转人工。裁判只要在角色编排里配了就会一直打分,这个信号比关键词更贴近内容本身。`}),(c(!0),i(l,null,a(Y.AI_REVIEW_RULES,(a,o)=>(c(),i(`div`,{key:o,class:`mb-3 flex items-start gap-3 border-b border-dashed pb-3 last:border-0`},[r(`div`,k,[e(n(w),{value:a.label,"onUpdate:value":e=>a.label=e,disabled:!L.value,placeholder:`规则名称,例如「诊断」`},null,8,[`value`,`onUpdate:value`,`disabled`])]),r(`div`,A,[e(n(_),{value:a.keywords,"onUpdate:value":e=>a.keywords=e,mode:`tags`,disabled:!L.value,placeholder:`输入关键词后按回车,可以加多个`,style:{width:`100%`}},null,8,[`value`,`onUpdate:value`,`disabled`])]),e(n(E),{checked:a.enabled,"onUpdate:checked":e=>a.enabled=e,disabled:!L.value,class:`mt-1 shrink-0`},null,8,[`checked`,`onUpdate:checked`,`disabled`]),e(n(b),{danger:``,type:`link`,size:`small`,disabled:!L.value,class:`shrink-0`,onClick:e=>xe(o)},{default:u(()=>[...s[27]||(s[27]=[t(` 删除 `,-1)])]),_:1},8,[`disabled`,`onClick`])]))),128)),L.value?(c(),ne(n(b),{key:0,size:`small`,onClick:be},{default:u(()=>[...s[28]||(s[28]=[t(`+ 新增规则`,-1)])]),_:1})):p(``,!0),Y.AI_REVIEW_RULES.length===0?(c(),i(`p`,j,` 还没有配任何规则——除了上面那条内置的裁判高风险检查,其余全部自动发送。 `)):p(``,!0)]),_:1},8,[`loading`]),e(n(x),{title:`MCP 服务器`,class:`mb-4`,loading:z.value},{default:u(()=>[r(`div`,M,[e(n(g),{label:`单次最多调用工具轮数(1–20)`},{default:u(()=>[e(n(T),{value:Y.AI_MCP_MAX_ROUNDS,"onUpdate:value":s[9]||(s[9]=e=>Y.AI_MCP_MAX_ROUNDS=e),min:1,max:20,disabled:!L.value,class:`w-full`},null,8,[`value`,`disabled`])]),_:1})]),e(n(g),{label:`服务器清单(JSON 数组)`},{default:u(()=>[e(n(ce),{value:q.value,"onUpdate:value":s[10]||(s[10]=e=>q.value=e),rows:8,disabled:!L.value,status:J.value?`error`:void 0,spellcheck:`false`,class:`font-mono text-xs`,onBlur:Q},null,8,[`value`,`disabled`,`status`]),J.value?(c(),i(`p`,N,d(J.value),1)):(c(),i(`p`,P,` 工具跑在客户端本机(要连内网),所以配置在这里而不是模型清单里。 格式错误会当场拦下,不会等到桌面端同步之后才发现。 `))]),_:1})]),_:1},8,[`loading`]),L.value?(c(),i(`div`,F,[e(n(b),{type:`primary`,loading:B.value,onClick:Ce},{default:u(()=>[...s[30]||(s[30]=[t(`保存并发布新版本`,-1)])]),_:1},8,[`loading`]),e(n(b),{class:`ml-2`,disabled:B.value,onClick:X},{default:u(()=>[...s[31]||(s[31]=[t(`放弃修改`,-1)])]),_:1},8,[`disabled`])])):p(``,!0)]),_:1},8,[`model`])]))}});export{I as default}; |