<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>第03讲: 简单的枚举法(有序枚举)</title>
<style>
/* 基础重置 */
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
/* 隐藏滚动条 */
::-webkit-scrollbar { display: none; width: 0 !important; height: 0 !important; }
* { -ms-overflow-style: none; scrollbar-width: none; }
html, body { margin: 0; padding: 0; background: #F0F0F0; overflow: hidden; height: 100vh; }
/* 主容器 */
#app { height: 100vh; max-width: 480px; margin: 0 auto; background: #fff; display: flex; flex-direction: column; }
.content-area { flex: 1; overflow-y: auto; padding-bottom: 80px; scroll-behavior: smooth; background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%); }
/* 底部导航 - 关键! */
.bottom-nav {
position: fixed; bottom: 0; left: 50%; transform: translateX(-50%);
width: 100%; max-width: 480px; height: 70px;
background: white; border-top: 1px solid #e0e0e0;
display: flex; justify-content: space-around; align-items: center; z-index: 9999;
box-shadow: 0 -4px 15px rgba(0,0,0,0.06);
}
.nav-item { flex: 1; display: flex; flex-direction: column; align-items: center; cursor: pointer; padding: 8px 0; transition: all 0.3s; border-radius: 8px; margin: 0 5px; }
.nav-item.active { color: #ff7eb3; background-color: rgba(255, 126, 179, 0.1); }
.nav-item:hover { background-color: rgba(0,0,0,0.02); }
.nav-icon { font-size: 24px; margin-bottom: 4px; }
.nav-label { font-size: 12px; font-weight: 500; }
/* 通用页面样式 */
.page-header {
text-align: center; font-size: 28px; font-weight: bold; color: #333;
margin: 30px 20px 20px; padding-bottom: 10px;
border-bottom: 3px dotted #a8edea;
}
.card { background: white; border-radius: 20px; padding: 25px; margin: 20px; box-shadow: 0 10px 30px rgba(0,0,0,0.08); }
/* 页面1: 概念引入 */
.intro-page .intro-title { font-size: 32px; font-weight: bold; text-align: center; margin: 40px 0 20px; color: #333; }
.intro-emoji { font-size: 80px; text-align: center; margin: 20px 0; }
.story-text { font-size: 18px; line-height: 1.6; color: #555; padding: 0 10px; margin-bottom: 30px; text-align: justify; }
.concept-box { background: linear-gradient(135deg, #e0f7fa 0%, #bbdefb 100%); padding: 20px; border-radius: 15px; margin: 25px 20px; }
.concept-item { margin-bottom: 15px; padding-left: 10px; border-left: 4px solid #ff7eb3; font-size: 16px; line-height: 1.5; }
.listen-btn { display: block; width: 80%; margin: 40px auto; padding: 18px; background: linear-gradient(to right, #ff7eb3, #ff758c); color: white; border: none; border-radius: 50px; font-size: 18px; font-weight: bold; cursor: pointer; box-shadow: 0 8px 20px rgba(255, 126, 179, 0.4); transition: transform 0.2s; }
.listen-btn:active { transform: scale(0.97); }
/* 页面2: 演示动画 */
.demo-page { padding: 15px; }
.tab-nav {
display: flex; background: #f8f9fa; border-radius: 50px; padding: 6px; margin: 10px 20px 25px;
box-shadow: inset 0 2px 5px rgba(0,0,0,0.06);
}
.tab-item {
flex: 1; padding: 12px; text-align: center; cursor: pointer; border-radius: 50px; font-weight: 500;
transition: all 0.3s;
}
.tab-item.active { background: #667eea; color: white; box-shadow: 0 4px 10px rgba(102, 126, 234, 0.3); }
.demo-section { display: none; }
.demo-section.active { display: block; }
/* 动画区域 */
.animation-area {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-radius: 20px; padding: 20px; height: 360px; position: relative; overflow: hidden;
box-shadow: inset 0 0 20px rgba(0,0,0,0.05);
display: flex; align-items: center; justify-content: center;
}
.svg-container { width: 100%; height: 100%; min-height: 320px; }
/* 公式和控制栏样式 */
.math-area {
background: white; border: 2px solid #667eea; border-radius: 12px;
padding: 12px; margin: 20px 0 15px; min-height: 90px;
display: flex; align-items: center; justify-content: center;
}
.math-formula { font-size: 20px; font-weight: bold; color: #333; text-align: center; }
.step-explanation {
background: #f0f8ff; padding: 15px; border-radius: 12px; color: #444; font-size: 16px;
text-align: center; margin-bottom: 20px; line-height: 1.5;
border: 1px dashed #a8edea;
}
/* 动画控制按钮 (悬浮在动画区域左右) */
.step-btn {
position: absolute; top: 50%; transform: translateY(-50%);
background: rgba(255, 255, 255, 0.9); color: #667eea;
border: 2px solid #667eea; border-radius: 50%;
width: 50px; height: 50px; font-size: 20px; cursor: pointer;
z-index: 100; box-shadow: 0 5px 15px rgba(0,0,0,0.1);
display: flex; align-items: center; justify-content: center;
transition: all 0.2s;
}
.step-btn:disabled { background: #eee; color: #aaa; border-color: #ccc; cursor: not-allowed; }
.step-btn:active:not(:disabled) { transform: translateY(-50%) scale(0.9); }
.prev-btn { left: 15px; }
.next-btn { right: 15px; }
/* 页面3: 讲解页 */
.explain-page { padding-bottom: 30px; }
.explain-tab-nav { margin: 10px 20px 20px; }
.example-card {
background: #fff; border-radius: 16px; padding: 20px; margin: 15px 20px;
border-left: 6px solid #ffb347; box-shadow: 0 5px 15px rgba(0,0,0,0.05);
}
.example-card h4 { margin-top: 0; color: #333; border-bottom: 1px solid #eee; padding-bottom: 8px; }
.example-card p { margin: 8px 0; line-height: 1.5; }
.example-card .answer { color: #ff7eb3; font-weight: bold; }
.example-card .explanation { background: #f9f9f9; padding: 10px; border-radius: 8px; font-size: 15px; color: #555; }
/* 页面4 & 5: 练习/奥数页 */
.question-card { background: white; border-radius: 20px; padding: 25px; margin: 20px; box-shadow: 0 10px 30px rgba(0,0,0,0.08); }
.question-header {
display: flex; justify-content: space-between; align-items: center;
margin-bottom: 25px; padding-bottom: 15px; border-bottom: 2px solid #f0f0f0;
}
.question-number { font-size: 16px; color: #666; }
.score-display { font-size: 18px; font-weight: bold; color: #ff7eb3; }
.question-text {
font-size: 18px; line-height: 1.6; color: #333; margin-bottom: 25px;
padding: 15px; background: #f9f9f9; border-radius: 12px;
}
.options-container { display: flex; flex-direction: column; gap: 15px; margin-bottom: 25px; }
.option-btn {
padding: 16px; text-align: left; border: 2px solid #e0e0e0;
border-radius: 12px; background: white; font-size: 16px; cursor: pointer;
transition: all 0.3s;
}
.option-btn:hover:not(:disabled) { border-color: #a8edea; background: #f0f8ff; }
.option-btn.correct { border-color: #4CAF50; background: #e8f5e9; color: #2e7d32; }
.option-btn.wrong { border-color: #f44336; background: #ffebee; color: #c62828; animation: shake 0.5s; }
.option-btn:disabled { cursor: not-allowed; }
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
.feedback-area {
padding: 20px; border-radius: 12px; margin: 20px 0; font-size: 16px; line-height: 1.5;
}
.feedback-correct { background: #e8f5e9; border: 1px solid #4CAF50; color: #2e7d32; }
.feedback-wrong { background: #ffebee; border: 1px solid #f44336; color: #c62828; }
.next-btn {
display: block; width: 100%; padding: 18px; background: linear-gradient(to right, #667eea, #764ba2);
color: white; border: none; border-radius: 50px; font-size: 18px; font-weight: bold; cursor: pointer;
margin-top: 20px; transition: transform 0.2s;
}
.next-btn:active { transform: scale(0.98); }
.hint-box {
background: #fff3cd; border: 1px solid #ffeaa7; color: #856404; padding: 15px;
border-radius: 10px; margin: 15px 0; font-size: 15px; line-height: 1.5;
}
/* 难度徽章 */
.difficulty-badge {
display: inline-block; padding: 4px 10px; border-radius: 20px;
font-size: 12px; font-weight: bold; margin-left: 10px;
}
.difficulty-1 { background: #c8e6c9; color: #2e7d32; }
.difficulty-2 { background: #fff9c4; color: #f57f17; }
.difficulty-3 { background: #ffccbc; color: #d84315; }
/* 完成页面 */
.completion-page { text-align: center; padding: 60px 20px; }
.completion-emoji { font-size: 80px; margin-bottom: 20px; }
.completion-title { font-size: 28px; font-weight: bold; color: #333; margin-bottom: 20px; }
.final-score { font-size: 50px; font-weight: bold; color: #ff7eb3; margin: 30px 0; }
.restart-btn {
padding: 18px 40px; background: linear-gradient(to right, #ff7eb3, #ff758c);
color: white; border: none; border-radius: 50px; font-size: 18px; font-weight: bold;
cursor: pointer; margin-top: 30px; box-shadow: 0 8px 20px rgba(255, 126, 179, 0.4);
}
/* 页面6: 通关秘籍 */
.secrets-container { padding: 20px; }
.secret-card {
width: 100%; min-height: 300px; background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
border-radius: 25px; padding: 30px; margin-bottom: 25px; box-shadow: 0 15px 35px rgba(0,0,0,0.15);
display: flex; flex-direction: column; justify-content: center;
}
.secret-card:nth-child(2) { background: linear-gradient(135deg, #fad0c4 0%, #ffd1ff 100%); }
.secret-card:nth-child(3) { background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%); }
.secret-emoji { font-size: 60px; text-align: center; margin-bottom: 20px; }
.secret-title {
font-size: 26px; font-weight: bold; text-align: center; margin-bottom: 25px;
color: #333; border-bottom: 2px dashed rgba(255,255,255,0.7); padding-bottom: 15px;
}
.secret-content { font-size: 18px; line-height: 1.6; color: #444; text-align: justify; }
/* 点阵背景 */
.dot-bg {
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
background-image: radial-gradient(#d0d0d0 1px, transparent 1px);
background-size: 20px 20px; opacity: 0.15; pointer-events: none;
}
</style>
</head>
<body>
<div id="app">
<div class="content-area">
<!-- 页面1: 概念引入 -->
<div v-show="currentPage === 1" class="intro-page">
<div class="dot-bg"></div>
<div class="intro-title">简单的枚举法</div>
<div class="intro-emoji">🧦</div>
<div class="card">
<div class="story-text">
小朋友们,如果妈妈让你数数家里有几双袜子,你会怎么数?是不是把它们排好队,一双一双地数?这就叫“有序枚举”。如果你乱数,一会儿数这里,一会儿数那里,肯定会数错的!
</div>
</div>
<div class="concept-box">
<div class="concept-item"><strong>枚举</strong>:就是一个一个地数出来,不遗漏。</div>
<div class="concept-item"><strong>有序</strong>:按照一定的顺序数,比如从小到大,或者按颜色分。</div>
<div class="concept-item"><strong>不重不漏</strong>:这是枚举法最重要的目标!</div>
</div>
<button class="listen-btn" @click="speak('大家好,今天我们来学习简单的枚举法。枚举就是一个一个地数出来,不遗漏。有序就是按照一定的顺序数,比如从小到大,或者按颜色分。记住,不重不漏是枚举法最重要的目标!')">
🔊 听老师讲解
</button>
</div>
<!-- 页面2: 演示动画 -->
<div v-show="currentPage === 2" class="demo-page">
<div class="tab-nav">
<div class="tab-item" :class="{active: demoTab === 'direct'}" @click="switchDemoTab('direct')">气球配对</div>
<div class="tab-item" :class="{active: demoTab === 'theory'}" @click="switchDemoTab('theory')">分类计数</div>
</div>
<!-- 气球配对 Tab -->
<div class="demo-section" :class="{active: demoTab === 'direct'}">
<div class="animation-area">
<svg class="svg-container" viewBox="0 0 400 330" id="balloon-svg">
<!-- 背景 -->
<rect x="0" y="0" width="400" height="330" fill="#f8f9fa" />
<!-- 未配对的气球 (初始状态) -->
<g id="balloons-ungrouped" v-if="directStep === 0">
<circle cx="80" cy="80" r="25" fill="#ff6b6b" />
<circle cx="150" cy="80" r="25" fill="#ff6b6b" />
<circle cx="220" cy="80" r="25" fill="#ffd166" />
<circle cx="290" cy="80" r="25" fill="#ffd166" />
<circle cx="110" cy="160" r="25" fill="#06d6a0" />
<circle cx="250" cy="160" r="25" fill="#06d6a0" />
<text x="80" y="80" text-anchor="middle" dy=".3em" fill="white" font-weight="bold">1</text>
<text x="150" y="80" text-anchor="middle" dy=".3em" fill="white" font-weight="bold">1</text>
<text x="220" y="80" text-anchor="middle" dy=".3em" fill="white" font-weight="bold">2</text>
<text x="290" y="80" text-anchor="middle" dy=".3em" fill="white" font-weight="bold">2</text>
<text x="110" y="160" text-anchor="middle" dy=".3em" fill="white" font-weight="bold">3</text>
<text x="250" y="160" text-anchor="middle" dy=".3em" fill="white" font-weight="bold">3</text>
</g>
<!-- 已配对的红色气球组 -->
<g id="red-pair" v-if="directStep >= 1">
<circle cx="100" cy="100" r="25" fill="#ff6b6b" />
<circle cx="160" cy="100" r="25" fill="#ff6b6b" />
<line x1="100" y1="125" x2="160" y2="125" stroke="#333" stroke-width="2" stroke-dasharray="5,5" />
<text x="130" y="180" text-anchor="middle" font-size="18" fill="#333">红色气球:1对</text>
</g>
<!-- 已配对的黄色气球组 -->
<g id="yellow-pair" v-if="directStep >= 2">
<circle cx="100" cy="200" r="25" fill="#ffd166" />
<circle cx="160" cy="200" r="25" fill="#ffd166" />
<line x1="100" y1="225" x2="160" y2="225" stroke="#333" stroke-width="2" stroke-dasharray="5,5" />
<text x="130" y="280" text-anchor="middle" font-size="18" fill="#333">黄色气球:1对</text>
</g>
<!-- 已配对的蓝色气球组 -->
<g id="blue-pair" v-if="directStep >= 3">
<circle cx="240" cy="150" r="25" fill="#06d6a0" />
<circle cx="300" cy="150" r="25" fill="#06d6a0" />
<line x1="240" y1="175" x2="300" y2="175" stroke="#333" stroke-width="2" stroke-dasharray="5,5" />
<text x="270" y="230" text-anchor="middle" font-size="18" fill="#333">蓝色气球:1对</text>
</g>
<!-- 总结文字 -->
<text v-if="directStep === 3" x="200" y="310" text-anchor="middle" font-size="24" font-weight="bold" fill="#667eea">分类数,不乱套</text>
</svg>
<button class="step-btn prev-btn" @click="prevDirectStep" :disabled="directStep === 0">◀</button>
<button class="step-btn next-btn" @click="nextDirectStep" :disabled="directStep === 3">▶</button>
</div>
<div class="math-area">
<div class="math-formula">
<span v-if="directStep === 0">红、黄、蓝气球各两个,乱糟糟的</span>
<span v-if="directStep === 1">先数红色气球:1对</span>
<span v-if="directStep === 2">再数黄色气球:1对,总共 1 + 1 = 2 对</span>
<span v-if="directStep === 3">最后数蓝色气球:1对,总共 1 + 1 + 1 = 3 对</span>
</div>
</div>
<div class="step-explanation">
<span v-if="directStep === 0">小丑面前有红、黄、蓝三种颜色的气球各两个,需要数一数有多少对气球。</span>
<span v-if="directStep === 1"><strong>第一步:分类。</strong> 先把所有红色的气球找出来放在一起,数一数:有2个,正好是1对。</span>
<span v-if="directStep === 2"><strong>第二步:继续分类。</strong> 接着把黄色的气球找出来,也有2个,又是1对。现在已经找到2对了。</span>
<span v-if="directStep === 3"><strong>第三步:完成枚举。</strong> 最后把蓝色的气球找出来,也有2个,是第3对。按照颜色分类,有序地数,一共是3对气球,不重不漏!</span>
</div>
</div>
<!-- 分类计数 Tab (原理演示) -->
<div class="demo-section" :class="{active: demoTab === 'theory'}">
<div class="animation-area" style="display: flex; align-items: center; justify-content: center;">
<div style="text-align: center; padding: 20px;">
<div style="font-size: 70px; margin-bottom: 20px;">🔢</div>
<div style="font-size: 24px; font-weight: bold; color: #333; margin-bottom: 15px;">有序枚举口诀</div>
<div style="font-size: 20px; color: #555; line-height: 1.6; background: white; padding: 20px; border-radius: 15px; box-shadow: 0 5px 15px rgba(0,0,0,0.05);">
先分类,再排序,<br>
一类一类数仔细,<br>
从小到大有规律,<br>
<strong style="color: #ff7eb3;">不重不漏</strong>是目的!
</div>
</div>
</div>
<div class="step-explanation">
枚举就像整理房间:先把玩具、书本、衣服<strong>分类</strong>放好,然后在每一类里<strong>有序</strong>地数,这样就能数得又快又准,保证<strong>不重不漏</strong>!
</div>
</div>
</div>
<!-- 页面3: 讲解页 -->
<div v-show="currentPage === 3" class="explain-page">
<div class="page-header">📝 例题讲解</div>
<div class="tab-nav explain-tab-nav">
<div class="tab-item" :class="{active: explainTab === 'example1'}" @click="explainTab='example1'">基础题</div>
<div class="tab-item" :class="{active: explainTab === 'example2'}" @click="explainTab='example2'">进阶题</div>
<div class="tab-item" :class="{active: explainTab === 'example3'}" @click="explainTab='example3'">易错题</div>
</div>
<div v-show="explainTab === 'example1'">
<div class="example-card">
<h4>📌 例题1:基础题</h4>
<p><strong>题目</strong>:用数字 1、2 能组成几个不同的两位数?</p>
<p class="answer">答案:2 个</p>
<div class="explanation">
<strong>解析</strong>:我们按十位是 1 的:12;十位是 2 的:21。一共 2 个。
</div>
</div>
</div>
<div v-show="explainTab === 'example2'">
<div class="example-card">
<h4>🚀 例题2:进阶题</h4>
<p><strong>题目</strong>:从 A 地到 B 地有 2 条路,从 B 地到 C 地有 3 条路。从 A 到 C 共有几种走法?</p>
<p class="answer">答案:6 种</p>
<div class="explanation">
<strong>解析</strong>:我们可以画图。走第一条 A-B 路时,后面有 3 种走法;走第二条 A-B 路时,后面也有 3 种。3 + 3 = 6。
</div>
</div>
</div>
<div v-show="explainTab === 'example3'">
<div class="example-card">
<h4>⚠️ 例题3:易错题</h4>
<p><strong>题目</strong>:小明有足够的 5 角、2 角、1 角硬币,凑 6 角钱,有几种付钱方法?</p>
<p class="answer">答案:5 种</p>
<div class="explanation">
<strong>解析</strong>:有序枚举很重要!<br>
1. 全用1角:6个1角。<br>
2. 用1个2角:2+1+1+1+1。<br>
3. 用2个2角:2+2+1+1。<br>
4. 用3个2角:2+2+2。<br>
5. 用1个5角和1个1角:5+1。<br>
<strong>【易错点】</strong>容易漏掉“3个2角”这种方法,或者忘记硬币可以重复使用。按照从大到小的顺序枚举,先考虑用5角,再考虑用2角,最后用1角,就能不重不漏。
</div>
</div>
</div>
</div>
<!-- 页面4: 课内练习 -->
<div v-show="currentPage === 4" class="practice-page">
<div v-if="!practiceCompleted">
<div class="question-card">
<div class="question-header">
<div class="question-number">第 {{currentQuestion + 1}}/{{practiceQuestions.length}} 题</div>
<div class="score-display">⭐ {{score}}分</div>
</div>
<div class="question-text" v-html="practiceQuestions[currentQuestion].text"></div>
<div class="options-container">
<button
v-for="(option, index) in practiceQuestions[currentQuestion].options"
:key="'opt'+index"
class="option-btn"
:class="{
'correct': answered && option.correct,
'wrong': answered && selectedOption === index && !option.correct
}"
@click="selectOption(index, option.correct)"
:disabled="answered"
>
{{option.text}}
</button>
</div>
<div v-if="answered" class="feedback-area" :class="isCorrect ? 'feedback-correct' : 'feedback-wrong'">
<div v-if="isCorrect">
<strong>🎉 太棒了!</strong><br>
{{practiceQuestions[currentQuestion].explanation}}
</div>
<div v-else>
<strong>💡 让我们一起分析:</strong><br>
{{practiceQuestions[currentQuestion].explanation}}
</div>
</div>
<button v-if="answered" class="next-btn" @click="nextQuestion">
{{currentQuestion < practiceQuestions.length - 1 ? '下一题 →' : '完成练习 ✓'}}
</button>
</div>
</div>
<div v-else class="completion-page">
<div class="completion-emoji">🎊</div>
<div class="completion-title">课内练习完成!</div>
<div class="final-score">{{score}}分</div>
<button class="restart-btn" @click="switchPage(5)">挑战奥数题 →</button>
</div>
</div>
<!-- 页面5: 奥数挑战 -->
<div v-show="currentPage === 5" class="practice-page">
<div v-if="!olympiadCompleted">
<div class="question-card">
<div class="question-header">
<div>
<span class="question-number">第 {{currentOlympiad + 1}}/{{olympiadQuestions.length}} 题</span>
<span class="difficulty-badge" :class="'difficulty-' + olympiadQuestions[currentOlympiad].difficulty">
{{olympiadQuestions[currentOlympiad].difficultyText}}
</span>
</div>
<div class="score-display">⭐ {{olympiadScore}}分</div>
</div>
<div class="question-text" v-html="olympiadQuestions[currentOlympiad].text"></div>
<div class="options-container">
<button
v-for="(option, index) in olympiadQuestions[currentOlympiad].options"
:key="'olopt'+index"
class="option-btn"
:class="{
'correct': olympiadAnswered && option.correct,
'wrong': olympiadAnswered && selectedOlympiadOption === index && !option.correct
}"
@click="selectOlympiadOption(index, option.correct)"
:disabled="olympiadAnswered"
>
{{option.text}}
</button>
</div>
<div v-if="wrongAttempts > 0 && !olympiadAnswered">
<div class="hint-box" v-if="wrongAttempts >= 1">
<strong>💡 提示1:</strong> {{olympiadQuestions[currentOlympiad].hint1}}
</div>
<div class="hint-box" v-if="wrongAttempts >= 2">
<strong>💡 提示2:</strong> {{olympiadQuestions[currentOlympiad].hint2}}
</div>
<div class="hint-box" v-if="wrongAttempts >= 3">
<strong>💡 提示3:</strong> {{olympiadQuestions[currentOlympiad].hint3}}
</div>
</div>
<div v-if="olympiadAnswered" class="feedback-area" :class="isOlympiadCorrect ? 'feedback-correct' : 'feedback-wrong'">
<div v-if="isOlympiadCorrect">
<strong>🏆 厉害!</strong><br>
{{olympiadQuestions[currentOlympiad].explanation}}
</div>
<div v-else>
<strong>📖 详细解析:</strong><br>
{{olympiadQuestions[currentOlympiad].explanation}}
</div>
</div>
<button v-if="olympiadAnswered || wrongAttempts >= 3" class="next-btn" @click="nextOlympiad">
{{currentOlympiad < olympiadQuestions.length - 1 ? '下一题 →' : '完成挑战 ✓'}}
</button>
</div>
</div>
<div v-else class="completion-page">
<div class="completion-emoji">🏆</div>
<div class="completion-title">奥数挑战完成!</div>
<div class="final-score">{{olympiadScore}}分</div>
<button class="restart-btn" @click="switchPage(6)">查看通关秘籍 →</button>
</div>
</div>
<!-- 页面6: 通关秘籍 -->
<div v-show="currentPage === 6" class="secrets-container">
<div class="page-header">🎁 通关秘籍</div>
<div class="secret-card">
<div class="secret-emoji">🧭</div>
<div class="secret-title">秘籍1:有序思考</div>
<div class="secret-content">数数要按顺序,从大到小或从左到右,千万别乱指。</div>
</div>
<div class="secret-card">
<div class="secret-emoji">🗂️</div>
<div class="secret-title">秘籍2:分类讨论</div>
<div class="secret-content">情况复杂时,先分几类,一类一类数,最后加起来。</div>
</div>
<div class="secret-card">
<div class="secret-emoji">🏷️</div>
<div class="secret-title">秘籍3:标记法</div>
<div class="secret-content">数图形时,数过一个就在上面标个数字,这样就不会重复啦。</div>
</div>
</div>
</div>
<!-- 底部导航 -->
<div class="bottom-nav">
<div class="nav-item" :class="{active: currentPage === 1}" @click="switchPage(1)">
<div class="nav-icon">💡</div>
<div class="nav-label">概念</div>
</div>
<div class="nav-item" :class="{active: currentPage === 2}" @click="switchPage(2)">
<div class="nav-icon">🎬</div>
<div class="nav-label">演示</div>
</div>
<div class="nav-item" :class="{active: currentPage === 3}" @click="switchPage(3)">
<div class="nav-icon">📝</div>
<div class="nav-label">讲解</div>
</div>
<div class="nav-item" :class="{active: currentPage === 4}" @click="switchPage(4)">
<div class="nav-icon">✏️</div>
<div class="nav-label">练习</div>
</div>
<div class="nav-item" :class="{active: currentPage === 5}" @click="switchPage(5)">
<div class="nav-icon">🏆</div>
<div class="nav-label">奥数</div>
</div>
<div class="nav-item" :class="{active: currentPage === 6}" @click="switchPage(6)">
<div class="nav-icon">🎁</div>
<div class="nav-label">秘籍</div>
</div>
</div>
</div>
<!-- 隐藏的音频元素用于微信语音 -->
<audio id="tts-audio" style="display: none;"></audio>
<script src="https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/assets/js/vue.global.prod.js"></script>
<script src="https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/assets/js/gsap.min.js"></script>
<script src="https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/assets/js/confetti.browser.min.js"></script>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
currentPage: 1,
demoTab: 'direct',
explainTab: 'example1',
directStep: 0,
theoryStep: 0,
// 练习题数据
practiceQuestions: [
{ text: '用 1、2、3 能组成几个不同的两位数?', options: [{text: 'A. 4', correct: false}, {text: 'B. 5', correct: false}, {text: 'C. 6', correct: true}, {text: 'D. 9', correct: false}], explanation: '我们按十位是1的:12, 13;十位是2的:21, 23;十位是3的:31, 32。一共2+2+2=6个。' },
{ text: '小红有红、黄、蓝三件上衣,和白、黑两条裤子。她有多少种搭配方法?', options: [{text: 'A. 5', correct: false}, {text: 'B. 6', correct: true}, {text: 'C. 8', correct: false}, {text: 'D. 9', correct: false}], explanation: '选一件红上衣,可以配白裤或黑裤(2种)。黄上衣也有2种搭配,蓝上衣也有2种。2+2+2=6。' },
{ text: '数一数图中有几个长方形?(图为一个“日”字)', options: [{text: 'A. 2', correct: false}, {text: 'B. 3', correct: true}, {text: 'C. 4', correct: false}, {text: 'D. 1', correct: false}], explanation: '先数单个的小长方形:有2个。再数两个拼起来的大长方形,有1个。一共2+1=3个。' },
{ text: '往返于甲、乙、丙三地的客车,需要准备几种车票?', options: [{text: 'A. 3', correct: false}, {text: 'B. 4', correct: false}, {text: 'C. 5', correct: false}, {text: 'D. 6', correct: true}], explanation: '需要准备的车票有:甲到乙,乙到甲;甲到丙,丙到甲;乙到丙,丙到乙。一共是3对,每对2种,所以是3×2=6种。' },
{ text: '硬币凑数:用 1 角、2 角、5 角凑 7 角钱,有几种方法?', options: [{text: 'A. 4', correct: false}, {text: 'B. 5', correct: false}, {text: 'C. 6', correct: true}, {text: 'D. 3', correct: false}], explanation: '按顺序罗列:1. 5+2;2. 5+1+1;3. 2+2+2+1;4. 2+2+1+1+1;5. 2+1+1+1+1+1;6. 1+1+1+1+1+1+1。一共6种方法。' },
{ text: '书架上有 3 本故事书和 2 本漫画书,小明想买一本故事书和一本漫画书,有几种选法?', options: [{text: 'A. 5', correct: false}, {text: 'B. 6', correct: true}, {text: 'C. 9', correct: false}, {text: 'D. 4', correct: false}], explanation: '每本故事书都可以和2本漫画书搭配,所以有3种选择故事书的方式,每种对应2种漫画书选择。总共是3 × 2 = 6种选法。' },
{ text: '数三角形:一个三角形内部画一条线,共有几个三角形?', options: [{text: 'A. 2', correct: false}, {text: 'B. 3', correct: true}, {text: 'C. 4', correct: false}, {text: 'D. 1', correct: false}], explanation: '左边一个小三角形,右边一个小三角形,加上整体一个大三角形。一共是3个。' },
{ text: '有些三位数,它们的各位数字之和是 4,这样的三位数有几个?', options: [{text: 'A. 5', correct: false}, {text: 'B. 6', correct: false}, {text: 'C. 10', correct: true}, {text: 'D. 8', correct: false}], explanation: '百位最小是1。百位是1:103, 112, 121, 130(4个)。百位是2:202, 211, 220(3个)。百位是3:301, 310(2个)。百位是4:400(1个)。一共4+3+2+1=10个。' }
],
currentQuestion: 0,
score: 0,
answered: false,
selectedOption: null,
isCorrect: false,
practiceCompleted: false,
// 奥数题数据
olympiadQuestions: [
{ text: '数一数下图中有多少个正方形?(2x2的方格图)', options: [{text: 'A. 4', correct: false}, {text: 'B. 5', correct: true}, {text: 'C. 9', correct: false}, {text: 'D. 6', correct: false}], hint1: '分类数。先数最小的正方形。', hint2: '由1个格子组成的正方形有4个。', hint3: '由4个格子组成的大正方形有1个。', explanation: '分类枚举:由1个格子组成的正方形有4个;由4个格子组成的大正方形有1个。总共4 + 1 = 5个。', difficulty: 1, difficultyText: '⭐' },
{ text: '小兔、小狗、小猫排队照相,小兔不想站在中间,一共有几种排法?', options: [{text: 'A. 6', correct: false}, {text: 'B. 4', correct: true}, {text: 'C. 2', correct: false}, {text: 'D. 3', correct: false}], hint1: '先算出所有可能的排队方法。', hint2: '所有人全排列有 3×2×1=6 种。', hint3: '减去小兔站在中间的情况。', explanation: '排除法:总共有3×2×1=6种排法。小兔站在中间的情况有2种(狗兔猫,猫兔狗)。所以小兔不在中间的排法有6 - 2 = 4种。', difficulty: 2, difficultyText: '⭐⭐' },
{ text: '把 5 个苹果分给 3 个小朋友,每人至少 1 个,有几种分法?', options: [{text: 'A. 3', correct: false}, {text: 'B. 6', correct: true}, {text: 'C. 9', correct: false}, {text: 'D. 5', correct: false}], hint1: '先保证每人至少有1个苹果。', hint2: '先每人给1个,还剩2个苹果需要分。', hint3: '思考怎么分剩下的2个苹果。', explanation: '先给每人1个苹果,剩下2个。分剩下的2个苹果:① (2,0,0):有3种分法(给其中一个人)。② (1,1,0):有3种分法(给其中两个人各一个)。总共3+3=6种分法。', difficulty: 3, difficultyText: '⭐⭐⭐' },
{ text: '从 1 到 20 这 20 个数中,有多少个数字是含有 \"1\" 的?', options: [{text: 'A. 10', correct: false}, {text: 'B. 11', correct: true}, {text: 'C. 12', correct: false}, {text: 'D. 9', correct: false}], hint1: '分成个位是1和十位是1两种情况来数。', hint2: '个位是1的数有:1, 11。', hint3: '十位是1的数有:10, 11, 12...19 (共10个)。注意别重复数。', explanation: '分类枚举:1. 个位是1:1, 11(2个)。2. 十位是1:10, 11, 12, 13, 14, 15, 16, 17, 18, 19(10个)。注意数字11被数了两次,所以总共有 2 + 10 - 1 = 11 个不同的数含有数字\"1\"。', difficulty: 2, difficultyText: '⭐⭐' },
{ text: '一本书共 20 页,页码中一共用了多少个数字 \"1\"?', options: [{text: 'A. 10', correct: false}, {text: 'B. 11', correct: false}, {text: 'C. 12', correct: true}, {text: 'D. 13', correct: false}], hint1: '分别数一数个位上的1和十位上的1。', hint2: '个位是1的页码:1, 11(出现2次1)。', hint3: '十位是1的页码:10, 11...19(共10个数,每个数的十位是1)。注意数字11算了两次。', explanation: '个位是1的页码:1, 11(出现2次数字1)。十位是1的页码:10, 11, 12, 13, 14, 15, 16, 17, 18, 19(共10个数,每个数的十位是1)。特别注意页码11,它的个位和十位都是1,所以被算了两次。总共使用的数字\"1\"的个数是:2(个位)+ 10(十位)= 12次。', difficulty: 2, difficultyText: '⭐⭐' },
{ text: '数线段:一条线上有 4 个点,一共有多少条线段?', options: [{text: 'A. 4', correct: false}, {text: 'B. 5', correct: false}, {text: 'C. 6', correct: true}, {text: 'D. 7', correct: false}], hint1: '有序枚举:以第一个点为起点。', hint2: '以第1个点开头的线段:有3条。', hint3: '接着以第2个点、第3个点为起点枚举。', explanation: '有序枚举:以第1个点开头的线段有3条(1-2, 1-3, 1-4)。以第2个点开头的线段有2条(2-3, 2-4)。以第3个点开头的线段有1条(3-4)。总共3+2+1=6条线段。', difficulty: 1, difficultyText: '⭐' },
{ text: '老师要从 4 名男生和 3 名女生中各选出 1 人做主持人,有几种选法?', options: [{text: 'A. 7', correct: false}, {text: 'B. 12', correct: true}, {text: 'C. 10', correct: false}, {text: 'D. 14', correct: false}], hint1: '这是分步计数问题。', hint2: '第一步选男生有4种选法。', hint3: '第二步选女生有3种选法。', explanation: '分步计数(乘法原理):选男生有4种选择,选女生有3种选择。搭配起来总共有 4 × 3 = 12 种不同的选法。', difficulty: 1, difficultyText: '⭐' },
{ text: '用红、黄、蓝三种颜色给地图上的三个国家涂色,要求相邻国家颜色不同,三个国家排成一行。有多少种涂法?', options: [{text: 'A. 6', correct: false}, {text: 'B. 12', correct: true}, {text: 'C. 27', correct: false}, {text: 'D. 9', correct: false}], hint1: '分步给第一个、第二个、第三个国涂色。', hint2: '第一个国家有3种颜色可选。', hint3: '第二个国家不能和第一个同色,有2种选择。第三个国不能和第二个同色。', explanation: '分步计数:第一步,给第一个国家涂色,有3种选择。第二步,给第二个国家涂色,不能和第一个国家同色,所以有2种选择。第三步,给第三个国家涂色,不能和第二个国家同色,所以也有2种选择(可以和第一个国家同色)。根据乘法原理,总共有 3 × 2 × 2 = 12 种涂法。', difficulty: 3, difficultyText: '⭐⭐⭐' }
],
currentOlympiad: 0,
olympiadScore: 0,
olympiadAnswered: false,
selectedOlympiadOption: null,
isOlympiadCorrect: false,
olympiadCompleted: false,
wrongAttempts: 0
};
},
mounted() {
this.runDirectAnimation();
},
methods: {
switchPage(page) {
// 停止语音
this.stopSpeak();
if (window.speechSynthesis) {
window.speechSynthesis.cancel();
}
const audio = document.getElementById('tts-audio');
if (audio) {
audio.pause();
audio.currentTime = 0;
}
this.currentPage = page;
if (page === 2) {
this.directStep = 0;
this.theoryStep = 0;
this.runDirectAnimation();
}
if (page === 4 && this.practiceCompleted) {
this.currentQuestion = 0;
this.score = 0;
this.practiceCompleted = false;
this.answered = false;
}
if (page === 5 && this.olympiadCompleted) {
this.currentOlympiad = 0;
this.olympiadScore = 0;
this.olympiadCompleted = false;
this.olympiadAnswered = false;
this.wrongAttempts = 0;
}
},
stopSpeak() {
const isWeChat = /MicroMessenger/i.test(navigator.userAgent);
if (isWeChat) {
const audio = document.getElementById('tts-audio');
if (audio) {
audio.pause();
audio.currentTime = 0;
}
} else {
if (window.speechSynthesis) {
window.speechSynthesis.cancel();
}
}
},
switchDemoTab(tab) {
this.stopSpeak();
this.demoTab = tab;
},
// 语音合成
speak(text) {
const isWeChat = /MicroMessenger/i.test(navigator.userAgent);
if (isWeChat) {
let audio = document.getElementById('tts-audio');
if (!audio) {
audio = document.createElement('audio');
audio.id = 'tts-audio';
audio.style.display = 'none';
document.body.appendChild(audio);
}
const url = `https://www.xinghuo.tv/wp-content/themes/xinghuo-tv/tts.php?text=${encodeURIComponent(text)}&t=${Date.now()}`;
audio.src = url;
audio.play().catch(err => { console.log('语音播放失败:', err); });
} else {
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'zh-CN';
utterance.rate = 0.9;
window.speechSynthesis.speak(utterance);
}
},
// GSAP 动画逻辑 (气球配对)
runDirectAnimation() {
if (typeof gsap === 'undefined') return;
const tl = gsap.timeline();
tl.kill(); // 清除之前的动画
// 根据步骤设置初始状态 (SVG组的显示/隐藏由v-if控制)
// 为每个步骤添加生动的动画
if (this.directStep === 1) {
// 红色气球移动到左上角并配对
tl.fromTo("#red-pair circle", {scale: 0, opacity: 0}, {scale: 1, opacity: 1, duration: 0.8, stagger: 0.2, ease: "bounce.out"})
.fromTo("#red-pair line", {scaleX: 0}, {scaleX: 1, duration: 0.5, ease: "power2.out"}, "-=0.4");
} else if (this.directStep === 2) {
// 黄色气球移动到下方并配对
tl.fromTo("#yellow-pair circle", {scale: 0, opacity: 0}, {scale: 1, opacity: 1, duration: 0.8, stagger: 0.2, ease: "bounce.out"})
.fromTo("#yellow-pair line", {scaleX: 0}, {scaleX: 1, duration: 0.5, ease: "power2.out"}, "-=0.4");
} else if (this.directStep === 3) {
// 蓝色气球移动到右侧并配对,然后显示总结文字
tl.fromTo("#blue-pair circle", {scale: 0, opacity: 0}, {scale: 1, opacity: 1, duration: 0.8, stagger: 0.2, ease: "bounce.out"})
.fromTo("#blue-pair line", {scaleX: 0}, {scaleX: 1, duration: 0.5, ease: "power2.out"}, "-=0.4")
.fromTo("text:contains('分类数')", {y: 330, opacity: 0}, {y: 310, opacity: 1, duration: 0.8, ease: "back.out(1.7)"});
}
},
// 步骤导航 (不自动播放语音)
nextDirectStep() {
if (this.directStep < 3) {
this.directStep++;
this.runDirectAnimation();
}
},
prevDirectStep() {
if (this.directStep > 0) {
this.directStep--;
this.runDirectAnimation();
}
},
// 练习题逻辑
selectOption(index, correct) {
if (this.answered) return;
this.answered = true;
this.selectedOption = index;
this.isCorrect = correct;
if (correct) {
this.score += 20;
if (typeof confetti !== 'undefined') {
confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } });
}
} else {
// 错误反馈震动
if (navigator.vibrate) {
navigator.vibrate(200);
}
}
},
nextQuestion() {
if (this.currentQuestion < this.practiceQuestions.length - 1) {
this.currentQuestion++;
this.answered = false;
this.selectedOption = null;
} else {
this.practiceCompleted = true;
}
},
// 奥数题逻辑
selectOlympiadOption(index, correct) {
if (this.olympiadAnswered) return;
this.selectedOlympiadOption = index;
if (correct) {
this.olympiadAnswered = true;
this.isOlympiadCorrect = true;
// 得分根据尝试次数
if (this.wrongAttempts === 0) {
this.olympiadScore += 30;
} else if (this.wrongAttempts === 1) {
this.olympiadScore += 20;
} else {
this.olympiadScore += 10;
}
if (typeof confetti !== 'undefined') {
confetti({ particleCount: 150, spread: 100, origin: { y: 0.6 } });
}
} else {
this.wrongAttempts++;
if (this.wrongAttempts >= 3) {
this.olympiadAnswered = true;
this.isOlympiadCorrect = false;
}
// 错误反馈震动
if (navigator.vibrate) {
navigator.vibrate(200);
}
}
},
nextOlympiad() {
if (this.currentOlympiad < this.olympiadQuestions.length - 1) {
this.currentOlympiad++;
this.olympiadAnswered = false;
this.selectedOlympiadOption = null;
this.isOlympiadCorrect = false;
this.wrongAttempts = 0;
} else {
this.olympiadCompleted = true;
}
}
}
}).mount('#app');
</script>
</body>
</html>
💡 这段代码完全由 AI 生成。
登录后可复制完整代码