2400 lines
		
	
	
		
			78 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			2400 lines
		
	
	
		
			78 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <!DOCTYPE html>
 | ||
| <html lang="en">
 | ||
|   <head>
 | ||
|     <meta charset="UTF-8" />
 | ||
|     <meta http-equiv="X-UA-Compatible" content="IE=edge" />
 | ||
|     <meta
 | ||
|       name="viewport"
 | ||
|       content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"
 | ||
|     />
 | ||
|     <link
 | ||
|       href="/static/images/favicon.ico"
 | ||
|       rel="shortcut icon"
 | ||
|       type="image/x-icon"
 | ||
|     />
 | ||
|     <link rel="dns-prefetch" href="https://www.googletagmanager.com" />
 | ||
|     <link rel="dns-prefetch" href="https://googleads.g.doubleclick.net" />
 | ||
| 
 | ||
|     <title>Proxy - The world's best and fastest residential proxies</title>
 | ||
|     <meta
 | ||
|       name="description"
 | ||
|       content="Proxy has the fastest residential IP, with 100 million residential IPs from 180+ countries."
 | ||
|     />
 | ||
|     <link
 | ||
|       rel="stylesheet"
 | ||
|       href="https://unpkg.com/vuesax@3.11.13/dist/vuesax.css"
 | ||
|     />
 | ||
|     <!-- 引入字体图标 -->
 | ||
|     <link
 | ||
|       rel="stylesheet"
 | ||
|       href="https://fonts.googleapis.com/css?family=Material+Icons"
 | ||
|     />
 | ||
|     <!-- 引入 Vue 2 -->
 | ||
|     <script src="https://unpkg.com/vue@2.7.16/dist/vue.js"></script>
 | ||
|     <!-- 引入 Vuesax JS -->
 | ||
|     <script src="https://unpkg.com/vuesax@3.11.13/dist/vuesax.umd.js"></script>
 | ||
|     <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>
 | ||
|   </head>
 | ||
|   <body>
 | ||
|     <div id="app">
 | ||
|       <div class="header">
 | ||
|         <div class="logo">
 | ||
|           <img
 | ||
|             style="height: 100%"
 | ||
|             src="static/picture/logo.png?v=20250726"
 | ||
|             @click="homeClick"
 | ||
|             alt=""
 | ||
|           />
 | ||
|         </div>
 | ||
|         <div style="display: flex; align-items: center; gap: 20px">
 | ||
|           <p data-v-d585ebde="" class="balance-btn" @click="handleRecharge">
 | ||
|             余额 :$ {{user.balance}}
 | ||
|           </p>
 | ||
|           <vs-select v-model="language" style="width: 100px">
 | ||
|             <vs-select-item value="zh" text="中文" />
 | ||
|             <vs-select-item value="en" text="English" />
 | ||
|           </vs-select>
 | ||
| 
 | ||
|           <vs-dropdown class="user-dropdown">
 | ||
|             <vs-button
 | ||
|               icon="account_circle"
 | ||
|               color="rgba(0,0,0,0.9)"
 | ||
|               type="flat"
 | ||
|             >
 | ||
|               {{ user.name }}
 | ||
|             </vs-button>
 | ||
|             <vs-dropdown-menu class="dropdown-menu">
 | ||
|               <!-- <vs-dropdown-item>{{user}}</vs-dropdown-item> -->
 | ||
|               <vs-dropdown-item @click="handleLogout">退出登录</vs-dropdown-item>
 | ||
|             </vs-dropdown-menu>
 | ||
|           </vs-dropdown>
 | ||
|         </div>
 | ||
|       </div>
 | ||
| 
 | ||
|       <!-- 主体区域 -->
 | ||
|       <div class="main-area">
 | ||
|         <!-- 左侧菜单 -->
 | ||
|         <div class="sidebar">
 | ||
|           <vs-sidebar
 | ||
|               static-position
 | ||
|               :hidden-background="true"
 | ||
|               default-index="2"
 | ||
|               color="primary"
 | ||
|               spacer
 | ||
|               v-model="active"
 | ||
|             >
 | ||
|           <vs-sidebar-group open title="代理管理">
 | ||
|             
 | ||
|               <vs-sidebar-item
 | ||
|                 index="1"
 | ||
|                 @click="handleActiveMenu(1)"
 | ||
|                 icon="settings"
 | ||
|                 >长效IP</vs-sidebar-item
 | ||
|               >
 | ||
|               <vs-sidebar-item
 | ||
|                 index="2"
 | ||
|                 @click="handleActiveMenu(2)"
 | ||
|                 icon="dashboard"
 | ||
|               >
 | ||
|                 动态IP
 | ||
|               </vs-sidebar-item>
 | ||
|           </vs-sidebar-group>
 | ||
|           <vs-sidebar-group open title="号码管理">
 | ||
|               <vs-sidebar-item
 | ||
|                 index="3"
 | ||
|                 @click="handleActiveMenu(3)"
 | ||
|                 icon="settings"
 | ||
|                 >短效号码</vs-sidebar-item
 | ||
|               >
 | ||
|               <vs-sidebar-item
 | ||
|                 index="4"
 | ||
|                 @click="handleActiveMenu(4)"
 | ||
|                 icon="dashboard"
 | ||
|               >
 | ||
|                 长效号码
 | ||
|               </vs-sidebar-item>
 | ||
|           </vs-sidebar-group>
 | ||
|           
 | ||
|         </div>
 | ||
| 
 | ||
|         <!-- 内容展示区 -->
 | ||
|         <div class="content">
 | ||
|           <div class="content-conter">
 | ||
|             <div class="content-conter-header" v-if="activeMenu === 1||activeMenu===2">
 | ||
|               <vs-select
 | ||
|                 autocomplete
 | ||
|                 v-model="query.area"
 | ||
|                 autocomplete
 | ||
|                 class="selectExample"
 | ||
|                 placeholder="area"
 | ||
|                 label="国家/地区"
 | ||
|               >
 | ||
|                 <vs-select-item
 | ||
|                   v-for="item in traffictList"
 | ||
|                   :key="'area'+item.code"
 | ||
|                   :value="item.code"
 | ||
|                   :text="item.name"
 | ||
|                   @click="handleAreaChange(item)"
 | ||
|                 ></vs-select-item>
 | ||
|               </vs-select>
 | ||
|               <vs-select
 | ||
|                 autocomplete
 | ||
|                 style="margin-left: 10px;"
 | ||
|                 v-model="query.state"
 | ||
|                 class="selectExample"
 | ||
|                 label="城市"
 | ||
|                 placeholder="city"
 | ||
|               >
 | ||
|                 <vs-select-item
 | ||
|                   v-for="(item,index) in cityList"
 | ||
|                   :key="'city'+index"
 | ||
|                   :value="item.state"
 | ||
|                   :text="item.state"
 | ||
|                 ></vs-select-item>
 | ||
|               </vs-select>
 | ||
|               <div class="btn-group">
 | ||
|                 <vs-button color="primary" size="small" @click="reloadList"
 | ||
|                 >查询</vs-button
 | ||
|               >
 | ||
|               <vs-button
 | ||
|                 color="primary"
 | ||
|                 type="border"
 | ||
|                 size="small"
 | ||
|                 @click="handleResetList"
 | ||
|               >
 | ||
|                 重置
 | ||
|               </vs-button>
 | ||
| 
 | ||
|               <vs-button
 | ||
|                 color="primary"
 | ||
|                 type="border"
 | ||
|                 size="small"
 | ||
|                 @click="handleGetProxy"
 | ||
|                 >获取代理</vs-button
 | ||
|               >
 | ||
|               
 | ||
|               <vs-button color="danger" type="filled" size="small" @click="handleClearProxy">清除数据</vs-button>
 | ||
|               </div>
 | ||
|               
 | ||
|             </div>
 | ||
|             
 | ||
|             <div class="content-conter-header" v-if="activeMenu === 3||activeMenu===4">
 | ||
|               <vs-select
 | ||
|                 autocomplete
 | ||
|                 v-model="smsQuery.serviceCode"
 | ||
|                 autocomplete
 | ||
|                 class="selectExample"
 | ||
|                 placeholder="服务"
 | ||
|                 label="服务"
 | ||
|               >
 | ||
|                 <vs-select-item
 | ||
|                   v-for="item in smsServices"
 | ||
|                   :key="'sms_service_'+item.code"
 | ||
|                   :value="item.code"
 | ||
|                   :text="item.name"
 | ||
|                   @click="handleSmsServiceChange(item)"
 | ||
|                 ></vs-select-item>
 | ||
|               </vs-select>
 | ||
| 
 | ||
|                <div class="btn-group">
 | ||
|                 <vs-button color="primary" size="small" @click="reloadSmsList"
 | ||
|                 >查询</vs-button
 | ||
|               >
 | ||
|               <vs-button
 | ||
|                 color="primary"
 | ||
|                 type="border"
 | ||
|                 size="small"
 | ||
|                 @click="handleResetSmsList"
 | ||
|               >
 | ||
|                 重置
 | ||
|               </vs-button>
 | ||
|               
 | ||
|               <vs-button
 | ||
|                 color="primary"
 | ||
|                 type="border"
 | ||
|                 size="small"
 | ||
|                 @click="handleShowGetSms"
 | ||
|                 >租赁号码</vs-button
 | ||
|               >
 | ||
|               <vs-button color="danger" type="filled" size="small" @click="handleClearSms">清除数据</vs-button>
 | ||
|               </div>
 | ||
|             </div>
 | ||
| 
 | ||
|             <vs-row v-if="activeMenu === 2" class="content-conter-container">
 | ||
|               <vs-table  class="tablex" :data="proxys">
 | ||
|                 <template slot="thead">
 | ||
|                   <vs-th> ID</vs-th>
 | ||
|                   <vs-th> 国家/地区 </vs-th>
 | ||
|                   <vs-th> 城市 </vs-th>
 | ||
|                   <vs-th> 状态 </vs-th>
 | ||
|                   <vs-th> 过期时间 </vs-th>
 | ||
|                   <vs-th> 操作 </vs-th>
 | ||
|                 </template>
 | ||
| 
 | ||
|                 <template slot-scope="{data}">
 | ||
|                   <vs-tr :key="indextr" v-for="(tr, indextr) in data">
 | ||
|                     <vs-td :data="data[indextr].id">
 | ||
|                       {{data[indextr].id}}
 | ||
|                     </vs-td>
 | ||
|                     <vs-td :data="data[indextr].area">
 | ||
|                       {{data[indextr].area}}
 | ||
|                     </vs-td>
 | ||
| 
 | ||
|                     <vs-td :data="data[indextr].state">
 | ||
|                       {{data[indextr].state}}
 | ||
|                     </vs-td>
 | ||
| 
 | ||
|                     <vs-td :data="data[indextr].status">
 | ||
|                       <vs-chip
 | ||
|                         v-if="data[indextr].status === 1"
 | ||
|                         transparent
 | ||
|                         color="success"
 | ||
|                       >
 | ||
