2026-07-08 09:52:38 +08:00

287 lines
9.8 KiB
JavaScript
Raw 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.

(function () {
var VALID_TABS = ['cp', 'brother', 'sister'];
var LAYER_BASE = './assets/yumi/layers/';
var THEMES = {
cp: {
bg: 'bg-cp.png',
tab: 'tab-cp.png',
card: 'card-cp.png',
center: 'center-cp.png',
level: 'level-cp.png',
},
brother: {
bg: 'bg-brother.png',
tab: 'tab-brother.png',
card: 'card-brother.png',
center: 'center-brother.png',
level: 'level-brother.png',
},
sister: {
bg: 'bg-sister.png',
tab: 'tab-sister.png',
card: 'card-sister.png',
center: 'center-sister.png',
level: 'level-sister.png',
},
};
var DEFAULT_RELATION = {
days: 999,
togetherDays: 4,
currentLevel: 1,
nextLevel: 1,
currentExp: 1000,
nextExp: 200000,
neededExp: 888,
nextExpLevel: 2,
progressPercent: 22.3,
me: { avatar: '' },
partner: { avatar: '' },
};
var page = document.getElementById('yumiCpPage');
var backButton = document.getElementById('yumiBackButton');
var helpButton = document.getElementById('yumiHelpButton');
var tabButtons = Array.prototype.slice.call(
document.querySelectorAll('.yumi-tab-button[data-tab]')
);
var bgLayer = document.getElementById('yumiBgLayer');
var tabJewel = document.getElementById('yumiTabJewel');
var cardSkin = document.getElementById('yumiCardSkin');
var centerArt = document.getElementById('yumiCenterArt');
var levelBgs = Array.prototype.slice.call(
document.querySelectorAll('.yumi-level-bg')
);
var leftAvatar = document.getElementById('yumiLeftAvatar');
var rightAvatar = document.getElementById('yumiRightAvatar');
var daysValue = document.getElementById('yumiDaysValue');
var expAway = document.getElementById('yumiExpAway');
var currentLevel = document.getElementById('yumiCurrentLevel');
var nextLevel = document.getElementById('yumiNextLevel');
var progressFill = document.getElementById('yumiProgressFill');
var progressText = document.getElementById('yumiProgressText');
var timeText = document.getElementById('yumiTimeText');
var state = {
activeTab: normalizeTab(readParam('tab')) || 'cp',
data: clone(DEFAULT_RELATION),
};
function clone(value) {
return JSON.parse(JSON.stringify(value));
}
function readParam(name) {
var params = new URLSearchParams(window.location.search || '');
return params.get(name);
}
function normalizeTab(value) {
var tab = String(value || '')
.trim()
.toLowerCase();
return VALID_TABS.indexOf(tab) >= 0 ? tab : '';
}
function readNumber(value, fallback) {
var number = Number(value);
return Number.isFinite(number) ? number : fallback || 0;
}
function t(key, fallback) {
if (window.HyAppI18n && window.HyAppI18n.t) {
return window.HyAppI18n.t(key, fallback);
}
return fallback || key;
}
function formatNumber(value) {
var number = Math.max(0, Math.floor(readNumber(value, 0)));
return String(number).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
function asset(name) {
return LAYER_BASE + name;
}
function syncURL(tab) {
if (!window.history || !window.history.replaceState) return;
var url = new URL(window.location.href);
url.searchParams.set('tab', tab);
// tab 是当前视觉主题状态replaceState 让刷新可复现,同时不把每次切换压入 App WebView 返回栈。
window.history.replaceState(null, '', url.toString());
}
function setImage(node, fileName) {
if (!node) return;
var nextSrc = asset(fileName);
if (node.getAttribute('src') !== nextSrc)
node.setAttribute('src', nextSrc);
}
function renderAvatar(container, url) {
if (!container) return;
container.innerHTML = '';
if (!url) return;
var image = document.createElement('img');
image.src = url;
image.alt = '';
container.appendChild(image);
}
function normalizedRelation(raw) {
var data = raw || {};
return {
days: readNumber(data.days, DEFAULT_RELATION.days),
togetherDays: readNumber(
data.togetherDays || data.together_days,
DEFAULT_RELATION.togetherDays
),
currentLevel: readNumber(
data.currentLevel || data.current_level || data.level,
1
),
nextLevel: readNumber(data.nextLevel || data.next_level, 1),
currentExp: readNumber(
data.currentExp || data.current_exp,
DEFAULT_RELATION.currentExp
),
nextExp: readNumber(
data.nextExp || data.next_exp,
DEFAULT_RELATION.nextExp
),
neededExp: readNumber(
data.neededExp || data.needed_exp,
DEFAULT_RELATION.neededExp
),
nextExpLevel: readNumber(
data.nextExpLevel || data.next_exp_level,
DEFAULT_RELATION.nextExpLevel
),
progressPercent: Math.max(
0,
Math.min(
100,
readNumber(
data.progressPercent || data.progress_percent,
DEFAULT_RELATION.progressPercent
)
)
),
me: {
avatar: String(
(data.me && data.me.avatar) || data.me_avatar || ''
),
},
partner: {
avatar: String(
(data.partner && data.partner.avatar) ||
data.partner_avatar ||
''
),
},
};
}
function renderCopy() {
var data = state.data;
daysValue.textContent = formatNumber(data.days);
expAway.textContent = t(
'cpSpace.expAway',
'{exp} Exp away from Lv. {level}'
)
.replace('{exp}', formatNumber(data.neededExp))
.replace('{level}', formatNumber(data.nextExpLevel));
currentLevel.textContent = t(
'cpSpace.levelValue',
'Lv.{level}'
).replace('{level}', formatNumber(data.currentLevel));
nextLevel.textContent = t('cpSpace.levelValue', 'Lv.{level}').replace(
'{level}',
formatNumber(data.nextLevel)
);
progressText.textContent = [
formatNumber(data.currentExp),
formatNumber(data.nextExp),
].join('/');
timeText.textContent = t(
'cpSpace.timeTogether',
'Time spent together: {days} days'
).replace('{days}', formatNumber(data.togetherDays));
progressFill.style.width =
Math.max(1, Math.min(233, (data.progressPercent / 100) * 233)) +
'px';
renderAvatar(leftAvatar, data.me.avatar);
renderAvatar(rightAvatar, data.partner.avatar);
}
function setActiveTab(tab, options) {
var nextTab = normalizeTab(tab) || 'cp';
var theme = THEMES[nextTab];
state.activeTab = nextTab;
page.setAttribute('data-tab', nextTab);
setImage(bgLayer, theme.bg);
setImage(tabJewel, theme.tab);
setImage(cardSkin, theme.card);
setImage(centerArt, theme.center);
levelBgs.forEach(function (node) {
setImage(node, theme.level);
});
tabButtons.forEach(function (button) {
var selected = button.getAttribute('data-tab') === nextTab;
button.setAttribute('aria-selected', selected ? 'true' : 'false');
});
renderCopy();
if (!options || options.syncURL !== false) syncURL(nextTab);
}
function applyExternalData(payload) {
var data = payload || {};
var tabData = data[state.activeTab] || data.relation || data;
state.data = normalizedRelation(tabData);
renderCopy();
}
function bindEvents() {
tabButtons.forEach(function (button) {
button.addEventListener('click', function () {
setActiveTab(button.getAttribute('data-tab'));
});
});
if (backButton) {
backButton.addEventListener('click', function () {
if (window.HyAppBridge && window.HyAppBridge.back) {
window.HyAppBridge.back();
return;
}
// 无客户端 bridge 的浏览器验收环境仍需保留真实返回热区。
window.history.back();
});
}
if (helpButton) {
helpButton.addEventListener('click', function () {
if (window.HyAppBridge && window.HyAppBridge.post) {
window.HyAppBridge.post('cpSpaceHelpTap', {
page: 'yumi-cp-space',
});
}
});
}
window.addEventListener('hyapp:i18n-ready', renderCopy);
window.addEventListener('hyapp:cp-space-yumi-data', function (event) {
applyExternalData(event.detail || {});
});
}
bindEvents();
if (window.HyAppCPSpaceYumiData)
applyExternalData(window.HyAppCPSpaceYumiData);
setActiveTab(state.activeTab, { syncURL: false });
if (window.HyAppBridge && window.HyAppBridge.ready) {
// ready 只声明页面能力;真实关系数据由客户端事件或网关注入,避免 H5 自行判断关系状态。
window.HyAppBridge.ready({ page: 'yumi-cp-space' });
}
})();