(function () { 'use strict'; var root = document.getElementById('app'); var apiBase = (window.PK_CONFIG.apiBase || '').replace(/\/$/, ''); var state = { tests: [], test: null, bracket: null, result: null, template: 'celebration', sticker: true }; function toast(message) { var el = document.getElementById('toast'); el.textContent = message; el.classList.add('show'); setTimeout(function () { el.classList.remove('show'); }, 2200); } function escapeHtml(value) { return String(value).replace(/[&<>"']/g, function (char) { return ({'&':'&','<':'<','>':'>','"':'"',"'":'''})[char]; }); } function request(path, options) { options = options || {}; var token = localStorage.getItem('pk_token'); var headers = {'Content-Type':'application/json'}; if (token) headers.Authorization = 'Bearer ' + token; return fetch(apiBase + path, {method:options.method || 'GET',headers:headers,body:options.data ? JSON.stringify(options.data) : undefined}).then(function (response) { return response.json().catch(function () { return {}; }).then(function (data) { if (!response.ok) throw new Error(data.detail || '请求失败'); return data; }); }); } function ensureLogin() { if (localStorage.getItem('pk_token')) return Promise.resolve(); var id = localStorage.getItem('pk_preview_id') || (Date.now().toString(36) + Math.random().toString(36).slice(2)); localStorage.setItem('pk_preview_id', id); return request('/api/v1/auth/xhs', {method:'POST',data:{app_key:'hub',code:'dev:web-' + id}}).then(function (data) { localStorage.setItem('pk_token', data.token); }); } function loadHome() { root.innerHTML = '
正在摆好擂台…
'; request('/api/v1/tests').then(function (tests) { state.tests = tests; renderHome(); }).catch(renderError); } function renderHome() { root.innerHTML = '
TASTE LAB · PUBLIC PREVIEW

今天,认真选一个。

没有“都可以”。每轮只选一个,让你的偏爱一路赢到底。

全部测试

' + state.tests.length + ' 个题库
' + state.tests.map(function (test, index) { return ''; }).join('') + '
'; root.querySelectorAll('.test-card').forEach(function (el) { el.addEventListener('click', function () { loadTest(el.dataset.slug); }); }); } function loadTest(slug) { root.innerHTML = '
正在抽签…
'; request('/api/v1/tests/' + slug).then(function (test) { state.test = test; state.bracket = {round:1,pool:test.candidates.slice(),index:0,winners:[],eliminated:[],decisions:[]}; renderPair(); }).catch(renderError); } function pair() { return [state.bracket.pool[state.bracket.index], state.bracket.pool[state.bracket.index + 1] || null]; } function renderPair() { var items = pair(), done = state.bracket.decisions.length, total = state.test.candidates.length - 1; root.innerHTML = '
第 ' + state.bracket.round + ' 轮' + done + ' / ' + total + '

此刻只能留一个,
你会选谁?

VS
'; root.querySelectorAll('.choice').forEach(function (el) { el.addEventListener('click', function () { choose(items[Number(el.dataset.side)]); }); }); } function choose(winner) { var b = state.bracket, items = pair(), loser = items[0].id === winner.id ? items[1] : items[0]; b.winners.push(winner); b.eliminated.unshift(loser); b.decisions.push({round:b.round,left_id:items[0].id,right_id:items[1].id,winner_id:winner.id}); b.index += 2; if (b.index < b.pool.length && !b.pool[b.index + 1]) { b.winners.push(b.pool[b.index]); b.index += 1; } if (b.index >= b.pool.length) { b.pool = b.winners; b.winners = []; b.index = 0; b.round += 1; } if (b.pool.length === 1) complete(); else renderPair(); } function complete() { var b = state.bracket, champion = b.pool[0], final = b.decisions[b.decisions.length - 1], runner = final.left_id === champion.id ? final.right_id : final.left_id; root.innerHTML = '
正在保存你的冠军…
'; ensureLogin().then(function () { return request('/api/v1/tests/' + state.test.slug + '/runs',{method:'POST',data:{app_key:'hub',champion_id:champion.id,runner_up_id:runner,ranking_ids:b.pool.concat(b.eliminated).map(function(i){return i.id;}),decisions:b.decisions}}); }).then(function (result) { state.result = result; renderResult(); }).catch(renderError); } function renderResult() { var r = state.result; root.innerHTML = '
FINAL ANSWER · ' + escapeHtml(r.test_title) + '

' + escapeHtml(r.champion_name) + '

' + (r.runner_up_name ? '决赛击败 ' + escapeHtml(r.runner_up_name) : '一路赢到最后') + '

'; drawPoster(); root.querySelectorAll('[data-theme]').forEach(function (el) { el.addEventListener('click', function () { state.template = el.dataset.theme; root.querySelectorAll('[data-theme]').forEach(function(x){x.classList.remove('active');});el.classList.add('active');drawPoster(); }); }); document.getElementById('stickerToggle').addEventListener('click',function(){state.sticker=!state.sticker;this.textContent='手绘贴纸:'+(state.sticker?'开启':'关闭');drawPoster();}); document.getElementById('savePoster').addEventListener('click',function(){var a=document.createElement('a');a.download='我的PK冠军-'+r.champion_name+'.png';a.href=document.getElementById('poster').toDataURL('image/png');a.click();toast('分享图已保存');}); document.getElementById('replay').addEventListener('click',function(){loadTest(r.test_slug);}); } function drawPoster() { var canvas=document.getElementById('poster'),ctx=canvas.getContext('2d'),r=state.result,themes={celebration:['#ff6b35','#fffaf4','#25211f','#ffe568'],editorial:['#f3eadf','#201d1b','#fff','#285ee8'],sticker:['#c9f06b','#1e2810','#ff72a7','#fff']},t=themes[state.template]; ctx.fillStyle=t[0];ctx.fillRect(0,0,750,1000);ctx.fillStyle=t[1];ctx.font='700 24px system-ui';ctx.fillText('PK 研究所 / MY PICK',54,72);ctx.font='900 58px system-ui';ctx.fillText('我的冠军是',54,156);ctx.fillStyle=t[2];ctx.fillRect(46,205,658,440);ctx.fillStyle=state.template==='celebration'?'#fffaf4':t[1];ctx.font='600 26px system-ui';ctx.fillText(r.test_title,84,270);ctx.font='900 '+(r.champion_name.length>7?62:82)+'px system-ui';ctx.fillText(r.champion_name,84,405);ctx.fillStyle=t[3];ctx.fillRect(84,440,Math.min(500,r.champion_name.length*80),18);ctx.fillStyle=state.template==='celebration'?'#fffaf4':t[1];ctx.font='24px system-ui';ctx.fillText('一路二选一,它赢到了最后。',84,525);ctx.fillStyle=t[1];ctx.font='700 25px system-ui';ctx.fillText('我的 TOP 4',54,720);(r.ranking_names||[]).slice(0,4).forEach(function(name,i){ctx.font='26px system-ui';ctx.fillText((i+1)+'. '+name,64,774+i*48);});if(state.sticker){ctx.fillStyle=t[3];ctx.beginPath();ctx.arc(645,125,58,0,Math.PI*2);ctx.fill();ctx.fillStyle=t[2];ctx.font='900 28px system-ui';ctx.fillText('WIN!',607,134);ctx.font='52px system-ui';ctx.fillText('✦',640,700);ctx.fillText('♡',550,900);}ctx.fillStyle=t[1];ctx.font='20px system-ui';ctx.fillText('保存这张图,把答案分享给同频的人',54,956); } function loadHistory() { root.innerHTML='
正在翻档案…
';ensureLogin().then(function(){return request('/api/v1/me/results');}).then(renderHistory).catch(renderError); } function renderHistory(results) { root.innerHTML='
MY TASTE ARCHIVE

我的偏爱档案

每一次纠结,都算数。

做过的测试

'+results.length+' 条结果
'+(results.length?results.map(function(r,i){return '';}).join(''):'
还没有结果,去选第一个冠军吧。
')+'
';document.getElementById('linkCode').addEventListener('click',function(){request('/api/v1/me/link-code',{method:'POST'}).then(function(d){document.getElementById('code').textContent=d.code;}).catch(renderError);});root.querySelectorAll('.result-row').forEach(function(el){el.addEventListener('click',function(){state.result=results[Number(el.dataset.i)];renderResult();});}); } function renderError(error) { root.innerHTML='

暂时没接上服务

'+escapeHtml(error.message||'未知错误')+'

';document.getElementById('retry').addEventListener('click',loadHome); } document.getElementById('homeButton').addEventListener('click',loadHome); document.getElementById('meButton').addEventListener('click',loadHistory); loadHome(); }());