(function () { var DESIGN_WIDTH = 1080; var MAX_PAGE_WIDTH = 768; var STORAGE_KEY = 'hy_cp_activity_end_at'; var stageHeights = { plaza: 4283, rank: 3670, rewards: 4797, }; var activeTab = 'plaza'; var countdownEndAt = 0; var countdownTimer = 0; var activityData = { rank: null, rewards: null, periodEndMS: 0, }; var viewport = document.getElementById('appViewport'); var page = document.getElementById('page'); var stage = document.querySelector('.stage'); var stageWrap = document.getElementById('stageWrap'); var tabContent = document.getElementById('tabContent'); var copyright = document.querySelector('.copyright'); var tabs = Array.prototype.slice.call(document.querySelectorAll('.tab')); var mockData = { plaza: { exchangeRows: buildRows('gift', 4), previousRows: buildRows('score', 4), }, rank: [ { place: 1, score: '1,234,567', leftName: 'Namename', rightName: 'Namename', }, { place: 2, score: '987,654', leftName: 'Namename', rightName: 'Namename', }, { place: 3, score: '876,543', leftName: 'Namename', rightName: 'Namename', }, ], rewards: [ { crown: 'crown-gold.png', items: [ { key: 'cp.rewardMedal7', noteKey: 'cp.rewardMedalNote' }, { key: 'cp.rewardHonorBanner7' }, { key: 'cp.rewardHeadwear7' }, { key: 'cp.rewardMount7' }, { key: 'cp.rewardCoins20000' }, { key: 'cp.rewardDuke7' }, ], }, { crown: 'crown-silver.png', items: [ { key: 'cp.rewardMedal7', noteKey: 'cp.rewardMedalNote' }, { key: 'cp.rewardHonorBanner7' }, { key: 'cp.rewardMount7' }, { key: 'cp.rewardCoins20000' }, { key: 'cp.rewardMarquis7' }, ], }, { crown: 'crown-bronze.png', items: [ { key: 'cp.rewardMedal7', noteKey: 'cp.rewardMedalNote' }, { key: 'cp.rewardHonorBanner7' }, { key: 'cp.rewardCoins10000' }, { key: 'cp.rewardCount7' }, ], }, ], }; function buildRows(type, count) { var rows = []; for (var index = 0; index < count; index += 1) { rows.push({ type: type, leftName: 'Namename', rightName: 'Namename', score: '1234567', }); } return rows; } function t(key, fallback) { if (window.HyAppI18n && window.HyAppI18n.t) { return window.HyAppI18n.t(key, fallback); } return fallback || key; } function escapeHTML(value) { return String(value == null ? '' : value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function twoDigits(value) { return String(Math.max(0, value)).padStart(2, '0'); } function resolveFallbackEndAt() { var params = new URLSearchParams(window.location.search || ''); var explicitEnd = Date.parse( params.get('end_at') || params.get('endAt') || '' ); if (Number.isFinite(explicitEnd) && explicitEnd > Date.now()) return explicitEnd; try { var cached = Number(window.localStorage.getItem(STORAGE_KEY)); if (Number.isFinite(cached) && cached > Date.now()) return cached; var fallback = Date.now() + 2 * 86400 * 1000 + 2 * 3600 * 1000 + 2 * 60 * 1000 + 2 * 1000; window.localStorage.setItem(STORAGE_KEY, String(fallback)); return fallback; } catch (error) { return ( Date.now() + 2 * 86400 * 1000 + 2 * 3600 * 1000 + 2 * 60 * 1000 + 2 * 1000 ); } } function getTimeParts() { var diff = Math.max(0, countdownEndAt - Date.now()); var totalSeconds = Math.floor(diff / 1000); return { days: Math.floor(totalSeconds / 86400), hours: Math.floor((totalSeconds % 86400) / 3600), minutes: Math.floor((totalSeconds % 3600) / 60), seconds: totalSeconds % 60, }; } function updateCountdown() { var parts = getTimeParts(); Object.keys(parts).forEach(function (key) { document .querySelectorAll('[data-count="' + key + '"]') .forEach(function (node) { node.textContent = twoDigits(parts[key]); }); }); } function renderPanel(className, top, height, bodyHTML) { return [ '
', '', '', '', '
', bodyHTML, '
', '
', ].join(''); } function renderCPRow(row) { var center = row.type === 'score' ? [ '', '', escapeHTML(row.score), '', ].join('') : [ '
', '', '
', ].join(''); return [ '
', '
', '
', '', escapeHTML(row.leftName), '', '', escapeHTML(row.rightName), '', '', '', center, '
', ].join(''); } function renderPlaza() { var firstBody = [ '

', t( 'cp.plazaGiftTitle', 'Gifts exchanged between CPs ≥ 5000 gold will be displayed here' ), '

', '

', t( 'cp.plazaGiftSubtitle', '(Display the last 10 records, sorted in ascending order.)' ), '

', '
', mockData.plaza.exchangeRows.map(renderCPRow).join(''), '
', ].join(''); var secondBody = [ '

', t('cp.previousNo1Title', 'The previous number one position'), '

', '

', t( 'cp.plazaGiftSubtitle', '(Display the last 10 records, sorted in ascending order.)' ), '

', '
', mockData.plaza.previousRows.map(renderCPRow).join(''), '
', ].join(''); return [ renderPanel('plaza-panel-a', 1816, 1152, firstBody), renderPanel('plaza-panel-b', 3024, 1126, secondBody), ].join(''); } function renderInlineCountdown() { var units = [ ['days', 'cp.days', 'D'], ['hours', 'cp.hours', 'H'], ['minutes', 'cp.minutes', 'M'], ['seconds', 'cp.seconds', 'S'], ]; return [ '
', units .map(function (unit) { return [ '
', '', '02', '', t(unit[1], unit[2]), '', '
', ].join(''); }) .join(''), '
', ].join(''); } function renderPodiumCard(item) { var crown = item.place === 1 ? 'rank-crown-gold.png' : item.place === 2 ? 'rank-crown-silver.png' : 'crown-bronze.png'; return [ '
', '', '', '
', '
', '
', escapeHTML(item.leftName), '', escapeHTML(item.rightName), '
', '
', ].join(''); } function renderRankRow(item) { return renderCPRow({ type: 'score', leftName: item.leftName, rightName: item.rightName, score: item.score, }); } function renderRank() { var rankRows = activityData.rank || mockData.rank; var podium = [ '
', rankRows.slice(0, 3).map(renderPodiumCard).join(''), '
', ].join(''); var panelRows = rankRows.length ? rankRows : mockData.rank; var panelBody = [ renderInlineCountdown(), '
', panelRows.map(renderRankRow).join(''), '
', ].join(''); return podium + renderPanel('rank-panel', 2430, 1107, panelBody); } function renderRewardItem(item) { var name = item.name || t(item.key, item.key); var note = item.note || (item.noteKey ? t(item.noteKey, '') : ''); var cover = item.coverUrl || './assets/reward-frame.png'; return [ '
', '
', '
', escapeHTML(name), '
', note ? '
' + escapeHTML(note) + '
' : '', '
', ].join(''); } function renderRewardPanel(section, index) { var className = [ 'reward-panel', 'reward-' + ['a', 'b', 'c'][index], ].join(' '); var top = [1816, 2784, 3752][index]; var body = [ '', '
', section.items.map(renderRewardItem).join(''), '
', ].join(''); return renderPanel(className, top, 912, body); } function renderRewards() { return (activityData.rewards || mockData.rewards) .map(renderRewardPanel) .join(''); } function formatScore(value) { var number = Number(value || 0); if (!Number.isFinite(number)) return '0'; return number.toLocaleString('en-US'); } function normalizeUser(user) { return user || {}; } function normalizeRankEntry(entry) { var userA = normalizeUser(entry.user_a); var userB = normalizeUser(entry.user_b); return { place: Number(entry.rank || 0), score: formatScore(entry.score), leftName: userA.username || userA.display_user_id || 'Namename', rightName: userB.username || userB.display_user_id || 'Namename', leftAvatar: userA.avatar || '', rightAvatar: userB.avatar || '', }; } function rewardName(item) { var type = item.resource_type || 'reward'; if (item.duration_days > 0) return type + ' ' + item.duration_days + 'D'; if (item.quantity > 0) return type + ' x' + item.quantity; return type; } function normalizeRewardSection(reward, index) { var crowns = ['crown-gold.png', 'crown-silver.png', 'crown-bronze.png']; return { crown: crowns[index] || 'crown-bronze.png', items: (reward.items || []).map(function (item) { return { name: rewardName(item), note: item.duration_days > 0 ? item.duration_days + ' days' : item.quantity > 0 ? 'x' + item.quantity : '', coverUrl: item.cover_url || '', }; }), }; } function applyActivityStatus(status) { if (!status) return; if (status.period && status.period.end_ms > Date.now()) { activityData.periodEndMS = status.period.end_ms; countdownEndAt = status.period.end_ms; } if (Array.isArray(status.entries) && status.entries.length) { activityData.rank = status.entries.map(normalizeRankEntry); } if (Array.isArray(status.rewards) && status.rewards.length) { activityData.rewards = status.rewards.map(normalizeRewardSection); } } function loadActivityStatus() { if (!window.HyAppAPI || !window.HyAppAPI.cpWeeklyRank) return; // 周榜配置和奖励由后端控制;H5 只在接口成功时替换展示数据,失败保持静态兜底,避免活动页因为鉴权或网络问题白屏。 window.HyAppAPI.cpWeeklyRank .status({ limit: 10 }) .then(function (status) { applyActivityStatus(status); render(); }) .catch(function () {}); } function render() { document.title = t('cp.pageTitle', 'Last Weekly CP'); tabContent.innerHTML = activeTab === 'rank' ? renderRank() : activeTab === 'rewards' ? renderRewards() : renderPlaza(); applyTabState(); updateStageHeight(); updateCountdown(); } function applyTabState() { tabs.forEach(function (tab) { var isActive = tab.getAttribute('data-tab') === activeTab; tab.classList.toggle('is-active', isActive); tab.querySelector('img').src = isActive ? './assets/tab-active.png' : './assets/tab-inactive.png'; }); page.setAttribute('data-tab', activeTab); } function getPageWidth() { var viewportWidth = window.innerWidth || document.documentElement.clientWidth || DESIGN_WIDTH; return Math.min(Math.max(viewportWidth, 1), MAX_PAGE_WIDTH); } function updateStageHeight() { var designHeight = stageHeights[activeTab] || stageHeights.plaza; var pageWidth = getPageWidth(); var designScale = pageWidth / DESIGN_WIDTH; stage.style.minHeight = designHeight + 'px'; stage.style.height = designHeight + 'px'; // 页面保持 1080 设计稿坐标系,大屏只放大到 768px,避免桌面或平板 WebView 把活动页拉得过宽。 document.documentElement.style.setProperty( '--page-width', pageWidth + 'px' ); document.documentElement.style.setProperty( '--design-scale', String(designScale) ); stageWrap.style.height = Math.ceil(designHeight * designScale) + 'px'; copyright.style.top = designHeight - 77 + 'px'; document.body.style.minHeight = stageWrap.style.height; viewport.style.minHeight = stageWrap.style.height; page.style.minHeight = stageWrap.style.height; } function bindTabs() { tabs.forEach(function (tab) { tab.addEventListener('click', function () { activeTab = tab.getAttribute('data-tab') || 'plaza'; render(); }); }); } function init() { countdownEndAt = resolveFallbackEndAt(); bindTabs(); render(); loadActivityStatus(); countdownTimer = window.setInterval(updateCountdown, 1000); window.addEventListener('resize', updateStageHeight); window.addEventListener('hyapp:i18n-ready', render); } window.addEventListener('beforeunload', function () { if (countdownTimer) window.clearInterval(countdownTimer); }); if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();