|                         启用
 | ||
|                       </vs-chip>
 | ||
|                       <vs-chip v-else transparent color="warning">
 | ||
|                         禁用
 | ||
|                       </vs-chip>
 | ||
|                     </vs-td>
 | ||
|                     <vs-td :data="data[indextr].expired">
 | ||
|                         {{formatDate(data[indextr].expired)}}
 | ||
|                     </vs-td>
 | ||
|                     <!-- <vs-td :data="data[indextr].autoRenewal">
 | ||
|                         <vs-switch v-model="data[indextr].autoRenewal" @input="(val) => onSwitchChange(val, data[indextr],indextr)"/>
 | ||
|                     </vs-td> -->
 | ||
|                     <vs-td :data="data[indextr].id">
 | ||
|                       <vs-button
 | ||
|                         v-if="data[indextr].status === 1"
 | ||
|                         color="success"
 | ||
|                         class="operat-btn" 
 | ||
|                         type="flat"
 | ||
|                         size="small"
 | ||
|                         icon="qr_code"
 | ||
|                         @click="handleShowQrCode(data[indextr],2)"
 | ||
|                       ></vs-button>
 | ||
|                       <vs-button color="warning"class="operat-btn"  size="small" @click="handleDeleteProxy(data[indextr])">
 | ||
|                         删除
 | ||
|                       </vs-button>
 | ||
|                     </vs-td>
 | ||
|                   </vs-tr>
 | ||
|                 </template>
 | ||
|               </vs-table>
 | ||
|               <vs-pagination
 | ||
|                 class="content-conter-footer"
 | ||
|                 v-model="query.page"
 | ||
|                 :total="query.total"
 | ||
|                 :page-size="query.pageSize"
 | ||
|                 @change="getMyProxy"
 | ||
|               ></vs-pagination>
 | ||
|             </vs-row>
 | ||
| 
 | ||
|             <vs-row v-else-if="activeMenu===1" class="content-conter-container">
 | ||
|               <vs-table class="tablex" :data="proxys">
 | ||
|                 <template slot="thead">
 | ||
|                   <vs-th> 国家/地区 </vs-th>
 | ||
|                   <vs-th> 城市 </vs-th>
 | ||
|                   <vs-th> IP:端口 </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>
 | ||
|                 </template>
 | ||
| 
 | ||
|                 <template slot-scope="{data}">
 | ||
|                   <vs-tr :key="indextr" v-for="(tr, indextr) in data">
 | ||
|                     <vs-td :data="data[indextr].area">
 | ||
|                       {{data[indextr].area}}
 | ||
|                     </vs-td>
 | ||
| 
 | ||
|                     <vs-td :data="data[indextr].state">
 | ||
|                       {{data[indextr].state}}
 | ||
|                     </vs-td>
 | ||
|                     <vs-td :data="data[indextr].ip">
 | ||
|                       {{data[indextr].ip}}:{{data[indextr].port}}
 | ||
|                     </vs-td>
 | ||
|                     <vs-td :data="data[indextr].userName">
 | ||
|                       {{data[indextr].userName}}
 | ||
|                     </vs-td>
 | ||
| 
 | ||
|                     <vs-td :data="data[indextr].password">
 | ||
|                       {{data[indextr].password}}
 | ||
|                     </vs-td>
 | ||
| 
 | ||
|                     <vs-td :data="data[indextr].id">
 | ||
|                       <vs-chip
 | ||
|                         v-if="data[indextr].status === 1"
 | ||
|                         transparent
 | ||
|                         color="success"
 | ||
|                       >
 | ||
|                         启用
 | ||
|                       </vs-chip>
 | ||
|                       <vs-chip v-else transparent color="warning">
 | ||
|                         禁用
 | ||
|                       </vs-chip>
 | ||
|                     </vs-td>
 | ||
|                     <vs-td :data="data[indextr].expired">
 | ||
|                         {{formatDate(data[indextr].expired)}}
 | ||
|                     </vs-td>
 | ||
|                     <vs-td :data="data[indextr].autoRenewal">
 | ||
|                         <vs-switch v-if="data[indextr].status === 1" :value="data[indextr].autoRenewal" @input="(val) => onSwitchChange(val, data[indextr],indextr)"/>
 | ||
|                     </vs-td>
 | ||
|                     <vs-td :data="data[indextr].id">
 | ||
|                       <vs-button
 | ||
|                         v-if="data[indextr].status === 1"
 | ||
|                         color="success"
 | ||
|                         type="flat"
 | ||
|                         size="small"
 | ||
|                         icon="qr_code"
 | ||
|                         @click="handleShowQrCode(data[indextr],1)"
 | ||
|                       ></vs-button>
 | ||
|                       <vs-button v-if="data[indextr].status === 1"
 | ||
|                        color="rgb(187, 138, 200)" 
 | ||
|                        text-color="warning" 
 | ||
|                        size="small" 
 | ||
|                        @click="renewal(data[indextr])">续期</vs-button>
 | ||
|                       <vs-button color="warning" size="small" @click="handleDeleteProxy(data[indextr])">
 | ||
|                         删除
 | ||
|                       </vs-button>
 | ||
|                   </vs-tr>
 | ||
|                 </template>
 | ||
|               </vs-table>
 | ||
|               <vs-pagination
 | ||
|                 class="content-conter-footer"
 | ||
|                 v-model="query.page"
 | ||
|                 :total="query.total"
 | ||
|                 :page-size="query.pageSize"
 | ||
|                 @change="getMyProxy"
 | ||
|               ></vs-pagination>
 | ||
|             </vs-row>
 | ||
| 
 | ||
|             <!--短效号码-->
 | ||
|             <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> Phone </vs-th>
 | ||
|                   <vs-th> 验证码 </vs-th>
 | ||
|                   <vs-th> 状态 </vs-th>
 | ||
|                   <vs-th> 过期时间 </vs-th>
 | ||
|                   <vs-th> 操作 </vs-th>
 | ||
|                 </template>
 | ||
| 
 | ||
|                 <template slot-scope="{data}">
 | ||
|                   <vs-tr :key="indextr" v-for="(tr, indextr) in data">
 | ||
|                     <vs-td :data="data[indextr].service">
 | ||
|                       {{data[indextr].service}}
 | ||
|                     </vs-td>
 | ||
| 
 | ||
|                     <vs-td :data="data[indextr].phone">
 | ||
|                       {{data[indextr].phone}}
 | ||
|                     </vs-td>
 | ||
|                     <vs-td :data="data[indextr].code">
 | ||
