Files
AI_English/src/index.html
T
eddy 5a7149151e fix: 增强学习流程与数据处理的稳定性
- 确保异步提词操作结束后可靠恢复按钮状态
- 改进批量文件解析、清空操作与来源列表同步
- 防止复习提前完成,并拦截无效的会话跳转
- 校验 AI 翻译数据并保留 JSON 最外层结构
- 仅使用测验记录计算正确率和易错单词排名
- 限制自动播放设置范围,并在词表变化时停止播放
- 避免云端语音被中断后触发重复的降级朗读
- 改进 Service Worker 对临时网络故障的缓存回退
- 增加 AI 响应解析和测验统计相关测试
2026-07-30 01:47:54 +08:00

135 lines
5.7 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="theme-color" content="#6366f1">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>AI 英语助教</title>
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='16' fill='%236366f1'/%3E%3Ctext x='32' y='42' text-anchor='middle' font-size='34' font-family='Arial, sans-serif' fill='white'%3EAI%3C/text%3E%3C/svg%3E">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
integrity="sha384-t1nt8BQoYMLFN5p42tRAtuAAFQaCQODekUVeKKZrEnEyp4H2R0RHFz0KWpmj7i8g"
crossorigin="anonymous" referrerpolicy="no-referrer">
<script>
const ASSET_VERSION = '20260720-1';
window.APP_VERSION = ASSET_VERSION;
const stylesheets = [
'styles/variables.css',
'styles/base.css',
'styles/layout.css',
'styles/components.css',
'styles/pages/home.css',
'styles/pages/words.css',
'styles/pages/learn.css',
'styles/pages/quiz.css',
'styles/pages/review.css',
'styles/pages/stats.css',
'styles/pages/settings.css',
'styles/responsive.css',
'styles/pages/extract.css',
'styles/pages/mail-learn.css',
'styles/pages/reader.css'
];
document.write(stylesheets
.map(path => `<link rel="stylesheet" href="${path}?v=${ASSET_VERSION}">`)
.join(''));
</script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"
integrity="sha384-vsrfeLOOY6KuIYKDlmVH5UiBmgIdB1oEf7p01YgWHuqmOHfZr374+odEv96n9tNC"
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<div id="app">
<header id="mobile-header">
<button id="menu-btn" aria-label="菜单">
<i class="fas fa-bars"></i>
</button>
<h1>AI 英语助教</h1>
<button id="theme-toggle-mobile" aria-label="切换主题"><i class="fas fa-moon"></i></button>
</header>
<aside id="sidebar">
<div class="sidebar-header">
<span class="logo"><i class="fas fa-graduation-cap"></i></span>
<h2>AI 英语助教</h2>
</div>
<nav class="sidebar-nav">
<a href="#home" class="nav-item active" data-page="home">
<span class="nav-icon"><i class="fas fa-chart-pie"></i></span><span class="nav-text">仪表盘</span>
</a>
<a href="#words" class="nav-item" data-page="words">
<span class="nav-icon"><i class="fas fa-book"></i></span><span class="nav-text">单词库</span>
</a>
<a href="#quiz" class="nav-item" data-page="quiz">
<span class="nav-icon"><i class="fas fa-robot"></i></span><span class="nav-text">AI 测试</span>
</a>
<a href="#review" class="nav-item" data-page="review">
<span class="nav-icon"><i class="fas fa-redo-alt"></i></span><span class="nav-text">错题复习</span>
</a>
<a href="#extract" class="nav-item" data-page="extract">
<span class="nav-icon"><i class="fas fa-envelope"></i></span><span class="nav-text">邮件提词</span>
</a>
<a href="#mail-learn" class="nav-item" data-page="mail-learn">
<span class="nav-icon"><i class="fas fa-graduation-cap"></i></span><span class="nav-text">邮件学习</span>
</a>
<a href="#stats" class="nav-item" data-page="stats">
<span class="nav-icon"><i class="fas fa-chart-line"></i></span><span class="nav-text">统计</span>
</a>
<a href="#settings" class="nav-item" data-page="settings">
<span class="nav-icon"><i class="fas fa-cog"></i></span><span class="nav-text">设置</span>
</a>
</nav>
<div class="sidebar-footer">
<button id="theme-toggle">
<span id="theme-icon"><i class="fas fa-moon"></i></span>
<span>切换主题</span>
</button>
</div>
</aside>
<div id="overlay"></div>
<main id="main-content">
<div id="page-content"></div>
</main>
<div id="toast-container"></div>
</div>
<div id="modal-root"></div>
<script>
document.write(`<script type="module" src="js/main.js?v=${ASSET_VERSION}"><\/script>`);
</script>
<script>
// 注册 Service Worker:由它统一保证模块图不陈旧并提供离线回退。
// 注册失败(如旧版 iOS 不支持)不影响使用,上方 ?v= 版本号仍作兜底。
if ('serviceWorker' in navigator) {
const needsFirstControlledReload = !navigator.serviceWorker.controller;
let reloadingForServiceWorker = false;
if (needsFirstControlledReload) {
// 首次取得页面控制权后刷新一次,让首轮已加载的子资源也经过 SW 并写入离线缓存。
navigator.serviceWorker.addEventListener('controllerchange', () => {
if (reloadingForServiceWorker) return;
reloadingForServiceWorker = true;
window.location.reload();
});
}
window.addEventListener('load', () => {
// updateViaCache: 'none' 让 sw.js 本身始终走网络校验,避免被 HTTP 缓存拖住策略更新。
navigator.serviceWorker.register('sw.js', { updateViaCache: 'none' }).catch(error => {
console.warn('Service Worker 注册失败,应用仍可正常使用:', error);
});
});
}
</script>
</body>
</html>