Compare commits
	
		
			2 Commits
		
	
	
		
			dcf587dc8a
			...
			cb99b03c87
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| cb99b03c87 | |||
| 8e941d6fbd | 
							
								
								
									
										544
									
								
								cli-traffic.html
									
									
									
									
									
								
							
							
						
						
									
										544
									
								
								cli-traffic.html
									
									
									
									
									
								
							| @ -36,6 +36,7 @@ | ||||
|     <script src="static/js/countdown.js"></script> | ||||
|     <script src="https://cdn.jsdelivr.net/npm/qrcode@1.4.4/build/qrcode.min.js"></script> | ||||
|     <script src="static/js/common.js?20250717"></script> | ||||
|     <link rel="stylesheet" href="static/css/cli-traffic.css"> | ||||
|   </head> | ||||
|   <body> | ||||
|     <div id="app"> | ||||
| @ -116,6 +117,14 @@ | ||||
|                 长效号码 | ||||
|               </vs-sidebar-item> | ||||
|           </vs-sidebar-group> | ||||
|           <vs-sidebar-group open title="API管理"> | ||||
|               <vs-sidebar-item | ||||
|                 index="5" | ||||
|                 @click="handleActiveMenu(5)" | ||||
|                 icon="vpn_key" | ||||
|                 >API密钥</vs-sidebar-item | ||||
|               > | ||||
|           </vs-sidebar-group> | ||||
|            | ||||
|         </div> | ||||
|  | ||||
| @ -181,8 +190,25 @@ | ||||
|             </div> | ||||
|              | ||||
|             <div class="content-conter-header" v-if="activeMenu === 3||activeMenu===4"> | ||||
|               <vs-select | ||||
|               v-model="smsQuery.platform" | ||||
|               autocomplete | ||||
|               clearable | ||||
|               class="selectExample" | ||||
|               placeholder="平台" | ||||
|               label="平台" | ||||
|                 @change="handlePlatformChange" | ||||
|               > | ||||
|               <vs-select-item v-for="item in smsPlatformList"  | ||||
|               :key="'sms_platform_'+item.value"  | ||||
|               :value="item.value"  | ||||
|               :text="item.label" | ||||
|               > | ||||
|               </vs-select-item> | ||||
|             </vs-select> | ||||
|               <vs-select | ||||
|                 autocomplete | ||||
|                 clearable | ||||
|                 v-model="smsQuery.serviceCode" | ||||
|                 autocomplete | ||||
|                 class="selectExample" | ||||
| @ -194,7 +220,7 @@ | ||||
|                   :key="'sms_service_'+item.code" | ||||
|                   :value="item.code" | ||||
|                   :text="item.name" | ||||
|                   @click="handleSmsServiceChange(item)" | ||||
|                   @click="handleFormServiceChange(item)" | ||||
|                 ></vs-select-item> | ||||
|               </vs-select> | ||||
|  | ||||
| @ -375,6 +401,7 @@ | ||||
|             <vs-row v-else-if="activeMenu===3" class="content-conter-container"> | ||||
|                 <vs-table class="tablex" :data="smsList"> | ||||
|                 <template slot="thead"> | ||||
|                   <vs-th> 通道 </vs-th> | ||||
|                   <vs-th> 服务 </vs-th> | ||||
|                   <vs-th> Phone </vs-th> | ||||
|                   <vs-th> 验证码 </vs-th> | ||||
| @ -385,6 +412,10 @@ | ||||
|  | ||||
|                 <template slot-scope="{data}"> | ||||
|                   <vs-tr :key="indextr" v-for="(tr, indextr) in data"> | ||||
|                     <vs-td :data="data[indextr].platformName"> | ||||
|                       {{data[indextr].platformName}} | ||||
|                     </vs-td> | ||||
|  | ||||
|                     <vs-td :data="data[indextr].service"> | ||||
|                       {{data[indextr].service}} | ||||
|                     </vs-td> | ||||
| @ -435,11 +466,13 @@ | ||||
|             <vs-row v-else-if="activeMenu===4" class="content-conter-container"> | ||||
|               <vs-table class="tablex" :data="smsList"> | ||||
|                 <template slot="thead"> | ||||
|                   <vs-th> 通道 </vs-th> | ||||
|                   <vs-th> 服务 </vs-th> | ||||
|                   <vs-th> Phone </vs-th> | ||||
|                   <vs-th> 期限(月)</vs-th> | ||||
|                   <vs-th> 验证码 </vs-th> | ||||
|                   <vs-th> 状态 </vs-th> | ||||
|                   <vs-th> 说明 </vs-th> | ||||
|                   <vs-th> 过期时间 </vs-th> | ||||
|                   <vs-th> 自动续期 </vs-th> | ||||
|                   <vs-th> 操作 </vs-th> | ||||
| @ -447,6 +480,9 @@ | ||||
|  | ||||
|                 <template slot-scope="{data}"> | ||||
|                   <vs-tr :key="indextr" v-for="(tr, indextr) in data"> | ||||
|                     <vs-td :data="data[indextr].platformName"> | ||||
|                       {{data[indextr].platformName}} | ||||
|                     </vs-td> | ||||
|                     <vs-td :data="data[indextr].service"> | ||||
|                       {{data[indextr].service}} | ||||
|                     </vs-td> | ||||
| @ -461,12 +497,31 @@ | ||||
|                       {{data[indextr].code}} | ||||
|                     </vs-td> | ||||
|                     <vs-td :data="data[indextr].status"> | ||||
|                        <vs-chip v-if="data[indextr].status===0" transparent color="primary">未唤醒</vs-chip> | ||||
|                        <vs-chip v-else-if="data[indextr].status===1" transparent color="primary">等待中</vs-chip> | ||||
|                        <vs-chip v-else-if="data[indextr].status===2" transparent color="success">已使用</vs-chip> | ||||
|                        <vs-chip v-else-if="data[indextr].status===3" transparent color="warning">过期</vs-chip> | ||||
|                        <!-- 唤醒倒计时:当前时间小于startTime时显示 --> | ||||
|                        <vs-chip v-if="data[indextr].status===1&&data[indextr].startTime && new Date() < new Date(data[indextr].startTime)" transparent color="info"> | ||||
|                          唤醒中   | ||||
|                          <countdown :time="new Date(data[indextr].startTime).getTime() - new Date().getTime()" v-slot="timeObj"> | ||||
|                            {{ timeObj.minutes.toString().padStart(2, '0') }}:{{ timeObj.seconds.toString().padStart(2, '0') }} | ||||
|                          </countdown> | ||||
|                        </vs-chip> | ||||
|                        <!-- 接码中倒计时:当前时间在startTime和endTime之间时显示 --> | ||||
|                        <vs-chip v-else-if="data[indextr].status===1&&data[indextr].startTime && data[indextr].endTime && new Date() >= new Date(data[indextr].startTime) && new Date() < new Date(data[indextr].endTime)" transparent color="warning"> | ||||
|                          接码中   | ||||
|                          <countdown :time="new Date(data[indextr].endTime).getTime() - new Date().getTime()" v-slot="timeObj"> | ||||
|                            {{ timeObj.minutes.toString().padStart(2, '0') }}:{{ timeObj.seconds.toString().padStart(2, '0') }} | ||||
|                          </countdown> | ||||
|                        </vs-chip> | ||||
|                        <!-- 原有状态显示 --> | ||||
|                        <div v-else> | ||||
|                          <vs-chip v-if="data[indextr].status===0" transparent color="primary">未唤醒</vs-chip> | ||||
|                          <vs-chip v-else-if="data[indextr].status===1" transparent color="primary">等待中</vs-chip> | ||||
|                          <vs-chip v-else-if="data[indextr].status===2" transparent color="success">已使用</vs-chip> | ||||
|                          <vs-chip v-else-if="data[indextr].status===3" transparent color="warning">过期</vs-chip> | ||||
|                        </div> | ||||
|                     </vs-td> | ||||
|                     <vs-td :data="data[indextr].remark"> | ||||
|                       {{data[indextr].remark}} | ||||
|                     </vs-td> | ||||
|  | ||||
|                     <vs-td :data="data[indextr].expireTime"> | ||||
|                         {{formatDate(data[indextr].expireTime)}} | ||||
|                     </vs-td> | ||||
| @ -497,7 +552,49 @@ | ||||
|                 :page-size="smsQuery.pageSize" | ||||
|                 @change="getSmsList" | ||||
|               ></vs-pagination> | ||||
|             </vs-row> | ||||
|  | ||||
|             <!--API管理--> | ||||
|             <vs-row v-else-if="activeMenu===5" class="content-conter-container"> | ||||
|               <div class="api-management"> | ||||
|                 <div class="api-section"> | ||||
|                   <h3>API密钥管理</h3> | ||||
|                   <div class="api-key-container"> | ||||
|                     <div class="api-key-item"> | ||||
|                       <label>API Key:</label> | ||||
|                       <div class="api-key-display"> | ||||
|                         <vs-input  | ||||
|                           :type="showApiKey ? 'text' : 'password'" | ||||
|                           :value="apiKey" | ||||
|                           readonly | ||||
|                           placeholder="暂无API Key" | ||||
|                         ></vs-input> | ||||
|                         <vs-button  | ||||
|                           icon="visibility" | ||||
|                           type="flat" | ||||
|                           size="small" | ||||
|                           @click="toggleApiKeyVisibility" | ||||
|                         > | ||||
|                           {{ showApiKey ? '隐藏' : '显示' }} | ||||
|                         </vs-button> | ||||
|                       </div> | ||||
|                     </div> | ||||
|                      | ||||
|                     <div class="api-status-item"> | ||||
|                       <label>API状态:</label> | ||||
|                       <div class="api-status-display"> | ||||
|                         <vs-switch  | ||||
|                           v-model="apiStatus" | ||||
|                           @input="handleApiStatusChange" | ||||
|                         /> | ||||
|                         <span class="status-text"> | ||||
|                           {{ apiStatus ? '开启' : '关闭' }} | ||||
|                         </span> | ||||
|                       </div> | ||||
|                     </div> | ||||
|                   </div> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </vs-row> | ||||
|  | ||||
|           </div> | ||||
| @ -700,6 +797,20 @@ | ||||
|       <!--获取号码--> | ||||
|       <vs-popup title="租赁号码" :active.sync="showGetSms" :loading="loading"> | ||||
|         <div> | ||||
|           <vs-select | ||||
|             autocomplete | ||||
|             v-model="smsForm.platform" | ||||
|             autocomplete | ||||
|             class="selectExample" | ||||
|             placeholder="请选择平台" | ||||
|             label="平台" | ||||
|             @change="handlePlatformChange"> | ||||
|             <vs-select-item | ||||
|               v-for="item in smsPlatformList" | ||||
|               :key="'form_platform'+item.value" | ||||
|               :value="item.value" | ||||
|               :text="item.label"></vs-select-item> | ||||
|           </vs-select> | ||||
|           <vs-select | ||||
|             autocomplete | ||||
|             v-model="smsForm.serviceCode" | ||||
| @ -707,7 +818,7 @@ | ||||
|             class="selectExample" | ||||
|             placeholder="请选择服务" | ||||
|             label="服务" | ||||
|           > | ||||
|             @change="handleSmsServiceChange"> | ||||
|             <vs-select-item | ||||
|               v-for="item in smsServices" | ||||
|               :key="'form_service'+item.code" | ||||
| @ -720,10 +831,10 @@ | ||||
|           <vs-input  class="inputx" placeholder="租赁月份" label="租赁月份" v-model="smsForm.period"/> | ||||
|         </div> | ||||
|         <div class="form-item" style="text-align: right;"> | ||||
|           价格U: {{ this.smsForm.type===1&&this.smsForm.period>0? this.smsPrice*this.smsForm.period :this.smsPrice}} | ||||
|           价格U: {{ totalSmsPrice}} | ||||
|         </div> | ||||
|         <div class="form-item" style="text-align: right"> | ||||
|           <vs-button size="small" @click="getNumber" | ||||
|         <div class="form-item" v-if="totalSmsPrice>0" style="text-align: right"> | ||||
|           <vs-button size="small" @click="getNumber" v-if="smsPrice" | ||||
|             >确认租赁</vs-button> | ||||
|         </div> | ||||
|       </vs-popup> | ||||
| @ -786,15 +897,23 @@ | ||||
|             pageSize:10, | ||||
|             total:0, | ||||
|             serviceCode:"", | ||||
|             platform:"", | ||||
|           }, | ||||
|           //短信列表 | ||||
|           smsList:[], | ||||
|           showGetSms:false, | ||||
|           smsForm:{}, | ||||
|           smsPlatformList:[], | ||||
|           checkSmsCodeTimer:null, | ||||
|           longSmsStatusTimer:null, | ||||
|           smsPrice:undefined, | ||||
|           //Ip单价 | ||||
|           price:undefined, | ||||
|           //API管理相关 | ||||
|           apiId:undefined, | ||||
|           apiKey: '', | ||||
|           showApiKey: false, | ||||
|           apiStatus: false, | ||||
|         }; | ||||
|       }, | ||||
|       mounted() { | ||||
| @ -815,10 +934,22 @@ | ||||
|         this.getMyProxy(); | ||||
|         this.getTraffictList(); | ||||
|         this.getTrafficServer(); | ||||
|         this.getSmsServices(); | ||||
|         // this.getSmsServices(); | ||||
|         this.getSmsPlatformList(); | ||||
|  | ||||
|         //定时器 | ||||
|         this.createCheckSmsCodeTimer(); | ||||
|         this.createLongSmsStatusTimer(); | ||||
|       }, | ||||
|       computed:{ | ||||
|         totalSmsPrice(){ | ||||
|           // 使用 parseFloat() 将值转换为浮点数,如果无法转换则默认为 0 | ||||
|           const period = parseFloat(this.smsForm.period) || 1; | ||||
|           const price = parseFloat(this.smsPrice) || 0; | ||||
|  | ||||
|           // 确保在执行乘法或 toFixed() 前,price 和 period 都是数字 | ||||
|           return period > 0 ? (price * period).toFixed(2) : price.toFixed(2); | ||||
|         } | ||||
|       }, | ||||
|       watch: { | ||||
|         showGetProxy(val, oldVal) { | ||||
| @ -858,7 +989,7 @@ | ||||
|         }, | ||||
|       }, | ||||
|       methods: { | ||||
|         getSmsServices(){ | ||||
|         getSmsServices(platform){ | ||||
|           fetch(requestApi + "/sms-services/list", { | ||||
|             headers:{ | ||||
|               Authorization: `Bearer ${this.token}`, | ||||
| @ -868,9 +999,10 @@ | ||||
|           }) | ||||
|           .then((res) => res.json()) | ||||
|           .then((response) => { | ||||
|             console.log('getSmsServices',response); | ||||
|               if (response.code === 200) { | ||||
|                 this.smsServices = response.data; | ||||
|                 let datas= response.data.filter(x=>x.platformCode===platform) | ||||
|                 console.log("getSmsServices:",datas) | ||||
|                 this.smsServices = datas?datas:[]; | ||||
|               } | ||||
|             }) | ||||
|           .catch((error) => { | ||||
| @ -1265,8 +1397,9 @@ | ||||
|               this.smsForm.type=0; | ||||
|               this.smsForm.serviceCode=""; | ||||
|               this.smsForm.period=undefined; | ||||
|               this.smsForm.platform=""; | ||||
|               this.reloadSmsList(); | ||||
|               this.handleGetPrice(this.smsForm.type); | ||||
|               // this.handleGetPrice(this.smsForm.type); | ||||
|               break; | ||||
|             case 4: | ||||
|               this.smsQuery.page=1; | ||||
| @ -1274,8 +1407,12 @@ | ||||
|               this.smsForm.type=1; | ||||
|               this.smsForm.serviceCode=""; | ||||
|               this.smsForm.period=undefined; | ||||
|               this.smsForm.platform=""; | ||||
|               this.reloadSmsList(); | ||||
|               this.handleGetPrice(this.smsForm.type); | ||||
|               // this.handleGetPrice(this.smsForm.type); | ||||
|               break; | ||||
|             case 5: | ||||
|               this.getApiKey(); | ||||
|               break; | ||||
|           } | ||||
|         }, | ||||
| @ -1631,10 +1768,17 @@ | ||||
|         }, | ||||
|         //sms service切换 | ||||
|         handleSmsServiceChange(item){ | ||||
|           this.smsQuery.serviceCode=item.code; | ||||
|           let service=this.smsServices.find(x=>x.code===item); | ||||
|  | ||||
|           if(service&&this.smsForm.type===1){ | ||||
|             this.smsPrice=service.longPrice; | ||||
|           }else if(service&&this.smsForm.type===0){ | ||||
|             this.smsPrice=service.price; | ||||
|             } | ||||
|         }, | ||||
|         //重置sms列表 | ||||
|         handleResetSmsList(){ | ||||
|           this.smsQuery.platform=""; | ||||
|           this.smsQuery.serviceCode=""; | ||||
|           this.smsQuery.page=1; | ||||
|  | ||||
| @ -1658,6 +1802,7 @@ | ||||
|             type:type, | ||||
|             pageIndex: this.smsQuery.page, | ||||
|             pageSize: this.smsQuery.pageSize, | ||||
|             platformCode:this.smsQuery.platform, | ||||
|             serviceCode: this.smsQuery.serviceCode, | ||||
|           }; | ||||
|  | ||||
| @ -1691,6 +1836,7 @@ | ||||
|         handleShowGetSms(){ | ||||
|           this.smsForm.period=undefined; | ||||
|           this.smsForm.serviceCode=""; | ||||
|           this.smsForm.platform=""; | ||||
|           this.showGetSms = true; | ||||
|         }, | ||||
|         resetSmsForm(){ | ||||
| @ -1700,6 +1846,7 @@ | ||||
|             type = 1; | ||||
|           } | ||||
|           this.smsForm={ | ||||
|             platform: "", | ||||
|             serviceCode: "", | ||||
|             period:undefined, | ||||
|             type:type, | ||||
| @ -1728,6 +1875,7 @@ | ||||
|           } | ||||
|  | ||||
|           let data = JSON.stringify({ | ||||
|             platformCode: this.smsForm.platform, | ||||
|             serviceCode: this.smsForm.serviceCode, | ||||
|             type: this.smsForm.type, | ||||
|             period: Number(this.smsForm.period), | ||||
| @ -1802,14 +1950,14 @@ | ||||
|                       codesMap.set(item.activationId, item); | ||||
|                   }); | ||||
|                  | ||||
|                   console.log(codesMap); | ||||
|  | ||||
|                   this.smsList.forEach(item => { | ||||
|                     const newItem = codesMap.get(item.activationId); // 尝试从 Map 中获取新 code | ||||
|  | ||||
|                     if (newItem!==undefined&&newItem!==null&&newItem.status===2) { // 如果找到了匹配的 activationId | ||||
|                         item.code = newItem.code;   // 替换 code | ||||
|                         item.status = newItem.status;       // 修改 status 为 2 | ||||
|                     }else if (newItem!==undefined&&newItem!==null&&newItem.status===3){ | ||||
|                         item.status=newItem.status | ||||
|                     }}) | ||||
|                 } | ||||
|             }) | ||||
| @ -1930,23 +2078,25 @@ | ||||
|                 this.hiddenLoading(); | ||||
|              }); | ||||
|         }, | ||||
|         handleGetPrice(type){ | ||||
|           fetch(requestApi + "/sms-services/price?type=" + type, { | ||||
|             headers: { | ||||
|               Authorization: `Bearer ${this.token}`, | ||||
|               "Content-Type": "application/json", | ||||
|               Accept: "application/json", | ||||
|             } | ||||
|           }) | ||||
|           .then((res) => res.json()) | ||||
|            .then((response) => { | ||||
|               if (response.code === 200) { | ||||
|                 this.smsPrice = response.data; | ||||
|         handleGetPrice(type,platformCode,servicesCode){ | ||||
|           if (type&&platformCode&&servicesCode) { | ||||
|             fetch(`${requestApi}/sms-services/price?type=${type}&platformCode=${platformCode}&serviceCode=${servicesCode}`, { | ||||
|               headers: { | ||||
|                 Authorization: `Bearer ${this.token}`, | ||||
|                 "Content-Type": "application/json", | ||||
|                 Accept: "application/json", | ||||
|               } | ||||
|            }) | ||||
|            .catch((error) => { | ||||
|               console.log(error); | ||||
|             }); | ||||
|             }) | ||||
|             .then((res) => res.json()) | ||||
|             .then((response) => { | ||||
|                 if (response.code === 200) { | ||||
|                   this.smsPrice = response.data; | ||||
|                 } | ||||
|             }) | ||||
|             .catch((error) => { | ||||
|                 console.log(error); | ||||
|               }); | ||||
|           } | ||||
|         }, | ||||
|         //取消sms | ||||
|         handleCancelSms(row){ | ||||
| @ -2244,156 +2394,182 @@ | ||||
|               this.hiddenLoading(); | ||||
|             }); | ||||
|         }, | ||||
|         getSmsPlatformList(){ | ||||
|           fetch(requestApi + "/sms-services/platform", { | ||||
|             headers: { | ||||
|               Authorization: `Bearer ${this.token}`, | ||||
|               "Content-Type": "application/json", | ||||
|               Accept: "application/json", | ||||
|             } | ||||
|           }) | ||||
|           .then((res) => res.json()) | ||||
|            .then((response) => { | ||||
|             console.log(response);   | ||||
|               if (response.code === 200) { | ||||
|                 this.smsPlatformList = response.data; | ||||
|               } | ||||
|            }) | ||||
|            .catch((error) => { | ||||
|               console.log(error); | ||||
|             }); | ||||
|         }, | ||||
|         handlePlatformChange(item){ | ||||
|           console.log("handlePlatformChange:",item); | ||||
|           this.smsQuery.serviceCode=""; | ||||
|           this.smsForm.serviceCode=""; | ||||
|            | ||||
|           this.getSmsServices(item); | ||||
|         }, | ||||
|         handleFormServiceChange(serviceCode){ | ||||
|           this.handleGetPrice(this.smsForm.type,this.smsForm.platformCode,serviceCode); | ||||
|         }, | ||||
|         // API管理相关方法 | ||||
|         getApiKey() { | ||||
|           console.log("",requestApi+"/api-info"); | ||||
|           fetch(requestApi + "/api-info", { | ||||
|             headers: { | ||||
|               Authorization: `Bearer ${this.token}`, | ||||
|               "Content-Type": "application/json", | ||||
|               Accept: "application/json", | ||||
|             }, | ||||
|           }) | ||||
|           .then((res) => res.json()) | ||||
|           .then((response) => { | ||||
|             if (response.code === 200) { | ||||
|               this.apiId = response.data.id | ||||
|               this.apiKey = response.data.api || ''; | ||||
|               this.apiStatus = response.data.status === 1; | ||||
|             } | ||||
|           }) | ||||
|           .catch((error) => { | ||||
|             console.log(error); | ||||
|           }); | ||||
|         }, | ||||
|         toggleApiKeyVisibility() { | ||||
|           this.showApiKey = !this.showApiKey; | ||||
|         }, | ||||
|         handleApiStatusChange(status) { | ||||
|           this.showLoading(); | ||||
|           const data = JSON.stringify({ | ||||
|             id:this.apiId, | ||||
|             status: status ? 1 : 2 | ||||
|           }); | ||||
|            | ||||
|           fetch(requestApi + "/api-info", { | ||||
|             method: "PUT", | ||||
|             headers: { | ||||
|               Authorization: `Bearer ${this.token}`, | ||||
|               "Content-Type": "application/json", | ||||
|               Accept: "application/json", | ||||
|             }, | ||||
|             body: data, | ||||
|           }) | ||||
|           .then((res) => res.json()) | ||||
|           .then((response) => { | ||||
|             if (response.code === 200) { | ||||
|               this.$vs.notify({ | ||||
|                 title: "提示", | ||||
|                 color: "success", | ||||
|                 text: `API已${status ? '开启' : '关闭'}`, | ||||
|                 position: "top-right", | ||||
|               }); | ||||
|             } else { | ||||
|               // 如果请求失败,恢复原状态 | ||||
|               this.apiStatus = !status; | ||||
|               this.$vs.notify({ | ||||
|                 title: "提示", | ||||
|                 color: "danger", | ||||
|                 text: response.msg || "操作失败", | ||||
|                 position: "top-right", | ||||
|               }); | ||||
|             } | ||||
|           }) | ||||
|           .catch((error) => { | ||||
|             // 如果请求失败,恢复原状态 | ||||
|             this.apiStatus = !status; | ||||
|             console.log(error); | ||||
|             this.$vs.notify({ | ||||
|               title: "提示", | ||||
|               color: "danger", | ||||
|               text: "网络错误,请稍后重试", | ||||
|               position: "top-right", | ||||
|             }); | ||||
|           }) | ||||
|           .finally(() => { | ||||
|             this.hiddenLoading(); | ||||
|           }); | ||||
|         }, | ||||
|         /** | ||||
|          * 创建长效号码状态检查定时器 | ||||
|          * 用于定期检查和更新长效号码的倒计时状态 | ||||
|          */ | ||||
|         createLongSmsStatusTimer() { | ||||
|           if (this.longSmsStatusTimer) { | ||||
|             clearInterval(this.longSmsStatusTimer); | ||||
|           } | ||||
|           // 每秒检查一次状态更新 | ||||
|           this.longSmsStatusTimer = setInterval(() => { | ||||
|             this.updateLongSmsStatus(); | ||||
|           }, 1000); | ||||
|         }, | ||||
|         /** | ||||
|          * 清理长效号码状态检查定时器 | ||||
|          */ | ||||
|         cleanLongSmsStatusTimer() { | ||||
|           if (this.longSmsStatusTimer) { | ||||
|             clearInterval(this.longSmsStatusTimer); | ||||
|             this.longSmsStatusTimer = null; | ||||
|           } | ||||
|         }, | ||||
|         /** | ||||
|          * 更新长效号码状态 | ||||
|          * 检查倒计时是否结束,并触发相应的状态更新 | ||||
|          */ | ||||
|         updateLongSmsStatus() { | ||||
|           if (this.activeMenu !== 4 || !this.smsList.length) { | ||||
|             return; | ||||
|           } | ||||
|            | ||||
|           const now = new Date(); | ||||
|           let needRefresh = false; | ||||
|            | ||||
|           this.smsList.forEach(item => { | ||||
|             // 检查唤醒倒计时是否结束 | ||||
|             if (item.startTime && now >= new Date(item.startTime) &&  | ||||
|                 item.endTime && now < new Date(item.endTime)) { | ||||
|               // 唤醒倒计时结束,进入接码中状态 | ||||
|               if (!item._inReceivingState) { | ||||
|                 item._inReceivingState = true; | ||||
|                 needRefresh = true; | ||||
|               } | ||||
|             } | ||||
|              | ||||
|             // 检查接码中倒计时是否结束 | ||||
|             if (item.endTime && now >= new Date(item.endTime)) { | ||||
|               // 接码中倒计时结束,恢复原有逻辑 | ||||
|               if (item._inReceivingState) { | ||||
|                 item._inReceivingState = false; | ||||
|                 needRefresh = true; | ||||
|               } | ||||
|             } | ||||
|           }); | ||||
|            | ||||
|           // 如果有状态变化,强制更新视图 | ||||
|           if (needRefresh) { | ||||
|             this.$forceUpdate(); | ||||
|           } | ||||
|         }, | ||||
|       }, | ||||
|       beforeDestroy() { | ||||
|         // 清理定时器,防止内存泄漏 | ||||
|         this.cleanLongSmsStatusTimer(); | ||||
|         if (this.checkSmsCodeTimer) { | ||||
|           clearInterval(this.checkSmsCodeTimer); | ||||
|         } | ||||
|         if (this.checkOrderTimer) { | ||||
|           clearInterval(this.checkOrderTimer); | ||||
|         } | ||||
|       }, | ||||
|     }); | ||||
|   </script> | ||||
|   <style> | ||||
|     html, | ||||
|     body, | ||||
|     #app { | ||||
|       padding: 0; | ||||
|       margin: 0; | ||||
|       width: 100%; | ||||
|       height: 100%; | ||||
|       font-family: DMSans-Regular; | ||||
|     } | ||||
|  | ||||
|     .header { | ||||
|       display: flex; | ||||
|       justify-content: space-between; | ||||
|       align-items: center; | ||||
|       height: 60px; | ||||
|       background-color: #fff; | ||||
|       border-bottom: 1px solid #eee; | ||||
|       padding: 0 20px; | ||||
|     } | ||||
|  | ||||
|     .parentx-static { | ||||
|       overflow: hidden; | ||||
|       height: 500px; | ||||
|       position: relative; | ||||
|     } | ||||
|  | ||||
|     .logo { | ||||
|       font-weight: bold; | ||||
|       font-size: 20px; | ||||
|       padding: 15px 0px 15px 15px; | ||||
|       height: 100%; | ||||
|       cursor: pointer; | ||||
|     } | ||||
|  | ||||
|     .main-area { | ||||
|       display: flex; | ||||
|       height: calc(100vh - 60px); /* 减去 header 高度 */ | ||||
|     } | ||||
|  | ||||
|     .sidebar { | ||||
|       width: 200px; | ||||
|       background-color: #ffffff; | ||||
|       border-right: 1px solid #eee; | ||||
|     } | ||||
|  | ||||
|     .sidebar .vs-content-sidebar .vs-sidebar { | ||||
|       background-color: unset !important; | ||||
|     } | ||||
|  | ||||
|     .content { | ||||
|       flex: 1; | ||||
|       padding: 8px; | ||||
|       overflow: auto; | ||||
|       background: #eeeeee; | ||||
|     } | ||||
|  | ||||
|     .content-conter { | ||||
|       background-color: white; | ||||
|       padding: 16px; | ||||
|       height: 100%; | ||||
|     } | ||||
|  | ||||
|     /* .user-dropdown{ | ||||
|         color: #000; | ||||
|     } */ | ||||
|     .dropdown-menu { | ||||
|       width: 100px; | ||||
|     } | ||||
|  | ||||
|     .tablex { | ||||
|       width: 100%; | ||||
|       padding-top: 10px; | ||||
|     } | ||||
|  | ||||
|     .popup-content { | ||||
|       padding-bottom: 15px; | ||||
|     } | ||||
|  | ||||
|     .popup-bottom { | ||||
|       text-align: right; | ||||
|       padding-top: 10px; | ||||
|     } | ||||
|  | ||||
|     .qr-code { | ||||
|       height: 155px; | ||||
|     } | ||||
|  | ||||
|     .success-contianer { | ||||
|       height: 100%; | ||||
|       line-height: 155px; | ||||
|     } | ||||
|  | ||||
|     .success-contianer img { | ||||
|       height: 40px; | ||||
|     } | ||||
|  | ||||
|     .content-conter-header { | ||||
|       display: flex; | ||||
|       padding-bottom: 5px; | ||||
|     } | ||||
|  | ||||
|     /* .content-conter-header > *:not(:first-child) { | ||||
|       margin-left: 10px; | ||||
|     } */ | ||||
|  | ||||
|     .balance-btn[data-v-d585ebde] { | ||||
|       padding: 0 10px; | ||||
|       box-sizing: border-box; | ||||
|       max-width: max-content; | ||||
|       height: 30px; | ||||
|       line-height: 30px; | ||||
|       background: #ff916f33; | ||||
|       border-radius: 4px; | ||||
|       font-weight: 700; | ||||
|       font-size: 14px; | ||||
|       color: #ff916f; | ||||
|       margin-right: 28px; | ||||
|       cursor: pointer; | ||||
|     } | ||||
|  | ||||
|     .table-cell-center { | ||||
|       text-align: left; | ||||
|     } | ||||
|  | ||||
|     .btn-group { | ||||
|             display: flex; | ||||
|     gap: 10px; | ||||
|     place-items: end; | ||||
|     padding-left: 10px; | ||||
|     } | ||||
|  | ||||
|     .operat-btn{ | ||||
|         margin-top: 10px; | ||||
|     } | ||||
|      | ||||
|     .form-item{ | ||||
|       padding-top:  10px | ||||
|     } | ||||
|  | ||||
|     .content-conter-container{ | ||||
|       height: calc(100% - 45px); | ||||
|       overflow-y: auto; | ||||
|     } | ||||
|  | ||||
|     .content-conter-footer{ | ||||
|       height: 45px; | ||||
|     } | ||||
|   </style> | ||||
| </html> | ||||
|  | ||||
							
								
								
									
										41
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								index.html
									
									
									
									
									
								
							| @ -1446,51 +1446,10 @@ | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="footer_right"> | ||||
