Files
flvx/deployment/dist/js/app.9855802f.js
T
2025-06-12 15:32:13 +08:00

2 lines
95 KiB
JavaScript

(function(){"use strict";var t={3705:function(t,e,s){s(8111),s(2489);var i=s(5471),a=function(){var t=this,e=t._self._c;return e("div",{attrs:{id:"app"}},[e("router-view")],1)},n=[],l={name:"App",created(){this.setRem(),window.addEventListener("resize",this.setRem)},beforeDestroy(){window.removeEventListener("resize",this.setRem)},methods:{setRem(){const t=document.documentElement.clientWidth||document.body.clientWidth;if(t<768){const e=t/375;document.documentElement.style.fontSize=14*e+"px"}else document.documentElement.style.fontSize="16px"}}},o=l,r=s(1656),d=(0,r.A)(o,a,n,!1,null,null,null),c=d.exports,u=s(173),m=function(){var t=this,e=t._self._c;return e("el-container",[e("el-header",{staticStyle:{height:"50px"}},[e("span",{staticStyle:{"line-height":"50px"}},[t._v("管理后台")]),e("div",{staticClass:"header-avatar"},[e("el-avatar",{staticStyle:{"margin-right":"10px"},attrs:{size:"medium",src:"https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"}}),e("el-dropdown",[e("span",{staticClass:"el-dropdown-link",staticStyle:{color:"white"}},[t._v(t._s(t.name))]),e("el-dropdown-menu",{attrs:{slot:"dropdown"},slot:"dropdown"},[e("el-dropdown-item",{nativeOn:{click:function(e){return t.showPasswordDialog.apply(null,arguments)}}},[e("i",{staticClass:"el-icon-key"}),t._v(" 修改密码 ")]),e("el-dropdown-item",{nativeOn:{click:function(e){return t.logout.apply(null,arguments)}}},[e("i",{staticClass:"el-icon-switch-button"}),t._v(" 退出登录 ")])],1)],1)],1)]),e("el-container",[e("el-aside",{attrs:{width:"200px"}},[e("el-menu",{staticClass:"el-menu-vertical-demo",attrs:{"default-active":t.path}},[e("router-link",{attrs:{to:"/index"}},[e("el-menu-item",{attrs:{index:"/index"},on:{click:function(e){return t.save("/index")}}},[e("template",{slot:"title"},[e("i",{staticClass:"el-icon-box"}),e("span",{attrs:{slot:"title"},slot:"title"},[t._v("首页")])])],2)],1),e("router-link",{directives:[{name:"admin",rawName:"v-admin"}],attrs:{to:"/limit"}},[e("el-menu-item",{attrs:{index:"/limit"},on:{click:function(e){return t.save("/limit")}}},[e("template",{slot:"title"},[e("i",{staticClass:"el-icon-box"}),e("span",{attrs:{slot:"title"},slot:"title"},[t._v("限速管理")])])],2)],1),e("router-link",{attrs:{to:"/forward"}},[e("el-menu-item",{attrs:{index:"/forward"},on:{click:function(e){return t.save("/forward")}}},[e("template",{slot:"title"},[e("i",{staticClass:"el-icon-postcard"}),e("span",{attrs:{slot:"title"},slot:"title"},[t._v("转发管理")])])],2)],1),e("router-link",{directives:[{name:"admin",rawName:"v-admin"}],attrs:{to:"/tunnel"}},[e("el-menu-item",{attrs:{index:"/tunnel"},on:{click:function(e){return t.save("/tunnel")}}},[e("template",{slot:"title"},[e("i",{staticClass:"el-icon-postcard"}),e("span",{attrs:{slot:"title"},slot:"title"},[t._v("隧道管理")])])],2)],1),e("router-link",{directives:[{name:"admin",rawName:"v-admin"}],attrs:{to:"/node"}},[e("el-menu-item",{attrs:{index:"/node"},on:{click:function(e){return t.save("/node")}}},[e("template",{slot:"title"},[e("i",{staticClass:"el-icon-postcard"}),e("span",{attrs:{slot:"title"},slot:"title"},[t._v("节点监控")])])],2)],1),e("router-link",{directives:[{name:"admin",rawName:"v-admin"}],attrs:{to:"/user"}},[e("el-menu-item",{attrs:{index:"/user"},on:{click:function(e){return t.save("/user")}}},[e("template",{slot:"title"},[e("i",{staticClass:"el-icon-user"}),e("span",{attrs:{slot:"title"},slot:"title"},[t._v("用户管理")])])],2)],1)],1)],1),e("el-main",[e("div",{staticStyle:{margin:"15px 15px"}},[e("router-view"),e("div",{staticStyle:{"background-color":"white",height:"50px",position:"absolute",bottom:"0",left:"199px",right:"0","line-height":"50px","padding-left":"30px","font-size":"12px",color:"#58666e"}},[t._v(" © 2023 Copyright. ")])],1)])],1),e("el-dialog",{attrs:{title:"修改密码",visible:t.passwordDialogVisible,width:"400px","before-close":t.handlePasswordDialogClose},on:{"update:visible":function(e){t.passwordDialogVisible=e}}},[e("el-form",{ref:"passwordForm",attrs:{model:t.passwordForm,rules:t.passwordRules,"label-width":"100px"}},[e("el-form-item",{attrs:{label:"当前密码",prop:"currentPassword"}},[e("el-input",{attrs:{type:"password",placeholder:"请输入当前密码","show-password":"",clearable:""},model:{value:t.passwordForm.currentPassword,callback:function(e){t.$set(t.passwordForm,"currentPassword",e)},expression:"passwordForm.currentPassword"}})],1),e("el-form-item",{attrs:{label:"新密码",prop:"newPassword"}},[e("el-input",{attrs:{type:"password",placeholder:"请输入新密码","show-password":"",clearable:""},model:{value:t.passwordForm.newPassword,callback:function(e){t.$set(t.passwordForm,"newPassword",e)},expression:"passwordForm.newPassword"}})],1),e("el-form-item",{attrs:{label:"确认密码",prop:"confirmPassword"}},[e("el-input",{attrs:{type:"password",placeholder:"请再次输入新密码","show-password":"",clearable:""},model:{value:t.passwordForm.confirmPassword,callback:function(e){t.$set(t.passwordForm,"confirmPassword",e)},expression:"passwordForm.confirmPassword"}})],1)],1),e("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[e("el-button",{on:{click:t.handlePasswordDialogClose}},[t._v("取 消")]),e("el-button",{attrs:{type:"primary",loading:t.passwordLoading},on:{click:t.submitPasswordChange}},[t._v("确 定")])],1)],1)],1)},p=[],h=(s(4114),s(4335)),f={get:function(t="",e={}){return new Promise((function(s,i){h.A.get(t,{params:e,timeout:6e4,headers:{Authorization:window.localStorage.getItem("token")}}).then((function(t){s(t.data)})).catch((function(t){console.error("GET请求错误:",t),s({code:-1,msg:t.message||"网络请求失败"})}))}))},post:function(t="",e={}){return new Promise((function(s,i){h.A.post(t,e,{timeout:6e4,headers:{Authorization:window.localStorage.getItem("token"),"Content-Type":"application/json"}}).then((function(t){s(t.data)})).catch((function(t){console.error("POST请求错误:",t),s({code:-1,msg:t.message||"网络请求失败"})}))}))},put:function(t="",e={}){return new Promise((function(s,i){h.A.put(t,e,{timeout:6e4,headers:{Authorization:window.localStorage.getItem("token"),"Content-Type":"application/json"}}).then((function(t){s(t.data)})).catch((function(t){console.error("PUT请求错误:",t),s({code:-1,msg:t.message||"网络请求失败"})}))}))},delete:function(t=""){return new Promise((function(e,s){h.A.delete(t,{timeout:6e4,headers:{Authorization:window.localStorage.getItem("token")}}).then((function(t){e(t.data)})).catch((function(t){console.error("DELETE请求错误:",t),e({code:-1,msg:t.message||"网络请求失败"})}))}))}};const g=t=>f.post("/user/login",t),w=t=>f.post("/user/create",t),v=(t={})=>f.post("/user/list",t),b=t=>f.post("/user/update",t),_=t=>f.post("/user/delete",{id:t}),y=()=>f.post("/user/package"),C=t=>f.post("/node/create",t),F=()=>f.post("/node/list"),T=t=>f.post("/node/update",t),x=t=>f.post("/node/delete",{id:t}),S=t=>f.post("/node/install",{id:t}),k=t=>f.post("/tunnel/create",t),L=()=>f.post("/tunnel/list"),$=t=>f.post("/tunnel/delete",{id:t}),I=t=>f.post("/tunnel/user/assign",t),E=(t={})=>f.post("/tunnel/user/list",t),P=t=>f.post("/tunnel/user/remove",t),N=t=>f.post("/tunnel/user/update",t),A=()=>f.post("/tunnel/user/tunnel"),R=t=>f.post("/forward/create",t),U=()=>f.post("/forward/list"),D=t=>f.post("/forward/update",t),V=t=>f.post("/forward/delete",{id:t}),B=t=>f.post("/forward/pause",{id:t}),z=t=>f.post("/forward/resume",{id:t}),O=t=>f.post("/speed-limit/create",t),q=()=>f.post("/speed-limit/list"),M=t=>f.post("/speed-limit/update",t),W=t=>f.post("/speed-limit/delete",{id:t}),K=t=>f.post("/user/updatePassword",t);var G={name:"home",data(){return{path:"",name:"",passwordDialogVisible:!1,passwordLoading:!1,passwordForm:{currentPassword:"",newPassword:"",confirmPassword:""},passwordRules:{currentPassword:[{required:!0,message:"请输入当前密码",trigger:"blur"},{min:1,message:"密码不能为空",trigger:"blur"}],newPassword:[{required:!0,message:"请输入新密码",trigger:"blur"},{min:6,message:"新密码长度不能少于6位",trigger:"blur"},{max:20,message:"新密码长度不能超过20位",trigger:"blur"}],confirmPassword:[{required:!0,message:"请再次输入新密码",trigger:"blur"},{validator:this.validateConfirmPassword,trigger:"blur"}]}}},created(){this.path=localStorage.getItem("e"),this.name=localStorage.getItem("name")},methods:{save(t){localStorage.setItem("e",t),this.path=t},logout(){localStorage.clear(),this.$router.push("/login")},showPasswordDialog(){this.passwordDialogVisible=!0},handlePasswordDialogClose(){this.passwordDialogVisible=!1,this.resetPasswordForm()},resetPasswordForm(){this.passwordForm={currentPassword:"",newPassword:"",confirmPassword:""},this.$refs.passwordForm&&this.$refs.passwordForm.clearValidate()},validateConfirmPassword(t,e,s){""===e?s(new Error("请再次输入密码")):e!==this.passwordForm.newPassword?s(new Error("两次输入密码不一致")):s()},async submitPasswordChange(){try{await this.$refs.passwordForm.validate(),this.passwordLoading=!0;const t=await K(this.passwordForm);0===t.code?(this.$message.success(t.msg||"密码修改成功"),this.handlePasswordDialogClose(),this.$confirm("密码修改成功,请重新登录","提示",{confirmButtonText:"确定",type:"success",showCancelButton:!1}).then((()=>{this.logout()})).catch((()=>{this.logout()}))):this.$message.error(t.msg||"密码修改失败")}catch(t){"validate"!==t&&(this.$message.error("修改密码时发生错误"),console.error("修改密码错误:",t))}finally{this.passwordLoading=!1}}}},j=G,Y=(0,r.A)(j,m,p,!1,null,"ddcf7afa",null),J=Y.exports,H=function(){var t=this,e=t._self._c;return e("div",{staticClass:"login-container"},[t._m(0),e("div",{staticClass:"login-wrapper"},[e("div",{staticClass:"login-form-container"},[e("div",{staticClass:"login-form"},[t._m(1),e("el-form",{ref:"loginForm",staticClass:"login-form-content",attrs:{model:t.loginForm,rules:t.rules},nativeOn:{keyup:function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:t.handleLogin.apply(null,arguments)}}},[e("el-form-item",{attrs:{prop:"username"}},[e("div",{staticClass:"input-wrapper"},[e("el-input",{staticClass:"custom-input",attrs:{"prefix-icon":"el-icon-user",placeholder:"请输入用户名",clearable:"",size:"large"},model:{value:t.loginForm.username,callback:function(e){t.$set(t.loginForm,"username",e)},expression:"loginForm.username"}})],1)]),e("el-form-item",{attrs:{prop:"password"}},[e("div",{staticClass:"input-wrapper"},[e("el-input",{staticClass:"custom-input",attrs:{"prefix-icon":"el-icon-lock",placeholder:"请输入密码","show-password":"",size:"large"},model:{value:t.loginForm.password,callback:function(e){t.$set(t.loginForm,"password",e)},expression:"loginForm.password"}})],1)]),e("el-form-item",{staticClass:"login-btn-item"},[e("el-button",{staticClass:"login-btn",attrs:{type:"primary",loading:t.loading,size:"large"},on:{click:t.handleLogin}},[t.loading?e("span",[t._v("登录中...")]):e("span",[t._v("立即登录")])])],1)],1),t._m(2)],1)])])])},Q=[function(){var t=this,e=t._self._c;return e("div",{staticClass:"bg-decoration"},[e("div",{staticClass:"circle circle-1"}),e("div",{staticClass:"circle circle-2"}),e("div",{staticClass:"circle circle-3"})])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"form-header"},[e("h2",{staticClass:"form-title"},[t._v("账户登录")]),e("p",{staticClass:"form-subtitle"},[t._v("请输入您的登录凭据")])])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"form-footer"},[e("p",{staticClass:"copyright"},[t._v("© 2024 管理系统. All rights reserved.")])])}],X={name:"Login",data(){return{loginForm:{username:"",password:""},rules:{username:[{required:!0,message:"请输入用户名",trigger:"blur"}],password:[{required:!0,message:"请输入密码",trigger:"blur"},{min:6,message:"密码长度至少6位",trigger:"blur"}]},loading:!1}},methods:{handleLogin(){this.$refs.loginForm.validate((t=>{t&&(this.loading=!0,g(this.loginForm).then((t=>{if(this.loading=!1,0!==t.code)return this.$message.error(t.msg);localStorage.setItem("token",t.data.token),localStorage.setItem("e","/index"),localStorage.setItem("role_id",t.data.role_id),localStorage.setItem("name",t.data.name),this.$message.success("登录成功"),this.$router.push("/index")})).catch((()=>{this.loading=!1})))}))}}},Z=X,tt=(0,r.A)(Z,H,Q,!1,null,"354d4782",null),et=tt.exports,st=function(){var t=this,e=t._self._c;return e("div",{staticClass:"package-container"},[e("div",{staticClass:"overview-section"},[e("div",{staticClass:"overview-card"},[e("div",{staticClass:"card-header"},[e("h3",[t._v("账户概览")]),t.userInfo.expTime?e("el-tag",{attrs:{type:t.getExpStatus(t.userInfo.expTime).type}},[t._v(" "+t._s(t.getExpStatus(t.userInfo.expTime).text)+" ")]):e("el-tag",{attrs:{type:"success"}},[t._v("永久账户")])],1),e("div",{staticClass:"overview-stats"},[e("div",{staticClass:"stat-item"},[t._m(0),e("div",{staticClass:"stat-content"},[e("div",{staticClass:"stat-value"},[t._v(t._s(t.formatFlow(t.userInfo.flow,"gb")))]),e("div",{staticClass:"stat-label"},[t._v("总流量配额")])])]),e("div",{staticClass:"stat-item"},[t._m(1),e("div",{staticClass:"stat-content"},[e("div",{staticClass:"stat-value"},[t._v(t._s(t.formatFlow(t.calculateUserTotalUsedFlow())))]),e("div",{staticClass:"stat-label"},[t._v("已用流量")])])]),e("div",{staticClass:"stat-item"},[t._m(2),e("div",{staticClass:"stat-content"},[e("div",{staticClass:"stat-value"},[t._v(t._s(t.userInfo.num||0))]),e("div",{staticClass:"stat-label"},[t._v("转发配额")])])]),e("div",{staticClass:"stat-item"},[t._m(3),e("div",{staticClass:"stat-content"},[e("div",{staticClass:"stat-value"},[t._v(t._s(t.totalUsedForwards))]),e("div",{staticClass:"stat-label"},[t._v("已用转发")])])]),t.userInfo.flowResetTime?e("div",{staticClass:"stat-item"},[t._m(4),e("div",{staticClass:"stat-content"},[e("div",{staticClass:"stat-value"},[t._v(t._s(t.formatResetTime(t.userInfo.flowResetTime)))]),e("div",{staticClass:"stat-label"},[t._v("流量重置时间")])])]):t._e()]),t.userInfo.flow>0?e("div",{staticClass:"progress-section"},[e("div",{staticClass:"progress-item"},[e("div",{staticClass:"progress-header"},[e("span",[t._v("流量使用率")]),e("span",[t._v(t._s(t.calculateUsagePercentage("flow").toFixed(1))+"%")])]),e("el-progress",{attrs:{percentage:t.calculateUsagePercentage("flow"),status:t.getUsageStatus("flow"),"stroke-width":8}})],1),e("div",{staticClass:"progress-item"},[e("div",{staticClass:"progress-header"},[e("span",[t._v("转发使用率")]),e("span",[t._v(t._s(t.calculateUsagePercentage("forwards").toFixed(1))+"%")])]),e("el-progress",{attrs:{percentage:t.calculateUsagePercentage("forwards"),status:t.getUsageStatus("forwards"),"stroke-width":8}})],1)]):t._e()])]),e("div",{staticClass:"tunnels-section"},[e("h3",{staticClass:"section-title"},[t._v("隧道权限")]),e("div",{directives:[{name:"loading",rawName:"v-loading",value:t.loading,expression:"loading"}],staticClass:"tunnels-grid"},[0!==t.userTunnels.length||t.loading?t._e():e("div",{staticClass:"empty-state"},[e("el-empty",{attrs:{description:"暂无隧道权限"}})],1),t._l(t.userTunnels,(function(s){return e("div",{key:s.id,staticClass:"tunnel-card"},[e("div",{staticClass:"tunnel-header"},[e("div",{staticClass:"tunnel-name"},[e("i",{staticClass:"el-icon-link"}),t._v(" "+t._s(s.tunnelName)+" ")]),e("div",{staticClass:"tunnel-status"},[s.expTime?e("el-tag",{attrs:{type:t.getTunnelExpStatus(s.expTime).type,size:"mini"}},[t._v(" "+t._s(t.getTunnelExpStatus(s.expTime).text)+" ")]):e("el-tag",{attrs:{type:"success",size:"mini"}},[t._v("永久")]),1===s.tunnelFlow?e("el-tag",{attrs:{type:"info",size:"mini"}},[t._v("单向计费")]):e("el-tag",{attrs:{type:"warning",size:"mini"}},[t._v("双向计费")])],1)]),e("div",{staticClass:"tunnel-stats"},[e("div",{staticClass:"tunnel-stat-row"},[e("div",{staticClass:"tunnel-stat-item"},[e("span",{staticClass:"stat-label"},[t._v("流量配额:")]),e("span",{staticClass:"stat-value"},[t._v(t._s(t.formatFlow(s.flow,"gb")))])]),e("div",{staticClass:"tunnel-stat-item"},[e("span",{staticClass:"stat-label"},[t._v("已用流量:")]),e("span",{staticClass:"stat-value used"},[t._v(t._s(t.formatFlow(t.calculateTunnelUsedFlow(s))))])])]),e("div",{staticClass:"tunnel-stat-row"},[e("div",{staticClass:"tunnel-stat-item"},[e("span",{staticClass:"stat-label"},[t._v("转发配额:")]),e("span",{staticClass:"stat-value"},[t._v(t._s(s.num))])]),e("div",{staticClass:"tunnel-stat-item"},[e("span",{staticClass:"stat-label"},[t._v("已用转发:")]),e("span",{staticClass:"stat-value used"},[t._v(t._s(t.getTunnelUsedForwards(s.tunnelId)))])])])]),e("div",{staticClass:"tunnel-progress"},[e("div",{staticClass:"progress-mini"},[e("div",{staticClass:"progress-mini-header"},[e("span",[t._v("流量使用")]),e("span",[t._v(t._s(t.calculateTunnelFlowPercentage(s).toFixed(1))+"%")])]),e("el-progress",{attrs:{percentage:t.calculateTunnelFlowPercentage(s),status:t.getTunnelFlowStatus(s),"stroke-width":6,"show-text":!1}})],1),e("div",{staticClass:"progress-mini"},[e("div",{staticClass:"progress-mini-header"},[e("span",[t._v("转发使用")]),e("span",[t._v(t._s(t.calculateTunnelForwardPercentage(s).toFixed(1))+"%")])]),e("el-progress",{attrs:{percentage:t.calculateTunnelForwardPercentage(s),status:t.getTunnelForwardStatus(s),"stroke-width":6,"show-text":!1}})],1)]),s.flowResetTime?e("div",{staticClass:"tunnel-footer"},[e("span",{staticClass:"reset-time"},[e("i",{staticClass:"el-icon-time"}),t._v(" 流量重置: "+t._s(t._f("dateFormat")(new Date(s.flowResetTime)))+" ")])]):t._e()])}))],2)]),e("div",{staticClass:"forwards-section"},[e("h3",{staticClass:"section-title"},[t._v("转发详情")]),e("div",{directives:[{name:"loading",rawName:"v-loading",value:t.loading,expression:"loading"}],staticClass:"forwards-container"},[0!==t.groupedForwards.length||t.loading?t._e():e("div",{staticClass:"empty-state"},[e("el-empty",{attrs:{description:"暂无转发配置"}})],1),t._l(t.groupedForwards,(function(s){return e("div",{key:s.tunnelName,staticClass:"forward-group"},[e("div",{staticClass:"group-header"},[e("h4",[t._v(t._s(s.tunnelName))]),e("el-tag",{attrs:{type:"info",size:"mini"}},[t._v(t._s(s.forwards.length)+" 个转发")])],1),e("div",{staticClass:"forward-list"},t._l(s.forwards,(function(s){return e("div",{key:s.id,staticClass:"forward-item"},[e("div",{staticClass:"forward-info"},[e("div",{staticClass:"forward-name"},[t._v(t._s(s.name))]),e("div",{staticClass:"forward-address"},[e("div",{staticClass:"address-source"},[t._v(t._s(s.inIp)+":"+t._s(s.inPort))]),e("div",{staticClass:"address-arrow"},[t._v("↓")]),e("div",{staticClass:"address-target"},[t._v(t._s(s.remoteAddr))])])]),e("div",{staticClass:"forward-traffic"},[e("div",{staticClass:"traffic-item"},[e("span",{staticClass:"traffic-label"},[t._v("入站:")]),e("span",{staticClass:"traffic-value in"},[t._v(t._s(t.formatFlow(s.inFlow||0)))])]),e("div",{staticClass:"traffic-item"},[e("span",{staticClass:"traffic-label"},[t._v("出站:")]),e("span",{staticClass:"traffic-value out"},[t._v(t._s(t.formatFlow(s.outFlow||0)))])]),e("div",{staticClass:"traffic-item total"},[e("span",{staticClass:"traffic-label"},[t._v("计费:")]),e("span",{staticClass:"traffic-value total"},[t._v(t._s(t.formatFlow(t.calculateForwardBillingFlow(s))))])])])])})),0)])}))],2)])])},it=[function(){var t=this,e=t._self._c;return e("div",{staticClass:"stat-icon"},[e("i",{staticClass:"el-icon-pie-chart"})])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"stat-icon"},[e("i",{staticClass:"el-icon-upload"})])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"stat-icon"},[e("i",{staticClass:"el-icon-connection"})])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"stat-icon"},[e("i",{staticClass:"el-icon-s-operation"})])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"stat-icon"},[e("i",{staticClass:"el-icon-refresh"})])}],at=(s(116),s(7588),{name:"Index",data(){return{refreshLoading:!1,loading:!1,userInfo:{},userTunnels:[],forwardList:[]}},computed:{totalUsedForwards(){return this.forwardList.length},groupedForwards(){const t={};return this.forwardList.forEach((e=>{const s=e.tunnelName||"未知隧道";t[s]||(t[s]={tunnelName:s,forwards:[]}),t[s].forwards.push(e)})),Object.values(t)}},mounted(){this.loadPackageData()},methods:{async loadPackageData(){this.loading=!0;try{const t=await y();if(0===t.code){const e=t.data;this.userInfo=e.userInfo,this.userTunnels=e.tunnelPermissions||[],this.forwardList=e.forwards||[]}else this.$message.error(t.msg||"获取套餐信息失败")}catch(t){console.error("获取套餐信息失败:",t),this.$message.error("获取套餐信息失败")}finally{this.loading=!1}},async refreshData(){this.refreshLoading=!0;try{await this.loadPackageData(),this.$message.success("数据刷新成功")}catch(t){this.$message.error("数据刷新失败")}finally{this.refreshLoading=!1}},formatFlow(t,e="bytes"){return"gb"===e?t+" GB":0===t?"0 B":t<1024?t+" B":t<1048576?(t/1024).toFixed(2)+" KB":t<1073741824?(t/1048576).toFixed(2)+" MB":(t/1073741824).toFixed(2)+" GB"},getExpStatus(t){const e=new Date,s=new Date(t);if(s<e)return{type:"danger",text:"已过期"};const i=s-e,a=Math.ceil(i/864e5);return a<=7?{type:"danger",text:`${a}天后过期`}:a<=30?{type:"warning",text:`${a}天后过期`}:{type:"success",text:"有效期充足"}},getTunnelExpStatus(t){return this.getExpStatus(t)},calculateUsagePercentage(t){if("flow"===t){const t=this.calculateUserTotalUsedFlow(),e=1024*(this.userInfo.flow||0)*1024*1024;return e>0?Math.min(t/e*100,100):0}if("forwards"===t){const t=this.totalUsedForwards,e=this.userInfo.num||0;return e>0?Math.min(t/e*100,100):0}return 0},getUsageStatus(t){const e=this.calculateUsagePercentage(t);return e>=90?"exception":e>=70?"warning":"success"},calculateTunnelFlowPercentage(t){const e=this.calculateTunnelUsedFlow(t),s=1024*(t.flow||0)*1024*1024;return s>0?Math.min(e/s*100,100):0},calculateTunnelForwardPercentage(t){const e=this.getTunnelUsedForwards(t.tunnelId),s=t.num||0;return s>0?Math.min(e/s*100,100):0},getTunnelFlowStatus(t){const e=this.calculateTunnelFlowPercentage(t);return e>=90?"exception":e>=70?"warning":"success"},getTunnelForwardStatus(t){const e=this.calculateTunnelForwardPercentage(t);return e>=90?"exception":e>=70?"warning":"success"},getTunnelUsedForwards(t){return this.forwardList.filter((e=>e.tunnelId===t)).length},calculateTunnelUsedFlow(t){if(!t)return 0;const e=t.inFlow||0,s=t.outFlow||0;return 1===t.tunnelFlow?s:e+s},calculateUserTotalUsedFlow(){return(this.userInfo.inFlow||0)+(this.userInfo.outFlow||0)},formatResetTime(t){if(!t)return"未设置";const e=new Date,s=e.getDate();let i;if(t>s)i=t-s;else if(t<s){const s=new Date(e.getFullYear(),e.getMonth()+1,t),a=s-e;i=Math.ceil(a/864e5)}else i=0;return 0===i?`今天重置(${t}号)`:1===i?`明天重置(${t}号)`:`${i}天后重置(${t}号)`},calculateForwardBillingFlow(t){if(!t)return 0;const e=this.userTunnels.find((e=>e.tunnelId===t.tunnelId));if(!e)return(t.inFlow||0)+(t.outFlow||0);const s=t.inFlow||0,i=t.outFlow||0;return 1===e.tunnelFlow?i:s+i}},filters:{dateFormat(t){if(!t)return"";const e=new Date(t);return e.toLocaleString()}}}),nt=at,lt=(0,r.A)(nt,st,it,!1,null,"6e2af254",null),ot=lt.exports,rt=function(){var t=this,e=t._self._c;return e("div",{staticClass:"user-container"},[e("div",{staticClass:"page-header"},[e("h1",{staticClass:"page-title"},[t._v("用户管理")]),e("div",{staticClass:"header-actions"},[e("el-input",{staticStyle:{width:"250px","margin-right":"15px"},attrs:{placeholder:"搜索用户名或姓名","prefix-icon":"el-icon-search",clearable:""},nativeOn:{keyup:function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:t.handleSearch.apply(null,arguments)}},model:{value:t.searchKeyword,callback:function(e){t.searchKeyword=e},expression:"searchKeyword"}},[e("el-button",{attrs:{slot:"append",icon:"el-icon-search"},on:{click:t.handleSearch},slot:"append"})],1),e("el-button",{staticClass:"add-btn",attrs:{type:"primary",icon:"el-icon-plus"},on:{click:t.handleAdd}},[t._v(" 新增用户 ")])],1)]),e("div",{staticClass:"table-container"},[e("el-table",{directives:[{name:"loading",rawName:"v-loading",value:t.loading,expression:"loading"}],staticStyle:{width:"100%"},attrs:{data:t.userList,stripe:"",border:""}},[e("el-table-column",{attrs:{prop:"id",label:"ID"}}),e("el-table-column",{attrs:{prop:"name",label:"姓名"}}),e("el-table-column",{attrs:{prop:"user",label:"用户名"}}),e("el-table-column",{attrs:{label:"流量统计",width:"220"},scopedSlots:t._u([{key:"default",fn:function(s){return[e("div",{staticClass:"flow-stats"},[e("div",{staticClass:"flow-item"},[e("span",{staticClass:"flow-label"},[t._v("总流量:")]),e("span",{staticClass:"flow-value"},[t._v(t._s(t.formatFlow(s.row.flow,"gb")))])]),e("div",{staticClass:"flow-item"},[e("span",{staticClass:"flow-label"},[t._v("已用:")]),e("span",{staticClass:"flow-value used"},[t._v(t._s(t.formatFlow(t.calculateUserTotalUsedFlow(s.row))))])])])]}}])}),e("el-table-column",{attrs:{prop:"num",label:"转发数量",width:"100"}}),e("el-table-column",{attrs:{prop:"status",label:"状态"},scopedSlots:t._u([{key:"default",fn:function(s){return[e("el-tag",{attrs:{type:1===s.row.status?"success":"danger"}},[t._v(" "+t._s(1===s.row.status?"正常":"禁用")+" ")])]}}])}),e("el-table-column",{attrs:{prop:"flowResetTime",label:"流量重置日期",width:"130"},scopedSlots:t._u([{key:"default",fn:function(s){return[s.row.flowResetTime?e("span",[t._v(" 每月"+t._s(s.row.flowResetTime)+"号 ")]):e("span",{staticClass:"no-reset-time"},[t._v("未设置")])]}}])}),e("el-table-column",{attrs:{prop:"expTime",label:"过期时间",width:"180"},scopedSlots:t._u([{key:"default",fn:function(s){return[s.row.expTime?e("span",{class:t.getExpTimeClass(s.row.expTime)},[t._v(" "+t._s(t._f("dateFormat")(s.row.expTime))+" ")]):e("span",{staticClass:"no-exptime"},[t._v("永久")])]}}])}),e("el-table-column",{attrs:{prop:"createdTime",label:"创建时间"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(t._f("dateFormat")(e.row.createdTime))+" ")]}}])}),e("el-table-column",{attrs:{prop:"updatedTime",label:"更新时间"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(t._f("dateFormat")(e.row.updatedTime))+" ")]}}])}),e("el-table-column",{attrs:{label:"操作",width:"280"},scopedSlots:t._u([{key:"default",fn:function(s){return[e("el-button",{attrs:{size:"mini",type:"primary",icon:"el-icon-edit"},on:{click:function(e){return t.handleEdit(s.row)}}},[t._v(" 编辑 ")]),e("el-button",{attrs:{size:"mini",type:"success",icon:"el-icon-s-tools"},on:{click:function(e){return t.handleAssignTunnel(s.row)}}},[t._v(" 分配 ")]),e("el-button",{attrs:{size:"mini",type:"danger",icon:"el-icon-delete"},on:{click:function(e){return t.handleDelete(s.row)}}},[t._v(" 删除 ")])]}}])})],1),e("div",{staticClass:"pagination-container"},[e("el-pagination",{attrs:{"current-page":t.pagination.current,"page-sizes":[10,20,50,100],"page-size":t.pagination.size,layout:"total, sizes, prev, pager, next, jumper",total:t.pagination.total},on:{"size-change":t.handleSizeChange,"current-change":t.handleCurrentChange}})],1)],1),e("el-dialog",{attrs:{title:t.dialogTitle,visible:t.dialogVisible,width:"500px"},on:{"update:visible":function(e){t.dialogVisible=e},close:t.resetForm}},[e("el-form",{ref:"userForm",attrs:{model:t.userForm,rules:t.rules,"label-width":"80px"}},[e("el-form-item",{attrs:{label:"姓名",prop:"name"}},[e("el-input",{attrs:{placeholder:"请输入姓名",clearable:""},model:{value:t.userForm.name,callback:function(e){t.$set(t.userForm,"name",e)},expression:"userForm.name"}})],1),e("el-form-item",{attrs:{label:"用户名",prop:"user"}},[e("el-input",{attrs:{placeholder:"请输入用户名",clearable:""},model:{value:t.userForm.user,callback:function(e){t.$set(t.userForm,"user",e)},expression:"userForm.user"}})],1),e("el-form-item",{attrs:{label:"密码",prop:"pwd"}},[e("el-input",{attrs:{type:"password",placeholder:t.isEdit?"留空则不修改密码":"请输入密码",clearable:"","show-password":""},model:{value:t.userForm.pwd,callback:function(e){t.$set(t.userForm,"pwd",e)},expression:"userForm.pwd"}})],1),e("el-row",{attrs:{gutter:20}},[e("el-col",{attrs:{span:12}},[e("el-form-item",{attrs:{label:"流量(GB)",prop:"flow"}},[e("el-input-number",{staticStyle:{width:"100%"},attrs:{min:0,max:99999,placeholder:"请输入流量"},model:{value:t.userForm.flow,callback:function(e){t.$set(t.userForm,"flow",e)},expression:"userForm.flow"}})],1)],1),e("el-col",{attrs:{span:12}},[e("el-form-item",{attrs:{label:"转发数量",prop:"num"}},[e("el-input-number",{staticStyle:{width:"100%"},attrs:{min:0,max:9999,placeholder:"请输入转发数量"},model:{value:t.userForm.num,callback:function(e){t.$set(t.userForm,"num",e)},expression:"userForm.num"}})],1)],1)],1),e("el-form-item",{attrs:{label:"过期时间",prop:"expTime"}},[e("el-date-picker",{staticStyle:{width:"100%"},attrs:{type:"datetime",placeholder:"选择过期时间","picker-options":t.pickerOptions},model:{value:t.userForm.expTime,callback:function(e){t.$set(t.userForm,"expTime",e)},expression:"userForm.expTime"}})],1),e("el-form-item",{attrs:{label:"流量重置日期",prop:"flowResetTime"}},[e("el-select",{staticStyle:{width:"100%"},attrs:{placeholder:"选择每月重置日期"},model:{value:t.userForm.flowResetTime,callback:function(e){t.$set(t.userForm,"flowResetTime",e)},expression:"userForm.flowResetTime"}},t._l(31,(function(t){return e("el-option",{key:t,attrs:{label:`每月${t}号(0点重置)`,value:t}})})),1)],1),e("el-form-item",{attrs:{label:"状态",prop:"status"}},[e("el-radio-group",{model:{value:t.userForm.status,callback:function(e){t.$set(t.userForm,"status",e)},expression:"userForm.status"}},[e("el-radio",{attrs:{label:1}},[t._v("正常")]),e("el-radio",{attrs:{label:0}},[t._v("禁用")])],1)],1)],1),e("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[e("el-button",{on:{click:function(e){t.dialogVisible=!1}}},[t._v("取 消")]),e("el-button",{attrs:{type:"primary",loading:t.submitLoading},on:{click:t.handleSubmit}},[t._v(" "+t._s(t.submitLoading?"提交中...":"确 定")+" ")])],1)],1),e("el-dialog",{attrs:{title:`用户 ${t.currentUser.name} 的隧道权限管理`,visible:t.tunnelDialogVisible,width:"50%"},on:{"update:visible":function(e){t.tunnelDialogVisible=e},close:t.resetTunnelDialog}},[e("div",{staticClass:"assign-section"},[e("h4",[t._v("分配新隧道权限")]),e("el-form",{ref:"tunnelForm",attrs:{model:t.tunnelForm,rules:t.tunnelRules,"label-width":"120px"}},[e("el-row",{attrs:{gutter:20}},[e("el-col",{attrs:{span:12}},[e("el-form-item",{attrs:{label:"选择隧道",prop:"tunnelId"}},[e("el-select",{staticStyle:{width:"100%"},attrs:{placeholder:"请选择隧道"},model:{value:t.tunnelForm.tunnelId,callback:function(e){t.$set(t.tunnelForm,"tunnelId",e)},expression:"tunnelForm.tunnelId"}},t._l(t.availableTunnels,(function(t){return e("el-option",{key:t.id,attrs:{label:`${t.name}`,value:t.id}})})),1)],1)],1),e("el-col",{attrs:{span:12}},[e("el-form-item",{attrs:{label:"限速规则",prop:"speedId"}},[e("el-select",{staticStyle:{width:"100%"},attrs:{placeholder:"请选择限速规则",clearable:""},model:{value:t.tunnelForm.speedId,callback:function(e){t.$set(t.tunnelForm,"speedId",e)},expression:"tunnelForm.speedId"}},[e("el-option",{attrs:{label:"不限速",value:null}}),t._l(t.speedLimitList,(function(t){return e("el-option",{key:t.id,attrs:{label:`${t.name}`,value:t.id}})}))],2)],1)],1)],1),e("el-row",{attrs:{gutter:20}},[e("el-col",{attrs:{span:12}},[e("el-form-item",{attrs:{label:"流量限制(GB)",prop:"flow"}},[e("el-input-number",{staticStyle:{width:"100%"},attrs:{min:0,max:99999,placeholder:"流量限制"},model:{value:t.tunnelForm.flow,callback:function(e){t.$set(t.tunnelForm,"flow",e)},expression:"tunnelForm.flow"}})],1)],1),e("el-col",{attrs:{span:12}},[e("el-form-item",{attrs:{label:"转发数量",prop:"num"}},[e("el-input-number",{staticStyle:{width:"100%"},attrs:{min:0,max:9999,placeholder:"转发数量"},model:{value:t.tunnelForm.num,callback:function(e){t.$set(t.tunnelForm,"num",e)},expression:"tunnelForm.num"}})],1)],1)],1),e("el-row",{attrs:{gutter:20}},[e("el-col",{attrs:{span:12}},[e("el-form-item",{attrs:{label:"流量重置日期",prop:"flowResetTime"}},[e("el-select",{staticStyle:{width:"100%"},attrs:{placeholder:"选择每月重置日期"},model:{value:t.tunnelForm.flowResetTime,callback:function(e){t.$set(t.tunnelForm,"flowResetTime",e)},expression:"tunnelForm.flowResetTime"}},t._l(31,(function(t){return e("el-option",{key:t,attrs:{label:`每月${t}号(0点重置)`,value:t}})})),1)],1)],1),e("el-col",{attrs:{span:12}},[e("el-form-item",{attrs:{label:"到期时间",prop:"expTime"}},[e("el-date-picker",{staticStyle:{width:"100%"},attrs:{type:"datetime",placeholder:"选择到期时间","picker-options":t.pickerOptions},model:{value:t.tunnelForm.expTime,callback:function(e){t.$set(t.tunnelForm,"expTime",e)},expression:"tunnelForm.expTime"}})],1)],1)],1),e("el-form",{attrs:{item:""}},[e("el-button",{attrs:{type:"primary",loading:t.assignLoading},on:{click:t.handleAssignTunnelPermission}},[t._v(" 分配权限 ")])],1)],1)],1),e("el-divider"),e("div",{staticClass:"permission-list-section"},[e("h4",[t._v("已有隧道权限")]),e("el-table",{directives:[{name:"loading",rawName:"v-loading",value:t.tunnelListLoading,expression:"tunnelListLoading"}],staticStyle:{width:"100%"},attrs:{data:t.userTunnelList,border:""}},[e("el-table-column",{attrs:{prop:"tunnelName",label:"隧道名称"}}),e("el-table-column",{attrs:{label:"状态",width:"80"},scopedSlots:t._u([{key:"default",fn:function(s){return[e("el-tag",{attrs:{type:1===s.row.status?"success":"danger",size:"small"}},[t._v(" "+t._s(1===s.row.status?"正常":"禁用")+" ")])]}}])}),e("el-table-column",{attrs:{label:"流量统计",width:"180"},scopedSlots:t._u([{key:"default",fn:function(s){return[e("div",{staticClass:"tunnel-flow-stats"},[e("div",{staticClass:"tunnel-flow-item"},[e("span",{staticClass:"tunnel-flow-label"},[t._v("限制:")]),e("span",{staticClass:"tunnel-flow-value"},[t._v(t._s(t.formatFlow(s.row.flow,"gb")))])]),e("div",{staticClass:"tunnel-flow-item"},[e("span",{staticClass:"tunnel-flow-label"},[t._v("已用:")]),e("span",{staticClass:"tunnel-flow-value used"},[t._v(t._s(t.formatFlow(t.calculateTunnelUsedFlow(s.row))))])])])]}}])}),e("el-table-column",{attrs:{prop:"num",label:"转发数量",width:"100"}}),e("el-table-column",{attrs:{prop:"speedLimitName",label:"限速规则",width:"150"},scopedSlots:t._u([{key:"default",fn:function(s){return[s.row.speedLimitName?e("el-tag",{attrs:{type:"warning"}},[t._v(" "+t._s(s.row.speedLimitName)+" ")]):e("el-tag",{attrs:{type:"success"}},[t._v("不限速")])]}}])}),e("el-table-column",{attrs:{prop:"flowResetTime",label:"流量重置日期",width:"130"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" 每月"+t._s(e.row.flowResetTime)+"号 ")]}}])}),e("el-table-column",{attrs:{prop:"expTime",label:"到期时间",width:"180"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(t._f("dateFormat")(e.row.expTime))+" ")]}}])}),e("el-table-column",{attrs:{label:"操作",width:"150"},scopedSlots:t._u([{key:"default",fn:function(s){return[e("el-button",{attrs:{size:"mini",type:"primary",icon:"el-icon-edit"},on:{click:function(e){return t.handleEditTunnelPermission(s.row)}}}),e("el-button",{attrs:{size:"mini",type:"danger",icon:"el-icon-delete"},on:{click:function(e){return t.handleRemoveTunnelPermission(s.row)}}})]}}])})],1)],1),e("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[e("el-button",{on:{click:function(e){t.tunnelDialogVisible=!1}}},[t._v("关 闭")])],1)],1),e("el-dialog",{attrs:{title:`编辑隧道权限 - ${t.editTunnelForm.tunnelName}`,visible:t.editTunnelDialogVisible,width:"50%"},on:{"update:visible":function(e){t.editTunnelDialogVisible=e},close:t.resetEditTunnelForm}},[e("el-form",{ref:"editTunnelForm",attrs:{model:t.editTunnelForm,rules:t.editTunnelRules,"label-width":"120px"}},[e("el-row",{attrs:{gutter:20}},[e("el-col",{attrs:{span:12}},[e("el-form-item",{attrs:{label:"流量限制(GB)",prop:"flow"}},[e("el-input-number",{staticStyle:{width:"100%"},attrs:{min:0,max:99999,placeholder:"流量限制"},model:{value:t.editTunnelForm.flow,callback:function(e){t.$set(t.editTunnelForm,"flow",e)},expression:"editTunnelForm.flow"}})],1)],1),e("el-col",{attrs:{span:12}},[e("el-form-item",{attrs:{label:"转发数量",prop:"num"}},[e("el-input-number",{staticStyle:{width:"100%"},attrs:{min:0,max:9999,placeholder:"转发数量"},model:{value:t.editTunnelForm.num,callback:function(e){t.$set(t.editTunnelForm,"num",e)},expression:"editTunnelForm.num"}})],1)],1)],1),e("el-form-item",{attrs:{label:"限速规则",prop:"speedId"}},[e("el-select",{staticStyle:{width:"100%"},attrs:{placeholder:"请选择限速规则",clearable:""},model:{value:t.editTunnelForm.speedId,callback:function(e){t.$set(t.editTunnelForm,"speedId",e)},expression:"editTunnelForm.speedId"}},[e("el-option",{attrs:{label:"不限速",value:null}}),t._l(t.speedLimitList,(function(t){return e("el-option",{key:t.id,attrs:{label:`${t.name}`,value:t.id}})}))],2)],1),e("el-form-item",{attrs:{label:"流量重置日期",prop:"flowResetTime"}},[e("el-select",{staticStyle:{width:"100%"},attrs:{placeholder:"选择每月重置日期"},model:{value:t.editTunnelForm.flowResetTime,callback:function(e){t.$set(t.editTunnelForm,"flowResetTime",e)},expression:"editTunnelForm.flowResetTime"}},t._l(31,(function(t){return e("el-option",{key:t,attrs:{label:`每月${t}号(0点重置)`,value:t}})})),1)],1),e("el-form-item",{attrs:{label:"到期时间",prop:"expTime"}},[e("el-date-picker",{staticStyle:{width:"100%"},attrs:{type:"datetime",placeholder:"选择到期时间","picker-options":t.pickerOptions},model:{value:t.editTunnelForm.expTime,callback:function(e){t.$set(t.editTunnelForm,"expTime",e)},expression:"editTunnelForm.expTime"}})],1)],1),e("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[e("el-button",{on:{click:function(e){t.editTunnelDialogVisible=!1}}},[t._v("取 消")]),e("el-button",{attrs:{type:"primary",loading:t.updateLoading},on:{click:t.handleUpdateTunnelPermission}},[t._v(" 确 定 ")])],1)],1)],1)},dt=[],ct=(s(1701),{name:"User",data(){return{userList:[],loading:!1,dialogVisible:!1,dialogTitle:"",isEdit:!1,submitLoading:!1,searchKeyword:"",pagination:{current:1,size:10,total:0},userForm:{id:null,name:"",user:"",pwd:"",status:1,flow:0,num:0,expTime:null,flowResetTime:1},rules:{name:[{required:!0,message:"请输入姓名",trigger:"blur"}],user:[{required:!0,message:"请输入用户名",trigger:"blur"},{min:3,message:"用户名长度至少3位",trigger:"blur"}],pwd:[{validator:(t,e,s)=>{this.isEdit||e&&""!==e.trim()?e&&e.length>0&&e.length<6?s(new Error("密码长度至少6位")):s():s(new Error("请输入密码"))},trigger:"blur"}],status:[{required:!0,message:"请选择状态",trigger:"change"}],flow:[{required:!0,message:"请输入流量",trigger:"blur"}],num:[{required:!0,message:"请输入转发数量",trigger:"blur"}],expTime:[{required:!0,message:"请选择过期时间",trigger:"change"}],flowResetTime:[{required:!0,message:"请选择流量重置日期",trigger:"change"}]},tunnelDialogVisible:!1,currentUser:{},userTunnelList:[],tunnelListLoading:!1,assignLoading:!1,allTunnels:[],tunnelForm:{tunnelId:null,flow:100,flowResetTime:1,expTime:null,speedId:null,num:0},tunnelRules:{tunnelId:[{required:!0,message:"请选择隧道",trigger:"change"}],flow:[{required:!0,message:"请输入流量限制",trigger:"blur"}],num:[{required:!0,message:"请输入转发数量",trigger:"blur"}],flowResetTime:[{required:!0,message:"请选择流量重置日期",trigger:"change"}],expTime:[{required:!0,message:"请选择到期时间",trigger:"change"}]},pickerOptions:{disabledDate(t){return t.getTime()<Date.now()-864e5}},editTunnelDialogVisible:!1,editTunnelForm:{id:null,tunnelId:null,tunnelName:"",flow:100,flowResetTime:1,expTime:null,speedId:null,num:0},editTunnelRules:{flow:[{required:!0,message:"请输入流量限制",trigger:"blur"}],num:[{required:!0,message:"请输入转发数量",trigger:"blur"}],flowResetTime:[{required:!0,message:"请选择流量重置日期",trigger:"change"}],expTime:[{required:!0,message:"请选择到期时间",trigger:"change"}]},updateLoading:!1,speedLimitList:[]}},computed:{availableTunnels(){const t=this.userTunnelList.map((t=>t.tunnelId));return this.allTunnels.filter((e=>!t.includes(e.id)))}},mounted(){this.getUserList(),this.getTunnelList(),this.getSpeedLimitList()},methods:{getUserList(){this.loading=!0;const t={current:this.pagination.current,size:this.pagination.size,keyword:this.searchKeyword};v(t).then((t=>{if(this.loading=!1,0!==t.code)return this.$message.error(t.msg);const e=t.data||{};this.userList=e.records||[],this.pagination.total=e.total||0,this.pagination.current=e.current||1,this.pagination.size=e.size||10})).catch((()=>{this.loading=!1,this.$message.error("获取用户列表失败")}))},getTunnelList(){L().then((t=>{0===t.code&&(this.allTunnels=t.data||[])})).catch((()=>{console.error("获取隧道列表失败")}))},handleSearch(){this.pagination.current=1,this.getUserList()},handleSizeChange(t){this.pagination.size=t,this.pagination.current=1,this.getUserList()},handleCurrentChange(t){this.pagination.current=t,this.getUserList()},handleAdd(){this.dialogTitle="新增用户",this.isEdit=!1,this.dialogVisible=!0},handleEdit(t){this.dialogTitle="编辑用户",this.isEdit=!0,this.userForm={id:t.id,name:t.name,user:t.user,pwd:"",status:t.status,flow:t.flow,num:t.num,expTime:t.expTime?new Date(t.expTime):null,flowResetTime:t.flowResetTime||1},this.dialogVisible=!0},handleAssignTunnel(t){this.currentUser=t,this.tunnelDialogVisible=!0,this.getUserTunnelList(),this.setDefaultTimes()},setDefaultTimes(){const t=new Date,e=new Date(t.getFullYear(),t.getMonth()+1,t.getDate(),t.getHours(),t.getMinutes());this.tunnelForm.expTime=e},handleDelete(t){this.$confirm(`确定要删除用户 "${t.name}" 吗?`,"提示",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then((()=>{_(t.id).then((t=>{if(0!==t.code)return this.$message.error(t.msg);this.$message.success("删除成功"),this.getUserList()})).catch((()=>{this.$message.error("删除失败")}))}))},handleSubmit(){this.$refs.userForm.validate((t=>{if(t){this.submitLoading=!0;const t=this.isEdit?b:w,e={...this.userForm};this.isEdit&&!e.pwd&&delete e.pwd,e.expTime&&(e.expTime=e.expTime.getTime()),t(e).then((t=>{if(this.submitLoading=!1,0!==t.code)return this.$message.error(t.msg);this.$message.success(this.isEdit?"更新成功":"创建成功"),this.dialogVisible=!1,this.getUserList()})).catch((()=>{this.submitLoading=!1,this.$message.error(this.isEdit?"更新失败":"创建失败")}))}}))},getUserTunnelList(){this.tunnelListLoading=!0,E({userId:this.currentUser.id}).then((t=>{this.tunnelListLoading=!1,0===t.code?this.userTunnelList=t.data||[]:this.$message.error(t.msg||"获取隧道权限列表失败")})).catch((()=>{this.tunnelListLoading=!1,this.$message.error("获取隧道权限列表失败")}))},handleAssignTunnelPermission(){this.$refs.tunnelForm.validate((t=>{if(t){this.assignLoading=!0;const t={userId:this.currentUser.id,tunnelId:this.tunnelForm.tunnelId,flow:this.tunnelForm.flow,flowResetTime:this.tunnelForm.flowResetTime,expTime:this.tunnelForm.expTime.getTime(),speedId:this.tunnelForm.speedId,num:this.tunnelForm.num};I(t).then((t=>{this.assignLoading=!1,0===t.code?(this.$message.success("分配成功"),this.resetTunnelForm(),this.getUserTunnelList()):this.$message.error(t.msg||"分配失败")})).catch((()=>{this.assignLoading=!1,this.$message.error("分配失败")}))}}))},handleRemoveTunnelPermission(t){this.$confirm("确定要删除该隧道权限吗?","提示",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then((()=>{P({id:t.id}).then((t=>{0===t.code?(this.$message.success("删除成功"),this.getUserTunnelList()):this.$message.error(t.msg||"删除失败")})).catch((()=>{this.$message.error("删除失败")}))}))},resetForm(){this.userForm={id:null,name:"",user:"",pwd:"",status:1,flow:0,num:0,expTime:null,flowResetTime:1},this.$refs.userForm&&this.$refs.userForm.resetFields()},resetTunnelDialog(){this.currentUser={},this.userTunnelList=[],this.resetTunnelForm()},resetTunnelForm(){this.tunnelForm={tunnelId:null,flow:100,flowResetTime:1,expTime:null,speedId:null,num:0},this.$refs.tunnelForm&&this.$refs.tunnelForm.resetFields(),this.setDefaultTimes()},handleEditTunnelPermission(t){this.editTunnelForm={id:t.id,tunnelId:t.tunnelId,tunnelName:t.tunnelName,flow:t.flow,flowResetTime:t.flowResetTime,expTime:new Date(t.expTime),speedId:t.speedId,num:t.num},this.editTunnelDialogVisible=!0},handleUpdateTunnelPermission(){this.$refs.editTunnelForm.validate((t=>{if(t){this.updateLoading=!0;const t={id:this.editTunnelForm.id,flow:this.editTunnelForm.flow,flowResetTime:this.editTunnelForm.flowResetTime,expTime:this.editTunnelForm.expTime.getTime(),speedId:this.editTunnelForm.speedId,num:this.editTunnelForm.num};N(t).then((t=>{this.updateLoading=!1,0===t.code?(this.$message.success("更新成功"),this.editTunnelDialogVisible=!1,this.getUserTunnelList()):this.$message.error(t.msg||"更新失败")})).catch((()=>{this.updateLoading=!1,this.$message.error("更新失败")}))}}))},resetEditTunnelForm(){this.editTunnelForm={id:null,tunnelId:null,tunnelName:"",flow:100,flowResetTime:1,expTime:null,speedId:null,num:0},this.$refs.editTunnelForm&&this.$refs.editTunnelForm.resetFields()},getSpeedLimitList(){q().then((t=>{0===t.code&&(this.speedLimitList=t.data||[])})).catch((()=>{console.error("获取限速规则列表失败")}))},formatFlow(t,e="bytes"){return"gb"===e?t+" GB":0===t?"0 B":t<1024?t+" B":t<1048576?(t/1024).toFixed(2)+" KB":t<1073741824?(t/1048576).toFixed(2)+" MB":(t/1073741824).toFixed(2)+" GB"},calculateFlowPercentage(t){const e=this.calculateTunnelUsedFlow(t);if(0===t.flow)return 0;const s=1024*t.flow*1024*1024,i=e/s*100;return Math.min(i,100)},getFlowStatus(t){const e=this.calculateFlowPercentage(t);return e>=90?"exception":e>=70?"warning":"success"},getExpTimeClass(t){const e=new Date,s=new Date(t);if(s<e)return"exp-time-expired";const i=s-e,a=Math.ceil(i/864e5);return a<=7?"exp-time-soon":a<=30?"exp-time-mid":"exp-time-long"},calculateTunnelUsedFlow(t){if(!t)return 0;const e=t.inFlow||0,s=t.outFlow||0;return 1===t.tunnelFlow?s:e+s},calculateUserTotalUsedFlow(t){return(t.inFlow||0)+(t.outFlow||0)}},filters:{dateFormat(t){if(!t)return"";const e=new Date(t);return e.toLocaleString()}}}),ut=ct,mt=(0,r.A)(ut,rt,dt,!1,null,"1ce36542",null),pt=mt.exports,ht=function(){var t=this,e=t._self._c;return e("div",{staticClass:"node-container"},[e("div",{staticClass:"page-header"},[t._m(0),e("div",{staticClass:"header-actions"},[e("el-button",{staticClass:"add-btn",attrs:{type:"primary",icon:"el-icon-plus"},on:{click:t.handleAdd}},[t._v(" 新增节点 ")]),e("el-button",{attrs:{type:"success",icon:"el-icon-refresh",loading:t.loading},on:{click:t.loadNodes}},[t._v(" 刷新 ")])],1)]),e("div",{directives:[{name:"loading",rawName:"v-loading",value:t.loading,expression:"loading"}],staticClass:"cards-container"},[e("div",{staticClass:"cards-grid"},t._l(t.nodeList,(function(s){return e("div",{key:s.id,staticClass:"node-card",class:{online:"online"===s.connectionStatus,offline:"online"!==s.connectionStatus}},[e("div",{staticClass:"status-indicator"},[e("div",{staticClass:"status-dot",class:{online:"online"===s.connectionStatus,offline:"online"!==s.connectionStatus}}),e("span",{staticClass:"status-text"},[t._v(" "+t._s("online"===s.connectionStatus?"在线":"离线")+" ")])]),e("div",{staticClass:"node-info"},[e("div",{staticClass:"node-header"},[e("h3",{staticClass:"node-name"},[t._v(t._s(s.name))])]),e("div",{staticClass:"system-stats"},[e("div",{staticClass:"charts-container",class:{"offline-charts":"online"!==s.connectionStatus}},[e("div",{staticClass:"chart-item"},[e("div",{staticClass:"chart-title"},[t._v("CPU使用率")]),e("div",{staticClass:"chart-wrapper"},[e("v-chart",{staticStyle:{height:"120px",width:"100%"},attrs:{option:t.getCpuChartOption("online"===s.connectionStatus&&s.systemInfo?.cpuUsage||0,"online"!==s.connectionStatus)}})],1)]),e("div",{staticClass:"chart-item"},[e("div",{staticClass:"chart-title"},[t._v("内存使用率")]),e("div",{staticClass:"chart-wrapper"},[e("v-chart",{staticStyle:{height:"120px",width:"100%"},attrs:{option:t.getMemoryChartOption("online"===s.connectionStatus&&s.systemInfo?.memoryUsage||0,"online"!==s.connectionStatus)}})],1)])]),e("div",{staticClass:"traffic-stats",class:{"offline-stats":"online"!==s.connectionStatus}},[e("div",{staticClass:"stat-item"},[t._m(1,!0),e("div",{staticClass:"stat-content"},[e("span",{staticClass:"traffic-value"},[t._v(" "+t._s("online"===s.connectionStatus?t.formatBytes(s.systemInfo?.uploadTraffic||0):"-")+" ")])])]),e("div",{staticClass:"stat-item"},[t._m(2,!0),e("div",{staticClass:"stat-content"},[e("span",{staticClass:"traffic-value"},[t._v(" "+t._s("online"===s.connectionStatus?t.formatBytes(s.systemInfo?.downloadTraffic||0):"-")+" ")])])]),e("div",{staticClass:"stat-item"},[t._m(3,!0),e("div",{staticClass:"stat-content"},["online"===s.connectionStatus?e("div",{staticClass:"speed-info"},[e("div",{staticClass:"speed-item"},[e("span",{staticClass:"speed-label"},[t._v("↑")]),e("span",{staticClass:"speed-value"},[t._v(t._s(t.formatSpeed(s.systemInfo?.uploadSpeed||0)))])]),e("div",{staticClass:"speed-item"},[e("span",{staticClass:"speed-label"},[t._v("↓")]),e("span",{staticClass:"speed-value"},[t._v(t._s(t.formatSpeed(s.systemInfo?.downloadSpeed||0)))])])]):e("span",{staticClass:"traffic-value total"},[t._v("-")])])]),e("div",{staticClass:"stat-item"},[t._m(4,!0),e("div",{staticClass:"stat-content"},[e("span",{staticClass:"ip-value"},[t._v(t._s("online"===s.connectionStatus&&s.ip||"-"))])])])])])]),e("div",{staticClass:"node-actions"},[e("el-button",{attrs:{size:"small",type:"success",icon:"el-icon-copy-document",loading:s.copyLoading},on:{click:function(e){return t.handleCopyInstallCommand(s)}}},[t._v(" 复制安装命令 ")]),e("el-button",{attrs:{size:"small",type:"primary",icon:"el-icon-edit"},on:{click:function(e){return t.handleEdit(s)}}},[t._v(" 编辑 ")]),e("el-button",{attrs:{size:"small",type:"danger",icon:"el-icon-delete"},on:{click:function(e){return t.handleDelete(s)}}},[t._v(" 删除 ")])],1)])})),0),t.loading||0!==t.nodeList.length?t._e():e("div",{staticClass:"empty-state"},[e("i",{staticClass:"el-icon-box"}),e("p",[t._v("暂无节点数据")]),e("el-button",{attrs:{type:"primary"},on:{click:t.handleAdd}},[t._v("创建第一个节点")])],1)]),e("el-dialog",{attrs:{title:t.dialogTitle,visible:t.dialogVisible,width:"500px"},on:{"update:visible":function(e){t.dialogVisible=e},close:t.resetForm}},[e("el-form",{ref:"nodeForm",attrs:{model:t.nodeForm,rules:t.rules,"label-width":"80px"}},[e("el-form-item",{attrs:{label:"节点名称",prop:"name"}},[e("el-input",{attrs:{placeholder:"请输入节点名称",clearable:""},model:{value:t.nodeForm.name,callback:function(e){t.$set(t.nodeForm,"name",e)},expression:"nodeForm.name"}})],1),e("el-form-item",{attrs:{label:"控制端口",prop:"port"}},[e("el-input",{attrs:{placeholder:"请输入控制端口",type:"number",min:"1",max:"65535",clearable:""},model:{value:t.nodeForm.port,callback:function(e){t.$set(t.nodeForm,"port",t._n(e))},expression:"nodeForm.port"}})],1),t.isEdit?t._e():e("el-alert",{staticStyle:{"margin-bottom":"20px"},attrs:{title:"系统将自动为新节点生成UUID密钥",type:"info",closable:!1,"show-icon":""}}),t.isEdit?e("el-alert",{staticStyle:{"margin-bottom":"20px"},attrs:{title:"编辑模式下只能修改节点名称和端口,密钥不可更改",type:"warning",closable:!1,"show-icon":""}}):t._e()],1),e("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[e("el-button",{on:{click:function(e){t.dialogVisible=!1}}},[t._v("取 消")]),e("el-button",{attrs:{type:"primary",loading:t.submitLoading},on:{click:t.handleSubmit}},[t._v(" "+t._s(t.submitLoading?"提交中...":"确 定")+" ")])],1)],1)],1)},ft=[function(){var t=this,e=t._self._c;return e("h1",{staticClass:"page-title"},[e("i",{staticClass:"el-icon-s-platform"}),t._v(" 节点管理 ")])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"stat-header"},[e("i",{staticClass:"el-icon-upload2"}),e("span",[t._v("上传流量")])])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"stat-header"},[e("i",{staticClass:"el-icon-download"}),e("span",[t._v("下载流量")])])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"stat-header"},[e("i",{staticClass:"el-icon-s-grid"}),e("span",[t._v("实时网速")])])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"stat-header"},[e("i",{staticClass:"el-icon-position"}),e("span",[t._v("主机IP")])])}],gt=s(6589),wt=s(3087),vt=s(6411),bt=s(4042),_t=s(2927);const yt={API_BASE_URL:window.APP_CONFIG?.VUE_APP_API_URL||{NODE_ENV:"production",BASE_URL:""}.VUE_APP_API_BASE_URL||"http://127.0.0.1:6365/api/v1",WS_BASE_URL:window.APP_CONFIG?.VUE_APP_WS_URL||{NODE_ENV:"production",BASE_URL:""}.VUE_APP_WS_BASE_URL||"ws://127.0.0.1:6365",REQUEST_TIMEOUT:{NODE_ENV:"production",BASE_URL:""}.VUE_APP_REQUEST_TIMEOUT||6e4};var Ct=yt;(0,wt.Y)([vt.a,bt.a,_t.a]);var Ft={name:"Node",components:{VChart:gt.Ay},data(){return{nodeList:[],loading:!1,dialogVisible:!1,dialogTitle:"",isEdit:!1,submitLoading:!1,nodeForm:{id:null,name:"",port:null},rules:{name:[{required:!0,message:"请输入节点名称",trigger:"blur"},{min:2,message:"节点名称长度至少2位",trigger:"blur"},{max:50,message:"节点名称长度不能超过50位",trigger:"blur"}],port:[{required:!0,message:"请输入控制端口",trigger:"blur"},{type:"number",message:"控制端口必须为数字",trigger:"blur"},{validator:(t,e,s)=>{e&&(e<1||e>65535)?s(new Error("端口号必须在1-65535之间")):s()},trigger:"blur"}]},websocket:null,reconnectTimer:null,maxReconnectAttempts:5,reconnectAttempts:0}},filters:{dateFormat(t){if(!t)return"-";const e=new Date(t);return e.toLocaleString("zh-CN")}},mounted(){this.loadNodes(),this.initWebSocket()},activated(){this.websocket&&this.websocket.readyState===WebSocket.OPEN||this.initWebSocket()},deactivated(){this.closeWebSocket()},beforeDestroy(){this.closeWebSocket()},beforeRouteLeave(t,e,s){this.closeWebSocket(),s()},methods:{loadNodes(){this.loading=!0,F().then((t=>{this.loading=!1,0===t.code?this.nodeList=t.data.map((t=>({...t,connectionStatus:1===t.status?"online":"offline",systemInfo:null,copyLoading:!1}))):this.$message.error(t.msg||"加载节点列表失败")})).catch((t=>{this.loading=!1,this.$message.error("网络错误,请重试")}))},handleAdd(){this.dialogTitle="新增节点",this.isEdit=!1,this.dialogVisible=!0,this.resetForm()},handleEdit(t){this.dialogTitle="编辑节点",this.isEdit=!0,this.nodeForm={id:t.id,name:t.name,port:t.port},this.dialogVisible=!0},handleDelete(t){this.$confirm(`确定要删除节点 "${t.name}" 吗?`,"提示",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then((()=>{x(t.id).then((e=>{if(0===e.code){this.$message.success("删除成功");const e=this.nodeList.findIndex((e=>e.id===t.id));-1!==e&&this.nodeList.splice(e,1)}else this.$message.error(e.msg||"删除失败")}))})).catch((()=>{}))},handleCopyInstallCommand(t){this.$set(t,"copyLoading",!0),S(t.id).then((e=>{this.$set(t,"copyLoading",!1),0===e.code&&e.data?(this.copyToClipboard(e.data),this.$message.success("安装命令已复制到剪贴板")):this.$message.error(e.msg||"获取安装命令失败")})).catch((e=>{this.$set(t,"copyLoading",!1),this.$message.error("网络错误,请重试")}))},copyToClipboard(t){const e=document.createElement("textarea");e.value=t,e.style.position="fixed",e.style.left="-999999px",e.style.top="-999999px",document.body.appendChild(e),e.focus(),e.select();try{document.execCommand("copy")}catch(s){navigator.clipboard&&navigator.clipboard.writeText(t)}document.body.removeChild(e)},handleSubmit(){this.$refs.nodeForm.validate((t=>{if(t){this.submitLoading=!0;const t=this.isEdit?T:C,e=this.isEdit?this.nodeForm:{name:this.nodeForm.name,port:this.nodeForm.port};t(e).then((t=>{if(this.submitLoading=!1,0===t.code)if(this.$message.success(this.isEdit?"更新成功":"创建成功"),this.dialogVisible=!1,this.isEdit){const t=this.nodeList.find((t=>t.id===this.nodeForm.id));t&&(t.name=this.nodeForm.name,t.port=this.nodeForm.port,t.updatedTime=Date.now())}else this.loadNodesWithStatePreservation();else this.$message.error(t.msg||(this.isEdit?"更新失败":"创建失败"))})).catch((()=>{this.submitLoading=!1,this.$message.error("网络错误,请重试")}))}}))},loadNodesWithStatePreservation(){F().then((t=>{if(0===t.code){const e={};this.nodeList.forEach((t=>{e[t.id]={connectionStatus:t.connectionStatus,systemInfo:t.systemInfo}})),this.nodeList=t.data.map((t=>({...t,connectionStatus:e[t.id]?.connectionStatus||(1===t.status?"online":"offline"),systemInfo:e[t.id]?.systemInfo||null,copyLoading:!1})))}else this.$message.error(t.msg||"加载节点列表失败")})).catch((t=>{this.$message.error("网络错误,请重试")}))},resetForm(){this.nodeForm={id:null,name:"",port:null},this.$refs.nodeForm&&this.$refs.nodeForm.clearValidate()},initWebSocket(){if(this.websocket&&(this.websocket.readyState===WebSocket.OPEN||this.websocket.readyState===WebSocket.CONNECTING))return;this.websocket&&this.closeWebSocket();const t=`${Ct.WS_BASE_URL}/system-info?type=0&secret=`+localStorage.getItem("token");try{this.websocket=new WebSocket(t),this.websocket.onopen=()=>{this.reconnectAttempts=0},this.websocket.onmessage=t=>{try{const e=JSON.parse(t.data);this.handleWebSocketMessage(e)}catch(e){}},this.websocket.onerror=t=>{},this.websocket.onclose=t=>{this.websocket=null,this._isDestroyed||this._inactive||this.attemptReconnect()}}catch(e){this.attemptReconnect()}},handleWebSocketMessage(t){const{id:e,type:s,data:i}=t;if("status"===s){const t=this.nodeList.find((t=>t.id==e));t&&(t.connectionStatus=1===i?"online":"offline",0===i&&(t.systemInfo=null))}else if("info"===s){const t=this.nodeList.find((t=>t.id==e));if(t){t.connectionStatus="online";try{let e;e="string"===typeof i?JSON.parse(i):i;const s=parseInt(e.bytes_transmitted)||0,a=parseInt(e.bytes_received)||0,n=parseInt(e.uptime)||0;let l=0,o=0;if(t.systemInfo&&t.systemInfo.uptime){const e=n-t.systemInfo.uptime;if(e>0&&e<=10){const i=t.systemInfo.uploadTraffic||0,n=t.systemInfo.downloadTraffic||0,r=s-i,d=a-n,c=s<i,u=a<n;!c&&r>=0&&(l=r/e),!u&&d>=0&&(o=d/e)}}t.systemInfo={cpuUsage:parseFloat(e.cpu_usage)||0,memoryUsage:parseFloat(e.memory_usage)||0,uploadTraffic:s,downloadTraffic:a,uploadSpeed:l,downloadSpeed:o,uptime:n}}catch(a){}}}},attemptReconnect(){this._isDestroyed||this._inactive||this.reconnectAttempts<this.maxReconnectAttempts&&(this.reconnectAttempts++,this.reconnectTimer=setTimeout((()=>{this._isDestroyed||this._inactive||this.initWebSocket()}),3e3*this.reconnectAttempts))},closeWebSocket(){this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.reconnectAttempts=0,this.websocket&&(this.websocket.onopen=null,this.websocket.onmessage=null,this.websocket.onerror=null,this.websocket.onclose=null,this.websocket.readyState!==WebSocket.OPEN&&this.websocket.readyState!==WebSocket.CONNECTING||this.websocket.close(),this.websocket=null),this.nodeList.forEach((t=>{t.connectionStatus="offline",t.systemInfo=null}))},getCpuChartOption(t,e=!1){const s=e?[[1,"#d4d4d4"]]:[[.5,"#67c23a"],[.8,"#e6a23c"],[1,"#f56c6c"]];return{series:[{type:"gauge",radius:"85%",center:["50%","55%"],startAngle:180,endAngle:0,min:0,max:100,splitNumber:5,axisLine:{lineStyle:{width:8,color:s}},pointer:{width:3,length:"45%",itemStyle:{color:e?"#909399":"#409eff"}},axisTick:{show:!1},splitLine:{show:!1},axisLabel:{show:!1},detail:{formatter:e?"-":"{value}%",fontSize:18,fontWeight:"bold",color:e?"#909399":"#303133",offsetCenter:[0,"25%"]},title:{show:!1},data:[{value:e?0:t.toFixed(1)}]}]}},getMemoryChartOption(t,e=!1){const s=e?[[1,"#d4d4d4"]]:[[.5,"#67c23a"],[.8,"#e6a23c"],[1,"#f56c6c"]];return{series:[{type:"gauge",radius:"85%",center:["50%","55%"],startAngle:180,endAngle:0,min:0,max:100,splitNumber:5,axisLine:{lineStyle:{width:8,color:s}},pointer:{width:3,length:"45%",itemStyle:{color:e?"#909399":"#409eff"}},axisTick:{show:!1},splitLine:{show:!1},axisLabel:{show:!1},detail:{formatter:e?"-":"{value}%",fontSize:18,fontWeight:"bold",color:e?"#909399":"#303133",offsetCenter:[0,"25%"]},title:{show:!1},data:[{value:e?0:t.toFixed(1)}]}]}},formatBytes(t){if(0===t)return"0 B";const e=1024,s=["B","KB","MB","GB","TB"],i=Math.floor(Math.log(t)/Math.log(e));return parseFloat((t/Math.pow(e,i)).toFixed(2))+" "+s[i]},formatSpeed(t){if(0===t)return"0 B/s";const e=1024,s=["B/s","KB/s","MB/s","GB/s","TB/s"],i=Math.floor(Math.log(t)/Math.log(e));return parseFloat((t/Math.pow(e,i)).toFixed(2))+" "+s[i]}}},Tt=Ft,xt=(0,r.A)(Tt,ht,ft,!1,null,"0e93ac7f",null),St=xt.exports,kt=function(){var t=this,e=t._self._c;return e("div",{staticClass:"tunnel-container"},[e("div",{staticClass:"page-header"},[t._m(0),e("div",{staticClass:"header-actions"},[e("el-button",{staticClass:"add-btn",attrs:{type:"primary",icon:"el-icon-plus"},on:{click:t.handleAdd}},[t._v(" 新增隧道 ")]),e("el-button",{attrs:{type:"success",icon:"el-icon-refresh",loading:t.loading},on:{click:t.loadTunnels}},[t._v(" 刷新 ")])],1)]),e("div",{directives:[{name:"loading",rawName:"v-loading",value:t.loading,expression:"loading"}],staticClass:"cards-container"},[e("div",{staticClass:"cards-grid"},t._l(t.tunnelList,(function(s){return e("div",{key:s.id,staticClass:"tunnel-card",class:{disabled:0===s.status}},[e("div",{staticClass:"tunnel-header"},[e("h3",{staticClass:"tunnel-name"},[t._v(t._s(s.name))]),e("div",{staticClass:"tunnel-badges"},[1===s.type?e("el-tag",{attrs:{type:"primary",size:"mini"}},[t._v("端口转发")]):e("el-tag",{attrs:{type:"warning",size:"mini"}},[t._v("隧道转发")]),1===s.flow?e("el-tag",{attrs:{type:"success",size:"mini"}},[t._v("单向计算")]):e("el-tag",{attrs:{type:"info",size:"mini"}},[t._v("双向计算")])],1)]),e("div",{staticClass:"tunnel-flow"},[t._m(1,!0),t._m(2,!0),e("div",{staticClass:"flow-item entrance"},[t._m(3,!0),e("div",{staticClass:"flow-label"},[t._v("入口")]),e("div",{staticClass:"flow-detail"},[e("div",{staticClass:"detail-item"},[e("span",{staticClass:"label"},[t._v("IP:")]),e("span",{staticClass:"value"},[t._v(t._s(s.inIp))])]),e("div",{staticClass:"detail-item"},[e("span",{staticClass:"label"},[t._v("端口:")]),e("span",{staticClass:"value"},[t._v(t._s(s.inPortSta===s.inPortEnd?s.inPortSta:`${s.inPortSta}-${s.inPortEnd}`))])])])]),t._m(4,!0),e("div",{staticClass:"flow-item exit"},[t._m(5,!0),e("div",{staticClass:"flow-label"},[t._v("出口")]),e("div",{staticClass:"flow-detail"},[e("div",{staticClass:"detail-item"},[e("span",{staticClass:"label"},[t._v("IP:")]),e("span",{staticClass:"value"},[t._v(t._s(s.outIp))])]),e("div",{staticClass:"detail-item"},[e("span",{staticClass:"label"},[t._v("端口:")]),e("span",{staticClass:"value"},[t._v(t._s(s.outIpSta===s.outIpEnd?s.outIpSta:`${s.outIpSta}-${s.outIpEnd}`))])])])]),t._m(6,!0),t._m(7,!0)]),e("div",{staticClass:"tunnel-meta"},[e("div",{staticClass:"meta-item"},[e("i",{staticClass:"el-icon-time"}),e("span",[t._v(t._s(t._f("dateFormat")(s.createdTime)))])])]),e("div",{staticClass:"tunnel-actions"},[e("el-button",{attrs:{size:"small",type:"danger",icon:"el-icon-delete"},on:{click:function(e){return t.handleDelete(s)}}},[t._v(" 删除 ")])],1)])})),0),t.loading||0!==t.tunnelList.length?t._e():e("div",{staticClass:"empty-state"},[e("i",{staticClass:"el-icon-connection"}),e("p",[t._v("暂无隧道数据")]),e("el-button",{attrs:{type:"primary"},on:{click:t.handleAdd}},[t._v("创建第一个隧道")])],1)]),e("el-dialog",{attrs:{title:t.dialogTitle,visible:t.dialogVisible,width:"600px"},on:{"update:visible":function(e){t.dialogVisible=e},close:t.resetForm}},[e("el-form",{ref:"tunnelForm",attrs:{model:t.tunnelForm,rules:t.rules,"label-width":"100px"}},[e("el-form-item",{attrs:{label:"隧道名称",prop:"name"}},[e("el-input",{attrs:{placeholder:"请输入隧道名称",clearable:""},model:{value:t.tunnelForm.name,callback:function(e){t.$set(t.tunnelForm,"name",e)},expression:"tunnelForm.name"}})],1),e("el-form-item",{attrs:{label:"隧道类型",prop:"type"}},[e("el-radio-group",{on:{change:t.handleTypeChange},model:{value:t.tunnelForm.type,callback:function(e){t.$set(t.tunnelForm,"type",e)},expression:"tunnelForm.type"}},[e("el-radio",{attrs:{label:1}},[t._v("端口转发")]),e("el-radio",{attrs:{label:2}},[t._v("隧道转发")])],1)],1),e("el-form-item",{attrs:{label:"流量计算",prop:"flow"}},[e("el-radio-group",{model:{value:t.tunnelForm.flow,callback:function(e){t.$set(t.tunnelForm,"flow",e)},expression:"tunnelForm.flow"}},[e("el-radio",{attrs:{label:1}},[t._v("单向计算(仅上传)")]),e("el-radio",{attrs:{label:2}},[t._v("双向计算(上传+下载)")])],1),e("div",{staticClass:"form-hint"},[t._v(" 单向:仅计算上传流量;双向:计算上传和下载的总流量 ")])],1),e("el-divider",{attrs:{"content-position":"left"}},[t._v("入口配置")]),e("el-form-item",{attrs:{label:"入口节点",prop:"inNodeId"}},[e("el-select",{staticStyle:{width:"100%"},attrs:{placeholder:"请选择入口节点"},on:{change:t.handleNodeChange},model:{value:t.tunnelForm.inNodeId,callback:function(e){t.$set(t.tunnelForm,"inNodeId",e)},expression:"tunnelForm.inNodeId"}},t._l(t.onlineNodes,(function(s){return e("el-option",{key:s.id,attrs:{label:`${s.name} (${1===s.status?"在线":"离线"})`,value:s.id,disabled:!1}},[e("span",{staticStyle:{float:"left"}},[t._v(t._s(s.name))]),e("span",{staticStyle:{float:"right","font-size":"13px"},style:{color:1===s.status?"#67c23a":"#f56c6c"}},[t._v(" "+t._s(1===s.status?"在线":"离线")+" ")])])})),1)],1),e("el-row",{attrs:{gutter:20}},[e("el-col",{attrs:{span:12}},[e("el-form-item",{attrs:{label:"起始端口",prop:"inPortSta"}},[e("el-input-number",{staticStyle:{width:"100%"},attrs:{min:1,max:65535,placeholder:"起始端口"},model:{value:t.tunnelForm.inPortSta,callback:function(e){t.$set(t.tunnelForm,"inPortSta",e)},expression:"tunnelForm.inPortSta"}})],1)],1),e("el-col",{attrs:{span:12}},[e("el-form-item",{attrs:{label:"结束端口",prop:"inPortEnd"}},[e("el-input-number",{staticStyle:{width:"100%"},attrs:{min:1,max:65535,placeholder:"结束端口"},model:{value:t.tunnelForm.inPortEnd,callback:function(e){t.$set(t.tunnelForm,"inPortEnd",e)},expression:"tunnelForm.inPortEnd"}})],1)],1)],1),2===t.tunnelForm.type?[e("el-divider",{attrs:{"content-position":"left"}},[t._v("出口配置")]),e("el-form-item",{attrs:{label:"出口节点",prop:"outNodeId"}},[e("el-select",{staticStyle:{width:"100%"},attrs:{placeholder:"请选择出口节点"},on:{change:t.handleNodeChange},model:{value:t.tunnelForm.outNodeId,callback:function(e){t.$set(t.tunnelForm,"outNodeId",e)},expression:"tunnelForm.outNodeId"}},t._l(t.onlineNodes,(function(s){return e("el-option",{key:s.id,attrs:{label:`${s.name} (${1===s.status?"在线":"离线"})`,value:s.id,disabled:t.tunnelForm.inNodeId&&s.id===t.tunnelForm.inNodeId}},[e("span",{staticStyle:{float:"left"}},[t._v(t._s(s.name))]),e("span",{staticStyle:{float:"right","font-size":"13px"},style:{color:1===s.status?"#67c23a":"#f56c6c"}},[t._v(" "+t._s(1===s.status?"在线":"离线")+" "+t._s(t.tunnelForm.inNodeId&&s.id===t.tunnelForm.inNodeId?" (已选为入口)":"")+" ")])])})),1)],1),e("el-row",{attrs:{gutter:20}},[e("el-col",{attrs:{span:12}},[e("el-form-item",{attrs:{label:"起始端口",prop:"outIpSta"}},[e("el-input-number",{staticStyle:{width:"100%"},attrs:{min:1e4,max:65535,placeholder:"起始端口"},model:{value:t.tunnelForm.outIpSta,callback:function(e){t.$set(t.tunnelForm,"outIpSta",e)},expression:"tunnelForm.outIpSta"}})],1)],1),e("el-col",{attrs:{span:12}},[e("el-form-item",{attrs:{label:"结束端口",prop:"outIpEnd"}},[e("el-input-number",{staticStyle:{width:"100%"},attrs:{min:1e4,max:65535,placeholder:"结束端口"},model:{value:t.tunnelForm.outIpEnd,callback:function(e){t.$set(t.tunnelForm,"outIpEnd",e)},expression:"tunnelForm.outIpEnd"}})],1)],1)],1)]:t._e(),1===t.tunnelForm.type?e("el-alert",{staticStyle:{"margin-top":"20px"},attrs:{title:"端口转发模式:出口配置将自动使用入口配置",type:"info",closable:!1,"show-icon":""}}):t._e()],2),e("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[e("el-button",{on:{click:function(e){t.dialogVisible=!1}}},[t._v("取 消")]),e("el-button",{attrs:{type:"primary",loading:t.submitLoading},on:{click:t.handleSubmit}},[t._v(" "+t._s(t.submitLoading?"创建中...":"创 建")+" ")])],1)],1)],1)},Lt=[function(){var t=this,e=t._self._c;return e("h1",{staticClass:"page-title"},[e("i",{staticClass:"el-icon-connection"}),t._v(" 隧道管理 ")])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"flow-item client"},[e("div",{staticClass:"flow-icon"},[e("i",{staticClass:"el-icon-monitor"})]),e("div",{staticClass:"flow-label"},[t._v("客户端")])])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"flow-arrow"},[e("i",{staticClass:"el-icon-right"})])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"flow-icon"},[e("i",{staticClass:"el-icon-upload"})])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"flow-arrow"},[e("i",{staticClass:"el-icon-right"})])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"flow-icon"},[e("i",{staticClass:"el-icon-download"})])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"flow-arrow"},[e("i",{staticClass:"el-icon-right"})])},function(){var t=this,e=t._self._c;return e("div",{staticClass:"flow-item target"},[e("div",{staticClass:"flow-icon"},[e("i",{staticClass:"el-icon-position"})]),e("div",{staticClass:"flow-label"},[t._v("目标")]),e("div",{staticClass:"flow-detail"},[e("div",{staticClass:"detail-item"},[e("span",{staticClass:"label"},[t._v("目标服务")])])])])}],$t={name:"Tunnel",data(){return{tunnelList:[],nodeList:[],loading:!1,dialogVisible:!1,dialogTitle:"新增隧道",submitLoading:!1,tunnelForm:{id:null,name:"",inNodeId:null,inPortSta:1e4,inPortEnd:65535,outNodeId:null,outIpSta:1e4,outIpEnd:65535,type:1,flow:1,status:1},rules:{name:[{required:!0,message:"请输入隧道名称",trigger:"blur"},{min:2,message:"隧道名称长度至少2位",trigger:"blur"},{max:50,message:"隧道名称长度不能超过50位",trigger:"blur"}],inNodeId:[{required:!0,message:"请选择入口节点",trigger:"change"}],inPortSta:[{required:!0,message:"请输入起始端口",trigger:"blur"}],inPortEnd:[{required:!0,message:"请输入结束端口",trigger:"blur"}],type:[{required:!0,message:"请选择隧道类型",trigger:"change"}],flow:[{required:!0,message:"请选择流量计算类型",trigger:"change"}],outNodeId:[{required:!1,message:"请选择出口节点",trigger:"change"}],outIpSta:[{required:!1,message:"请输入起始端口",trigger:"blur"}],outIpEnd:[{required:!1,message:"请输入结束端口",trigger:"blur"}]}}},filters:{dateFormat(t){if(!t)return"-";const e=new Date(t);return e.toLocaleString("zh-CN")}},computed:{onlineNodes(){return this.nodeList||[]}},mounted(){this.loadTunnels()},methods:{loadNodes(){F().then((t=>{0===t.code?this.nodeList=t.data||[]:this.$message.error(t.msg||"加载节点列表失败")})).catch((t=>{this.$message.error("加载节点列表失败")}))},handleNodeChange(){},loadTunnels(){this.loading=!0,L().then((t=>{this.loading=!1,0===t.code?this.tunnelList=t.data||[]:this.$message.error(t.msg||"加载隧道列表失败")})).catch((t=>{this.loading=!1,this.$message.error("网络错误,请重试")}))},handleAdd(){this.dialogVisible=!0,this.resetForm(),this.loadNodes()},handleDelete(t){this.$confirm(`确定要删除隧道 "${t.name}" 吗?`,"提示",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"}).then((()=>{$(t.id).then((t=>{0===t.code?(this.$message.success("删除成功"),this.loadTunnels()):this.$message.error(t.msg||"删除失败")}))})).catch((()=>{}))},handleTypeChange(t){2===t?(this.rules.outNodeId[0].required=!0,this.rules.outIpSta[0].required=!0,this.rules.outIpEnd[0].required=!0):(this.rules.outNodeId[0].required=!1,this.rules.outIpSta[0].required=!1,this.rules.outIpEnd[0].required=!1,this.tunnelForm.outNodeId=null,this.tunnelForm.outIpSta=null,this.tunnelForm.outIpEnd=null)},handleSubmit(){this.$refs.tunnelForm.validate((t=>{if(t){if(this.tunnelForm.inPortSta>this.tunnelForm.inPortEnd)return void this.$message.error("入口起始端口不能大于结束端口");if(2===this.tunnelForm.type&&this.tunnelForm.inNodeId===this.tunnelForm.outNodeId)return void this.$message.error("隧道转发模式下,入口和出口不能是同一个节点");if(2===this.tunnelForm.type&&this.tunnelForm.outIpSta&&this.tunnelForm.outIpEnd&&this.tunnelForm.outIpSta>this.tunnelForm.outIpEnd)return void this.$message.error("出口起始端口不能大于结束端口");this.submitLoading=!0;const t={...this.tunnelForm};k(t).then((t=>{this.submitLoading=!1,0===t.code?(this.$message.success("创建成功"),this.dialogVisible=!1,this.loadTunnels()):this.$message.error(t.msg||"创建失败")})).catch((()=>{this.submitLoading=!1,this.$message.error("网络错误,请重试")}))}}))},resetForm(){this.tunnelForm={id:null,name:"",inNodeId:null,inPortSta:1e4,inPortEnd:65535,outNodeId:null,outIpSta:1e4,outIpEnd:65535,type:1,flow:1,status:1},this.$refs.tunnelForm&&this.$refs.tunnelForm.clearValidate()}}},It=$t,Et=(0,r.A)(It,kt,Lt,!1,null,"0242d058",null),Pt=Et.exports,Nt=function(){var t=this,e=t._self._c;return e("div",{staticClass:"forward-container"},[e("div",{staticClass:"page-header"},[e("h1",{staticClass:"page-title"},[t._v("转发管理")]),e("div",{staticClass:"header-actions"},[e("el-input",{staticStyle:{width:"250px","margin-right":"15px"},attrs:{placeholder:"搜索转发名称或远程地址","prefix-icon":"el-icon-search",clearable:""},nativeOn:{keyup:function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:t.handleSearch.apply(null,arguments)}},model:{value:t.searchKeyword,callback:function(e){t.searchKeyword=e},expression:"searchKeyword"}},[e("el-button",{attrs:{slot:"append",icon:"el-icon-search"},on:{click:t.handleSearch},slot:"append"})],1),e("el-button",{staticClass:"add-btn",attrs:{type:"primary",icon:"el-icon-plus"},on:{click:t.handleAdd}},[t._v(" 新增转发 ")])],1)]),e("div",{staticClass:"table-container"},[e("div",{directives:[{name:"loading",rawName:"v-loading",value:t.loading,expression:"loading"}],staticClass:"cards-container"},[0!==t.forwardList.length||t.loading?t._e():e("div",{staticClass:"empty-state"},[e("el-empty",{attrs:{description:"暂无转发配置"}})],1),t._l(t.forwardList,(function(s){return e("div",{key:s.id,staticClass:"forward-card"},[e("div",{staticClass:"card-header"},[e("div",{staticClass:"card-title"},[e("i",{staticClass:"el-icon-connection"}),t._v(" "+t._s(s.name)+" ")]),e("div",{staticClass:"card-actions"},[e("el-switch",{staticStyle:{"margin-right":"10px"},attrs:{loading:s.switchLoading,disabled:1!==s.status&&0!==s.status,"active-color":"#13ce66","inactive-color":"#ff4949"},on:{change:function(e){return t.handleServiceToggle(s)}},model:{value:s.serviceRunning,callback:function(e){t.$set(s,"serviceRunning",e)},expression:"forward.serviceRunning"}}),e("el-button",{attrs:{size:"mini",type:"primary",icon:"el-icon-edit",circle:""},on:{click:function(e){return t.handleEdit(s)}}}),e("el-button",{attrs:{size:"mini",type:"danger",icon:"el-icon-delete",circle:""},on:{click:function(e){return t.handleDelete(s)}}})],1)]),e("div",{staticClass:"card-body"},[e("div",{staticClass:"info-row"},[e("div",{staticClass:"info-item"},[e("span",{staticClass:"label"},[t._v("隧道名称:")]),e("span",{staticClass:"value"},[t._v(t._s(s.tunnelName||"未知隧道"))])]),e("div",{staticClass:"info-item"},[e("span",{staticClass:"label"},[t._v("状态:")]),e("el-tag",{attrs:{type:t.getStatusType(s.status),size:"small"}},[t._v(" "+t._s(t.getStatusText(s.status))+" ")])],1)]),e("div",{staticClass:"info-row"},[e("div",{staticClass:"info-item"},[e("span",{staticClass:"label"},[t._v("入口地址:")]),e("span",{staticClass:"value"},[t._v(t._s(s.inIp)+":"+t._s(s.inPort))])]),e("div",{staticClass:"info-item"},[e("span",{staticClass:"label"},[t._v("目标地址:")]),e("span",{staticClass:"value"},[t._v(t._s(s.remoteAddr))])])]),e("div",{staticClass:"info-row"},[e("div",{staticClass:"info-item full-width"},[e("span",{staticClass:"label"},[t._v("创建时间:")]),e("span",{staticClass:"value"},[t._v(t._s(t._f("dateFormat")(s.createdTime)))])]),e("div",{directives:[{name:"admin",rawName:"v-admin"}],staticClass:"info-item full-width"},[e("span",{staticClass:"label"},[t._v("归属账号:")]),e("span",{staticClass:"value"},[t._v(t._s(s.userName))])])]),e("div",{staticClass:"info-row"},[e("div",{staticClass:"info-item"},[e("span",{staticClass:"label"},[t._v("流量统计:")]),e("div",{staticClass:"flow-stats-mini"},[e("div",{staticClass:"flow-stat-item"},[e("span",{staticClass:"flow-stat-label"},[t._v("入站:")]),e("span",{staticClass:"flow-stat-value in"},[t._v(t._s(t.formatFlow(s.inFlow||0)))])]),e("div",{staticClass:"flow-stat-item"},[e("span",{staticClass:"flow-stat-label"},[t._v("出站:")]),e("span",{staticClass:"flow-stat-value out"},[t._v(t._s(t.formatFlow(s.outFlow||0)))])]),e("div",{staticClass:"flow-stat-item"},[e("span",{staticClass:"flow-stat-label"},[t._v("总计:")]),e("span",{staticClass:"flow-stat-value total"},[t._v(t._s(t.formatFlow((s.inFlow||0)+(s.outFlow||0))))])])])])])])])}))],2)]),e("el-dialog",{attrs:{title:t.dialogTitle,visible:t.dialogVisible,width:"600px"},on:{"update:visible":function(e){t.dialogVisible=e},close:t.resetForm}},[e("el-form",{ref:"forwardForm",attrs:{model:t.forwardForm,rules:t.rules,"label-width":"100px"}},[e("el-form-item",{attrs:{label:"转发名称",prop:"name"}},[e("el-input",{attrs:{placeholder:"请输入转发名称",clearable:""},model:{value:t.forwardForm.name,callback:function(e){t.$set(t.forwardForm,"name",e)},expression:"forwardForm.name"}})],1),e("el-form-item",{attrs:{label:"选择隧道",prop:"tunnelId"}},[e("el-select",{staticStyle:{width:"100%"},attrs:{placeholder:"请选择关联的隧道"},on:{change:t.handleTunnelChange},model:{value:t.forwardForm.tunnelId,callback:function(e){t.$set(t.forwardForm,"tunnelId",e)},expression:"forwardForm.tunnelId"}},t._l(t.tunnelList,(function(s){return e("el-option",{key:s.id||s.tunnelId,attrs:{label:t.getTunnelDisplayName(s),value:s.id||s.tunnelId}})})),1)],1),e("el-form-item",{attrs:{label:"远程地址",prop:"remoteAddr"}},[e("el-input",{attrs:{placeholder:"例如: 192.168.1.100:8080",clearable:""},model:{value:t.forwardForm.remoteAddr,callback:function(e){t.$set(t.forwardForm,"remoteAddr",e)},expression:"forwardForm.remoteAddr"}},[e("template",{slot:"prepend"},[t._v("目标")])],2),e("div",{staticClass:"form-hint"},[t._v(" 格式: IP:端口 或 域名:端口 ")])],1)],1),e("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[e("el-button",{on:{click:function(e){t.dialogVisible=!1}}},[t._v("取 消")]),e("el-button",{attrs:{type:"primary",loading:t.submitLoading},on:{click:t.handleSubmit}},[t._v(" "+t._s(t.submitLoading?"提交中...":"确 定")+" ")])],1)],1)],1)},At=[],Rt={name:"Forward",data(){return{loading:!1,submitLoading:!1,searchKeyword:"",forwardList:[],tunnelList:[],selectedTunnel:null,dialogVisible:!1,dialogTitle:"",isEdit:!1,forwardForm:{id:null,userId:null,name:"",tunnelId:null,remoteAddr:""},rules:{name:[{required:!0,message:"请输入转发名称",trigger:"blur"},{min:2,max:50,message:"长度在 2 到 50 个字符",trigger:"blur"}],tunnelId:[{required:!0,message:"请选择关联隧道",trigger:"change"}],remoteAddr:[{required:!0,message:"请输入远程地址",trigger:"blur"},{pattern:/^[\w\.-]+:\d+$/,message:"请输入正确的地址格式,如: 192.168.1.100:8080",trigger:"blur"}]}}},created(){this.loadForwardList()},methods:{async loadForwardList(){try{this.loading=!0;const t=await U();0===t.code?this.forwardList=t.data.map((t=>({...t,serviceRunning:1===t.status,switchLoading:!1}))):this.$message.error(t.msg||"获取转发列表失败")}catch(t){console.error("加载转发列表失败:",t),this.$message.error("加载转发列表失败")}finally{this.loading=!1}},async loadTunnelList(t,e=!1){try{if(this.tunnelList.length>0&&!e)return t&&"function"===typeof t&&t(),Promise.resolve();let s=await A();if(0===s.code&&s.data&&s.data.length>0)return this.tunnelList=s.data,t&&"function"===typeof t&&t(),Promise.resolve();throw new Error(s.msg||"没有可用的隧道")}catch(s){return console.error("加载隧道列表失败:",s),this.$message.error("加载隧道列表失败"),Promise.reject(s)}},handleSearch(){this.searchKeyword.trim()?(this.loading=!0,setTimeout((()=>{const t=this.forwardList.filter((t=>t.name.includes(this.searchKeyword)||t.remoteAddr.includes(this.searchKeyword)));this.forwardList=t,this.loading=!1}),300)):this.loadForwardList()},async handleAdd(){this.isEdit=!1,this.dialogTitle="新增转发";try{await this.loadTunnelList(),this.dialogVisible=!0}catch(t){this.$message.error("加载隧道列表失败,无法新增")}},async handleEdit(t){this.isEdit=!0,this.dialogTitle="编辑转发";try{await this.loadTunnelList(),this.forwardForm={...t,userId:t.userId},this.handleTunnelChange(t.tunnelId),this.dialogVisible=!0}catch(e){this.$message.error("加载隧道列表失败,无法编辑")}},async handleDelete(t){try{await this.$confirm(`确定要删除转发 "${t.name}" 吗?`,"提示",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"});const e=await V(t.id);0===e.code?(this.$message.success("删除成功"),this.loadForwardList()):this.$message.error(e.msg||"删除失败")}catch(e){"cancel"!==e&&(console.error("删除转发失败:",e),this.$message.error("删除失败"))}},handleTunnelChange(t){this.selectedTunnel=this.tunnelList.find((e=>e.id===t||e.tunnelId===t))||null},async handleSubmit(){try{let t;if(await this.$refs.forwardForm.validate(),this.submitLoading=!0,this.isEdit){const e={id:this.forwardForm.id,userId:this.forwardForm.userId,name:this.forwardForm.name,tunnelId:this.forwardForm.tunnelId,remoteAddr:this.forwardForm.remoteAddr};t=await D(e)}else{const e={name:this.forwardForm.name,tunnelId:this.forwardForm.tunnelId,remoteAddr:this.forwardForm.remoteAddr};t=await R(e)}0===t.code?(this.$message.success(this.isEdit?"修改成功":"创建成功"),this.dialogVisible=!1,await this.loadForwardList()):this.$message.error(t.msg||"操作失败")}catch(t){!1!==t&&(console.error("提交失败:",t),this.$message.error("操作失败"))}finally{this.submitLoading=!1}},resetForm(){this.forwardForm={id:null,userId:null,name:"",tunnelId:null,remoteAddr:""},this.selectedTunnel=null,this.$nextTick((()=>{this.$refs.forwardForm&&this.$refs.forwardForm.clearValidate()}))},getTunnelDisplayName(t){if(!t)return"未知隧道";if(t.tunnelId){const e=this.tunnelList.find((e=>e.id===t.tunnelId));return e&&e.ip&&e.port?`${e.name||t.tunnelId} (${e.ip}:${e.port})`:`隧道ID: ${t.tunnelId}`}return t.name?t.ip&&t.port?`${t.name} (${t.ip}:${t.port})`:t.name:`隧道ID: ${t.id||"unknown"}`},formatFlow(t){return 0===t?"0":t<1024?t+" B":t<1048576?(t/1024).toFixed(2)+" KB":t<1073741824?(t/1048576).toFixed(2)+" MB":(t/1073741824).toFixed(2)+" GB"},getStatusType(t){switch(parseInt(t)){case 1:return"success";case 0:return"danger";case-1:return"danger";default:return"info"}},getStatusText(t){switch(parseInt(t)){case 1:return"正常";case 0:return"停用";case-1:return"异常";default:return"未知"}},async handleServiceToggle(t){if(1!==t.status&&0!==t.status)return this.$message.warning("转发状态异常,无法操作"),void this.$set(t,"serviceRunning",!t.serviceRunning);const e=t.serviceRunning;try{let s;this.$set(t,"switchLoading",!0),s=e?await z(t.id):await B(t.id),0===s.code?(this.$message.success(e?"服务已启动":"服务已暂停"),t.status=e?1:0):(this.$set(t,"serviceRunning",!e),this.$message.error(s.msg||"操作失败"))}catch(s){console.error("服务开关操作失败:",s),this.$set(t,"serviceRunning",!e),this.$message.error("网络错误,操作失败")}finally{this.$set(t,"switchLoading",!1)}}},filters:{dateFormat(t){if(!t)return"";const e=new Date(t);return e.toLocaleString()}}},Ut=Rt,Dt=(0,r.A)(Ut,Nt,At,!1,null,"066b1068",null),Vt=Dt.exports,Bt=function(){var t=this,e=t._self._c;return e("div",{staticClass:"speed-limit-container"},[e("div",{staticClass:"page-header"},[t._m(0),e("div",{staticClass:"header-actions"},[e("el-input",{staticStyle:{width:"250px","margin-right":"15px"},attrs:{placeholder:"搜索规则名称","prefix-icon":"el-icon-search",clearable:""},nativeOn:{keyup:function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:t.handleSearch.apply(null,arguments)}},model:{value:t.searchKeyword,callback:function(e){t.searchKeyword=e},expression:"searchKeyword"}},[e("el-button",{attrs:{slot:"append",icon:"el-icon-search"},on:{click:t.handleSearch},slot:"append"})],1),e("el-button",{staticClass:"add-btn",attrs:{type:"primary",icon:"el-icon-plus"},on:{click:t.handleAdd}},[t._v(" 新增限速规则 ")]),e("el-button",{attrs:{type:"success",icon:"el-icon-refresh",loading:t.loading},on:{click:t.loadSpeedLimits}},[t._v(" 刷新 ")])],1)]),e("div",{staticClass:"table-container"},[e("el-table",{directives:[{name:"loading",rawName:"v-loading",value:t.loading,expression:"loading"}],staticStyle:{width:"100%"},attrs:{data:t.speedLimitList,stripe:"",border:""}},[e("el-table-column",{attrs:{prop:"id",label:"ID",width:"80"}}),e("el-table-column",{attrs:{prop:"name",label:"规则名称","show-overflow-tooltip":""}}),e("el-table-column",{attrs:{prop:"speed",label:"速度限制"},scopedSlots:t._u([{key:"default",fn:function(s){return[e("el-tag",{attrs:{type:"info"}},[t._v(t._s(s.row.speed)+" Mbps")])]}}])}),e("el-table-column",{attrs:{prop:"status",label:"状态",width:"100"},scopedSlots:t._u([{key:"default",fn:function(s){return[e("el-tag",{attrs:{type:1===s.row.status?"success":"danger"}},[t._v(" "+t._s(1===s.row.status?"运行":"异常")+" ")])]}}])}),e("el-table-column",{attrs:{prop:"tunnelName",label:"绑定隧道","show-overflow-tooltip":""},scopedSlots:t._u([{key:"default",fn:function(s){return[s.row.tunnelName?e("el-tag",{attrs:{type:"success"}},[t._v(t._s(s.row.tunnelName))]):e("span",{staticClass:"text-muted"},[t._v("未绑定")])]}}])}),e("el-table-column",{attrs:{prop:"createdTime",label:"创建时间"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(t._f("dateFormat")(e.row.createdTime))+" ")]}}])}),e("el-table-column",{attrs:{prop:"updatedTime",label:"更新时间"},scopedSlots:t._u([{key:"default",fn:function(e){return[t._v(" "+t._s(t._f("dateFormat")(e.row.updatedTime))+" ")]}}])}),e("el-table-column",{attrs:{label:"操作",fixed:"right"},scopedSlots:t._u([{key:"default",fn:function(s){return[e("el-button",{attrs:{size:"mini",type:"primary",icon:"el-icon-edit"},on:{click:function(e){return t.handleEdit(s.row)}}}),e("el-button",{attrs:{size:"mini",type:"danger",icon:"el-icon-delete"},on:{click:function(e){return t.handleDelete(s.row)}}})]}}])})],1),t.loading||0!==t.speedLimitList.length?t._e():e("div",{staticClass:"empty-state"},[e("el-empty",{attrs:{description:"暂无限速规则"}},[e("el-button",{attrs:{type:"primary"},on:{click:t.handleAdd}},[t._v("创建第一个限速规则")])],1)],1)],1),e("el-dialog",{attrs:{title:t.dialogTitle,visible:t.dialogVisible,width:"500px"},on:{"update:visible":function(e){t.dialogVisible=e},close:t.resetForm}},[e("el-form",{ref:"speedLimitForm",attrs:{model:t.speedLimitForm,rules:t.rules,"label-width":"100px"}},[e("el-form-item",{attrs:{label:"规则名称",prop:"name"}},[e("el-input",{attrs:{placeholder:"请输入限速规则名称",clearable:""},model:{value:t.speedLimitForm.name,callback:function(e){t.$set(t.speedLimitForm,"name",e)},expression:"speedLimitForm.name"}})],1),e("el-form-item",{attrs:{label:"速度限制",prop:"speed"}},[e("el-input-number",{staticStyle:{width:"100%"},attrs:{min:1,max:1e6,placeholder:"请输入速度限制"},model:{value:t.speedLimitForm.speed,callback:function(e){t.$set(t.speedLimitForm,"speed",e)},expression:"speedLimitForm.speed"}}),e("div",{staticClass:"form-hint"},[t._v(" 单位:Mbps ")])],1),e("el-form-item",{attrs:{label:"绑定隧道",prop:"tunnelId"}},[e("el-select",{staticStyle:{width:"100%"},attrs:{placeholder:"请选择要绑定的隧道",filterable:""},on:{change:t.handleTunnelChange},model:{value:t.speedLimitForm.tunnelId,callback:function(e){t.$set(t.speedLimitForm,"tunnelId",e)},expression:"speedLimitForm.tunnelId"}},t._l(t.tunnelList,(function(t){return e("el-option",{key:t.id,attrs:{label:t.name,value:t.id}})})),1),e("div",{staticClass:"form-hint"},[t._v(" 选择要应用限速规则的隧道 ")])],1)],1),e("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[e("el-button",{on:{click:function(e){t.dialogVisible=!1}}},[t._v("取 消")]),e("el-button",{attrs:{type:"primary",loading:t.submitLoading},on:{click:t.handleSubmit}},[t._v(" "+t._s(t.submitLoading?"提交中...":"确 定")+" ")])],1)],1)],1)},zt=[function(){var t=this,e=t._self._c;return e("h1",{staticClass:"page-title"},[e("i",{staticClass:"el-icon-odometer"}),t._v(" 限速管理 ")])}],Ot={name:"Limit",data(){return{speedLimitList:[],tunnelList:[],loading:!1,dialogVisible:!1,dialogTitle:"",isEdit:!1,submitLoading:!1,searchKeyword:"",speedLimitForm:{id:null,name:"",speed:100,tunnelId:null,tunnelName:"",status:1},rules:{name:[{required:!0,message:"请输入规则名称",trigger:"blur"},{min:2,max:50,message:"长度在 2 到 50 个字符",trigger:"blur"}],speed:[{required:!0,message:"请输入速度限制",trigger:"blur"},{type:"number",min:1,message:"速度限制必须大于0",trigger:"blur"}],tunnelId:[{required:!0,message:"请选择要绑定的隧道",trigger:"change"}]}}},mounted(){this.loadSpeedLimits(),this.loadTunnels()},methods:{async loadSpeedLimits(){try{this.loading=!0;const t=await q();0===t.code?this.speedLimitList=t.data||[]:this.$message.error(t.msg||"获取限速规则列表失败")}catch(t){console.error("加载限速规则列表失败:",t),this.$message.error("加载限速规则列表失败")}finally{this.loading=!1}},async loadTunnels(){try{const t=await L();0===t.code?this.tunnelList=t.data||[]:console.error("获取隧道列表失败:",t.msg)}catch(t){console.error("加载隧道列表失败:",t)}},handleSearch(){this.searchKeyword.trim()?(this.loading=!0,setTimeout((()=>{const t=this.speedLimitList.filter((t=>t.name.includes(this.searchKeyword)));this.speedLimitList=t,this.loading=!1}),300)):this.loadSpeedLimits()},handleTunnelChange(t){const e=this.tunnelList.find((e=>e.id===t));this.speedLimitForm.tunnelName=e?e.name:""},handleAdd(){this.isEdit=!1,this.dialogTitle="新增限速规则",this.dialogVisible=!0},handleEdit(t){this.isEdit=!0,this.dialogTitle="编辑限速规则",this.speedLimitForm={id:t.id,name:t.name,speed:t.speed,tunnelId:t.tunnelId,tunnelName:t.tunnelName,status:t.status||1},this.dialogVisible=!0},async handleDelete(t){try{await this.$confirm(`确定要删除限速规则 "${t.name}" 吗?`,"提示",{confirmButtonText:"确定",cancelButtonText:"取消",type:"warning"});const e=await W(t.id);0===e.code?(this.$message.success("删除成功"),this.loadSpeedLimits()):this.$message.error(e.msg||"删除失败")}catch(e){"cancel"!==e&&(console.error("删除限速规则失败:",e),this.$message.error("删除失败"))}},async handleSubmit(){try{let t;if(await this.$refs.speedLimitForm.validate(),this.submitLoading=!0,this.isEdit)t=await M(this.speedLimitForm);else{const{id:e,...s}=this.speedLimitForm;t=await O(s)}0===t.code?(this.$message.success(this.isEdit?"修改成功":"创建成功"),this.dialogVisible=!1,this.loadSpeedLimits()):this.$message.error(t.msg||"操作失败")}catch(t){!1!==t&&(console.error("提交失败:",t),this.$message.error("操作失败"))}finally{this.submitLoading=!1}},resetForm(){this.speedLimitForm={id:null,name:"",speed:100,tunnelId:null,tunnelName:"",status:1},this.$nextTick((()=>{this.$refs.speedLimitForm&&this.$refs.speedLimitForm.clearValidate()}))}},filters:{dateFormat(t){if(!t)return"";const e=new Date(t);return e.toLocaleString()}}},qt=Ot,Mt=(0,r.A)(qt,Bt,zt,!1,null,"c8993518",null),Wt=Mt.exports;i["default"].use(u.Ay);const Kt=[{path:"/",redirect:"/login"},{path:"/login",component:et},{path:"/home",component:J,redirect:"/index",children:[{path:"/index",component:ot},{path:"/user",component:pt},{path:"/node",component:St},{path:"/tunnel",component:Pt},{path:"/forward",component:Vt},{path:"/limit",component:Wt}]}],Gt=new u.Ay({base:"",routes:Kt});Gt.beforeEach(((t,e,s)=>{if("/login"===t.path)return s();const i=localStorage.getItem("token");if(!i)return s("/login");s()}));var jt=Gt,Yt=s(1052),Jt=s.n(Yt);s(4979);function Ht(t){try{if(!t)return null;const e=t.split(".");if(3!==e.length)return null;const s=e[1],i=atob(s);return JSON.parse(i)}catch(e){return null}}function Qt(t){const e=Ht(t);return e?e.role_id:null}function Xt(t){const e=Ht(t);if(!e)return!1;const s=Math.floor(Date.now()/1e3);return e.exp>s}function Zt(){return localStorage.getItem("token")}function te(){const t=Zt();return t&&Xt(t)?Qt(t):null}function ee(){const t=te();return 0===t}function se(t){const e=te();return e===t}function ie(){const t=Zt();return t&&Xt(t)}const ae={inserted(t,e){ee()||t.parentNode&&t.parentNode.removeChild(t)},update(t,e){ee()?t.style.display="":t.style.display="none"}},ne={inserted(t,e){const s=e.value;se(s)||t.parentNode&&t.parentNode.removeChild(t)},update(t,e){const s=e.value;se(s)?t.style.display="":t.style.display="none"}},le={inserted(t,e){ie()||t.parentNode&&t.parentNode.removeChild(t)},update(t,e){ie()?t.style.display="":t.style.display="none"}};function oe(t){t.directive("admin",ae),t.directive("role",ne),t.directive("auth",le)}i["default"].use(Jt()),oe(i["default"]),i["default"].prototype.$isAdmin=ee,i["default"].prototype.$hasRole=se,i["default"].prototype.$isLoggedIn=ie,i["default"].prototype.$getCurrentUserRoleId=te;const re=Ct.API_BASE_URL;i["default"].prototype.baseUrl=re,i["default"].prototype.$axios=h.A,h.A.defaults.baseURL=re,i["default"].config.productionTip=!1,i["default"].filter("dateFormat",(function(t){const e=new Date(t),s=e.getFullYear(),i=(e.getMonth()+1+"").padStart(2,"0"),a=(e.getDate()+"").padStart(2,"0"),n=(e.getHours()+"").padStart(2,"0"),l=(e.getMinutes()+"").padStart(2,"0"),o=(e.getSeconds()+"").padStart(2,"0");return`${s}-${i}-${a} ${n}:${l}:${o}`})),new i["default"]({router:jt,render:t=>t(c)}).$mount("#app")}},e={};function s(i){var a=e[i];if(void 0!==a)return a.exports;var n=e[i]={id:i,loaded:!1,exports:{}};return t[i].call(n.exports,n,n.exports,s),n.loaded=!0,n.exports}s.m=t,function(){s.amdO={}}(),function(){var t=[];s.O=function(e,i,a,n){if(!i){var l=1/0;for(c=0;c<t.length;c++){i=t[c][0],a=t[c][1],n=t[c][2];for(var o=!0,r=0;r<i.length;r++)(!1&n||l>=n)&&Object.keys(s.O).every((function(t){return s.O[t](i[r])}))?i.splice(r--,1):(o=!1,n<l&&(l=n));if(o){t.splice(c--,1);var d=a();void 0!==d&&(e=d)}}return e}n=n||0;for(var c=t.length;c>0&&t[c-1][2]>n;c--)t[c]=t[c-1];t[c]=[i,a,n]}}(),function(){s.n=function(t){var e=t&&t.__esModule?function(){return t["default"]}:function(){return t};return s.d(e,{a:e}),e}}(),function(){s.d=function(t,e){for(var i in e)s.o(e,i)&&!s.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:e[i]})}}(),function(){s.g=function(){if("object"===typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"===typeof window)return window}}()}(),function(){s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)}}(),function(){s.r=function(t){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}}(),function(){s.nmd=function(t){return t.paths=[],t.children||(t.children=[]),t}}(),function(){var t={524:0};s.O.j=function(e){return 0===t[e]};var e=function(e,i){var a,n,l=i[0],o=i[1],r=i[2],d=0;if(l.some((function(e){return 0!==t[e]}))){for(a in o)s.o(o,a)&&(s.m[a]=o[a]);if(r)var c=r(s)}for(e&&e(i);d<l.length;d++)n=l[d],s.o(t,n)&&t[n]&&t[n][0](),t[n]=0;return s.O(c)},i=self["webpackChunkgost"]=self["webpackChunkgost"]||[];i.forEach(e.bind(null,0)),i.push=e.bind(null,i.push.bind(i))}();var i=s.O(void 0,[504],(function(){return s(3705)}));i=s.O(i)})();
//# sourceMappingURL=app.9855802f.js.map