|                       {{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>
 | ||
|                     </vs-td>
 | ||
|                     <vs-td :data="data[indextr].expireTime">
 | ||
|                        <countdown
 | ||
|                           :time="(new Date( data[indextr].expireTime)) - Date.now()"
 | ||
|                           v-if="data[indextr].expireTime &&new Date()<new Date(data[indextr].expireTime)&& data[indextr].status===1"
 | ||
|                         >
 | ||
|                           <template slot-scope="props">
 | ||
|                            {{ Math.floor((new Date( data[indextr].expireTime) - Date.now()) / 1000) || 0 }} 秒
 | ||
|                           </template>
 | ||
|                        </countdown>
 | ||
|                     </vs-td>
 | ||
|                     
 | ||
|                     <vs-td :data="data[indextr].id">
 | ||
|                       <vs-button color="primary" size="small" 
 | ||
|                       v-if="data[indextr].actived === 1 && data[indextr].status === 1 &&new Date()<new Date(data[indextr].expireTime)" 
 | ||
|                       @click="handleCancelSms(data[indextr])">取消</vs-button>
 | ||
|                       <vs-button color="warning" size="small" @click="handleDeleteSms(data[indextr])">
 | ||
|                         删除
 | ||
|                       </vs-button>
 | ||
|                   </vs-tr>
 | ||
|                 </template>
 | ||
|               </vs-table>
 | ||
|               <vs-pagination
 | ||
|                 class="content-conter-footer"
 | ||
|                 v-model="smsQuery.page"
 | ||
|                 :total="smsQuery.total"
 | ||
|                 :page-size="smsQuery.pageSize"
 | ||
|                 @change="getSmsList"
 | ||
|               ></vs-pagination>
 | ||
|             </vs-row>
 | ||
| 
 | ||
|             <!--长效-->
 | ||
|             <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> 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>
 | ||
|                 </template>
 | ||
| 
 | ||
|                 <template slot-scope="{data}">
 | ||
|                   <vs-tr :key="indextr" v-for="(tr, indextr) in data">
 | ||
|                     <vs-td :data="data[indextr].service">
 | ||
|                       {{data[indextr].service}}
 | ||
|                     </vs-td>
 | ||
| 
 | ||
|                     <vs-td :data="data[indextr].phone">
 | ||
|                       {{data[indextr].phone}}
 | ||
|                     </vs-td>
 | ||
|                     <vs-td :data="data[indextr].period">
 | ||
|                       {{data[indextr].period}}
 | ||
|                     </vs-td>
 | ||
|                     <vs-td :data="data[indextr].code">
 | ||
|                       {{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>
 | ||
|                     </vs-td>
 | ||
| 
 | ||
|                     <vs-td :data="data[indextr].expireTime">
 | ||
|                         {{formatDate(data[indextr].expireTime)}}
 | ||
|                     </vs-td>
 | ||
|                     <vs-td :data="data[indextr].autoRenewal">
 | ||
|                         <vs-switch :value="data[indextr].autoRenewal===1"  @input="(val) => handleSmsRenew(val, data[indextr],indextr)"/>
 | ||
|                     </vs-td>
 | ||
|                     <vs-td :data="data[indextr].id">
 | ||
|                       <vs-button color="primary" size="small" 
 | ||
|                       v-if="data[indextr].actived === 1 && data[indextr].status === 1 &&new Date()<new Date(data[indextr].expireTime)" 
 | ||
|                       @click="handleCancelSms(data[indextr])">取消</vs-button>
 | ||
|                       
 | ||
|                       <vs-button
 | ||
|                         v-if="data[indextr].actived ===2 && data[indextr].status!==1 && new Date()<new Date(data[indextr].expireTime)"
 | ||
|                         color="primary"
 | ||
|                         size="small"
 | ||
|                         @click="handleWeakUp(data[indextr],1)"
 | ||
|                       >唤醒并获取</vs-button>
 | ||
|                       <vs-button color="warning" size="small" @click="handleDeleteSms(data[indextr])">
 | ||
|                         删除
 | ||
|                       </vs-button>
 | ||
|                   </vs-tr>
 | ||
|                 </template>
 | ||
|               </vs-table>
 | ||
|               <vs-pagination
 | ||
|                 class="content-conter-footer"
 | ||
|                 v-model="smsQuery.page"
 | ||
|                 :total="smsQuery.total"
 | ||
|                 :page-size="smsQuery.pageSize"
 | ||
|                 @change="getSmsList"
 | ||
|               ></vs-pagination>
 | ||
| 
 | ||
|             </vs-row>
 | ||
| 
 | ||
|           </div>
 | ||
|         </div>
 | ||
|       </div>
 | ||
| 
 | ||
|       <vs-popup
 | ||
|         title="提取长效IP"
 | ||
|         :active.sync="showUseProxy"
 | ||
|         :loading="loading"
 | ||
|       >
 | ||
|         <div style="text-align: right">
 | ||
|           <div style="    display: flex;align-items: center;scrollbar-gutter: 20px;column-gap: 20px;justify-content: right;">
 | ||
|             <span>价格U: {{this.price}}</span> <vs-button olor="primary" type="border" size="small" @click="getIpList"
 | ||
|             >刷新</vs-button
 | ||
|           >
 | ||
|           </div>
 | ||
|           <vs-table :data="useList" class="tablex">
 | ||
|             <template slot="thead">
 | ||
|               <vs-th class="table-cell-center"> IP </vs-th>
 | ||
|               <vs-th class="table-cell-center"> 国家 </vs-th>
 | ||
|               <vs-th class="table-cell-center"> 城市 </vs-th>
 | ||
|               <vs-th class="table-cell-center"> 操作 </vs-th>
 | ||
|             </template>
 | ||
|             <template slot-scope="{data}">
 | ||
|               <vs-tr :key="indextr" v-for="(tr, indextr) in data">
 | ||
|                 <vs-td :data="data[indextr].ip" class="table-cell-center">
 | ||
|                   {{data[indextr].ip}}
 | ||
|                 </vs-td>
 | ||
| 
 | ||
|                 <vs-td :data="data[indextr].country" class="table-cell-center">
 | ||
|                   {{data[indextr].country}}
 | ||
|                 </vs-td>
 | ||
| 
 | ||
|                 <vs-td :data="data[indextr].city" class="table-cell-center">
 | ||
|                   {{data[indextr].city}}
 | ||
|                 </vs-td>
 | ||
| 
 | ||
|                 <vs-td :data="data[indextr].id" class="table-cell-center">
 | ||
|                   <vs-button
 | ||
|                     color="primary"
 | ||
|                     size="small"
 | ||
|                     @click="handleUseProxy(data[indextr])"
 | ||
|                     >提取</vs-button
 | ||
|                   >
 | ||
|                 </vs-td>
 | ||
|               </vs-tr>
 | ||
|             </template>
 | ||
|           </vs-table>
 | ||
|         </div>
 | ||
|       </vs-popup>
 | ||
| 
 | ||
|       <vs-popup class="holamundo" title="获取代理" :active.sync="showGetProxy">
 | ||
|         <div>
 | ||
|           <vs-select
 | ||
|             autocomplete
 | ||
|             v-model="proxyForm.area"
 | ||
|             autocomplete
 | ||
|             class="selectExample"
 | ||
|             placeholder="area"
 | ||
|             label="国家/地区"
 | ||
|           >
 | ||
|             <vs-select-item
 | ||
|               v-for="item in traffictList"
 | ||
|               :key="'detail_area'+item.code"
 | ||
|               :value="item.code"
 | ||
|               :text="item.name"
 | ||
|             ></vs-select-item>
 | ||
|           </vs-select>
 | ||
|         </div>
 | ||
|         <div style="padding: top 10px">
 | ||
|           <vs-select
 | ||
|             autocomplete
 | ||
|             v-model="proxyForm.state"
 | ||
|             class="selectExample"
 | ||
|             label="城市"
 | ||
|             placeholder="city"
 | ||
|           >
 | ||
|             <vs-select-item
 | ||
|               v-for="(item,index) in detailCityList"
 | ||
|               :key="'detail_city'+index"
 | ||
|               :value="item.state"
 | ||
|               :text="item.state"
 | ||
|             ></vs-select-item>
 | ||
|           </vs-select>
 | ||
|         </div>
 | ||
|         <div style="padding: top 10px">
 | ||
|           <vs-select
 | ||
|             autocomplete
 | ||
|             v-model="proxyForm.port"
 | ||
|             class="selectExample"
 | ||
|             label="端口"
 | ||
|             placeholder="port"
 | ||
|           >
 | ||
|             <vs-select-item
 | ||
|               v-for="(item,index) in trafficServer"
 | ||
|               :key="'detail_port'+index"
 | ||
|               :value="item.hostname"
 | ||
|               :text="item.hostname"
 | ||
|             ></vs-select-item>
 | ||
|           </vs-select>
 | ||
|         </div>
 | ||
| 
 | ||
|         <div class="form-item" style="text-align: right;">
 | ||
|           价格U: {{this.price}}
 | ||
|         </div>
 | ||
|         <div class="form-item" style="text-align: right">
 | ||
|           <vs-button size="small" @click="handleGetTrafficProxy"
 | ||
|             >提取</vs-button
 | ||
|           >
 | ||
|         </div>
 | ||
|       </vs-popup>
 | ||
| 
 | ||
|       <vs-popup title="余额充值" :active.sync="showRecharge" :loading="loading">
 | ||
|         <div class="popup-content" v-if="step===1">
 | ||
|           充值金额:<vs-input
 | ||
|             class="inputx"
 | ||
|             placeholder="请输入充值金额(U)"
 | ||
|             v-model.number="rechargeForm.amount"
 | ||
|           />
 | ||
|         </div>
 | ||
|         <div v-else style="text-align: center">
 | ||
|           <div class="qr-code">
 | ||
|             <div v-show="!rechargeForm.finished" id="qrcode"></div>
 | ||
|             <div v-show="rechargeForm.finished" class="success-contianer">
 | ||
|               <img src="static/image/pay-sucess.png" alt="" />
 | ||
|             </div>
 | ||
|           </div>
 | ||
|           <div class="wallet-info">
 | ||
|             <p>主链:<strong>{{ rechargeData.blockChain }}</strong></p>
 | ||
|             <p>钱包地址: <strong>{{ rechargeData.receiveAddress }}</strong></p>
 | ||
|             <p>支付金额: <strong>{{ rechargeData.amount }} USDT</strong></p>
 | ||
|             <div>
 | ||
|               <countdown
 | ||
|                 :time="(rechargeData.expireUnix * 1000) - Date.now()"
 | ||
|                 v-if="rechargeData.expireUnix && (rechargeData.expireUnix * 1000) > Date.now() && !rechargeForm.finished"
 | ||
|               >
 | ||
|                 <template slot-scope="props">
 | ||
|                   倒计时:
 | ||
|                   <span v-if="props.minutes>0||props.hours>0||props.days>0"
 | ||
|                     >{{ props.minutes }} 分</span
 | ||
|                   >
 | ||
|                   <span
 | ||
|                     v-if="props.seconds>0||props.minutes>0||props.hours>0||props.days>0"
 | ||
|                     >{{ props.seconds }} 秒</span
 | ||
|                   >
 | ||
|                   <span
 | ||
|                     style="color: red"
 | ||
|                     v-if="props.days==0&&props.hours==0&&props.minutes==0&&props.seconds==0"
 | ||
|                     >订单已过期,请勿支付!</span
 | ||
|                   >
 | ||
|                 </template>
 | ||
|               </countdown>
 | ||
|               <div v-else-if="rechargeForm.finished" style="color: green">
 | ||
|                 充值成功,请勿重复支付!
 | ||
|               </div>
 | ||
|             </div>
 | ||
|           </div>
 | ||
|         </div>
 | ||
|         <div class="popup-bottom">
 | ||
|           <vs-button
 | ||
|             color="primary"
 | ||
|             type="border"
 | ||
|             size="small"
 | ||
|             @click="handleCancelRecharge"
 | ||
|           >
 | ||
|             取消
 | ||
|           </vs-button>
 | ||
|           <vs-button
 | ||
|             v-if="step===1"
 | ||
|             ref="nextBtn"
 | ||
|             color="primary"
 | ||
|             type="filled"
 | ||
|             size="small"
 | ||
|             class="vs-con-loading__container"
 | ||
|             :disabled="rechargeForm.amount<=0"
 | ||
|             @click="handleNextStep()"
 | ||
|           >
 | ||
|             下一步
 | ||
|           </vs-button>
 | ||
|           <vs-button
 | ||
|             v-else
 | ||
|             color="primary"
 | ||
|             size="small"
 | ||
|             @click="handlePrevStep()"
 | ||
|           >
 | ||
|             上一步
 | ||
|           </vs-button>
 | ||
|         </div>
 | ||
|       </vs-popup>
 | ||
| 
 | ||
|       <vs-popup
 | ||
|         title="扫码使用"
 | ||
|         style="text-align: center"
 | ||
|         :active.sync="showQrCode"
 | ||
|       >
 | ||
|         <div id="proxy-qrcode"></div>
 | ||
|       </vs-popup>
 | ||
| 
 | ||
|       <!--获取号码-->
 | ||
|       <vs-popup title="租赁号码" :active.sync="showGetSms" :loading="loading">
 | ||
|         <div>
 | ||
|           <vs-select
 | ||
|             autocomplete
 | ||
|             v-model="smsForm.serviceCode"
 | ||
|             autocomplete
 | ||
|             class="selectExample"
 | ||
|             placeholder="请选择服务"
 | ||
|             label="服务"
 | ||
|           >
 | ||
|             <vs-select-item
 | ||
|               v-for="item in smsServices"
 | ||
|               :key="'form_service'+item.code"
 | ||
|               :value="item.code"
 | ||
|               :text="item.name"
 | ||
|             ></vs-select-item>
 | ||
|           </vs-select>
 | ||
|         </div>
 | ||
|         <div class="form-item" v-if="smsForm.type===1">
 | ||
|           <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}}
 | ||
|         </div>
 | ||
|         <div class="form-item" style="text-align: right">
 | ||
|           <vs-button size="small" @click="getNumber"
 | ||
|             >确认租赁</vs-button>
 | ||
|         </div>
 | ||
|       </vs-popup>
 | ||
| 
 | ||
|     </div>
 | ||
|   </body>
 | ||
| 
 | ||
|   <script>
 | ||
|     new Vue({
 | ||
|       el: "#app",
 | ||
|       components: {
 | ||
|         countdown: window.VueCountdown,
 | ||
|       },
 | ||
|       data() {
 | ||
|         return {
 | ||
|           active: false,
 | ||
|           activeMenu: 2,
 | ||
|           showGetProxy: false,
 | ||
|           showUseProxy: false,
 | ||
|           showQrCode: false,
 | ||
|           proxyQrCode: "",
 | ||
|           proxyForm: {},
 | ||
|           showRecharge: false,
 | ||
|           rechargeForm: {
 | ||
|             amount: 10,
 | ||
|             finished: false,
 | ||
|           },
 | ||
|           token: "",
 | ||
|           rechargeData: {},
 | ||
|           loading: false,
 | ||
|           step: 1,
 | ||
|           language: "zh",
 | ||
|           languageList: [
 | ||
|             { value: "en", text: "English" },
 | ||
|             { value: "zh", text: "简体中文" },
 | ||
|           ],
 | ||
|           user: {
 | ||
|             name: "",
 | ||
|           },
 | ||
|           query: {
 | ||
|             area: "",
 | ||
|             state: "",
 | ||
|             page: 1,
 | ||
|             pageSize: 10,
 | ||
|             total: 0,
 | ||
|           },
 | ||
|           proxys: [],
 | ||
|           checkOrderTimer: null,
 | ||
|           traffictList: [],
 | ||
|           cityList: [],
 | ||
|           detailCityList: [],
 | ||
|           trafficServer: [],
 | ||
|           //可提取ip列表
 | ||
|           useList: [],
 | ||
|           //短信服务列表
 | ||
|           smsServices:[],
 | ||
|           //短信query
 | ||
|           smsQuery:{
 | ||
|             page:1,
 | ||
|             pageSize:10,
 | ||
|             total:0,
 | ||
|             serviceCode:"",
 | ||
|           },
 | ||
|           //短信列表
 | ||
|           smsList:[],
 | ||
|           showGetSms:false,
 | ||
|           smsForm:{},
 | ||
|           checkSmsCodeTimer:null,
 | ||
|           smsPrice:undefined,
 | ||
|           //Ip单价
 | ||
|           price:undefined,
 | ||
|         };
 | ||
|       },
 | ||
|       mounted() {
 | ||
|         let token = localStorage.getItem("session");
 | ||
|         if (token) {
 | ||
|           this.token = token;
 | ||
|           this.getUserInfo(token);
 | ||
|         } else {
 | ||
|           if (this.language === "zh") {
 | ||
|             location.href = "sign-in1.html";
 | ||
|           } else {
 | ||
|             location.href = "sign-in.html";
 | ||
|           }
 | ||
|         }
 | ||
| 
 | ||
|         this.resetSmsForm();
 | ||
| 
 | ||
|         this.getMyProxy();
 | ||
|         this.getTraffictList();
 | ||
|         this.getTrafficServer();
 | ||
|         this.getSmsServices();
 | ||
| 
 | ||
|         //定时器
 | ||
|         this.createCheckSmsCodeTimer();
 | ||
|       },
 | ||
|       watch: {
 | ||
|         showGetProxy(val, oldVal) {
 | ||
|             if (!val && val !== oldVal) {
 | ||
|                 this.proxyForm.area = "";
 | ||
|                 this.proxyForm.state = "";
 | ||
|                 this.proxyForm.port = "";
 | ||
|             }
 | ||
|         },
 | ||
|         showRecharge(val, oldVal) {
 | ||
|           console.log("showRecharge", val, oldVal);
 | ||
|           if (!val && val !== oldVal) {
 | ||
|             this.cleanCheckOrderTimer(this.checkOrderTimer);
 | ||
|             this.resetRechargeForm();
 | ||
|             this.rechargeData = {};
 | ||
|             this.step = 1;
 | ||
| 
 | ||
|             this.getMyBalance();
 | ||
|           }
 | ||
|         },
 | ||
|         "query.area"(val, oldVal) {
 | ||
|           console.log("xxxxxxx", val, oldVal);
 | ||
|           if (val && val !== oldVal) {
 | ||
|             let area = this.traffictList.find((item) => item.code === val);
 | ||
|             if (area) {
 | ||
|               this.cityList = area.states;
 | ||
|             }
 | ||
|           }
 | ||
|         },
 | ||
|         "proxyForm.area"(val, oldVal) {
 | ||
|           if (val && val !== oldVal) {
 | ||
|             let area = this.traffictList.find((item) => item.code === val);
 | ||
|             if (area) {
 | ||
|               this.detailCityList = area.states;
 | ||
|             }
 | ||
|           }
 | ||
|         },
 | ||
|       },
 | ||
|       methods: {
 | ||
|         getSmsServices(){
 | ||
|           fetch(requestApi + "/sms-services/list", {
 | ||
|             headers:{
 | ||
|               Authorization: `Bearer ${this.token}`,
 | ||
|               "Content-Type": "application/json",
 | ||
|               Accept: "application/json",
 | ||
|             }
 | ||
|           })
 | ||
|           .then((res) => res.json())
 | ||
|           .then((response) => {
 | ||
|             console.log('getSmsServices',response);
 | ||
|               if (response.code === 200) {
 | ||
|                 this.smsServices = response.data;
 | ||
|               }
 | ||
|             })
 | ||
|           .catch((error) => {
 | ||
|               console.log("获取服务错误:",error);
 | ||
|             });
 | ||
|         },
 | ||
|         getPrice(){
 | ||
|           this.price=undefined;
 | ||
|           let configKey;
 | ||
| 
 | ||
|           switch(this.activeMenu){
 | ||
|             case 1:
 | ||
|               configKey="ip_deduction_standard";
 | ||
|               break;
 | ||
|             case 2:
 | ||
|               configKey="deduction_standard";
 | ||
|               break;
 | ||
|             default:
 | ||
|               return;
 | ||
|           }
 | ||
| 
 | ||
|           this.getPriceByKey(configKey)
 | ||
|          .then((price) => {
 | ||
|             this.price=price;
 | ||
|           })
 | ||
|          .catch((error) => {
 | ||
|             console.error("Failed to fetch price:", error); 
 | ||
|           });
 | ||
|         },
 | ||
|         //根据key获取价格
 | ||
|         async getPriceByKey(key){
 | ||
|           try {
 | ||
|             const response = await fetch(`${requestApi}/configKey/${key}`, {
 | ||
|               headers: {
 | ||
|                 Authorization: `Bearer ${this.token}`,
 | ||
|                 "Content-Type": "application/json",
 | ||
|                 Accept: "application/json",
 | ||
|               },
 | ||
|             });
 | ||
| 
 | ||
|             if (!response.ok) {
 | ||
|               // 处理非 2xx 状态码
 | ||
|               const errorData = await response.json();
 | ||
|               throw new Error(`HTTP error! status: ${response.status}, message: ${errorData.message || 'Unknown error'}`);
 | ||
|             }
 | ||
| 
 | ||
|             const data = await response.json();
 | ||
| 
 | ||
|             if (data.code === 200) {
 | ||
|               return Number(data.data.configValue);
 | ||
|             } else {
 | ||
|               // 处理接口返回的业务错误码
 | ||
|               throw new Error(`API error: ${data.message || 'Unknown API error'}`);
 | ||
|             }
 | ||
|           } catch (error) {
 | ||
|             console.error("Failed to fetch price:", error); // 使用 console.error 更好地记录错误
 | ||
|             // 你可以选择在这里重新抛出错误,或者返回一个默认值/undefined
 | ||
|             throw error; // 重新抛出错误,让调用者处理
 | ||
|           }
 | ||
|         },
 | ||
|         showLoading(ref) {
 | ||
|           //   ref.$el.setAttribute("disabled", true);
 | ||
|           this.$vs.loading();
 | ||
|         },
 | ||
|         getOrderStatus(orderNo) {
 | ||
|           fetch(
 | ||
|             requestApi + "/member-recharge/order-status?orderNo=" + orderNo,
 | ||
|             {
 | ||
|               headers: {
 | ||
|                 Authorization: `Bearer ${this.token}`,
 | ||
| 
 | ||
|                 "Content-Type": "application/json",
 | ||
|                 Accept: "application/json",
 | ||
|               },
 | ||
|             }
 | ||
|           )
 | ||
|             .then((res) => res.json())
 | ||
|             .then((response) => {
 | ||
|               if (response.code === 200 && response.data === 2) {
 | ||
|                 //订单已完成
 | ||
|                 this.rechargeForm.finished = true;
 | ||
|                 this.cleanCheckOrderTimer();
 | ||
|               }
 | ||
|             });
 | ||
|         },
 | ||
|         hiddenLoading(ref) {
 | ||
|           //   ref.$el.removeAttribute("disabled");
 | ||
|           this.$vs.loading.close();
 | ||
|         },
 | ||
|         homeClick() {
 | ||
|           window.location.href = "/";
 | ||
|         },
 | ||
|         handleGetProxy() {
 | ||
|           this.getPrice();
 | ||
|           if (this.activeMenu === 1) {
 | ||
|             this.showUseProxy = true;
 | ||
|             this.getIpList();
 | ||
|           } else {
 | ||
|             this.showGetProxy = true;
 | ||
|           }
 | ||
|         },
 | ||
|         resetRechargeForm() {
 | ||
|           this.rechargeForm = {
 | ||
|             amount: 10,
 | ||
|             finished: false,
 | ||
|           };
 | ||
|         },
 | ||
|         handleCancelRecharge() {
 | ||
|           this.showRecharge = false;
 | ||
|           this.resetRechargeForm();
 | ||
|         },
 | ||
|         handleRecharge() {
 | ||
|           this.step = 1;
 | ||
|           this.showRecharge = true;
 | ||
|         },
 | ||
|         handleNextStep() {
 | ||
|           this.loading = true;
 | ||
|           this.showLoading(this.$refs.nextBtn);
 | ||
| 
 | ||
|           fetch(requestApi + "/member-recharge/recharge", {
 | ||
|             method: "POST",
 | ||
|             headers: {
 | ||
|               "Content-Type": "application/json",
 | ||
|               Authorization: `Bearer ${this.token}`,
 | ||
|             },
 | ||
|             body: JSON.stringify(this.rechargeForm),
 | ||
|           })
 | ||
|             .then((res) => res.json())
 | ||
|             .then((response) => {
 | ||
|               console.log(response);
 | ||
|               if (response.code === 200) {
 | ||
|                 this.rechargeData = response.data;
 | ||
|                 this.step = 2;
 | ||
| 
 | ||
|                 this.$nextTick(() => {
 | ||
|                   this.generateQRCode(
 | ||
|                     this.rechargeData.receiveAddress,
 | ||
|                     "qrcode"
 | ||
|                   );
 | ||
|                 });
 | ||
|                 this.createCheckOrderTimer();
 | ||
|               } else {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "Position top-center",
 | ||
|                   text: response.msg,
 | ||
|                   color: "danger",
 | ||
|                   position: "top-center",
 | ||
|                 });
 | ||
|               }
 | ||
|             })
 | ||
|             .finally(() => {
 | ||
|               this.hiddenLoading();
 | ||
|             });
 | ||
|         },
 | ||
|         handlePrevStep() {
 | ||
|           this.step = 1;
 | ||
|         },
 | ||
|         getUserInfo(token) {
 | ||
|           let that = this;
 | ||
| 
 | ||
|           fetch(requestApi + "/getinfo", {
 | ||
|             headers: {
 | ||
|               Authorization: `Bearer ${token}`,
 | ||
| 
 | ||
|               "Content-Type": "application/json",
 | ||
|               Accept: "application/json",
 | ||
|             },
 | ||
|           })
 | ||
|             .then((res) => res.json())
 | ||
|             .then((response) => {
 | ||
|               console.log(response);
 | ||
|               console.log("code", response.code);
 | ||
|               if (response.code === 200) {
 | ||
|                 that.user = response.data;
 | ||
|               } else {
 | ||
|                 localStorage.removeItem("session");
 | ||
| 
 | ||
|                 if (this.language === "zh") {
 | ||
|                   location.href = "sign-in1.html";
 | ||
|                 } else {
 | ||
|                   location.href = "sign-in.html";
 | ||
|                 }
 | ||
|               }
 | ||
|             });
 | ||
|         },
 | ||
|         //生成二维码
 | ||
|         generateQRCode(qrString, elId) {
 | ||
|           const text = qrString.trim(); // 获取输入框内容并去除首尾空格
 | ||
|           const qrcodeContainer = document.getElementById(elId);
 | ||
| 
 | ||
|           if (text) {
 | ||
|             // 1. 清空容器内的所有内容,包括旧的二维码或提示信息
 | ||
|             qrcodeContainer.innerHTML = "";
 | ||
|             this.showPlaceholder = false;
 | ||
|             this.qrError = null;
 | ||
| 
 | ||
|             // 2. 创建一个新的 canvas 元素
 | ||
|             const canvasElement = document.createElement("canvas");
 | ||
|             // 可以给 canvas 设置一个 ID 或者类名,如果需要后续引用或样式控制
 | ||
|             // canvasElement.id = 'qrCanvas';
 | ||
|             qrcodeContainer.appendChild(canvasElement); // 将 canvas 添加到容器中
 | ||
| 
 | ||
|             // 3. 使用 QRCode.toCanvas 在新创建的 canvas 上生成二维码
 | ||
|             QRCode.toCanvas(
 | ||
|               canvasElement,
 | ||
|               text,
 | ||
|               {
 | ||
|                 // 将 canvasElement 传递给 toCanvas
 | ||
|                 width: 150, // 设置二维码宽度
 | ||
|                 color: {
 | ||
|                   dark: "#000000", // 二维码颜色
 | ||
|                   light: "#ffffff", // 背景颜色
 | ||
|                 },
 | ||
|               },
 | ||
|               (error) => {
 | ||
|                 if (error) {
 | ||
|                   qrcodeContainer.innerHTML = `<p class="text-red-500">获取地址失败请刷新</p>`; // 重新显示错误信息
 | ||
|                 } else {
 | ||
|                   console.log("二维码生成成功!");
 | ||
|                 }
 | ||
|               }
 | ||
|             );
 | ||
|           } else {
 | ||
|             // 如果输入为空,给出提示
 | ||
|             qrcodeContainer.innerHTML = ""; // 清空可能存在的旧二维码
 | ||
|           }
 | ||
|         },
 | ||
|         //定时检查订单状态
 | ||
|         createCheckOrderTimer() {
 | ||
|           if (this.checkOrderTimer) {
 | ||
|             clearInterval(this.checkOrderTimer);
 | ||
|           } 
 | ||
|           
 | ||
|             console.log("createCheckOrderTimer")
 | ||
|             this.checkOrderTimer = setInterval(() => {
 | ||
|               this.getOrderStatus(this.rechargeData.orderNo);
 | ||
|             }, 10000);
 | ||
|         },
 | ||
|         getMyBalance(){
 | ||
|             fetch(requestApi + "/member-balance/balance", {
 | ||
|               headers: {
 | ||
|                 Authorization: `Bearer ${this.token}`,
 | ||
|                 "Content-Type": "application/json",
 | ||
|                 Accept: "application/json",
 | ||
|               },
 | ||
|             })
 | ||
|              .then((res) => res.json())
 | ||
|              .then((response) => {
 | ||
|                 if (response.code === 200) {
 | ||
|                   this.user.balance=response.data;
 | ||
|                 }
 | ||
|               })
 | ||
|              .catch((error) => {
 | ||
|                 console.log(error);
 | ||
|               });
 | ||
|         },
 | ||
|         formatDate(expired) {
 | ||
|             const date = new Date(expired);
 | ||
| 
 | ||
|             // 自动格式化为用户本地时间格式
 | ||
|             const readable = date.toLocaleString();
 | ||
|             return readable;
 | ||
|         },
 | ||
|         cleanCheckOrderTimer() {
 | ||
|           if (this.checkOrderTimer) {
 | ||
|             clearInterval(this.checkOrderTimer);
 | ||
|           }
 | ||
|         },
 | ||
|         getTraffictList() {
 | ||
|           fetch(requestApi + "/member-proxy/traffic", {
 | ||
|             headers: {
 | ||
|               Authorization: `Bearer ${this.token}`,
 | ||
| 
 | ||
|               "Content-Type": "application/json",
 | ||
|               Accept: "application/json",
 | ||
|             },
 | ||
|           })
 | ||
|             .then((res) => res.json())
 | ||
|             .then((response) => {
 | ||
|               if (response.code === 200) {
 | ||
|                 this.traffictList = response.data;
 | ||
|               }
 | ||
|             })
 | ||
|             .catch((error) => {
 | ||
|               console.log(error);
 | ||
|             });
 | ||
|         },
 | ||
|         handleAreaChange(item) {
 | ||
|           console.log("xxxxxxx", item);
 | ||
|           if (item) {
 | ||
|             this.cityList = item.states;
 | ||
|           }
 | ||
|         },
 | ||
|         getTrafficServer() {
 | ||
|           fetch(requestApi + "/member-proxy/traffic-server", {
 | ||
|             headers: {
 | ||
|               Authorization: `Bearer ${this.token}`,
 | ||
| 
 | ||
|               "Content-Type": "application/json",
 | ||
|               Accept: "application/json",
 | ||
|             },
 | ||
|           })
 | ||
|             .then((res) => res.json())
 | ||
|             .then((response) => {
 | ||
|               if (response.code === 200) {
 | ||
|                 console.log(response.data);
 | ||
|                 this.trafficServer = response.data;
 | ||
|               }
 | ||
|             })
 | ||
|             .catch((error) => {
 | ||
|               console.log(error);
 | ||
|             });
 | ||
|         },
 | ||
|         //生成流量账号
 | ||
|         handleGetTrafficProxy() {
 | ||
|           if (!this.proxyForm.area) {
 | ||
|             this.$vs.notify({
 | ||
|               title: "提示",
 | ||
|               text: "请先选择国家/区域",
 | ||
|               color: "danger",
 | ||
|               position: "top-right",
 | ||
|             });
 | ||
| 
 | ||
|             return;
 | ||
|           }
 | ||
| 
 | ||
|           if (!this.proxyForm.port) {
 | ||
|             this.$vs.notify({
 | ||
|               title: "提示",
 | ||
|               text: "请选择主机:端口",
 | ||
|               color: "danger",
 | ||
|               position: "top-right",
 | ||
|             });
 | ||
| 
 | ||
|             return;
 | ||
|           }
 | ||
| 
 | ||
|           this.showLoading();
 | ||
| 
 | ||
|           fetch(requestApi + "/member-proxy/generate-proxy", {
 | ||
|             method: "POST",
 | ||
|             headers: {
 | ||
|               "Content-Type": "application/json",
 | ||
|               Authorization: `Bearer ${this.token}`,
 | ||
|             },
 | ||
|             body: JSON.stringify(this.proxyForm),
 | ||
|           })
 | ||
|             .then((res) => res.json())
 | ||
|             .then((response) => {
 | ||
|               if (response.code === 200) {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "提示",
 | ||
|                   color: "success",
 | ||
|                   text: "获取成功",
 | ||
|                   position: "top-right",
 | ||
|                 });
 | ||
| 
 | ||
|                 if(response.data){
 | ||
|                     this.user.balance=response.data;
 | ||
|                 }
 | ||
|                 this.showGetProxy = false;
 | ||
|                 this.getMyProxy();
 | ||
|               } else {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "提示",
 | ||
|                   color: "danger",
 | ||
|                   text: response.msg,
 | ||
|                   position: "top-right",
 | ||
|                 });
 | ||
|               }
 | ||
|             })
 | ||
