737 lines
25 KiB
JavaScript
737 lines
25 KiB
JavaScript
(function () {
|
||
var DESIGN_WIDTH = 1080;
|
||
var MAX_PAGE_WIDTH = 768;
|
||
var LALU_COIN_IMAGE = '../../common/png/lalu-coin.png';
|
||
var REWARD_FIRST_TOP = 1816;
|
||
var REWARD_PANEL_GAP = 56;
|
||
var REWARD_GRID_TOP = 220;
|
||
var REWARD_CARD_MIN_HEIGHT = 264;
|
||
var REWARD_GRID_ROW_GAP = 24;
|
||
var REWARD_PANEL_BOTTOM_PADDING = 80;
|
||
var REWARD_STAGE_BOTTOM_PADDING = 133;
|
||
var REWARD_MIN_PANEL_HEIGHT = 320;
|
||
var PLAZA_VISIBLE_ROWS = 4;
|
||
var PLAZA_ROW_HEIGHT = 199;
|
||
var PLAZA_ROW_GAP = 24;
|
||
var PLAZA_SECONDS_PER_ROW = 3.2;
|
||
var PLAZA_FEED_LIMIT = 20;
|
||
var STORAGE_KEY = 'hy_cp_activity_end_at';
|
||
var stageHeights = {
|
||
plaza: 4283,
|
||
rank: 3670,
|
||
rewards: 4797,
|
||
};
|
||
var rewardStageHeight = stageHeights.rewards;
|
||
var activeTab = 'rank';
|
||
var countdownEndAt = 0;
|
||
var countdownTimer = 0;
|
||
var activityData = {
|
||
plazaRows: [],
|
||
previousRows: [],
|
||
rank: [],
|
||
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 = {
|
||
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 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, '"')
|
||
.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 [
|
||
'<section class="ornate-panel ',
|
||
className,
|
||
'" style="top:',
|
||
top,
|
||
'px;height:',
|
||
height,
|
||
'px">',
|
||
'<img class="panel-top" src="./assets/panel-top.webp" alt="" aria-hidden="true" />',
|
||
'<div class="panel-mid" aria-hidden="true"></div>',
|
||
'<img class="panel-bottom" src="./assets/panel-bottom.webp" alt="" aria-hidden="true" />',
|
||
'<div class="panel-body">',
|
||
bodyHTML,
|
||
'</div>',
|
||
'</section>',
|
||
].join('');
|
||
}
|
||
|
||
function estimateRewardPanelHeight(section) {
|
||
var count =
|
||
section && Array.isArray(section.items) ? section.items.length : 0;
|
||
var rows = count > 0 ? Math.ceil(count / 3) : 0;
|
||
var gridHeight =
|
||
rows > 0
|
||
? rows * REWARD_CARD_MIN_HEIGHT +
|
||
Math.max(0, rows - 1) * REWARD_GRID_ROW_GAP
|
||
: 0;
|
||
return Math.max(
|
||
REWARD_MIN_PANEL_HEIGHT,
|
||
REWARD_GRID_TOP + gridHeight + REWARD_PANEL_BOTTOM_PADDING
|
||
);
|
||
}
|
||
|
||
function renderAvatar(className, avatar) {
|
||
if (!avatar) return '';
|
||
return [
|
||
'<div class="avatar ',
|
||
className,
|
||
'">',
|
||
'<img src="',
|
||
escapeHTML(avatar),
|
||
'" alt="" />',
|
||
'</div>',
|
||
].join('');
|
||
}
|
||
|
||
function renderCPRow(row) {
|
||
var center =
|
||
row.type === 'score'
|
||
? [
|
||
'<img class="score-heart" src="./assets/score-heart.png" alt="" aria-hidden="true" />',
|
||
'<strong class="score-value">',
|
||
escapeHTML(row.score),
|
||
'</strong>',
|
||
].join('')
|
||
: [
|
||
'<div class="gift-center">',
|
||
'<img src="./assets/gift-plane.png" alt="" aria-hidden="true" />',
|
||
'</div>',
|
||
].join('');
|
||
|
||
return [
|
||
'<article class="cp-row">',
|
||
renderAvatar('avatar-left', row.leftAvatar),
|
||
renderAvatar('avatar-right', row.rightAvatar),
|
||
'<span class="name name-left">',
|
||
'<bdi>',
|
||
escapeHTML(row.leftName),
|
||
'</bdi>',
|
||
'</span>',
|
||
'<span class="name name-right">',
|
||
'<bdi>',
|
||
escapeHTML(row.rightName),
|
||
'</bdi>',
|
||
'</span>',
|
||
'<img class="deco-line deco-left" src="./assets/deco-line.png" alt="" aria-hidden="true" />',
|
||
'<img class="deco-line deco-right" src="./assets/deco-line.png" alt="" aria-hidden="true" />',
|
||
center,
|
||
'</article>',
|
||
].join('');
|
||
}
|
||
|
||
function renderPlazaCarousel(rows) {
|
||
var sourceRows = Array.isArray(rows) ? rows : [];
|
||
var shouldLoop = sourceRows.length > PLAZA_VISIBLE_ROWS;
|
||
var rowHTML = sourceRows.map(renderCPRow).join('');
|
||
var loopDistance =
|
||
sourceRows.length * (PLAZA_ROW_HEIGHT + PLAZA_ROW_GAP);
|
||
var loopDuration = Math.max(
|
||
sourceRows.length * PLAZA_SECONDS_PER_ROW,
|
||
PLAZA_VISIBLE_ROWS * PLAZA_SECONDS_PER_ROW
|
||
);
|
||
|
||
// 一组数据末尾保留一个 row-gap,再紧接完全相同的第二组;轨道移动一整组高度后,第二组首行与初始首行位置一致,循环重置时不会闪白或跳帧。
|
||
return [
|
||
'<div class="plaza-carousel-viewport">',
|
||
'<div class="plaza-carousel-track',
|
||
shouldLoop ? ' is-looping' : '',
|
||
'" style="--plaza-loop-distance:',
|
||
loopDistance,
|
||
'px;--plaza-loop-duration:',
|
||
loopDuration,
|
||
's">',
|
||
'<div class="plaza-carousel-set">',
|
||
rowHTML,
|
||
'</div>',
|
||
shouldLoop
|
||
? '<div class="plaza-carousel-set" aria-hidden="true">' +
|
||
rowHTML +
|
||
'</div>'
|
||
: '',
|
||
'</div>',
|
||
'</div>',
|
||
].join('');
|
||
}
|
||
|
||
function renderPlaza() {
|
||
var plazaRows = activityData.plazaRows || [];
|
||
var previousRows = activityData.previousRows || [];
|
||
var firstBody = [
|
||
'<h2 class="panel-title">',
|
||
t(
|
||
'cp.plazaGiftTitle',
|
||
'CP formation gifts worth more than 5,000 coins are shown here'
|
||
),
|
||
'</h2>',
|
||
renderPlazaCarousel(plazaRows),
|
||
].join('');
|
||
var panels = [renderPanel('plaza-panel-a', 1816, 1152, firstBody)];
|
||
if (previousRows.length) {
|
||
var secondBody = [
|
||
'<h2 class="panel-title">',
|
||
t('cp.previousNo1Title', 'Previous Top 3'),
|
||
'</h2>',
|
||
'<div class="cp-list">',
|
||
previousRows.map(renderCPRow).join(''),
|
||
'</div>',
|
||
].join('');
|
||
panels.push(renderPanel('plaza-panel-b', 3024, 1126, secondBody));
|
||
}
|
||
|
||
return panels.join('');
|
||
}
|
||
|
||
function renderInlineCountdown() {
|
||
var units = [
|
||
['days', 'cp.days', 'D'],
|
||
['hours', 'cp.hours', 'H'],
|
||
['minutes', 'cp.minutes', 'M'],
|
||
['seconds', 'cp.seconds', 'S'],
|
||
];
|
||
return [
|
||
'<div class="countdown-inline">',
|
||
units
|
||
.map(function (unit) {
|
||
return [
|
||
'<div class="time-unit">',
|
||
'<img src="./assets/countdown-heart.png" alt="" aria-hidden="true" />',
|
||
'<strong data-count="',
|
||
unit[0],
|
||
'">02</strong>',
|
||
'<span>',
|
||
t(unit[1], unit[2]),
|
||
'</span>',
|
||
'</div>',
|
||
].join('');
|
||
})
|
||
.join(''),
|
||
'</div>',
|
||
].join('');
|
||
}
|
||
|
||
function renderPodiumCard(item) {
|
||
var crown =
|
||
item.place === 1
|
||
? 'rank-crown-gold.png'
|
||
: item.place === 2
|
||
? 'rank-crown-silver.png'
|
||
: 'crown-bronze.png';
|
||
return [
|
||
'<article class="podium-card rank-',
|
||
item.place,
|
||
'">',
|
||
'<img class="podium-bg" src="./assets/rank-wing-heart.png" alt="" aria-hidden="true" />',
|
||
'<img class="podium-crown" src="./assets/',
|
||
crown,
|
||
'" alt="" aria-hidden="true" />',
|
||
renderAvatar('podium-avatar left', item.leftAvatar),
|
||
renderAvatar('podium-avatar right', item.rightAvatar),
|
||
'<div class="podium-names"><span>',
|
||
escapeHTML(item.leftName),
|
||
'</span><span>',
|
||
escapeHTML(item.rightName),
|
||
'</span></div>',
|
||
'</article>',
|
||
].join('');
|
||
}
|
||
|
||
function renderRankRow(item) {
|
||
return renderCPRow({
|
||
type: 'score',
|
||
leftName: item.leftName,
|
||
rightName: item.rightName,
|
||
leftAvatar: item.leftAvatar,
|
||
rightAvatar: item.rightAvatar,
|
||
score: item.score,
|
||
});
|
||
}
|
||
|
||
function renderRank() {
|
||
var rankRows = activityData.rank || [];
|
||
var podium = [
|
||
'<section class="rank-podium">',
|
||
rankRows.slice(0, 3).map(renderPodiumCard).join(''),
|
||
'</section>',
|
||
].join('');
|
||
var panelBody = [
|
||
renderInlineCountdown(),
|
||
'<div class="cp-list">',
|
||
rankRows.map(renderRankRow).join(''),
|
||
'</div>',
|
||
].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';
|
||
var cardClass =
|
||
item.resourceType === 'coin'
|
||
? 'reward-card is-coin'
|
||
: 'reward-card';
|
||
return [
|
||
'<article class="',
|
||
cardClass,
|
||
'">',
|
||
'<div class="reward-icon-box"><img src="',
|
||
escapeHTML(cover),
|
||
'" alt="" aria-hidden="true" /></div>',
|
||
'<div class="reward-name">',
|
||
escapeHTML(name),
|
||
'</div>',
|
||
note
|
||
? '<div class="reward-note">' + escapeHTML(note) + '</div>'
|
||
: '',
|
||
'</article>',
|
||
].join('');
|
||
}
|
||
|
||
function renderRewardPanel(section, index) {
|
||
var className = [
|
||
'reward-panel',
|
||
'reward-' + ['a', 'b', 'c'][index],
|
||
].join(' ');
|
||
var top = REWARD_FIRST_TOP;
|
||
for (var cursor = 0; cursor < index; cursor += 1) {
|
||
top +=
|
||
estimateRewardPanelHeight(
|
||
(activityData.rewards || mockData.rewards)[cursor]
|
||
) + REWARD_PANEL_GAP;
|
||
}
|
||
var height = estimateRewardPanelHeight(section);
|
||
var body = [
|
||
'<img class="reward-crown" src="./assets/',
|
||
section.crown,
|
||
'" alt="" aria-hidden="true" />',
|
||
'<div class="reward-grid">',
|
||
section.items.map(renderRewardItem).join(''),
|
||
'</div>',
|
||
].join('');
|
||
return renderPanel(className, top, height, body);
|
||
}
|
||
|
||
function renderRewards() {
|
||
var sections = activityData.rewards || mockData.rewards;
|
||
var bottom = REWARD_FIRST_TOP;
|
||
var html = sections
|
||
.map(function (section, index) {
|
||
var panel = renderRewardPanel(section, index);
|
||
bottom += estimateRewardPanelHeight(section) + REWARD_PANEL_GAP;
|
||
return panel;
|
||
})
|
||
.join('');
|
||
rewardStageHeight =
|
||
bottom - REWARD_PANEL_GAP + REWARD_STAGE_BOTTOM_PADDING;
|
||
return html;
|
||
}
|
||
|
||
function syncRewardPanelLayout() {
|
||
if (activeTab !== 'rewards') return;
|
||
var nextTop = REWARD_FIRST_TOP;
|
||
var panels = Array.prototype.slice.call(
|
||
tabContent.querySelectorAll('.reward-panel')
|
||
);
|
||
panels.forEach(function (panel) {
|
||
var grid = panel.querySelector('.reward-grid');
|
||
var measuredHeight = REWARD_MIN_PANEL_HEIGHT;
|
||
// 奖励内容来自接口,不同档位数量和多语言换行都可能变化;渲染后用真实 grid 高度回写面板高度,避免固定 912px 留出大空白。
|
||
if (grid) {
|
||
measuredHeight = Math.max(
|
||
measuredHeight,
|
||
grid.offsetTop +
|
||
grid.offsetHeight +
|
||
REWARD_PANEL_BOTTOM_PADDING
|
||
);
|
||
}
|
||
measuredHeight = Math.ceil(measuredHeight);
|
||
panel.style.top = nextTop + 'px';
|
||
panel.style.height = measuredHeight + 'px';
|
||
nextTop += measuredHeight + REWARD_PANEL_GAP;
|
||
});
|
||
if (panels.length) {
|
||
rewardStageHeight =
|
||
nextTop - REWARD_PANEL_GAP + REWARD_STAGE_BOTTOM_PADDING;
|
||
}
|
||
}
|
||
|
||
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 normalizePairUser(user) {
|
||
var source = normalizeUser(user);
|
||
var userID = source.user_id || source.userId || '';
|
||
var displayID = source.display_user_id || source.displayUserId || '';
|
||
var name = source.username || source.name || displayID || userID || '';
|
||
if (!name && !source.avatar) return null;
|
||
return {
|
||
name: name,
|
||
avatar: source.avatar || '',
|
||
};
|
||
}
|
||
|
||
function normalizeRankEntry(entry) {
|
||
var userA = normalizePairUser(entry.user_a);
|
||
var userB = normalizePairUser(entry.user_b);
|
||
if (!userA || !userB) return null;
|
||
return {
|
||
type: 'score',
|
||
place: Number(entry.rank || 0),
|
||
score: formatScore(entry.score),
|
||
leftName: userA.name,
|
||
rightName: userB.name,
|
||
leftAvatar: userA.avatar,
|
||
rightAvatar: userB.avatar,
|
||
};
|
||
}
|
||
|
||
function normalizeFormationGiftEntry(entry) {
|
||
var giftCoinValue = Number(
|
||
(entry &&
|
||
(entry.gift_coin_value ||
|
||
entry.giftCoinValue ||
|
||
entry.coin_spent ||
|
||
entry.coinSpent)) ||
|
||
0
|
||
);
|
||
var requester = normalizePairUser(
|
||
entry && (entry.requester || entry.user_a || entry.userA)
|
||
);
|
||
var target = normalizePairUser(
|
||
entry && (entry.target || entry.user_b || entry.userB)
|
||
);
|
||
// 业务定义是“超过 5000 金币”,不是大于等于;即使旧网关误返回 5000 或更低的记录,H5 也不能把它展示出来。
|
||
if (
|
||
!Number.isFinite(giftCoinValue) ||
|
||
giftCoinValue <= 5000 ||
|
||
!requester ||
|
||
!target
|
||
)
|
||
return null;
|
||
return {
|
||
type: 'score',
|
||
rawGiftCoinValue: giftCoinValue,
|
||
score: formatScore(giftCoinValue),
|
||
leftName: requester.name,
|
||
rightName: target.name,
|
||
leftAvatar: requester.avatar,
|
||
rightAvatar: target.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 rewardResourceType(item) {
|
||
return String(
|
||
item.resource_type || item.resourceType || ''
|
||
).toLowerCase();
|
||
}
|
||
|
||
function rewardCoverUrl(item) {
|
||
var resourceType = rewardResourceType(item);
|
||
if (resourceType === 'coin') return LALU_COIN_IMAGE;
|
||
return item.cover_url || item.coverUrl || '';
|
||
}
|
||
|
||
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) {
|
||
var resourceType = rewardResourceType(item);
|
||
return {
|
||
name: rewardName(item),
|
||
resourceType: resourceType,
|
||
note:
|
||
item.duration_days > 0
|
||
? item.duration_days + ' days'
|
||
: item.quantity > 0
|
||
? 'x' + item.quantity
|
||
: '',
|
||
coverUrl: rewardCoverUrl(item),
|
||
};
|
||
}),
|
||
};
|
||
}
|
||
|
||
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)) {
|
||
var rankRows = status.entries
|
||
.map(normalizeRankEntry)
|
||
.filter(function (item) {
|
||
return Boolean(item);
|
||
});
|
||
activityData.rank = rankRows;
|
||
}
|
||
if (Array.isArray(status.previous_entries)) {
|
||
activityData.previousRows = status.previous_entries
|
||
.map(normalizeRankEntry)
|
||
.filter(function (item) {
|
||
return Boolean(item);
|
||
})
|
||
.slice(0, 3);
|
||
}
|
||
if (Array.isArray(status.rewards) && status.rewards.length) {
|
||
activityData.rewards = status.rewards.map(normalizeRewardSection);
|
||
}
|
||
}
|
||
|
||
function applyPlazaFormationGiftFeed(data) {
|
||
var items = data && Array.isArray(data.items) ? data.items : [];
|
||
// feed 由后端按 CP 成立时间倒序返回;H5 只做口径校验并保留返回顺序,不再按金币额排成另一个榜单。
|
||
activityData.plazaRows = items
|
||
.map(normalizeFormationGiftEntry)
|
||
.filter(function (item) {
|
||
return Boolean(item);
|
||
});
|
||
}
|
||
|
||
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 loadPlazaFormationGiftFeed() {
|
||
if (
|
||
!window.HyAppAPI ||
|
||
!window.HyAppAPI.cpSpace ||
|
||
!window.HyAppAPI.cpSpace.formationGiftFeed
|
||
) {
|
||
return;
|
||
}
|
||
// 20 条可让四行视窗持续轮播,同时限制头像 DOM 和图片解码数量;gateway/user-service 还会再次夹紧上限,避免参数被篡改后无界拉取。
|
||
window.HyAppAPI.cpSpace
|
||
.formationGiftFeed({ limit: PLAZA_FEED_LIMIT })
|
||
.then(function (data) {
|
||
applyPlazaFormationGiftFeed(data);
|
||
render();
|
||
})
|
||
.catch(function () {});
|
||
}
|
||
|
||
function render() {
|
||
document.title = t('cp.pageTitle', 'Last Weekly CP');
|
||
tabContent.innerHTML =
|
||
activeTab === 'rank'
|
||
? renderRank()
|
||
: activeTab === 'rewards'
|
||
? renderRewards()
|
||
: renderPlaza();
|
||
applyTabState();
|
||
syncRewardPanelLayout();
|
||
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 =
|
||
activeTab === 'rewards'
|
||
? rewardStageHeight
|
||
: 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();
|
||
loadPlazaFormationGiftFeed();
|
||
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();
|
||
}
|
||
})();
|