This repository has been archived on 2026-08-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
2026-08-08 15:49:06 +08:00

326 lines
13 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
let testData = null, answers = [], currentQ = 0, myResult = null;
async function init() {
try {
const r = await fetch('/api/math_personality');
const j = await r.json();
if (j.code !== 0) { showToast('测试数据加载失败'); return; }
testData = j.data;
renderIntro();
} catch (e) {
document.getElementById('introScreen').innerHTML =
'<div style="text-align:center;padding:60px 20px"><p style="color:#94A3B8;margin-bottom:12px">加载失败,请检查网络</p><button class="btn-start" onclick="init()">重试</button></div>';
}
}
function renderIntro() {
const axesEl = document.getElementById('introAxes');
if (axesEl && testData.axes) {
axesEl.innerHTML = testData.axes.map(a =>
'<div class="intro-axis-item">' +
'<span class="axis-emoji">' + (a.emoji_left || '') + '</span>' +
'<span class="axis-label">' + a.name + '</span>' +
'<span class="axis-emoji">' + (a.emoji_right || '') + '</span>' +
'</div>'
).join('');
}
document.querySelector('.intro-meta').textContent =
'⏱ 约2分钟 · 4选项 · 16种人格';
}
function startQuiz() {
answers = new Array(testData.questions.length).fill(-1);
currentQ = 0;
switchScreen('quizScreen');
renderQuestion();
}
function renderQuestion() {
const q = testData.questions[currentQ];
document.getElementById('quizQuestion').textContent = q.question;
const optEl = document.getElementById('quizOptions');
const sel = answers[currentQ];
optEl.innerHTML = q.options.map((o, i) =>
'<label class="quiz-option' + (sel === i ? ' selected' : '') + '" onclick="selectOption(' + i + ')">' +
'<input type="radio" name="q' + currentQ + '" value="' + i + '"' + (sel === i ? ' checked' : '') + '>' +
'<span class="opt-text">' + o.text + '</span></label>'
).join('');
const pct = Math.round((currentQ + 1) / testData.questions.length * 100);
document.getElementById('progressFill').style.width = pct + '%';
const isLast=currentQ===testData.questions.length-1;
document.getElementById('progressFill').style.setProperty('--is-last',isLast?'1':'0');
document.getElementById('progressText').textContent = (currentQ + 1) + '/' + testData.questions.length;
const btn = document.getElementById('btnNext');
btn.disabled = answers[currentQ] === -1;
btn.textContent = currentQ === testData.questions.length - 1 ? '查看结果' : '下一题';
}
function selectOption(idx) {
answers[currentQ] = idx;
document.getElementById('btnNext').disabled = false;
document.querySelectorAll('.quiz-option').forEach((el, i) => {
el.classList.toggle('selected', i === idx);
});
}
async function nextQuestion() {
if (answers[currentQ] === -1) return;
if (currentQ < testData.questions.length - 1) {
currentQ++;
renderQuestion();
return;
}
const btn = document.getElementById('btnNext');
btn.disabled = true;
btn.textContent = '计算中…';
try {
const r = await fetch('/api/math_personality/calculate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ answers })
});
const j = await r.json();
if (j.code !== 0) { showToast('计算结果失败'); return; }
myResult = j.data;
const mbtiCode=j.data.binary_code;
let gotNewCard=false;
if(mbtiCode){
let cardState2=JSON.parse(localStorage.getItem('hulu_card_state')||'{}');
if(!cardState2.owned)cardState2.owned=[];
if(!cardState2.drawHistory)cardState2.drawHistory=[];
if(cardState2.owned.indexOf(mbtiCode)===-1){
cardState2.owned.push(mbtiCode);
cardState2.drawHistory.push({code:mbtiCode,date:new Date().toISOString().slice(0,10),method:'mathbti'});
localStorage.setItem('hulu_card_state',JSON.stringify(cardState2));
gotNewCard=true;
}
}
renderResult();
if(gotNewCard){
const res=j.data.result;
const notice=document.getElementById('cardNotice');
if(notice){notice.style.display='block';document.getElementById('cardNoticeName').textContent=res?res.name:'—'}
showToast('🎴 获得数学人物小卡!前往葫芦数学查看卡册');
}
switchScreen('resultScreen');
} catch (e) {
showToast('提交失败,请重试');
btn.disabled = false;
btn.textContent = currentQ === testData.questions.length - 1 ? '查看结果' : '下一题';
}
}
function renderResult() {
const res = myResult.result;
if (!res) return;
const portraitEl = document.getElementById('resultPortrait');
if (res.portrait) {
portraitEl.innerHTML = '<div class="result-portrait" style="background:' + (res.icon_bg || '#F0F0F0') + '"><img src="' + res.portrait + '" alt="" style="width:100%;height:100%;object-fit:cover;border-radius:50%" onerror="this.style.display=\'none\';this.nextElementSibling.style.display=\'flex\'"><span class="result-icon-fallback" style="display:none;font-size:48px">' + (res.icon || 'Σ') + '</span></div>';
} else {
portraitEl.innerHTML = '<div class="result-portrait" style="background:' + (res.icon_bg || '#F0F0F0') + '"><span class="result-icon-fallback" style="font-size:48px">' + (res.icon || 'Σ') + '</span></div>';
}
document.getElementById('resultName').innerHTML = res.name + ' <span class="type-code" style="font-size:13px;color:#94A3B8;font-weight:400">' + myResult.binary_code + '</span>';
document.getElementById('resultTagline').textContent = res.tagline || '';
const clanData = testData.clans[res.clan];
document.getElementById('resultMathematician').innerHTML =
(clanData ? '<span class="clan-badge" style="background:' + (clanData.color || '#888') + '15;color:' + (clanData.color || '#888') + ';border:1px solid ' + (clanData.color || '#888') + '30">' + clanData.emoji + ' ' + clanData.name + ' · ' + res.mathematician + '</span>' : '') +
'<span class="mathematician-name">代表数学家:' + (res.mathematician || '—') + '</span>';
const kwEl = document.getElementById('resultKeywords');
if (res.keywords) {
kwEl.innerHTML = res.keywords.map(k => '<span class="kw-tag">' + k + '</span>').join('');
kwEl.style.display = 'flex';
} else {
kwEl.style.display = 'none';
}
document.getElementById('resultDesc').textContent = res.description || '';
document.getElementById('resultShort').textContent = res.short_desc || '';
document.getElementById('bioTitle').textContent = '关于 ' + (res.mathematician || '这位数学家');
document.getElementById('bioText').textContent = res.bio || '';
document.getElementById('bioFact').textContent = res.bio_fact || '';
const axesEl = document.getElementById('resultAxes');
if (testData.axes) {
axesEl.innerHTML = testData.axes.map(a => {
const score = myResult.axis_scores[a.id] || 0;
const maxScore = testData.scoring.max_per_axis || 9;
const pct = Math.round(score / maxScore * 100);
return '<div class="axis-bar-row">' +
'<span class="axis-bar-label">' + (a.emoji_left || '') + ' ' + a.left + '</span>' +
'<div class="axis-bar-track"><div class="axis-bar-fill" style="width:' + pct + '%"></div></div>' +
'<span class="axis-bar-label">' + a.right + ' ' + (a.emoji_right || '') + '</span>' +
'</div>';
}).join('');
axesEl.style.display = 'block';
}
const petData = JSON.parse(localStorage.getItem('mb_pet') || '{}');
const adoptBtn = document.querySelector('.btn-adopt');
if (petData.type) {
adoptBtn.textContent = '✅ 已领养 · 我的数学精灵';
adoptBtn.classList.add('adopted');
}
if (clanData) {
document.getElementById('resultScreen').style.setProperty('--clan-color', clanData.color);
}
}
function toggleBio() {
const body = document.getElementById('bioBody');
const arrow = document.getElementById('bioArrow');
const expanded = body.style.maxHeight && body.style.maxHeight !== '0px';
if (expanded) {
body.style.maxHeight = '0px';
arrow.textContent = '▾';
} else {
body.style.maxHeight = body.scrollHeight + 'px';
arrow.textContent = '▴';
}
}
function adoptPet() {
if (!myResult) return;
const res = myResult.result;
const pet = {
type: res.name || '—',
icon: res.icon || 'Σ',
color: res.color || '#5B6AF0',
mathematician: res.mathematician || '',
clan: res.clan || '',
binary_code: myResult.binary_code
};
localStorage.setItem('mb_pet', JSON.stringify(pet));
const btn = document.querySelector('.btn-adopt');
btn.textContent = '✅ 已领养 · 我的数学精灵';
btn.classList.add('adopted');
showToast('🧬 数学精灵已领养!每天来葫芦数学 App 喂它成长');
}
function copyShare() {
if (!myResult) return;
const res = myResult.result;
const clanData = testData.clans[res.clan];
const text = '我的数学人格是「' + res.name + '」' + (res.icon || '') + '\n' +
(clanData ? clanData.emoji + ' ' + clanData.name + ' · ' + res.mathematician : '') + '\n' +
(res.short_desc || '') + '\n\n' +
'👉 你也来测测:' + window.location.href + '\n\n' +
'#数学人格测试 #MathBTI';
navigator.clipboard.writeText(text).then(() => showToast('已复制分享文案!'));
}
function shareResult() {
if (!myResult) return;
const res = myResult.result;
const text = '我的数学人格是「' + res.name + '」' + (res.icon || '') + '' + (res.short_desc || '');
if (navigator.share) {
navigator.share({ title: 'MathBTI · 数学人格测试', text: text, url: window.location.href }).catch(() => {});
} else {
copyShare();
}
}
function generateShareCard() {
if (!myResult) return;
const res = myResult.result;
const clanData = testData.clans[res.clan];
const canvas = document.getElementById('shareCardCanvas');
const ctx = canvas.getContext('2d');
canvas.width = 600;
canvas.height = 800;
const gradient = ctx.createLinearGradient(0, 0, 600, 800);
gradient.addColorStop(0, (res.color || '#5B6AF0') + '22');
gradient.addColorStop(1, (res.color || '#5B6AF0') + '08');
ctx.fillStyle = '#FAFBFC';
ctx.fillRect(0, 0, 600, 800);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 600, 800);
ctx.fillStyle = res.color || '#5B6AF0';
ctx.fillRect(0, 0, 600, 6);
ctx.fillStyle = res.icon_bg || '#F0F0F0';
ctx.beginPath();
ctx.arc(300, 180, 70, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#334155';
ctx.font = '60px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(res.icon || 'Σ', 300, 180);
ctx.fillStyle = '#1E293B';
ctx.font = 'bold 36px "PingFang SC", sans-serif';
ctx.fillText(res.name, 300, 280);
ctx.fillStyle = '#64748B';
ctx.font = '18px "PingFang SC", sans-serif';
if (clanData) {
ctx.fillText(clanData.emoji + ' ' + clanData.name + ' · ' + res.mathematician, 300, 315);
}
ctx.fillStyle = res.color || '#5B6AF0';
ctx.font = 'italic 15px "PingFang SC", sans-serif';
const tagline = res.tagline || '';
wrapText(ctx, tagline, 300, 350, 480, 22);
ctx.fillStyle = '#E2E8F0';
ctx.fillRect(60, 380, 480, 1);
ctx.fillStyle = '#334155';
ctx.font = '15px "PingFang SC", sans-serif';
const desc = res.short_desc || '';
wrapText(ctx, desc, 300, 410, 480, 24);
let ay = 450;
if (testData.axes && myResult.axis_scores) {
ctx.fillStyle = '#64748B';
ctx.font = '14px "PingFang SC", sans-serif';
testData.axes.forEach(a => {
const s = myResult.axis_scores[a.id] || 0;
const maxS = testData.scoring.max_per_axis || 9;
const pct = Math.round(s / maxS * 100);
ctx.fillText(a.emoji_left + ' ' + a.left + ' — ' + pct + '% — ' + a.right + ' ' + a.emoji_right, 300, ay);
ay += 28;
});
}
ctx.fillStyle = '#94A3B8';
ctx.font = '13px "PingFang SC", sans-serif';
ctx.fillText('葫芦数学 · MathBTI', 300, 730);
ctx.save();ctx.globalAlpha=.06;ctx.font='80px sans-serif';ctx.fillStyle='#A8907E';
ctx.fillText('🏮',480,680);
ctx.restore();
ctx.fillText('你也来测测你的数学人格类型 →', 300, 755);
ctx.fillStyle = '#E2E8F0';
ctx.fillRect(255, 640, 90, 90);
ctx.fillStyle = '#94A3B8';
ctx.font = '11px sans-serif';
ctx.fillText('扫码测', 300, 685);
document.getElementById('shareCardPreview').style.display = 'block';
document.getElementById('shareCardPreview').scrollIntoView({ behavior: 'smooth' });
}
function downloadShareCard() {
const canvas = document.getElementById('shareCardCanvas');
const link = document.createElement('a');
link.download = 'mathbti_' + (myResult ? myResult.binary_code : 'result') + '.png';
link.href = canvas.toDataURL('image/png');
link.click();
showToast('📸 图片已保存!');
}
function wrapText(ctx, text, x, y, maxWidth, lineHeight) {
const lines = [];
let current = '';
for (let i = 0; i < text.length; i++) {
const test = current + text[i];
if (ctx.measureText(test).width > maxWidth && current.length > 0) {
lines.push(current);
current = text[i];
} else {
current = test;
}
}
if (current) lines.push(current);
lines.forEach((line, idx) => {
ctx.fillText(line, x, y + idx * lineHeight);
});
}
function retryQuiz() {
answers = [];
currentQ = 0;
myResult = null;
document.getElementById('bioBody').style.maxHeight = '0px';
document.getElementById('bioArrow').textContent = '▾';
document.getElementById('shareCardPreview').style.display = 'none';
switchScreen('introScreen');
}
function switchScreen(id) {
document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
document.getElementById(id).classList.add('active');
window.scrollTo({ top: 0, behavior: 'smooth' });
}
let toastTimer = null;
function showToast(msg) {
const el = document.getElementById('toast');
el.textContent = msg;
el.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(() => el.classList.remove('show'), 2200);
}
document.addEventListener('DOMContentLoaded', init);