|             .catch((error) => {
 | ||
|               console.log(error);
 | ||
|             })
 | ||
|             .finally(() => {
 | ||
|               this.hiddenLoading();
 | ||
|             });
 | ||
|         },
 | ||
|         handleActiveMenu(index) {
 | ||
|           this.activeMenu = index;
 | ||
|           
 | ||
|           switch(index){
 | ||
|             case 1:
 | ||
|             case 2:
 | ||
|               this.query.page=1;
 | ||
|               this.query.total=1;
 | ||
|               this.reloadList();
 | ||
|               break;
 | ||
|             case 3:
 | ||
|               this.smsQuery.page=1;
 | ||
|               this.smsQuery.total=1;
 | ||
|               this.smsForm.type=0;
 | ||
|               this.smsForm.serviceCode="";
 | ||
|               this.smsForm.period=undefined;
 | ||
|               this.reloadSmsList();
 | ||
|               this.handleGetPrice(this.smsForm.type);
 | ||
|               break;
 | ||
|             case 4:
 | ||
|               this.smsQuery.page=1;
 | ||
|               this.smsQuery.total=1;
 | ||
|               this.smsForm.type=1;
 | ||
|               this.smsForm.serviceCode="";
 | ||
|               this.smsForm.period=undefined;
 | ||
|               this.reloadSmsList();
 | ||
|               this.handleGetPrice(this.smsForm.type);
 | ||
|               break;
 | ||
|           }
 | ||
|         },
 | ||
