Refactor code structure for improved readability and maintainability
All checks were successful
Deploy / deploy (push) Successful in 2s

This commit is contained in:
M1ngdaXie
2026-03-27 01:39:09 -07:00
parent db30afb035
commit fc1eb2040c
4 changed files with 742 additions and 573 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.superpowers

103
app.js
View File

@@ -93,7 +93,7 @@ function sfxComplete() {
const APP_VERSION = 2; const APP_VERSION = 2;
var state = { var state = {
sound: true, sound: true,
bigFont: false, theme: "ink",
difficulty: 1, difficulty: 1,
score: 0, score: 0,
combo: 0, combo: 0,
@@ -123,7 +123,7 @@ function saveState() {
"chengyu_s", "chengyu_s",
JSON.stringify({ JSON.stringify({
sound: state.sound, sound: state.sound,
bigFont: state.bigFont, theme: state.theme,
difficulty: state.difficulty, difficulty: state.difficulty,
totalCount: state.totalCount, totalCount: state.totalCount,
streak: state.streak, streak: state.streak,
@@ -237,7 +237,7 @@ function loadState() {
try { try {
var s = JSON.parse(localStorage.getItem("chengyu_s") || "{}"); var s = JSON.parse(localStorage.getItem("chengyu_s") || "{}");
state.sound = s.sound !== undefined ? s.sound : true; state.sound = s.sound !== undefined ? s.sound : true;
state.bigFont = s.bigFont || false; state.theme = s.theme || "ink";
state.totalCount = s.totalCount || 0; state.totalCount = s.totalCount || 0;
state.streak = s.streak || 0; state.streak = s.streak || 0;
state.checkinDays = s.checkinDays || []; state.checkinDays = s.checkinDays || [];
@@ -312,16 +312,15 @@ function pickIdiom() {
return item; return item;
} }
var pool = IDIOMS.filter(function (i) { var pool = IDIOMS.filter(function (i) {
return i.l <= state.difficulty; return i.l <= state.difficulty && !state.usedIdiomIndices[i.w];
}); });
var a = 0, if (!pool.length) {
idx; state.usedIdiomIndices = {};
do { pool = IDIOMS.filter(function (i) { return i.l <= state.difficulty; });
idx = Math.floor(Math.random() * pool.length); }
a++; var item = pool[Math.floor(Math.random() * pool.length)];
} while (state.usedIdiomIndices[pool[idx].w] && a < 100); state.usedIdiomIndices[item.w] = 1;
state.usedIdiomIndices[pool[idx].w] = 1; return item;
return pool[idx];
} }
function generateBlanks(w, d) { function generateBlanks(w, d) {
var n = d === 1 ? 1 : d === 2 ? 2 : 3; var n = d === 1 ? 1 : d === 2 ? 2 : 3;
@@ -375,6 +374,11 @@ function renderStats() {
document.getElementById("statCombo").textContent = state.combo; document.getElementById("statCombo").textContent = state.combo;
document.getElementById("statTotal").textContent = state.totalCount; document.getElementById("statTotal").textContent = state.totalCount;
document.getElementById("statStreak").textContent = state.streak; document.getElementById("statStreak").textContent = state.streak;
var flame = document.getElementById("flameIcon");
flame.className = "";
if (state.combo >= 8) flame.className = "flame-high";
else if (state.combo >= 5) flame.className = "flame-mid";
else if (state.combo >= 3) flame.className = "flame-low";
} }
function renderProgress() { function renderProgress() {
var bar = document.getElementById("progressBar"); var bar = document.getElementById("progressBar");
@@ -455,6 +459,12 @@ function renderCombo() {
"学富五车!", "学富五车!",
"梅子无人能挡!", "梅子无人能挡!",
"登峰造极!", "登峰造极!",
"出口成章!",
"满腹经纶!",
"博古通今!",
"梅子开挂了!",
"无可匹敌!",
"梅子是成语王!",
]; ];
var i = Math.min(Math.floor((state.combo - 3) / 2), msgs.length - 1); var i = Math.min(Math.floor((state.combo - 3) / 2), msgs.length - 1);
var txt = "🔥 连击 ×" + state.combo + " " + msgs[i]; var txt = "🔥 连击 ×" + state.combo + " " + msgs[i];
@@ -477,6 +487,7 @@ function renderCombo() {
function startRound() { function startRound() {
state.round = 0; state.round = 0;
state.roundResults = []; state.roundResults = [];
state.usedIdiomIndices = {};
nextQuestion(); nextQuestion();
} }
function nextQuestion() { function nextQuestion() {
@@ -620,7 +631,19 @@ function showResult(ok, nc) {
'"><h3 style="color:' + '"><h3 style="color:' +
(ok ? "var(--green)" : "var(--red)") + (ok ? "var(--green)" : "var(--red)") +
'">' + '">' +
(ok ? "✅ 回答正确!" : "❌ 答错了~") + (ok ? [
"✅ 回答正确!",
"✅ 太棒了!",
"✅ 梅子答对了!",
"✅ 完全正确!",
"✅ 厉害!",
"✅ 对!梅子真棒!",
][Math.floor(Math.random() * 6)] : [
"❌ 答错了~",
"❌ 没关系,记住它!",
"❌ 这次没中,下次加油!",
"❌ 差一点点~",
][Math.floor(Math.random() * 4)]) +
(ok && state.combo >= 3 ? " 🔥" : "") + (ok && state.combo >= 3 ? " 🔥" : "") +
'</h3><div class="idiom-word">' + '</h3><div class="idiom-word">' +
idiom.w + idiom.w +
@@ -642,10 +665,25 @@ function finishRound() {
state.reviewMode = false; state.reviewMode = false;
var msg = var msg =
c === ROUND_SIZE c === ROUND_SIZE
? "满分!梅子太厉害了!🎉" ? [
"满分!梅子太厉害了!🎉",
"完美!梅子真是成语达人!✨",
"全对!梅子今天状态绝佳!🌟",
"漂亮!一题不差,梅子威武!👏",
][Math.floor(Math.random() * 4)]
: c >= 3 : c >= 3
? "梅子表现不错!继续加油~" ? [
: "没关系梅子,多练习就会进步!💪"; "梅子表现不错!继续加油~",
"答得很好,梅子越来越厉害了!",
"不错不错,梅子进步了!🌸",
"梅子答题越来越顺了,加油!",
][Math.floor(Math.random() * 4)]
: [
"没关系梅子,多练习就会进步!💪",
"继续努力,梅子肯定能行!",
"不怕不怕,熟能生巧!加油梅子~",
"错了没关系,下次梅子一定更好!",
][Math.floor(Math.random() * 4)];
var reviewBtn = var reviewBtn =
wasReview && state.wrongBook.length wasReview && state.wrongBook.length
? '<button class="action-btn secondary" onclick="showWrongBookModal()" style="margin-top:8px;width:100%">再次练习错题 📖</button>' ? '<button class="action-btn secondary" onclick="showWrongBookModal()" style="margin-top:8px;width:100%">再次练习错题 📖</button>'
@@ -743,11 +781,11 @@ function init() {
loadState(); loadState();
loadWrongBook(); loadWrongBook();
renderWbBadge(); renderWbBadge();
if (state.bigFont) document.body.classList.add("big-font"); document.documentElement.setAttribute("data-theme", state.theme);
document.getElementById("btnSound").classList.toggle("active", state.sound); document.getElementById("btnSound").classList.toggle("active", state.sound);
document.getElementById("btnSound").textContent = state.sound ? "🔊" : "🔇"; document.getElementById("btnSound").textContent = state.sound ? "🔊" : "🔇";
if (state.bigFont) document.getElementById("btnTheme").textContent = state.theme === "ink" ? "🌙" : "☀️";
document.getElementById("btnBigFont").classList.add("active"); showThemeTooltip();
document.querySelectorAll(".diff-btn").forEach(function (b) { document.querySelectorAll(".diff-btn").forEach(function (b) {
b.classList.toggle("active", +b.dataset.diff === state.difficulty); b.classList.toggle("active", +b.dataset.diff === state.difficulty);
}); });
@@ -761,10 +799,29 @@ document.getElementById("btnSound").addEventListener("click", function () {
this.textContent = state.sound ? "🔊" : "🔇"; this.textContent = state.sound ? "🔊" : "🔇";
saveState(); saveState();
}); });
document.getElementById("btnBigFont").addEventListener("click", function () { function showThemeTooltip() {
state.bigFont = !state.bigFont; if (localStorage.getItem("chengyu_theme_tip")) return;
document.body.classList.toggle("big-font", state.bigFont); var tip = document.getElementById("themeTooltip");
this.classList.toggle("active", state.bigFont); if (!tip) return;
setTimeout(function () {
tip.classList.add("show");
var timer = setTimeout(dismissThemeTooltip, 5000);
tip.addEventListener("click", function () {
clearTimeout(timer);
dismissThemeTooltip();
}, { once: true });
}, 1200);
}
function dismissThemeTooltip() {
var tip = document.getElementById("themeTooltip");
if (tip) tip.classList.remove("show");
localStorage.setItem("chengyu_theme_tip", "1");
}
document.getElementById("btnTheme").addEventListener("click", function () {
dismissThemeTooltip();
state.theme = state.theme === "ink" ? "paper" : "ink";
document.documentElement.setAttribute("data-theme", state.theme);
this.textContent = state.theme === "ink" ? "🌙" : "☀️";
saveState(); saveState();
}); });
document document

View File

@@ -1,5 +1,5 @@
<!doctype html> <!doctype html>
<html lang="zh-CN"> <html lang="zh-CN" data-theme="ink">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta <meta
@@ -21,7 +21,13 @@
<div class="header-title">梅子的成语填字</div> <div class="header-title">梅子的成语填字</div>
<div class="header-right"> <div class="header-right">
<button class="icon-btn" id="btnSound">🔊</button> <button class="icon-btn" id="btnSound">🔊</button>
<button class="icon-btn" id="btnBigFont"></button> <div class="theme-btn-wrap" id="themeBtnWrap">
<button class="icon-btn" id="btnTheme" title="切换主题">🌙</button>
<div class="theme-tooltip" id="themeTooltip">
新!点我切换白天/夜间模式 ✨
<div class="theme-tooltip-arrow"></div>
</div>
</div>
<button class="icon-btn" id="btnCheckin">📅</button> <button class="icon-btn" id="btnCheckin">📅</button>
</div> </div>
</div> </div>
@@ -29,7 +35,7 @@
<div class="info-item">今日&nbsp;<span id="statScore">0</span></div> <div class="info-item">今日&nbsp;<span id="statScore">0</span></div>
<div class="info-divider"></div> <div class="info-divider"></div>
<div class="info-item"> <div class="info-item">
🔥&nbsp;<span id="statCombo">0</span>&nbsp;连击 <span id="flameIcon">🔥</span>&nbsp;<span id="statCombo">0</span>&nbsp;连击
</div> </div>
<div class="info-divider"></div> <div class="info-divider"></div>
<div class="info-item info-wb" id="btnWrongBook"> <div class="info-item info-wb" id="btnWrongBook">
@@ -65,7 +71,7 @@
<div class="modal"> <div class="modal">
<div <div
class="modal-header" class="modal-header"
style="background: linear-gradient(135deg, #6b4f2a, #c8941a)" style=""
> >
<div class="streak-display" id="wbCount">0</div> <div class="streak-display" id="wbCount">0</div>
<div class="streak-label">道错题待复习 📖</div> <div class="streak-label">道错题待复习 📖</div>
@@ -86,7 +92,7 @@
<div class="modal"> <div class="modal">
<div <div
class="modal-header" class="modal-header"
style="background: linear-gradient(135deg, #2a7a52, #4caf50)" style=""
> >
<div style="font-size: 2.2rem">🎉</div> <div style="font-size: 2.2rem">🎉</div>
<div <div

1195
style.css

File diff suppressed because it is too large Load Diff