<!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>第11讲:简单的逻辑推理(列表法)</title>
<style>
/* ==============================
1. 全局基础样式 & 变量
============================== */
:root {
--primary-color: #667eea;
--accent-pink: #ff9a9e;
--accent-teal: #a8edea;
--text-main: #333;
--text-sub: #666;
--bg-gradient: radial-gradient(circle at top left, #fdfbfb 0%, #ebedee 100%);
--card-shadow: 0 10px 30px -10px rgba(50, 50, 93, 0.15), 0 5px 15px -10px rgba(0, 0, 0, 0.1);
--card-radius: 24px;
}
* { 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: #F5F7FA;
/* 更加细腻的背景纹理 */
background-image: radial-gradient(#E1E1E1 1px, transparent 1px);
background-size: 24px 24px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
overflow: hidden; height: 100vh;
color: var(--text-main);
}
#app { height: 100vh; max-width: 480px; margin: 0 auto; background: rgba(255,255,255,0.95); display: flex; flex-direction: column; position: relative; box-shadow: 0 0 50px rgba(0,0,0,0.05); }
.content-area { flex: 1; overflow-y: auto; padding-bottom: 90px; scroll-behavior: smooth; }
.page { padding: 24px; padding-top: 10px; }
/* 标题样式 */
.page-title {
text-align: center; font-size: 26px; font-weight: 800; color: var(--primary-color);
margin: 20px 0 10px; position: relative; display: inline-block; width: 100%;
}
.page-title::after {
content: ''; display: block; width: 40px; height: 4px; background: var(--accent-pink);
border-radius: 2px; margin: 8px auto 0;
}
.page-subtitle { text-align: center; color: #888; font-size: 14px; margin-bottom: 25px; letter-spacing: 1px; }
/* ==============================
2. 底部导航 (玻璃拟态)
============================== */
.bottom-nav {
position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
width: 90%; max-width: 440px; height: 65px;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
border-radius: 35px;
box-shadow: 0 15px 35px rgba(0,0,0,0.1);
display: flex; justify-content: space-around; align-items: center; z-index: 9999;
padding: 0 10px; border: 1px solid rgba(255,255,255,0.5);
}
.nav-item { flex: 1; display: flex; flex-direction: column; align-items: center; cursor: pointer; color: #ccc; transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); font-size: 10px; }
.nav-icon { font-size: 22px; margin-bottom: 2px; filter: grayscale(100%); transition: all 0.3s; }
.nav-item.active { color: var(--primary-color); transform: translateY(-5px); }
.nav-item.active .nav-icon { filter: grayscale(0%); transform: scale(1.2); }
/* ==============================
3. 概念引入页 (微投影卡片)
============================== */
.intro-card {
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
border-radius: var(--card-radius); padding: 35px 25px; text-align: center;
box-shadow: var(--card-shadow);
border: 1px solid rgba(255,255,255,0.6);
}
.intro-emoji { font-size: 72px; margin-bottom: 20px; animation: float 3s ease-in-out infinite; }
@keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }
.intro-title { font-size: 24px; font-weight: 800; color: #333; margin-bottom: 15px; }
.intro-story { font-size: 16px; line-height: 1.7; color: #444; text-align: left; margin-bottom: 25px; background: rgba(255,255,255,0.4); padding: 15px; border-radius: 16px; }
.intro-why { background: rgba(255,255,255,0.8); padding: 20px; border-radius: 16px; margin-bottom: 30px; font-size: 15px; color: #555; text-align: left; box-shadow: inset 0 2px 5px rgba(0,0,0,0.03); }
/* 按钮通用升级 */
.listen-btn, .next-btn, .restart-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white; border: none; padding: 16px 32px;
border-radius: 50px; font-size: 17px; font-weight: bold;
cursor: pointer; display: flex; align-items: center; justify-content: center;
margin: 0 auto; box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
transition: all 0.2s; width: 100%; max-width: 300px;
}
.listen-btn:active, .next-btn:active, .restart-btn:active { transform: scale(0.96); box-shadow: 0 5px 10px rgba(102, 126, 234, 0.2); }
.listen-icon { margin-right: 10px; font-size: 20px; }
/* ==============================
4. 动画与 SVG 区域 (升级)
============================== */
.tab-nav {
display: flex; background: #f0f2f5; border-radius: 16px; padding: 6px; margin-bottom: 25px;
}
.tab-item {
flex: 1; text-align: center; padding: 12px; font-weight: bold; border-radius: 12px; cursor: pointer;
color: #999; transition: all 0.3s; font-size: 15px;
}
.tab-item.active { background: white; color: var(--primary-color); box-shadow: 0 2px 8px rgba(0,0,0,0.05); }
.animation-area {
background: white;
border-radius: var(--card-radius); padding: 20px; min-height: 340px; position: relative;
margin-bottom: 20px; overflow: hidden;
box-shadow: var(--card-shadow);
border: 1px solid #f0f0f0;
}
.svg-container { width: 100%; height: 300px; }
.step-btn {
position: absolute; bottom: 20px; z-index: 100;
background: white; color: var(--primary-color); border: 2px solid #f0f0f0;
width: 44px; height: 44px; border-radius: 50%; font-size: 20px;
cursor: pointer; display: flex; align-items: center; justify-content: center;
box-shadow: 0 4px 12px rgba(0,0,0,0.08); transition: all 0.2s;
}
.step-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.step-btn:not(:disabled):active { transform: scale(0.9); }
.step-btn.prev { left: 20px; }
.step-btn.next { right: 20px; background: var(--primary-color); color: white; border: none; }
/* ==============================
5. 概念讲解卡片 (Macaron Style)
============================== */
.concept-card {
background: white; border-radius: 20px; overflow: hidden;
box-shadow: 0 8px 20px rgba(0,0,0,0.05); margin-bottom: 24px;
transition: transform 0.2s;
}
.concept-card:hover { transform: translateY(-2px); }
.concept-header {
padding: 18px 24px; color: white; font-weight: bold; font-size: 17px;
display: flex; justify-content: space-between; align-items: center;
}
.bg-pink { background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%); }
.bg-purple { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
.bg-blue { background: linear-gradient(135deg, #89f7fe 0%, #66a6ff 100%); }
.concept-body { padding: 24px; }
.highlight-box {
background: #FFFBF0; border-left: none; border-radius: 12px;
padding: 16px; margin: 15px 0;
color: #d35400; font-size: 15px; line-height: 1.6;
border: 1px solid #FFE5B4;
position: relative;
}
/* 装饰性小图标 */
.highlight-box::before { content: '💡'; position: absolute; right: 10px; top: 10px; opacity: 0.5; }
.formula-text { font-weight: 800; color: #e67e22; margin-top: 8px; display: block; }
/* ==============================
6. 讲解页折叠卡片
============================== */
.expand-card {
background: white; border-radius: 20px; margin-bottom: 16px;
box-shadow: 0 4px 15px rgba(0,0,0,0.03); overflow: hidden;
border: 1px solid #f8f9fa;
}
.expand-card.active { box-shadow: 0 10px 30px rgba(0,0,0,0.08); border-color: transparent; }
.card-header {
padding: 22px; display: flex; justify-content: space-between; align-items: center;
cursor: pointer; background: #fff;
}
.card-title { font-weight: 700; font-size: 16px; color: #444; }
.card-content { padding: 0 24px 24px; border-top: 1px dashed #eee; display: none; background: #fff; }
.expand-card.active .card-content { display: block; }
.toggle-icon { font-size: 12px; color: #ccc; transition: transform 0.3s; }
.expand-card.active .toggle-icon { transform: rotate(180deg); color: var(--primary-color); }
/* ==============================
7. 练习与挑战 (交互增强)
============================== */
.question-card {
background: white; border-radius: 24px; padding: 28px;
box-shadow: var(--card-shadow); margin-bottom: 24px;
}
.question-header {
display: flex; justify-content: space-between; align-items: center;
margin-bottom: 24px; padding-bottom: 15px; border-bottom: 2px dashed #f0f0f0;
}
.question-number { font-weight: 800; color: var(--primary-color); font-size: 15px; background: #EEF2FF; padding: 5px 12px; border-radius: 20px; }
.score-display { font-weight: 800; color: #ff9a00; font-size: 18px; }
.question-text {
font-size: 19px; line-height: 1.6; color: #2c3e50; margin-bottom: 30px;
font-weight: 600;
}
.option-btn {
background: #F7F9FC; border: 2px solid transparent; border-radius: 16px;
padding: 18px 20px; font-size: 16px; text-align: left; cursor: pointer;
transition: all 0.2s; color: #555; width: 100%; margin-bottom: 12px;
box-shadow: 0 2px 5px rgba(0,0,0,0.03); font-weight: 500;
}
.option-btn:active { transform: scale(0.98); }
.option-btn.correct { background: #E3F9E5; border-color: #28a745; color: #155724; font-weight: bold; }
.option-btn.wrong { background: #FFF0F1; border-color: #dc3545; color: #721c24; }
/* 难度徽章 */
.difficulty-badge { display: inline-block; padding: 4px 10px; border-radius: 20px; font-size: 12px; font-weight: bold; margin-left: 10px; vertical-align: middle; }
.difficulty-1 { background: #d4edda; color: #28a745; }
.difficulty-2 { background: #fff3cd; color: #d35400; }
.difficulty-3 { background: #f8d7da; color: #dc3545; }
/* ==============================
8. 通关秘籍 (横向滚动 - Carousel)
============================== */
.secret-page { padding: 0; overflow-x: hidden; } /* 特殊处理秘籍页padding */
.secret-page .page-title { margin-top: 40px; }
.secret-container {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
gap: 20px;
padding: 20px 24px 50px 24px; /* 底部留出空间 */
width: 100%;
-webkit-overflow-scrolling: touch; /* iOS顺滑滚动 */
}
/* 隐藏滚动条 */
.secret-container::-webkit-scrollbar { display: none; }
.secret-card {
flex: 0 0 100%; /* 强制宽度为容器的100%,实现单页显示 */
scroll-snap-align: center;
min-height: 480px;
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
border-radius: 24px;
padding: 40px 30px;
box-shadow: 0 15px 35px rgba(0,0,0,0.15), inset 0 0 0 1px rgba(255,255,255,0.4);
display: flex; flex-direction: column; justify-content: center; align-items: center;
position: relative;
}
.secret-card::after {
content: '← 滑动查看更多 →';
position: absolute; bottom: 20px;
font-size: 12px; color: rgba(0,0,0,0.3); font-weight: bold;
}
.secret-card h3 {
text-align: center; font-size: 32px; color: #667eea; margin-bottom: 30px;
background: rgba(255,255,255,0.6); padding: 10px 25px; border-radius: 50px;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.2);
}
.secret-card p {
font-size: 24px; line-height: 1.6; color: #333; text-align: center; font-weight: bold;
text-shadow: 0 2px 4px rgba(255,255,255,0.5);
}
/* 交互反馈动画 */
@keyframes vibrate {
0%, 100% { transform: translate(0, 0); }
10%, 30%, 50%, 70%, 90% { transform: translate(-3px, 0); }
20%, 40%, 60%, 80% { transform: translate(3px, 0); }
}
.option-btn.vibrate { animation: vibrate 0.3s ease-in-out; }
/* 页面切换动画 */
.page-enter { animation: slideUp 0.6s cubic-bezier(0.16, 1, 0.3, 1); }
@keyframes slideUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
</style>
</head>
<body>
<div id="app">
<div class="content-area">
<div v-show="currentPage === 1" class="page intro-page">
<h1 class="page-title">简单的逻辑推理</h1>
<div class="page-subtitle">第11讲:列表法</div>
<div class="intro-card">
<div class="intro-emoji">🕵️</div>
<div class="intro-title">欢迎,小侦探!</div>
<div class="intro-story">
学校的奖品柜里,小红、小兰、小绿的奖品被拿错了!红书包、黄水壶、蓝文具盒,每人应该拿回自己的。我们只知道:小红没拿红色书包,小兰拿的是蓝色的。天哪,这可怎么办?<strong>你能帮他们推理出谁拿了什么吗?</strong>
</div>
<div class="intro-why">
<strong>💡 为什么学逻辑推理?</strong><br>
生活中充满了需要推理的小事:是谁忘了关灯?礼物是谁送的?学会逻辑推理,你就能像侦探一样,从线索中找到真相!
</div>
<button class="listen-btn" @click="speak('欢迎来到逻辑推理课堂!我是你的推理助手。今天,我们将像侦探一样,利用表格和线索,解决有趣的谜题。准备好了吗?点击下方导航,开始我们的推理之旅吧!')">
<span class="listen-icon">🔊</span> 听老师讲解
</button>
</div>
</div>
<div v-show="currentPage === 2" class="page demo-page">
<h1 class="page-title">推理工具:列表法</h1>
<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 v-show="demoTab === 'direct'">
<div class="concept-card">
<div class="concept-header bg-pink">
<span>判断1:遇到“谁是什么”的问题</span>
</div>
<div class="concept-body">
<div class="sub-title">如何判断?</div>
<div>题目有3个人,3个职业,给了几句真假难辨的话。</div>
<div class="highlight-box">
马上画 $3 \times 3$ 的表格。
</div>
</div>
</div>
<div class="concept-card">
<div class="concept-header bg-blue">
<span>判断2:处理否定线索</span>
</div>
<div class="concept-body">
<div class="sub-title">如何判断?</div>
<div>线索说“甲不是医生”。</div>
<div class="highlight-box">
甲对应的医生格打×。
</div>
</div>
</div>
<div class="concept-card">
<div class="concept-header bg-purple">
<span>判断3:处理肯定线索</span>
</div>
<div class="concept-body">
<div class="sub-title">如何判断?</div>
<div>线索说“乙是司机”。</div>
<div class="highlight-box">
乙对应的司机格打√,同时把这一行、这一列的其他格全打×。
<span class="formula-text">关键:一个确定,排除一片!</span>
</div>
</div>
</div>
</div>
<div v-show="demoTab === 'theory'">
<div class="concept-card">
<div class="concept-header bg-purple">
<span>核心原理:萝卜占坑</span>
</div>
<div class="concept-body">
<div class="sub-title">为什么一个萝卜一个坑?</div>
<div>一个萝卜一个坑。如果红萝卜占了第一个坑,白萝卜就不能去第一个坑,红萝卜也不能去第二个坑。</div>
<div class="highlight-box">
<strong>对应到列表:</strong><br>
<strong>行(人)</strong>就像一个坑,只能放一个物品。<br>
<strong>列(物品)</strong>就像一个萝卜,只能去一个坑。<br>
所以,一个√出现,它所在的行和列就立刻被“占满”了!
</div>
</div>
</div>
<div class="animation-area">
<div class="svg-container" id="demo-svg"></div>
<button class="step-btn prev" @click="prevStep" :disabled="demoStep === 0">◀</button>
<button class="step-btn next" @click="nextStep" :disabled="demoStep >= 2">▶</button>
</div>
</div>
</div>
<div v-show="currentPage === 3" class="page explain-page">
<h1 class="page-title">典型例题讲解</h1>
<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 class="example-list">
<div class="tab-content" v-show="explainTab === 'basic'">
<div class="expand-card" :class="{active: expandedCard === 0}" @click="toggleCard(0)">
<div class="card-header">
<span class="card-title" style="color: #4a90e2;">例题1:基础题(裙子颜色)</span>
<span class="toggle-icon">▼</span>
</div>
<div class="card-content" @click.stop>
<div><strong>题目:</strong>小红、小兰、小绿分别穿红、黄、蓝裙子。小红不穿红,小兰穿蓝。小绿穿什么?</div>
<div><strong>答案:</strong>红裙子</div>
<div><strong>解析:</strong></div>
<div>1. 列表格。</div>
<div>2. 小兰穿蓝 -> 小兰蓝格√(红、黄×;小红、小绿蓝×)。</div>
<div>3. 小红不穿红 -> 小红红格×。小红只能穿黄√。</div>
<div>4. 剩下一格:小绿穿红√。</div>
</div>
</div>
</div>
<div class="tab-content" v-show="explainTab === 'advanced'">
<div class="expand-card" :class="{active: expandedCard === 1}" @click="toggleCard(1)">
<div class="card-header">
<span class="card-title" style="color: #9b59b6;">例题2:进阶题(职业推理)</span>
<span class="toggle-icon">▼</span>
</div>
<div class="card-content" @click.stop>
<div><strong>题目:</strong>甲乙丙三人是厨师、司机、医生。甲说:我不是医生。乙说:我不是司机也不是医生。丙是?</div>
<div><strong>答案:</strong>医生</div>
<div><strong>解析:</strong></div>
<div>1. 看乙:不是司机,不是医生 -> 乙是厨师。</div>
<div>2. 看甲:不是医生,也不能是厨师(乙是了) -> 甲是司机。</div>
<div>3. 看丙:只剩医生 -> 丙是医生。</div>
</div>
</div>
</div>
<div class="tab-content" v-show="explainTab === 'mistake'">
<div class="expand-card" :class="{active: expandedCard === 2}" @click="toggleCard(2)">
<div class="card-header">
<span class="card-title" style="color: #e74c3c;">例题3:易错题(高矮排序)</span>
<span class="toggle-icon">▼</span>
</div>
<div class="card-content" @click.stop>
<div><strong>题目:</strong>A、B、C比高矮。A说:我不是最高的。B说:我不是最高的,也不是最矮的。谁最高?</div>
<div><strong>答案:</strong>C</div>
<div><strong>解析:</strong></div>
<div>1. 突破口是B:B不是高,不是矮 -> B是中间。</div>
<div>2. A不是最高,也不能是中间 -> A是最矮。</div>
<div>3. 剩下的C是最高。</div>
</div>
</div>
</div>
</div>
</div>
<div v-show="currentPage === 4" class="page practice-page">
<h1 class="page-title">课内练习</h1>
<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'" style="background: #f8f9fa; padding: 15px; border-radius: 12px; margin-bottom: 15px;">
<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" style="text-align: center; padding: 40px;">
<div class="completion-emoji" style="font-size: 80px;">🎊</div>
<div class="completion-title" style="font-size: 28px; margin: 20px 0;">课内练习完成!</div>
<div class="final-score" style="font-size: 50px; color: #ff9a00; font-weight: bold; margin-bottom: 30px;">{{score}}分</div>
<button class="restart-btn" @click="switchPage(5)">挑战奥数题 →</button>
</div>
</div>
<div v-show="currentPage === 5" class="page practice-page">
<h1 class="page-title">奥数挑战</h1>
<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>
<div v-if="olympiadAnswered" class="feedback-area" :class="isOlympiadCorrect ? 'feedback-correct' : 'feedback-wrong'" style="background: #f8f9fa; padding: 15px; border-radius: 12px; margin-bottom: 15px;">
<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" style="text-align: center; padding: 40px;">
<div class="completion-emoji" style="font-size: 80px;">🏆</div>
<div class="completion-title" style="font-size: 28px; margin: 20px 0;">奥数挑战完成!</div>
<div class="final-score" style="font-size: 50px; color: #ff9a00; font-weight: bold; margin-bottom: 30px;">{{olympiadScore}}分</div>
<button class="restart-btn" @click="switchPage(6)">查看通关秘籍 →</button>
</div>
</div>
<div v-show="currentPage === 6" class="page secret-page">
<h1 class="page-title">通关秘籍</h1>
<div class="secret-container">
<div class="secret-card">
<h3>秘籍 1:列表格</h3>
<p>脑子记不住,表格来帮助,行写人名列写物。</p>
</div>
<div class="secret-card">
<h3>秘籍 2:找矛盾</h3>
<p>两句话打架,肯定一真一假,盯着它俩看。</p>
</div>
<div class="secret-card">
<h3>秘籍 3:假设法</h3>
<p>如果推不动,就先猜一个,看看通不通。</p>
</div>
</div>
<div style="padding: 0 24px;">
<button class="restart-btn" style="margin-top: 10px;" @click="switchPage(1)">再学一遍 ↺</button>
</div>
</div>
</div>
<div class="bottom-nav">
<div class="nav-item" :class="{active: currentPage === 1}" @click="switchPage(1)">
<div class="nav-icon">🕵️</div>
<div>引入</div>
</div>
<div class="nav-item" :class="{active: currentPage === 2}" @click="switchPage(2)">
<div class="nav-icon">📚</div>
<div>概念</div>
</div>
<div class="nav-item" :class="{active: currentPage === 3}" @click="switchPage(3)">
<div class="nav-icon">💡</div>
<div>讲解</div>
</div>
<div class="nav-item" :class="{active: currentPage === 4}" @click="switchPage(4)">
<div class="nav-icon">✏️</div>
<div>练习</div>
</div>
<div class="nav-item" :class="{active: currentPage === 5}" @click="switchPage(5)">
<div class="nav-icon">🚀</div>
<div>挑战</div>
</div>
<div class="nav-item" :class="{active: currentPage === 6}" @click="switchPage(6)">
<div class="nav-icon">📜</div>
<div>秘籍</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, ref, onMounted, nextTick } = Vue;
createApp({
setup() {
// 语音功能 (保持原样)
const 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 {
if (window.speechSynthesis) {
window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'zh-CN';
utterance.rate = 0.9;
window.speechSynthesis.speak(utterance);
}
}
}
const 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();
}
}
}
// 页面状态
const currentPage = ref(1);
const switchPage = (page) => {
stopSpeak();
currentPage.value = page;
nextTick(() => {
const pageEl = document.querySelector(`[v-show="currentPage === ${page}"]`);
if (pageEl) {
pageEl.classList.add('page-enter');
setTimeout(() => pageEl.classList.remove('page-enter'), 600);
}
});
if (page === 2) {
demoStep.value = 0;
runDirectAnimation();
}
}
// 页面2:演示动画状态
const demoTab = ref('direct');
const demoStep = ref(0);
const switchDemoTab = (tab) => {
demoTab.value = tab;
if (tab === 'theory') {
demoStep.value = 0;
nextTick(() => runDirectAnimation());
}
}
const prevStep = () => { if (demoStep.value > 0) demoStep.value--; runDirectAnimation(); }
const nextStep = () => { if (demoStep.value < 2) demoStep.value++; runDirectAnimation(); }
// 升级版 SVG 动画渲染
const runDirectAnimation = () => {
const svg = document.getElementById('demo-svg');
if (!svg) return;
svg.innerHTML = '';
const ns = "http://www.w3.org/2000/svg";
const width = 400, height = 300; // 调整高度适应容器
const svgEl = document.createElementNS(ns, "svg");
svgEl.setAttribute("width", "100%");
svgEl.setAttribute("height", "100%");
svgEl.setAttribute("viewBox", `0 0 ${width} ${height}`);
svg.appendChild(svgEl);
const animals = ['猫', '兔', '狗'];
const foods = ['草', '萝卜', '骨头'];
// 布局参数
const startX = 60, startY = 60;
const gridW = width - startX - 20;
const gridH = height - startY - 40;
const cellW = gridW / 3;
const cellH = gridH / 3;
const tl = gsap.timeline();
// 绘制背景表格(更柔和)
// 纵线
for (let i = 0; i <= 3; i++) {
const line = document.createElementNS(ns, "line");
line.setAttribute("x1", startX + i * cellW); line.setAttribute("y1", startY);
line.setAttribute("x2", startX + i * cellW); line.setAttribute("y2", startY + gridH);
line.setAttribute("stroke", "#e0e0e0");
line.setAttribute("stroke-width", "2");
line.setAttribute("stroke-linecap", "round");
svgEl.appendChild(line);
}
// 横线
for (let i = 0; i <= 3; i++) {
const line = document.createElementNS(ns, "line");
line.setAttribute("x1", startX); line.setAttribute("y1", startY + i * cellH);
line.setAttribute("x2", startX + gridW); line.setAttribute("y2", startY + i * cellH);
line.setAttribute("stroke", "#e0e0e0");
line.setAttribute("stroke-width", "2");
line.setAttribute("stroke-linecap", "round");
svgEl.appendChild(line);
}
// 表头文字
for (let i = 0; i < 3; i++) {
// 左侧动物
const animalText = document.createElementNS(ns, "text");
animalText.textContent = animals[i];
animalText.setAttribute("x", startX - 15);
animalText.setAttribute("y", startY + i * cellH + cellH/2 + 5);
animalText.setAttribute("text-anchor", "end");
animalText.setAttribute("fill", "#667eea");
animalText.setAttribute("font-weight", "bold");
animalText.setAttribute("font-size", "18");
svgEl.appendChild(animalText);
// 顶部食物
const foodText = document.createElementNS(ns, "text");
foodText.textContent = foods[i];
foodText.setAttribute("x", startX + i * cellW + cellW/2);
foodText.setAttribute("y", startY - 15);
foodText.setAttribute("text-anchor", "middle");
foodText.setAttribute("fill", "#ff9a9e");
foodText.setAttribute("font-weight", "bold");
foodText.setAttribute("font-size", "18");
svgEl.appendChild(foodText);
// 给每个格子加一个透明rect用于点击或高亮(视觉优化)
for(let j=0; j<3; j++) {
const cellBg = document.createElementNS(ns, "rect");
cellBg.setAttribute("x", startX + j*cellW + 2);
cellBg.setAttribute("y", startY + i*cellH + 2);
cellBg.setAttribute("width", cellW - 4);
cellBg.setAttribute("height", cellH - 4);
cellBg.setAttribute("fill", "#f9f9f9");
cellBg.setAttribute("rx", "8"); // 圆角
svgEl.insertBefore(cellBg, svgEl.firstChild);
}
}
// 动画逻辑
if (demoStep.value >= 0) {
const clue1 = document.createElementNS(ns, "text");
clue1.textContent = "线索:猫不吃草";
clue1.setAttribute("x", width/2); clue1.setAttribute("y", 25);
clue1.setAttribute("text-anchor", "middle");
clue1.setAttribute("font-size", "18"); clue1.setAttribute("fill", "#333");
clue1.setAttribute("font-weight", "bold");
svgEl.appendChild(clue1);
tl.from(clue1, {opacity: 0, y: -20, duration: 0.5});
}
if (demoStep.value >= 1) {
// 猫(0) 不吃 草(0) -> [0,0] 打叉
const cross = document.createElementNS(ns, "path");
// 手绘风格的叉
const cx = startX + 0 * cellW + cellW/2;
const cy = startY + 0 * cellH + cellH/2;
const size = 15;
const d = `M${cx-size},${cy-size} L${cx+size},${cy+size} M${cx+size},${cy-size} L${cx-size},${cy+size}`;
cross.setAttribute("d", d);
cross.setAttribute("stroke", "#ff6b6b");
cross.setAttribute("stroke-width", "4");
cross.setAttribute("stroke-linecap", "round");
cross.setAttribute("opacity", "0");
svgEl.appendChild(cross);
tl.to(cross, {opacity: 1, scale: 1.2, transformOrigin: "center", duration: 0.4, ease: "back.out"});
const clue2 = document.createElementNS(ns, "text");
clue2.textContent = "线索:兔吃萝卜";
clue2.setAttribute("x", width/2); clue2.setAttribute("y", 25); // 覆盖位置
clue2.setAttribute("text-anchor", "middle");
clue2.setAttribute("font-size", "18"); clue2.setAttribute("fill", "#28a745");
clue2.setAttribute("font-weight", "bold");
clue2.setAttribute("opacity", "0");
svgEl.appendChild(clue2);
// 简单的文字切换效果
tl.to("text[fill='#333']", {opacity: 0, duration: 0.2});
tl.to(clue2, {opacity: 1, duration: 0.3});
}
if (demoStep.value >= 2) {
// 兔(1) 吃 萝卜(1) -> [1,1] 打勾
const check = document.createElementNS(ns, "path");
const cx = startX + 1 * cellW + cellW/2;
const cy = startY + 1 * cellH + cellH/2;
// 手绘风格的勾
const d = `M${cx-15},${cy} L${cx-5},${cy+15} L${cx+20},${cy-20}`;
check.setAttribute("d", d);
check.setAttribute("fill", "none");
check.setAttribute("stroke", "#20bf6b");
check.setAttribute("stroke-width", "4");
check.setAttribute("stroke-linecap", "round");
check.setAttribute("stroke-linejoin", "round");
// 路径动画
const length = 100; // 近似长度
check.setAttribute("stroke-dasharray", length);
check.setAttribute("stroke-dashoffset", length);
svgEl.appendChild(check);
tl.to(check, {strokeDashoffset: 0, duration: 0.6, ease: "power2.out"});
// 排除其他格
const excludePositions = [
[0,1], [2,1], // 兔行的其他 (列0,2)
[1,0], [1,2] // 萝卜列的其他 (行0,2)
];
excludePositions.forEach(([col, row], idx) => {
const cross2 = document.createElementNS(ns, "path");
const cx2 = startX + col * cellW + cellW/2;
const cy2 = startY + row * cellH + cellH/2;
const size2 = 12;
const d2 = `M${cx2-size2},${cy2-size2} L${cx2+size2},${cy2+size2} M${cx2+size2},${cy2-size2} L${cx2-size2},${cy2+size2}`;
cross2.setAttribute("d", d2);
cross2.setAttribute("stroke", "#ff6b6b");
cross2.setAttribute("stroke-width", "3");
cross2.setAttribute("stroke-linecap", "round");
cross2.setAttribute("opacity", "0");
svgEl.appendChild(cross2);
tl.to(cross2, {opacity: 0.6, scale: 1, duration: 0.2}, ">");
});
const explain = document.createElementNS(ns, "text");
explain.textContent = "一个确定,排除一行一列!";
explain.setAttribute("x", width/2);
explain.setAttribute("y", height - 15);
explain.setAttribute("text-anchor", "middle");
explain.setAttribute("font-size", "16");
explain.setAttribute("fill", "#667eea");
explain.setAttribute("font-weight", "bold");
explain.setAttribute("opacity", "0");
svgEl.appendChild(explain);
tl.to(explain, {opacity: 1, y: height - 20, duration: 0.5, ease: "elastic.out(1, 0.3)"});
}
}
// 页面3:讲解页状态
const explainTab = ref('basic');
const expandedCard = ref(null);
const toggleCard = (index) => {
expandedCard.value = expandedCard.value === index ? null : index;
}
// 页面4:课内练习状态与数据 (保持原样)
const practiceCompleted = ref(false);
const currentQuestion = ref(0);
const score = ref(0);
const answered = ref(false);
const selectedOption = ref(null);
const isCorrect = ref(false);
const practiceQuestions = [
{
text: `小猫、小狗、小兔赛跑。小猫说:我不是最后。小狗说:我不是第一也不是最后。谁是第一?`,
options: [
{text: 'A.小猫', correct: true},
{text: 'B.小狗', correct: false},
{text: 'C.小兔', correct: false},
{text: 'D.不知道', correct: false}
],
explanation: `小狗不是第一也不是最后,所以小狗是第二。小猫不是最后,而最后只能是剩下的小兔,所以小猫是第一。`
},
{
text: `红黄蓝三盒装苹果、梨、桃。红盒:我不装梨。黄盒:我装桃。蓝盒装?`,
options: [
{text: 'A.苹果', correct: false},
{text: 'B.梨', correct: true},
{text: 'C.桃', correct: false},
{text: 'D.空', correct: false}
],
explanation: `黄盒装桃(√)。红盒不装梨,也不能装桃(黄盒装了),所以红盒装苹果(√)。剩下的蓝盒装梨(√)。`
},
{
text: `甲乙丙丁住101-104。甲住103。乙不住101。丙不住101也不住102。丁住哪?`,
options: [
{text: 'A.101', correct: true},
{text: 'B.102', correct: false},
{text: 'C.103', correct: false},
{text: 'D.104', correct: false}
],
explanation: `甲住103(√)。丙不住101,102,也不能住103(甲住了),所以丙住104(√)。乙不住101,也不能住103、104,所以乙住102(√)。最后丁住101(√)。`
},
{
text: `石头剪刀布。A:我出石头。B:我没出剪刀。C出什么?`,
options: [
{text: 'A.石头', correct: false},
{text: 'B.剪刀', correct: true},
{text: 'C.布', correct: false},
{text: 'D.不知道', correct: false}
],
explanation: `A出石头(√)。B没出剪刀,也不能出石头(A出了),所以B出布(√)。剩下剪刀给C(√)。`
},
{
text: `爸爸买球(篮、排、足)。小明:我不喜欢足。小亮:没买篮也没买足。给小红买了啥?`,
options: [
{text: 'A.篮球', correct: false},
{text: 'B.排球', correct: false},
{text: 'C.足球', correct: true},
{text: 'D.气球', correct: false}
],
explanation: `小亮没买篮也没买足,所以小亮买排球(√)。小明不喜欢足,也不能喜欢排(小亮买了),所以小明买篮球(√)。剩下足球给小红(√)。`
},
{
text: `1号、2号、3号箱装金银铜。1号:不装金。2号:不装银。3号:装金。只有一句真话。金在哪?`,
options: [
{text: 'A.1号', correct: true},
{text: 'B.2号', correct: false},
{text: 'C.3号', correct: false},
{text: 'D.不知道', correct: false}
],
explanation: `假设法。假设3号说真话(3号有金),则1号说“不装金”也是真话(因为金在3号),两句真话矛盾。所以3号说假话(3号没金)。那么金在1号或2号。继续分析可知,只有金在1号时,满足“只有一句真话”。`
},
{
text: `A比B重,C比A重。谁最重?`,
options: [
{text: 'A.A', correct: false},
{text: 'B.B', correct: false},
{text: 'C.C', correct: true},
{text: 'D.一样重', correct: false}
],
explanation: `由“A比B重”得:A > B。由“C比A重”得:C > A。所以顺序是:C > A > B。C最重。`
},
{
text: `张三、李四、王五是京沪穗人。张三不是北京人,李四不是北京也不是上海。王五是?`,
options: [
{text: 'A.北京', correct: true},
{text: 'B.上海', correct: false},
{text: 'C.广州', correct: false},
{text: 'D.深圳', correct: false}
],
explanation: `李四不是北京也不是上海,所以李四是广州人(√)。张三不是北京人,也不能是广州人(李四是),所以张三是上海人(√)。剩下的王五是北京人(√)。`
}
];
const selectOption = (index, correct) => {
if (answered.value) return;
selectedOption.value = index;
answered.value = true;
isCorrect.value = correct;
const btn = document.querySelectorAll('.option-btn')[index];
if (!correct) {
btn.classList.add('vibrate');
setTimeout(() => btn.classList.remove('vibrate'), 500);
} else {
score.value += 10;
speak('答对了,真棒!');
if (typeof confetti === 'function') {
confetti({
particleCount: 100,
spread: 70,
origin: { y: 0.6 }
});
}
}
}
const nextQuestion = () => {
if (currentQuestion.value < practiceQuestions.length - 1) {
currentQuestion.value++;
} else {
practiceCompleted.value = true;
}
answered.value = false;
selectedOption.value = null;
isCorrect.value = false;
}
// 页面5:奥数挑战状态与数据 (保持原样)
const olympiadCompleted = ref(false);
const currentOlympiad = ref(0);
const olympiadScore = ref(0);
const olympiadAnswered = ref(false);
const selectedOlympiadOption = ref(null);
const isOlympiadCorrect = ref(false);
const wrongAttempts = ref(0);
const olympiadQuestions = [
{
text: `老师问谁打碎花瓶。甲:是乙。乙:是丁。丙:不是我。丁:乙说谎。只有一人说真话。谁干的?`,
options: [
{text: 'A.甲', correct: false},
{text: 'B.乙', correct: false},
{text: 'C.丙', correct: true},
{text: 'D.丁', correct: false}
],
hint1: `注意乙和丁的话,它们互相矛盾。矛盾的话会一真一假。`,
hint2: `既然真话在乙和丁之间,那么甲和丙说的都是假话。丙说“不是我”是假话,意味着什么?`,
explanation: `乙和丁的话矛盾(一个说“是丁”,一个说“乙说谎”),必有一真一假。已知只有一人说真话,所以真话在乙丁之间,甲和丙都说假话。丙说“不是我”是假的 -> 就是丙干的。`,
difficulty: 2,
difficultyText: '中等'
},
{
text: `A、B、C、D比赛。A:我第一。B:我第三。C:我不是第四。D:我不是第一。只有一人假话。谁假话?`,
options: [
{text: 'A.A', correct: false},
{text: 'B.B', correct: false},
{text: 'C.C', correct: false},
{text: 'D.D', correct: true}
],
hint1: `注意A和D都提到了“第一”,他们的话可能存在冲突。尝试假设其中一人说假话。`,
hint2: `假设A说真话(A第一),看看是否会导致矛盾?如果A第一为真,D说“我不是第一”就是假话,其他人必须全说真话,检查B和C的话能同时成立吗?`,
explanation: `假设A真(A第一),则D假(D说不是第一,矛盾)。此时B真(B第三),C真(C不是第四 -> C第二),那么D就是第四。所有陈述成立,且只有D假。若假设A假,则会产生更多矛盾。所以D说假话。`,
difficulty: 3,
difficultyText: '困难'
},
{
text: `甲乙丙丁四个嫌疑人。甲:是乙。乙:是丁。丙:是乙。丁:不是我。四人只有一人说真话。谁是罪犯?`,
options: [
{text: 'A.甲', correct: false},
{text: 'B.乙', correct: false},
{text: 'C.丙', correct: false},
{text: 'D.丁', correct: true}
],
hint1: `甲和丙都指控乙。如果乙是罪犯,会怎样?`,
hint2: `如果乙是罪犯,那么甲和丙都说真话,这与“只有一人说真话”矛盾。所以乙不是罪犯。那么甲和丙都在说谎。`,
explanation: `甲和丙都说是乙,如果乙是罪犯,那就有两人说真话了(矛盾)。所以乙不是罪犯。甲、丙都说假话。乙说“是丁”,丁说“不是我”。若乙真,则丁是罪犯,丁假。符合。罪犯是丁。`,
difficulty: 2,
difficultyText: '中等'
},
{
text: `三只袋子,分别装:两黑球、两白球、一黑一白。袋子上标签全贴错了。从“黑白”袋里摸一个球是黑的。问:这袋子到底装什么?`,
options: [
{text: 'A.两黑', correct: true},
{text: 'B.两白', correct: false},
{text: 'C.一黑一白', correct: false}
],
hint1: `标签全错!“黑白”袋的标签是“黑白”,这意味着它实际上不可能装什么?`,
hint2: `它不可能装“一黑一白”。现在摸出一个黑球,它能是“两白”吗?`,
explanation: `标签全错。“黑白”袋贴着“黑白”,说明它肯定不是一黑一白。摸出黑球 -> 它不可能是两白(因为摸不出黑)。所以它只能是“两黑”。`,
difficulty: 1,
difficultyText: '基础'
}
];
const selectOlympiadOption = (index, correct) => {
if (olympiadAnswered.value) return;
selectedOlympiadOption.value = index;
isOlympiadCorrect.value = correct;
const btn = document.querySelectorAll('.option-btn')[index];
if (!correct) {
wrongAttempts.value++;
btn.classList.add('vibrate');
setTimeout(() => btn.classList.remove('vibrate'), 500);
if (wrongAttempts.value >= 3) {
olympiadAnswered.value = true;
}
} else {
olympiadAnswered.value = true;
isOlympiadCorrect.value = true;
olympiadScore.value += 25;
speak('挑战成功!你真是推理高手!');
if (typeof confetti === 'function') {
confetti({
particleCount: 150,
spread: 100,
origin: { y: 0.6 }
});
}
}
}
const nextOlympiad = () => {
if (currentOlympiad.value < olympiadQuestions.length - 1) {
currentOlympiad.value++;
} else {
olympiadCompleted.value = true;
}
olympiadAnswered.value = false;
selectedOlympiadOption.value = null;
isOlympiadCorrect.value = false;
wrongAttempts.value = 0;
}
onMounted(() => {
// 初始化
});
return {
speak, stopSpeak,
currentPage, switchPage,
demoTab, demoStep, switchDemoTab, prevStep, nextStep,
explainTab, expandedCard, toggleCard,
practiceCompleted, currentQuestion, score, answered, selectedOption, isCorrect, practiceQuestions, selectOption, nextQuestion,
olympiadCompleted, currentOlympiad, olympiadScore, olympiadAnswered, selectedOlympiadOption, isOlympiadCorrect, wrongAttempts, olympiadQuestions, selectOlympiadOption, nextOlympiad
};
}
}).mount('#app');
</script>
</body>
</html>
💡 这段代码完全由 AI 生成。
登录后可复制完整代码