|         reloadList() {
 | ||
|           this.query.pageIndex = 1;
 | ||
|           this.getMyProxy();
 | ||
|         },
 | ||
|         getMyProxy() {
 | ||
|           console.log("触发getMyProxy");
 | ||
|           this.showLoading();
 | ||
|           let query = {
 | ||
|             type: this.activeMenu,
 | ||
|             pageIndex: this.query.page,
 | ||
|             pageSize: this.query.pageSize,
 | ||
|             area: this.query.area,
 | ||
|             state: this.query.state,
 | ||
|           };
 | ||
| 
 | ||
|           const queryString = new URLSearchParams(query).toString();
 | ||
|           const url = `${requestApi}/member-proxy/my-proxy?${queryString}`;
 | ||
|           fetch(url, {
 | ||
|             headers: {
 | ||
|               Authorization: `Bearer ${this.token}`,
 | ||
| 
 | ||
|               "Content-Type": "application/json",
 | ||
|               Accept: "application/json",
 | ||
|             },
 | ||
|           })
 | ||
|             .then((res) => res.json())
 | ||
|             .then((response) => {
 | ||
|               if (response.code === 200) {
 | ||
|                 this.proxys = response.data.list;
 | ||
|                 let total = Math.ceil(
 | ||
|                   response.data.count / this.query.pageSize
 | ||
|                 );
 | ||
| 
 | ||
|                 if(this.query.total!==total){
 | ||
|                 this.query.total = total;
 | ||
|                 }
 | ||
|               }
 | ||
|             })
 | ||
|             .catch((error) => {
 | ||
|               console.log(error);
 | ||
|             })
 | ||
|             .finally(() => {
 | ||
|               this.hiddenLoading();
 | ||
|             });
 | ||
|         },
 | ||
