Files
eSIM-Tools/index.html
Abner 6379bbef90 ♻️ refactor: 移除外部二维码服务依赖,改用本地生成方案
- 将前端二维码生成从 `qrcode.show` 远程服务切换为 `qrcode-generator` 本地库(UMD 格式),消除对外部 CDN 渲染服务的依赖,降低隐私风险和第三方服务不可用时的故障点
- 重写 `generateQRCodeLocal` 函数适配 `qrcode-generator` API,使用 `qrcode(typeNumber, errorCorrectionLevel).addData().make().createDataURL()` 模式,并基于 QR 码模块数动态计算 `cellSize`,新增大尺寸预览图支持(400px)
- 移除所有页面中的 `qrcode.show` 域名引用,包括 CSP `connectSrc` 策略、preconnect 标签、resource-hints DNS 预解析配置及 Simyo/Giffgaff API 配置中的 `qrcode` 端点
- 完善二维码生成的日志与监控:在 CDN 加载成功后输出 `console.log`,本地/后端生成成功及失败时分别输出对应级别日志,并在 `trackQRCodeEvent` 中增加 `isBrowser` 守卫避免非浏览器环境报错
- 增加生成耗时和 QR 码长度(不含内容)到后端 BFF 日志,同时统一使用 `Date.now()` 计算请求耗时,避免 LPA 激活码等敏感信息进入日志
- 全面更新所有相关测试用例,适配新的 `window.qrcode` 工厂函数 mock,新增日志输出和 Sentry 上报断言,验证本地成功、本地失败降级、后端成功、后端失败四种场景的可观测性
2026-06-23 21:22:16 +08:00

