<!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>
/* ================= 3.1 微信/移动端兼容性 (🚨 必须遵守) ================= */
* {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
html, body {
margin: 0;
padding: 0;
background: #F0F0F0;
overflow: hidden;
height: 100vh;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
/* 🚨 核心:禁止微信缩放字体 */
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
}
/* ================= 布局框架 ================= */
#app {
height: 100vh;
width: 100%;
max-width: 480px; /* 模拟手机宽度 */
margin: 0 auto;
background: #fff;
display: flex;
flex-direction: column;
position: relative;
box-shadow: 0 0 20px rgba(0,0,0,0.05);
}
.content-area {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding-bottom: 80px; /* 给底部导航留空间 */
position: relative;
-webkit-overflow-scrolling: touch;
}
/* ================= 底部导航 ================= */
.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: 1000;
box-shadow: 0 -2px 10px rgba(0,0,0,0.05);
}
.nav-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
padding: 8px;
transition: all 0.3s;
color: #64748B;
}
.nav-item.active {
color: #2563EB;
}
.nav-icon {
font-size: 24px;
margin-bottom: 4px;
}
.nav-label {
font-size: 12px;
font-weight: 500;
}
/* ================= 通用样式 ================= */
.page-container {
padding: 20px;
}
.section-title {
font-size: 20px;
font-weight: bold;
color: #1E293B;
margin: 20px 0 15px 0;
display: flex;
align-items: center;
gap: 8px;
}
.card {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 15px;
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
border: 1px solid #F1F5F9;
}
/* ================= Page 1: 动画实验室样式 ================= */
.anim-stage {
background: #1E293B; /* 深色背景突出方块 */
border-radius: 16px;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
min-height: 320px;
position: relative;
overflow: hidden;
margin-bottom: 20px;
border: 2px solid #334155;
}
.blocks-wrapper {
height: 180px;
width: 100%;
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
.anim-block {
width: 40px;
height: 40px;
background: linear-gradient(135deg, #FCD34D 0%, #F59E0B 100%);
border-radius: 8px;
position: absolute; /* 绝对定位方便GSAP移动 */
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
color: #78350F;
box-shadow: 0 4px 0 #D97706;
font-size: 18px;
z-index: 10;
}
.anim-feedback {
background: rgba(255,255,255,0.1);
color: white;
padding: 10px 15px;
border-radius: 8px;
margin: 15px 0;
font-size: 14px;
text-align: center;
min-height: 50px;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
transition: all 0.3s;
}
.anim-feedback.success {
background: #ECFDF5;
color: #047857;
border: 2px solid #10B981;
font-weight: bold;
}
.anim-feedback.fail {
background: #EFF6FF;
color: #1E40AF;
border: 2px solid #3B82F6;
font-weight: bold;
}
.anim-controls {
display: flex;
gap: 15px;
width: 100%;
}
.anim-btn {
flex: 1;
border: none;
padding: 12px;
border-radius: 12px;
font-weight: bold;
cursor: pointer;
font-size: 14px;
transition: transform 0.1s;
}
.anim-btn:active { transform: scale(0.95); }
.anim-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-composite { background: #10B981; color: white; box-shadow: 0 4px 0 #047857; }
.btn-prime { background: #3B82F6; color: white; box-shadow: 0 4px 0 #1D4ED8; }
/* ================= Page 2: 质数表样式 ================= */
.prime-grid {
display: grid;
grid-template-columns: repeat(10, 1fr);
gap: 4px;
margin: 20px 0;
}
.grid-cell {
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
border-radius: 4px;
background: #F8FAFC;
color: #94A3B8;
transition: all 0.3s;
cursor: pointer;
}
.grid-cell.prime {
background: #2563EB;
color: white;
font-weight: bold;
box-shadow: 0 2px 5px rgba(37, 99, 235, 0.3);
transform: scale(1.1);
z-index: 1;
}
.grid-cell.composite {
color: #64748B;
}
.grid-cell.one {
background: #CBD5E1;
color: #fff;
}
/* ================= Page 3: 例题折叠样式 ================= */
.example-item {
border: 1px solid #E2E8F0;
border-radius: 8px;
margin-bottom: 10px;
overflow: hidden;
}
.example-header {
padding: 15px;
background: #F8FAFC;
font-weight: bold;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
}
.example-content {
padding: 15px;
border-top: 1px solid #E2E8F0;
background: white;
line-height: 1.6;
font-size: 14px;
color: #334155;
display: none;
}
.example-item.active .example-content {
display: block;
animation: fadeIn 0.3s;
}
/* ================= Page 4 & 5: 练习题样式 ================= */
.quiz-container {
height: 100%;
display: flex;
flex-direction: column;
}
.question-card {
background: white;
border-radius: 16px;
padding: 25px;
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
flex: 1;
display: flex;
flex-direction: column;
}
.q-tag {
background: #EFF6FF;
color: #2563EB;
padding: 4px 10px;
border-radius: 12px;
font-size: 12px;
font-weight: bold;
align-self: flex-start;
margin-bottom: 10px;
}
.q-text {
font-size: 18px;
font-weight: bold;
color: #1E293B;
margin-bottom: 20px;
line-height: 1.5;
}
.options-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.option-btn {
padding: 15px;
border: 2px solid #E2E8F0;
border-radius: 12px;
background: white;
text-align: left;
font-size: 16px;
cursor: pointer;
transition: all 0.2s;
}
.option-btn.selected {
border-color: #2563EB;
background: #EFF6FF;
}
.option-btn.correct {
border-color: #10B981;
background: #ECFDF5;
color: #047857;
}
.option-btn.wrong {
border-color: #EF4444;
background: #FEF2F2;
color: #B91C1C;
}
.feedback-box {
margin-top: 20px;
padding: 15px;
border-radius: 8px;
background: #F8FAFC;
font-size: 14px;
line-height: 1.5;
}
.next-btn {
margin-top: auto;
background: #2563EB;
color: white;
border: none;
padding: 15px;
border-radius: 12px;
font-size: 16px;
font-weight: bold;
width: 100%;
cursor: pointer;
}
/* ================= Page 6: 秘籍卡片 ================= */
.secret-scroll {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
padding: 20px;
gap: 15px;
height: 100%;
align-items: center;
}
.secret-card {
min-width: 85%;
height: 450px;
scroll-snap-align: center;
background: linear-gradient(135deg, #2563EB 0%, #1D4ED8 100%);
border-radius: 20px;
padding: 30px;
color: white;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
box-shadow: 0 10px 25px rgba(37, 99, 235, 0.4);
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-5px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body>
<div id="app">
<div class="content-area">
<div v-show="currentPage === 1" class="page-container">
<div class="section-title">🎬 什么是质数与合数?</div>
<div style="font-size: 14px; color: #64748B; margin-bottom: 15px;">
把数字变成“小方块”,看看它们能不能排成整齐的矩形队伍!
</div>
<div class="anim-stage">
<div class="blocks-wrapper">
<div v-for="n in currentAnimNum" :key="n"
class="anim-block"
:id="'block-'+n">
{{ n }}
</div>
</div>
<div class="anim-feedback" :class="animStatusClass">
{{ animFeedback }}
</div>
<div class="anim-controls">
<button class="anim-btn btn-composite" @click="runCompositeAnim" :disabled="isAnimating">
🧪 测试 6 (合数)
</button>
<button class="anim-btn btn-prime" @click="runPrimeAnim" :disabled="isAnimating">
🧪 测试 5 (质数)
</button>
</div>
</div>
<div class="card" style="border-left: 4px solid #10B981;">
<div style="font-weight: bold; color: #059669; font-size: 16px; margin-bottom: 8px;">
🧱 合数 (如 6)
</div>
<p style="font-size: 14px; margin:0; line-height: 1.6;">
除了排成一排,<b>还能</b>排成别的整齐矩形。<br>
<small style="color:#666">因数:1, 2, 3, 6 (超过2个)</small>
</p>
</div>
<div class="card" style="border-left: 4px solid #2563EB;">
<div style="font-weight: bold; color: #2563EB; font-size: 16px; margin-bottom: 8px;">
💎 质数 (如 5)
</div>
<p style="font-size: 14px; margin:0; line-height: 1.6;">
脾气很倔!<b>只能</b>排成一排,排别的队形都会乱。<br>
<small style="color:#666">因数:只有 1 和它自己</small>
</p>
</div>
<div class="card" style="background: #F1F5F9; border: none; text-align: center;">
<div style="font-size: 24px;">1️⃣</div>
<div style="font-size: 13px; color: #64748B;">
1 只有一个方块,无法排队,所以它<b>既不是质数也不是合数</b>。
</div>
</div>
</div>
<div v-show="currentPage === 2" class="page-container">
<div class="section-title">📊 100以内质数表 (25个)</div>
<div style="font-size: 13px; color: #64748B; margin-bottom: 10px;">
点击数字听语音,蓝色背景为质数。
</div>
<div class="prime-grid">
<div v-for="n in 100" :key="n"
class="grid-cell"
:class="{
'prime': isPrime(n),
'composite': !isPrime(n) && n !== 1,
'one': n === 1
}"
@click="showNumberInfo(n)">
{{ n }}
</div>
</div>
<div class="card">
<div style="font-weight: bold; margin-bottom: 10px; color: #DC2626;">⚠️ 考试最爱考的伪质数</div>
<div style="display: flex; gap: 8px; flex-wrap: wrap;">
<span style="background: #FEF2F2; color: #DC2626; padding: 6px 10px; border-radius: 6px; font-size: 13px; font-weight: bold;">51 = 3×17</span>
<span style="background: #FEF2F2; color: #DC2626; padding: 6px 10px; border-radius: 6px; font-size: 13px; font-weight: bold;">57 = 3×19</span>
<span style="background: #FEF2F2; color: #DC2626; padding: 6px 10px; border-radius: 6px; font-size: 13px; font-weight: bold;">91 = 7×13</span>
</div>
</div>
</div>
<div v-show="currentPage === 3" class="page-container">
<div class="section-title">💡 5道经典例题</div>
<div class="example-item" :class="{active: activeExample === index}" v-for="(ex, index) in examples" :key="index">
<div class="example-header" @click="toggleExample(index)">
<span>{{ ex.title }}</span>
<span>{{ activeExample === index ? '▲' : '▼' }}</span>
</div>
<div class="example-content">
<div style="margin-bottom: 10px; font-weight: 500;">{{ ex.question }}</div>
<div style="background: #ECFDF5; padding: 10px; border-radius: 6px; color: #047857;">
<strong>解析:</strong><br>
<span v-html="ex.analysis"></span>
</div>
</div>
</div>
</div>
<div v-show="currentPage === 4" class="page-container" style="height: 100%;">
<div v-if="!basicPracticeDone" class="quiz-container">
<div class="question-card">
<div class="q-tag">基础巩固 {{ currentBasicIndex + 1 }}/10</div>
<div class="q-text">{{ basicQuestions[currentBasicIndex].text }}</div>
<div class="options-list">
<div v-for="(opt, idx) in basicQuestions[currentBasicIndex].options"
:key="idx"
class="option-btn"
:class="{
'correct': basicAnswered && opt.correct,
'wrong': basicAnswered && selectedBasicOpt === idx && !opt.correct
}"
@click="handleBasicAnswer(idx, opt.correct)">
{{ opt.text }}
</div>
</div>
<div v-if="basicAnswered" class="feedback-box">
<div v-if="isBasicCorrect" style="color: #10B981; font-weight: bold;">🎉 回答正确!</div>
<div v-else style="color: #EF4444; font-weight: bold;">💡 解析:</div>
<div style="margin-top: 5px;">{{ basicQuestions[currentBasicIndex].explanation }}</div>
</div>
<button v-if="basicAnswered" class="next-btn" @click="nextBasicQuestion">
{{ currentBasicIndex < 9 ? '下一题 →' : '查看结果' }}
</button>
</div>
</div>
<div v-else class="secret-card" style="background: white; color: #333; height: 100%; box-shadow: none;">
<div style="font-size: 60px;">🎉</div>
<h2 style="margin: 20px 0;">基础练习完成!</h2>
<div style="font-size: 24px; font-weight: bold; color: #2563EB;">得分: {{ basicScore }}/100</div>
<button class="next-btn" @click="switchPage(5)">挑战奥数题 →</button>
</div>
</div>
<div v-show="currentPage === 5" class="page-container" style="height: 100%;">
<div v-if="!olympiadDone" class="quiz-container">
<div class="question-card" style="border: 2px solid #F59E0B;">
<div class="q-tag" style="background: #FFFBEB; color: #D97706;">🏆 奥数真题 {{ currentOlympiadIndex + 1 }}/10</div>
<div class="q-text" v-html="olympiadQuestions[currentOlympiadIndex].text"></div>
<div class="options-list">
<div v-for="(opt, idx) in olympiadQuestions[currentOlympiadIndex].options"
:key="idx"
class="option-btn"
:class="{
'correct': olympiadAnswered && opt.correct,
'wrong': olympiadAnswered && selectedOlympiadOpt === idx && !opt.correct
}"
@click="handleOlympiadAnswer(idx, opt.correct)">
{{ opt.text }}
</div>
</div>
<div v-if="olympiadAnswered" class="feedback-box">
<div style="font-weight: bold; margin-bottom: 5px;">深度解析:</div>
<div v-html="olympiadQuestions[currentOlympiadIndex].explanation"></div>
</div>
<button v-if="olympiadAnswered" class="next-btn" style="background: #D97706;" @click="nextOlympiadQuestion">
{{ currentOlympiadIndex < 9 ? '下一题 →' : '完成挑战' }}
</button>
</div>
</div>
<div v-else class="secret-card" style="background: white; color: #333; height: 100%; box-shadow: none;">
<div style="font-size: 60px;">🏆</div>
<h2 style="margin: 20px 0;">奥数挑战结束!</h2>
<div style="font-size: 24px; font-weight: bold; color: #D97706;">得分: {{ olympiadScore }}/100</div>
<div style="margin: 20px 0; color: #666;">{{ olympiadScore >= 60 ? '你是真正的数学小天才!' : '继续加油,熟能生巧!' }}</div>
<button class="next-btn" @click="switchPage(6)">查看通关秘籍 →</button>
</div>
</div>
<div v-show="currentPage === 6" class="secret-scroll">
<div class="secret-card">
<div style="font-size: 50px;">📜</div>
<h3>唯一性</h3>
<p style="font-size: 18px; line-height: 1.8;">
2 是唯一的偶数质数<br>
3, 5, 7 是唯一的连续奇数质数<br>
(三胞胎)
</p>
</div>
<div class="secret-card" style="background: linear-gradient(135deg, #10B981 0%, #059669 100%);">
<div style="font-size: 50px;">💣</div>
<h3>避坑指南</h3>
<p style="font-size: 18px; line-height: 1.8;">
看到 91 不要选质数!<br>
91 = 7 × 13<br><br>
51 = 3 × 17<br>
57 = 3 × 19
</p>
</div>
<div class="secret-card" style="background: linear-gradient(135deg, #F59E0B 0%, #D97706 100%);">
<div style="font-size: 50px;">🧮</div>
<h3>奇偶分析法</h3>
<p style="font-size: 16px; line-height: 1.6;">
如果两个质数的和是奇数,<br>
那么其中一个必须是 2!<br><br>
(因为奇数+奇数=偶数,<br>只有偶数+奇数=奇数)
</p>
</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>
<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,
// --- 动画实验室数据 ---
currentAnimNum: 0,
isAnimating: false,
animFeedback: '点击下方按钮,开始排队实验!',
animStatusClass: '',
// 质数数据
primes: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97],
// 例题数据
activeExample: null,
examples: [
{
title: '例题1: 判断性质',
question: '在 1, 2, 4, 9, 11, 51, 91 中,质数有几个?合数有几个?',
analysis: '1既不是质数也不是合数。<br>质数:2, 11 (共2个)<br>合数:4, 9, 51(3×17), 91(7×13) (共4个)'
},
{
title: '例题2: 奇偶性分析',
question: '两个质数的和是 39,这两个质数的积是多少?',
analysis: '39是奇数 = 奇数+偶数。质数中唯一的偶数是2。<br>所以一个数是2,另一个是39-2=37(质数)。<br>积 = 2 × 37 = 74。'
},
{
title: '例题3: 连续自然数',
question: '找出三个连续的奇数,它们都是质数。',
analysis: '唯一的“三胞胎”质数组合:3, 5, 7。<br>注意:5,7,9不行(9合数);11,13,15不行(15合数)。'
},
{
title: '例题4: 最值问题',
question: '一个两位数,个位十位互换后与原数之和是110。原数是质数,求原数。',
analysis: '10A+B + 10B+A = 11(A+B) = 110 → A+B=10。<br>组合:19, 28, 37, 46, 55, 64, 73, 82, 91。<br>质数只有:19, 37, 73。'
},
{
title: '例题5: 日期问题',
question: '今天是周一,过P天是周四,P是质数。P最小是多少?',
analysis: '周一到周四经过3天。P除以7余3。<br>试数:3 (符合质数);10 (合数)。<br>最小是 3。'
}
],
// 基础练习
currentBasicIndex: 0,
basicScore: 0,
basicAnswered: false,
isBasicCorrect: false,
selectedBasicOpt: null,
basicPracticeDone: false,
basicQuestions: [
{
text: '【判断】所有的质数都是奇数。',
explanation: '错误。2是偶数,也是质数。',
options: [{text:'对', correct:false}, {text:'错', correct:true}]
},
{
text: '【判断】两个质数的积一定是合数。',
explanation: '正确。积的因数至少有:1、两个质数本身、积,共4个。',
options: [{text:'对', correct:true}, {text:'错', correct:false}]
},
{
text: '【判断】所有的偶数都是合数。',
explanation: '错误。2是偶数,但它是质数。',
options: [{text:'对', correct:false}, {text:'错', correct:true}]
},
{
text: '100以内最大的质数是?',
explanation: '97是100以内最大的质数。',
options: [{text:'99', correct:false}, {text:'97', correct:true}, {text:'91', correct:false}]
},
{
text: '既是奇数又是合数的最小数是?',
explanation: '1、3、5、7不是合数。9 = 3×3,是奇数也是合数。',
options: [{text:'1', correct:false}, {text:'9', correct:true}, {text:'15', correct:false}]
},
{
text: '20以内的质数中,加上2以后还是质数的有3, 5, 11, ?',
explanation: '17+2=19,19是质数。13+2=15(合),7+2=9(合)。',
options: [{text:'13', correct:false}, {text:'17', correct:true}, {text:'19', correct:false}]
},
{
text: '下列全是质数的一组是?',
explanation: 'A中1不是;B中9不是;D中4,6,8不是。C中3,5,7,11全是。',
options: [{text:'1,3,5,7', correct:false}, {text:'3,5,7,9', correct:false}, {text:'3,5,7,11', correct:true}, {text:'2,4,6,8', correct:false}]
},
{
text: '两个质数的乘积是33,这两个质数是?',
explanation: '33 = 3 × 11。',
options: [{text:'1和33', correct:false}, {text:'3和11', correct:true}, {text:'11和33', correct:false}]
},
{
text: '100以内所有个位是1的质数有几个?',
explanation: '有11, 31, 41, 61, 71。共5个。(注意91=7×13不是)',
options: [{text:'4个', correct:false}, {text:'5个', correct:true}, {text:'6个', correct:false}]
},
{
text: '两个不同的质数相乘,积有几个因数?',
explanation: '因数有:1、质数A、质数B、积。共4个。',
options: [{text:'2个', correct:false}, {text:'3个', correct:false}, {text:'4个', correct:true}]
}
],
// 奥数挑战
currentOlympiadIndex: 0,
olympiadScore: 0,
olympiadAnswered: false,
selectedOlympiadOpt: null,
olympiadDone: false,
olympiadQuestions: [
{
text: '若两个质数的和是 2001,则这两个质数的积是?',
explanation: '2001是奇数,只能是 偶数+奇数。质数中偶数只有2。<br>另一个数=2001-2=1999。<br>积=2×1999=3998。',
options: [{text:'2000', correct:false}, {text:'3998', correct:true}, {text:'4002', correct:false}]
},
{
text: '三个连续自然数,两质一合,这三个数和最小是?',
explanation: '最小组合是 2(质), 3(质), 4(合)。<br>和 = 2+3+4 = 9。',
options: [{text:'6', correct:false}, {text:'9', correct:true}, {text:'12', correct:false}]
},
{
text: '两个质数的和是 40,这两个质数的乘积最大是多少?',
explanation: '和一定,差小积大。最接近的一组是 17+23=40。<br>17×23 = 391。',
options: [{text:'391', correct:true}, {text:'300', correct:false}, {text:'91', correct:false}]
},
{
text: '1到100之间,有多少个质数的个位数字是3?',
explanation: '3, 13, 23, 43, 53, 73, 83。<br>共7个。(33, 63, 93是合数)',
options: [{text:'6个', correct:false}, {text:'7个', correct:true}, {text:'8个', correct:false}]
},
{
text: '100以内的“绝对质数”(交换个十位仍是质数)包含13,17,31...',
explanation: '11, 13, 17, 31, 37, 71, 73, 79, 97。<br>例如 17->71, 79->97。',
options: [{text:'13,31,79,97等', correct:true}, {text:'包含19', correct:false}, {text:'包含23', correct:false}]
},
{
text: 'a,b,c都是质数,且a+b=c,a×b×c最小值?',
explanation: 'a+b=c(奇数),必含2。设a=2。<br>2+b=c,且b,c均为质数。最小b=3,此时c=5。<br>积=2×3×5=30。',
options: [{text:'30', correct:true}, {text:'42', correct:false}, {text:'70', correct:false}]
},
{
text: '20到50之间的所有孪生质数(差2)对有?',
explanation: '29和31;41和43。',
options: [{text:'23和25', correct:false}, {text:'29和31; 41和43', correct:true}, {text:'39和41', correct:false}]
},
{
text: '三个不同质数的和是52,积最大是多少?',
explanation: '和52(偶),必含2。<br>剩两数和50,要积大找最接近:19+31=50。<br>最大积=2×19×31=1178。',
options: [{text:'1178', correct:true}, {text:'589', correct:false}, {text:'2262', correct:false}]
},
{
text: '长方形周长36,长宽均为质数,面积最大?',
explanation: '长+宽=18。和为18的质数对:5+13=18(积65), 7+11=18(积77)。<br>最大77。',
options: [{text:'65', correct:false}, {text:'77', correct:true}, {text:'81', correct:false}]
},
{
text: '将1-9分成三组,每组三个数和为质数,其中一组可以是?',
explanation: '例如 (1,6,4)和11质数; (2,7,8)和17质数; (3,5,9)和17质数。',
options: [{text:'1,2,3', correct:false}, {text:'1,6,4', correct:true}, {text:'2,4,8', correct:false}]
}
]
}
},
methods: {
// ================= 3.1.2 核心音频代码 (严格复制) =================
speak(text) {
if (window.speechSynthesis) window.speechSynthesis.cancel();
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 themePath = 'https://www.xinghuo.tv/wp-content/themes/xinghuo-tv';
const url = `${themePath}/tts.php?text=${encodeURIComponent(text)}&t=${Date.now()}`;
audio.src = url;
const playPromise = audio.play();
if (playPromise !== undefined) {
playPromise.catch(error => {
console.error("Audio playback failed:", error);
if (window.WeixinJSBridge) {
window.WeixinJSBridge.invoke('getNetworkType', {}, function (e) {
audio.play();
});
}
});
}
} else {
if (window.speechSynthesis) {
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'zh-CN';
utterance.rate = 0.9;
window.speechSynthesis.speak(utterance);
}
}
},
// ================= 页面切换逻辑 =================
switchPage(page) {
// 🚨 切换页面时强制停止音频
if (window.speechSynthesis) window.speechSynthesis.cancel();
const audio = document.getElementById('tts-audio');
if (audio) audio.pause();
this.currentPage = page;
window.scrollTo(0, 0);
},
// ================= Page 1: 动画实验室逻辑 =================
runCompositeAnim() {
if (this.isAnimating) return;
this.isAnimating = true;
this.currentAnimNum = 6;
this.animFeedback = '数字 6 正在集合...';
this.animStatusClass = '';
this.speak("来看看数字6会不会排成整齐的矩形吗?");
this.$nextTick(() => {
const blocks = gsap.utils.toArray('.anim-block');
gsap.set(blocks, { x: 0, y: 0, opacity: 0, scale: 0 });
const tl = gsap.timeline({
onComplete: () => {
this.isAnimating = false;
}
});
tl.to(blocks, {
opacity: 1,
scale: 1,
x: (i) => (i - 2.5) * 45, // 横向排列 (1行6列)
duration: 0.5,
stagger: 0.1,
ease: "back.out(1.7)"
})
.add(() => { this.animFeedback = '6 可以排成一排 (1 × 6)'; }, "+=0.3")
.to(blocks, {
x: (i) => (i % 3 - 1) * 45, // 3列
y: (i) => (Math.floor(i / 3) - 0.5) * 45, // 2行
duration: 0.8,
ease: "power2.inOut",
delay: 0.8
})
.add(() => {
this.animFeedback = '哇!6 还能排成 2行3列 的整齐矩形!';
this.animStatusClass = 'success';
this.speak("6可以排成两行三列,所以它是合数!");
if (typeof confetti !== 'undefined') confetti({ particleCount: 50, spread: 50, origin: { y: 0.4 } });
});
});
},
runPrimeAnim() {
if (this.isAnimating) return;
this.isAnimating = true;
this.currentAnimNum = 5;
this.animFeedback = '数字 5 正在集合...';
this.animStatusClass = '';
this.speak("数字5脾气很倔,我们试试看。");
this.$nextTick(() => {
const blocks = gsap.utils.toArray('.anim-block');
gsap.set(blocks, { x: 0, y: 0, opacity: 0, scale: 0 });
const tl = gsap.timeline({
onComplete: () => {
this.isAnimating = false;
}
});
tl.to(blocks, {
opacity: 1,
scale: 1,
x: (i) => (i - 2) * 45, // 1行5列
duration: 0.5,
stagger: 0.1,
ease: "back.out(1.7)"
})
.add(() => { this.animFeedback = '5 可以排成一排 (1 × 5)'; }, "+=0.3")
// 尝试排成2列 (必然多1个)
.to(blocks, {
x: (i) => (i % 2 - 0.5) * 45,
y: (i) => (Math.floor(i / 2) - 1) * 45,
duration: 0.8,
ease: "power2.inOut",
delay: 0.8
})
.to(blocks, {
x: "+=5", // 抖动效果
yoyo: true,
repeat: 5,
duration: 0.05
})
.add(() => {
this.animFeedback = '哎呀!排不整齐,多出来一个!';
this.animStatusClass = 'fail';
this.speak("排不整齐,多了一个!除了排成一排,5没法变其他矩形,所以它是质数。");
});
});
},
// ================= 通用逻辑 =================
isPrime(n) {
return this.primes.includes(n);
},
showNumberInfo(n) {
if (this.isPrime(n)) {
this.speak(`${n}是质数`);
} else if (n === 1) {
this.speak("1既不是质数也不是合数");
} else {
this.speak(`${n}是合数`);
}
},
toggleExample(index) {
this.activeExample = this.activeExample === index ? null : index;
},
// 基础练习逻辑
handleBasicAnswer(index, correct) {
if (this.basicAnswered) return;
this.selectedBasicOpt = index;
this.basicAnswered = true;
this.isBasicCorrect = correct;
if (correct) {
this.basicScore += 10;
if (typeof confetti !== 'undefined') confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } });
}
},
nextBasicQuestion() {
if (this.currentBasicIndex < 9) {
this.currentBasicIndex++;
this.basicAnswered = false;
this.selectedBasicOpt = null;
} else {
this.basicPracticeDone = true;
}
},
// 奥数逻辑
handleOlympiadAnswer(index, correct) {
if (this.olympiadAnswered) return;
this.selectedOlympiadOpt = index;
this.olympiadAnswered = true;
if (correct) {
this.olympiadScore += 10;
if (typeof confetti !== 'undefined') confetti({ particleCount: 150, spread: 90, colors: ['#D97706', '#F59E0B'] });
}
},
nextOlympiadQuestion() {
if (this.currentOlympiadIndex < 9) {
this.currentOlympiadIndex++;
this.olympiadAnswered = false;
this.selectedOlympiadOpt = null;
} else {
this.olympiadDone = true;
}
}
}
}).mount('#app');
</script>
</body>
</html>
💡 这段代码完全由 claude 生成。
登录后可复制完整代码