|         //提取长效ip
 | ||
|         handleUseProxy(row) {
 | ||
|           this.showLoading();
 | ||
|           let data = JSON.stringify({
 | ||
|             id: row.id,
 | ||
|             lang: this.language,
 | ||
|             code: row.code,
 | ||
|             city: row.city,
 | ||
|           });
 | ||
| 
 | ||
|           fetch(requestApi + "/member-proxy/use", {
 | ||
|             headers: {
 | ||
|               Authorization: `Bearer ${this.token}`,
 | ||
|               "Content-Type": "application/json",
 | ||
|               Accept: "application/json",
 | ||
|             },
 | ||
|             method: "POST",
 | ||
|             body: data,
 | ||
|           })
 | ||
|             .then((res) => res.json())
 | ||
|             .then((response) => {
 | ||
|               if (response.code === 200) {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "提示",
 | ||
|                   color: "success",
 | ||
|                   text: "提取成功",
 | ||
|                   position: "top-right",
 | ||
|                 });
 | ||
| 
 | ||
|                 if(response.data){
 | ||
|                     this.user.balance=response.data;
 | ||
|                 }
 | ||
| 
 | ||
|                 this.showUseProxy = false;
 | ||
|                 this.getMyProxy();
 | ||
|               } else {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "提示",
 | ||
|                   color: "danger",
 | ||
|                   text: response.msg,
 | ||
|                   position: "top-right",
 | ||
|                 });
 | ||
|               }
 | ||
|             })
 | ||
|             .catch((error) => {
 | ||
|               console.log(error);
 | ||
|             })
 | ||
|             .finally(() => {
 | ||
|               this.hiddenLoading();
 | ||
|             });
 | ||
|         },
 | ||
|         //获取可提取ip列表
 | ||
|         getIpList() {
 | ||
|           this.showLoading();
 | ||
| 
 | ||
|           fetch(requestApi + "/member-proxy/ip-list?lang=" + this.language, {
 | ||
|             headers: {
 | ||
|               Authorization: `Bearer ${this.token}`,
 | ||
|               "Content-Type": "application/json",
 | ||
|               Accept: "application/json",
 | ||
|             },
 | ||
|           })
 | ||
|             .then((res) => res.json())
 | ||
|             .then((response) => {
 | ||
|               if (response.code === 200) {
 | ||
|                 console.log(response);
 | ||
|                 this.useList = response.data;
 | ||
|               }
 | ||
|             })
 | ||
|             .catch((error) => {
 | ||
|               console.log(error);
 | ||
|             })
 | ||
|             .finally(() => {
 | ||
|               this.hiddenLoading();
 | ||
|             });
 | ||
|         },
 | ||
|         handleResetList() {
 | ||
|           this.query.area = "";
 | ||
|           this.query.state = "";
 | ||
|           this.query.page = 1;
 | ||
| 
 | ||
|           this.getMyProxy();
 | ||
|         },
 | ||
|         //展示二维码
 | ||
|         handleShowQrCode(row, type) {
 | ||
|           this.showQrCode = true;
 | ||
|           let baseData = "";
 | ||
| 
 | ||
|           if (type === 1) {
 | ||
|              baseData = `${row.userName}:${row.password}@${row.ip}:${row.port}`;
 | ||
|           } else {
 | ||
|              baseData = `${row.userName}:${row.password}@${row.port}`;
 | ||
|           }
 | ||
| 
 | ||
|           if(baseData){
 | ||
|             console.log(baseData);
 | ||
|             let code = `socks://${btoa(baseData)}`;
 | ||
|             console.log(code);
 | ||
|             this.generateQRCode(code, "proxy-qrcode");
 | ||
|           }
 | ||
|         },
 | ||
|         //重置流量账号
 | ||
|         handleResetGenerateProxy(row) {
 | ||
|           this.showLoading();
 | ||
|           let data = JSON.stringify({
 | ||
|             id: row.id,
 | ||
|           });
 | ||
| 
 | ||
|           fetch(requestApi + "/member-proxy/reset-generate-proxy", {
 | ||
|             headers: {
 | ||
|               Authorization: `Bearer ${this.token}`,
 | ||
|               "Content-Type": "application/json",
 | ||
|               Accept: "application/json",
 | ||
|             },
 | ||
|             method: "POST",
 | ||
|             body: data,
 | ||
|           })
 | ||
|             .then((res) => res.json())
 | ||
|             .then((response) => {
 | ||
|               if (response.code === 200) {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "提示",
 | ||
|                   color: "success",
 | ||
|                   text: "重置成功",
 | ||
|                   position: "top-right",
 | ||
|                 });
 | ||
| 
 | ||
|                 this.showGetProxy = false;
 | ||
|                 this.getMyProxy();
 | ||
|               } else {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "提示",
 | ||
|                   color: "danger",
 | ||
|                   text: response.msg,
 | ||
|                   position: "top-right",
 | ||
|                 });
 | ||
|               }
 | ||
|             })
 | ||
|             .catch((error) => {
 | ||
|               console.log(error);
 | ||
|             })
 | ||
|             .finally(() => {
 | ||
|               this.hiddenLoading();
 | ||
|             });
 | ||
|         },
 | ||
|         handleLogout(){
 | ||
|             this.showLoading();
 | ||
|             fetch(requestApi + "/logout", {
 | ||
|               headers: {
 | ||
|                 Authorization: `Bearer ${this.token}`,
 | ||
|                 "Content-Type": "application/json",
 | ||
|                 Accept: "application/json",
 | ||
|               },
 | ||
|               method: "POST",})
 | ||
|              .then((res) => res.json())
 | ||
|              .then((response) => {
 | ||
|                 if (response.code === 200) {
 | ||
|                   localStorage.removeItem("session");
 | ||
| 
 | ||
|                   if (this.language === "zh") {
 | ||
|                     location.href = "sign-in1.html";
 | ||
|                   } else {
 | ||
|                     location.href = "sign-in.html";
 | ||
|                   }
 | ||
|                 } else {
 | ||
|                   this.$vs.notify({
 | ||
|                     title: "提示",
 | ||
|                     color: "danger",
 | ||
|                     text: response.msg,
 | ||
|                     position: "top-right",
 | ||
|                   });
 | ||
|                 }
 | ||
|               })
 | ||
|              .catch((error) => {
 | ||
|                 console.log(error);
 | ||
|               })
 | ||
|              .finally(() => {
 | ||
|                 this.hiddenLoading();
 | ||
|              });
 | ||
|         },
 | ||
|         // 切换状态
 | ||
|         async onSwitchChange(val,row,index){
 | ||
|             let text = val?'启用':'取消';
 | ||
| 
 | ||
|             this.$vs.dialog({
 | ||
|                 color:"primary",
 | ||
|                 title:"提示",
 | ||
|                 text: `确定${text}自动续期吗?`,
 | ||
|                 acceptText:"确定",
 | ||
|                 accept:() => this.doSwitchProxyAutoRenewal(row,val)
 | ||
|             });
 | ||
|         },
 | ||
|         // 代理自动续费修改changeStatus
 | ||
|         doSwitchProxyAutoRenewal(row,val){
 | ||
|             this.showLoading();
 | ||
|             let data = JSON.stringify({
 | ||
|                 proxyId: row.id,
 | ||
|                 autoRenewal: val,
 | ||
|             });
 | ||
|             console.log('data:',data);
 | ||
|             fetch(requestApi + "/member-proxy/change-auto-renewal", {
 | ||
|               headers: {
 | ||
|                 Authorization: `Bearer ${this.token}`,
 | ||
|                 "Content-Type": "application/json",
 | ||
|                 Accept: "application/json",
 | ||
|               },
 | ||
|               method: "POST",
 | ||
|               body: data,
 | ||
|             })
 | ||
|              .then((res) => res.json())
 | ||
|              .then((response) => {
 | ||
|                 if (response.code === 200) {
 | ||
|                   this.$vs.notify({
 | ||
|                     title: "提示",
 | ||
|                     color: "success",
 | ||
|                     text: "修改成功",
 | ||
|                     position: "top-right",
 | ||
|                   });
 | ||
| 
 | ||
|                   this.getMyProxy();
 | ||
|                 } else {
 | ||
|                   this.$vs.notify({
 | ||
|                     title: "提示",
 | ||
|                     color: "danger",
 | ||
|                     text: response.msg,
 | ||
|                     position: "top-right",
 | ||
|                   });
 | ||
|                 }
 | ||
|               })
 | ||
|              .catch((error) => {
 | ||
|                 console.log(error);
 | ||
|               })
 | ||
|              .finally(() => {
 | ||
|                 this.hiddenLoading();
 | ||
|               });
 | ||
|         },
 | ||
|         async renewal(data){
 | ||
|           let price=await this.getPriceByKey("ip_renew_deduction_standard");
 | ||
|           
 | ||
|           if(!price){
 | ||
|             return this.$vs.$notify({
 | ||
|                title: "提示",
 | ||
|                color: "danger",
 | ||
|                text: "获取续期价格失败,请刷新重试",
 | ||
|                position: "top-right",
 | ||
|             })
 | ||
| 
 | ||
|             return
 | ||
|           }
 | ||
| 
 | ||
|           let tip=`续期费用:${price}U/30天`;
 | ||
| 
 | ||
|              this.$vs.dialog({
 | ||
|                 color:"primary",
 | ||
|                 title: `代理续期提示`,
 | ||
|                 text: `${tip},确定续期30天吗?`,
 | ||
|                 acceptText:"确定",
 | ||
|                 accept:() => this.acceptAlert(data.id)
 | ||
|             });
 | ||
|         },
 | ||
|         acceptAlert(id){
 | ||
|             this.showLoading();
 | ||
|             let data=JSON.stringify({
 | ||
|                 proxyId: id,
 | ||
|             });
 | ||
| 
 | ||
|             fetch(requestApi + "/member-proxy/user-renewal", {
 | ||
|               headers: {
 | ||
|                 Authorization: `Bearer ${this.token}`,
 | ||
|                 "Content-Type": "application/json",
 | ||
|                 Accept: "application/json",
 | ||
|               },
 | ||
|               method: "POST",
 | ||
|               body: data,
 | ||
|             })
 | ||
|              .then((res) => res.json())
 | ||
|              .then((response) => {
 | ||
|                 if (response.code === 200) {
 | ||
|                   this.$vs.notify({
 | ||
|                     title: "提示",
 | ||
|                     color: "success",
 | ||
|                     text: "续期成功",
 | ||
|                     position: "top-right",
 | ||
|                   });
 | ||
| 
 | ||
|                   if(response.data){
 | ||
|                     this.user.balance=response.data;
 | ||
|                   }
 | ||
|                   this.getMyProxy();
 | ||
|                 } else {
 | ||
|                   this.$vs.notify({
 | ||
|                     title: "提示",
 | ||
|                     color: "danger",
 | ||
|                     text: response.msg,
 | ||
|                     position: "top-right",
 | ||
|                   });
 | ||
|                 }
 | ||
|               })
 | ||
|              .catch((error) => {
 | ||
|                 console.log(error);
 | ||
|               })
 | ||
|              .finally(() => {
 | ||
|                 this.hiddenLoading();
 | ||
|               });
 | ||
|         },
 | ||
