287 lines
9.8 KiB
JavaScript
287 lines
9.8 KiB
JavaScript
(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' });
|
||
}
|
||
})();
|