192 lines
10 KiB
HTML
192 lines
10 KiB
HTML
{{define "learn_number"}}
|
|
<div id="panel-learn-number" class="tool-panel">
|
|
<header class="page-header">
|
|
<h1>幼儿数学助手</h1>
|
|
<p>通过趣味游戏,培养孩子的数感、逻辑思维与书写能力。</p>
|
|
</header>
|
|
<div class="tabs-container">
|
|
<div id="tab-counting" class="tab-btn active" onclick="switchMathTab('counting')">数图形练习</div>
|
|
<div id="tab-writing" class="tab-btn" onclick="switchMathTab('writing')">数字连连看</div>
|
|
</div>
|
|
|
|
<!-- 数图形面板 -->
|
|
<div id="math-counting-controls" class="card">
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 20px; align-items: flex-end;">
|
|
<div class="input-group">
|
|
<label style="font-size: 13px; font-weight: 600; color: #86868b; margin-bottom: 8px; display: block;">练习主题</label>
|
|
<select id="math_category" onchange="updateIconPreview()" class="apple-select">
|
|
<option value="fruits">素材库 (SVG)</option>
|
|
<option value="shapes">基础几何图形</option>
|
|
</select>
|
|
</div>
|
|
<div class="input-group">
|
|
<label style="font-size: 13px; font-weight: 600; color: #86868b; margin-bottom: 8px; display: block;">种类 (Max 6)</label>
|
|
<input type="number" id="icon_types" min="1" max="6" value="3" class="apple-input">
|
|
</div>
|
|
<div class="input-group">
|
|
<label style="font-size: 13px; font-weight: 600; color: #86868b; margin-bottom: 8px; display: block;">总量 (Max 30)</label>
|
|
<input type="number" id="total_count" min="1" max="30" value="15" class="apple-input">
|
|
</div>
|
|
<div class="input-group">
|
|
<label style="font-size: 13px; font-weight: 600; color: #86868b; margin-bottom: 8px; display: block;">页数 (1-10)</label>
|
|
<input type="number" id="page_count" min="1" max="10" value="1" class="apple-input">
|
|
</div>
|
|
<div class="input-group">
|
|
<label style="font-size: 13px; font-weight: 600; color: #86868b; margin-bottom: 8px; display: block;">纸张</label>
|
|
<select id="math_paper_size" class="apple-select">
|
|
<option value="Letter" selected>Letter</option>
|
|
<option value="A4">A4</option>
|
|
</select>
|
|
</div>
|
|
<button onclick="generateMathPDF()" id="btn-math-counting">生成练习帖</button>
|
|
</div>
|
|
<div id="icon-preview-container" style="margin-top: 24px; padding: 20px; background: #fbfbfd; border-radius: 12px; border: 1px solid #e5e5e7;">
|
|
<div id="icon-list" style="display: flex; gap: 12px; flex-wrap: wrap;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 数字连连看面板 (已简化页数选择) -->
|
|
<div id="math-writing-controls" class="card" style="display:none;">
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 24px; align-items: flex-end;">
|
|
<div class="input-group">
|
|
<label style="font-size: 13px; font-weight: 600; color: #86868b; margin-bottom: 8px; display: block;">起始数字</label>
|
|
<input type="number" id="start_num" min="0" value="1" class="apple-input">
|
|
</div>
|
|
<div class="input-group">
|
|
<label style="font-size: 13px; font-weight: 600; color: #86868b; margin-bottom: 8px; display: block;">结束数字</label>
|
|
<input type="number" id="end_num" min="1" value="15" class="apple-input">
|
|
</div>
|
|
<div class="input-group">
|
|
<label style="font-size: 13px; font-weight: 600; color: #86868b; margin-bottom: 8px; display: block;">纸张大小</label>
|
|
<select id="write_paper_size" class="apple-select">
|
|
<option value="Letter" selected>Letter (8.5x11in)</option>
|
|
<option value="A4">A4 (210x297mm)</option>
|
|
</select>
|
|
</div>
|
|
<button onclick="generateWritingPDF()" id="btn-math-writing">生成闯关地图</button>
|
|
</div>
|
|
<p style="margin-top: 16px; font-size: 13px; color: #86868b;">💡 规则:系统将根据数字范围自动分页(每页约 15 个)。描红数字,并寻找下一个数字进行连线。</p>
|
|
</div>
|
|
|
|
<div id="math-error-msg" style="color: #ff3b30; font-size: 14px; display: none; font-weight: 500; text-align: center; padding: 10px; background: #fff2f2; border-radius: 8px; margin-bottom: 20px;"></div>
|
|
|
|
<div id="math-preview" style="display:none; width: 100%; height: 850px; border-radius: 24px; overflow: hidden; box-shadow: 0 20px 60px rgba(0,0,0,0.12); background: #fff; border: 1px solid #d2d2d7;">
|
|
<iframe id="math-frame" style="width:100%; height:100%; border:none;"></iframe>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.apple-input, .apple-select {
|
|
width: 100%; height: 46px; padding: 0 12px; border: 1px solid #d2d2d7; border-radius: 10px;
|
|
font-size: 15px; font-weight: 500; background-color: #ffffff; transition: all 0.2s ease;
|
|
outline: none; box-shadow: inset 0 1px 2px rgba(0,0,0,0.05); line-height: 46px;
|
|
}
|
|
.apple-input:focus, .apple-select:focus { border-color: #0071e3; box-shadow: 0 0 0 4px rgba(0,113,227,0.15); }
|
|
.icon-thumbnail {
|
|
width: 52px; height: 52px; background: white; border-radius: 10px; border: 1px solid #e5e5e7;
|
|
display: flex; align-items: center; justify-content: center; padding: 6px;
|
|
box-shadow: 0 2px 6px rgba(0,0,0,0.04); transition: all 0.2s ease;
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
let mathTab = 'counting';
|
|
function switchMathTab(tab) {
|
|
mathTab = tab;
|
|
document.querySelectorAll('#panel-learn-number .tab-btn').forEach(b => b.classList.remove('active'));
|
|
document.getElementById(`tab-${tab}`).classList.add('active');
|
|
document.getElementById('math-counting-controls').style.display = (tab === 'counting') ? 'block' : 'none';
|
|
document.getElementById('math-writing-controls').style.display = (tab === 'writing') ? 'block' : 'none';
|
|
}
|
|
|
|
let allCategories = {};
|
|
async function initMathTool() {
|
|
try {
|
|
const res = await fetch('/api/learn-number/categories');
|
|
allCategories = await res.json();
|
|
updateIconPreview();
|
|
} catch (e) { console.error(e); }
|
|
}
|
|
|
|
function calculateBBox(paths) {
|
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
const numRegex = /-?\d+\.?\d*/g;
|
|
paths.forEach(path => {
|
|
const matches = path.match(numRegex);
|
|
if (matches) {
|
|
for (let i=0; i<matches.length; i+=2) {
|
|
const x = parseFloat(matches[i]); const y = parseFloat(matches[i+1]);
|
|
if (!isNaN(x) && !isNaN(y)) {
|
|
if (x < minX) minX = x; if (x > maxX) maxX = x;
|
|
if (y < minY) minY = y; if (y > maxY) maxY = y;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
if (minX === Infinity) return "0 0 1024 1024";
|
|
const w = maxX - minX, h = maxY - minY;
|
|
const padding = Math.max(w, h) * 0.15;
|
|
return `${minX - padding} ${minY - padding} ${w + padding*2} ${h + padding*2}`;
|
|
}
|
|
|
|
function updateIconPreview() {
|
|
const cat = document.getElementById('math_category').value;
|
|
const icons = allCategories[cat] || [];
|
|
const container = document.getElementById('icon-list');
|
|
container.innerHTML = '';
|
|
icons.forEach(icon => {
|
|
const div = document.createElement('div');
|
|
div.className = 'icon-thumbnail';
|
|
const viewBox = calculateBBox(icon.Paths);
|
|
const pathsHtml = icon.Paths.map(p => `<path d="${p}" fill="none" stroke="black" stroke-width="2%" stroke-linecap="round" stroke-linejoin="round" />`).join('');
|
|
div.innerHTML = `<svg viewBox="${viewBox}" style="width: 100%; height: 100%; overflow: visible;">${pathsHtml}</svg>`;
|
|
container.appendChild(div);
|
|
});
|
|
}
|
|
|
|
async function generateMathPDF() {
|
|
const total_count = parseInt(document.getElementById('total_count').value);
|
|
const icon_types = parseInt(document.getElementById('icon_types').value);
|
|
const page_count = parseInt(document.getElementById('page_count').value || 1);
|
|
const category = document.getElementById('math_category').value;
|
|
const paper_size = document.getElementById('math_paper_size').value;
|
|
if (total_count < icon_types) { alert('总量必须大于种类'); return; }
|
|
|
|
const btn = document.getElementById('btn-math-counting');
|
|
btn.innerText = '生成中...'; btn.disabled = true;
|
|
try {
|
|
const response = await fetch('/api/learn-number/counting', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ total_count, icon_types, page_count, category, paper_size })
|
|
});
|
|
const blob = await response.blob();
|
|
document.getElementById('math-preview').style.display = 'block';
|
|
document.getElementById('math-frame').src = URL.createObjectURL(blob);
|
|
} finally { btn.innerText = '生成练习帖'; btn.disabled = false; }
|
|
}
|
|
|
|
async function generateWritingPDF() {
|
|
const start_num = parseInt(document.getElementById('start_num').value);
|
|
const end_num = parseInt(document.getElementById('end_num').value);
|
|
const paper_size = document.getElementById('write_paper_size').value;
|
|
if (end_num <= start_num) { alert('结束数字必须大于起始数字'); return; }
|
|
|
|
const btn = document.getElementById('btn-math-writing');
|
|
btn.innerText = '生成中...'; btn.disabled = true;
|
|
try {
|
|
const response = await fetch('/api/learn-number/writing', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ start_num, end_num, paper_size })
|
|
});
|
|
const blob = await response.blob();
|
|
document.getElementById('math-preview').style.display = 'block';
|
|
document.getElementById('math-frame').src = URL.createObjectURL(blob);
|
|
} finally { btn.innerText = '生成闯关地图'; btn.disabled = false; }
|
|
}
|
|
|
|
setTimeout(initMathTool, 200);
|
|
</script>
|
|
{{end}}
|