|         //sms service切换
 | ||
|         handleSmsServiceChange(item){
 | ||
|           this.smsQuery.serviceCode=item.code;
 | ||
|         },
 | ||
|         //重置sms列表
 | ||
|         handleResetSmsList(){
 | ||
|           this.smsQuery.serviceCode="";
 | ||
|           this.smsQuery.page=1;
 | ||
| 
 | ||
|           this.getSmsList();
 | ||
|         },
 | ||
|         //查询sms
 | ||
|         reloadSmsList(){
 | ||
|           this.smsQuery.page=1;
 | ||
|           this.getSmsList();
 | ||
|         },
 | ||
|         //获取sms列表
 | ||
|         getSmsList(){
 | ||
|           this.showLoading();
 | ||
|           let type=0;
 | ||
| 
 | ||
|           if(this.activeMenu===4){
 | ||
|             type=1
 | ||
|           }
 | ||
| 
 | ||
|           let query = {
 | ||
|             type:type,
 | ||
|             pageIndex: this.smsQuery.page,
 | ||
|             pageSize: this.smsQuery.pageSize,
 | ||
|             serviceCode: this.smsQuery.serviceCode,
 | ||
|           };
 | ||
| 
 | ||
|           const queryString = new URLSearchParams(query).toString();
 | ||
|           fetch(requestApi + "/sms-phone/list?" + queryString, {
 | ||
|             headers: {
 | ||
|               Authorization: `Bearer ${this.token}`,
 | ||
|               "Content-Type": "application/json",
 | ||
|               Accept: "application/json",
 | ||
|             }})
 | ||
|             .then((res) => res.json())
 | ||
|             .then((response) => {
 | ||
|               if (response.code === 200) {
 | ||
|                 console.log(response);
 | ||
|                 this.smsList = response.data.list;
 | ||
|                 let total = Math.ceil(
 | ||
|                   response.data.count / this.smsQuery.pageSize
 | ||
|                 );
 | ||
| 
 | ||
|                 this.smsQuery.total = total;
 | ||
|               }
 | ||
|             })
 | ||
|             .catch((error) => {
 | ||
|               console.log(error);
 | ||
|             })
 | ||
|             .finally(() => {
 | ||
|               this.hiddenLoading();
 | ||
|             });
 | ||
|         },
 | ||
|         //显示租赁弹框
 | ||
|         handleShowGetSms(){
 | ||
|           this.smsForm.period=undefined;
 | ||
|           this.smsForm.serviceCode="";
 | ||
|           this.showGetSms = true;
 | ||
|         },
 | ||
|         resetSmsForm(){
 | ||
|           let type =0;
 | ||
| 
 | ||
|           if (this.activeMenu === 4) {
 | ||
|             type = 1;
 | ||
|           }
 | ||
|           this.smsForm={
 | ||
|             serviceCode: "",
 | ||
|             period:undefined,
 | ||
|             type:type,
 | ||
|           }
 | ||
|         },
 | ||
|         //获取号码
 | ||
|         getNumber(){
 | ||
|           if(!this.smsForm.serviceCode){
 | ||
|              this.$vs.notify({
 | ||
|                     title: "提示",
 | ||
|                     color: "danger",
 | ||
|                     text: "请选择服务",
 | ||
|                     position: "top-right",
 | ||
|                   });
 | ||
|             return;
 | ||
|           }
 | ||
| 
 | ||
|           if (this.smsForm.type===1&& (this.smsForm.period===undefined||this.smsForm.period<=0)){
 | ||
|              this.$vs.notify({
 | ||
|                     title: "提示",
 | ||
|                     color: "danger",
 | ||
|                     text: "请填写时间",
 | ||
|                     position: "top-right",
 | ||
|                   });
 | ||
|             return;
 | ||
|           }
 | ||
| 
 | ||
|           let data = JSON.stringify({
 | ||
|             serviceCode: this.smsForm.serviceCode,
 | ||
|             type: this.smsForm.type,
 | ||
|             period: Number(this.smsForm.period),
 | ||
|           })
 | ||
| 
 | ||
|           this.showLoading();
 | ||
| 
 | ||
|           fetch(requestApi + "/sms-phone/getNumber", {
 | ||
|             headers: {
 | ||
|               Authorization: `Bearer ${this.token}`,
 | ||
|               "Content-Type": "application/json",
 | ||
|               Accept: "application/json",
 | ||
|             },
 | ||
|             method: "POST",
 | ||
|             body: data,
 | ||
|           })
 | ||
|            .then((res) => res.json())
 | ||
|            .then((response) => {
 | ||
|               if (response.code === 200) {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "提示",
 | ||
|                   color: "success",
 | ||
|                   text: "获取成功",
 | ||
|                   position: "top-right",
 | ||
|                 });
 | ||
| 
 | ||
|                 this.showGetSms = false;
 | ||
|                 this.getSmsList();
 | ||
|                 this.user.balance=response.data;
 | ||
|               } else {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "提示",
 | ||
|                   color: "danger",
 | ||
|                   text: response.msg,
 | ||
|                   position: "top-right",
 | ||
|                 });
 | ||
|               }
 | ||
|             })
 | ||
|            .catch((error) => {
 | ||
|               console.log(error);
 | ||
|             })
 | ||
|            .finally(() => {
 | ||
|               this.hiddenLoading();
 | ||
|             });
 | ||
|         },
 | ||
|         //查看接受中的验证码
 | ||
|         getCodeByActivationIds(){
 | ||
|           if(this.smsList&&this.smsList.length>0){
 | ||
|             let activationIds=this.smsList.filter(x=>x.status===1).map(x=>x.activationId)
 | ||
| 
 | ||
|             if(activationIds.length<=0){
 | ||
|               return;
 | ||
|             }
 | ||
| 
 | ||
|             const params = new URLSearchParams();
 | ||
|             activationIds.forEach(id => {
 | ||
|               params.append('activationIds', id); // Appends 'activationIds=123', 'activationIds=456', etc.
 | ||
|             });
 | ||
| 
 | ||
|             fetch(`${requestApi}/sms-phone/getCodeByActivationId?${params}`, {
 | ||
|               headers: {
 | ||
|                 Authorization: `Bearer ${this.token}`,
 | ||
|                 "Content-Type": "application/json",
 | ||
|                 Accept: "application/json",
 | ||
|               },
 | ||
|             })
 | ||
|             .then((res) => res.json())
 | ||
|             .then((response) => {
 | ||
|                 if (response.code === 200) {
 | ||
|                   const codesMap = new Map();
 | ||
|                   response.data.forEach(item => {
 | ||
|                       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
 | ||
|                     }})
 | ||
|                 }
 | ||
|             })
 | ||
|             .catch((error) => {
 | ||
|                 console.log(error);
 | ||
|             });
 | ||
|           }
 | ||
|         },
 | ||
|         //定时检查等待中的验证码
 | ||
|         createCheckSmsCodeTimer() {
 | ||
|           if (this.checkSmsCodeTimer) {
 | ||
|             clearInterval(this.checkSmsCodeTimer);
 | ||
|           } else {
 | ||
|             this.checkSmsCodeTimer = setInterval(() => {
 | ||
|               this.getCodeByActivationIds(this.rechargeData.orderNo);
 | ||
|             }, 10000);
 | ||
|           }
 | ||
|         },
 | ||
|         handleWeakUp(row,index){
 | ||
|           if(row.status===1){
 | ||
|               this.$vs.notify({
 | ||
|                     title: "提示",
 | ||
|                     color: "danger",
 | ||
|                     text: "无需重复唤醒",
 | ||
|                     position: "top-right",
 | ||
|                   });
 | ||
|             return;
 | ||
|           }
 | ||
| 
 | ||
|           this.showLoading();
 | ||
|           let data = JSON.stringify({
 | ||
|             activationId: row.activationId,
 | ||
|           });
 | ||
| 
 | ||
|           fetch(requestApi + "/sms-phone/weakUp", {
 | ||
|             headers: {
 | ||
|               Authorization: `Bearer ${this.token}`,
 | ||
|               "Content-Type": "application/json",
 | ||
|               Accept: "application/json",
 | ||
|             },
 | ||
|             method: "POST",
 | ||
|             body: data,
 | ||
|           })
 | ||
|            .then((res) => res.json())
 | ||
|            .then((response) => {
 | ||
|               if (response.code === 200) {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "提示",
 | ||
|                   color: "success",
 | ||
|                   text: "唤醒成功",
 | ||
|                   position: "top-right",
 | ||
|                 });
 | ||
| 
 | ||
|                 this.getSmsList();
 | ||
|               } else {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "提示",
 | ||
|                   color: "danger",
 | ||
|                   text: response.msg,
 | ||
|                   position: "top-right",
 | ||
|                 });
 | ||
|               }
 | ||
|             })
 | ||
|            .catch((error) => {
 | ||
|               console.log(error);
 | ||
|             })
 | ||
|            .finally(() => {
 | ||
|               this.hiddenLoading();
 | ||
|            });
 | ||
|         },
 | ||
|         handleDeleteSms(row){
 | ||
|            this.$vs.dialog({
 | ||
|                 color:"primary",
 | ||
|                 title: `提示`,
 | ||
|                 text: `确定要删除[${row.phone}]?`,
 | ||
|                 acceptText:"确定",
 | ||
|                 accept:() => this.deleteSms(row)
 | ||
|             });
 | ||
|         },
 | ||
|         deleteSms(row){
 | ||
|           this.showLoading();
 | ||
| 
 | ||
|           let data = JSON.stringify({
 | ||
|             id:row.id});
 | ||
|             fetch(requestApi + "/sms-phone/my-number", {
 | ||
|               headers: {
 | ||
|                 Authorization: `Bearer ${this.token}`,
 | ||
|                 "Content-Type": "application/json",
 | ||
|                 Accept: "application/json",
 | ||
|               },
 | ||
|               method: "DELETE",
 | ||
|               body: data,
 | ||
|             })
 | ||
|              .then((res) => res.json())
 | ||
|              .then((response) => {
 | ||
|                 if (response.code === 200) {
 | ||
|                   this.$vs.notify({
 | ||
|                     title: "提示",
 | ||
|                     color: "success",
 | ||
|                     text: "删除成功",
 | ||
|                     position: "top-right",
 | ||
|                   });
 | ||
| 
 | ||
|                   this.getSmsList();
 | ||
|                 } else {
 | ||
|                   this.$vs.notify({
 | ||
|                     title: "提示",
 | ||
|                     color: "danger",
 | ||
|                     text: response.msg,
 | ||
|                     position: "top-right",
 | ||
|                   });
 | ||
|                 }
 | ||
|              })
 | ||
|              .catch((error) => {
 | ||
|                 console.log(error);
 | ||
|              })
 | ||
|              .finally(() => {
 | ||
|                 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;
 | ||
|               }
 | ||
|            })
 | ||
