<!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>乘法分配律(拆数法)</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: #F0F4F8; /* 柔和的蓝灰背景 */
background-image:
radial-gradient(#E1E8ED 15%, transparent 16%),
radial-gradient(#E1E8ED 15%, transparent 16%);
background-size: 20px 20px;
background-position: 0 0, 10px 10px;
overflow: hidden; height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
#app { height: 100vh; max-width: 480px; margin: 0 auto; background: rgba(255,255,255,0.95); display: flex; flex-direction: column; box-shadow: 0 0 30px rgba(0,0,0,0.05); }
.content-area { flex: 1; overflow-y: auto; padding-bottom: 90px; scroll-behavior: smooth; }
/* ================= 通用 UI 组件 ================= */
/* 按钮渐变 */
.btn-gradient-primary {
background: linear-gradient(135deg, #FF9A9E 0%, #FECFEF 100%);
color: #555; font-weight: bold; border: none;
box-shadow: 0 4px 15px rgba(255, 154, 158, 0.4);
}
.btn-gradient-blue {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white; border: none;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}
/* 顶部 Tab */
.tab-nav { display: flex; background: #fff; padding: 10px 15px 0; border-radius: 0 0 20px 20px; box-shadow: 0 4px 10px rgba(0,0,0,0.03); z-index: 10; }
.tab-item {
flex: 1; text-align: center; padding: 12px 5px; cursor: pointer;
font-size: 15px; color: #999; font-weight: 500;
position: relative; transition: all 0.3s;
}
.tab-item.active { color: #667eea; font-weight: 800; transform: scale(1.05); }
.tab-item.active::after {
content: ''; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%);
width: 20px; height: 4px; background: #667eea; border-radius: 4px;
}
/* 底部导航 */
.bottom-nav {
position: fixed; bottom: 0; left: 50%; transform: translateX(-50%);
width: 100%; max-width: 480px; height: 80px;
background: white;
border-radius: 24px 24px 0 0;
box-shadow: 0 -5px 20px rgba(0,0,0,0.05);
display: flex; justify-content: space-around; align-items: center; z-index: 9999;
padding-bottom: 10px;
}
.nav-item { flex: 1; display: flex; flex-direction: column; align-items: center; cursor: pointer; font-size: 11px; color: #B0B0B0; transition: all 0.3s; }
.nav-item.active { color: #667eea; transform: translateY(-5px); }
.nav-icon { font-size: 22px; margin-bottom: 5px; filter: grayscale(100%); transition: all 0.3s; }
.nav-item.active .nav-icon { filter: grayscale(0%); transform: scale(1.2); }
/* 卡片通用 */
.card-base {
background: white; border-radius: 20px;
box-shadow: 0 8px 20px rgba(149, 157, 165, 0.1);
margin-bottom: 20px; overflow: hidden;
transition: transform 0.2s;
border: 1px solid rgba(0,0,0,0.02);
}
/* 页面动画 */
.page-enter { animation: pageSlideUp 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); }
@keyframes pageSlideUp {
from { opacity: 0; transform: translateY(30px) scale(0.95); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
/* ================= 页面特定样式 ================= */
/* 1. 引入页 */
.intro-page { padding: 40px 25px; text-align: center; }
.intro-hero {
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
border-radius: 30px; padding: 40px 20px; margin-bottom: 30px;
box-shadow: 0 10px 25px rgba(168, 237, 234, 0.4);
}
.intro-emoji { font-size: 70px; margin-bottom: 15px; display: block; animation: float 3s ease-in-out infinite; }
.intro-title { font-size: 26px; font-weight: 900; color: #444; margin-bottom: 10px; letter-spacing: 1px; }
.intro-text { line-height: 1.8; color: #666; text-align: left; font-size: 16px; background: rgba(255,255,255,0.6); padding: 20px; border-radius: 15px; margin-top: 20px; }
.listen-btn {
background: #4facfe; background: linear-gradient(to right, #4facfe 0%, #00f2fe 100%);
color: white; border: none; padding: 16px 50px; border-radius: 50px;
font-size: 18px; font-weight: bold; cursor: pointer;
box-shadow: 0 8px 20px rgba(79, 172, 254, 0.4);
transition: transform 0.2s;
}
.listen-btn:active { transform: scale(0.95); }
@keyframes float { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }
/* 2. 演示页 */
.demo-page { padding: 20px; }
.concept-header {
padding: 18px 25px; color: white; font-weight: bold; font-size: 17px;
display: flex; justify-content: space-between; align-items: center; cursor: pointer;
}
/* 渐变色定义 */
.bg-gradient-pink { background: linear-gradient(120deg, #ff9a9e 0%, #fecfef 100%); }
.bg-gradient-blue { background: linear-gradient(120deg, #89f7fe 0%, #66a6ff 100%); }
.bg-gradient-purple { background: linear-gradient(120deg, #e0c3fc 0%, #8ec5fc 100%); }
.concept-body { padding: 25px; background: #fff; }
.highlight-box {
background: #FFFBF0; border: 2px dashed #FFE082;
padding: 15px; border-radius: 15px; color: #5D4037;
font-size: 15px; line-height: 1.7;
}
.formula-text { font-family: "Courier New", monospace; font-weight: bold; color: #FF9800; display: block; margin-top: 8px; font-size: 16px; }
/* SVG 容器 */
.animation-area {
background: white; border-radius: 24px;
box-shadow: inset 0 0 20px rgba(0,0,0,0.03);
border: 1px solid #eee;
padding: 10px; margin-bottom: 20px;
height: 300px; position: relative; overflow: hidden;
}
.step-btn {
padding: 12px 30px; border-radius: 30px; font-size: 14px; font-weight: bold; cursor: pointer; margin: 0 8px; border: none;
transition: all 0.2s; color: white; background: #ccc;
}
.step-btn.active { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); }
/* 3. 讲解页 */
.explain-page { padding: 20px; }
.expand-card .card-header { padding: 20px 25px; background: #fff; border-bottom: 1px solid transparent; transition: background 0.2s; }
.expand-card.active .card-header { background: #F8F9FF; border-bottom: 1px solid #eee; }
.card-title { font-weight: 800; font-size: 16px; }
.toggle-icon { font-size: 12px; color: #ccc; transition: transform 0.3s; }
/* 4 & 5. 练习与挑战 */
.practice-page { padding: 20px; }
.question-card { padding: 30px 25px; border-radius: 24px; position: relative; }
.question-card::before {
content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 6px;
background: linear-gradient(90deg, #a18cd1 0%, #fbc2eb 100%);
}
.question-text { font-size: 20px; font-weight: bold; color: #333; margin: 20px 0 30px; text-align: center; font-family: "Comic Sans MS", cursive, sans-serif; }
.option-btn {
background: #fff; border: 2px solid #E0E7FF; color: #666;
padding: 18px; border-radius: 16px; font-size: 16px; font-weight: 600;
margin-bottom: 12px; width: 100%; text-align: left; position: relative;
transition: all 0.2s;
}
.option-btn:hover { border-color: #667eea; background: #F0F4FF; }
.option-btn.correct { background: #D1FAE5; border-color: #34D399; color: #065F46; }
.option-btn.wrong { background: #FEE2E2; border-color: #F87171; color: #991B1B; }
/* 6. 秘籍 (全屏横向滚动) */
.secret-page {
padding: 20px 0;
display: flex; flex-direction: column; height: 100%;
}
.secret-header { text-align: center; margin-bottom: 10px; color: #444; font-weight: 900; }
.secret-container {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
gap: 20px;
padding: 10px 20px 40px 20px; /* 底部留白给阴影 */
-webkit-overflow-scrolling: touch;
/* 隐藏滚动条 */
scrollbar-width: none;
-ms-overflow-style: none;
height: 100%;
align-items: center;
}
.secret-container::-webkit-scrollbar { display: none; }
.secret-card {
flex: 0 0 100%; /* 全宽 */
min-height: 420px;
scroll-snap-align: center;
background: white;
border-radius: 30px;
padding: 40px 30px;
box-shadow: 0 15px 35px rgba(0,0,0,0.1), 0 5px 15px rgba(0,0,0,0.05);
display: flex; flex-direction: column; justify-content: center; align-items: center;
position: relative;
transform-style: preserve-3d;
border: 1px solid rgba(255,255,255,0.8);
}
/* 不同的渐变背景给不同的卡片 */
.secret-card:nth-child(1) { background: linear-gradient(135deg, #FFF6B7 0%, #F6416C 100%); }
.secret-card:nth-child(1) .secret-content { background: rgba(255,255,255,0.9); color: #D81B60; }
.secret-card:nth-child(2) { background: linear-gradient(135deg, #84fab0 0%, #8fd3f4 100%); }
.secret-card:nth-child(2) .secret-content { background: rgba(255,255,255,0.9); color: #00838F; }
.secret-card:nth-child(3) { background: linear-gradient(135deg, #e0c3fc 0%, #8ec5fc 100%); }
.secret-card:nth-child(3) .secret-content { background: rgba(255,255,255,0.9); color: #512DA8; }
.secret-title {
font-size: 40px; color: white; margin-bottom: 30px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
font-weight: 900;
}
.secret-content {
border-radius: 20px; padding: 30px; width: 100%;
text-align: center; font-size: 20px; line-height: 1.6; font-weight: bold;
box-shadow: 0 8px 20px rgba(0,0,0,0.05);
}
.swipe-hint {
text-align: center; color: #aaa; font-size: 12px; margin-top: 5px;
animation: bounce 2s infinite;
}
@keyframes bounce { 0%, 20%, 50%, 80%, 100% {transform: translateX(0);} 40% {transform: translateX(-5px);} 60% {transform: translateX(-3px);} }
/* 反馈效果 */
@keyframes vibrate { 0%, 100% { transform: translateX(0); } 20% { transform: translateX(-4px); } 40% { transform: translateX(4px); } 60% { transform: translateX(-4px); } 80% { transform: translateX(4px); } }
.option-btn.vibrate { animation: vibrate 0.4s ease; border-color: #F87171; background: #FEF2F2; }
</style>
</head>
<body>
<div id="app">
<div class="content-area">
<div v-show="currentPage === 1" class="intro-page">
<div class="intro-hero">
<div class="intro-emoji">🎁</div>
<h1 class="intro-title">乘法分配律<br><span style="font-size: 18px; color: #666; font-weight: normal;">(拆数法)</span></h1>
</div>
<div class="intro-text">
<p>💡 <strong>小明的问题:</strong><br>给101个小朋友分糖果,每人23颗。<br><code>101 × 23</code> 怎么算最快?</p>
<div style="height: 1px; background: #eee; margin: 15px 0;"></div>
<p>✨ <strong>魔法口诀:</strong><br>接近整百拆大包,变成两堆分别算!<br><code>100×23</code> 加 <code>1×23</code>,就像切蛋糕一样简单。</p>
</div>
<br>
<button class="listen-btn" @click="speak('欢迎学习乘法分配律拆数法!把接近整百的数拆开算,就像分糖果一样简单。')">
🎧 听老师讲解
</button>
</div>
<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>
<div class="demo-section" v-show="demoTab === 'direct'">
<div class="animation-area">
<svg class="svg-container" viewBox="0 0 400 300" id="direct-animation-svg" style="width:100%; height:100%;">
<defs>
<marker id="arrow" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#888" />
</marker>
<linearGradient id="gradIce" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#E3F2FD;stop-opacity:1" />
<stop offset="100%" style="stop-color:#90CAF9;stop-opacity:1" />
</linearGradient>
</defs>
<g id="block-total">
<rect x="120" y="50" width="160" height="100" rx="10" fill="url(#gradIce)" stroke="#42A5F5" stroke-width="3"/>
<text x="200" y="110" text-anchor="middle" font-size="40" font-weight="bold" fill="#1565C0">101</text>
<text x="200" y="180" text-anchor="middle" font-size="24" fill="#555" id="times-text">× 23</text>
</g>
<g id="hammer" opacity="0">
<rect x="300" y="20" width="15" height="70" fill="#8D6E63" rx="2"/>
<rect x="280" y="10" width="55" height="25" fill="#5D4037" rx="3"/>
</g>
<g id="block-split-group" opacity="0">
<g id="block-100">
<rect x="60" y="80" width="120" height="80" rx="8" fill="#E8F5E9" stroke="#66BB6A" stroke-width="2"/>
<text x="120" y="130" text-anchor="middle" font-size="32" font-weight="bold" fill="#2E7D32">100</text>
</g>
<g id="block-1">
<rect x="220" y="100" width="60" height="40" rx="6" fill="#FFF3E0" stroke="#FFA726" stroke-width="2"/>
<text x="250" y="130" text-anchor="middle" font-size="24" font-weight="bold" fill="#EF6C00">1</text>
</g>
</g>
<g id="distribute-arrows" opacity="0">
<path d="M120,165 L120,200" stroke="#ccc" stroke-width="2" marker-end="url(#arrow)" stroke-dasharray="5,5"/>
<path d="M250,145 L250,200" stroke="#ccc" stroke-width="2" marker-end="url(#arrow)" stroke-dasharray="5,5"/>
<text x="120" y="230" text-anchor="middle" font-size="20" font-weight="bold" fill="#2E7D32">× 23</text>
<text x="250" y="230" text-anchor="middle" font-size="20" font-weight="bold" fill="#EF6C00">× 23</text>
</g>
<g id="final-result" opacity="0">
<text x="200" y="270" text-anchor="middle" font-size="22" font-weight="bold" fill="#667eea">
= 2300 + 23 = 2323
</text>
</g>
</svg>
</div>
<div style="text-align: center; margin-bottom: 20px;">
<button class="step-btn" @click="prevDirectStep" :disabled="directStep <= 0" :class="{active: directStep > 0}">⏪ 上一步</button>
<button class="step-btn active" @click="nextDirectStep" :disabled="directStep >= 4">下一步 ⏩</button>
</div>
<div class="card-base concept-card">
<div class="concept-header bg-gradient-pink" @click="toggleConcept(0)">
<span>🧐 怎么判断拆哪个数?</span>
<span>{{conceptVisible[0] ? '➖' : '➕'}}</span>
</div>
<div class="concept-body" v-show="conceptVisible[0]">
<div class="highlight-box">
找那个<strong>特别接近整百</strong>的数!<br>
👉 看到 101, 102 <span style="color:#aaa">→</span> 拆成 <span style="color:#E91E63; font-weight:bold;">(100 + ?)</span><br>
👉 看到 99, 98 <span style="color:#aaa">→</span> 拆成 <span style="color:#2196F3; font-weight:bold;">(100 - ?)</span>
</div>
</div>
</div>
<div class="card-base concept-card">
<div class="concept-header bg-gradient-blue" @click="toggleConcept(1)">
<span>📝 举个例子 (多一点点)</span>
<span>{{conceptVisible[1] ? '➖' : '➕'}}</span>
</div>
<div class="concept-body" v-show="conceptVisible[1]">
<div class="highlight-box">
$$102 \times 45$$
$$= (100 + 2) \times 45$$
$$= 100 \times 45 + 2 \times 45$$
</div>
</div>
</div>
</div>
<div class="demo-section" v-show="demoTab === 'theory'">
<div class="card-base concept-card">
<div class="concept-header bg-gradient-purple" @click="toggleConcept(2)">
<span>📐 几何面积法理解</span>
<span>{{conceptVisible[2] ? '➖' : '➕'}}</span>
</div>
<div class="concept-body" v-show="conceptVisible[2]" style="text-align: center;">
<svg viewBox="0 0 300 150" style="width: 100%; max-width: 300px; margin: 10px auto;">
<rect x="10" y="20" width="220" height="80" fill="none" stroke="#ccc" stroke-dasharray="4"/>
<rect x="10" y="20" width="200" height="80" fill="#E8F5E9" stroke="#66BB6A" stroke-width="2"/>
<text x="110" y="65" font-size="14" fill="#2E7D32" text-anchor="middle">100 (长)</text>
<rect x="215" y="20" width="15" height="80" fill="#FFF3E0" stroke="#FFA726" stroke-width="2"/>
<text x="222" y="65" font-size="14" fill="#EF6C00" text-anchor="middle">1</text>
<text x="250" y="65" font-size="14" fill="#333">宽 23</text>
<path d="M240,20 L240,100" stroke="#333" marker-start="url(#arrow)" marker-end="url(#arrow)"/>
</svg>
<p style="font-size: 14px; color: #666; text-align: left;">
大长方形的面积 = 两个小长方形面积之和。<br>
<strong>(100+1)×23 = 100×23 + 1×23</strong>
</p>
</div>
</div>
</div>
</div>
<div v-show="currentPage === 3" class="explain-page">
<div class="tab-nav">
<div class="tab-item" :class="{active: explainTab === 'basic'}" @click="explainTab = 'basic'">基础</div>
<div class="tab-item" :class="{active: explainTab === 'advanced'}" @click="explainTab = 'advanced'">进阶</div>
<div class="tab-item" :class="{active: explainTab === 'mistake'}" @click="explainTab = 'mistake'">易错</div>
</div>
<div style="margin-top: 20px;">
<div class="card-base expand-card" :class="{active: expandedCard === 0}" @click="toggleCard(0)" v-show="explainTab === 'basic'">
<div class="card-header">
<span class="card-title" style="color: #4facfe;">📘 例题:101 × 23</span>
<span class="toggle-icon">▼</span>
</div>
<div class="card-content" style="padding: 20px; display: none;" v-show="expandedCard === 0">
<p><strong>思路:</strong>101 比 100 多 1。</p>
<p><strong>步骤:</strong><br>1. 拆:(100 + 1) × 23<br>2. 分:100×23 + 1×23<br>3. 算:2300 + 23 = 2323</p>
</div>
</div>
<div class="card-base expand-card" :class="{active: expandedCard === 1}" @click="toggleCard(1)" v-show="explainTab === 'advanced'">
<div class="card-header">
<span class="card-title" style="color: #a18cd1;">🚀 例题:99 × 57</span>
<span class="toggle-icon">▼</span>
</div>
<div class="card-content" style="padding: 20px; display: none;" v-show="expandedCard === 1">
<p><strong>思路:</strong>99 比 100 少 1。</p>
<p><strong>步骤:</strong><br>1. 拆:(100 - 1) × 57<br>2. 分:100×57 - 1×57<br>3. 算:5700 - 57 = 5643</p>
</div>
</div>
<div class="card-base expand-card" :class="{active: expandedCard === 2}" @click="toggleCard(2)" v-show="explainTab === 'mistake'">
<div class="card-header">
<span class="card-title" style="color: #ff9a9e;">❌ 警惕:102 × 45</span>
<span class="toggle-icon">▼</span>
</div>
<div class="card-content" style="padding: 20px; display: none;" v-show="expandedCard === 2">
<p style="background:#fff0f0; padding:10px; border-radius:8px;">❌ 错误拆法:(100+1)×45</p>
<p style="background:#f0fff4; padding:10px; border-radius:8px;">✅ 正确拆法:(100+<strong>2</strong>)×45</p>
<p>102是比100多2,不是多1哦!</p>
</div>
</div>
</div>
</div>
<div v-show="currentPage === 4" class="practice-page">
<div v-if="!practiceCompleted">
<div class="card-base question-card">
<div style="display:flex; justify-content:space-between; color:#999; font-size:12px; margin-bottom:10px;">
<span>第 {{currentQuestion + 1}}/{{practiceQuestions.length}} 题</span>
<span style="color:#FBC02D; font-weight:bold; font-size:14px;">⭐ {{score}}</span>
</div>
<div class="question-text" v-html="practiceQuestions[currentQuestion].text"></div>
<div>
<button v-for="(option, index) in practiceQuestions[currentQuestion].options"
:key="index"
class="option-btn"
:class="{
'correct': answered && option.correct,
'wrong': answered && selectedOption === index && !option.correct,
'vibrate': selectedOption === index && !option.correct && answered
}"
@click="selectOption(index, option.correct)"
:disabled="answered">
{{option.text}}
<span v-if="answered && option.correct" style="float:right;">✅</span>
<span v-if="answered && selectedOption === index && !option.correct" style="float:right;">❌</span>
</button>
</div>
<div v-if="answered" style="margin-top:15px; background:#f9f9f9; padding:15px; border-radius:12px; font-size:14px; color:#555;">
{{practiceQuestions[currentQuestion].explanation}}
</div>
<button v-if="answered" class="btn-gradient-blue" style="width:100%; padding:15px; border-radius:15px; margin-top:15px; font-size:16px;" @click="nextQuestion">
{{currentQuestion < practiceQuestions.length - 1 ? '下一题 →' : '查看成绩'}}
</button>
</div>
</div>
<div v-else class="intro-page">
<div class="intro-emoji">🏆</div>
<h2>练习完成!</h2>
<div style="font-size:40px; color:#FBC02D; font-weight:bold; margin:20px 0;">{{score}}分</div>
<button class="btn-gradient-primary" style="padding:15px 40px; border-radius:30px; font-size:16px;" @click="switchPage(5)">去挑战奥数题 ⚡</button>
</div>
</div>
<div v-show="currentPage === 5" class="practice-page">
<div v-if="!olympiadCompleted">
<div class="card-base question-card" style="border-top: 5px solid #ff9a9e;">
<div style="display:flex; justify-content:space-between; color:#999; font-size:12px; margin-bottom:10px;">
<span style="background:#ffebee; color:#d32f2f; padding:2px 8px; border-radius:4px;">🔥 挑战题</span>
<span style="color:#FBC02D; font-weight:bold;">⭐ {{olympiadScore}}</span>
</div>
<div class="question-text" v-html="olympiadQuestions[currentOlympiad].text"></div>
<div>
<button v-for="(option, index) in olympiadQuestions[currentOlympiad].options"
:key="'ol'+index"
class="option-btn"
:class="{
'correct': olympiadAnswered && option.correct,
'wrong': olympiadAnswered && selectedOlympiadOption === index && !option.correct,
'vibrate': selectedOlympiadOption === index && !option.correct && olympiadAnswered
}"
@click="selectOlympiadOption(index, option.correct)"
:disabled="olympiadAnswered || wrongAttempts >= 3">
{{option.text}}
</button>
</div>
<div v-if="wrongAttempts > 0 && !olympiadAnswered" style="background:#fff3e0; padding:10px; border-radius:8px; margin-top:10px; font-size:14px; color:#e65100;">
💡 {{wrongAttempts === 1 ? olympiadQuestions[currentOlympiad].hint1 : olympiadQuestions[currentOlympiad].hint2}}
</div>
<div v-if="olympiadAnswered" style="margin-top:15px; padding:15px; background:#f0f9ff; border-radius:12px; color:#0277bd; font-size:14px;">
{{olympiadQuestions[currentOlympiad].explanation}}
</div>
<button v-if="olympiadAnswered" class="btn-gradient-blue" style="width:100%; padding:15px; border-radius:15px; margin-top:15px;" @click="nextOlympiad">
{{currentOlympiad < olympiadQuestions.length - 1 ? '下一题 →' : '完成挑战'}}
</button>
</div>
</div>
<div v-else class="intro-page">
<div class="intro-emoji">👑</div>
<h2>奥数大师!</h2>
<div style="font-size:40px; color:#FBC02D; font-weight:bold; margin:20px 0;">{{olympiadScore}}分</div>
<button class="btn-gradient-primary" style="padding:15px 40px; border-radius:30px; font-size:16px;" @click="switchPage(6)">查看通关秘籍 📜</button>
</div>
</div>
<div v-show="currentPage === 6" class="secret-page">
<div class="secret-header">左右滑动查看秘籍</div>
<div class="secret-container">
<div class="secret-card">
<div class="secret-title">拆</div>
<div class="secret-content">
看到 101,<br>心中想 (100 + 1)
</div>
</div>
<div class="secret-card">
<div class="secret-title">变</div>
<div class="secret-content">
看到 99,<br>心中想 (100 - 1)
</div>
</div>
<div class="secret-card">
<div class="secret-title">分</div>
<div class="secret-content">
拆开后,<br>两个数都要乘!<br>
<span style="font-size:14px; opacity:0.8;">(别漏了后面那个小的)</span>
</div>
</div>
</div>
<div class="swipe-hint">← 左右滑动 →</div>
<div style="text-align:center; margin-top:10px;">
<button class="btn-gradient-blue" style="padding:12px 30px; border-radius:30px;" @click="switchPage(1)">再学一遍 ↺</button>
</div>
</div>
</div>
<div class="bottom-nav">
<div class="nav-item" :class="{active: currentPage === 1}" @click="switchPage(1)">
<span class="nav-icon">🏠</span><span>引入</span>
</div>
<div class="nav-item" :class="{active: currentPage === 2}" @click="switchPage(2)">
<span class="nav-icon">📺</span><span>演示</span>
</div>
<div class="nav-item" :class="{active: currentPage === 3}" @click="switchPage(3)">
<span class="nav-icon">📖</span><span>讲解</span>
</div>
<div class="nav-item" :class="{active: currentPage === 4}" @click="switchPage(4)">
<span class="nav-icon">✏️</span><span>练习</span>
</div>
<div class="nav-item" :class="{active: currentPage === 5}" @click="switchPage(5)">
<span class="nav-icon">⚡</span><span>奥数</span>
</div>
<div class="nav-item" :class="{active: currentPage === 6}" @click="switchPage(6)">
<span class="nav-icon">📜</span><span>秘籍</span>
</div>
</div>
</div>
<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',
directStep: 0,
conceptVisible: [false, false, false, false],
// 讲解页数据
explainTab: 'basic',
expandedCard: null,
// 练习数据
practiceQuestions: [
{ text: '$101 \\times 34 = ?$', options: [
{text: 'A. 3400', correct: false}, {text: 'B. 3434', correct: true},
{text: 'C. 3034', correct: false}, {text: 'D. 3403', correct: false}
], explanation: '拆成 (100+1)×34 = 3400+34=3434。' },
{ text: '$99 \\times 25 = ?$', options: [
{text: 'A. 2500', correct: false}, {text: 'B. 2475', correct: true},
{text: 'C. 2525', correct: false}, {text: 'D. 2400', correct: false}
], explanation: '拆成 (100-1)×25 = 2500-25=2475。' },
{ text: '$102 \\times 15 = ?$', options: [
{text: 'A. 1500', correct: false}, {text: 'B. 1515', correct: false},
{text: 'C. 1530', correct: true}, {text: 'D. 1520', correct: false}
], explanation: '拆成 (100+2)×15 = 1500+30=1530。' },
{ text: '$98 \\times 50 = ?$', options: [
{text: 'A. 4900', correct: true}, {text: 'B. 5000', correct: false},
{text: 'C. 4800', correct: false}, {text: 'D. 4950', correct: false}
], explanation: '拆成 (100-2)×50 = 5000-100=4900。' },
{ text: '$1001 \\times 8 = ?$', options: [
{text: 'A. 8008', correct: true}, {text: 'B. 8080', correct: false},
{text: 'C. 8000', correct: false}, {text: 'D. 8888', correct: false}
], explanation: '拆成 (1000+1)×8 = 8000+8=8008。' }
],
currentQuestion: 0,
selectedOption: null,
answered: false,
isCorrect: false,
score: 0,
practiceCompleted: false,
// 奥数数据
olympiadQuestions: [
// -----------------------------------------------------------
// 请复制以下代码,替换您 Vue 代码中 data() 里的 olympiadQuestions 数组
// -----------------------------------------------------------
// 第1题:基础拆分(多一点点)
{
text: '计算 101 × 35',
options: [
{text: 'A. 3500', correct: false}, {text: 'B. 3535', correct: true},
{text: 'C. 3550', correct: false}, {text: 'D. 3505', correct: false}
],
explanation: '101 接近 100,拆成 (100 + 1)。<br>原式 = 100×35 + 1×35 = 3500 + 35 = 3535。',
hint1: '把 101 拆成 (100 + 1)',
hint2: '别忘了 1 也要乘 35 哦',
difficulty: 'normal',
difficultyText: '热身'
},
// 第2题
{
text: '计算 99 × 43',
options: [
{text: 'A. 4300', correct: false}, {text: 'B. 4257', correct: true},
{text: 'C. 4243', correct: false}, {text: 'D. 4343', correct: false}
],
explanation: '99 接近 100,拆成 (100 - 1)。<br>原式 = 100×43 - 1×43 = 4300 - 43 = 4257。',
hint1: '把 99 看作 (100 - 1)',
hint2: '用 4300 减去 43',
difficulty: 'normal',
difficultyText: '基础'
},
// 第3题
{
text: '计算 25 × 44',
options: [
{text: 'A. 1000', correct: false}, {text: 'B. 1100', correct: true},
{text: 'C. 1200', correct: false}, {text: 'D. 1044', correct: false}
],
explanation: '44 可以拆成 (40 + 4)。<br>原式 = 25×40 + 25×4 = 1000 + 100 = 1100。<br>(或者拆成 25×4×11 也可以哦!)',
hint1: '试试把 44 拆成 (40 + 4)',
hint2: '25 乘以 4 是 100',
difficulty: 'hard',
difficultyText: '进阶'
},
// 第4题
{
text: '计算 125 × 88',
options: [
{text: 'A. 10000', correct: false}, {text: 'B. 11000', correct: true},
{text: 'C. 10125', correct: false}, {text: 'D. 10880', correct: false}
],
explanation: '88 接近 80,拆成 (80 + 8)。<br>原式 = 125×80 + 125×8。<br>记住口诀:125×8=1000。<br>结果 = 10000 + 1000 = 11000。',
hint1: '看到 125 要找 8,把 88 拆成 (80 + 8)',
hint2: '125 × 8 等于 1000',
difficulty: 'hard',
difficultyText: '进阶'
},
// 第5题
{
text: '计算 37 × 101 - 37',
options: [
{text: 'A. 3737', correct: false}, {text: 'B. 3700', correct: true},
{text: 'C. 3600', correct: false}, {text: 'D. 3663', correct: false}
],
explanation: '后面的 37 其实是 37×1。<br>提取公因数 37:<br>37 × (101 - 1) = 37 × 100 = 3700。',
hint1: '把减号后面的 37 看成 37×1',
hint2: '使用乘法分配律的逆运算',
difficulty: 'hard',
difficultyText: '技巧'
},
// 第6题
{
text: '计算 56 × 99 + 56',
options: [
{text: 'A. 5600', correct: true}, {text: 'B. 5544', correct: false},
{text: 'C. 5656', correct: false}, {text: 'D. 6500', correct: false}
],
explanation: '后面的 56 也是 56×1。<br>原式 = 56 × (99 + 1) = 56 × 100 = 5600。<br>这叫“凑整法”。',
hint1: '把后面的 +56 想象成 +56×1',
hint2: '99 和 1 是好朋友,可以凑成 100',
difficulty: 'hard',
difficultyText: '陷阱'
},
// 第7题
{
text: '计算 98 × 15 + 30',
options: [
{text: 'A. 1470', correct: false}, {text: 'B. 1500', correct: true},
{text: 'C. 1530', correct: false}, {text: 'D. 1600', correct: false}
],
explanation: '注意观察:30 正好是 2×15。<br>原式 = 98×15 + 2×15<br>= (98 + 2) × 15 = 100 × 15 = 1500。',
hint1: '30 可以写成 2 × 15',
hint2: '大家都有 ×15,可以提取出来',
difficulty: 'hard',
difficultyText: '烧脑'
},
// 第8题
{
text: '计算 999 × 6 + 1001 × 4',
options: [
{text: 'A. 9998', correct: true}, {text: 'B. 10000', correct: false},
{text: 'C. 9990', correct: false}, {text: 'D. 10002', correct: false}
],
explanation: '分别拆分:<br>(1000 - 1)×6 = 6000 - 6<br>(1000 + 1)×4 = 4000 + 4<br>合并:6000 + 4000 - 6 + 4 = 10000 - 2 = 9998。',
hint1: '999拆成(1000-1),1001拆成(1000+1)',
hint2: '分别计算后再相加,注意符号',
difficulty: 'hard',
difficultyText: '大神'
}
],
currentOlympiad: 0,
selectedOlympiadOption: null,
olympiadAnswered: false,
isOlympiadCorrect: false,
olympiadScore: 0,
olympiadCompleted: false,
wrongAttempts: 0
}
},
methods: {
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('TTS Error:', err));
} else {
if (window.speechSynthesis) {
window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'zh-CN';
utterance.rate = 0.9;
window.speechSynthesis.speak(utterance);
}
}
},
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();
}
},
switchPage(page) {
this.stopSpeak();
this.currentPage = page;
this.$nextTick(() => {
const pageEl = document.querySelector(`[v-show="currentPage === ${page}"]`);
if (pageEl) {
pageEl.classList.add('page-enter');
setTimeout(() => pageEl.classList.remove('page-enter'), 500);
}
});
if (page === 2) {
this.directStep = 0;
this.runDirectAnimation();
}
this.conceptVisible = [false, false, false, false];
},
switchDemoTab(tab) {
this.demoTab = tab;
this.directStep = 0;
this.$nextTick(() => {
if (tab === 'direct') this.runDirectAnimation();
});
},
toggleConcept(index) {
this.conceptVisible[index] = !this.conceptVisible[index];
},
toggleCard(index) {
this.expandedCard = this.expandedCard === index ? null : index;
},
// 动画控制
prevDirectStep() { if(this.directStep > 0) { this.directStep--; this.runDirectAnimation(); } },
nextDirectStep() { if(this.directStep < 4) { this.directStep++; this.runDirectAnimation(); } },
runDirectAnimation() {
const tl = gsap.timeline({ defaults: { duration: 0.6, ease: "back.out(1.7)" } });
tl.clear();
// 初始状态
if (this.directStep === 0) {
gsap.set("#block-total", { opacity: 1, scale: 1 });
gsap.set(["#hammer", "#block-split-group", "#distribute-arrows", "#final-result"], { opacity: 0 });
}
// 第一步:锤子出现并敲击
if (this.directStep >= 1) {
tl.set("#hammer", { opacity: 1, rotation: 0, x: 0, y: 0 })
.to("#hammer", { rotation: -45, transformOrigin: "bottom right", duration: 0.3, ease: "power1.in" })
.to("#hammer", { rotation: 0, duration: 0.1, ease: "power1.out" }); // Hit!
}
// 第二步:冰块碎裂
if (this.directStep >= 2) {
tl.to("#block-total", { opacity: 0, duration: 0.1, scale: 0.9 }) // Old block disappears
.set("#block-split-group", { opacity: 1 })
.fromTo("#block-100", { x: 60, scale: 0.5 }, { x: 0, scale: 1 }, "<")
.fromTo("#block-1", { x: -60, scale: 0.5 }, { x: 0, scale: 1 }, "<")
.to("#hammer", { opacity: 0, duration: 0.2 }, "<");
}
// 第三步:箭头分配
if (this.directStep >= 3) {
tl.set("#distribute-arrows", { opacity: 1 })
.fromTo("#distribute-arrows path", { strokeDashoffset: 100 }, { strokeDashoffset: 0, duration: 0.8, stagger: 0.2, ease: "none" })
.fromTo("#distribute-arrows text", { opacity: 0, y: -10 }, { opacity: 1, y: 0, stagger: 0.2 }, "-=0.5");
}
// 第四步:结果
if (this.directStep >= 4) {
tl.set("#final-result", { opacity: 1 })
.from("#final-result", { scale: 0.5, opacity: 0, duration: 0.5 });
}
},
// 答题逻辑 (保持原样)
selectOption(index, correct) {
if (this.answered) return;
this.selectedOption = index;
this.isCorrect = correct;
this.answered = true;
if (correct) this.score += 20;
},
nextQuestion() {
if (this.currentQuestion < this.practiceQuestions.length - 1) {
this.currentQuestion++;
this.selectedOption = null;
this.answered = false;
} else {
this.practiceCompleted = true;
}
},
selectOlympiadOption(index, correct) {
if (this.olympiadAnswered) return;
this.selectedOlympiadOption = index;
if (correct) {
this.isOlympiadCorrect = true;
this.olympiadAnswered = true;
this.olympiadScore += 50;
confetti({ particleCount: 150, spread: 80, origin: { y: 0.6 } });
} else {
this.wrongAttempts++;
if (this.wrongAttempts >= 3) this.olympiadAnswered = true;
}
},
nextOlympiad() {
if (this.currentOlympiad < this.olympiadQuestions.length - 1) {
this.currentOlympiad++;
this.selectedOlympiadOption = null;
this.olympiadAnswered = false;
this.wrongAttempts = 0;
} else {
this.olympiadCompleted = true;
}
}
},
mounted() {
setTimeout(() => {
this.speak('欢迎学习乘法分配律拆数法!');
}, 800);
}
}).mount('#app');
</script>
</body>
</html>
💡 这段代码完全由 AI 生成。
登录后可复制完整代码