|           <!-- <div> | ||||
|             <h2>Solutions</h2> | ||||
|             <a href="download.html">Download</a> | ||||
|             <a href="socks5-ip.html">ISP S5</a> | ||||
|             <a href="enterprise-socks5-ip.html">Enterprise ISP S5</a> | ||||
|             <a href="iso-code.html">ISO Code</a> | ||||
|           </div> | ||||
|           <div> | ||||
|             <h2>Pricing</h2> | ||||
|             <a href="isp-socks5.html">Residential (socks5) proxies</a> | ||||
|             <a href="residential-proxies.html" | ||||
|               >Residential proxies - Traffic Plan</a | ||||
|             > | ||||
|             <a href="static-isp.html">Static ISP - Long time</a> | ||||
|             <a href="unlimit-proxies.html">Unlimited residential proxies</a> | ||||
|           </div> | ||||
|           <div> | ||||
|             <h2>Proxy Services</h2> | ||||
|             <a href="terms-service.html">Terms of Service</a> | ||||
|             <a href="privacy-policy.html">Privacy Policy</a> | ||||
|             <a href="refund-policy.html">Refund Policy</a> | ||||
|             <a href="anti-fraud.html">Anti-Money Laundering Policy</a> | ||||
|           </div> --> | ||||
|           <!-- <div> | ||||
|             <h2>Use Cases</h2> | ||||
|             <a href="price-monitoring.html">Price Monitoring</a> | ||||
|             <a href="advertising-verificat.html">Advertising Verificat</a> | ||||
|             <a href="brand-protection.html">Brand Protection</a> | ||||
|             <a href="market-research.html">Market Research</a> | ||||
|             <a href="travel-fare-summary.html">Travel Fare Summary</a> | ||||
|             <a href="serp-seo-proxies.html">SERP&SEO</a> | ||||
|           </div> --> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|     </footer> | ||||
|     <!-- <script> | ||||
|     var _hmt = _hmt || []; | ||||
|     (function() { | ||||
|       var hm = document.createElement("script"); | ||||
|       hm.src = "https://hm.baidu.com/hm.js?c4dddce310908c98ca8617f0056e59e9"; | ||||
|       var s = document.getElementsByTagName("script")[0];  | ||||
|       s.parentNode.insertBefore(hm, s); | ||||
|     })(); | ||||
| </script> --> | ||||
|  | ||||
|     <script data-cfasync="false" src="static/js/email-decode.min.js"></script> | ||||
|     <script> | ||||
|  | ||||
							
								
								
									
										203
									
								
								static/css/cli-traffic.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								static/css/cli-traffic.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,203 @@ | ||||
