diff --git a/CHANGELOG.md b/CHANGELOG.md index efa7734..59997d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [3.5.8] - 2026-03-22 +### ✨ Added +- **WebUI** + - 增加批量操作实例设置代理或者删除 + ### 🐛 Fixed - **适配器** - 修复豆包适配器无法点击选择模型的问题 diff --git a/webui/dist/assets/workers.js b/webui/dist/assets/workers.js index 2865842..b0004ff 100644 --- a/webui/dist/assets/workers.js +++ b/webui/dist/assets/workers.js @@ -1 +1 @@ -import{k as re,n as D,o as ie,r as _,b as W,d as i,w as s,c as n,g as t,f as r,h as v,j as d,e as x,t as g,F as z,p as pe}from"./index.js";const ue={style:{"margin-bottom":"24px"}},de={style:{"margin-bottom":"24px"}},ve={style:{"margin-bottom":"24px"}},me={style:{"margin-bottom":"8px"}},ye={style:{"margin-bottom":"8px"}},xe={style:{display:"flex","justify-content":"flex-end","margin-top":"24px"}},ge={style:{display:"flex","justify-content":"space-between","align-items":"center"}},fe={key:0},ke={key:3},we=["onClick"],be=["onClick"],_e={style:{"margin-bottom":"24px"}},Ce={style:{"margin-bottom":"16px"}},Me={style:{"margin-bottom":"16px"}},ce={style:{"margin-bottom":"16px"}},Ue={style:{"margin-bottom":"16px"}},Te={style:{"margin-left":"8px"}},he={key:0,style:{"margin-bottom":"16px"}},Pe={key:1,style:{"margin-bottom":"16px"}},De={key:2,style:{"margin-bottom":"16px"}},Ae={key:3,style:{"margin-bottom":"16px"}},Se={style:{"margin-left":"8px"}},We={key:4,style:{"margin-bottom":"16px"}},ze={key:5,style:{"margin-bottom":"16px"}},$e={style:{display:"flex","justify-content":"space-between","align-items":"center","margin-bottom":"8px"}},Ie=["onClick"],Ne=["onClick"],He={style:{"font-weight":"600"}},je={style:{"font-size":"12px",color:"#8c8c8c"}},Fe={key:0},Oe={key:0},Ve={style:{"text-align":"right"}},Be={style:{"margin-bottom":"16px"}},Ee={style:{"margin-bottom":"16px"}},Le={style:{"margin-bottom":"16px"}},Re={style:{"margin-bottom":"16px"}},Ge={__name:"workers",setup(Ke){const u=re(),m=D({get:()=>u.poolConfig,set:a=>u.poolConfig=a}),F=async()=>{await u.savePoolConfig(m.value)};ie(async()=>{await Promise.all([u.fetchWorkerConfig(),u.fetchPoolConfig(),u.fetchAdaptersMeta()])});const O=D(()=>{const a=u.adaptersMeta.map(e=>({label:e.displayName||e.id,value:e.id}));return a.find(e=>e.value==="merge")||a.unshift({label:"Merge(聚合模式)",value:"merge"}),a}),V=D(()=>u.adaptersMeta.filter(a=>a.id!=="merge").map(a=>({label:a.displayName||a.id,value:a.id}))),h=a=>a==="merge"?"Merge(聚合模式)":u.adaptersMeta.find(k=>k.id===a)?.displayName||a,B=[{title:"实例名称",dataIndex:"name",key:"name"},{title:"Worker 数量",dataIndex:"workerCount",key:"workerCount"},{title:"代理",dataIndex:"proxy",key:"proxy"},{title:"数据标记",key:"userDataMark",dataIndex:"userDataMark"},{title:"操作",key:"action"}],P=D({get:()=>u.workerConfig,set:a=>{u.workerConfig=a}}),w=_(!1),f=_(null),l=_({name:"",userDataMark:"",proxy:!1,proxyType:"socks5",proxyHost:"",proxyPort:1080,proxyAuth:!1,proxyUsername:"",proxyPassword:"",workers:[]}),E=()=>{f.value=null;const a=Math.random().toString(36).substring(2,7);l.value={name:`instance-${(P.value||[]).length+1}-${a}`,userDataMark:"",proxy:!1,proxyType:"socks5",proxyHost:"",proxyPort:1080,proxyAuth:!1,proxyUsername:"",proxyPassword:"",workers:[]},w.value=!0},L=a=>{f.value=a,l.value={name:a.name,userDataMark:a.userDataMark||"",proxy:!!a.proxy,proxyType:a.proxy?.type||"socks5",proxyHost:a.proxy?.host||"",proxyPort:a.proxy?.port||1080,proxyAuth:a.proxy?.auth||!1,proxyUsername:a.proxy?.username||"",proxyPassword:a.proxy?.password||"",workers:a.workers?[...a.workers]:[]},(a.proxy===null||a.proxy===void 0)&&(l.value.proxy=!1),w.value=!0},R=async a=>{const e=P.value.filter(k=>k.id!==a.id);await u.saveWorkerConfig(e)},K=async()=>{const a={id:f.value?f.value.id:`inst_${Date.now()}`,name:l.value.name,userDataMark:l.value.userDataMark,workers:l.value.workers,proxy:l.value.proxy?{enable:!0,type:l.value.proxyType,host:l.value.proxyHost,port:l.value.proxyPort,auth:l.value.proxyAuth,username:l.value.proxyUsername,password:l.value.proxyPassword}:null};let e=[...P.value||[]];if(f.value===null)e.push(a);else{const b=e.findIndex(c=>c.id===f.value.id);b>-1&&(e[b]=a)}await u.saveWorkerConfig(e)&&(w.value=!1)},C=_(-1),M=_(!1),p=_({name:"",type:"lmarena",mergeTypes:[],mergeMonitor:""}),q=()=>{C.value=-1;const a=Math.random().toString(36).substring(2,7);p.value={name:`worker-${l.value.workers.length+1}-${a}`,type:"lmarena",mergeTypes:[],mergeMonitor:""},M.value=!0},G=a=>{C.value=a;const e=l.value.workers[a];p.value={name:e.name,type:e.type,mergeTypes:e.mergeTypes?[...e.mergeTypes]:[],mergeMonitor:e.mergeMonitor||""},M.value=!0},J=()=>{C.value===-1?l.value.workers.push({...p.value}):l.value.workers[C.value]={...p.value},M.value=!1},Q=a=>{l.value.workers.splice(a,1)};return(a,e)=>{const k=r("a-segmented"),b=r("a-input-number"),c=r("a-switch"),$=r("a-col"),X=r("a-row"),I=r("a-collapse-panel"),N=r("a-collapse"),U=r("a-button"),H=r("a-card"),Y=r("a-tag"),Z=r("a-divider"),ee=r("a-table"),T=r("a-input"),te=r("a-input-password"),oe=r("a-list-item"),le=r("a-list"),ae=r("a-drawer"),A=r("a-select"),j=r("a-select-option"),ne=r("a-modal"),se=r("a-layout");return i(),W(se,{style:{background:"transparent"}},{default:s(()=>[n(H,{title:"负载均衡",bordered:!1,style:{width:"100%","margin-bottom":"10px"}},{default:s(()=>[t("div",ue,[e[20]||(e[20]=t("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"调度策略",-1)),e[21]||(e[21]=t("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"12px"}}," 选择任务分配到工作实例的调度算法 ",-1)),n(k,{value:m.value.strategy,"onUpdate:value":e[0]||(e[0]=o=>m.value.strategy=o),block:"",options:[{label:"最少繁忙",value:"least_busy"},{label:"轮询",value:"round_robin"},{label:"随机",value:"random"}]},null,8,["value"])]),t("div",de,[e[23]||(e[23]=t("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"生成等待超时",-1)),e[24]||(e[24]=t("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"12px"}}," 等待 AI 生成结果的最长时间,单位:秒(默认 120 秒) ",-1)),n(b,{value:m.value.waitTimeout,"onUpdate:value":e[1]||(e[1]=o=>m.value.waitTimeout=o),min:30,max:3600,step:30,style:{width:"100%"},placeholder:"请输入超时秒数"},{addonAfter:s(()=>[...e[22]||(e[22]=[v("秒",-1)])]),_:1},8,["value"])]),t("div",ve,[n(N,null,{default:s(()=>[n(I,{key:"failover",header:"故障转移"},{default:s(()=>[n(X,{gutter:16},{default:s(()=>[n($,{xs:24,md:12},{default:s(()=>[t("div",me,[e[25]||(e[25]=t("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"启用故障转移",-1)),e[26]||(e[26]=t("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"12px"}}," 启用后,任务失败时会自动切换到其他可用实例重试 ",-1)),n(c,{checked:m.value.failover.enabled,"onUpdate:checked":e[2]||(e[2]=o=>m.value.failover.enabled=o)},null,8,["checked"])])]),_:1}),n($,{xs:24,md:12},{default:s(()=>[t("div",ye,[e[27]||(e[27]=t("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"重试次数",-1)),e[28]||(e[28]=t("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"12px"}}," 故障转移时最大重试次数,范围 1-10 ",-1)),n(b,{value:m.value.failover.maxRetries,"onUpdate:value":e[3]||(e[3]=o=>m.value.failover.maxRetries=o),min:1,max:10,disabled:!m.value.failover.enabled,style:{width:"100%"},placeholder:"请输入重试次数"},null,8,["value","disabled"])])]),_:1})]),_:1})]),_:1})]),_:1})]),t("div",xe,[n(U,{type:"primary",onClick:F},{default:s(()=>[...e[29]||(e[29]=[v(" 保存设置 ",-1)])]),_:1})])]),_:1}),n(H,{bordered:!1,style:{width:"100%"}},{title:s(()=>[t("div",ge,[e[31]||(e[31]=t("span",null,"实例列表",-1)),n(U,{type:"primary",onClick:E},{default:s(()=>[...e[30]||(e[30]=[v(" 创建实例 ",-1)])]),_:1})])]),default:s(()=>[n(ee,{columns:B,"data-source":P.value,pagination:!1},{bodyCell:s(({column:o,record:y})=>[o.key==="name"?(i(),d("a",fe,g(y.name),1)):o.key==="workerCount"?(i(),d(z,{key:1},[v(g(y.workers?y.workers.length:0),1)],64)):o.key==="proxy"?(i(),W(Y,{key:2,color:y.proxy?"green":"default"},{default:s(()=>[v(g(y.proxy?"已启用":"未启用"),1)]),_:2},1032,["color"])):o.key==="action"?(i(),d("span",ke,[t("a",{onClick:S=>L(y)},"编辑",8,we),n(Z,{type:"vertical"}),t("a",{style:{color:"#ff4d4f"},onClick:S=>R(y)},"删除",8,be)])):x("",!0)]),_:1},8,["data-source"])]),_:1}),n(ae,{open:w.value,"onUpdate:open":e[14]||(e[14]=o=>w.value=o),title:f.value===null?"创建实例":`编辑实例 - ${f.value.name}`,placement:"right",width:"500"},{footer:s(()=>[t("div",Ve,[n(U,{style:{"margin-right":"8px"},onClick:e[13]||(e[13]=o=>w.value=!1)},{default:s(()=>[...e[44]||(e[44]=[v("取消",-1)])]),_:1}),n(U,{type:"primary",onClick:K},{default:s(()=>[...e[45]||(e[45]=[v("保存",-1)])]),_:1})])]),default:s(()=>[t("div",_e,[t("div",Ce,[e[32]||(e[32]=t("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"实例名称",-1)),e[33]||(e[33]=t("div",{style:{"font-size":"12px",color:"#ff4d4f","margin-bottom":"8px"}}," * 名称必须全局唯一,不可重复 ",-1)),n(T,{value:l.value.name,"onUpdate:value":e[4]||(e[4]=o=>l.value.name=o),placeholder:"请输入实例名称"},null,8,["value"])]),t("div",Me,[e[34]||(e[34]=t("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"数据标记",-1)),e[35]||(e[35]=t("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}}," 用于区分实例数据存储的文件夹名称 (userDataMark) ",-1)),n(T,{value:l.value.userDataMark,"onUpdate:value":e[5]||(e[5]=o=>l.value.userDataMark=o),placeholder:"请输入数据标记,如: main-gemini"},null,8,["value"])]),t("div",ce,[n(N,null,{default:s(()=>[n(I,{key:"proxy",header:"代理设置"},{default:s(()=>[t("div",Ue,[n(c,{checked:l.value.proxy,"onUpdate:checked":e[6]||(e[6]=o=>l.value.proxy=o)},null,8,["checked"]),t("span",Te,g(l.value.proxy?"已启用代理":"未启用代理"),1)]),l.value.proxy?(i(),d("div",he,[e[36]||(e[36]=t("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"代理类型",-1)),n(k,{value:l.value.proxyType,"onUpdate:value":e[7]||(e[7]=o=>l.value.proxyType=o),block:"",options:[{label:"SOCKS5",value:"socks5"},{label:"HTTP",value:"http"}],style:{width:"100%"}},null,8,["value"])])):x("",!0),l.value.proxy?(i(),d("div",Pe,[e[37]||(e[37]=t("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"服务器地址",-1)),n(T,{value:l.value.proxyHost,"onUpdate:value":e[8]||(e[8]=o=>l.value.proxyHost=o),placeholder:"例如: 127.0.0.1"},null,8,["value"])])):x("",!0),l.value.proxy?(i(),d("div",De,[e[38]||(e[38]=t("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"端口",-1)),n(b,{value:l.value.proxyPort,"onUpdate:value":e[9]||(e[9]=o=>l.value.proxyPort=o),min:1,max:65535,style:{width:"100%"},placeholder:"例如: 1080"},null,8,["value"])])):x("",!0),l.value.proxy?(i(),d("div",Ae,[e[39]||(e[39]=t("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"身份验证",-1)),n(c,{checked:l.value.proxyAuth,"onUpdate:checked":e[10]||(e[10]=o=>l.value.proxyAuth=o)},null,8,["checked"]),t("span",Se,g(l.value.proxyAuth?"需要验证":"无需验证"),1)])):x("",!0),l.value.proxy&&l.value.proxyAuth?(i(),d("div",We,[e[40]||(e[40]=t("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"用户名",-1)),n(T,{value:l.value.proxyUsername,"onUpdate:value":e[11]||(e[11]=o=>l.value.proxyUsername=o),placeholder:"请输入用户名"},null,8,["value"])])):x("",!0),l.value.proxy&&l.value.proxyAuth?(i(),d("div",ze,[e[41]||(e[41]=t("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"密码",-1)),n(te,{value:l.value.proxyPassword,"onUpdate:value":e[12]||(e[12]=o=>l.value.proxyPassword=o),placeholder:"请输入密码"},null,8,["value"])])):x("",!0)]),_:1})]),_:1})]),t("div",null,[t("div",$e,[e[43]||(e[43]=t("div",{style:{"font-weight":"600"}},"Worker 列表",-1)),n(U,{size:"small",type:"primary",onClick:q},{default:s(()=>[...e[42]||(e[42]=[v(" 添加 Worker ",-1)])]),_:1})]),n(le,{bordered:"","data-source":l.value.workers,style:{"margin-top":"8px"}},{renderItem:s(({item:o,index:y})=>[n(oe,null,{actions:s(()=>[t("a",{onClick:S=>G(y)},"编辑",8,Ie),t("a",{style:{color:"#ff4d4f"},onClick:S=>Q(y)},"删除",8,Ne)]),default:s(()=>[t("div",null,[t("div",He,g(o.name),1),t("div",je,[v(" 类型: "+g(h(o.type))+" ",1),o.type==="merge"?(i(),d("span",Fe,[v(" | 聚合: "+g(o.mergeTypes?.map(h).join(", ")||"无")+" ",1),o.mergeMonitor?(i(),d("span",Oe," | 监控: "+g(h(o.mergeMonitor)),1)):x("",!0)])):x("",!0)])])]),_:2},1024)]),_:1},8,["data-source"])])])]),_:1},8,["open","title"]),n(ne,{open:M.value,"onUpdate:open":e[19]||(e[19]=o=>M.value=o),title:C.value===-1?"添加 Worker":"编辑 Worker",okText:"确定",cancelText:"取消",onOk:J},{default:s(()=>[t("div",Be,[e[46]||(e[46]=t("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"Worker 名称",-1)),e[47]||(e[47]=t("div",{style:{"font-size":"12px",color:"#ff4d4f","margin-bottom":"8px"}}," * 名称必须全局唯一,不可重复 ",-1)),n(T,{value:p.value.name,"onUpdate:value":e[15]||(e[15]=o=>p.value.name=o),placeholder:"例如: default"},null,8,["value"])]),t("div",Ee,[e[48]||(e[48]=t("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"适配器类型",-1)),n(A,{value:p.value.type,"onUpdate:value":e[16]||(e[16]=o=>p.value.type=o),style:{width:"100%"},options:O.value},null,8,["value","options"])]),p.value.type==="merge"?(i(),d(z,{key:0},[t("div",Le,[e[49]||(e[49]=t("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"聚合类型",-1)),e[50]||(e[50]=t("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}}," 选择要聚合的后端适配器(可多选) ",-1)),n(A,{value:p.value.mergeTypes,"onUpdate:value":e[17]||(e[17]=o=>p.value.mergeTypes=o),mode:"multiple",style:{width:"100%"},placeholder:"选择要聚合的适配器",options:V.value},null,8,["value","options"])]),t("div",Re,[e[52]||(e[52]=t("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"空闲监控后端",-1)),e[53]||(e[53]=t("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}}," 空闲时挂机监控的后端(可选) ",-1)),n(A,{value:p.value.mergeMonitor,"onUpdate:value":e[18]||(e[18]=o=>p.value.mergeMonitor=o),style:{width:"100%"},placeholder:"选择监控后端(可留空)","allow-clear":""},{default:s(()=>[n(j,{value:""},{default:s(()=>[...e[51]||(e[51]=[v("无",-1)])]),_:1}),(i(!0),d(z,null,pe(p.value.mergeTypes,o=>(i(),W(j,{key:o,value:o},{default:s(()=>[v(g(h(o)),1)]),_:2},1032,["value"]))),128))]),_:1},8,["value"])])],64)):x("",!0)]),_:1},8,["open","title"])]),_:1})}}};export{Ge as default}; +import{k as xe,n as S,o as ge,r as b,b as D,d as i,w as r,c as n,g as o,f as u,h as g,j as p,e as d,t as y,F as I,p as fe,M as ke}from"./index.js";const we={style:{"margin-bottom":"24px"}},be={style:{"margin-bottom":"24px"}},_e={style:{"margin-bottom":"24px"}},ce={style:{"margin-bottom":"8px"}},he={style:{"margin-bottom":"8px"}},Ce={style:{display:"flex","justify-content":"flex-end","margin-top":"24px"}},Ue={style:{display:"flex","justify-content":"space-between","align-items":"center"}},Te={key:0},Pe={key:3},Me=["onClick"],Ae=["onClick"],Se={style:{"margin-bottom":"24px"}},De={style:{"margin-bottom":"16px"}},We={style:{"margin-bottom":"16px"}},He={style:{"margin-bottom":"16px"}},ze={style:{"margin-bottom":"16px"}},$e={style:{"margin-left":"8px"}},Ie={key:0,style:{"margin-bottom":"16px"}},Ne={key:1,style:{"margin-bottom":"16px"}},Oe={key:2,style:{"margin-bottom":"16px"}},Be={key:3,style:{"margin-bottom":"16px"}},Fe={style:{"margin-left":"8px"}},Ve={key:4,style:{"margin-bottom":"16px"}},je={key:5,style:{"margin-bottom":"16px"}},Le={style:{display:"flex","justify-content":"space-between","align-items":"center","margin-bottom":"8px"}},Re=["onClick"],Ee=["onClick"],Ke={style:{"font-weight":"600"}},qe={style:{"font-size":"12px",color:"#8c8c8c"}},Ge={key:0},Je={key:0},Qe={style:{"text-align":"right"}},Xe={style:{"margin-bottom":"16px"}},Ye={style:{"margin-bottom":"16px"}},Ze={style:{"margin-bottom":"16px"}},et={style:{"margin-bottom":"16px"}},tt={style:{"margin-bottom":"16px"}},ot={style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"16px"}},lt={style:{"margin-bottom":"16px"}},at={style:{"margin-left":"8px"}},nt={key:0,style:{"margin-bottom":"16px"}},st={key:1,style:{"margin-bottom":"16px"}},rt={key:2,style:{"margin-bottom":"16px"}},it={key:3,style:{"margin-bottom":"16px"}},ut={style:{"margin-left":"8px"}},pt={key:4,style:{"margin-bottom":"16px"}},dt={key:5,style:{"margin-bottom":"16px"}},mt={__name:"workers",setup(vt){const v=xe(),k=S({get:()=>v.poolConfig,set:l=>v.poolConfig=l}),R=async()=>{await v.savePoolConfig(k.value)};ge(async()=>{await Promise.all([v.fetchWorkerConfig(),v.fetchPoolConfig(),v.fetchAdaptersMeta()])});const E=S(()=>{const l=v.adaptersMeta.map(e=>({label:e.displayName||e.id,value:e.id}));return l.find(e=>e.value==="merge")||l.unshift({label:"Merge(聚合模式)",value:"merge"}),l}),K=S(()=>v.adaptersMeta.filter(l=>l.id!=="merge").map(l=>({label:l.displayName||l.id,value:l.id}))),W=l=>l==="merge"?"Merge(聚合模式)":v.adaptersMeta.find(x=>x.id===l)?.displayName||l,q=[{title:"实例名称",dataIndex:"name",key:"name"},{title:"Worker 数量",dataIndex:"workerCount",key:"workerCount"},{title:"代理",dataIndex:"proxy",key:"proxy"},{title:"数据标记",key:"userDataMark",dataIndex:"userDataMark"},{title:"操作",key:"action"}],T=S({get:()=>v.workerConfig,set:l=>{v.workerConfig=l}}),f=b([]),G=S(()=>({selectedRowKeys:f.value,onChange:l=>{f.value=l}})),H=b(!1),s=b({proxy:!0,proxyType:"socks5",proxyHost:"",proxyPort:1080,proxyAuth:!1,proxyUsername:"",proxyPassword:""}),J=()=>{s.value={proxy:!0,proxyType:"socks5",proxyHost:"",proxyPort:1080,proxyAuth:!1,proxyUsername:"",proxyPassword:""},H.value=!0},Q=async()=>{const l=(T.value||[]).map(x=>f.value.includes(x.id)?{...x,proxy:s.value.proxy?{enable:!0,type:s.value.proxyType,host:s.value.proxyHost,port:s.value.proxyPort,auth:s.value.proxyAuth,username:s.value.proxyUsername,password:s.value.proxyPassword}:null}:x);await v.saveWorkerConfig(l)&&(H.value=!1,f.value=[])},X=()=>{ke.confirm({title:"批量删除实例",content:`确定要删除选中的 ${f.value.length} 个实例吗?此操作不可撤销。`,okText:"删除",okType:"danger",cancelText:"取消",async onOk(){const l=(T.value||[]).filter(x=>!f.value.includes(x.id));await v.saveWorkerConfig(l)&&(f.value=[])}})},P=b(!1),_=b(null),a=b({name:"",userDataMark:"",proxy:!1,proxyType:"socks5",proxyHost:"",proxyPort:1080,proxyAuth:!1,proxyUsername:"",proxyPassword:"",workers:[]}),Y=()=>{_.value=null;const l=Math.random().toString(36).substring(2,7);a.value={name:`instance-${(T.value||[]).length+1}-${l}`,userDataMark:"",proxy:!1,proxyType:"socks5",proxyHost:"",proxyPort:1080,proxyAuth:!1,proxyUsername:"",proxyPassword:"",workers:[]},P.value=!0},Z=l=>{_.value=l,a.value={name:l.name,userDataMark:l.userDataMark||"",proxy:!!l.proxy,proxyType:l.proxy?.type||"socks5",proxyHost:l.proxy?.host||"",proxyPort:l.proxy?.port||1080,proxyAuth:l.proxy?.auth||!1,proxyUsername:l.proxy?.username||"",proxyPassword:l.proxy?.password||"",workers:l.workers?[...l.workers]:[]},(l.proxy===null||l.proxy===void 0)&&(a.value.proxy=!1),P.value=!0},ee=async l=>{const e=T.value.filter(x=>x.id!==l.id);await v.saveWorkerConfig(e)},te=async()=>{const l={id:_.value?_.value.id:`inst_${Date.now()}`,name:a.value.name,userDataMark:a.value.userDataMark,workers:a.value.workers,proxy:a.value.proxy?{enable:!0,type:a.value.proxyType,host:a.value.proxyHost,port:a.value.proxyPort,auth:a.value.proxyAuth,username:a.value.proxyUsername,password:a.value.proxyPassword}:null};let e=[...T.value||[]];if(_.value===null)e.push(l);else{const c=e.findIndex(h=>h.id===_.value.id);c>-1&&(e[c]=l)}await v.saveWorkerConfig(e)&&(P.value=!1)},M=b(-1),A=b(!1),m=b({name:"",type:"lmarena",mergeTypes:[],mergeMonitor:""}),oe=()=>{M.value=-1;const l=Math.random().toString(36).substring(2,7);m.value={name:`worker-${a.value.workers.length+1}-${l}`,type:"lmarena",mergeTypes:[],mergeMonitor:""},A.value=!0},le=l=>{M.value=l;const e=a.value.workers[l];m.value={name:e.name,type:e.type,mergeTypes:e.mergeTypes?[...e.mergeTypes]:[],mergeMonitor:e.mergeMonitor||""},A.value=!0},ae=()=>{M.value===-1?a.value.workers.push({...m.value}):a.value.workers[M.value]={...m.value},A.value=!1},ne=l=>{a.value.workers.splice(l,1)};return(l,e)=>{const x=u("a-segmented"),c=u("a-input-number"),h=u("a-switch"),N=u("a-col"),se=u("a-row"),O=u("a-collapse-panel"),B=u("a-collapse"),C=u("a-button"),F=u("a-card"),re=u("a-space"),ie=u("a-tag"),ue=u("a-divider"),pe=u("a-table"),U=u("a-input"),V=u("a-input-password"),de=u("a-list-item"),ve=u("a-list"),ye=u("a-drawer"),z=u("a-select"),j=u("a-select-option"),L=u("a-modal"),me=u("a-layout");return i(),D(me,{style:{background:"transparent"}},{default:r(()=>[n(F,{title:"负载均衡",bordered:!1,style:{width:"100%","margin-bottom":"10px"}},{default:r(()=>[o("div",we,[e[28]||(e[28]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"调度策略",-1)),e[29]||(e[29]=o("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"12px"}}," 选择任务分配到工作实例的调度算法 ",-1)),n(x,{value:k.value.strategy,"onUpdate:value":e[0]||(e[0]=t=>k.value.strategy=t),block:"",options:[{label:"最少繁忙",value:"least_busy"},{label:"轮询",value:"round_robin"},{label:"随机",value:"random"}]},null,8,["value"])]),o("div",be,[e[31]||(e[31]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"生成等待超时",-1)),e[32]||(e[32]=o("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"12px"}}," 等待 AI 生成结果的最长时间,单位:秒(默认 120 秒) ",-1)),n(c,{value:k.value.waitTimeout,"onUpdate:value":e[1]||(e[1]=t=>k.value.waitTimeout=t),min:30,max:3600,step:30,style:{width:"100%"},placeholder:"请输入超时秒数"},{addonAfter:r(()=>[...e[30]||(e[30]=[g("秒",-1)])]),_:1},8,["value"])]),o("div",_e,[n(B,null,{default:r(()=>[n(O,{key:"failover",header:"故障转移"},{default:r(()=>[n(se,{gutter:16},{default:r(()=>[n(N,{xs:24,md:12},{default:r(()=>[o("div",ce,[e[33]||(e[33]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"启用故障转移",-1)),e[34]||(e[34]=o("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"12px"}}," 启用后,任务失败时会自动切换到其他可用实例重试 ",-1)),n(h,{checked:k.value.failover.enabled,"onUpdate:checked":e[2]||(e[2]=t=>k.value.failover.enabled=t)},null,8,["checked"])])]),_:1}),n(N,{xs:24,md:12},{default:r(()=>[o("div",he,[e[35]||(e[35]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"重试次数",-1)),e[36]||(e[36]=o("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"12px"}}," 故障转移时最大重试次数,范围 1-10 ",-1)),n(c,{value:k.value.failover.maxRetries,"onUpdate:value":e[3]||(e[3]=t=>k.value.failover.maxRetries=t),min:1,max:10,disabled:!k.value.failover.enabled,style:{width:"100%"},placeholder:"请输入重试次数"},null,8,["value","disabled"])])]),_:1})]),_:1})]),_:1})]),_:1})]),o("div",Ce,[n(C,{type:"primary",onClick:R},{default:r(()=>[...e[37]||(e[37]=[g(" 保存设置 ",-1)])]),_:1})])]),_:1}),n(F,{bordered:!1,style:{width:"100%"}},{title:r(()=>[o("div",Ue,[e[39]||(e[39]=o("span",null,"实例列表",-1)),n(re,null,{default:r(()=>[f.value.length>0?(i(),D(C,{key:0,onClick:J},{default:r(()=>[g(" 批量设置代理 ("+y(f.value.length)+") ",1)]),_:1})):d("",!0),f.value.length>0?(i(),D(C,{key:1,danger:"",onClick:X},{default:r(()=>[g(" 批量删除 ("+y(f.value.length)+") ",1)]),_:1})):d("",!0),n(C,{type:"primary",onClick:Y},{default:r(()=>[...e[38]||(e[38]=[g(" 创建实例 ",-1)])]),_:1})]),_:1})])]),default:r(()=>[n(pe,{columns:q,"data-source":T.value,pagination:!1,"row-selection":G.value,"row-key":"id"},{bodyCell:r(({column:t,record:w})=>[t.key==="name"?(i(),p("a",Te,y(w.name),1)):t.key==="workerCount"?(i(),p(I,{key:1},[g(y(w.workers?w.workers.length:0),1)],64)):t.key==="proxy"?(i(),D(ie,{key:2,color:w.proxy?"green":"default"},{default:r(()=>[g(y(w.proxy?"已启用":"未启用"),1)]),_:2},1032,["color"])):t.key==="action"?(i(),p("span",Pe,[o("a",{onClick:$=>Z(w)},"编辑",8,Me),n(ue,{type:"vertical"}),o("a",{style:{color:"#ff4d4f"},onClick:$=>ee(w)},"删除",8,Ae)])):d("",!0)]),_:1},8,["data-source","row-selection"])]),_:1}),n(ye,{open:P.value,"onUpdate:open":e[14]||(e[14]=t=>P.value=t),title:_.value===null?"创建实例":`编辑实例 - ${_.value.name}`,placement:"right",width:"500"},{footer:r(()=>[o("div",Qe,[n(C,{style:{"margin-right":"8px"},onClick:e[13]||(e[13]=t=>P.value=!1)},{default:r(()=>[...e[52]||(e[52]=[g("取消",-1)])]),_:1}),n(C,{type:"primary",onClick:te},{default:r(()=>[...e[53]||(e[53]=[g("保存",-1)])]),_:1})])]),default:r(()=>[o("div",Se,[o("div",De,[e[40]||(e[40]=o("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"实例名称",-1)),e[41]||(e[41]=o("div",{style:{"font-size":"12px",color:"#ff4d4f","margin-bottom":"8px"}}," * 名称必须全局唯一,不可重复 ",-1)),n(U,{value:a.value.name,"onUpdate:value":e[4]||(e[4]=t=>a.value.name=t),placeholder:"请输入实例名称"},null,8,["value"])]),o("div",We,[e[42]||(e[42]=o("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"数据标记",-1)),e[43]||(e[43]=o("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}}," 用于区分实例数据存储的文件夹名称 (userDataMark) ",-1)),n(U,{value:a.value.userDataMark,"onUpdate:value":e[5]||(e[5]=t=>a.value.userDataMark=t),placeholder:"请输入数据标记,如: main-gemini"},null,8,["value"])]),o("div",He,[n(B,null,{default:r(()=>[n(O,{key:"proxy",header:"代理设置"},{default:r(()=>[o("div",ze,[n(h,{checked:a.value.proxy,"onUpdate:checked":e[6]||(e[6]=t=>a.value.proxy=t)},null,8,["checked"]),o("span",$e,y(a.value.proxy?"已启用代理":"未启用代理"),1)]),a.value.proxy?(i(),p("div",Ie,[e[44]||(e[44]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"代理类型",-1)),n(x,{value:a.value.proxyType,"onUpdate:value":e[7]||(e[7]=t=>a.value.proxyType=t),block:"",options:[{label:"SOCKS5",value:"socks5"},{label:"HTTP",value:"http"}],style:{width:"100%"}},null,8,["value"])])):d("",!0),a.value.proxy?(i(),p("div",Ne,[e[45]||(e[45]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"服务器地址",-1)),n(U,{value:a.value.proxyHost,"onUpdate:value":e[8]||(e[8]=t=>a.value.proxyHost=t),placeholder:"例如: 127.0.0.1"},null,8,["value"])])):d("",!0),a.value.proxy?(i(),p("div",Oe,[e[46]||(e[46]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"端口",-1)),n(c,{value:a.value.proxyPort,"onUpdate:value":e[9]||(e[9]=t=>a.value.proxyPort=t),min:1,max:65535,style:{width:"100%"},placeholder:"例如: 1080"},null,8,["value"])])):d("",!0),a.value.proxy?(i(),p("div",Be,[e[47]||(e[47]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"身份验证",-1)),n(h,{checked:a.value.proxyAuth,"onUpdate:checked":e[10]||(e[10]=t=>a.value.proxyAuth=t)},null,8,["checked"]),o("span",Fe,y(a.value.proxyAuth?"需要验证":"无需验证"),1)])):d("",!0),a.value.proxy&&a.value.proxyAuth?(i(),p("div",Ve,[e[48]||(e[48]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"用户名",-1)),n(U,{value:a.value.proxyUsername,"onUpdate:value":e[11]||(e[11]=t=>a.value.proxyUsername=t),placeholder:"请输入用户名"},null,8,["value"])])):d("",!0),a.value.proxy&&a.value.proxyAuth?(i(),p("div",je,[e[49]||(e[49]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"密码",-1)),n(V,{value:a.value.proxyPassword,"onUpdate:value":e[12]||(e[12]=t=>a.value.proxyPassword=t),placeholder:"请输入密码"},null,8,["value"])])):d("",!0)]),_:1})]),_:1})]),o("div",null,[o("div",Le,[e[51]||(e[51]=o("div",{style:{"font-weight":"600"}},"Worker 列表",-1)),n(C,{size:"small",type:"primary",onClick:oe},{default:r(()=>[...e[50]||(e[50]=[g(" 添加 Worker ",-1)])]),_:1})]),n(ve,{bordered:"","data-source":a.value.workers,style:{"margin-top":"8px"}},{renderItem:r(({item:t,index:w})=>[n(de,null,{actions:r(()=>[o("a",{onClick:$=>le(w)},"编辑",8,Re),o("a",{style:{color:"#ff4d4f"},onClick:$=>ne(w)},"删除",8,Ee)]),default:r(()=>[o("div",null,[o("div",Ke,y(t.name),1),o("div",qe,[g(" 类型: "+y(W(t.type))+" ",1),t.type==="merge"?(i(),p("span",Ge,[g(" | 聚合: "+y(t.mergeTypes?.map(W).join(", ")||"无")+" ",1),t.mergeMonitor?(i(),p("span",Je," | 监控: "+y(W(t.mergeMonitor)),1)):d("",!0)])):d("",!0)])])]),_:2},1024)]),_:1},8,["data-source"])])])]),_:1},8,["open","title"]),n(L,{open:A.value,"onUpdate:open":e[19]||(e[19]=t=>A.value=t),title:M.value===-1?"添加 Worker":"编辑 Worker",okText:"确定",cancelText:"取消",onOk:ae},{default:r(()=>[o("div",Xe,[e[54]||(e[54]=o("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"Worker 名称",-1)),e[55]||(e[55]=o("div",{style:{"font-size":"12px",color:"#ff4d4f","margin-bottom":"8px"}}," * 名称必须全局唯一,不可重复 ",-1)),n(U,{value:m.value.name,"onUpdate:value":e[15]||(e[15]=t=>m.value.name=t),placeholder:"例如: default"},null,8,["value"])]),o("div",Ye,[e[56]||(e[56]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"适配器类型",-1)),n(z,{value:m.value.type,"onUpdate:value":e[16]||(e[16]=t=>m.value.type=t),style:{width:"100%"},options:E.value},null,8,["value","options"])]),m.value.type==="merge"?(i(),p(I,{key:0},[o("div",Ze,[e[57]||(e[57]=o("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"聚合类型",-1)),e[58]||(e[58]=o("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}}," 选择要聚合的后端适配器(可多选) ",-1)),n(z,{value:m.value.mergeTypes,"onUpdate:value":e[17]||(e[17]=t=>m.value.mergeTypes=t),mode:"multiple",style:{width:"100%"},placeholder:"选择要聚合的适配器",options:K.value},null,8,["value","options"])]),o("div",et,[e[60]||(e[60]=o("div",{style:{"font-weight":"600","margin-bottom":"4px"}},"空闲监控后端",-1)),e[61]||(e[61]=o("div",{style:{"font-size":"12px",color:"#8c8c8c","margin-bottom":"8px"}}," 空闲时挂机监控的后端(可选) ",-1)),n(z,{value:m.value.mergeMonitor,"onUpdate:value":e[18]||(e[18]=t=>m.value.mergeMonitor=t),style:{width:"100%"},placeholder:"选择监控后端(可留空)","allow-clear":""},{default:r(()=>[n(j,{value:""},{default:r(()=>[...e[59]||(e[59]=[g("无",-1)])]),_:1}),(i(!0),p(I,null,fe(m.value.mergeTypes,t=>(i(),D(j,{key:t,value:t},{default:r(()=>[g(y(W(t)),1)]),_:2},1032,["value"]))),128))]),_:1},8,["value"])])],64)):d("",!0)]),_:1},8,["open","title"]),n(L,{open:H.value,"onUpdate:open":e[27]||(e[27]=t=>H.value=t),title:"批量设置代理",okText:"应用",cancelText:"取消",onOk:Q},{default:r(()=>[o("div",tt,[o("div",ot," 将对选中的 "+y(f.value.length)+" 个实例统一设置代理 ",1),o("div",lt,[n(h,{checked:s.value.proxy,"onUpdate:checked":e[20]||(e[20]=t=>s.value.proxy=t)},null,8,["checked"]),o("span",at,y(s.value.proxy?"启用代理":"关闭代理(将清除选中实例的代理配置)"),1)]),s.value.proxy?(i(),p("div",nt,[e[62]||(e[62]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"代理类型",-1)),n(x,{value:s.value.proxyType,"onUpdate:value":e[21]||(e[21]=t=>s.value.proxyType=t),block:"",options:[{label:"SOCKS5",value:"socks5"},{label:"HTTP",value:"http"}],style:{width:"100%"}},null,8,["value"])])):d("",!0),s.value.proxy?(i(),p("div",st,[e[63]||(e[63]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"服务器地址",-1)),n(U,{value:s.value.proxyHost,"onUpdate:value":e[22]||(e[22]=t=>s.value.proxyHost=t),placeholder:"例如: 127.0.0.1"},null,8,["value"])])):d("",!0),s.value.proxy?(i(),p("div",rt,[e[64]||(e[64]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"端口",-1)),n(c,{value:s.value.proxyPort,"onUpdate:value":e[23]||(e[23]=t=>s.value.proxyPort=t),min:1,max:65535,style:{width:"100%"},placeholder:"例如: 1080"},null,8,["value"])])):d("",!0),s.value.proxy?(i(),p("div",it,[e[65]||(e[65]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"身份验证",-1)),n(h,{checked:s.value.proxyAuth,"onUpdate:checked":e[24]||(e[24]=t=>s.value.proxyAuth=t)},null,8,["checked"]),o("span",ut,y(s.value.proxyAuth?"需要验证":"无需验证"),1)])):d("",!0),s.value.proxy&&s.value.proxyAuth?(i(),p("div",pt,[e[66]||(e[66]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"用户名",-1)),n(U,{value:s.value.proxyUsername,"onUpdate:value":e[25]||(e[25]=t=>s.value.proxyUsername=t),placeholder:"请输入用户名"},null,8,["value"])])):d("",!0),s.value.proxy&&s.value.proxyAuth?(i(),p("div",dt,[e[67]||(e[67]=o("div",{style:{"font-weight":"600","margin-bottom":"8px"}},"密码",-1)),n(V,{value:s.value.proxyPassword,"onUpdate:value":e[26]||(e[26]=t=>s.value.proxyPassword=t),placeholder:"请输入密码"},null,8,["value"])])):d("",!0)])]),_:1},8,["open"])]),_:1})}}};export{mt as default}; diff --git a/webui/src/components/settings/workers.vue b/webui/src/components/settings/workers.vue index bb03cfa..7b8a89f 100644 --- a/webui/src/components/settings/workers.vue +++ b/webui/src/components/settings/workers.vue @@ -1,6 +1,7 @@