Files
PeojectTWT/base64_gen.html

805 lines
29 KiB
HTML
Raw Normal View History

2025-10-11 10:47:07 +08:00
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>单词默写系统 - 教师端生成器</title>
2025-10-11 10:47:07 +08:00
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #0a2a1a, #0c3a4a, #0d4b66);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
overflow-x: hidden;
position: relative;
}
.container {
width: 100%;
max-width: 1000px;
text-align: center;
position: relative;
z-index: 10;
}
.header {
margin-bottom: 40px;
animation: fadeInDown 1s ease-out;
position: relative;
}
.logo {
font-size: 4.5rem;
margin-bottom: 20px;
background: linear-gradient(45deg, #7cffcb, #00d2ff, #5de6ff);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: 0 0 20px rgba(100, 255, 200, 0.3);
animation: pulse 3s infinite;
}
.title {
font-size: 2.8rem;
font-weight: 700;
margin-bottom: 15px;
color: #fff;
text-shadow: 0 0 15px rgba(100, 255, 200, 0.5);
position: relative;
z-index: 20;
}
.subtitle {
font-size: 1.2rem;
color: #c0ffea;
max-width: 600px;
margin: 0 auto;
line-height: 1.6;
position: relative;
z-index: 20;
}
.main-content {
background: rgba(10, 42, 26, 0.7);
border-radius: 30px;
padding: 40px;
margin: 30px 0;
backdrop-filter: blur(15px);
border: 1px solid rgba(100, 255, 200, 0.3);
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.4);
animation: fadeInUp 1s ease-out 0.6s both;
position: relative;
overflow: hidden;
height: auto;
}
.tabs {
display: flex;
justify-content: center;
gap: 15px;
margin-bottom: 30px;
}
.tab-btn {
padding: 12px 25px;
border-radius: 20px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(100, 255, 200, 0.3);
color: #c0ffea;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 500;
}
.tab-btn.active {
background: linear-gradient(45deg, #7cffcb, #00d2ff, #5de6ff);
color: #0a2a1a;
font-weight: 600;
}
.tab-btn:hover:not(.active) {
background: rgba(255, 255, 255, 0.2);
}
.search-container {
display: flex;
gap: 15px;
margin: 25px 0;
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
.search-input {
flex: 1;
padding: 15px 20px;
border-radius: 15px;
border: 1px solid rgba(100, 255, 200, 0.3);
background: rgba(0, 0, 0, 0.2);
color: #fff;
font-size: 1rem;
outline: none;
}
.search-input::placeholder {
color: #a0ffe0;
}
.search-input:focus {
border-color: #7cffcb;
box-shadow: 0 0 15px rgba(124, 255, 203, 0.3);
}
.button-group {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 15px;
margin: 25px 0;
}
.action-btn {
padding: 12px 25px;
border-radius: 15px;
border: none;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.btn-primary {
background: linear-gradient(45deg, #7cffcb, #00d2ff, #5de6ff);
color: #0a2a1a;
}
.btn-secondary {
background: rgba(255, 255, 255, 0.1);
color: #c0ffea;
border: 1px solid rgba(100, 255, 200, 0.3);
}
.btn-success {
background: linear-gradient(45deg, #34C759, #7cffcb);
color: #0a2a1a;
}
.btn-warning {
background: linear-gradient(45deg, #FFCC00, #ffd93d);
color: #0a2a1a;
}
.btn-danger {
background: linear-gradient(45deg, #FF3B30, #ff6b6b);
color: white;
}
.action-btn:hover {
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(100, 255, 200, 0.3);
}
.action-btn:active {
transform: translateY(-1px);
}
.action-btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
transition: 0.5s;
}
.action-btn:hover::before {
left: 100%;
}
.stats-container {
display: flex;
justify-content: space-around;
margin: 30px 0;
flex-wrap: wrap;
gap: 20px;
}
.stat-card {
background: rgba(255, 255, 255, 0.1);
border-radius: 15px;
padding: 20px;
min-width: 120px;
border: 1px solid rgba(100, 255, 200, 0.2);
}
.stat-number {
font-size: 2rem;
font-weight: 700;
color: #7cffcb;
margin-bottom: 5px;
}
.stat-label {
color: #c0ffea;
font-size: 0.9rem;
}
.word-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 15px;
margin: 30px 0;
max-height: 400px;
2025-10-11 10:47:07 +08:00
overflow-y: auto;
padding: 10px;
background: rgba(0, 0, 0, 0.1);
border-radius: 10px;
}
.word-grid::-webkit-scrollbar {
width: 8px;
}
.word-grid::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
}
.word-grid::-webkit-scrollbar-thumb {
background: linear-gradient(45deg, #7cffcb, #00d2ff);
border-radius: 4px;
}
.word-item {
padding: 15px 10px;
border-radius: 10px;
background: rgba(255, 255, 255, 0.1);
color: #c0ffea;
cursor: pointer;
transition: all 0.3s ease;
border: 1px solid transparent;
font-weight: 500;
display: flex;
flex-direction: column;
align-items: center;
}
.word-item:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-2px);
}
.word-item.selected {
background: linear-gradient(45deg, #7cffcb, #00d2ff, #5de6ff);
color: #0a2a1a;
border: 1px solid rgba(255, 255, 255, 0.5);
font-weight: 600;
}
.word-text {
font-weight: bold;
margin-bottom: 5px;
}
.word-translation {
font-size: 0.8em;
opacity: 0.8;
}
.add-word-suggestion {
background: linear-gradient(135deg, rgba(255, 204, 0, 0.2), rgba(255, 107, 107, 0.2));
border-radius: 15px;
padding: 20px;
margin: 20px 0;
border: 1px solid rgba(255, 204, 0, 0.3);
display: none;
}
.add-word-suggestion p {
color: #ffd93d;
margin-bottom: 15px;
}
.add-word-inputs {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 10px;
}
.add-word-input {
padding: 10px;
border-radius: 8px;
border: 1px solid rgba(100, 255, 200, 0.3);
background: rgba(0, 0, 0, 0.2);
color: #fff;
}
.particles {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
overflow: hidden;
}
.particle {
position: absolute;
border-radius: 50%;
background: rgba(100, 255, 200, 0.2);
animation: float 15s infinite linear;
}
@keyframes float {
0% {
transform: translateY(0) translateX(0) rotate(0deg);
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 0.5;
}
100% {
transform: translateY(-100vh) translateX(100px) rotate(360deg);
opacity: 0;
}
}
.cube-container {
position: absolute;
top: 20%;
left: 50%;
transform: translate(-50%, -50%);
width: 150px;
height: 150px;
perspective: 1000px;
z-index: 5;
opacity: 0.7;
}
.cube {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
animation: rotate 20s infinite linear;
}
.cube-face {
position: absolute;
width: 150px;
height: 150px;
background: rgba(100, 255, 200, 0.1);
border: 2px solid rgba(100, 255, 200, 0.3);
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem;
font-weight: bold;
color: rgba(100, 255, 200, 0.7);
backdrop-filter: blur(2px);
}
.cube-face-front { transform: rotateY(0deg) translateZ(75px); }
.cube-face-back { transform: rotateY(180deg) translateZ(75px); }
.cube-face-right { transform: rotateY(90deg) translateZ(75px); }
.cube-face-left { transform: rotateY(-90deg) translateZ(75px); }
.cube-face-top { transform: rotateX(90deg) translateZ(75px); }
.cube-face-bottom { transform: rotateX(-90deg) translateZ(75px); }
@keyframes rotate {
0% { transform: rotateX(0) rotateY(0) rotateZ(0); }
100% { transform: rotateX(360deg) rotateY(360deg) rotateZ(360deg); }
}
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes pulse {
0% { text-shadow: 0 0 10px rgba(100, 255, 200, 0.3); }
50% { text-shadow: 0 0 30px rgba(100, 255, 200, 0.6); }
100% { text-shadow: 0 0 10px rgba(100, 255, 200, 0.3); }
}
.export-info {
background: rgba(0, 0, 0, 0.2);
border-radius: 15px;
padding: 20px;
margin: 20px 0;
text-align: left;
}
.export-info h3 {
color: #7cffcb;
margin-bottom: 15px;
}
.export-info ul {
color: #c0ffea;
padding-left: 20px;
}
.export-info li {
margin-bottom: 10px;
2025-10-11 10:47:07 +08:00
}
</style>
</head>
<body>
<div class="particles" id="particles"></div>
<div class="cube-container">
<div class="cube">
<div class="cube-face cube-face-front"></div>
<div class="cube-face cube-face-back"></div>
<div class="cube-face cube-face-right"></div>
<div class="cube-face cube-face-left"></div>
<div class="cube-face cube-face-top"></div>
<div class="cube-face cube-face-bottom"></div>
2025-10-11 10:47:07 +08:00
</div>
</div>
2025-10-11 10:47:07 +08:00
<div class="container">
<div class="header">
<div class="logo">
<i class="fas fa-chalkboard-teacher"></i>
</div>
<h1 class="title">单词默写系统 - 教师端生成器</h1>
<p class="subtitle">创建和管理单词词库,生成加密配置文件</p>
2025-10-11 10:47:07 +08:00
</div>
2025-10-11 10:47:07 +08:00
<div class="main-content">
<div class="tabs">
<button class="tab-btn active" onclick="switchTab('manage')">词库管理</button>
<button class="tab-btn" onclick="switchTab('export')">导出配置</button>
2025-10-11 10:47:07 +08:00
</div>
<div id="manageTab" class="tab-content active">
<h3 style="color: #fff; margin: 20px 0;">管理单词词库</h3>
2025-10-11 10:47:07 +08:00
<div class="search-container">
<input type="text" class="search-input" id="searchInput" placeholder="搜索单词..." oninput="filterWords()">
<button class="action-btn btn-secondary" onclick="clearSearch()">
<i class="fas fa-times"></i> 清除
</button>
</div>
2025-10-11 10:47:07 +08:00
<div id="addWordSuggestion" class="add-word-suggestion">
<p><i class="fas fa-lightbulb"></i> 没有找到您要的单词?可以添加到词库中!</p>
<div class="add-word-inputs">
<input type="text" class="add-word-input" id="newWordInput" placeholder="英文单词">
<input type="text" class="add-word-input" id="newTranslationInput" placeholder="中文翻译">
<button class="action-btn btn-warning" onclick="addNewWordWithTranslation()">
<i class="fas fa-plus"></i> 添加单词
</button>
</div>
</div>
2025-10-11 10:47:07 +08:00
<div class="button-group">
<button class="action-btn btn-primary" onclick="selectAllWords()">
<i class="fas fa-check-square"></i> 全选
</button>
<button class="action-btn btn-secondary" onclick="deselectAllWords()">
<i class="fas fa-square"></i> 取消全选
</button>
<button class="action-btn btn-warning" onclick="randomSelectWords()">
<i class="fas fa-dice"></i> 随机60个
</button>
<button class="action-btn btn-danger" onclick="deleteSelectedWords()">
<i class="fas fa-trash"></i> 删除选中
2025-10-11 10:47:07 +08:00
</button>
</div>
2025-10-11 10:47:07 +08:00
<div class="stats-container">
<div class="stat-card">
<div class="stat-number" id="totalWords">0</div>
<div class="stat-label">总单词</div>
</div>
<div class="stat-card">
<div class="stat-number" id="selectedCount">0</div>
<div class="stat-label">已选择</div>
</div>
<div class="stat-card">
<div class="stat-number" id="percentage">0%</div>
<div class="stat-label">完成度</div>
</div>
</div>
2025-10-11 10:47:07 +08:00
<div class="word-grid" id="wordGrid">
<!-- 单词项将通过JavaScript动态生成 -->
</div>
</div>
<div id="exportTab" class="tab-content">
<h3 style="color: #fff; margin: 20px 0;">导出配置文件</h3>
<div class="button-group">
<button class="action-btn btn-success" onclick="exportConfig()">
<i class="fas fa-download"></i> 导出加密配置文件
</button>
</div>
<div class="stats-container">
<div class="stat-card">
<div class="stat-number" id="exportWordCount">0</div>
<div class="stat-label">待导出单词</div>
</div>
</div>
<div class="export-info">
<h3><i class="fas fa-info-circle"></i> 导出说明</h3>
<ul>
<li>导出的配置文件将包含完整的JSON结构</li>
<li>英文单词部分使用Base64加密</li>
<li>中文翻译保持明文</li>
<li>学生端可直接导入使用</li>
<li>无需修改应用程序即可更换单词列表</li>
</ul>
</div>
</div>
2025-10-11 10:47:07 +08:00
</div>
</div>
2025-10-11 10:47:07 +08:00
<script>
// 词库数据
let wordLibrary = [
{ "word": "instrument", "translation": "n. 工具、手段" },
{ "word": "therefore", "translation": "adv. 因此" },
{ "word": "tradition", "translation": "n. 传统" },
{ "word": "represent", "translation": "v. 代表、描绘" },
{ "word": "iceberg", "translation": "n. 冰山" },
{ "word": "store", "translation": "v. 储存" },
{ "word": "depend", "translation": "v. 依赖" },
{ "word": "general", "translation": "adj. 整体的、普遍的、大体的" },
{ "word": "contrast", "translation": "n. 对比、差别" },
{ "word": "predator", "translation": "n. 捕食者" },
{ "word": "against", "translation": "prep. 与……相反、违背、对比、以…为背景" },
{ "word": "survive", "translation": "v. 存活、挺过、比……活的长" },
{ "word": "nature", "translation": "n. 自然、本质" },
{ "word": "solar", "translation": "adj. 太阳的" },
{ "word": "architecture", "translation": "n. 建筑" },
{ "word": "primarily", "translation": "adv. 主要地" },
{ "word": "observe", "translation": "v. 观察(到)、遵从(法律等)" },
{ "word": "canal", "translation": "n. 运河" },
{ "word": "craft", "translation": "n. 手艺" },
{ "word": "reflect", "translation": "v. 反映、反射、反思" },
{ "word": "argue", "translation": "v. 论证" },
{ "word": "draw", "translation": "v. 画、得出、提取、轻拉" },
{ "word": "theater", "translation": "n. 剧院、戏剧表演" },
{ "word": "especially", "translation": "adv. 特别是、尤其" },
{ "word": "aggressive", "translation": "adj. 攻击性的" },
{ "word": "extreme", "translation": "adj.&n. 极度、极端、极大(的)" },
{ "word": "sense", "translation": "n. 意识" },
{ "word": "single", "translation": "adj. 单一的、单身的 v. 选中" },
{ "word": "previous", "translation": "adj. 先前的" },
{ "word": "obtain", "translation": "v. 获得" }
];
// 选中的单词索引
let selectedIndices = new Set();
2025-10-11 10:47:07 +08:00
// 页面加载时初始化
document.addEventListener('DOMContentLoaded', function() {
createParticles();
updateStats();
renderWordGrid();
2025-10-11 10:47:07 +08:00
});
// 创建背景粒子
function createParticles() {
const particlesContainer = document.getElementById('particles');
const particleCount = 30;
for (let i = 0; i < particleCount; i++) {
const particle = document.createElement('div');
particle.classList.add('particle');
const size = Math.random() * 20 + 5;
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
particle.style.left = `${Math.random() * 100}%`;
particle.style.top = `${Math.random() * 100}%`;
const delay = Math.random() * 15;
const duration = 15 + Math.random() * 10;
particle.style.animationDelay = `${delay}s`;
particle.style.animationDuration = `${duration}s`;
const colors = ['rgba(100, 255, 200, 0.2)', 'rgba(0, 210, 255, 0.2)', 'rgba(93, 230, 255, 0.2)'];
particle.style.background = colors[Math.floor(Math.random() * colors.length)];
particlesContainer.appendChild(particle);
}
}
// 标签页切换
function switchTab(tabId) {
// 移除所有标签的active状态
2025-10-11 10:47:07 +08:00
document.querySelectorAll('.tab-btn').forEach(tab => {
tab.classList.remove('active');
});
// 移除所有内容区域的active状态
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
// 设置当前标签为active
2025-10-11 10:47:07 +08:00
event.target.classList.add('active');
// 显示对应的内容区域
document.getElementById(tabId + 'Tab').classList.add('active');
// 如果是导出标签,更新导出统计
if (tabId === 'export') {
document.getElementById('exportWordCount').textContent = selectedIndices.size;
2025-10-11 10:47:07 +08:00
}
}
// 更新统计信息
function updateStats() {
document.getElementById('totalWords').textContent = wordLibrary.length;
document.getElementById('selectedCount').textContent = selectedIndices.size;
const percentage = wordLibrary.length > 0 ? Math.round((selectedIndices.size / wordLibrary.length) * 100) : 0;
document.getElementById('percentage').textContent = percentage + '%';
}
2025-10-11 10:47:07 +08:00
// 渲染单词网格
function renderWordGrid(filteredWords = wordLibrary, filteredIndices = null) {
2025-10-11 10:47:07 +08:00
const wordGrid = document.getElementById('wordGrid');
wordGrid.innerHTML = '';
filteredWords.forEach((wordObj, index) => {
// 如果是过滤后的数组,需要找到原始索引
const actualIndex = filteredIndices ? filteredIndices[index] : index;
2025-10-11 10:47:07 +08:00
const wordItem = document.createElement('div');
wordItem.className = `word-item ${selectedIndices.has(actualIndex) ? 'selected' : ''}`;
2025-10-11 10:47:07 +08:00
wordItem.innerHTML = `
<div class="word-text">${wordObj.word}</div>
<div class="word-translation">${wordObj.translation}</div>
`;
wordItem.addEventListener('click', () => toggleWordSelection(actualIndex));
2025-10-11 10:47:07 +08:00
wordGrid.appendChild(wordItem);
});
}
// 切换单词选择状态
function toggleWordSelection(index) {
if (selectedIndices.has(index)) {
selectedIndices.delete(index);
2025-10-11 10:47:07 +08:00
} else {
selectedIndices.add(index);
2025-10-11 10:47:07 +08:00
}
updateStats();
renderWordGrid(); // 重新渲染以更新选中状态
2025-10-11 10:47:07 +08:00
}
// 全选单词
function selectAllWords() {
for (let i = 0; i < wordLibrary.length; i++) {
selectedIndices.add(i);
}
2025-10-11 10:47:07 +08:00
updateStats();
renderWordGrid();
2025-10-11 10:47:07 +08:00
}
// 取消全选
function deselectAllWords() {
selectedIndices.clear();
2025-10-11 10:47:07 +08:00
updateStats();
renderWordGrid();
2025-10-11 10:47:07 +08:00
}
// 随机选择60个单词
function randomSelectWords() {
selectedIndices.clear();
const totalWords = wordLibrary.length;
const count = Math.min(60, totalWords);
// 创建索引数组并随机打乱
const indices = Array.from({length: totalWords}, (_, i) => i);
for (let i = indices.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[indices[i], indices[j]] = [indices[j], indices[i]];
}
// 选择前count个
for (let i = 0; i < count; i++) {
selectedIndices.add(indices[i]);
}
2025-10-11 10:47:07 +08:00
updateStats();
renderWordGrid();
2025-10-11 10:47:07 +08:00
}
// 搜索单词
function filterWords() {
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
if (!searchTerm) {
2025-10-11 10:47:07 +08:00
renderWordGrid();
return;
2025-10-11 10:47:07 +08:00
}
const filteredIndices = [];
const filteredWords = wordLibrary.filter((wordObj, index) => {
const match = wordObj.word.toLowerCase().includes(searchTerm) ||
wordObj.translation.toLowerCase().includes(searchTerm);
if (match) filteredIndices.push(index);
return match;
});
renderWordGrid(filteredWords, filteredIndices);
2025-10-11 10:47:07 +08:00
}
// 清除搜索
function clearSearch() {
document.getElementById('searchInput').value = '';
renderWordGrid();
}
// 添加新单词
function addNewWordWithTranslation() {
const wordInput = document.getElementById('newWordInput');
const translationInput = document.getElementById('newTranslationInput');
const word = wordInput.value.trim();
const translation = translationInput.value.trim();
if (!word || !translation) {
alert('请输入完整的单词和翻译!');
2025-10-11 10:47:07 +08:00
return;
}
// 检查是否已存在
const exists = wordLibrary.some(item => item.word.toLowerCase() === word.toLowerCase());
if (exists) {
alert('该单词已存在于词库中!');
return;
}
// 添加到词库
wordLibrary.push({ word, translation });
// 清空输入框
wordInput.value = '';
translationInput.value = '';
// 更新显示
updateStats();
renderWordGrid();
// 隐藏添加提示
document.getElementById('addWordSuggestion').style.display = 'none';
}
2025-10-11 10:47:07 +08:00
// 删除选中的单词
function deleteSelectedWords() {
if (selectedIndices.size === 0) {
alert('请先选择要删除的单词!');
return;
}
if (!confirm(`确定要删除选中的 ${selectedIndices.size} 个单词吗?`)) {
return;
}
// 按索引从大到小排序,避免删除时索引变化的问题
const sortedIndices = Array.from(selectedIndices).sort((a, b) => b - a);
// 删除单词
for (const index of sortedIndices) {
wordLibrary.splice(index, 1);
}
// 清空选中状态
selectedIndices.clear();
// 更新显示
updateStats();
renderWordGrid();
}
2025-10-11 10:47:07 +08:00
// 导出配置文件Base64加密英文单词中文翻译保持明文
function exportConfig() {
if (selectedIndices.size === 0) {
alert('请先选择要导出的单词!');
return;
}
// 获取选中的单词对象
const selectedWords = Array.from(selectedIndices).map(index => wordLibrary[index]);
// 创建包含加密英文单词和明文翻译的JSON结构
const exportData = {
words: selectedWords.map(item => ({
word: btoa(item.word), // 加密英文单词
translation: item.translation // 保持明文翻译
})),
created: new Date().toISOString(),
count: selectedWords.length
};
// 生成JavaScript文件内容
const jsContent = `var configData = ${JSON.stringify(exportData, null, 2)};\n`;
2025-10-11 10:47:07 +08:00
// 创建下载链接
const blob = new Blob([jsContent], { type: 'application/javascript' });
2025-10-11 10:47:07 +08:00
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `word_dictation_config_${new Date().getTime()}.js`;
2025-10-11 10:47:07 +08:00
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
alert(`成功导出包含 ${selectedWords.length} 个单词的配置文件!`);
2025-10-11 10:47:07 +08:00
}
</script>
</body>
</html>