b98ad9dce4
stats-overhaul 的设计稿(CONTRACT.md + mobile/tablet/desktop ui_kits)。 注:这批 ui_kits 描述的是统计页「上聚合·下分设备」原方案;统计页后续已 重设计为「月/周/日 周期卡 + 设备下拉 + 两周折线」(见 client/lib/screens/ stats_page.dart)。设计稿与新页布局有出入,待更新到新布局(单列 TODO 跟踪)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
163 lines
17 KiB
React
163 lines
17 KiB
React
/* dparts.jsx — 穿山甲 VPN desktop · atoms (inline Lucide paths) */
|
||
const DLUCIDE = {
|
||
power: '<path d="M12 2v10"/><path d="M18.36 6.64a9 9 0 1 1-12.73 0"/>',
|
||
'shield-check': '<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/>',
|
||
globe: '<circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/>',
|
||
'loader-circle': '<path d="M21 12a9 9 0 1 1-6.219-8.56"/>',
|
||
'chart-no-axes-column': '<line x1="18" x2="18" y1="20" y2="10"/><line x1="12" x2="12" y1="20" y2="4"/><line x1="6" x2="6" y1="20" y2="14"/>',
|
||
settings: '<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/>',
|
||
check: '<path d="M20 6 9 17l-5-5"/>',
|
||
zap: '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>',
|
||
'arrow-down': '<path d="M12 5v14"/><path d="m19 12-7 7-7-7"/>',
|
||
'arrow-up': '<path d="M12 19V5"/><path d="m5 12 7-7 7 7"/>',
|
||
search: '<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>',
|
||
moon: '<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>',
|
||
sun: '<circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/>',
|
||
crown: '<path d="M11.562 3.266a.5.5 0 0 1 .876 0L15.39 8.87a1 1 0 0 0 1.516.294L21.183 5.5a.5.5 0 0 1 .798.519l-2.834 10.246a1 1 0 0 1-.956.734H5.81a1 1 0 0 1-.957-.734L2.02 6.02a.5.5 0 0 1 .798-.519l4.276 3.664a1 1 0 0 0 1.516-.294z"/><path d="M5 21h14"/>',
|
||
mail: '<rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/>',
|
||
lock: '<rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
|
||
'check-circle': '<path d="M21.801 10A10 10 0 1 1 17 3.335"/><path d="m9 11 3 3L22 4"/>',
|
||
'arrow-left': '<path d="m12 19-7-7 7-7"/><path d="M19 12H5"/>',
|
||
'chevron-right': '<path d="m9 18 6-6-6-6"/>',
|
||
send: '<path d="M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z"/><path d="m21.854 2.147-10.94 10.939"/>',
|
||
'message-circle': '<path d="M7.9 20A9 9 0 1 0 4 16.1L2 22Z"/>',
|
||
'external-link': '<path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>',
|
||
'shopping-bag': '<path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/><path d="M3 6h18"/><path d="M16 10a4 4 0 0 1-8 0"/>',
|
||
laptop: '<path d="M18 5a2 2 0 0 1 2 2v8.526a2 2 0 0 0 .212.894l1.575 3.15A.5.5 0 0 1 21.342 21H2.658a.5.5 0 0 1-.445-.724l1.575-3.15A2 2 0 0 0 4 16.526V7a2 2 0 0 1 2-2z"/><path d="M20.054 15.987H3.946"/>',
|
||
smartphone: '<rect width="14" height="20" x="5" y="2" rx="2" ry="2"/><path d="M12 18h.01"/>',
|
||
'monitor-smartphone': '<path d="M18 8V5a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8"/><path d="M10 19v-3.96 3.15"/><path d="M7 19h5"/><rect width="6" height="10" x="16" y="12" rx="2"/>',
|
||
'log-out': '<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/>',
|
||
ticket: '<path d="M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z"/><path d="M13 5v2"/><path d="M13 17v2"/><path d="M13 11v2"/>',
|
||
clock: '<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>',
|
||
'play-circle': '<circle cx="12" cy="12" r="10"/><polygon points="10 8 16 12 10 16 10 8"/>',
|
||
compass: '<path d="m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"/><circle cx="12" cy="12" r="10"/>',
|
||
'refresh-cw': '<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M3 21v-5h5"/>',
|
||
'credit-card': '<rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/>',
|
||
user: '<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>',
|
||
};
|
||
function DIcon({ name, size = 20, stroke = 2, color = 'currentColor', style = {} }) {
|
||
return <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth={stroke} strokeLinecap="round" strokeLinejoin="round" style={{ display: 'block', flexShrink: 0, ...style }} dangerouslySetInnerHTML={{ __html: DLUCIDE[name] || '' }} />;
|
||
}
|
||
function DMark({ size = 28, color = 'var(--accent)' }) {
|
||
return (
|
||
<svg width={size} height={size} viewBox="0 0 96 96" fill="none" style={{ display: 'block' }}>
|
||
<g transform="translate(96,0) scale(-1,1)">
|
||
<path d="M16 59 Q17 51 26 50 Q34 31 45 30 Q55 30 62 37 Q69 41 75 45 Q85 49 89 44 Q92 50 84 52 Q75 53 67 53 Q66 62 60 62 L56 62 Q54 55 49 55 Q47 62 37 62 L33 62 Q31 56 26 56 Q20 59 16 59 Z" fill={color}/>
|
||
<g stroke="#fff" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" fill="none" opacity="0.5">
|
||
<path d="M26 50 Q34 43 42 50"/><path d="M36 48 Q44 41 52 48"/><path d="M46 48 Q54 41 62 49"/>
|
||
</g>
|
||
<circle cx="23" cy="55" r="2.2" fill="#fff" opacity="0.85"/>
|
||
</g>
|
||
</svg>
|
||
);
|
||
}
|
||
function DCC({ code, active, size = 34 }) {
|
||
return <div style={{ width: size, height: size, borderRadius: 'var(--radius-md)', flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: 'var(--font-mono)', fontSize: size*0.31, fontWeight: 600, background: active ? 'var(--accent)' : 'var(--bg-subtle)', color: active ? '#fff' : 'var(--fg2)' }}>{code}</div>;
|
||
}
|
||
function DSignal({ ping }) {
|
||
const lvl = ping < 40 ? 3 : ping < 90 ? 2 : 1;
|
||
return <div style={{ display: 'flex', gap: 2, alignItems: 'flex-end', height: 13 }}>{[4,8,13].map((h,i)=><div key={i} style={{ width: 3, height: h, borderRadius: 1, background: i<lvl?'var(--success)':'var(--border-strong)' }} />)}</div>;
|
||
}
|
||
const DSTRINGS = {
|
||
connect:{zh:'连接',en:'Connect'}, servers:{zh:'选择节点',en:'Choose server'}, stats:{zh:'统计',en:'Stats'}, settings:{zh:'设置',en:'Settings'},
|
||
navConnect:{zh:'连接',en:'Connect'}, navServers:{zh:'节点',en:'Servers'}, navStats:{zh:'统计',en:'Stats'}, navSettings:{zh:'设置',en:'Settings'},
|
||
proMember:{zh:'PRO 会员',en:'PRO member'}, until:{zh:'至 2026-12-31',en:'until 2026-12-31'},
|
||
connected:{zh:'已连接',en:'Connected'}, notConnected:{zh:'未连接',en:'Not connected'},
|
||
capOff:{zh:'未连接',en:'Not connected'}, subOff:{zh:'点击下方按钮开始保护',en:'Tap the button below to start'},
|
||
capConnecting:{zh:'连接中…',en:'Connecting…'}, subConnecting:{zh:'正在建立加密隧道',en:'Establishing encrypted tunnel'},
|
||
capOn:{zh:'已连接',en:'Connected'}, subOn:{zh:'你的网络已加密保护',en:'Your network is encrypted'},
|
||
connectNow:{zh:'点击连接',en:'CONNECT'},
|
||
search:{zh:'搜索国家 / 城市',en:'Search country / city'},
|
||
trafficMonth:{zh:'本月流量',en:'Traffic'}, avgPing:{zh:'平均延迟',en:'Avg ping'}, durMonth:{zh:'本月时长',en:'Time'},
|
||
weekTraffic:{zh:'本周流量 (GB)',en:'This week (GB)'},
|
||
byDevice:{zh:'设备明细',en:'By device'}, noDeviceUsage:{zh:'暂无设备用量',en:'No device usage yet'},
|
||
autostart:{zh:'开机自启动',en:'Launch at login'}, autostartSub:{zh:'登录系统时自动启动穿山甲',en:'Start Pangolin when you log in'},
|
||
smartRoute:{zh:'智能分流',en:'Smart routing'}, smartRouteSub:{zh:'海外应用加速,本地直连',en:'Accelerate overseas apps, keep local direct'},
|
||
killSwitch:{zh:'Kill Switch',en:'Kill Switch'}, killSwitchSub:{zh:'断线时阻断网络,防止 IP 泄露',en:'Block all traffic if connection drops'},
|
||
language:{zh:'语言',en:'Language'}, darkAppearance:{zh:'深色外观',en:'Dark appearance'}, stateOn:{zh:'已开启',en:'On'}, followLight:{zh:'跟随浅色',en:'Off'},
|
||
protocol:{zh:'协议',en:'Protocol'}, version:{zh:'版本',en:'Version'},
|
||
days:{zh:['周一','周二','周三','周四','周五','周六','周日'],en:['Mon','Tue','Wed','Thu','Fri','Sat','Sun']},
|
||
/* auth */
|
||
authTagline:{zh:'极速 · 稳定 · 省心',en:'Fast · Stable · Effortless'},
|
||
tabLogin:{zh:'登录',en:'Log in'}, tabRegister:{zh:'注册',en:'Sign up'},
|
||
emailLabel:{zh:'邮箱',en:'Email'}, emailPh:{zh:'你的邮箱地址',en:'your@email.com'},
|
||
pwLabel:{zh:'密码',en:'Password'}, pwPh:{zh:'输入密码',en:'Enter password'}, setPwPh:{zh:'设置登录密码(用于多端登录)',en:'Set a password (for multi-device login)'},
|
||
codeLabel:{zh:'邮箱验证码',en:'Verification code'}, codeSentTo:{zh:'验证码已发送至',en:'Code sent to'},
|
||
sendCode:{zh:'发送验证码',en:'Send code'}, resend:{zh:'重新发送',en:'Resend'},
|
||
doLogin:{zh:'登录',en:'Log in'}, doNext:{zh:'下一步',en:'Next'}, doCreate:{zh:'创建账户',en:'Create account'},
|
||
forgotPw:{zh:'忘记密码?',en:'Forgot password?'},
|
||
tos:{zh:'继续即代表同意《服务条款》与《隐私政策》',en:'By continuing you agree to our Terms & Privacy Policy'},
|
||
authSidePitch:{zh:'一个账户,全平台同步。守护你的每一次连接。',en:'One account, every device. Protecting every connection.'},
|
||
/* redeem & buy */
|
||
redeemEntry:{zh:'兑换 & 购买',en:'Redeem & buy'}, redeemTitle:{zh:'兑换 & 购买',en:'Redeem & buy'},
|
||
redeemCodeTitle:{zh:'兑换激活码',en:'Redeem a code'}, redeemPh:{zh:'输入激活码',en:'Enter activation code'}, redeemBtn:{zh:'激活',en:'Redeem'},
|
||
redeemOk:{zh:'激活成功 · PRO 已开通',en:'Activated · PRO unlocked'},
|
||
buyTitle:{zh:'购买渠道',en:'Where to buy'},
|
||
buySub:{zh:'客户端内不支持直接支付。请通过以下渠道获取激活码:',en:'In-app payment is unavailable. Get an activation code via:'},
|
||
chStore:{zh:'自助发卡商店',en:'Self-serve store'}, chStoreSub:{zh:'支付宝 / 微信 · 自动发码',en:'Alipay / WeChat · instant code'},
|
||
chUsdtSub:{zh:'链上转账 · 最隐私 · 自动发码',en:'On-chain · most private · instant code'},
|
||
smartSelect:{zh:'智能选择',en:'Smart select'}, smartSub:{zh:'根据当前网络环境,自动选择最优节点',en:'Picks the best node for your network'},
|
||
recommended:{zh:'推荐',en:'Recommended'},
|
||
twoFA:{zh:'双重认证',en:'Two-factor auth'}, twoFASub:{zh:'PRO 专享 · 登录需身份验证器动态码',en:'PRO only · TOTP code at login'},
|
||
ucEntry:{zh:'Web 用户中心',en:'Web account center'}, ucEntrySub:{zh:'订阅导入 · 邀请返利',en:'Subscription import · referral'}, ucOpen:{zh:'打开',en:'Open'},
|
||
/* quota + free (口径见 CLAUDE.md §7,与移动端一致) */
|
||
quotaToday:{zh:'今日剩余',en:'Left today'}, minutes:{zh:'分钟',en:'min'},
|
||
quotaFree:{zh:'免费版 · 每日 10 分钟',en:'Free · 10 min/day'},
|
||
watchAd:{zh:'看广告开始使用',en:'Watch ad to start'},
|
||
adUnlocked:{zh:'已解锁 · 今日可用',en:'Unlocked for today'},
|
||
freePlanName:{zh:'免费版',en:'Free'},
|
||
freeDemo:{zh:'演示:免费版视角',en:'Demo: free-tier view'}, freeDemoSub:{zh:'切换连接页的额度展示与套餐横幅',en:'Toggles quota UI & plan banner'},
|
||
checkUpdate:{zh:'检查更新',en:'Check for updates'},
|
||
replayOnboarding:{zh:'查看首次引导',en:'View onboarding'}, replayOnboardingSub:{zh:'演示用 · 重看 3 屏引导',en:'Demo · replay the 3 intro screens'},
|
||
/* 2FA login step (与用户中心一致) */
|
||
twoFATitle:{zh:'双重认证',en:'Two-factor auth'},
|
||
twoFAHint:{zh:'你的账户已开启双重认证。请输入身份验证器 App 中的 6 位动态码。',en:'Two-factor auth is on for this account. Enter the 6-digit code from your authenticator app.'},
|
||
twoFAConfirm:{zh:'验证并登录',en:'Verify & log in'},
|
||
twoFABack:{zh:'返回上一步',en:'Back'},
|
||
chTgSub:{zh:'@PangolinVPN_bot',en:'@PangolinVPN_bot'}, chLineSub:{zh:'@pangolinvpn',en:'@pangolinvpn'},
|
||
chEmail:{zh:'邮箱客服',en:'Email support'}, chEmailSub:{zh:'buy@pangolin.vpn',en:'buy@pangolin.vpn'},
|
||
/* account management (integrated dashboard) */
|
||
navAccount:{zh:'账户',en:'Account'},
|
||
curPlan:{zh:'当前套餐',en:'Current plan'}, renew:{zh:'续费 / 升级',en:'Renew / Upgrade'},
|
||
myDevices:{zh:'我的设备',en:'My devices'}, devicesSub:{zh:'PRO 套餐最多 5 台设备同时在线',en:'Up to 5 devices on PRO'},
|
||
thisDevice:{zh:'当前设备',en:'This device'}, lastActive:{zh:'最近活跃',en:'Last active'}, remove:{zh:'移除',en:'Remove'},
|
||
choosePlan:{zh:'选择套餐',en:'Choose your plan'}, free:{zh:'免费版',en:'Free'}, proPlan:{zh:'专业版',en:'Pro'}, team:{zh:'团队版',en:'Team'},
|
||
perMonth:{zh:'/月',en:'/mo'}, current:{zh:'当前',en:'Current'}, upgrade:{zh:'立即升级',en:'Upgrade'}, choose:{zh:'选择',en:'Choose'}, mostPopular:{zh:'最受欢迎',en:'Popular'},
|
||
feFree:{zh:['仅 1 个基础节点','每日 10 分钟时长','使用前观看广告','注册享 7 天免费试用'],en:['1 basic node only','10 min per day','Watch an ad to start','7-day free trial on sign-up']},
|
||
fePro:{zh:['80+ 全球节点','无限流量 · 极速','5 台设备同时在线','流媒体优化'],en:['80+ locations','Unlimited · fast','5 devices','Streaming optimized']},
|
||
feTeam:{zh:['Pro 全部功能','10 个成员席位','集中计费与管理','优先客服'],en:['Everything in Pro','10 seats','Central billing','Priority support']},
|
||
/* account info */
|
||
accInfoTitle:{zh:'账户信息',en:'Account info'}, accEmail:{zh:'邮箱',en:'Email'}, accPassword:{zh:'密码',en:'Password'}, accChange:{zh:'修改',en:'Change'},
|
||
accUsage:{zh:'本月用量',en:'Used this month'}, accSignOut:{zh:'退出登录',en:'Sign out'},
|
||
backToAccount:{zh:'返回账户',en:'Back to account'},
|
||
/* demo replay */
|
||
replayAuth:{zh:'查看登录 / 注册流程',en:'View login / sign-up'}, replayAuthSub:{zh:'演示用 · 退出到登录页',en:'Demo · back to login'},
|
||
replayOnboarding:{zh:'查看首次引导',en:'View onboarding'}, replayOnboardingSub:{zh:'演示用 · 重看引导页',en:'Demo · replay intro'},
|
||
/* onboarding */
|
||
obSkip:{zh:'跳过',en:'Skip'}, obNext:{zh:'下一步',en:'Next'}, obAllow:{zh:'允许并继续',en:'Allow & continue'}, obStart:{zh:'开始使用',en:'Get started'},
|
||
ob1Title:{zh:'一键连接全球',en:'Connect worldwide'}, ob1Sub:{zh:'80+ 节点,智能选择最快线路,稳定不掉线。',en:'80+ locations, auto-fastest routing, rock-solid.'},
|
||
ob2Title:{zh:'授权网络配置',en:'Allow network setup'}, ob2Sub:{zh:'系统会请求添加一个网络配置,用于建立加密隧道。',en:'Your system will ask to add a network profile to build the encrypted tunnel.'},
|
||
ob3Title:{zh:'安全 · 无日志',en:'Private by design'}, ob3Sub:{zh:'端到端加密,我们不记录你的任何浏览数据。',en:'End-to-end encrypted. We keep zero browsing logs.'},
|
||
/* contact us */
|
||
navContact:{zh:'联系我们',en:'Contact us'},
|
||
contactIntro:{zh:'遇到问题?通过以下任一渠道联系我们,通常数分钟内回复。',en:'Need help? Reach us through any channel below — we usually reply within minutes.'},
|
||
contactTg:{zh:'Telegram 客服',en:'Telegram support'}, contactLine:{zh:'LINE 客服',en:'LINE support'},
|
||
contactEmail:{zh:'邮箱客服',en:'Email support'}, contactStore:{zh:'自助发卡商店',en:'Self-serve store'},
|
||
contactHoursTitle:{zh:'服务时间',en:'Support hours'}, contactHours:{zh:'每日 9:00 – 24:00 (GMT+8)',en:'Daily 9:00 – 24:00 (GMT+8)'},
|
||
contactFaqTitle:{zh:'常见问题',en:'FAQ'}, contactFaq:{zh:'连不上 / 兑换码无效 / 多设备登录,先查阅帮助中心。',en:'Connection issues, invalid codes, multi-device — check the Help Center first.'},
|
||
contactFaqBtn:{zh:'打开帮助中心',en:'Open Help Center'},
|
||
};
|
||
function dT(lang){ return k => { const v=DSTRINGS[k]; return v ? (v[lang]||k) : k; }; }
|
||
function dName(s,lang){ return lang==='zh'?s.name:s.en; }
|
||
function dSub(s,lang){ if(lang==='zh') return s.tag||s.en; return s.tag==='流媒体优化'?'Streaming':s.tag==='P2P'?'P2P':s.name; }
|
||
Object.assign(window, { DIcon, DMark, DCC, DSignal, DSTRINGS, dT, dName, dSub, DSERVERS: [
|
||
{ code:'HK', name:'香港 · 流媒体', en:'Hong Kong', ping:18, tag:'流媒体优化' },
|
||
{ code:'JP', name:'日本 东京', en:'Tokyo', ping:32, tag:'P2P' },
|
||
{ code:'TW', name:'台湾 台北', en:'Taipei', ping:28, tag:'' },
|
||
{ code:'SG', name:'新加坡', en:'Singapore', ping:54, tag:'P2P' },
|
||
{ code:'KR', name:'韩国 首尔', en:'Seoul', ping:41, tag:'' },
|
||
{ code:'US', name:'美国 洛杉矶', en:'Los Angeles', ping:146, tag:'流媒体优化' },
|
||
{ code:'DE', name:'德国 法兰克福', en:'Frankfurt', ping:198, tag:'' },
|
||
{ code:'UK', name:'英国 伦敦', en:'London', ping:210, tag:'' },
|
||
] });
|