mirror of
https://github.com/Silentely/eSIM-Tools.git
synced 2026-09-03 06:24:20 +08:00
将内联CSS样式从index.html迁移到模块化的notification.css文件 为Giffgaff和Simyo页面添加通知系统样式引用 实现状态面板的可折叠设计,改善用户体验 添加深色模式支持,增强可访问性 优化悬停效果,仅在支持设备上显示 添加官方暂停通道的提示信息 统一管理网络状态指示器和加载动画样式
374 lines
17 KiB
HTML
374 lines
17 KiB
HTML
<!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 申请与管理、二维码生成及验证码处理。">
|
||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.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; font-src 'self' data:; connect-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.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://challenges.cloudflare.com https://www.google.com https://www.gstatic.com; img-src 'self' data: https:; frame-src 'self' https://challenges.cloudflare.com https://www.google.com;">
|
||
<title>eSIM Tools - Giffgaff & Simyo</title>
|
||
<link rel="manifest" href="/manifest.webmanifest">
|
||
<meta name="theme-color" content="#6366f1">
|
||
|
||
<!-- 性能优化:预连接与预加载关键资源 -->
|
||
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
|
||
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
|
||
<link rel="preconnect" href="https://www.googletagmanager.com" crossorigin>
|
||
<link rel="preconnect" href="https://qrcode.show" 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优先:preload 后备 rel=stylesheet -->
|
||
<link rel="preload" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" as="style" onload="this.rel='stylesheet'">
|
||
<noscript><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"></noscript>
|
||
<!-- 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="preload" href="/src/styles/design-system.css" as="style" onload="this.rel='stylesheet'">
|
||
<link rel="preload" href="/src/styles/animations.css" as="style" onload="this.rel='stylesheet'">
|
||
<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;
|
||
}
|
||
|
||
.github-btn{
|
||
background: #24292e;
|
||
border: 1px solid rgba(255,255,255,.18);
|
||
font-size: 16px;
|
||
color:#fff;
|
||
cursor:pointer;
|
||
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;
|
||
margin-top: var(--s3);
|
||
outline: none;
|
||
will-change: transform;
|
||
}
|
||
.github-btn:hover{ transform: translateY(-1px); box-shadow: var(--shadow-md); text-decoration: none; color:#fff; background:#111; }
|
||
.github-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; }
|
||
}
|
||
</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"></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>
|
||
<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>
|
||
</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>
|
||
|
||
<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">
|
||
import NotificationService from '/src/js/modules/notification-service.js';
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
NotificationService.init();
|
||
});
|
||
</script>
|
||
|
||
<!-- 性能优化脚本 -->
|
||
<script src="/src/js/performance.js"></script>
|
||
</body>
|
||
</html>
|