| /* CLI Traffic 页面样式 */ | ||||
| html, | ||||
| body, | ||||
| #app { | ||||
|   padding: 0; | ||||
|   margin: 0; | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   font-family: DMSans-Regular; | ||||
| } | ||||
|  | ||||
| .header { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
|   height: 60px; | ||||
|   background-color: #fff; | ||||
|   border-bottom: 1px solid #eee; | ||||
|   padding: 0 20px; | ||||
| } | ||||
|  | ||||
| .parentx-static { | ||||
|   overflow: hidden; | ||||
|   height: 500px; | ||||
|   position: relative; | ||||
| } | ||||
|  | ||||
| .logo { | ||||
|   font-weight: bold; | ||||
|   font-size: 20px; | ||||
|   padding: 15px 0px 15px 15px; | ||||
|   height: 100%; | ||||
|   cursor: pointer; | ||||
| } | ||||
|  | ||||
| .main-area { | ||||
|   display: flex; | ||||
|   height: calc(100vh - 60px); /* 减去 header 高度 */ | ||||
| } | ||||
|  | ||||
| .sidebar { | ||||
|   width: 200px; | ||||
|   background-color: #ffffff; | ||||
|   border-right: 1px solid #eee; | ||||
| } | ||||
|  | ||||
| .sidebar .vs-content-sidebar .vs-sidebar { | ||||
|   background-color: unset !important; | ||||
| } | ||||
|  | ||||
| .content { | ||||
|   flex: 1; | ||||
|   padding: 8px; | ||||
|   overflow: auto; | ||||
|   background: #eeeeee; | ||||
| } | ||||
|  | ||||
| .content-conter { | ||||
|   background-color: white; | ||||
|   padding: 16px; | ||||
|   height: 100%; | ||||
| } | ||||
|  | ||||
| /* .user-dropdown{ | ||||
|     color: #000; | ||||
| } */ | ||||
| .dropdown-menu { | ||||
|   width: 100px; | ||||
| } | ||||
|  | ||||
| .tablex { | ||||
|   width: 100%; | ||||
|   padding-top: 10px; | ||||
| } | ||||
|  | ||||
| .popup-content { | ||||
|   padding-bottom: 15px; | ||||
| } | ||||
|  | ||||
| .popup-bottom { | ||||
|   text-align: right; | ||||
|   padding-top: 10px; | ||||
| } | ||||
|  | ||||
| .qr-code { | ||||
|   height: 155px; | ||||
| } | ||||
|  | ||||
| .success-contianer { | ||||
|   height: 100%; | ||||
|   line-height: 155px; | ||||
| } | ||||
|  | ||||
| .success-contianer img { | ||||
|   height: 40px; | ||||
| } | ||||
|  | ||||
| .content-conter-header { | ||||
|   display: flex; | ||||
|   padding-bottom: 5px; | ||||
| } | ||||
|  | ||||
| /* .content-conter-header > *:not(:first-child) { | ||||
|   margin-left: 10px; | ||||
| } */ | ||||
|  | ||||
| .balance-btn[data-v-d585ebde] { | ||||
|   padding: 0 10px; | ||||
|   box-sizing: border-box; | ||||
|   max-width: max-content; | ||||
|   height: 30px; | ||||
|   line-height: 30px; | ||||
|   background: #ff916f33; | ||||
|   border-radius: 4px; | ||||
|   font-weight: 700; | ||||
|   font-size: 14px; | ||||
|   color: #ff916f; | ||||
|   margin-right: 28px; | ||||
|   cursor: pointer; | ||||
| } | ||||
|  | ||||
| .table-cell-center { | ||||
|   text-align: left; | ||||
| } | ||||
|  | ||||
| .btn-group { | ||||
|   display: flex; | ||||
|   gap: 10px; | ||||
|   place-items: end; | ||||
|   padding-left: 10px; | ||||
| } | ||||
|  | ||||
| .operat-btn { | ||||
|   margin-top: 10px; | ||||
| } | ||||
|  | ||||
| .form-item { | ||||
|   padding-top: 10px; | ||||
| } | ||||
|  | ||||
| .content-conter-container { | ||||
|   height: calc(100% - 45px); | ||||
|   overflow-y: auto; | ||||
| } | ||||
|  | ||||
| .content-conter-footer { | ||||
|   height: 45px; | ||||
| } | ||||
|  | ||||
| /* API管理样式 */ | ||||
| .api-management { | ||||
|   padding: 20px; | ||||
| } | ||||
|  | ||||
| .api-section h3 { | ||||
|   margin-bottom: 20px; | ||||
|   color: #333; | ||||
|   font-size: 18px; | ||||
|   font-weight: 600; | ||||
| } | ||||
|  | ||||
| .api-key-container { | ||||
|   background: #f8f9fa; | ||||
|   padding: 20px; | ||||
|   border-radius: 8px; | ||||
|   border: 1px solid #e9ecef; | ||||
| } | ||||
|  | ||||
| .api-key-item, .api-status-item { | ||||
|   margin-bottom: 20px; | ||||
| } | ||||
|  | ||||
| .api-key-item:last-child, .api-status-item:last-child { | ||||
|   margin-bottom: 0; | ||||
| } | ||||
|  | ||||
| .api-key-item label, .api-status-item label { | ||||
|   display: block; | ||||
|   margin-bottom: 8px; | ||||
|   font-weight: 500; | ||||
|   color: #495057; | ||||
| } | ||||
|  | ||||
| .api-key-display { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   gap: 10px; | ||||
| } | ||||
|  | ||||
| .api-key-display .vs-input { | ||||
|   flex: 1; | ||||
| } | ||||
|  | ||||
| .api-status-display { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   gap: 10px; | ||||
| } | ||||
|  | ||||
| .status-text { | ||||
|   font-weight: 500; | ||||
|   color: #495057; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user