You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
9 lines
33 KiB
9 lines
33 KiB
import{g as Ae,C as Ee,z as ze,j as Re,c as i,k as m,v as L,a as e,d as c,F as x,r as S,t as d,D as le,m as C,q as ce,n as h,l as $,p as Y,s as w,A as me,x as E,E as je,o as r,_ as Fe}from"./CfpW9SkI.js";import{u as Pe}from"./DSu8q42f.js";import{u as Be}from"./CMwGY1Mu.js";import"./DL330dAC.js";const qe={class:"flex items-center justify-between mb-5"},He={key:0,class:"bg-white rounded-xl border border-slate-100 py-14 text-center"},Je={key:1,class:"bg-white rounded-xl border border-slate-100 py-14 text-center text-slate-400"},Ye={key:2,class:"space-y-4"},Ke=["onClick"],Ge={class:"chev"},Qe={class:"min-w-0 flex-1"},We={class:"flex items-center gap-2 flex-wrap"},Xe={class:"app-name"},Ze={key:1,class:"chip-mono"},et={class:"chip-count"},tt={class:"text-xs text-slate-400 mt-0.5 truncate"},st=["onClick"],nt=["onClick"],lt=["onClick"],ot={class:"app-body"},at={key:0,class:"text-center text-slate-300 py-5 text-xs"},it={key:1,class:"overflow-x-auto"},dt={class:"table table-sm text-sm"},rt={class:"font-medium text-slate-700 ml-1"},ut={class:"text-xs text-slate-400 ml-1 font-mono"},pt=["onUpdate:modelValue","disabled","onChange"],ct=["value"],mt={key:0,class:"badge badge-warning badge-sm text-amber-900"},bt={key:1,class:"badge badge-success badge-sm text-white"},gt={key:0,class:"chip-mono text-emerald-700"},xt={key:1,class:"text-amber-600 text-xs font-mono",title:"未固定版本,升级时建议选明确版本号(如 0.1.1)"},ft={class:"font-mono text-xs"},vt={key:0,class:"text-green-600"},_t={key:1,class:"text-slate-400"},yt={class:"text-right"},kt={class:"flex gap-1 justify-end"},ht=["onClick"],wt=["onClick"],Ct=["onClick"],Mt=["onClick"],Tt=["onClick"],$t=["onClick"],Nt=["onClick"],Ut={class:"flex items-center gap-3 mb-5"},Vt={class:"text-lg font-semibold text-slate-800"},St={class:"bg-white rounded-xl shadow-sm border border-slate-100 p-6 max-w-xl"},It={class:"form-control"},Lt={class:"form-control mt-4"},Ot={class:"form-control mt-4"},Dt={class:"flex items-center gap-2 mt-4"},At={class:"flex items-center gap-2 cursor-pointer"},Et={class:"flex gap-3 mt-6"},zt={class:"flex items-center gap-3 mb-5"},Rt={class:"text-lg font-semibold text-slate-800"},jt={class:"bg-white rounded-xl shadow-sm border border-slate-100 p-6 max-w-3xl"},Ft={key:0,class:"tabs tabs-boxed bg-slate-100 w-fit mb-5"},Pt={class:"grid grid-cols-2 gap-4"},Bt={class:"form-control"},qt=["value"],Ht={class:"form-control"},Jt={class:"grid grid-cols-2 gap-4 mt-4"},Yt={class:"form-control"},Kt=["value"],Gt={class:"form-control"},Qt={class:"label"},Wt={class:"label-text font-medium"},Xt={value:0},Zt=["value"],es={class:"grid grid-cols-3 gap-4 mt-4"},ts={class:"form-control"},ss={class:"form-control"},ns={class:"form-control"},ls={class:"form-control mt-4"},os={class:"label"},as={class:"label-text font-medium"},is={class:"form-control mt-4"},ds={class:"mt-4 text-sm"},rs={class:"form-control mt-4"},us={class:"flex items-center gap-2 mt-4"},ps={class:"flex items-center gap-2 cursor-pointer"},cs={class:"flex gap-3 mt-6"},ms=["disabled"],bs=["disabled"],gs={key:2,class:"text-xs text-slate-400 mt-2"},xs={class:"modal-box"},fs={class:"text-sm text-slate-500 mt-1"},vs={class:"grid grid-cols-2 gap-3 mt-4"},_s={key:0,class:"bg-slate-50 border border-slate-200 rounded-lg p-3"},ys={class:"flex justify-between items-center text-xs mb-1.5"},ks={class:"text-slate-600"},hs={class:"font-mono text-slate-400"},ws=["value"],Cs={class:"mt-2 text-xs space-y-0.5"},Ms={key:1,class:"text-red-600"},Ts={key:2,class:"bg-green-50 border border-green-100 rounded-lg p-3"},$s={class:"grid grid-cols-2 gap-x-4 gap-y-1 text-xs text-slate-600 mb-2"},Ns={class:"border-t border-green-100 pt-2 text-xs"},Us={class:"text-slate-500"},Vs={class:"font-mono text-slate-700"},Ss={class:"modal-action"},Is=["disabled"],Ls={class:"modal-box max-w-2xl"},Os={class:"text-sm text-slate-500 mt-1"},Ds={class:"grid grid-cols-1 gap-2 mt-4 text-xs"},As={class:"flex"},Es={class:"flex"},zs={class:"mt-3"},Rs={class:"flex items-center justify-between mb-1"},js=["disabled"],Fs={key:0,class:"flex gap-2"},Ps=["disabled"],Bs={value:""},qs=["value"],Hs=["value"],Js=["disabled"],Ys=["disabled"],Ks={key:2,class:"text-xs text-amber-600 mt-1"},Gs={key:3,class:"text-xs text-amber-600 mt-1"},Qs={class:"mt-3"},Ws={class:"flex items-center gap-2 text-xs mb-1"},Xs={key:0,class:"loading loading-spinner loading-xs text-amber-500"},Zs={key:1,class:"text-green-600 font-semibold"},en={key:2,class:"text-red-600 font-semibold"},tn={class:"text-slate-400"},sn={class:"modal-action"},nn=["disabled"],ln=["disabled"],on={method:"dialog",class:"modal-backdrop"},an=["disabled"],dn="__orphan__",rn=Ae({__name:"apps",setup(un){const{consoleApi:T}=Pe(),{success:N,error:k}=Be(),j=Ee(),K=ze(),O=w("list"),F=w([]),G=w([]),P=w([]),Q=w(!1),W=w(0),X=E({}),be=["中国","美国","东亚","东南亚","南亚","中东","欧洲","俄罗斯","非洲","南美","北美","大洋洲","巴西","印度"],oe=me(()=>{const n=new Map;for(const p of G.value){const b=p.app_name||"";n.has(b)||n.set(b,[]),n.get(b).push(p)}const t=new Set(F.value.map(p=>p.name)),s=F.value.map(p=>({key:p.name,product:p,deployments:n.get(p.name)||[]})),u=[];for(const[p,b]of n)(p===""||!t.has(p))&&u.push(...b);return u.length&&s.push({key:dn,product:null,deployments:u}),s});function ae(n){return X[n]!==!1}function ge(n){X[n]=X[n]===!1}function xe(n){if(!n)return"-";const t=P.value.find(s=>s.id===n);return t?t.name:"#"+n}function fe(n){const t=(n||"").match(/\/([^/?]+)\?/);return t?t[1]:"-"}function ve(n){const t=(n||"").trim();return!!t&&t!=="latest"}async function I(){Q.value=!0;try{const[n,t,s]=await Promise.all([T("products/list",{}),T("apps/list",{}),T("envs/list",{})]);F.value=n??[],G.value=t??[],P.value=s??[]}catch(n){k(n?.message??"获取应用列表失败")}finally{Q.value=!1}}function D(){O.value="list",I()}const B=w(!1),g=E({id:0,name:"",image_name:"",remark:"",enabled:!0});function ie(n){n?(B.value=!0,g.id=n.id,g.name=n.name||"",g.image_name=n.image_name||"",g.remark=n.remark||"",g.enabled=!!n.enabled):(B.value=!1,g.id=0,g.name="",g.image_name="",g.remark="",g.enabled=!0),O.value="productForm"}async function _e(){const n={name:g.name,image_name:g.image_name,remark:g.remark,enabled:g.enabled};let t="products/add";B.value&&g.id&&(n.id=Number(g.id),t="products/update");try{await T(t,n),N("保存成功"),D()}catch(s){k(s?.message??"失败")}}function ye(n){window.confirm(`删除应用「${n.name}」?(其下若有区域部署将被拒绝)`)&&T("products/del",{id:n.id}).then(()=>{N("已删除"),I()}).catch(t=>k(t?.message??"失败"))}const U=w(!1),f=w("existing"),a=E({id:0,app_name:"",name:"",region:"",env_id:0,base_url:"",service_dsn:"",deploy_dir:"",image_name:"",image_tag:"",remark:"",enabled:!0});function ke(n){return n&&F.value.find(s=>s.name===n)?.image_name||""}const he=me(()=>{const n=new Set(G.value.filter(t=>t.app_name===a.app_name&&t.id!==a.id).map(t=>t.region).filter(Boolean));return be.filter(t=>!n.has(t))}),z=w(!1),q=w(!1),v=E({show:!1,apiOk:!1,apiMsg:"",dbOk:!1,dbMsg:""});function H(){z.value=!1,v.show=!1,v.apiMsg="",v.dbMsg=""}function de(n){f.value=n,H()}function Z(n,t){t?(U.value=!0,f.value=t.status==="pending"?"deploy":"existing",a.id=t.id,a.app_name=t.app_name||n||"",a.name=t.name||"",a.region=t.region||"",a.env_id=t.env_id||0,a.base_url=t.base_url||"",a.service_dsn=t.service_dsn||"",a.deploy_dir=t.deploy_dir||"",a.image_name=t.image_name||"",a.image_tag=t.image_tag||"",a.remark=t.remark||"",a.enabled=!!t.enabled):(U.value=!1,f.value="existing",a.id=0,a.app_name=n||"",a.name="",a.region="",a.env_id=0,a.base_url="",a.service_dsn="",a.deploy_dir="",a.image_name=ke(n),a.image_tag="",a.remark="",a.enabled=!0),H(),O.value="deployForm"}async function we(){const n=a.base_url.trim(),t=a.service_dsn.trim();if(!n||!t){k("请先填应用地址和业务库 DSN");return}q.value=!0;try{const s=await T("apps/test",{base_url:n,service_dsn:t});v.apiOk=!!s.api_ok,v.apiMsg=s.api_msg,v.dbOk=!!s.db_ok,v.dbMsg=s.db_msg,v.show=!0,z.value=!!s.ok,z.value?N("检测通过,可以保存"):k("检测未通过")}catch(s){k(s?.message??"测试失败")}finally{q.value=!1}}async function Ce(){if(!a.app_name){k("请选择所属应用");return}if(!a.name.trim()){k("请填部署名称");return}if(f.value==="existing"&&!z.value){k("请先「测试连接」通过再保存");return}if(f.value==="deploy"&&!a.env_id){k("请选择部署目标环境");return}const n={app_name:a.app_name,status:f.value==="deploy"?"pending":"existing",name:a.name,region:a.region,env_id:Number(a.env_id)||0,base_url:f.value==="deploy"?"":a.base_url,service_dsn:a.service_dsn,deploy_dir:a.deploy_dir,image_name:a.image_name,image_tag:a.image_tag,remark:a.remark,enabled:a.enabled};let t="apps/add";U.value&&a.id&&(n.id=Number(a.id),t="apps/update");try{await T(t,n),N("保存成功"),D()}catch(s){k(s?.message??"失败")}}function Me(n){K.push({path:"/appconfig",query:{id:n.id,name:n.name||"部署#"+n.id,region:n.region||"",app_name:n.app_name||""}})}async function Te(n){W.value=n.id;try{await T("apps/bindenv",{id:n.id,env_id:Number(n.env_id)||0}),N("部署环境已更新")}catch(t){k(t?.message??"切换环境失败"),I()}finally{W.value=0}}function $e(n){window.confirm(`删除部署 #${n} ?(不影响应用自身的库)`)&&T("apps/del",{id:n}).then(()=>{N("已删除"),I()}).catch(t=>k(t?.message??"失败"))}const ee=w(null),o=E({appId:0,appLabel:"",start:"",end:"",resultShow:!1,resultClass:"",mode:"",steps:[],curIndex:0,curTotal:0,curName:"",pct:0,errMsg:"",report:null,running:!1,btnText:"开始分析"});function re(n){return parseInt((n||"").replace(/-/g,""))||0}function Ne(n,t){o.appId=n,o.appLabel="部署:"+t+" (#"+n+")",o.start="",o.end="",o.resultShow=!1,o.mode="",o.report=null,o.errMsg="",o.steps=[],o.curIndex=0,o.curTotal=0,o.curName="",o.pct=0,o.running=!1,o.btnText="开始分析",ee.value?.showModal()}function Ue(){ee.value?.close()}function Ve(n){o.steps[n.index-1]=n.name,o.pct=Math.round(n.index/(n.total+1)*100),o.curIndex=n.index,o.curTotal=n.total,o.curName=n.name}async function Se(){const n=o.appLabel,t=o.start||"最早",s=o.end||"至今";if(!window.confirm(`将先删除【${n}】在 ${t} ~ ${s} 区间的历史统计数据,然后重新分析写入。
|
|
|
|
确认继续?`))return;o.running=!0,o.btnText="分析中...",o.resultShow=!0,o.resultClass="",o.mode="progress",o.steps=[],o.curIndex=0,o.curTotal=0,o.curName="",o.pct=0,o.report=null,o.errMsg="";const u={app_id:o.appId,start_day:re(o.start),end_day:re(o.end)};let p=null,b=null,_=!1;function R(M){if(M=(M||"").trim(),!M)return;let y;try{y=JSON.parse(M)}catch{return}if(y.code===18){_=!0;return}if(y.code!==void 0&&y.code!==0){b=y.msg||"请求被拒绝";return}y.type==="progress"?Ve(y):y.type==="done"?p=y.report:y.type==="error"&&(b=y.msg)}try{const y=(await fetch("/console/api/apps/analyze",{method:"POST",headers:{"Content-Type":"application/json",Authorization:j.token},body:JSON.stringify(u)})).body.getReader(),A=new TextDecoder;let V="";for(;;){const pe=await y.read();if(pe.done)break;V+=A.decode(pe.value,{stream:!0});const se=V.split(`
|
|
`);V=se.pop();for(let ne=0;ne<se.length;ne++)R(se[ne])}R(V)}catch(M){b="网络异常: "+M.message}if(_){j.logout(),K.push("/");return}if(o.running=!1,o.btnText="重新分析",b){o.resultClass="text-red-600",o.mode="error",o.errMsg=b;return}if(!p){o.resultClass="text-red-600",o.mode="error",o.errMsg="分析中断,未收到完成事件";return}o.resultClass="",o.mode="done",o.report=p,N("分析完成"),I()}function Ie(n){window.confirm(`向部署 #${n} 发送一条配置 reload 事件?`)&&T("config/notify",{app_id:n,type:"all",action:"reload"}).then(t=>{N("已发送 → "+(t&&t.subject))}).catch(t=>k(t?.message??"失败"))}const te=w(null),J=w(null),l=E({id:0,label:"",envName:"",deployDir:"",tag:"",tags:[],tagMode:"list",tagsLoading:!1,tagsError:"",log:"",show:!1,running:!1,ok:!1,canRun:!1,btnText:"开始升级"});function Le(n){l.id=n.id,l.label="部署:"+(n.name||"部署#"+n.id)+" (#"+n.id+")"+(n.region?" · "+n.region:""),l.envName=n.env_id?xe(n.env_id):"",l.deployDir=n.deploy_dir||"",l.tag=n.image_tag||"",l.tags=[],l.tagMode="list",l.tagsError="",l.log="",l.show=!1,l.running=!1,l.ok=!1,l.canRun=!!(n.env_id&&(n.deploy_dir||"").trim()),l.btnText="开始升级",te.value?.showModal(),l.canRun&&ue()}async function ue(){if(l.id){l.tagsLoading=!0,l.tagsError="";try{const n=await T("apps/tags",{id:l.id});l.tags=n.tags||[],l.tagMode="list",(!l.tag||!l.tags.includes(l.tag))&&(l.tag=l.tag&&l.tags.includes(l.tag)?l.tag:l.tags.find(t=>t!=="latest")||l.tag||"")}catch(n){l.tagsError=n?.message??"拉取失败"}finally{l.tagsLoading=!1}}}function Oe(){l.running||te.value?.close()}async function De(){if(l.running)return;if(!l.canRun){const b=[];l.envName||b.push("绑定环境节点"),l.deployDir||b.push("填写部署目录"),window.alert(`无法升级:请先关闭本弹窗,点该部署的「编辑」完成 —— ${b.join("、")},保存后再来升级。`);return}const n=(l.tag||"").trim();if((!n||n==="latest")&&!window.confirm(`未选择明确版本号(当前 = ${n||"空 → 后端回退 latest"})。
|
|
latest 是可变 tag,升级后列表无法体现具体版本号,确定继续?`)||!window.confirm(`确认升级【${l.label}】?
|
|
目标版本:${n||"latest"}
|
|
将 SSH 连到「${l.envName}」,在 ${l.deployDir} 执行 docker compose 拉新镜像并重启容器。`))return;l.running=!0,l.show=!0,l.ok=!1,l.log="",l.btnText="升级中…";let t=null,s=!1;function u(b){l.log+=(l.log?`
|
|
`:"")+b,je(()=>{J.value&&(J.value.scrollTop=J.value.scrollHeight)})}function p(b){if(b=(b||"").trim(),!b)return;let _;try{_=JSON.parse(b)}catch{return}if(_.code===18){s=!0;return}if(_.code!==void 0&&_.code!==0&&_.type===void 0){t=_.msg||"请求被拒绝";return}_.type==="log"?u(_.line||""):_.type==="error"&&(t=_.msg,u("✗ "+(_.msg||"")))}try{const _=(await fetch("/console/api/apps/upgrade",{method:"POST",headers:{"Content-Type":"application/json",Authorization:j.token},body:JSON.stringify({id:l.id,tag:(l.tag||"").trim()})})).body.getReader(),R=new TextDecoder;let M="";for(;;){const y=await _.read();if(y.done)break;M+=R.decode(y.value,{stream:!0});const A=M.split(`
|
|
`);M=A.pop();for(let V=0;V<A.length;V++)p(A[V])}p(M)}catch(b){t="网络异常: "+b.message,u("✗ "+t)}if(s){j.logout(),K.push("/");return}l.running=!1,l.btnText="重新升级",l.ok=!t,t?k(t):(N("升级完成"),I())}return Re(I),(n,t)=>(r(),i("div",null,[m(e("div",null,[e("div",qe,[t[27]||(t[27]=e("div",null,[e("h2",{class:"text-lg font-semibold text-slate-800"},"应用管理"),e("p",{class:"text-sm text-slate-400 mt-1"},[e("span",{class:"chip-app"},"应用"),c(" 是一个产品(如 Voitrans);其下挂多条 "),e("span",{class:"chip-dep"},"部署"),c("(各区域一份)。 卡片 = 应用;卡片内表格 = 该应用的区域部署。 ")])],-1)),e("button",{class:"btn btn-primary btn-sm text-white",onClick:t[0]||(t[0]=s=>ie())},"+ 添加应用")]),Q.value?(r(),i("div",He,[...t[28]||(t[28]=[e("span",{class:"loading loading-spinner loading-sm"},null,-1)])])):oe.value.length?(r(),i("div",Ye,[(r(!0),i(x,null,S(oe.value,s=>(r(),i("div",{key:s.key,class:h(["app-card",{orphan:!s.product}])},[e("div",{class:"app-head",onClick:u=>ge(s.key)},[e("button",Ge,d(ae(s.key)?"▾":"▸"),1),e("div",{class:h(["app-logo",{"orphan-logo":!s.product}])},d(s.product?(s.product.name||"?").charAt(0).toUpperCase():"?"),3),e("div",Qe,[e("div",We,[t[29]||(t[29]=e("span",{class:"chip-app"},"应用",-1)),e("span",Xe,d(s.product?s.product.name:"未分组部署"),1),s.product?(r(),i("span",{key:0,class:h(["badge badge-xs",s.product.enabled?"badge-success":"badge-ghost"])},d(s.product.enabled?"启用":"停用"),3)):$("",!0),s.product&&s.product.image_name?(r(),i("span",Ze,"🐳 "+d(s.product.image_name),1)):$("",!0),e("span",et,d(s.deployments.length)+" 个部署",1)]),e("p",tt,d(s.product?s.product.remark||"—":"未填所属应用名、或应用已删除的历史部署;编辑部署可归位到某个应用"),1)]),s.product?(r(),i("div",{key:0,class:"flex gap-1 shrink-0",onClick:t[1]||(t[1]=le(()=>{},["stop"]))},[e("button",{class:"btn btn-primary btn-xs text-white",onClick:u=>Z(s.product.name)},"+ 部署",8,st),e("button",{class:"btn btn-ghost btn-xs",onClick:u=>ie(s.product)},"编辑应用",8,nt),e("button",{class:"btn btn-ghost btn-xs text-red-500",onClick:u=>ye(s.product)},"删除",8,lt)])):$("",!0)],8,Ke),m(e("div",ot,[s.deployments.length?(r(),i("div",it,[e("table",dt,[t[33]||(t[33]=e("thead",null,[e("tr",{class:"text-slate-400"},[e("th",null,"部署"),e("th",null,"区域"),e("th",null,"环境"),e("th",null,"状态"),e("th",null,"当前版本"),e("th",null,"业务库"),e("th",null,"启用"),e("th",{class:"text-right"},"操作")])],-1)),e("tbody",null,[(r(!0),i(x,null,S(s.deployments,u=>(r(),i("tr",{key:u.id,class:"dep-row hover"},[e("td",null,[t[31]||(t[31]=e("span",{class:"chip-dep"},"部署",-1)),e("span",rt,d(u.name||"部署#"+u.id),1),e("span",ut,"#"+d(u.id),1)]),e("td",null,d(u.region||"—"),1),e("td",null,[m(e("select",{"onUpdate:modelValue":p=>u.env_id=p,class:"select select-bordered select-xs w-36 text-xs",disabled:W.value===u.id,onChange:p=>Te(u)},[t[32]||(t[32]=e("option",{value:0},"未绑定",-1)),(r(!0),i(x,null,S(P.value,p=>(r(),i("option",{key:p.id,value:p.id},d(p.name),9,ct))),128))],40,pt),[[Y,u.env_id,void 0,{number:!0}]])]),e("td",null,[u.status==="pending"?(r(),i("span",mt,"待部署")):(r(),i("span",bt,"已接入"))]),e("td",null,[ve(u.image_tag)?(r(),i("span",gt,d(u.image_tag),1)):(r(),i("span",xt,d(u.image_tag||"latest")+" ⚠",1))]),e("td",ft,d(fe(u.service_dsn)),1),e("td",null,[u.enabled?(r(),i("span",vt,"是")):(r(),i("span",_t,"否"))]),e("td",yt,[e("div",kt,[u.status!=="pending"?(r(),i(x,{key:0},[e("button",{class:"btn btn-ghost btn-xs text-indigo-600",onClick:p=>Me(u)},"服务配置",8,ht),e("button",{class:"btn btn-ghost btn-xs text-slate-500",onClick:p=>Z(u.app_name,u)},"编辑",8,wt),e("button",{class:"btn btn-ghost btn-xs text-amber-600",onClick:p=>Le(u)},"升级",8,Ct),e("button",{class:"btn btn-ghost btn-xs text-emerald-600",onClick:p=>Ne(u.id,u.name||"部署#"+u.id)},"分析",8,Mt),e("button",{class:"btn btn-ghost btn-xs text-indigo-500",onClick:p=>Ie(u.id)},"发事件",8,Tt)],64)):(r(),i("button",{key:1,class:"btn btn-ghost btn-xs text-amber-600",onClick:p=>Z(u.app_name,u)},"编辑",8,$t)),e("button",{class:"btn btn-ghost btn-xs text-red-500",onClick:p=>$e(u.id)},"删除",8,Nt)])])]))),128))])])])):(r(),i("div",at,[t[30]||(t[30]=c(" 该应用暂无部署",-1)),s.product?(r(),i(x,{key:0},[c(",点右上「+ 部署」添加")],64)):$("",!0)]))],512),[[L,ae(s.key)]])],2))),128))])):(r(),i("div",Je,"暂无应用,先「添加应用」"))],512),[[L,O.value==="list"]]),m(e("div",null,[e("div",Ut,[e("button",{class:"btn btn-ghost btn-sm",onClick:t[2]||(t[2]=s=>D())},"← 返回"),e("h2",Vt,d(B.value?"编辑应用":"添加应用"),1)]),e("div",St,[e("form",{onSubmit:le(_e,["prevent"])},[e("div",It,[t[34]||(t[34]=e("label",{class:"label"},[e("span",{class:"label-text font-medium"},"应用名 *")],-1)),m(e("input",{"onUpdate:modelValue":t[3]||(t[3]=s=>g.name=s),type:"text",class:"input input-bordered w-full",required:"",placeholder:"如 Voitrans"},null,512),[[C,g.name]]),t[35]||(t[35]=e("span",{class:"text-xs text-slate-400 mt-1"},"账号作用域按应用名绑定,尽量与统计/账号里一致。",-1))]),e("div",Lt,[t[36]||(t[36]=e("label",{class:"label"},[e("span",{class:"label-text font-medium"},"默认镜像名")],-1)),m(e("input",{"onUpdate:modelValue":t[4]||(t[4]=s=>g.image_name=s),type:"text",class:"input input-bordered w-full",placeholder:"voitrans-a11(部署时用,可留空)"},null,512),[[C,g.image_name]])]),e("div",Ot,[t[37]||(t[37]=e("label",{class:"label"},[e("span",{class:"label-text font-medium"},"备注")],-1)),m(e("input",{"onUpdate:modelValue":t[5]||(t[5]=s=>g.remark=s),type:"text",class:"input input-bordered w-full"},null,512),[[C,g.remark]])]),e("div",Dt,[e("label",At,[m(e("input",{"onUpdate:modelValue":t[6]||(t[6]=s=>g.enabled=s),type:"checkbox",class:"toggle toggle-primary toggle-sm"},null,512),[[ce,g.enabled]]),t[38]||(t[38]=e("span",{class:"text-sm"},"启用",-1))])]),e("div",Et,[e("button",{type:"button",class:"btn btn-ghost",onClick:t[7]||(t[7]=s=>D())},"取消"),t[39]||(t[39]=e("button",{type:"submit",class:"btn btn-primary text-white"},"保存",-1))])],32)])],512),[[L,O.value==="productForm"]]),m(e("div",null,[e("div",zt,[e("button",{class:"btn btn-ghost btn-sm",onClick:t[8]||(t[8]=s=>D())},"← 返回"),e("h2",Rt,d(U.value?"编辑部署":"添加部署"),1)]),e("div",jt,[e("form",{onSubmit:le(Ce,["prevent"])},[U.value?$("",!0):(r(),i("div",Ft,[e("a",{class:h(["tab",f.value==="existing"?"tab-active":""]),onClick:t[9]||(t[9]=s=>de("existing"))},"添加现有",2),e("a",{class:h(["tab",f.value==="deploy"?"tab-active":""]),onClick:t[10]||(t[10]=s=>de("deploy"))},"部署到目标环境",2)])),e("p",{class:h(["text-xs text-slate-400 mb-4",U.value?"":"-mt-3"])},[U.value?(r(),i(x,{key:0},[c("编辑该区域部署的配置(镜像 Tag / 部署目录 / 环境节点 等);改好后回列表点「升级」执行远程部署(拉新镜像并重启)。")],64)):f.value==="existing"?(r(),i(x,{key:1},[c("把「已经部署好的应用区域」加进来:填地址与业务库 DSN,测试通过后保存。")],64)):(r(),i(x,{key:2},[t[40]||(t[40]=c("选一个「部署目标环境」,先建关联并标记",-1)),t[41]||(t[41]=e("b",null,"待部署",-1)),t[42]||(t[42]=c(";保存后回列表点「升级」执行远程部署。",-1))],64))],2),e("div",Pt,[e("div",Bt,[t[43]||(t[43]=e("label",{class:"label"},[e("span",{class:"label-text font-medium"},"所属应用")],-1)),e("input",{value:a.app_name,type:"text",class:"input input-bordered w-full bg-slate-100",readonly:""},null,8,qt)]),e("div",Ht,[t[44]||(t[44]=e("label",{class:"label"},[e("span",{class:"label-text font-medium"},"部署名称 *")],-1)),m(e("input",{"onUpdate:modelValue":t[11]||(t[11]=s=>a.name=s),type:"text",class:"input input-bordered w-full",required:"",placeholder:"如 Voitrans(国内)"},null,512),[[C,a.name]])])]),e("div",Jt,[e("div",Yt,[t[46]||(t[46]=e("label",{class:"label"},[e("span",{class:"label-text font-medium"},"区域")],-1)),m(e("select",{"onUpdate:modelValue":t[12]||(t[12]=s=>a.region=s),class:"select select-bordered w-full"},[t[45]||(t[45]=e("option",{value:""},"未指定",-1)),(r(!0),i(x,null,S(he.value,s=>(r(),i("option",{key:s,value:s},d(s),9,Kt))),128))],512),[[Y,a.region]]),t[47]||(t[47]=e("span",{class:"text-xs text-slate-400 mt-1"},"已部署的区域不再出现(一个应用每区域一份部署)。",-1))]),e("div",Gt,[e("label",Qt,[e("span",Wt,"部署环境"+d(f.value==="deploy"?" *":""),1)]),m(e("select",{"onUpdate:modelValue":t[13]||(t[13]=s=>a.env_id=s),class:"select select-bordered w-full"},[e("option",Xt,d(f.value==="deploy"?"请选择目标环境":"未关联"),1),(r(!0),i(x,null,S(P.value,s=>(r(),i("option",{key:s.id,value:s.id},d(s.name)+"("+d(s.code||"-")+")",9,Zt))),128))],512),[[Y,a.env_id,void 0,{number:!0}]])])]),e("div",es,[e("div",ts,[t[48]||(t[48]=e("label",{class:"label"},[e("span",{class:"label-text font-medium"},"部署目录")],-1)),m(e("input",{"onUpdate:modelValue":t[14]||(t[14]=s=>a.deploy_dir=s),type:"text",class:"input input-bordered w-full font-mono text-xs",placeholder:"/home/work/voitrans-a11"},null,512),[[C,a.deploy_dir]])]),e("div",ss,[t[49]||(t[49]=e("label",{class:"label"},[e("span",{class:"label-text font-medium"},"镜像名")],-1)),m(e("input",{"onUpdate:modelValue":t[15]||(t[15]=s=>a.image_name=s),type:"text",class:"input input-bordered w-full",placeholder:"voitrans-a11(默认取应用)"},null,512),[[C,a.image_name]])]),e("div",ns,[t[50]||(t[50]=e("label",{class:"label"},[e("span",{class:"label-text font-medium"},"镜像 Tag")],-1)),m(e("input",{"onUpdate:modelValue":t[16]||(t[16]=s=>a.image_tag=s),type:"text",class:"input input-bordered w-full",placeholder:"0.1.1 / latest"},null,512),[[C,a.image_tag]])])]),e("div",ls,[e("label",os,[e("span",as,"业务库 DSN"+d(f.value==="existing"?" *":""),1)]),m(e("input",{"onUpdate:modelValue":t[17]||(t[17]=s=>a.service_dsn=s),type:"text",class:"input input-bordered w-full font-mono text-xs",placeholder:"root:pwd@tcp(host:3306)/earphone?charset=utf8mb4&parseTime=True&loc=Local",onInput:H},null,544),[[C,a.service_dsn]]),t[51]||(t[51]=e("span",{class:"text-xs text-slate-400 mt-1"},"用于读取业务数据(重置设备码时清 userdevice/product_stat)",-1))]),f.value==="existing"?(r(),i(x,{key:1},[e("div",is,[t[52]||(t[52]=e("label",{class:"label"},[e("span",{class:"label-text font-medium"},"应用地址 *")],-1)),m(e("input",{"onUpdate:modelValue":t[18]||(t[18]=s=>a.base_url=s),type:"text",class:"input input-bordered w-full",placeholder:"https://api.xxx.com/",onInput:H},null,544),[[C,a.base_url]]),t[53]||(t[53]=e("span",{class:"text-xs text-slate-400 mt-1"},"用于通知应用业务服务事件(如配置更新)",-1))]),m(e("div",ds,[e("div",{class:h(v.apiOk?"text-green-700":"text-red-600")},[e("span",{class:h(v.apiOk?"text-green-600":"text-red-500")},d(v.apiOk?"✓":"✗"),3),c(" 应用地址:"+d(v.apiMsg),1)],2),e("div",{class:h(v.dbOk?"text-green-700":"text-red-600")},[e("span",{class:h(v.dbOk?"text-green-600":"text-red-500")},d(v.dbOk?"✓":"✗"),3),c(" 业务库:"+d(v.dbMsg),1)],2)],512),[[L,v.show]])],64)):$("",!0),e("div",rs,[t[54]||(t[54]=e("label",{class:"label"},[e("span",{class:"label-text font-medium"},"备注")],-1)),m(e("input",{"onUpdate:modelValue":t[19]||(t[19]=s=>a.remark=s),type:"text",class:"input input-bordered w-full"},null,512),[[C,a.remark]])]),e("div",us,[e("label",ps,[m(e("input",{"onUpdate:modelValue":t[20]||(t[20]=s=>a.enabled=s),type:"checkbox",class:"toggle toggle-primary toggle-sm"},null,512),[[ce,a.enabled]]),t[55]||(t[55]=e("span",{class:"text-sm"},"启用",-1))])]),e("div",cs,[e("button",{type:"button",class:"btn btn-ghost",onClick:t[21]||(t[21]=s=>D())},"取消"),f.value==="existing"?(r(),i("button",{key:0,type:"button",class:"btn btn-outline",disabled:q.value,onClick:we},d(q.value?"测试中...":"测试连接"),9,ms)):$("",!0),e("button",{type:"submit",class:"btn btn-primary text-white",disabled:!U.value&&f.value==="existing"&&!z.value},"保存",8,bs)]),!U.value&&f.value==="existing"?(r(),i("p",gs,"需先「测试连接」通过(应用地址可达 + 业务库可连)才能保存。")):$("",!0)],32)])],512),[[L,O.value==="deployForm"]]),e("dialog",{ref_key:"analyzeModal",ref:ee,class:"modal"},[e("div",xs,[t[63]||(t[63]=e("h3",{class:"font-bold text-lg"},"分析历史数据",-1)),e("p",fs,d(o.appLabel),1),t[64]||(t[64]=e("div",{class:"text-xs text-amber-700 mt-3 bg-amber-50 border border-amber-100 rounded-lg p-3 leading-relaxed"},[c(" 扫描该应用业务库,按 "),e("b",null,"天 + 产品 + 区域"),c(" 生成历史统计(新增用户 / 订单 / 资源发放·使用 / 激活绑定设备),汇入 console 主库。"),e("br"),c(" · "),e("b",null,"分析前会先清理该应用+区域在所选区间内的旧统计行"),c(",避免数据重叠。"),e("br"),c(" · 登录、活跃用户、AI对话等无每日原始记录,历史无法还原(由实时管道往后采集)。"),e("br"),c(" · 仅对实时管道上线前的区间运行。留空 = 全部历史。大库可能耗时。 ")],-1)),e("div",vs,[e("div",null,[t[56]||(t[56]=e("label",{class:"text-xs text-slate-500"},"起始日(含)",-1)),m(e("input",{"onUpdate:modelValue":t[22]||(t[22]=s=>o.start=s),type:"date",class:"input input-bordered input-sm w-full"},null,512),[[C,o.start]])]),e("div",null,[t[57]||(t[57]=e("label",{class:"text-xs text-slate-500"},"结束日(含)",-1)),m(e("input",{"onUpdate:modelValue":t[23]||(t[23]=s=>o.end=s),type:"date",class:"input input-bordered input-sm w-full"},null,512),[[C,o.end]])])]),m(e("div",{class:h(["text-sm mt-3",o.resultClass])},[o.mode==="progress"?(r(),i("div",_s,[e("div",ys,[e("span",ks,[o.curName?(r(),i(x,{key:0},[t[58]||(t[58]=e("span",{class:"loading loading-spinner loading-xs"},null,-1)),c(" 正在:"+d(o.curName)+" ("+d(o.curIndex)+"/"+d(o.curTotal)+") ",1)],64)):(r(),i(x,{key:1},[c("准备中...")],64))]),e("span",hs,d(o.pct)+"%",1)]),e("progress",{class:"progress progress-primary w-full h-2",value:o.pct,max:"100"},null,8,ws),e("div",Cs,[(r(!0),i(x,null,S(o.steps,(s,u)=>(r(),i("div",{key:u,class:h(u===o.curIndex-1?"text-slate-600":"text-green-600")},[u===o.curIndex-1?(r(),i(x,{key:0},[t[59]||(t[59]=e("span",{class:"loading loading-spinner loading-xs"},null,-1)),c(" "+d(s),1)],64)):(r(),i(x,{key:1},[c("✓ "+d(s),1)],64))],2))),128))])])):o.mode==="error"?(r(),i("div",Ms,"✗ "+d(o.errMsg),1)):o.mode==="done"&&o.report?(r(),i("div",Ts,[t[62]||(t[62]=e("div",{class:"font-semibold text-green-700 mb-2"},"✓ 分析完成",-1)),e("div",$s,[e("div",null,[t[60]||(t[60]=c("应用:",-1)),e("b",null,d(o.report.app_name||""),1)]),e("div",null,"区域:"+d(o.report.region||"未指定"),1),e("div",null,"区间:"+d((o.report.start_day||"不限")+" ~ "+(o.report.end_day||"不限")),1),e("div",null,"清理旧行:"+d(o.report.cleared_rows||0),1),e("div",null,[t[61]||(t[61]=c("写入天数:",-1)),e("b",null,d(o.report.days_written||0),1)]),e("div",null,"写入行数:"+d(o.report.rows_written||0),1)]),e("div",Ns,[(r(!0),i(x,null,S(o.report.steps||[],(s,u)=>(r(),i("div",{key:u,class:"flex justify-between py-0.5"},[e("span",Us,d(s.name),1),e("span",Vs,d((s.groups||0)+" 组"),1)]))),128))])])):$("",!0)],2),[[L,o.resultShow]]),e("div",Ss,[e("button",{class:"btn btn-ghost btn-sm",onClick:Ue},"关闭"),e("button",{class:"btn btn-primary btn-sm text-white",disabled:o.running,onClick:Se},d(o.btnText),9,Is)])]),t[65]||(t[65]=e("form",{method:"dialog",class:"modal-backdrop"},[e("button",null,"close")],-1))],512),e("dialog",{ref_key:"upgradeModal",ref:te,class:"modal"},[e("div",Ls,[t[69]||(t[69]=e("h3",{class:"font-bold text-lg"},"升级部署",-1)),e("p",Os,d(l.label),1),t[70]||(t[70]=e("div",{class:"text-xs text-amber-700 mt-3 bg-amber-50 border border-amber-100 rounded-lg p-3 leading-relaxed"},[c(" 将 "),e("b",null,"SSH 连接到该部署绑定的环境节点"),c(",进入部署目录执行:"),e("span",{class:"font-mono"},"docker login → compose pull → up -d 重启 → 清理旧镜像"),c("。"),e("br"),c(" · 需服务器上已维护好 "),e("span",{class:"font-mono"},"docker-compose.yml / .env / confs"),c("(本操作不上传真实配置)。"),e("br"),c(" · 会"),e("b",null,"重启该部署的容器"),c(",请确认无误后再执行。 ")],-1)),e("div",Ds,[e("div",As,[t[66]||(t[66]=e("span",{class:"w-20 text-slate-400 shrink-0"},"环境节点",-1)),e("span",{class:h(l.envName?"font-medium text-slate-700":"font-medium text-red-500")},d(l.envName||"未绑定 —— 请先在「编辑」里选择环境节点"),3)]),e("div",Es,[t[67]||(t[67]=e("span",{class:"w-20 text-slate-400 shrink-0"},"部署目录",-1)),e("span",{class:h(l.deployDir?"font-mono text-slate-700":"font-medium text-red-500")},d(l.deployDir||"未填 —— 请先在「编辑」里填写远端部署目录"),3)])]),e("div",zs,[e("div",Rs,[t[68]||(t[68]=e("label",{class:"text-xs text-slate-500"},"镜像版本 Tag",-1)),e("button",{type:"button",class:"text-xs text-indigo-500 hover:underline",disabled:l.running,onClick:t[24]||(t[24]=s=>l.tagMode=l.tagMode==="list"?"custom":"list")},d(l.tagMode==="list"?"手动输入…":"从仓库选择"),9,js)]),l.tagMode==="list"?(r(),i("div",Fs,[m(e("select",{"onUpdate:modelValue":t[25]||(t[25]=s=>l.tag=s),class:"select select-bordered select-sm flex-1 font-mono",disabled:l.running||l.tagsLoading},[e("option",Bs,d(l.tagsLoading?"加载可用版本中…":"请选择版本"),1),l.tag&&!l.tags.includes(l.tag)?(r(),i("option",{key:0,value:l.tag},d(l.tag)+"(当前记录)",9,qs)):$("",!0),(r(!0),i(x,null,S(l.tags,s=>(r(),i("option",{key:s,value:s},d(s)+d(s==="latest"?" · 不固定,不推荐":""),9,Hs))),128))],8,Ps),[[Y,l.tag]]),e("button",{type:"button",class:"btn btn-ghost btn-sm",disabled:l.running||l.tagsLoading,onClick:ue},"刷新",8,Js)])):m((r(),i("input",{key:1,"onUpdate:modelValue":t[26]||(t[26]=s=>l.tag=s),type:"text",class:"input input-bordered input-sm w-full font-mono",placeholder:"0.1.1",disabled:l.running},null,8,Ys)),[[C,l.tag]]),l.tagsError?(r(),i("p",Ks,"拉取仓库版本失败:"+d(l.tagsError)+"(可点上方「手动输入」直接填版本号,或点「刷新」重试)",1)):l.tag==="latest"||l.tag===""?(r(),i("p",Gs,"⚠ 未固定版本,升级后列表无法体现具体版本号,建议选明确版本(如 0.1.1)。")):$("",!0)]),m(e("div",Qs,[e("div",Ws,[l.running?(r(),i("span",Xs)):l.ok?(r(),i("span",Zs,"✓ 部署完成")):(r(),i("span",en,"✗ 部署失败")),e("span",tn,d(l.running?"执行中…":""),1)]),e("pre",{ref_key:"upLogBox",ref:J,class:"bg-slate-900 text-slate-100 text-[11px] leading-relaxed rounded-lg p-3 h-64 overflow-auto font-mono whitespace-pre-wrap"},d(l.log||"(等待输出…)"),513)],512),[[L,l.show]]),e("div",sn,[e("button",{class:"btn btn-ghost btn-sm",disabled:l.running,onClick:Oe},"关闭",8,nn),e("button",{class:"btn btn-warning btn-sm",disabled:l.running,onClick:De},d(l.btnText),9,ln)])]),e("form",on,[e("button",{disabled:l.running},"close",8,an)])],512)]))}}),gn=Fe(rn,[["__scopeId","data-v-955faaec"]]);export{gn as default};
|
|
|