|            .catch((error) => {
 | ||
|               console.log(error);
 | ||
|             });
 | ||
|         },
 | ||
|         //取消sms
 | ||
|         handleCancelSms(row){
 | ||
|            this.$vs.dialog({
 | ||
|                 color:"primary",
 | ||
|                 title: `提示`,
 | ||
|                 text: `确定要取消[${row.phone}]的短信服务吗?`,
 | ||
|                 acceptText:"确定",
 | ||
|                 accept:() => this.cancelSms(row)
 | ||
|             });
 | ||
|         },
 | ||
|         cancelSms(row){
 | ||
|           this.showLoading();
 | ||
| 
 | ||
|           let data = JSON.stringify({
 | ||
|             id:row.id});
 | ||
|             fetch(requestApi + "/sms-phone/cancel", {
 | ||
|               headers: {
 | ||
|                 Authorization: `Bearer ${this.token}`,
 | ||
|                 "Content-Type": "application/json",
 | ||
|                 Accept: "application/json",
 | ||
|               },
 | ||
|               method: "PUT",
 | ||
|               body: data,
 | ||
|             })
 | ||
|              .then((res) => res.json())
 | ||
|              .then((response) => {
 | ||
|                 if (response.code === 200) {
 | ||
|                   this.$vs.notify({
 | ||
|                     title: "提示",
 | ||
|                     color: "success",
 | ||
|                     text: "取消成功",
 | ||
|                     position: "top-right",
 | ||
|                   });
 | ||
| 
 | ||
|                   this.getMyBalance();
 | ||
|                   this.getSmsList();
 | ||
|                 } else {
 | ||
|                   this.$vs.notify({
 | ||
|                     title: "提示",
 | ||
|                     color: "danger",
 | ||
|                     text: response.msg,
 | ||
|                     position: "top-right",
 | ||
|                   });
 | ||
|                 }
 | ||
|              })
 | ||
|              .catch((error) => {
 | ||
|                 console.log(error);
 | ||
|              })
 | ||
|              .finally(() => {
 | ||
|                 this.hiddenLoading();
 | ||
|              });
 | ||
|         },
 | ||
|         //长效号码续期状态修改
 | ||
|        async handleSmsRenew(val,row,index){
 | ||
|           const newState=val?1:2;
 | ||
|           const originalState = row.autoRenewal;
 | ||
|           const title=val?`确定要对[${row.phone}]的短信服务续期吗?`:"确认取消自动续费吗?";
 | ||
|           this.$vs.dialog({
 | ||
|                 color:"primary",
 | ||
|                 title: `提示`,
 | ||
|                 text: title,
 | ||
|                 acceptText:"确定",
 | ||
|                 accept: ()=>this.doSmsRenew(row,originalState,newState,index)
 | ||
|             });
 | ||
|         },
 | ||
|         handleSmsRenewClose(){
 | ||
|           console.log("close");
 | ||
|         },
 | ||
|         //修改长效号码续期状态
 | ||
|         doSmsRenew(row,originalState,newState,index){
 | ||
|           this.showLoading();
 | ||
| 
 | ||
|           let data = JSON.stringify({
 | ||
|             id:row.id,
 | ||
|             autoRenew:newState
 | ||
|          });
 | ||
| 
 | ||
|           fetch(requestApi + "/sms-phone/auto-renewal", {
 | ||
|             headers: {
 | ||
|               Authorization: `Bearer ${this.token}`,
 | ||
|               "Content-Type": "application/json",
 | ||
|               Accept: "application/json",
 | ||
|             },
 | ||
|             method: "PUT",
 | ||
|             body: data,
 | ||
|           })
 | ||
|            .then((res) => res.json())
 | ||
|            .then((response) => {
 | ||
|               if (response.code === 200) {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "提示",
 | ||
|                   color: "success",
 | ||
|                   text: "修改成功",
 | ||
|                   position: "top-right",
 | ||
|                 });
 | ||
| 
 | ||
|                 this.getMyBalance();
 | ||
|                 this.$set(this.smsList[index], 'autoRenewal', newState); 
 | ||
|                 // this.getSmsList();
 | ||
|               } else {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "提示",
 | ||
|                   color: "danger",
 | ||
|                   text: response.msg,
 | ||
|                   position: "top-right",
 | ||
|                 });
 | ||
|               }
 | ||
|            })
 | ||
|            .catch((error) => {
 | ||
|               console.log(error);
 | ||
|            })
 | ||
|            .finally(() => {
 | ||
|               this.hiddenLoading();
 | ||
|            });
 | ||
|         },
 | ||
|         handleDeleteProxy(row){
 | ||
|           this.$vs.dialog({
 | ||
|                 color:"primary",
 | ||
|                 title: `提示`,
 | ||
|                 text:"确认删除吗?",
 | ||
|                 acceptText:"确定",
 | ||
|                 accept:() => this.doDeleteProxy(row)
 | ||
|             });
 | ||
|         },
 | ||
|         //删除代理
 | ||
|         doDeleteProxy(row){
 | ||
|           this.showLoading();
 | ||
| 
 | ||
|           let data = JSON.stringify({
 | ||
|             id:row.id});
 | ||
|             fetch(requestApi + "/member-proxy/my", {
 | ||
|               headers: {
 | ||
|                 Authorization: `Bearer ${this.token}`,
 | ||
|                 "Content-Type": "application/json",
 | ||
|                 Accept: "application/json",
 | ||
|               },
 | ||
|               method: "DELETE",
 | ||
|               body: data,
 | ||
|             })
 | ||
|              .then((res) => res.json())
 | ||
|              .then((response) => {
 | ||
|                 if (response.code === 200) {
 | ||
|                   this.$vs.notify({
 | ||
|                     title: "提示",
 | ||
|                     color: "success",
 | ||
|                     text: "删除成功",
 | ||
|                     position: "top-right",
 | ||
|                   });
 | ||
| 
 | ||
|                   this.getMyProxy();
 | ||
|                 } else {
 | ||
|                   this.$vs.notify({
 | ||
|                     title: "提示",
 | ||
|                     color: "danger",
 | ||
|                     text: response.msg,
 | ||
|                     position: "top-right",
 | ||
|                   });
 | ||
|                 }
 | ||
|              })
 | ||
|              .catch((error) => {
 | ||
|                 console.log(error);
 | ||
|              })
 | ||
|              .finally(() => {
 | ||
|                 this.hiddenLoading();
 | ||
|              })
 | ||
|         },
 | ||
|         //清理代理
 | ||
|         handleClearProxy(){
 | ||
|           let type,typeName;
 | ||
|           switch(this.activeMenu){
 | ||
|             case 1:
 | ||
|               type=1;
 | ||
|               typeName="长效ip";
 | ||
|               break;
 | ||
|             case 2:
 | ||
|               type=2;
 | ||
|               typeName="短效ip";
 | ||
|               break;
 | ||
|             default:
 | ||
|                 break;
 | ||
|           }
 | ||
| 
 | ||
|           this.$vs.dialog({
 | ||
|             color:"primary",
 | ||
|             title:"提示",
 | ||
|             text:`确认清理${typeName}数据吗?`,
 | ||
|             acceptText:"确定",
 | ||
|             accept:()=>this.doCleanProxy(type)
 | ||
|           })
 | ||
|         },
 | ||
|         doCleanProxy(type){
 | ||
|           this.showLoading();
 | ||
|           let data = JSON.stringify({
 | ||
|             type:type});
 | ||
| 
 | ||
|           fetch(requestApi + "/member-proxy/clean", {
 | ||
|             headers: {
 | ||
|               Authorization: `Bearer ${this.token}`,
 | ||
|               "Content-Type": "application/json",
 | ||
|               Accept: "application/json",
 | ||
|             },
 | ||
|             method: "DELETE",
 | ||
|             body: data,
 | ||
|           })
 | ||
|            .then((res) => res.json())
 | ||
|            .then((response) => {
 | ||
|               if (response.code === 200) {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "提示",
 | ||
|                   color: "success",
 | ||
|                   text: "清理成功",
 | ||
|                   position: "top-right",
 | ||
|                 });
 | ||
| 
 | ||
|                 this.getMyProxy();
 | ||
|               } else {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "提示",
 | ||
|                   color: "danger",
 | ||
|                   text: response.msg,
 | ||
|                   position: "top-right",
 | ||
|                 });
 | ||
|               }
 | ||
|             })
 | ||
|            .catch((error) => {
 | ||
|               console.log(error);
 | ||
|             })
 | ||
|            .finally(() => {
 | ||
|               this.hiddenLoading();
 | ||
|             });
 | ||
|         },
 | ||
|         //清理短信
 | ||
|         handleClearSms(){
 | ||
|           let type,typeName;
 | ||
|           switch(this.activeMenu){
 | ||
|             case 4:
 | ||
|               type=1;
 | ||
|               typeName="长效号码";
 | ||
|               break;
 | ||
|             case 3:
 | ||
|               type=0;
 | ||
|               typeName="短效号码";
 | ||
|               break;
 | ||
|             default:
 | ||
|                 break;
 | ||
|           }
 | ||
| 
 | ||
|           this.$vs.dialog({
 | ||
|             color:"primary",
 | ||
|             title:"提示",
 | ||
|             text:`确认清理${typeName}数据吗?`,
 | ||
|             acceptText:"确定",
 | ||
|             accept:()=>this.doCleanSms(type)
 | ||
|           })
 | ||
|         },
 | ||
|         doCleanSms(type){
 | ||
|           this.showLoading();
 | ||
|           let data = JSON.stringify({
 | ||
|             type:type});
 | ||
| 
 | ||
|           fetch(requestApi + "/sms-phone/clean", {
 | ||
|             headers: {
 | ||
|               Authorization: `Bearer ${this.token}`,
 | ||
|               "Content-Type": "application/json",
 | ||
|               Accept: "application/json",
 | ||
|             },
 | ||
|             method: "DELETE",
 | ||
|             body: data,
 | ||
|           })
 | ||
|            .then((res) => res.json())
 | ||
|            .then((response) => {
 | ||
|               if (response.code === 200) {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "提示",
 | ||
|                   color: "success",
 | ||
|                   text: "清理成功",
 | ||
|                   position: "top-right",
 | ||
|                 });
 | ||
| 
 | ||
|                 this.getSmsList();
 | ||
|               } else {
 | ||
|                 this.$vs.notify({
 | ||
|                   title: "提示",
 | ||
|                   color: "danger",
 | ||
|                   text: response.msg,
 | ||
|                   position: "top-right",
 | ||
|                 });
 | ||
|               }
 | ||
|             })
 | ||
|            .catch((error) => {
 | ||
|               console.log(error);
 | ||
|             })
 | ||
|            .finally(() => {
 | ||
|               this.hiddenLoading();
 | ||
|             });
 | ||
|         },
 | ||
|       },
 | ||
|     });
 | ||
|   </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>
 |