879 lines
40 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN" data-page="home">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="eSIM 设备管理工具集,支持已有 Giffgaff 与 Simyo 号码的 eSIM 转换、设备更换、二维码生成及验证码处理。">
<!-- Sentry 错误监控配置(必须在其他脚本之前) -->
<!-- 使用外链脚本以符合 CSP 策略 -->
<script src="/src/js/sentry-config.js"></script>
<script src="/src/js/sentry-loader.js"></script>
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-eval' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://browser.sentry-cdn.com https://www.googletagmanager.com https://challenges.cloudflare.com https://www.google.com https://www.gstatic.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://fonts.googleapis.com; font-src 'self' data: https://fonts.gstatic.com; connect-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://browser.sentry-cdn.com https://www.google-analytics.com https://analytics.google.com https://stats.g.doubleclick.net https://www.googletagmanager.com https://appapi.simyo.nl https://api.giffgaff.com https://id.giffgaff.com https://publicapi.giffgaff.com https://challenges.cloudflare.com https://www.google.com https://www.gstatic.com https://*.sentry.io; img-src 'self' data: https:; frame-src 'self' https://challenges.cloudflare.com https://www.google.com;">
<title>eSIM Tools - Giffgaff &amp; Simyo eSIM 在线管理工具 | 设备更换、激活与二维码生成</title>
<link rel="canonical" href="https://esim.cosr.eu.org/">
<meta name="robots" content="index, follow">
<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#6366f1">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://esim.cosr.eu.org/">
<meta property="og:title" content="eSIM Tools - Giffgaff & Simyo eSIM 在线管理工具">
<meta property="og:description" content="免费在线 eSIM 管理工具,支持已有 Giffgaff 与 Simyo 号码的 eSIM 转换、OAuth 认证、设备更换、QR 码生成及激活,浏览器端操作无需安装 App。">
<meta property="og:image" content="https://esim.cosr.eu.org/og-image.png">
<meta property="og:locale" content="zh_CN">
<meta property="og:site_name" content="eSIM Tools">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="eSIM Tools - Giffgaff & Simyo eSIM 在线管理工具">
<meta name="twitter:description" content="免费在线 eSIM 管理工具,支持已有 Giffgaff 与 Simyo 号码的 eSIM 转换、设备更换、QR 码生成及激活。">
<meta name="twitter:image" content="https://esim.cosr.eu.org/og-image.png">
<!-- JSON-LD 结构化数据 -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "eSIM Tools",
"url": "https://esim.cosr.eu.org",
"description": "免费在线 eSIM 管理工具,支持已有 Giffgaff 与 Simyo 号码的 eSIM 转换、OAuth 认证、设备更换、QR 码生成及激活。",
"applicationCategory": "UtilitiesApplication",
"operatingSystem": "Web Browser",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "GBP"
},
"featureList": [
"Giffgaff eSIM OAuth 认证与激活",
"Simyo eSIM 设备更换与管理",
"自动生成 eSIM QR 码",
"支持 MFA 多因素认证",
"浏览器端本地处理,保护隐私"
],
"inLanguage": ["zh", "en"],
"softwareHelp": {
"@type": "CreativeWork",
"url": "https://github.com/Silentely/eSIM-Tools"
}
}
</script>
<!-- 性能优化:预连接与预加载关键资源 -->
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
<link rel="preconnect" href="https://browser.sentry-cdn.com" crossorigin>
<link rel="preconnect" href="https://www.googletagmanager.com" crossorigin>
<link rel="preconnect" href="https://api.giffgaff.com" crossorigin>
<link rel="preconnect" href="https://appapi.simyo.nl" crossorigin>
<!-- Favicon -->
<link rel="icon" href="/src/assets/favicon.ico" type="image/x-icon">
<!-- 关键CSS优先直接加载避免 onload 违反 CSP -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
<!-- Font Awesome直接引入避免 onload 触发器被 CSP 阻断导致样式未生效 -->
<link rel="stylesheet" href="/src/assets/fontawesome/all.min.css">
<!-- Font Awesome 资源路径woff2在 /src/assets/fontawesome/,确保已发布且 CSP 允许 font-src 'self' -->
<!-- 本地样式文件 -->
<link rel="stylesheet" href="/src/styles/design-system.css">
<link rel="stylesheet" href="/src/styles/animations.css">
<link rel="stylesheet" href="/src/styles/fa-local.css">
<link rel="stylesheet" href="/src/styles/notification.css">
<meta name="build-rev" content="v1">
<style>
/* Design System: 语义化令牌与统一规范 */
:root{
/* Brand */
--brand-start:#6366f1; /* indigo-500 */
--brand-end:#8b5cf6; /* violet-500 */
--accent-ok:#10b981; /* emerald-500 */
--accent-warn:#f59e0b; /* amber-500 */
--accent-danger:#ef4444; /* red-500 */
/* Surface & Text */
/* 背景改为淡色 #f2f5c4 */
--bg-grad-start:#f2f5c4;
--bg-grad-end:#f1f5bb;
--surface:#ffffff;
--surface-alpha:92%;
--text-primary:#1f2937; /* slate-800 */
--text-secondary:#374151; /* gray-700 */
--text-muted:#6b7280; /* gray-500 */
/* Radius & Elevation & Spacing */
--radius-lg:18px;
--shadow-sm:0 4px 10px rgba(0,0,0,.08);
--shadow-md:0 10px 30px rgba(0,0,0,.10);
--shadow-lg:0 18px 40px rgba(0,0,0,.15);
--ring:0 0 0 3px rgba(99,102,241,.35);
--s1:8px; --s2:12px; --s3:16px; --s4:20px; --s5:24px; --s6:32px;
/* Motion */
--ease-standard:cubic-bezier(.2,.8,.2,1);
--duration-fast:.18s;
--duration-normal:.28s;
}
*{margin:0;padding:0;box-sizing:border-box}
body{
font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, 'Helvetica Neue', Arial, 'Noto Sans', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
background: linear-gradient(135deg, var(--bg-grad-start) 0%, var(--bg-grad-end) 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: var(--s5);
color: var(--text-primary);
}
.main-container{
background: color-mix(in oklab, white var(--surface-alpha), transparent);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
border-radius: var(--radius-lg);
padding: clamp(20px, 4vw, 40px);
box-shadow: var(--shadow-lg);
max-width: 960px;
width: 100%;
text-align: center;
border: 1px solid color-mix(in oklab, white 70%, transparent);
}
.language-switcher{
display:flex;
justify-content:flex-end;
width:100%;
margin-bottom: var(--s3);
}
.lang-switch{
display:inline-flex;
align-items:center;
gap:6px;
padding:6px 16px;
border-radius:999px;
border:1px solid rgba(99,102,241,.25);
background: rgba(255,255,255,.7);
color: var(--text-primary);
font-weight:600;
cursor:pointer;
transition: transform var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard);
}
.lang-switch:hover{
transform: translateY(-1px);
box-shadow: var(--shadow-sm);
}
.lang-switch:focus-visible{
outline:none;
box-shadow: var(--ring);
}
.header{ margin-bottom: var(--s6); }
.header h1{
font-size: clamp(1.9rem, 3vw, 2.6rem);
font-weight: 800;
color: var(--text-primary);
margin-bottom: var(--s2);
letter-spacing: .2px;
display:flex;align-items:center;justify-content:center;gap:10px;
}
.header .subtitle{
font-size: clamp(.95rem, 1.9vw, 1.12rem);
color: var(--text-muted);
margin: 0;
}
.header-actions{
display: flex;
gap: var(--s2);
justify-content: center;
flex-wrap: wrap;
margin-top: var(--s3);
}
.github-btn, .kofi-btn{
font-size: 16px;
font-weight: 600;
padding: 10px 18px;
display:inline-flex;
align-items:center;
justify-content:center;
border-radius: 999px;
transition: transform var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard), background var(--duration-fast) var(--ease-standard);
gap: 8px;
box-shadow: var(--shadow-sm);
text-decoration: none !important;
outline: none;
will-change: transform;
border: 1px solid rgba(255,255,255,.18);
}
.github-btn{ background: #24292e; color:#fff !important; }
.github-btn:hover{ transform: translateY(-1px); box-shadow: var(--shadow-md); background:#111; }
.kofi-btn{ background: #FF5E5B; color:#fff !important; }
.kofi-btn:hover{ transform: translateY(-1px); box-shadow: var(--shadow-md); background:#e64a47; }
.github-btn:focus-visible, .kofi-btn:focus-visible{ outline: none; box-shadow: var(--ring); }
.tools-grid{
display:grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: clamp(16px, 3vw, 28px);
margin-top: var(--s6);
}
@media (max-width: 720px){ .tools-grid{ grid-template-columns: 1fr; } }
.tool-card{
background: var(--surface);
border-radius: var(--radius-lg);
padding: clamp(20px, 3.2vw, 28px);
box-shadow: var(--shadow-md);
transition: transform var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard);
border: 2px solid transparent;
position: relative;
overflow: hidden;
will-change: transform, box-shadow;
}
.tool-card::before{
content: '';
position: absolute;
inset: 0 0 auto 0;
height: 4px;
background: linear-gradient(90deg, var(--primary), var(--secondary));
}
.tool-card:hover{
transform: translateY(-6px);
box-shadow: var(--shadow-lg);
border-color: color-mix(in oklab, var(--primary) 40%, transparent);
}
.tool-card:focus-within{ box-shadow: var(--ring); }
/* 品牌色注入 */
.giffgaff-card{ --primary:#ff6b00; --secondary:#ff8c42; }
.simyo-card{ --primary:#e74c3c; --secondary:#c0392b; }
.tool-icon{
font-size: clamp(2.2rem, 3.2vw, 2.8rem);
color: var(--primary);
margin-bottom: var(--s3);
}
.tool-title{
font-size: clamp(1.2rem, 2.2vw, 1.4rem);
font-weight: 800;
color: var(--text-primary);
margin-bottom: var(--s2);
}
.tool-description{
color: var(--text-muted);
margin-bottom: var(--s4);
line-height: 1.65;
}
.tool-features{
list-style: none;
padding: 0;
margin-bottom: var(--s4);
text-align: left;
}
.tool-features li{
display: flex;
align-items: baseline;
gap: 10px;
padding: 6px 0;
color: var(--text-secondary);
}
.tool-features li i{ color: var(--primary); flex: 0 0 auto; }
.tool-features li span{ flex: 1 1 auto; }
.tool-btn{
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: #fff;
border: none;
padding: 12px 22px;
border-radius: 999px;
font-size: 1rem;
font-weight: 800;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 10px;
transition: transform var(--duration-fast) var(--ease-standard), box-shadow var(--duration-fast) var(--ease-standard), filter var(--duration-fast) var(--ease-standard);
box-shadow: 0 6px 16px color-mix(in oklab, var(--primary) 35%, transparent);
outline: none;
will-change: transform, filter;
}
.tool-btn:hover{ transform: translateY(-2px); text-decoration: none; color: #fff; filter: brightness(1.03); }
.tool-btn:focus-visible{ box-shadow: var(--ring); }
.footer{
margin-top: var(--s6);
padding-top: var(--s5);
border-top: 1px solid color-mix(in oklab, #ecf0f1 85%, transparent);
color: #6b7280;
font-size: .94rem;
line-height: 1.6;
text-align: center;
}
/* 入场动画IntersectionObserver 触发,避免 setTimeout 抖动 */
.reveal{ opacity: 0; transform: translateY(12px); transition: opacity .5s var(--ease-standard), transform .5s var(--ease-standard); will-change: opacity, transform; }
.reveal.show{ opacity: 1; transform: translateY(0); }
/* 降低动效:尊重用户的减少动态偏好 */
@media (prefers-reduced-motion: reduce){
.reveal{ transition: none; transform: none; opacity: 1; }
.tool-card, .github-btn, .tool-btn{ transition: none!important; }
}
/* 小屏进一步优化 */
@media (max-width: 360px){
.github-btn{ padding: 9px 14px; font-size: 14px; }
.tool-btn{ padding: 10px 18px; font-size: .95rem; }
}
/* GEO 优化内容区块 */
.info-section{
margin-top: var(--s6);
text-align: left;
}
.section-title{
font-size: clamp(1.3rem, 2.5vw, 1.6rem);
font-weight: 700;
color: var(--text-primary);
margin-bottom: var(--s3);
display: flex;
align-items: center;
gap: var(--s2);
}
.section-title i{ color: var(--primary, var(--brand-start)); }
.section-desc{
color: var(--text-muted);
margin-bottom: var(--s4);
}
/* 设备兼容性 */
.compat-grid{
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--s3);
margin-bottom: var(--s3);
}
@media (max-width: 600px){ .compat-grid{ grid-template-columns: 1fr; } }
.compat-card{
background: color-mix(in oklab, var(--surface) 95%, transparent);
border-radius: var(--radius-lg);
padding: var(--s4);
border: 1px solid color-mix(in oklab, var(--brand-start) 20%, transparent);
}
.compat-card h3{
font-size: 1rem;
font-weight: 700;
margin-bottom: var(--s2);
color: var(--text-primary);
}
.compat-card p{
font-size: .9rem;
color: var(--text-secondary);
line-height: 1.6;
}
.compat-check{
background: color-mix(in oklab, var(--accent-ok) 10%, transparent);
padding: var(--s3);
border-radius: var(--radius-lg);
font-size: .9rem;
}
.compat-check code{
background: rgba(0,0,0,.06);
padding: 2px 6px;
border-radius: 4px;
font-weight: 600;
}
/* 使用教程 */
.tutorial-grid{
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--s4);
}
@media (max-width: 700px){ .tutorial-grid{ grid-template-columns: 1fr; } }
.tutorial-card{
background: var(--surface);
border-radius: var(--radius-lg);
padding: var(--s4);
border-left: 4px solid;
}
.giffgaff-tutorial{ border-left-color: #ff6b00; }
.simyo-tutorial{ border-left-color: #e74c3c; }
.tutorial-card h3{
font-size: 1.05rem;
font-weight: 700;
margin-bottom: var(--s3);
color: var(--text-primary);
}
.tutorial-steps{
padding-left: var(--s4);
margin-bottom: var(--s3);
}
.tutorial-steps li{
font-size: .9rem;
color: var(--text-secondary);
margin-bottom: var(--s1);
line-height: 1.5;
}
.tutorial-note{
font-size: .85rem;
color: var(--text-muted);
background: color-mix(in oklab, var(--accent-warn) 10%, transparent);
padding: var(--s2) var(--s3);
border-radius: var(--radius-lg);
}
/* 对比表格 */
.compare-table-wrapper{
overflow-x: auto;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
}
.compare-table{
width: 100%;
border-collapse: collapse;
background: var(--surface);
font-size: .9rem;
}
.compare-table th{
background: color-mix(in oklab, var(--brand-start) 10%, transparent);
padding: var(--s3);
font-weight: 700;
text-align: left;
color: var(--text-primary);
}
.compare-table td{
padding: var(--s3);
border-bottom: 1px solid color-mix(in oklab, #ecf0f1 80%, transparent);
color: var(--text-secondary);
}
.compare-table tr:last-child td{ border-bottom: none; }
.text-success{ color: var(--accent-ok); }
.text-warning{ color: var(--accent-warn); }
/* 地区限制 */
.region-grid{
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--s3);
}
@media (max-width: 600px){ .region-grid{ grid-template-columns: 1fr; } }
.region-card{
background: var(--surface);
border-radius: var(--radius-lg);
padding: var(--s4);
border: 1px solid color-mix(in oklab, var(--brand-start) 15%, transparent);
}
.region-card h3{
font-size: 1rem;
font-weight: 700;
margin-bottom: var(--s2);
color: var(--text-primary);
}
.region-card p{
font-size: .9rem;
color: var(--text-secondary);
margin-bottom: var(--s1);
}
.region-card strong{ color: var(--text-primary); }
/* 术语表 */
.glossary-grid{
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--s3);
}
@media (max-width: 800px){ .glossary-grid{ grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 500px){ .glossary-grid{ grid-template-columns: 1fr; } }
.glossary-item{
background: var(--surface);
border-radius: var(--radius-lg);
padding: var(--s3) var(--s4);
}
.glossary-term{
font-weight: 700;
font-size: .9rem;
color: var(--brand-start);
margin-bottom: var(--s1);
}
.glossary-desc{
font-size: .85rem;
color: var(--text-muted);
line-height: 1.5;
margin: 0;
}
/* FAQ */
.faq-list{
display: flex;
flex-direction: column;
gap: var(--s2);
}
.faq-item{
background: var(--surface);
border-radius: var(--radius-lg);
overflow: hidden;
}
.faq-item summary{
padding: var(--s3) var(--s4);
font-weight: 600;
font-size: .95rem;
color: var(--text-primary);
cursor: pointer;
transition: background var(--duration-fast) var(--ease-standard);
}
.faq-item summary:hover{
background: color-mix(in oklab, var(--brand-start) 5%, transparent);
}
.faq-item p{
padding: 0 var(--s4) var(--s3);
font-size: .9rem;
color: var(--text-secondary);
line-height: 1.6;
}
/* 安全声明 */
.disclaimer-content{
display: flex;
flex-direction: column;
gap: var(--s3);
}
.disclaimer-item{
display: flex;
align-items: flex-start;
gap: var(--s3);
padding: var(--s4);
border-radius: var(--radius-lg);
background: var(--surface);
}
.disclaimer-item i{
font-size: 1.5rem;
flex-shrink: 0;
margin-top: 2px;
}
.disclaimer-item.warning{ border-left: 4px solid var(--accent-warn); }
.disclaimer-item.warning i{ color: var(--accent-warn); }
.disclaimer-item.info{ border-left: 4px solid var(--brand-start); }
.disclaimer-item.info i{ color: var(--brand-start); }
.disclaimer-item.success{ border-left: 4px solid var(--accent-ok); }
.disclaimer-item.success i{ color: var(--accent-ok); }
.disclaimer-item strong{
display: block;
font-size: .95rem;
margin-bottom: var(--s1);
color: var(--text-primary);
}
.disclaimer-item p{
font-size: .9rem;
color: var(--text-secondary);
margin: 0;
line-height: 1.5;
}
</style>
<!-- Google tag (gtag.js) 延迟加载 -->
<script type="module" src="/src/js/captcha-client.js"></script>
<script defer src="https://www.googletagmanager.com/gtag/js?id=G-HGX38HMNL1" crossorigin="anonymous"></script></script>
</head>
<body aria-label="eSIM 工具选择">
<div class="main-container" role="region" aria-label="eSIM 工具选择">
<div class="language-switcher">
<button id="languageSwitcher" class="lang-switch" type="button" aria-label="切换语言到英文">🌐 English</button>
</div>
<div class="header">
<h1><i class="fas fa-sim-card" aria-hidden="true"></i> <span class="header-title-text">eSIM 工具集</span></h1>
<p class="subtitle">轻松管理您的 Giffgaff 与 Simyo eSIM 配置</p>
<div class="header-actions">
<a href="https://github.com/Silentely/eSIM-Tools" target="_blank" rel="noopener noreferrer" class="github-btn" aria-label="在新窗口打开 GitHub 仓库">
<i class="fab fa-github" aria-hidden="true"></i> GitHub
</a>
<a href="https://ko-fi.com/debug402" target="_blank" rel="noopener noreferrer" class="kofi-btn" aria-label="Buy me a coffee on Ko-fi">
<i class="fas fa-coffee" aria-hidden="true"></i> <span>Buy Me a Coffee</span>
</a>
</div>
</div>
<div class="tools-grid" aria-live="polite">
<!-- Giffgaff工具卡片 -->
<section class="tool-card giffgaff-card reveal" tabindex="-1" aria-labelledby="giffgaff-title" aria-describedby="giffgaff-desc">
<div class="tool-icon" aria-hidden="true">
<i class="fas fa-mobile-alt"></i>
</div>
<h2 id="giffgaff-title" class="tool-title"><span class="tool-title-text">Giffgaff eSIM 工具</span></h2>
<p id="giffgaff-desc" class="tool-description">
<span class="tool-description-text">完整的 Giffgaff eSIM 转换与管理,支持 OAuth 认证、MFA 验证与 GraphQL API 调用</span>
</p>
<ul class="tool-features">
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>OAuth 2.0 PKCE 认证</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>邮件验证码与 MFA 支持</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>GraphQL API 集成</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>自动生成 eSIM 二维码</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>完善的错误处理</span></li>
</ul>
<a href="/giffgaff" class="tool-btn" aria-describedby="giffgaff-desc">
<i class="fas fa-arrow-right" aria-hidden="true"></i><span>使用 Giffgaff 工具</span>
</a>
</section>
<!-- Simyo工具卡片 -->
<section class="tool-card simyo-card reveal" tabindex="-1" aria-labelledby="simyo-title" aria-describedby="simyo-desc">
<div class="tool-icon" aria-hidden="true">
<i class="fas fa-sim-card"></i>
</div>
<h2 id="simyo-title" class="tool-title"><span class="tool-title-text">Simyo 工具</span></h2>
<p id="simyo-desc" class="tool-description">
<span class="tool-description-text">专为 Simyo NL 用户的 eSIM 管理,支持设备更换与短信验证码处理</span>
</p>
<ul class="tool-features">
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>简单快捷的登录验证</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>设备更换完整流程</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>短信验证码支持</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>一键生成二维码</span></li>
<li><i class="fa-solid fa-check" aria-hidden="true"></i><span>安装确认功能</span></li>
</ul>
<a href="/simyo" class="tool-btn" aria-describedby="simyo-desc">
<i class="fas fa-arrow-right" aria-hidden="true"></i><span>使用 Simyo 工具</span>
</a>
</section>
</div>
<!-- 设备兼容性 -->
<section class="info-section reveal" aria-labelledby="compat-title">
<h2 id="compat-title" class="section-title"><i class="fas fa-mobile-alt" aria-hidden="true"></i> <span class="compat-title-text">设备兼容性</span></h2>
<p class="section-desc">使用前请确认您的设备支持 eSIM 功能</p>
<div class="compat-grid">
<div class="compat-card">
<h3><i class="fab fa-apple" aria-hidden="true"></i> Apple</h3>
<p>iPhone XS / XR 及更新机型<br>iPhone SE 2 及更新机型</p>
</div>
<div class="compat-card">
<h3><i class="fab fa-android" aria-hidden="true"></i> Android</h3>
<p>Samsung Galaxy S20 及更新<br>Google Pixel 3 及更新<br>部分 OnePlus、华为机型</p>
</div>
</div>
<p class="compat-check"><strong>检查方法:</strong>在手机拨号盘输入 <code>*#06#</code>,如显示 EID 号码则支持 eSIM</p>
</section>
<!-- 使用教程 -->
<section class="info-section reveal" aria-labelledby="tutorial-title">
<h2 id="tutorial-title" class="section-title"><i class="fas fa-book-open" aria-hidden="true"></i> <span class="tutorial-title-text">使用教程</span></h2>
<div class="tutorial-grid">
<div class="tutorial-card giffgaff-tutorial">
<h3>Giffgaff eSIM 激活流程</h3>
<ol class="tutorial-steps">
<li>点击「使用 Giffgaff 工具」进入工具页面</li>
<li>使用 Giffgaff 账号完成 OAuth 登录授权</li>
<li>输入邮箱验证码完成身份验证</li>
<li>完成 MFA 多因素认证(如已启用)</li>
<li>查询会员资格并选择 eSIM 操作</li>
<li>获取 eSIM 下载码或扫描 QR 码激活</li>
</ol>
<p class="tutorial-note"><i class="fas fa-clock" aria-hidden="true"></i> 激活时间窗口UK 时间 4:30am - 9:30pm</p>
</div>
<div class="tutorial-card simyo-tutorial">
<h3>Simyo eSIM 管理流程</h3>
<ol class="tutorial-steps">
<li>点击「使用 Simyo 工具」进入工具页面</li>
<li>使用 Simyo 账号完成登录验证</li>
<li>选择设备更换或 eSIM 激活</li>
<li>通过 SMS 短信或 iDEAL 支付验证身份</li>
<li>确认新设备并生成 eSIM 配置</li>
</ol>
<p class="tutorial-note"><i class="fas fa-info-circle" aria-hidden="true"></i> 本工具支持生成 eSIM QR 码,简化激活流程</p>
</div>
</div>
</section>
<!-- 与官方流程对比 -->
<section class="info-section reveal" aria-labelledby="compare-title">
<h2 id="compare-title" class="section-title"><i class="fas fa-balance-scale" aria-hidden="true"></i> <span class="compare-title-text">与官方 App 对比</span></h2>
<div class="compare-table-wrapper">
<table class="compare-table">
<thead>
<tr>
<th>对比项</th>
<th>本工具</th>
<th>官方 App</th>
</tr>
</thead>
<tbody>
<tr>
<td>操作平台</td>
<td><i class="fas fa-check text-success"></i> 浏览器 Web 端</td>
<td>仅移动端 App</td>
</tr>
<tr>
<td>QR 码生成</td>
<td><i class="fas fa-check text-success"></i> 自动生成</td>
<td>Simyo 不支持(本工具可生成)</td>
</tr>
<tr>
<td>认证方式</td>
<td>OAuth 2.0 PKCE</td>
<td>App 内置登录</td>
</tr>
<tr>
<td>官方支持</td>
<td><i class="fas fa-exclamation-triangle text-warning"></i> 非官方</td>
<td><i class="fas fa-check text-success"></i> 官方支持</td>
</tr>
<tr>
<td>服务条款</td>
<td><i class="fas fa-exclamation-triangle text-warning"></i> 可能违反</td>
<td><i class="fas fa-check text-success"></i> 符合条款</td>
</tr>
</tbody>
</table>
</div>
</section>
<!-- 地区与时间限制 -->
<section class="info-section reveal" aria-labelledby="region-title">
<h2 id="region-title" class="section-title"><i class="fas fa-globe-europe" aria-hidden="true"></i> <span class="region-title-text">地区与时间限制</span></h2>
<div class="region-grid">
<div class="region-card">
<h3><i class="fas fa-clock" aria-hidden="true"></i> Giffgaff</h3>
<p>激活时间窗口:<strong>UK 时间 4:30am - 9:30pm</strong></p>
<p>请在非工作时间内尝试激活可能失败</p>
</div>
<div class="region-card">
<h3><i class="fas fa-map-marker-alt" aria-hidden="true"></i> Simyo</h3>
<p>服务地区:<strong>荷兰 (NL)</strong></p>
<p>仅支持荷兰本地 SIM 卡用户</p>
</div>
</div>
</section>
<!-- 技术术语解释 -->
<section class="info-section reveal" aria-labelledby="glossary-title">
<h2 id="glossary-title" class="section-title"><i class="fas fa-book" aria-hidden="true"></i> <span class="glossary-title-text">技术术语说明</span></h2>
<div class="glossary-grid">
<div class="glossary-item">
<p class="glossary-term">OAuth 2.0 PKCE</p>
<p class="glossary-desc">一种安全的授权协议,允许第三方应用在无需存储密钥的情况下访问用户数据</p>
</div>
<div class="glossary-item">
<p class="glossary-term">GraphQL</p>
<p class="glossary-desc">一种 API 查询语言,比 REST 更灵活高效地获取所需数据</p>
</div>
<div class="glossary-item">
<p class="glossary-term">LPA String</p>
<p class="glossary-desc">本地配置文件激活字符串,包含 eSIM 下载所需的服务器地址和激活码</p>
</div>
<div class="glossary-item">
<p class="glossary-term">SM-DP+</p>
<p class="glossary-desc">订阅管理数据准备服务器,负责安全地存储和分发 eSIM 配置文件</p>
</div>
<div class="glossary-item">
<p class="glossary-term">MFA</p>
<p class="glossary-desc">多因素认证,通过多种方式验证用户身份,提高账户安全性</p>
</div>
<div class="glossary-item">
<p class="glossary-term">EID</p>
<p class="glossary-desc">eSIM 的唯一标识号码,可在拨号盘输入 *#06# 查看</p>
</div>
</div>
</section>
<!-- FAQ 常见问题 -->
<section class="info-section reveal" aria-labelledby="faq-title">
<h2 id="faq-title" class="section-title"><i class="fas fa-question-circle" aria-hidden="true"></i> <span class="faq-title-text">常见问题</span></h2>
<div class="faq-list">
<details class="faq-item">
<summary>无法接收 SMS 短信验证码怎么办?</summary>
<p>对于 Simyo 用户,可以选择 iDEAL 支付方式作为替代验证方案。如果仍然无法接收短信,请检查手机号码是否正确,或联系运营商确认短信服务状态。</p>
</details>
<details class="faq-item">
<summary>eSIM 激活需要多长时间?</summary>
<p>通常在几分钟内完成,最长可能需要 1 小时。如果超过 1 小时仍未激活成功,请检查网络连接或重新尝试。</p>
</details>
<details class="faq-item">
<summary>是否支持在海外激活 eSIM</summary>
<p>Giffgaff 的 eSIM 激活有 UK 时间窗口限制4:30am - 9:30pm UK 时间。Simyo 主要面向荷兰本地用户,海外激活可能受限。</p>
</details>
<details class="faq-item">
<summary>Simyo 支持 QR 码激活吗?</summary>
<p>Simyo 官方 App 不支持 QR 码激活,但本工具可以生成 eSIM QR 码,让您通过扫描方式快速完成激活,无需在 App 内操作。</p>
</details>
<details class="faq-item">
<summary>使用这个工具安全吗?</summary>
<p>本工具在本地浏览器运行,所有数据处理均在本地进行。但请注意:使用第三方工具可能违反运营商服务条款,建议使用非主力号码进行测试。</p>
</details>
<details class="faq-item">
<summary>激活失败怎么办?</summary>
<p>请检查1) 设备是否支持 eSIM2) 网络连接是否正常3) 是否在运营商服务时间内4) 账号状态是否正常。如问题持续,请联系运营商客服。</p>
</details>
</div>
</section>
<!-- 安全声明 -->
<section class="info-section reveal" aria-labelledby="disclaimer-title">
<h2 id="disclaimer-title" class="section-title"><i class="fas fa-shield-alt" aria-hidden="true"></i> <span class="disclaimer-title-text">安全声明</span></h2>
<div class="disclaimer-content">
<div class="disclaimer-item warning">
<i class="fas fa-exclamation-triangle" aria-hidden="true"></i>
<div>
<strong>第三方工具风险</strong>
<p>本工具为非官方第三方工具,使用可能违反运营商服务条款。请自行评估风险。</p>
</div>
</div>
<div class="disclaimer-item info">
<i class="fas fa-info-circle" aria-hidden="true"></i>
<div>
<strong>建议使用非主力号码</strong>
<p>为避免影响正常通信,建议使用备用号码进行测试和激活操作。</p>
</div>
</div>
<div class="disclaimer-item success">
<i class="fas fa-lock" aria-hidden="true"></i>
<div>
<strong>数据安全</strong>
<p>所有数据处理均在本地浏览器完成,不上传任何敏感信息到服务器。</p>
</div>
</div>
</div>
</section>
<footer class="footer" aria-label="免责声明与隐私说明">
<p>
<i class="fas fa-info-circle me-2" aria-hidden="true"></i>
本工具仅供个人使用,请遵守相关服务条款。<br>
<i class="fas fa-shield-alt me-2" aria-hidden="true"></i>
所有数据处理均在本地进行,保护您的隐私安全。
</p>
</footer>
</div>
<!-- 将 bootstrap 脚本 defer减少阻塞 -->
<script defer src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<!-- 首页逻辑脚本(无内联) -->
<script defer src="/src/js/home.js"></script>
<!-- 统一版权页脚(自动注入年份/品牌) -->
<script type="module" src="/src/js/bootstrap-footer.js"></script>
<!-- 国际化初始化 -->
<script type="module" src="/src/js/init-home-i18n.js"></script>
<!-- 通知系统初始化 -->
<script type="module" src="/src/js/init-notification.js"></script>
<!-- 性能优化脚本 -->
<script src="/src/js/performance.js"></script>
<!-- WebMCP: 向 AI Agent 暴露站点工具 -->
<script src="/src/js/webmcp.js" defer></script>
</body>
</html>