Files
eSIM-Tools/src/giffgaff/giffgaff_modular.html
Abner 8bd2bdb8a4 ♻️ refactor: 提取初始化脚本为独立模块并清理过时架构文档
- 将 giffgaff 和 Simyo 页面的国际化初始化内联脚本提取为独立的 `init-i18n.js` 模块
- 将通知系统初始化内联脚本提取为独立的 `init-notification.js` 模块
- 移除 docs 目录下的 ARCHITECTURE.md 和 FUTURE_ARCHITECTURE.md 过期架构文档
- 更新 giffgaff_modular.html 和 simyo_modular.html 以引用新的独立脚本模块
2026-06-13 21:58:09 +08:00

890 lines
54 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="giffgaff">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="light dark" />
<meta name="description" content="Giffgaff eSIM 工具,支持 OAuth 登录、MFA 验证、二维码生成与设备更换。">
<!-- Sentry 错误监控配置(必须在其他脚本之前) -->
<!-- 使用外链脚本以符合 CSP 策略 -->
<script src="/src/js/sentry-config.js"></script>
<script src="/src/js/sentry-loader.js"></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://qrcode.show" crossorigin>
<link rel="preconnect" href="https://api.giffgaff.com" crossorigin>
<link rel="preconnect" href="https://id.giffgaff.com" crossorigin>
<link rel="preconnect" href="https://publicapi.giffgaff.com" crossorigin>
<!-- CSP策略 -->
<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.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://cdnjs.cloudflare.com 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://www.googletagmanager.com https://qrcode.show https://api.qrserver.com https://appapi.simyo.nl https://api.giffgaff.com https://id.giffgaff.com https://publicapi.giffgaff.com https://www.google.com https://www.gstatic.com https://*.sentry.io; img-src 'self' data: https:; frame-src 'self' https://www.google.com;">
<title>Giffgaff eSIM 工具</title>
<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#ffcc00">
<link rel="icon" href="/src/assets/favicon.ico" type="image/x-icon">
<!-- 外部样式 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
<link rel="stylesheet" href="/src/assets/fontawesome/all.min.css">
<!-- 共享设计系统 -->
<link rel="stylesheet" href="/src/styles/design-system.css">
<link rel="stylesheet" href="/src/styles/animations.css">
<!-- Giffgaff专用样式 -->
<link rel="stylesheet" href="/src/giffgaff/styles/giffgaff-base.css">
<link rel="stylesheet" href="/src/giffgaff/styles/giffgaff-components.css">
<link rel="stylesheet" href="/src/giffgaff/styles/giffgaff-service-time.css">
<link rel="stylesheet" href="/src/giffgaff/styles/giffgaff-animations.css">
<link rel="stylesheet" href="/src/giffgaff/styles/giffgaff-responsive.css">
<link rel="stylesheet" href="/src/styles/notification.css">
</head>
<body aria-label="Giffgaff eSIM 工具">
<div class="container main-container">
<!-- 应用头部 -->
<div class="app-header">
<div class="logo-container">
<i class="fas fa-sim-card app-icon" aria-hidden="true"></i>
<div class="app-title">
<span class="lang-zh">Giffgaff eSIM工具</span>
<span class="lang-en">Giffgaff eSIM Tool</span>
</div>
</div>
<div class="header-buttons">
<div class="language-switcher">
<button id="languageSwitcher" class="lang-switch" type="button" aria-label="切换语言到英文">🌐 English</button>
</div>
<a id="tutorialBtn" class="login-btn" href="https://github.com/Silentely/eSIM-Tools/blob/main/docs/User_Guide.md" target="_blank" rel="noopener noreferrer" aria-label="查看教程">
<i class="fas fa-book" aria-hidden="true"></i>
<span class="lang-zh">查看教程</span>
<span class="lang-en">View Tutorial</span>
</a>
<a href="https://github.com/Silentely/eSIM-Tools" target="_blank" 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="通过 Ko-fi 赞助我">
<i class="fas fa-coffee" aria-hidden="true"></i> <span>赞助我</span>
</a>
</div>
</div>
<!-- 新用户提示 -->
<div class="new-user-notice reveal">
<span class="lang-zh">
<span class="notice-line1"><i class="fas fa-exclamation-circle" aria-hidden="true"></i> 本工具仅支持已有 Giffgaff 号码的用户,</span>
<span class="notice-line2">支持实体卡转 eSIM、eSIM 换设备、已支付 £10 直取 eSIM。新用户未有号码无法使用如需开卡请点击下方链接。</span>
</span>
<span class="lang-en">
<span class="notice-line1"><i class="fas fa-exclamation-circle" aria-hidden="true"></i> This tool supports existing Giffgaff users only:</span>
<span class="notice-line2">SIM-to-eSIM conversion, eSIM device change, and direct eSIM fetch for £10 paid users. New users without a number cannot use it. Click the link below to get started.</span>
</span>
</div>
<!-- 开卡推广 -->
<div class="promo-banner reveal">
<div class="promo-content">
<i class="fas fa-gift promo-icon"></i>
<span class="promo-text">
<span class="lang-zh">
如果您还没有Giffgaff账户可以
<a href="https://www.giffgaff.com/orders/affiliate/mowal44_1653194386268" target="_blank" class="promo-link">
点击这里开卡
</a>
享受额外5英镑话费赠送
</span>
<span class="lang-en">
New to Giffgaff?
<a href="https://www.giffgaff.com/orders/affiliate/mowal44_1653194386268" target="_blank" class="promo-link">
order here
</a>
to receive an extra £5 credit!
</span>
</span>
</div>
</div>
<!-- 页面标题 -->
<div class="page-title reveal">
<h1>
<span class="lang-zh">Giffgaff eSIM 工具</span>
<span class="lang-en">Giffgaff eSIM Tool</span>
</h1>
<p>
<span class="lang-zh">将您的实体 SIM 卡转换为 eSIM或在设备间切换 eSIM</span>
<span class="lang-en">Convert your physical SIM to eSIM, or switch eSIM between devices</span>
</p>
<div id="modeBadge" class="mode-badge" style="display:none" aria-live="polite">
<i class="fas fa-exchange-alt" aria-hidden="true"></i>
<span class="lang-zh">设备更换模式</span>
<span class="lang-en">Device change mode</span>
</div>
</div>
<!-- 服务时间提示 -->
<div id="serviceTimeAlert" class="alert mb-4 service-time-alert">
<div class="service-time-grid">
<div class="service-time-left">
<div class="service-time-header">
<div id="actionMessage" class="service-time-action-badge" style="display: none;"></div>
</div>
<div class="service-time-body">
<div class="service-time-layout">
<div class="service-time-icon-section">
<i id="serviceTimeIcon" class="fas" aria-hidden="true"></i>
</div>
<div class="service-time-content-section">
<div class="service-time-title">
<span class="lang-zh">服务时间提醒:</span>
<span class="lang-en">Service window reminder:</span>
</div>
<div id="serviceTimeMessage" class="service-time-message">
<span class="lang-zh">当前时间在服务时间外Giffgaff官方在英国时间04:30至21:30之间提供SIM交换服务。</span>
<span class="lang-en">Outside of service hours. Giffgaff processes SIM swaps between 04:30 and 21:30 UK time.</span>
</div>
</div>
</div>
</div>
</div>
<div class="current-time-card" aria-label="当前时间">
<div class="time-card-icon">
<i class="fas fa-clock" aria-hidden="true"></i>
</div>
<div class="time-card-text">
<div class="time-card-label">
<span class="lang-zh">当前时间</span>
<span class="lang-en">Current time</span>
</div>
<div id="currentTime" class="time-card-value">--:--</div>
<div id="ukTimeHint" class="time-card-hint">
<span class="lang-zh">英国时间 --:--</span>
<span class="lang-en">UK time --:--</span>
</div>
</div>
</div>
</div>
</div>
<!-- 状态显示面板 -->
<div class="status-panel card-elevated reveal" role="status" aria-live="polite">
<details open>
<summary class="d-flex align-items-center justify-content-between" style="cursor: pointer; list-style: none;">
<h3>
<i class="fas fa-info-circle" aria-hidden="true"></i>
<span class="lang-zh">当前状态</span>
<span class="lang-en">Current status</span>
</h3>
<i class="fas fa-chevron-down text-muted"></i>
</summary>
<div class="status-grid mt-3">
<div class="status-item">
<span class="status-label">
<span class="lang-zh">当前模式:</span>
<span class="lang-en">Current mode:</span>
</span>
<span id="statusMode" class="status-value connected">标准流程</span>
</div>
<div class="status-item">
<span class="status-label">
<span class="lang-zh">Access Token:</span>
<span class="lang-en">Access Token:</span>
</span>
<span id="statusAccessToken" class="status-value disconnected">未登录</span>
</div>
<div class="status-item">
<span class="status-label">
<span class="lang-zh">MFA 签名:</span>
<span class="lang-en">MFA signature:</span>
</span>
<span id="statusMfaSignature" class="status-value disconnected">未验证</span>
</div>
<div class="status-item">
<span class="status-label">
<span class="lang-zh">会员ID:</span>
<span class="lang-en">Member ID:</span>
</span>
<span id="statusMemberId" class="status-value disconnected">未获取</span>
</div>
<div class="status-item">
<span class="status-label">
<span class="lang-zh">eSIM状态:</span>
<span class="lang-en">eSIM status:</span>
</span>
<span id="statusEsimStatus" class="status-value disconnected">
<span id="esimStatusValue"></span><br>
<span id="esimStatusPhoneSuffix"></span>
</span>
</div>
<div class="status-item">
<span class="status-label">
<span class="lang-zh">激活码:</span>
<span class="lang-en">Activation code:</span>
</span>
<span id="statusActivationCode" class="status-value disconnected">
<span id="activationCodeValue"></span><br>
<span id="activationCodeSSN"></span>
</span>
</div>
<div class="status-item">
<span class="status-label">
<span class="lang-zh">LPA字符串:</span>
<span class="lang-en">LPA string:</span>
</span>
<span id="statusLpaString" class="status-value disconnected">未生成</span>
</div>
</div>
<button id="clearSessionBtn" class="clear-session-btn" aria-label="清除会话并重置所有进度">
<i class="fas fa-trash-alt" aria-hidden="true"></i>
<span class="lang-zh">清除会话</span>
<span class="lang-en">Clear session</span>
</button>
</details>
</div>
<!-- 步骤指示器 -->
<div class="step-indicator reveal" role="list" aria-label="进度步骤">
<div class="step active" role="listitem" aria-current="step" data-step="1">
<div class="step-number" aria-hidden="true">1</div>
<div class="step-text">
<span class="lang-zh">OAuth登录</span>
<span class="lang-en">OAuth login</span>
</div>
</div>
<div class="step" data-step="2">
<div class="step-number">2</div>
<div class="step-text">
<span class="lang-zh">邮件验证</span>
<span class="lang-en">Email verification</span>
</div>
</div>
<div class="step" data-step="3">
<div class="step-number">3</div>
<div class="step-text">
<span class="lang-zh">会员信息</span>
<span class="lang-en">Member info</span>
</div>
</div>
<div class="step" data-step="4">
<div class="step-number">4</div>
<div class="step-text">
<span class="lang-zh">转换 eSIM</span>
<span class="lang-en">Reserve eSIM</span>
</div>
</div>
<div class="step" data-step="5">
<div class="step-number">5</div>
<div class="step-text">
<span class="lang-zh">获取二维码</span>
<span class="lang-en">Get QR code</span>
</div>
</div>
</div>
<!-- 步骤1: 选择登录方式 -->
<div id="step1" class="section active reveal">
<div class="card card-elevated">
<div class="card-header">
<i class="fas fa-key"></i>
<span class="lang-zh"> 第一步:选择登录方式</span>
<span class="lang-en"> Step 1: Choose a login method</span>
</div>
<div class="card-body">
<p class="mb-4 lang-zh" style="font-size: 17px; color: #212529;">
请选择您偏好的登录方式:
</p>
<p class="mb-4 lang-en" style="font-size: 17px; color: #212529;">
Pick the login method you prefer:
</p>
<div class="row row-cols-1 row-cols-md-2 g-3">
<div class="col">
<div id="oauthCard" class="card h-100" style="cursor: pointer;" role="button" tabindex="0" aria-label="选择 OAuth 登录">
<div class="card-body text-center">
<i class="fas fa-shield-alt fa-3x mb-3" style="color: var(--primary);"></i>
<h5>
<span class="lang-zh">OAuth 2.0 登录</span>
<span class="lang-en">OAuth 2.0 login</span>
</h5>
<p class="text-muted">
<span class="lang-zh">安全的官方认证方式</span>
<span class="lang-en">Secure official authentication</span>
</p>
<small class="text-success">
<span class="lang-zh">✓ 推荐使用</span>
<span class="lang-en">✓ Recommended</span>
</small>
</div>
</div>
</div>
<div class="col">
<div id="cookieCard" class="card h-100 disabled" style="cursor: not-allowed; opacity: 0.5; pointer-events: none; background-color: #f8f9fa;" aria-label="Cookie 登录暂不可用" aria-disabled="true">
<div class="card-body text-center">
<i class="fas fa-cookie-bite fa-3x mb-3" style="color: #adb5bd;"></i>
<h5 style="color: #6c757d;">
<span class="lang-zh">Cookie 登录</span>
<span class="lang-en">Cookie login</span>
</h5>
<p class="text-muted">
<span class="lang-zh">使用已有登录Cookie</span>
<span class="lang-en">Use an existing session cookie</span>
</p>
<small class="text-danger">
<span class="lang-zh">⚠ 暂无法使用</span>
<span class="lang-en">⚠ Temporarily unavailable</span>
</small>
</div>
</div>
</div>
</div>
<div id="loginMethodStatus" class="status"></div>
<!-- OAuth登录区域 -->
<div id="oauthLoginSection" class="mt-4" style="display: none;">
<h5 class="mb-3">
<span class="lang-zh">OAuth 2.0 登录</span>
<span class="lang-en">OAuth 2.0 login</span>
</h5>
<div class="alert alert-danger mb-4">
<h6 class="lang-zh"><i class="fas fa-exclamation-triangle me-2"></i>⚠️ 重要OAuth 登录前置条件</h6>
<h6 class="lang-en"><i class="fas fa-exclamation-triangle me-2"></i>⚠️ Important: OAuth Login Prerequisites</h6>
<div class="lang-zh">
<p class="mb-2"><strong>在点击下方"开始OAuth登录"按钮之前,您必须先完成以下步骤:</strong></p>
<ol class="mb-2">
<li><strong>访问 <a href="https://www.giffgaff.com" target="_blank" rel="noopener noreferrer" class="text-dark text-decoration-underline">giffgaff.com</a></strong> 并登录您的账户</li>
<li><strong>确保登录状态有效</strong>(可以看到您的账户信息)</li>
<li><strong>保持该标签页打开</strong>,然后返回本页面</li>
</ol>
<p class="mb-0"><small><i class="fas fa-info-circle me-1"></i><strong>为什么需要这样做?</strong> Giffgaff 现在要求用户必须先在官网登录,才能通过 OAuth 授权。如果未登录就点击 OAuth 登录,将无法获取回调 URL。</small></p>
</div>
<div class="lang-en">
<p class="mb-2"><strong>Before clicking "Start OAuth Login" below, you MUST complete these steps first:</strong></p>
<ol class="mb-2">
<li><strong>Visit <a href="https://www.giffgaff.com" target="_blank" rel="noopener noreferrer" class="text-dark text-decoration-underline">giffgaff.com</a></strong> and log in to your account</li>
<li><strong>Ensure your login session is active</strong> (you can see your account info)</li>
<li><strong>Keep that tab open</strong>, then return to this page</li>
</ol>
<p class="mb-0"><small><i class="fas fa-info-circle me-1"></i><strong>Why is this required?</strong> Giffgaff now requires users to be logged in on their website before OAuth authorization. Without prior login, you won't be able to capture the callback URL.</small></p>
</div>
</div>
<div class="alert alert-warning mb-4">
<h6 class="lang-zh"><i class="fas fa-laptop-code me-2"></i>如何获取回调URL</h6>
<h6 class="lang-en"><i class="fas fa-laptop-code me-2"></i>How to capture the callback URL:</h6>
<ol class="mb-2 lang-zh">
<li><strong>打开开发者工具</strong>:按 F12 或右键选择"检查"</li>
<li><strong>切换到"控制台"Console标签页</strong></li>
<li><strong>点击下方"开始OAuth登录"按钮</strong>新窗口打开后切换回本页面eSIM-Tools 工具页面)</li>
<li><strong>在控制台中查找</strong>错误信息:<code>Failed to launch 'giffgaff://auth/callback/...'</code></li>
<li><strong>复制完整的回调URL</strong>(从 <code>giffgaff://</code> 开始到末尾)并粘贴到下方输入框</li>
</ol>
<ol class="mb-2 lang-en">
<li><strong>Open DevTools</strong>: press F12 or right-click → "Inspect"</li>
<li><strong>Switch to the "Console" tab</strong></li>
<li><strong>Click "Start OAuth Login" below</strong> After the new window opens, switch back to this page (the eSIM-Tools page).</li>
<li><strong>Look for the error message</strong> in Console: <code>Failed to launch 'giffgaff://auth/callback/...'</code></li>
<li><strong>Copy the full callback URL</strong> (from <code>giffgaff://</code> to the end) and paste it into the input field below</li>
</ol>
<p class="mb-0 lang-zh"><small class="text-muted">💡 提示如果控制台没有显示回调URL说明您未在 giffgaff.com 登录。请退出并重新登录 giffgaff.com然后重新开始 OAuth 流程。</small></p>
<p class="mb-0 lang-en"><small class="text-muted">💡 Tip: If the callback URL doesn't appear in Console, you're not logged in at giffgaff.com. Please log out and log back in at giffgaff.com, then restart the OAuth flow.</small></p>
</div>
<div class="alert alert-info mb-4">
<h6 class="lang-zh"><i class="fas fa-info-circle me-2"></i>回调URL格式示例</h6>
<h6 class="lang-en"><i class="fas fa-info-circle me-2"></i>Example callback URL:</h6>
<div class="lang-zh">
<p class="mb-2"><strong>控制台错误信息示例:</strong></p>
<p class="mb-2"><code>Failed to launch 'giffgaff://auth/callback/?code=Na22kPkMt3JoLW-w&state=TGo2ljM-2HRt21DaVBxYuw' because the scheme does not have a registered handler.</code></p>
<p class="mb-2"><strong>您需要复制的部分:</strong></p>
<p class="mb-2"><code>giffgaff://auth/callback/?code=Na22kPkMt3JoLW-w&state=TGo2ljM-2HRt21DaVBxYuw</code></p>
<p class="mb-0"><small class="text-muted">✓ 确保URL包含 <code>code</code><code>state</code> 参数</small></p>
</div>
<div class="lang-en">
<p class="mb-2"><strong>Example Console error message:</strong></p>
<p class="mb-2"><code>Failed to launch 'giffgaff://auth/callback/?code=Na22kPkMt3JoLW-w&state=TGo2ljM-2HRt21DaVBxYuw' because the scheme does not have a registered handler.</code></p>
<p class="mb-2"><strong>What you need to copy:</strong></p>
<p class="mb-2"><code>giffgaff://auth/callback/?code=Na22kPkMt3JoLW-w&state=TGo2ljM-2HRt21DaVBxYuw</code></p>
<p class="mb-0"><small class="text-muted">✓ Make sure the URL includes the <code>code</code> and <code>state</code> parameters</small></p>
</div>
</div>
<div class="btn-group">
<button id="oauthLoginBtn" class="btn btn-primary">
<i class="fas fa-sign-in-alt me-2"></i>
<span class="lang-zh">开始OAuth登录</span>
<span class="lang-en">Start OAuth login</span>
</button>
</div>
<div id="oauthStatus" class="status" role="status" aria-live="polite"></div>
<!-- OAuth回调处理区域 -->
<div id="oauthCallbackSection" class="verification-section">
<div class="form-floating mb-4">
<textarea id="callbackUrl" class="form-control" placeholder="粘贴从控制台复制的完整回调URL..." style="height: 100px;"></textarea>
<label for="callbackUrl">
<span class="lang-zh">回调URL从控制台复制</span>
<span class="lang-en">Callback URL (from Console)</span>
</label>
</div>
<div class="btn-group">
<button id="processCallbackBtn" class="btn btn-primary">
<i class="fas fa-check me-2"></i>
<span class="lang-zh">处理回调</span>
<span class="lang-en">Process callback</span>
</button>
</div>
<div id="callbackStatus" class="status mt-4" role="status" aria-live="polite"></div>
</div>
</div>
<!-- Cookie登录区域 -->
<div id="cookieLoginSection" class="mt-4" style="display: none;">
<h5 class="mb-3">Cookie 登录</h5>
<div class="alert alert-info mb-4">
<h6><i class="fas fa-info-circle me-2"></i>如何获取Cookie</h6>
<ol class="mb-2">
<li><strong>访问</strong> <a href="https://www.giffgaff.com" target="_blank" rel="noopener">giffgaff.com</a> 并登录您的账户</li>
<li><strong>打开开发者工具</strong>ChromeF12 或 右键→检查)</li>
<li>切换到 <strong>Application</strong>(应用)/ <strong>Storage</strong>(存储)标签页</li>
<li>在左侧选择 <strong>Cookies</strong><code>https://www.giffgaff.com</code></li>
<li>复制右侧所有条目,拼成 <code>name=value; name2=value2; ...</code> 格式</li>
</ol>
<div class="mt-2">
<strong></strong> 在已登录的 <code>giffgaff.com</code> 页签,打开控制台(Console)粘贴以下代码并回车:
<pre id="cookieConsoleSnippet" class="mt-2 p-2 bg-light border rounded" style="white-space:pre-wrap;word-break:break-all;">
;(() => {
try {
const out = document.cookie.split('; ').filter(Boolean).join('; ');
console.log(out);
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(out)
.then(() => console.log('Cookie 已复制到剪贴板'))
.catch(() => {});
}
} catch (e) {
console.log('读取 Cookie 失败:', e);
}
})();
</pre>
<div class="text-end mt-2">
<button id="copyConsoleSnippetBtn" type="button" class="btn btn-sm btn-warning text-dark">
<i class="fas fa-copy me-1"></i> 复制上方代码
</button>
</div>
</div>
<div class="alert alert-warning p-2 mt-2">
<small>
<i class="fas fa-exclamation-triangle me-1"></i>
安全提示:仅在可信设备上粘贴 Cookie。本工具不会存储您的 Cookie。
</small>
</div>
</div>
<div class="form-floating mb-4">
<textarea id="cookieInput" class="form-control" placeholder="输入完整的Cookie字符串" style="height: 120px;"></textarea>
<label for="cookieInput">Cookie 字符串</label>
</div>
<div class="btn-group">
<button id="verifyCookieBtn" class="btn btn-warning">
<i class="fas fa-check-circle me-2"></i> 验证Cookie
</button>
</div>
<div id="cookieStatus" class="status"></div>
</div>
</div>
</div>
</div>
<!-- 步骤2: 邮件验证 -->
<div id="step2" class="section reveal">
<div class="card card-elevated">
<div class="card-header">
<i class="fas fa-envelope"></i> 第二步:验证码验证(邮件/短信)
</div>
<div class="card-body">
<p class="mb-4" style="font-size: 17px; color: #212529;">
我们需要发送验证码到您的邮箱或手机进行身份验证:
</p>
<div class="row g-3 align-items-end mb-2">
<div class="col-sm-6">
<label for="mfaChannelSelect" class="form-label">验证码接收方式</label>
<select id="mfaChannelSelect" class="form-select">
<option value="EMAIL" selected>邮箱EMAIL</option>
<option value="TEXT">短信TEXT</option>
</select>
</div>
</div>
<div class="btn-group">
<button id="sendEmailBtn" class="btn btn-primary">
<i class="fas fa-paper-plane me-2"></i> 发送验证码
</button>
</div>
<div id="emailStatus" class="status" role="status" aria-live="polite"></div>
<!-- 验证码输入区域 -->
<div id="emailVerificationSection" class="verification-section">
<p class="mb-4" style="font-size: 17px; color: #212529; margin-top: 25px;">
请检查您的邮箱或短信并输入收到的6位验证码
</p>
<div class="form-floating mb-4">
<input type="text" id="emailCode" class="form-control" placeholder="输入6位验证码" maxlength="6">
<label for="emailCode">验证码</label>
</div>
<div class="btn-group">
<button id="verifyEmailBtn" class="btn btn-primary">
<i class="fas fa-check me-2"></i> 验证验证码
</button>
</div>
<div id="emailVerifyStatus" class="status mt-4" role="status" aria-live="polite"></div>
</div>
</div>
</div>
</div>
<!-- 步骤3: 获取会员信息 -->
<div id="step3" class="section reveal">
<div class="card card-elevated">
<div class="card-header">
<i class="fas fa-user"></i> 第三步:获取会员信息
</div>
<div class="card-body">
<!-- 操作前必读提示 -->
<div class="alert alert-danger mb-4" style="text-align: left; line-height: 1.8; border-left: 4px solid #dc2626; background-color: #fef2f2;">
<p class="mb-2" style="margin-bottom: 8px; font-weight: bold; font-size: 16px; color: #dc2626;">
<i class="fas fa-exclamation-circle me-2"></i>
操作前请确认
</p>
<p class="mb-2" style="margin-bottom: 8px;">本工具<strong>仅支持已有 Giffgaff 号码</strong>的用户,请在点击下方按钮前确认:</p>
<ul class="mb-2" style="margin-left: 20px; margin-bottom: 8px;">
<li><strong>实体卡转 eSIM</strong>(需已有实体 SIM 卡)</li>
<li><strong>eSIM 更换设备</strong>(需已有 eSIM 或实体卡号码)</li>
<li><strong>已支付 £10 直取 eSIM</strong>(已在官方 App 完成付款,直接拉取)</li>
</ul>
<p class="mb-2" style="margin-bottom: 8px; padding: 8px 12px; background: rgba(220, 38, 38, 0.1); border-radius: 4px;">
<i class="fas fa-times-circle me-2" style="color: #dc2626;"></i>
<strong>新用户(未有号码)无法使用本工具</strong>
</p>
<p class="mb-0">如果您还没有 Giffgaff 号码,可以
<a href="https://www.giffgaff.com/orders/affiliate/mowal44_1653194386268" target="_blank" rel="noopener noreferrer"
style="color: #dc2626; text-decoration: underline; font-weight: bold;">
点击这里开卡 <i class="fas fa-external-link-alt" style="font-size: 0.85em;"></i>
</a>
享受额外5英镑话费赠送
</p>
</div>
<p class="mb-4" style="font-size: 17px; color: #212529;">
获取您的Giffgaff会员信息
</p>
<div class="btn-group">
<button id="getMemberBtn" class="btn btn-primary">
<i class="fas fa-user-circle me-2"></i> 获取会员信息
</button>
</div>
<div id="memberStatus" class="status" role="status" aria-live="polite"></div>
<div id="memberInfo" class="mt-4"></div>
</div>
</div>
</div>
<!-- 步骤4: 转换 eSIM -->
<div id="step4" class="section reveal">
<div class="card card-elevated">
<div class="card-header">
<i class="fas fa-sim-card"></i> 第四步:转换/激活 eSIM
</div>
<div class="card-body">
<p class="mb-4" style="font-size: 17px; color: #212529;">
请选择一种方式完成 eSIM 激活:
</p>
<div class="row row-cols-1 row-cols-md-3 g-3">
<div class="col">
<div id="manualActivateCard" class="card h-100" style="cursor:not-allowed; opacity: 0.6;" role="button" tabindex="-1" aria-label="手动激活(已停用)">
<div class="card-body text-center">
<i class="fas fa-hand-point-up fa-2x mb-2"></i>
<h5 class="fw-bold">手动激活</h5>
<p class="small text-muted mb-0">实体卡转 eSIM 或设备更换此为预订空eSIM需手动前往官网确认激活。</p>
<div class="text-center mt-3 badge-spacer">
<span class="badge bg-danger badge-pill-cta" style="pointer-events: none;">暂停使用</span>
<div class="mt-2 small text-danger">
<i class="fas fa-info-circle me-1"></i>
<span class="lang-zh">官方已暂停此通道,请使用短信激活</span>
<span class="lang-en">Channel paused by official, use SMS activation</span>
</div>
</div>
</div>
</div>
</div>
<div class="col">
<div id="smsActivateCard" class="card h-100" style="cursor:pointer;" role="button" tabindex="0" aria-label="选择短信验证码激活">
<div class="card-body text-center">
<i class="fas fa-sms fa-2x mb-2"></i>
<h5 class="fw-bold">短信验证码激活</h5>
<p class="small text-muted mb-0">实体卡转 eSIM 或设备更换,输入验证码即可完成。</p>
<div class="text-center mt-3 badge-spacer">
<span class="badge bg-success text-white badge-pill-cta">✓ 推荐使用</span>
</div>
</div>
</div>
</div>
<div class="col">
<div id="directFetchCard" class="card h-100" style="cursor:pointer;" role="button" tabindex="0"
aria-label="已支付直取 eSIM">
<div class="card-body text-center">
<i class="fas fa-bolt fa-2x mb-2"></i>
<h5 class="fw-bold">
<span class="lang-zh">已支付直取 eSIM</span>
<span class="lang-en">Fetch eSIM directly</span>
</h5>
<p class="small text-muted mb-0">
<span class="lang-zh">已在官方 App 完成 £10 付款但未收到 eSIM可直接拉取。</span>
<span class="lang-en">Paid £10 in the official app but no eSIM received? Fetch it directly.</span>
</p>
<div class="text-center mt-3 badge-spacer">
<span class="badge bg-info badge-pill-cta">
<span class="lang-zh">已付款用户专用</span>
<span class="lang-en">For paid users</span>
</span>
</div>
</div>
</div>
</div>
</div>
<!-- 短信激活内联区域 -->
<div id="smsInlineSection" style="display:none;">
<div class="card card-body mt-3">
<div class="d-flex justify-content-between align-items-end flex-wrap gap-3">
<div style="min-width:260px;">
<label for="smsInlineChannel" class="form-label">验证码接收方式</label>
<select id="smsInlineChannel" class="form-select">
<option value="TEXT" selected>短信TEXT</option>
</select>
</div>
<div>
<button id="smsInlineSendBtn" class="btn btn-primary">
<i class="fas fa-paper-plane me-2"></i> 发送验证码
</button>
</div>
</div>
</div>
<div id="smsInlineStatus" class="status mt-2" role="status" aria-live="polite"></div>
<div id="smsCodeInputSection" class="mt-3" style="display:none;">
<div style="max-width:420px;">
<div class="form-floating">
<input id="smsInlineCode" type="text" class="form-control" placeholder="输入6位验证码" maxlength="6" />
<label for="smsInlineCode">短信验证码</label>
</div>
</div>
<div class="btn-group mt-3">
<button id="smsInlineVerifyBtn" class="btn btn-primary">
<i class="fas fa-check me-2"></i> 验证并继续激活
</button>
</div>
</div>
</div>
<!-- 直取 eSIM 内联区域 -->
<div id="directFetchSection" style="display:none;">
<div class="card card-body mt-3">
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
<span class="lang-zh">此选项适用于在官方 Giffgaff App 已完成 10 英镑付款,但 App 未成功生成 eSIM 的用户。将直接拉取账户上现存的 eSIM 并生成二维码(无需 reserveESim / swapSim</span>
<span class="lang-en">Use this option if you have already paid £10 in the official Giffgaff app but did not receive an eSIM. We will fetch any existing eSIM on the account directly and generate a QR code (no reserveESim / swapSim).</span>
</div>
<div class="alert alert-warning py-2 px-3 mb-3" style="font-size: 0.82rem; border-left: 3px solid #f72585;">
<i class="fas fa-exclamation-triangle me-1" style="color: #f72585;"></i>
<span class="lang-zh" style="color: #6c3a00;">
⚠️ 作者因条件不符未能实测此功能。如使用中遇到问题请打开浏览器开发者工具F12 → Console复制完整日志后<a href="https://github.com/Silentely/eSIM-Tools/issues" target="_blank" rel="noopener noreferrer" style="color: #d63384; text-decoration: underline;">提交 Issue</a>,非常感谢!
</span>
<span class="lang-en" style="color: #6c3a00;">
⚠️ This feature could not be fully tested by the author. If you encounter issues, please open the browser console (F12 → Console), copy the full log, and <a href="https://github.com/Silentely/eSIM-Tools/issues" target="_blank" rel="noopener noreferrer" style="color: #d63384; text-decoration: underline;">open an Issue</a>. Thank you!
</span>
</div>
<button id="directFetchPullBtn" class="btn btn-info">
<i class="fas fa-bolt me-2"></i>
<span class="lang-zh">拉取我的 eSIM</span>
<span class="lang-en">Fetch my eSIM</span>
</button>
<div id="directFetchStatus" class="status mt-2" role="status" aria-live="polite"></div>
<div id="directFetchSsnPicker" class="mt-3" style="display:none;"></div>
</div>
</div>
<!-- 手动激活区块 -->
<div id="manualBlock" class="mt-3" style="display:none;">
<div class="btn-group">
<button id="reserveESimBtn" class="btn btn-primary">
<i class="fas fa-bookmark me-2"></i> 预订eSIM
</button>
</div>
<!-- 已有激活码快捷入口 -->
<div class="mt-3">
<button id="manualEsimInputToggle" class="btn btn-link p-0" type="button">
我已完成 eSIM 预订(已有激活码/SSN
</button>
<div id="manualEsimInputSection" class="card card-body mt-2" style="display: none;">
<a href="https://www.giffgaff.com/activate" target="_blank" class="btn btn-link p-0 ms-2 mb-3" style="font-size: 0.9rem;">
前往激活页面
</a>
<div class="row g-2">
<div class="col-md-6">
<div class="form-floating">
<input id="manualActivationCode" class="form-control" placeholder="如Y6GKL6" />
<label for="manualActivationCode">激活码</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
<input id="manualSSN" class="form-control" placeholder="如8944…" />
<label for="manualSSN">SSN</label>
</div>
</div>
</div>
<button id="manualSaveAndNextBtn" class="btn btn-success mt-3" type="button">
<i class="fas fa-check me-2"></i>保存并进入下一步
</button>
</div>
</div>
<div id="esimReserveStatus" class="status mt-2" role="status" aria-live="polite"></div>
<!-- eSIM信息显示区域 -->
<div id="esimInfoDisplay" class="mt-4" style="display: none;">
<div class="card border-warning">
<div class="card-header bg-warning text-dark">
<i class="fas fa-exclamation-triangle me-2"></i>
<strong>重要eSIM已预订成功请立即手动激活</strong>
</div>
<div class="card-body">
<div class="alert alert-danger">
<i class="fas fa-warning me-2"></i>
<strong>请保持此网页开启状态!</strong>
</div>
<h5 class="text-success mb-3 text-center" id="esimStatusTitle">您的eSIM信息</h5>
<div class="row text-center">
<div class="col-md-6">
<p class="mb-2"><strong>激活码:</strong></p>
<div class="border p-3 mb-3 bg-light">
<code id="displayActivationCode" class="text-break fs-4"></code>
<button id="copyActivationCodeBtn" class="btn btn-sm btn-outline-primary mt-2" data-target="displayActivationCode">
<i class="fas fa-copy"></i> 复制
</button>
</div>
</div>
<div class="col-md-6">
<p class="mb-2"><strong>SSN:</strong></p>
<div class="border p-3 mb-3 bg-light">
<code id="displaySSN" class="text-break fs-5"></code>
<button id="copySSNBtn" class="btn btn-sm btn-outline-primary mt-2" data-target="displaySSN">
<i class="fas fa-copy"></i> 复制
</button>
</div>
</div>
</div>
<div class="mt-4">
<button id="confirmActivationBtn" class="btn btn-success btn-lg">
<i class="fas fa-check-circle me-2"></i>我已完成手动激活,继续下一步
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 步骤5: 获取eSIM -->
<div id="step5" class="section reveal">
<div class="card card-elevated">
<div class="card-header">
<i class="fas fa-qrcode"></i> 第五步获取eSIM二维码
</div>
<div class="card-body">
<p class="mb-4" style="font-size: 17px; color: #212529;">
获取您的eSIM下载代码和二维码
</p>
<div class="btn-group">
<button id="getESimTokenBtn" class="btn btn-primary">
<i class="fas fa-download me-2"></i> 获取eSIM Token
</button>
</div>
<div id="tokenStatus" class="status" role="status" aria-live="polite"></div>
<!-- eSIM结果显示 -->
<div id="resultContainer" class="mt-4">
<div class="card">
<div class="card-header">
<i class="fas fa-qrcode me-2"></i> 您的eSIM信息
</div>
<div class="card-body text-center">
<div id="qrcode" class="mt-4"></div>
<div id="esimInfo" class="mt-4"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 外部脚本 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
<!-- Bootstrap 错误守卫 -->
<script src="/src/giffgaff/js/bootstrap-guard.js"></script>
<!-- 入场动效 -->
<script src="/src/giffgaff/js/reveal-animation.js"></script>
<!-- 验证码加载脚本 -->
<script type="module" src="/src/js/captcha-client.js"></script>
<!-- 性能优化脚本 -->
<script src="/src/js/performance.js"></script>
<!-- 国际化初始化 -->
<script type="module" src="/src/giffgaff/js/init-i18n.js"></script>
<!-- 通知系统初始化 -->
<script type="module" src="/src/giffgaff/js/init-notification.js"></script>
<!-- 主应用脚本(模块化) -->
<script type="module" src="/src/giffgaff/js/giffgaff-app.js"></script>
<!-- 统一版权页脚 -->
<script type="module" src="/src/js/bootstrap-footer.js"></script>
</body>
</html>