×
-
-
![Avatar]()
-
Player Name
-
UUID
+
+
+
+
+
+
![Avatar]()
+
Player Name
+
UUID
+
+
+
+
+
+ 行走距离
+ 0 m
+
+
+ 放置方块
+ 0
+
+
+ 挖掘方块
+ 0
+
+
+ 死亡次数
+ 0
+
+
+ 击杀数量
+ 0
+
+
+ 游玩时间
+ 0 秒
+
+
+
-
-
- 行走距离
- 0 m
-
-
- 放置方块
- 0
-
-
- 挖掘方块
- 0
-
-
- 死亡次数
- 0
-
-
- 击杀数量
- 0
-
-
diff --git a/stats_script.js b/stats_script.js
index bad9996..536ee9d 100644
--- a/stats_script.js
+++ b/stats_script.js
@@ -222,18 +222,173 @@ function setupModal() {
}
function openModal(player) {
+ const modal = document.getElementById("player-modal");
+
+ // Top Section: Identity
document.getElementById('modal-name').innerText = player.name;
document.getElementById('modal-uuid').innerText = player.uuid;
const avatar = document.getElementById('modal-avatar');
avatar.src = player.avatar;
avatar.onerror = () => { avatar.src = `https://crafatar.com/avatars/${player.uuid}?size=64&overlay`; };
+ // Top Section: Summary Stats
+ // These are from summary.json which is already formatted
document.getElementById('modal-walk').innerText = player.stats.walk_fmt;
document.getElementById('modal-placed').innerText = player.stats.placed.toLocaleString();
document.getElementById('modal-mined').innerText = player.stats.mined.toLocaleString();
document.getElementById('modal-deaths').innerText = player.stats.deaths;
document.getElementById('modal-kills').innerText = player.stats.kills;
document.getElementById('modal-playtime').innerText = player.stats.play_time_fmt;
+
+ // Bottom Section: Reset and Load Details
+ const accordion = document.getElementById('stats-accordion');
+ accordion.innerHTML = '';
+ document.getElementById('loading-details').style.display = 'block';
+ document.getElementById('loading-details').innerText = '正在加载详细数据...';
modal.style.display = "flex";
+
+ // Load existing details
+ loadPlayerDetails(player.uuid);
+}
+
+async function loadPlayerDetails(uuid) {
+ const accordion = document.getElementById('stats-accordion');
+ const loading = document.getElementById('loading-details');
+
+ try {
+ const response = await fetch(`stats/${uuid}.json`);
+ if (!response.ok) throw new Error('Stats file not found');
+
+ const data = await response.json();
+
+ if (!data.stats) {
+ loading.innerText = '暂无详细统计数据。';
+ return;
+ }
+
+ loading.style.display = 'none';
+ renderDetailsAccordion(data.stats);
+
+ } catch (error) {
+ console.error('Error loading details:', error);
+ loading.innerText = '无法加载详细数据。';
+ }
+}
+
+function renderDetailsAccordion(statsObj) {
+ const accordion = document.getElementById('stats-accordion');
+
+ // Define category mappings for better display names
+ const categoryMap = {
+ 'minecraft:custom': { name: '通用统计', icon: 'fa-chart-bar' },
+ 'minecraft:mined': { name: '挖掘统计', icon: 'fa-hammer' },
+ 'minecraft:used': { name: '使用统计', icon: 'fa-hand-paper' },
+ 'minecraft:crafted': { name: '合成统计', icon: 'fa-tools' },
+ 'minecraft:broken': { name: '破坏统计', icon: 'fa-heart-broken' },
+ 'minecraft:picked_up': { name: '拾取统计', icon: 'fa-box-open' },
+ 'minecraft:dropped': { name: '丢弃统计', icon: 'fa-trash' },
+ 'minecraft:killed': { name: '击杀统计', icon: 'fa-crosshairs' },
+ 'minecraft:killed_by': { name: '死亡统计', icon: 'fa-skull' }
+ };
+
+ // Helper to format keys (minecraft:stone -> Stone)
+ const formatKey = (key) => {
+ if (key.includes(':')) key = key.split(':')[1];
+ return key.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
+ };
+
+ // Sort categories to put 'custom' first, then others alphabetically
+ const sortedKeys = Object.keys(statsObj).sort((a, b) => {
+ if (a === 'minecraft:custom') return -1;
+ if (b === 'minecraft:custom') return 1;
+ return a.localeCompare(b);
+ });
+
+ sortedKeys.forEach(catKey => {
+ const subStats = statsObj[catKey];
+ if (Object.keys(subStats).length === 0) return;
+
+ const catInfo = categoryMap[catKey] || { name: formatKey(catKey), icon: 'fa-folder' };
+
+ // Create Accordion Item
+ const item = document.createElement('div');
+ item.className = 'accordion-item';
+
+ // Header
+ const header = document.createElement('div');
+ header.className = 'accordion-header';
+ header.innerHTML = `
+
${catInfo.name}
+
+ `;
+
+ // Content
+ const content = document.createElement('div');
+ content.className = 'accordion-content';
+
+ // Grid for stats
+ const grid = document.createElement('div');
+ grid.className = 'detail-stats-grid';
+
+ // Sort sub-items by value descending
+ const subItems = Object.entries(subStats).sort((a, b) => b[1] - a[1]);
+
+ subItems.forEach(([k, v]) => {
+ let label = formatKey(k);
+ let val = v.toLocaleString();
+
+ // Special formatting for time/distance in 'custom'
+ if (catKey === 'minecraft:custom') {
+ if (k.includes('time') || k.includes('minute')) {
+ // ticks to hours/min?
+ // Assuming 'play_time' is ticks (1/20s)
+ if (k.includes('play_time') || k.includes('time_since')) {
+ const sec = v / 20;
+ if (sec > 3600) val = (sec/3600).toFixed(1) + ' h';
+ else if (sec > 60) val = (sec/60).toFixed(1) + ' m';
+ else val = sec.toFixed(0) + ' s';
+ }
+ }
+ if (k.includes('cmt') || k.includes('one_cm')) { // one_cm
+ const m = v / 100;
+ if (m > 1000) val = (m/1000).toFixed(2) + ' km';
+ else val = m.toFixed(1) + ' m';
+ }
+ }
+
+ const statDiv = document.createElement('div');
+ statDiv.className = 'detail-stat-item';
+ statDiv.innerHTML = `
+
${label}
+
${val}
+ `;
+ grid.appendChild(statDiv);
+ });
+
+ content.appendChild(grid);
+ item.appendChild(header);
+ item.appendChild(content);
+ accordion.appendChild(item);
+
+ // Click Event
+ header.addEventListener('click', () => {
+ const isActive = header.classList.contains('active');
+
+ // Close all others? Optional. Let's keep multiple openable.
+ // But if we want accordion behavior:
+ // document.querySelectorAll('.accordion-header').forEach(h => {
+ // h.classList.remove('active');
+ // h.nextElementSibling.classList.remove('show');
+ // });
+
+ if (!isActive) {
+ header.classList.add('active');
+ content.classList.add('show');
+ } else {
+ header.classList.remove('active');
+ content.classList.remove('show');
+ }
+ });
+ });
}